pax_global_header00006660000000000000000000000064146636204760014530gustar00rootroot0000000000000052 comment=68ab90ccb3406f22e1040b9e3d822845c063bb96 trillian-1.6.1/000077500000000000000000000000001466362047600133535ustar00rootroot00000000000000trillian-1.6.1/.github/000077500000000000000000000000001466362047600147135ustar00rootroot00000000000000trillian-1.6.1/.github/dependabot.yml000066400000000000000000000036431466362047600175510ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: gomod directory: / schedule: interval: weekly groups: go-deps: applies-to: version-updates patterns: - "*" - package-ecosystem: github-actions directory: / schedule: interval: weekly groups: github-actions-deps: applies-to: version-updates patterns: - "*" - package-ecosystem: docker directory: /examples/deployment/docker/db_client schedule: interval: weekly groups: docker-deps: applies-to: version-updates patterns: - "*" - package-ecosystem: docker directory: /examples/deployment/docker/db_server schedule: interval: weekly ignore: - dependency-name: "mysql" versions: [">= 9.0"] groups: docker-deps: applies-to: version-updates patterns: - "*" - package-ecosystem: docker directory: /examples/deployment/docker/envsubst schedule: interval: weekly groups: docker-deps: applies-to: version-updates patterns: - "*" - package-ecosystem: docker directory: /examples/deployment/docker/log_server schedule: interval: weekly groups: docker-deps: applies-to: version-updates patterns: - "*" - package-ecosystem: docker directory: /examples/deployment/docker/log_signer schedule: interval: weekly groups: docker-deps: applies-to: version-updates patterns: - "*" - package-ecosystem: docker directory: /examples/deployment/kubernetes/mysql/image schedule: interval: weekly groups: docker-deps: applies-to: version-updates patterns: - "*" - package-ecosystem: docker directory: /integration/cloudbuild/testbase schedule: interval: weekly groups: docker-deps: applies-to: version-updates patterns: - "*" - package-ecosystem: npm directory: /scripts/gcb2slack schedule: interval: weekly trillian-1.6.1/.github/workflows/000077500000000000000000000000001466362047600167505ustar00rootroot00000000000000trillian-1.6.1/.github/workflows/codeql.yml000066400000000000000000000056671466362047600207600ustar00rootroot00000000000000# 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: [ "master" ] pull_request: # The branches below must be a subset of the branches above branches: [ "master" ] schedule: - cron: '17 0 * * 3' 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://aka.ms/codeql-docs/language-support steps: - name: Checkout repository uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@2c779ab0d087cd7fe7b826087247c2c81f27bfa6 # v3.26.5 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. # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs # queries: security-extended,security-and-quality # Autobuild attempts to build any compiled languages (C/C++, C#, Go, 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@2c779ab0d087cd7fe7b826087247c2c81f27bfa6 # v3.26.5 # â„šī¸ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun # If the Autobuild fails above, remove it and uncomment the following three lines. # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. # - run: | # echo "Run, Build Application using script" # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@2c779ab0d087cd7fe7b826087247c2c81f27bfa6 # v3.26.5 with: category: "/language:${{matrix.language}}" trillian-1.6.1/.github/workflows/golangci-lint.yml000066400000000000000000000010521466362047600222200ustar00rootroot00000000000000name: golangci-lint on: push: pull_request: permissions: contents: read jobs: golangci: name: lint runs-on: ubuntu-latest steps: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version-file: go.mod - name: golangci-lint uses: golangci/golangci-lint-action@aaa42aa0628b4ae2578232a66b541047968fac86 # v6.1.0 with: version: v1.55.1 args: --timeout=8m trillian-1.6.1/.github/workflows/govulncheck.yml000066400000000000000000000006401466362047600220030ustar00rootroot00000000000000name: govulncheck on: push: branches: - master pull_request: branches: - master permissions: contents: read jobs: govulncheck_job: runs-on: ubuntu-latest name: Run govulncheck steps: - id: govulncheck uses: golang/govulncheck-action@dd0578b371c987f96d1185abb54344b44352bd58 # v1.0.3 with: go-version-file: go.mod go-package: ./... trillian-1.6.1/.github/workflows/scorecard.yml000066400000000000000000000056541466362047600214520ustar00rootroot00000000000000# 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: '16 6 * * 6' push: branches: [ "master" ] # 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@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: persist-credentials: false - name: "Run analysis" uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0 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@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 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@2c779ab0d087cd7fe7b826087247c2c81f27bfa6 # v3.26.5 with: sarif_file: results.sarif trillian-1.6.1/.github/workflows/test_crdb.yaml000066400000000000000000000044021466362047600216050ustar00rootroot00000000000000--- name: Test CRDB on: push: branches: - master pull_request: workflow_dispatch: permissions: contents: read jobs: lint: permissions: contents: read # for actions/checkout to fetch code pull-requests: read # for golangci/golangci-lint-action to fetch pull requests runs-on: ubuntu-latest steps: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version-file: go.mod check-latest: true cache: true - uses: golangci/golangci-lint-action@aaa42aa0628b4ae2578232a66b541047968fac86 # v6.1.0 with: version: 'v1.55.1' args: ./storage/crdb unit-test: runs-on: ubuntu-latest steps: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version-file: go.mod check-latest: true cache: true - name: Run tests run: go test -v ./storage/crdb/... ./quota/crdbqm/... integration: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version-file: go.mod check-latest: true cache: true - name: Build before tests run: go mod download && go build ./... - name: Run CockroachDB run: docker run --rm -d --name=roach -p 8080:8080 -p 26257:26257 -v "${PWD}/cockroach-data:/cockroach/cockroach-data" cockroachdb/cockroach:latest start-single-node --insecure - name: Wait for CockroachDB uses: nick-fields/retry@7152eba30c6575329ac0576536151aca5a72780e # v3.0.0 with: timeout_seconds: 15 max_attempts: 3 retry_on: error command: docker exec roach ./cockroach sql --insecure -e "SELECT 1" - name: Get crdb logs run: docker logs roach - name: Run tests run: ./integration/integration_test.sh env: TEST_COCKROACHDB_URI: postgresql://root@localhost:26257/defaultdb?sslmode=disable CRDB_IN_CONTAINER: true CRDB_CONTAINER_NAME: roach trillian-1.6.1/.gitignore000066400000000000000000000004331466362047600153430ustar00rootroot00000000000000*.iml *.swo *.swp *.tfstate *.tfstate.backup *~ /.idea /bazel-* /commit_log /coverage.txt /createtree /ct_hammer /ct_server /dump_tree /licenses /loglb /maphammer /mapreplay /mdmtest /protoc /trillian_log_server /trillian_log_signer /trillian_map_server default.etcd cockroach-data/ trillian-1.6.1/.golangci.yaml000066400000000000000000000020271466362047600161010ustar00rootroot00000000000000run: # timeout for analysis, e.g. 30s, 5m, default is 1m deadline: 90s skip-files: - types/internal/tls/tls.go linters-settings: gocyclo: # minimal code complexity to report, 30 by default (but we recommend 10-20) # TODO(mhutchinson): lower this again after reworking interceptor min-complexity: 26 depguard: list-type: blacklist packages: - golang.org/x/net/context - github.com/gogo/protobuf/proto issues: # Don't turn off any checks by default. We can do this explicitly if needed. exclude-use-default: false exclude-rules: # The following grpc linters are excluded because grpc.Dial, grpc.DialContext and grpc.WithBlock will be supported throughout 1.x. - linters: [staticcheck] text: 'SA1019: grpc.Dial is deprecated: use NewClient instead' - linters: [staticcheck] text: 'SA1019: grpc.DialContext is deprecated: use NewClient instead' - linters: [staticcheck] text: 'SA1019: grpc.WithBlock is deprecated: this DialOption is not supported by NewClient' trillian-1.6.1/AUTHORS000066400000000000000000000007051466362047600144250ustar00rootroot00000000000000# This is the official list of benchmark authors for copyright purposes. # This file is distinct from the CONTRIBUTORS files. # See the latter for an explanation. # # Names should be added to this file as: # Name or Organization # The email address is not required for organizations. # # Please keep the list sorted. Antonio Marcedone Google LLC Internet Security Research Group Vishal Kuo trillian-1.6.1/CHANGELOG.md000066400000000000000000003317301466362047600151730ustar00rootroot00000000000000# TRILLIAN Changelog ## HEAD ## v1.6.1 * Recommended go version for development: 1.22 * This is the version used by the cloudbuild presubmits. Using a different version can lead to presubmits failing due to unexpected diffs. ### MySQL * Add TLS support for MySQL by @fghanmi in https://github.com/google/trillian/pull/3593 * `--mysql_tls_ca`: users can provide a CA certificate, that is used to establish a secure communication with MySQL server. * `--mysql_server_name`: users can provide the name of the MySQL server to be used as the Server Name in the TLS configuration. * dedup leafidentityhash values ahead of SQL lookup of existing leaves by @bobcallaway in https://github.com/google/trillian/pull/3607 ### Documentation * Add instructions for using docker to regen derived files by @mhutchinson in https://github.com/google/trillian/pull/3489 ### Misc * Fix invalid Go toolchain version by @roger2hk in https://github.com/google/trillian/pull/3491 * Replace deprecated `prune-whitelist` flag with `prune-allowlist` for `kubectl` command by @roger2hk in https://github.com/google/trillian/pull/3307 * Remove @pphaneuf from CODEOWNERS by @roger2hk in https://github.com/google/trillian/pull/3516 * Don't bump to MySQL 9 until we explicitly choose to by @mhutchinson in https://github.com/google/trillian/pull/3560 * Don't update to MySQL 9.0 by @mhutchinson in https://github.com/google/trillian/pull/3584 ### Dependency updates * Bump google.golang.org/api from 0.155.0 to 0.156.0 by @dependabot in https://github.com/google/trillian/pull/3290 * Bump golang from `688ad7f` to `cbee5d2` in /integration/cloudbuild/testbase by @dependabot in https://github.com/google/trillian/pull/3286 * Bump golang from `688ad7f` to `cbee5d2` in /examples/deployment/docker/db_client by @dependabot in https://github.com/google/trillian/pull/3287 * Bump golang from `688ad7f` to `cbee5d2` in /examples/deployment/docker/log_signer by @dependabot in https://github.com/google/trillian/pull/3289 * Bump golang from `688ad7f` to `cbee5d2` in /examples/deployment/docker/log_server by @dependabot in https://github.com/google/trillian/pull/3288 * Bump actions/upload-artifact from 4.0.0 to 4.1.0 by @dependabot in https://github.com/google/trillian/pull/3292 * Bump golang.org/x/tools from 0.16.1 to 0.17.0 by @dependabot in https://github.com/google/trillian/pull/3291 * Bump go 1.20 -> 1.21 by @mhutchinson in https://github.com/google/trillian/pull/3293 * Bump github.com/apache/beam/sdks/v2 from 2.52.0 to 2.53.0 by @dependabot in https://github.com/google/trillian/pull/3281 * Bump CockroachDB to 22.2.17 by @roger2hk in https://github.com/google/trillian/pull/3301 * Bump github.com/cockroachdb/cockroach-go/v2 from 2.3.5 to 2.3.6 by @dependabot in https://github.com/google/trillian/pull/3305 * Bump actions/upload-artifact from 4.1.0 to 4.2.0 by @dependabot in https://github.com/google/trillian/pull/3302 * Bump k8s.io/klog/v2 from 2.120.0 to 2.120.1 by @dependabot in https://github.com/google/trillian/pull/3303 * Bump google.golang.org/api from 0.156.0 to 0.157.0 by @dependabot in https://github.com/google/trillian/pull/3304 * Bump golang from `cbee5d2` to `c4b696f` in /integration/cloudbuild/testbase by @dependabot in https://github.com/google/trillian/pull/3298 * Bump ubuntu from `6042500` to `e6173d4` in /examples/deployment/kubernetes/mysql/image by @dependabot in https://github.com/google/trillian/pull/3299 * Bump golang from `cbee5d2` to `c4b696f` in /examples/deployment/docker/db_client by @dependabot in https://github.com/google/trillian/pull/3295 * Bump golang from `cbee5d2` to `c4b696f` in /examples/deployment/docker/log_server by @dependabot in https://github.com/google/trillian/pull/3297 * Bump golang from `cbee5d2` to `c4b696f` in /examples/deployment/docker/log_signer by @dependabot in https://github.com/google/trillian/pull/3296 * Bump mysql from 8.2 to 8.3 in /examples/deployment/docker/db_server by @dependabot in https://github.com/google/trillian/pull/3306 * Bump golang from `c4b696f` to `d8c365d` in /examples/deployment/docker/log_signer by @dependabot in https://github.com/google/trillian/pull/3308 * Bump actions/upload-artifact from 4.2.0 to 4.3.0 by @dependabot in https://github.com/google/trillian/pull/3309 * Bump golang from `c4b696f` to `d8c365d` in /integration/cloudbuild/testbase by @dependabot in https://github.com/google/trillian/pull/3310 * Bump golang from `c4b696f` to `d8c365d` in /examples/deployment/docker/log_server by @dependabot in https://github.com/google/trillian/pull/3311 * Bump golang from `c4b696f` to `d8c365d` in /examples/deployment/docker/db_client by @dependabot in https://github.com/google/trillian/pull/3312 * Bump google.golang.org/grpc from 1.60.1 to 1.61.0 by @dependabot in https://github.com/google/trillian/pull/3314 * Bump google.golang.org/api from 0.157.0 to 0.158.0 by @dependabot in https://github.com/google/trillian/pull/3315 * Bump google-auth-library from 9.4.2 to 9.5.0 in /scripts/gcb2slack by @dependabot in https://github.com/google/trillian/pull/3316 * Bump google.golang.org/api from 0.158.0 to 0.159.0 by @dependabot in https://github.com/google/trillian/pull/3317 * Bump google-auth-library from 9.5.0 to 9.6.0 in /scripts/gcb2slack by @dependabot in https://github.com/google/trillian/pull/3319 * Bump google.golang.org/api from 0.159.0 to 0.160.0 by @dependabot in https://github.com/google/trillian/pull/3320 * Bump alpine from `51b6726` to `c5b1261` in /examples/deployment/docker/envsubst by @dependabot in https://github.com/google/trillian/pull/3321 * Bump cloud.google.com/go/spanner from 1.55.0 to 1.56.0 by @dependabot in https://github.com/google/trillian/pull/3322 * Bump go.etcd.io/etcd/v3 from 3.5.11 to 3.5.12 by @dependabot in https://github.com/google/trillian/pull/3327 * Bump google.golang.org/api from 0.160.0 to 0.161.0 by @dependabot in https://github.com/google/trillian/pull/3323 * Bump nick-fields/retry from 2.9.0 to 3.0.0 by @dependabot in https://github.com/google/trillian/pull/3328 * Bump golang from `d8c365d` to `3efef61` in /examples/deployment/docker/db_client by @dependabot in https://github.com/google/trillian/pull/3329 * Bump golang from `d8c365d` to `3efef61` in /integration/cloudbuild/testbase by @dependabot in https://github.com/google/trillian/pull/3331 * Bump golang from `d8c365d` to `3efef61` in /examples/deployment/docker/log_signer by @dependabot in https://github.com/google/trillian/pull/3330 * Bump golang from `d8c365d` to `3efef61` in /examples/deployment/docker/log_server by @dependabot in https://github.com/google/trillian/pull/3332 * Bump google-auth-library from 9.6.0 to 9.6.1 in /scripts/gcb2slack by @dependabot in https://github.com/google/trillian/pull/3333 * Bump google-auth-library from 9.6.1 to 9.6.2 in /scripts/gcb2slack by @dependabot in https://github.com/google/trillian/pull/3334 * Bump ubuntu from `e6173d4` to `e9569c2` in /examples/deployment/kubernetes/mysql/image by @dependabot in https://github.com/google/trillian/pull/3335 * Bump distroless/base-debian12 from `0a93daa` to `f47fa3d` in /examples/deployment/docker/log_server by @dependabot in https://github.com/google/trillian/pull/3336 * Bump distroless/base-debian12 from `0a93daa` to `f47fa3d` in /examples/deployment/docker/log_signer by @dependabot in https://github.com/google/trillian/pull/3337 * Bump google.golang.org/api from 0.161.0 to 0.162.0 by @dependabot in https://github.com/google/trillian/pull/3340 * Bump actions/upload-artifact from 4.3.0 to 4.3.1 by @dependabot in https://github.com/google/trillian/pull/3342 * Bump kaniko to v1.20.0 to fix #3338 by @AlCutter in https://github.com/google/trillian/pull/3339 * Bump golang.org/x/crypto from 0.18.0 to 0.19.0 by @dependabot in https://github.com/google/trillian/pull/3347 * Bump golang from 1.21.6-bookworm to 1.22.0-bookworm in /integration/cloudbuild/testbase by @dependabot in https://github.com/google/trillian/pull/3346 * Bump google-auth-library from 9.6.2 to 9.6.3 in /scripts/gcb2slack by @dependabot in https://github.com/google/trillian/pull/3352 * Bump golang from 1.21.6-bookworm to 1.22.0-bookworm in /examples/deployment/docker/log_server by @dependabot in https://github.com/google/trillian/pull/3351 * Bump golang from 1.21.6-bookworm to 1.22.0-bookworm in /examples/deployment/docker/log_signer by @dependabot in https://github.com/google/trillian/pull/3350 * Bump golang from 1.21.6-bookworm to 1.22.0-bookworm in /examples/deployment/docker/db_client by @dependabot in https://github.com/google/trillian/pull/3349 * Bump golangci/golangci-lint-action from 3.7.0 to 3.7.1 by @dependabot in https://github.com/google/trillian/pull/3354 * Bump google.golang.org/api from 0.162.0 to 0.163.0 by @dependabot in https://github.com/google/trillian/pull/3353 * Bump distroless/base-debian12 from `f47fa3d` to `2102ce1` in /examples/deployment/docker/log_server by @dependabot in https://github.com/google/trillian/pull/3355 * Bump distroless/base-debian12 from `f47fa3d` to `2102ce1` in /examples/deployment/docker/log_signer by @dependabot in https://github.com/google/trillian/pull/3356 * Bump golang from `874c267` to `925fe3f` in /integration/cloudbuild/testbase by @dependabot in https://github.com/google/trillian/pull/3361 * Bump cloud.google.com/go/spanner from 1.56.0 to 1.57.0 by @dependabot in https://github.com/google/trillian/pull/3358 * Bump github.com/apache/beam/sdks/v2 from 2.53.0 to 2.54.0 by @dependabot in https://github.com/google/trillian/pull/3365 * Bump google.golang.org/api from 0.163.0 to 0.165.0 by @dependabot in https://github.com/google/trillian/pull/3366 * Bump golang from `874c267` to `925fe3f` in /examples/deployment/docker/log_signer by @dependabot in https://github.com/google/trillian/pull/3357 * Bump golang from `874c267` to `925fe3f` in /examples/deployment/docker/db_client by @dependabot in https://github.com/google/trillian/pull/3362 * Bump golang.org/x/tools from 0.17.0 to 0.18.0 by @dependabot in https://github.com/google/trillian/pull/3360 * Bump google.golang.org/grpc from 1.61.0 to 1.61.1 by @dependabot in https://github.com/google/trillian/pull/3364 * Bump golang from `874c267` to `925fe3f` in /examples/deployment/docker/log_server by @dependabot in https://github.com/google/trillian/pull/3363 * Bump github.com/prometheus/client_model from 0.5.0 to 0.6.0 by @dependabot in https://github.com/google/trillian/pull/3367 * Bump ubuntu from `e9569c2` to `f9d633f` in /examples/deployment/kubernetes/mysql/image by @dependabot in https://github.com/google/trillian/pull/3368 * Bump golang/govulncheck-action from 1.0.1 to 1.0.2 by @dependabot in https://github.com/google/trillian/pull/3369 * Bump google.golang.org/api from 0.165.0 to 0.166.0 by @dependabot in https://github.com/google/trillian/pull/3370 * Bump google.golang.org/grpc from 1.61.1 to 1.62.0 by @dependabot in https://github.com/google/trillian/pull/3371 * Bump google.golang.org/api from 0.166.0 to 0.167.0 by @dependabot in https://github.com/google/trillian/pull/3374 * Bump distroless/base-debian12 from `2102ce1` to `5eae9ef` in /examples/deployment/docker/log_server by @dependabot in https://github.com/google/trillian/pull/3373 * Bump distroless/base-debian12 from `2102ce1` to `5eae9ef` in /examples/deployment/docker/log_signer by @dependabot in https://github.com/google/trillian/pull/3372 * Bump golang.org/x/crypto from 0.19.0 to 0.20.0 by @dependabot in https://github.com/google/trillian/pull/3375 * Bump distroless/base-debian12 from `5eae9ef` to `f9b0e86` in /examples/deployment/docker/log_signer by @dependabot in https://github.com/google/trillian/pull/3377 * Bump github.com/prometheus/client_golang from 1.18.0 to 1.19.0 by @dependabot in https://github.com/google/trillian/pull/3376 * Bump distroless/base-debian12 from `f9b0e86` to `5eae9ef` in /examples/deployment/docker/log_signer by @dependabot in https://github.com/google/trillian/pull/3379 * Bump golang.org/x/crypto from 0.20.0 to 0.21.0 by @dependabot in https://github.com/google/trillian/pull/3380 * Bump google.golang.org/api from 0.167.0 to 0.168.0 by @dependabot in https://github.com/google/trillian/pull/3382 * Bump go-version-input from 1.21.6 to 1.21.8 in govulncheck and bump google.golang.org/protobuf from 1.32.0 to 1.33.0 and bump github.com/golang/protobuf from 1.5.3 to 1.5.4 by @roger2hk in https://github.com/google/trillian/pull/3393 * Bump google.golang.org/grpc from 1.62.0 to 1.62.1 by @dependabot in https://github.com/google/trillian/pull/3385 * Bump golang.org/x/tools from 0.18.0 to 0.19.0 by @dependabot in https://github.com/google/trillian/pull/3383 * Bump cloud.google.com/go/spanner from 1.57.0 to 1.58.0 by @dependabot in https://github.com/google/trillian/pull/3391 * Bump golang from 1.22.0-bookworm to 1.22.1-bookworm in /integration/cloudbuild/testbase by @dependabot in https://github.com/google/trillian/pull/3389 * Bump ubuntu from `f9d633f` to `77906da` in /examples/deployment/kubernetes/mysql/image by @dependabot in https://github.com/google/trillian/pull/3392 * Bump golang from 1.22.0-bookworm to 1.22.1-bookworm in /examples/deployment/docker/log_server by @dependabot in https://github.com/google/trillian/pull/3387 * Bump github.com/cockroachdb/cockroach-go/v2 from 2.3.6 to 2.3.7 by @dependabot in https://github.com/google/trillian/pull/3390 * Bump golang from 1.22.0-bookworm to 1.22.1-bookworm in /examples/deployment/docker/db_client by @dependabot in https://github.com/google/trillian/pull/3388 * Bump golang from 1.22.0-bookworm to 1.22.1-bookworm in /examples/deployment/docker/log_signer by @dependabot in https://github.com/google/trillian/pull/3386 * Bump google.golang.org/api from 0.168.0 to 0.169.0 by @dependabot in https://github.com/google/trillian/pull/3394 * Bump distroless/base-debian12 from `5eae9ef` to `28a7f1f` in /examples/deployment/docker/log_server by @dependabot in https://github.com/google/trillian/pull/3396 * Bump github.com/go-sql-driver/mysql from 1.7.1 to 1.8.0 by @dependabot in https://github.com/google/trillian/pull/3395 * Bump distroless/base-debian12 from `5eae9ef` to `28a7f1f` in /examples/deployment/docker/log_signer by @dependabot in https://github.com/google/trillian/pull/3397 * Bump github.com/jackc/pgx/v4 from 4.18.1 to 4.18.2 by @dependabot in https://github.com/google/trillian/pull/3398 * Bump actions/checkout from 4.1.1 to 4.1.2 by @dependabot in https://github.com/google/trillian/pull/3401 * Bump golang from `6699d28` to `d996c64` in /examples/deployment/docker/log_server by @dependabot in https://github.com/google/trillian/pull/3399 * Bump golang from `6699d28` to `d996c64` in /integration/cloudbuild/testbase by @dependabot in https://github.com/google/trillian/pull/3403 * Bump golang from `6699d28` to `d996c64` in /examples/deployment/docker/db_client by @dependabot in https://github.com/google/trillian/pull/3402 * Bump golang from `6699d28` to `d996c64` in /examples/deployment/docker/log_signer by @dependabot in https://github.com/google/trillian/pull/3400 * Bump google-auth-library from 9.6.3 to 9.7.0 in /scripts/gcb2slack by @dependabot in https://github.com/google/trillian/pull/3404 * Bump cloud.google.com/go/spanner from 1.58.0 to 1.59.0 by @dependabot in https://github.com/google/trillian/pull/3405 * Bump google.golang.org/api from 0.169.0 to 0.170.0 by @dependabot in https://github.com/google/trillian/pull/3406 * Bump follow-redirects from 1.15.4 to 1.15.6 in /scripts/gcb2slack by @dependabot in https://github.com/google/trillian/pull/3407 * Bump cloud.google.com/go/spanner from 1.59.0 to 1.60.0 by @dependabot in https://github.com/google/trillian/pull/3408 * Bump github.com/docker/docker from 24.0.7+incompatible to 24.0.9+incompatible by @dependabot in https://github.com/google/trillian/pull/3409 * Bump google.golang.org/api from 0.170.0 to 0.171.0 by @dependabot in https://github.com/google/trillian/pull/3410 * Bump github.com/go-sql-driver/mysql from 1.8.0 to 1.8.1 by @dependabot in https://github.com/google/trillian/pull/3412 * Bump express from 4.18.2 to 4.19.2 in /scripts/gcb2slack by @dependabot in https://github.com/google/trillian/pull/3413 * Bump github.com/apache/beam/sdks/v2 from 2.54.0 to 2.55.0 by @dependabot in https://github.com/google/trillian/pull/3411 * Bump go.etcd.io/etcd/v3 from 3.5.12 to 3.5.13 by @dependabot in https://github.com/google/trillian/pull/3415 * Bump google.golang.org/api from 0.171.0 to 0.172.0 by @dependabot in https://github.com/google/trillian/pull/3414 * Bump distroless/base-debian12 from `28a7f1f` to `611d30d` in /examples/deployment/docker/log_signer by @dependabot in https://github.com/google/trillian/pull/3419 * Bump distroless/base-debian12 from `28a7f1f` to `611d30d` in /examples/deployment/docker/log_server by @dependabot in https://github.com/google/trillian/pull/3420 * Bump golang from 1.22.1-bookworm to 1.22.2-bookworm in /examples/deployment/docker/db_client by @dependabot in https://github.com/google/trillian/pull/3421 * update govulncheck go version from 1.21.8 to 1.21.9 and bump golang.org/x/net from v0.22.0 to v0.23.0 by @phbnf in https://github.com/google/trillian/pull/3427 * Bump golang from 1.22.1-bookworm to 1.22.2-bookworm in /integration/cloudbuild/testbase by @dependabot in https://github.com/google/trillian/pull/3426 * Bump github.com/prometheus/client_model from 0.6.0 to 0.6.1 by @dependabot in https://github.com/google/trillian/pull/3422 * Bump golang.org/x/sys from 0.18.0 to 0.19.0 by @dependabot in https://github.com/google/trillian/pull/3428 * Bump golang from 1.22.1-bookworm to 1.22.2-bookworm in /examples/deployment/docker/log_signer by @dependabot in https://github.com/google/trillian/pull/3424 * Bump golang from 1.22.1-bookworm to 1.22.2-bookworm in /examples/deployment/docker/log_server by @dependabot in https://github.com/google/trillian/pull/3425 * Bump golang.org/x/crypto from 0.21.0 to 0.22.0 by @dependabot in https://github.com/google/trillian/pull/3429 * Bump golang.org/x/tools from 0.19.0 to 0.20.0 by @dependabot in https://github.com/google/trillian/pull/3432 * Bump github.com/apache/beam/sdks/v2 from 2.55.0 to 2.55.1 by @dependabot in https://github.com/google/trillian/pull/3433 * Bump google.golang.org/grpc from 1.62.1 to 1.63.2 by @dependabot in https://github.com/google/trillian/pull/3434 * Bump golang from `48b942a` to `3c7ad81` in /examples/deployment/docker/log_signer by @dependabot in https://github.com/google/trillian/pull/3437 * Bump golang from `48b942a` to `3c7ad81` in /examples/deployment/docker/db_client by @dependabot in https://github.com/google/trillian/pull/3435 * Bump golang from `48b942a` to `3c7ad81` in /integration/cloudbuild/testbase by @dependabot in https://github.com/google/trillian/pull/3436 * Bump golang from `48b942a` to `fb54c61` in /examples/deployment/docker/log_server by @dependabot in https://github.com/google/trillian/pull/3439 * Bump golang from `3c7ad81` to `b03f3ba` in /integration/cloudbuild/testbase by @dependabot in https://github.com/google/trillian/pull/3440 * Bump golang from `3c7ad81` to `b03f3ba` in /examples/deployment/docker/db_client by @dependabot in https://github.com/google/trillian/pull/3442 * Bump golang from `3c7ad81` to `b03f3ba` in /examples/deployment/docker/log_signer by @dependabot in https://github.com/google/trillian/pull/3441 * Bump golang from `fb54c61` to `b03f3ba` in /examples/deployment/docker/log_server by @dependabot in https://github.com/google/trillian/pull/3443 * Bump google-auth-library from 9.7.0 to 9.8.0 in /scripts/gcb2slack by @dependabot in https://github.com/google/trillian/pull/3444 * Bump google.golang.org/api from 0.172.0 to 0.173.0 by @dependabot in https://github.com/google/trillian/pull/3445 * Bump ubuntu from `77906da` to `1b8d8ff` in /examples/deployment/kubernetes/mysql/image by @dependabot in https://github.com/google/trillian/pull/3446 * Bump google.golang.org/api from 0.173.0 to 0.174.0 by @dependabot in https://github.com/google/trillian/pull/3447 * Bump actions/upload-artifact from 4.3.1 to 4.3.2 by @dependabot in https://github.com/google/trillian/pull/3448 * Bump google.golang.org/api from 0.174.0 to 0.175.0 by @dependabot in https://github.com/google/trillian/pull/3449 * Bump actions/checkout from 4.1.2 to 4.1.3 by @dependabot in https://github.com/google/trillian/pull/3450 * Bump actions/upload-artifact from 4.3.2 to 4.3.3 by @dependabot in https://github.com/google/trillian/pull/3451 * Bump google.golang.org/api from 0.175.0 to 0.176.0 by @dependabot in https://github.com/google/trillian/pull/3452 * Bump google.golang.org/api from 0.176.0 to 0.176.1 by @dependabot in https://github.com/google/trillian/pull/3453 * Bump actions/checkout from 4.1.3 to 4.1.4 by @dependabot in https://github.com/google/trillian/pull/3455 * Bump golangci/golangci-lint-action from 4.0.0 to 5.0.0 by @dependabot in https://github.com/google/trillian/pull/3460 * Bump google-auth-library from 9.8.0 to 9.9.0 in /scripts/gcb2slack by @dependabot in https://github.com/google/trillian/pull/3456 * Bump golang from `b03f3ba` to `d0902ba` in /integration/cloudbuild/testbase by @dependabot in https://github.com/google/trillian/pull/3454 * Bump golang from `b03f3ba` to `d0902ba` in /examples/deployment/docker/db_client by @dependabot in https://github.com/google/trillian/pull/3457 * Bump ubuntu from `1b8d8ff` to `6d7b5d3` in /examples/deployment/kubernetes/mysql/image by @dependabot in https://github.com/google/trillian/pull/3461 * Bump golang from `b03f3ba` to `d0902ba` in /examples/deployment/docker/log_signer by @dependabot in https://github.com/google/trillian/pull/3459 * Bump golang from `b03f3ba` to `d0902ba` in /examples/deployment/docker/log_server by @dependabot in https://github.com/google/trillian/pull/3458 * Bump distroless/base-debian12 from `611d30d` to `d8d01e2` in /examples/deployment/docker/log_signer by @dependabot in https://github.com/google/trillian/pull/3463 * Bump distroless/base-debian12 from `611d30d` to `d8d01e2` in /examples/deployment/docker/log_server by @dependabot in https://github.com/google/trillian/pull/3464 * Bump cloud.google.com/go/spanner from 1.60.0 to 1.61.0 by @dependabot in https://github.com/google/trillian/pull/3468 * Bump golangci/golangci-lint-action from 5.0.0 to 5.1.0 by @dependabot in https://github.com/google/trillian/pull/3462 * Bump @google-cloud/functions-framework from 3.3.0 to 3.4.0 in /scripts/gcb2slack by @dependabot in https://github.com/google/trillian/pull/3465 * Bump mysql from 8.3 to 8.4 in /examples/deployment/docker/db_server by @dependabot in https://github.com/google/trillian/pull/3469 * Bump actions/setup-go from 5.0.0 to 5.0.1 by @dependabot in https://github.com/google/trillian/pull/3470 * Bump ubuntu from `6d7b5d3` to `a6d2b38` in /examples/deployment/kubernetes/mysql/image by @dependabot in https://github.com/google/trillian/pull/3471 * Bump github.com/apache/beam/sdks/v2 from 2.55.1 to 2.56.0 by @dependabot in https://github.com/google/trillian/pull/3472 * Bump github.com/cockroachdb/cockroach-go/v2 from 2.3.7 to 2.3.8 by @dependabot in https://github.com/google/trillian/pull/3473 * Bump distroless/base-debian12 from `d8d01e2` to `786007f` in /examples/deployment/docker/log_signer by @dependabot in https://github.com/google/trillian/pull/3474 * Bump golangci/golangci-lint-action from 5.1.0 to 5.3.0 by @dependabot in https://github.com/google/trillian/pull/3479 * Bump golang.org/x/crypto from 0.22.0 to 0.23.0 by @dependabot in https://github.com/google/trillian/pull/3476 * Bump distroless/base-debian12 from `d8d01e2` to `786007f` in /examples/deployment/docker/log_server by @dependabot in https://github.com/google/trillian/pull/3480 * Bump golang from 1.22.2-bookworm to 1.22.3-bookworm in /integration/cloudbuild/testbase by @dependabot in https://github.com/google/trillian/pull/3482 * Bump golang from 1.22.2-bookworm to 1.22.3-bookworm in /examples/deployment/docker/log_server by @dependabot in https://github.com/google/trillian/pull/3481 * Bump `go-version-input` to 1.21.10 in govulncheck.yml by @roger2hk in https://github.com/google/trillian/pull/3488 * Bump google.golang.org/protobuf from 1.33.0 to 1.34.1 by @dependabot in https://github.com/google/trillian/pull/3475 * Bump google.golang.org/api from 0.176.1 to 0.178.0 by @dependabot in https://github.com/google/trillian/pull/3484 * Bump github.com/fullstorydev/grpcurl from 1.8.9 to 1.9.1 by @dependabot in https://github.com/google/trillian/pull/3438 * Bump actions/checkout from 4.1.4 to 4.1.5 by @dependabot in https://github.com/google/trillian/pull/3478 * Bump golang.org/x/tools from 0.20.0 to 0.21.0 by @dependabot in https://github.com/google/trillian/pull/3485 * Bump golang from 1.22.2-bookworm to 1.22.3-bookworm in /examples/deployment/docker/db_client by @dependabot in https://github.com/google/trillian/pull/3487 * Bump golang from 1.22.2-bookworm to 1.22.3-bookworm in /examples/deployment/docker/log_signer by @dependabot in https://github.com/google/trillian/pull/3483 * Bump golangci/golangci-lint-action from 5.3.0 to 6.0.1 by @dependabot in https://github.com/google/trillian/pull/3490 * Bump ossf/scorecard-action from 2.3.1 to 2.3.3 by @dependabot in https://github.com/google/trillian/pull/3492 * Bump github.com/prometheus/client_golang from 1.19.0 to 1.19.1 by @dependabot in https://github.com/google/trillian/pull/3493 * Bump google.golang.org/api from 0.178.0 to 0.180.0 by @dependabot in https://github.com/google/trillian/pull/3494 * Bump google-auth-library from 9.9.0 to 9.10.0 in /scripts/gcb2slack by @dependabot in https://github.com/google/trillian/pull/3495 * Bump golang from `6d71b7c` to `c2bc4ef` in /integration/cloudbuild/testbase by @dependabot in https://github.com/google/trillian/pull/3496 * Bump golang from `6d71b7c` to `c2bc4ef` in /examples/deployment/docker/log_server by @dependabot in https://github.com/google/trillian/pull/3497 * Bump golang from `6d71b7c` to `c2bc4ef` in /examples/deployment/docker/log_signer by @dependabot in https://github.com/google/trillian/pull/3498 * Bump golang from `6d71b7c` to `c2bc4ef` in /examples/deployment/docker/db_client by @dependabot in https://github.com/google/trillian/pull/3499 * Bump golang from `c2bc4ef` to `ef27a3c` in /examples/deployment/docker/db_client by @dependabot in https://github.com/google/trillian/pull/3501 * Bump golang from `c2bc4ef` to `ef27a3c` in /examples/deployment/docker/log_server by @dependabot in https://github.com/google/trillian/pull/3500 * Bump golang from `c2bc4ef` to `ef27a3c` in /integration/cloudbuild/testbase by @dependabot in https://github.com/google/trillian/pull/3505 * Bump golang from `c2bc4ef` to `ef27a3c` in /examples/deployment/docker/log_signer by @dependabot in https://github.com/google/trillian/pull/3502 * Bump cloud.google.com/go/spanner from 1.61.0 to 1.62.0 by @dependabot in https://github.com/google/trillian/pull/3504 * Bump google.golang.org/api from 0.180.0 to 0.181.0 by @dependabot in https://github.com/google/trillian/pull/3506 * Bump actions/checkout from 4.1.5 to 4.1.6 by @dependabot in https://github.com/google/trillian/pull/3508 * Bump golang from `ef27a3c` to `5c56bd4` in /examples/deployment/docker/db_client by @dependabot in https://github.com/google/trillian/pull/3507 * Bump golang from `ef27a3c` to `5c56bd4` in /integration/cloudbuild/testbase by @dependabot in https://github.com/google/trillian/pull/3510 * Bump golang from `ef27a3c` to `5c56bd4` in /examples/deployment/docker/log_server by @dependabot in https://github.com/google/trillian/pull/3511 * Bump github/codeql-action from 2.13.4 to 3.25.5 by @dependabot in https://github.com/google/trillian/pull/3512 * Bump golang from `ef27a3c` to `5c56bd4` in /examples/deployment/docker/log_signer by @dependabot in https://github.com/google/trillian/pull/3509 * Bump github/codeql-action from 3.25.5 to 3.25.6 by @dependabot in https://github.com/google/trillian/pull/3513 * Bump alpine from 3.19 to 3.20 in /examples/deployment/docker/envsubst by @dependabot in https://github.com/google/trillian/pull/3514 * Bump google.golang.org/grpc from 1.63.2 to 1.64.0 by @dependabot in https://github.com/google/trillian/pull/3503 * Bump cloud.google.com/go/spanner from 1.62.0 to 1.63.0 by @dependabot in https://github.com/google/trillian/pull/3515 * Bump google.golang.org/api from 0.181.0 to 0.182.0 by @dependabot in https://github.com/google/trillian/pull/3517 * Bump go.etcd.io/etcd/v3 from 3.5.13 to 3.5.14 by @dependabot in https://github.com/google/trillian/pull/3520 * Bump github/codeql-action from 3.25.6 to 3.25.7 by @dependabot in https://github.com/google/trillian/pull/3523 * Bump golang/govulncheck-action from 1.0.2 to 1.0.3 by @dependabot in https://github.com/google/trillian/pull/3524 * Bump the version of go used by the vuln scanner by @mhutchinson in https://github.com/google/trillian/pull/3536 * Group dependabot updates together by @mhutchinson in https://github.com/google/trillian/pull/3535 * Bump ubuntu from `a6d2b38` to `19478ce` in /examples/deployment/kubernetes/mysql/image in the docker-deps group by @dependabot in https://github.com/google/trillian/pull/3537 * Bump the go-deps group with 8 updates by @dependabot in https://github.com/google/trillian/pull/3538 * Bump github/codeql-action from 3.25.7 to 3.25.8 by @dependabot in https://github.com/google/trillian/pull/3534 * Bump golang from 1.22.3-bookworm to 1.22.4-bookworm in /integration/cloudbuild/testbase by @dependabot in https://github.com/google/trillian/pull/3533 * Bump golang from 1.22.3-bookworm to 1.22.4-bookworm in /examples/deployment/docker/log_server by @dependabot in https://github.com/google/trillian/pull/3526 * Bump golang from 1.22.3-bookworm to 1.22.4-bookworm in /examples/deployment/docker/log_signer by @dependabot in https://github.com/google/trillian/pull/3528 * Bump golang from 1.22.3-bookworm to 1.22.4-bookworm in /examples/deployment/docker/db_client by @dependabot in https://github.com/google/trillian/pull/3527 * Bump golang from `aec4784` to `9678844` in /examples/deployment/docker/log_server in the docker-deps group by @dependabot in https://github.com/google/trillian/pull/3545 * Bump golang from `aec4784` to `9678844` in /examples/deployment/docker/log_signer in the docker-deps group by @dependabot in https://github.com/google/trillian/pull/3544 * Bump golang from `aec4784` to `9678844` in /examples/deployment/docker/db_client in the docker-deps group by @dependabot in https://github.com/google/trillian/pull/3543 * Bump the github-actions-deps group with 2 updates by @dependabot in https://github.com/google/trillian/pull/3540 * Bump google-auth-library from 9.10.0 to 9.11.0 in /scripts/gcb2slack by @dependabot in https://github.com/google/trillian/pull/3539 * Bump golang from `aec4784` to `9678844` in /integration/cloudbuild/testbase in the docker-deps group by @dependabot in https://github.com/google/trillian/pull/3542 * Bump alpine from `77726ef` to `b89d9c9` in /examples/deployment/docker/envsubst in the docker-deps group by @dependabot in https://github.com/google/trillian/pull/3546 * Bump the go-deps group across 1 directory with 7 updates by @dependabot in https://github.com/google/trillian/pull/3547 * Bump the go-deps group with 5 updates by @dependabot in https://github.com/google/trillian/pull/3550 * Bump github/codeql-action from 3.25.10 to 3.25.11 in the github-actions-deps group by @dependabot in https://github.com/google/trillian/pull/3549 * Bump the version of go, and make vuln check share version by @mhutchinson in https://github.com/google/trillian/pull/3551 * Bump golang from 1.22.4-bookworm to 1.22.5-bookworm in /integration/cloudbuild/testbase in the docker-deps group by @dependabot in https://github.com/google/trillian/pull/3552 * Bump actions/upload-artifact from 4.3.3 to 4.3.4 in the github-actions-deps group by @dependabot in https://github.com/google/trillian/pull/3556 * Bump golang from 1.22.4-bookworm to 1.22.5-bookworm in /examples/deployment/docker/db_client in the docker-deps group by @dependabot in https://github.com/google/trillian/pull/3559 * Bump the docker-deps group in /examples/deployment/docker/log_signer with 2 updates by @dependabot in https://github.com/google/trillian/pull/3554 * Bump ubuntu from `19478ce` to `340d9b0` in /examples/deployment/kubernetes/mysql/image in the docker-deps group by @dependabot in https://github.com/google/trillian/pull/3558 * Bump the docker-deps group in /examples/deployment/docker/log_server with 2 updates by @dependabot in https://github.com/google/trillian/pull/3555 * Bump the go-deps group with 5 updates by @dependabot in https://github.com/google/trillian/pull/3557 * Bump the go-deps group with 4 updates by @dependabot in https://github.com/google/trillian/pull/3564 * Bump @google-cloud/functions-framework from 3.4.0 to 3.4.1 in /scripts/gcb2slack by @dependabot in https://github.com/google/trillian/pull/3563 * Bump the github-actions-deps group with 2 updates by @dependabot in https://github.com/google/trillian/pull/3562 * Bump the go-deps group with 7 updates by @dependabot in https://github.com/google/trillian/pull/3565 * Bump alpine from `b89d9c9` to `a59bbcb` in /examples/deployment/docker/envsubst in the docker-deps group by @dependabot in https://github.com/google/trillian/pull/3567 * Bump github/codeql-action from 3.25.12 to 3.25.13 in the github-actions-deps group by @dependabot in https://github.com/google/trillian/pull/3566 * Bump golang from `6c27802` to `af9b40f` in /examples/deployment/docker/log_server in the docker-deps group by @dependabot in https://github.com/google/trillian/pull/3569 * Bump golang from `6c27802` to `af9b40f` in /integration/cloudbuild/testbase in the docker-deps group by @dependabot in https://github.com/google/trillian/pull/3576 * Bump alpine from `a59bbcb` to `0a4eaa0` in /examples/deployment/docker/envsubst in the docker-deps group by @dependabot in https://github.com/google/trillian/pull/3571 * Bump golang from `6c27802` to `af9b40f` in /examples/deployment/docker/log_signer in the docker-deps group by @dependabot in https://github.com/google/trillian/pull/3573 * Bump the go-deps group with 3 updates by @dependabot in https://github.com/google/trillian/pull/3568 * Bump the github-actions-deps group with 2 updates by @dependabot in https://github.com/google/trillian/pull/3572 * Bump @google-cloud/functions-framework from 3.4.1 to 3.4.2 in /scripts/gcb2slack by @dependabot in https://github.com/google/trillian/pull/3574 * Bump golang from `6c27802` to `af9b40f` in /examples/deployment/docker/db_client in the docker-deps group by @dependabot in https://github.com/google/trillian/pull/3570 * Bump google-auth-library from 9.11.0 to 9.12.0 in /scripts/gcb2slack by @dependabot in https://github.com/google/trillian/pull/3575 * Upgrade to go-licenses v2 by @mhutchinson in https://github.com/google/trillian/pull/3578 * Bump github.com/docker/docker from 25.0.5+incompatible to 25.0.6+incompatible in the go_modules group by @dependabot in https://github.com/google/trillian/pull/3579 * Bump the github-actions-deps group with 2 updates by @dependabot in https://github.com/google/trillian/pull/3582 * Bump google-auth-library from 9.12.0 to 9.13.0 in /scripts/gcb2slack by @dependabot in https://github.com/google/trillian/pull/3583 * Bump the go-deps group with 6 updates by @dependabot in https://github.com/google/trillian/pull/3580 * Bump golang from 1.22.5-bookworm to 1.22.6-bookworm in /integration/cloudbuild/testbase in the docker-deps group by @dependabot in https://github.com/google/trillian/pull/3586 * Bump the go-deps group with 6 updates by @dependabot in https://github.com/google/trillian/pull/3590 * Bump golang from 1.22.5-bookworm to 1.22.6-bookworm in /examples/deployment/docker/db_client in the docker-deps group by @dependabot in https://github.com/google/trillian/pull/3588 * Bump the github-actions-deps group with 2 updates by @dependabot in https://github.com/google/trillian/pull/3587 * Bump golang from 1.22.5-bookworm to 1.22.6-bookworm in /examples/deployment/docker/log_signer in the docker-deps group by @dependabot in https://github.com/google/trillian/pull/3589 * Bump golang from 1.22.5-bookworm to 1.22.6-bookworm in /examples/deployment/docker/log_server in the docker-deps group by @dependabot in https://github.com/google/trillian/pull/3591 * Bump ubuntu from `340d9b0` to `adbb901` in /examples/deployment/kubernetes/mysql/image in the docker-deps group by @dependabot in https://github.com/google/trillian/pull/3595 * Bump golang from 1.22.6-bookworm to 1.23.0-bookworm in /integration/cloudbuild/testbase in the docker-deps group by @dependabot in https://github.com/google/trillian/pull/3601 * Bump golang from 1.22.6-bookworm to 1.23.0-bookworm in /examples/deployment/docker/db_client in the docker-deps group by @dependabot in https://github.com/google/trillian/pull/3594 * Bump @slack/webhook from 7.0.2 to 7.0.3 in /scripts/gcb2slack by @dependabot in https://github.com/google/trillian/pull/3596 * Bump github/codeql-action from 3.26.0 to 3.26.3 in the github-actions-deps group by @dependabot in https://github.com/google/trillian/pull/3599 * Bump golang from 1.22.6-bookworm to 1.23.0-bookworm in /examples/deployment/docker/log_server in the docker-deps group by @dependabot in https://github.com/google/trillian/pull/3598 * Bump golang from 1.22.6-bookworm to 1.23.0-bookworm in /examples/deployment/docker/log_signer in the docker-deps group by @dependabot in https://github.com/google/trillian/pull/3600 * Bump the go-deps group with 4 updates by @dependabot in https://github.com/google/trillian/pull/3597 * Bump go version to 1.22.6 by @roger2hk in https://github.com/google/trillian/pull/3602 * Bump the go-deps group with 6 updates by @dependabot in https://github.com/google/trillian/pull/3606 * Bump google-auth-library from 9.13.0 to 9.14.0 in /scripts/gcb2slack by @dependabot in https://github.com/google/trillian/pull/3604 * Bump github/codeql-action from 3.26.3 to 3.26.5 in the github-actions-deps group by @dependabot in https://github.com/google/trillian/pull/3605 ## v1.6.0 (Jan 2024) ### MySQL: Changes to Subtree Revisions Support for skipping subtree revisions to increase read performance and reduce disk usage: added in #3201 TL;DR: existing trees will continue to be stored and queried as they were before, but new trees created with the MySQL storage layer will be stored and queried in a way that uses less space and allows for simpler and faster queries. No schema changes are required by log operators. The Trillian MySQL implementation stores the internal state of the log as Subtrees in the database. These are essentially tiles as described by [tlog: Tiling a log](https://research.swtch.com/tlog). Trees created with previous versions of Trillian stored a different revision of each Subtree when the tree was updated. This is somewhat redundant for append-only logs because an earlier version of a Subtree can always be derived from a later one by simply removing entries from the right of the Subtree. PR #3201 removes this Subtree revision history, and updates Subtrees in place when they are updated. Measurements from @n-canter show that revisionless storage saves around 75% storage costs for the Subtree table, and queries over this table are more than 15% faster. The same schema is used for both revisioned and unrevisioned subtrees. The difference is that we always write a revision of 0 in the unrevisioned case, which still means that there will only be a single entry per subtree. Support is maintained for the old way of revisioning Subtrees in order to avoid breaking changes to existing trees. There is no simple code change that would safely allow a previously revisioned tree to start becoming a revisionless tree. This new revisionless Subtree feature is only available for trees created with new versions of Trillian. Users with legacy revisioned trees that wish to take advantage of smaller storage costs and faster queries of the new revisionless storage should come speak to us on [transparency-dev Slack](https://join.slack.com/t/transparency-dev/shared_invite/zt-27pkqo21d-okUFhur7YZ0rFoJVIOPznQ). The safest option we have available is to use [migrillian](https://github.com/google/certificate-transparency-go/tree/master/trillian/migrillian) to create a new copy of trees, but this will be quite a manual process and will only work for CT logs. Other migration options are conceivable and we're eager to work with the community to develop and test tools for upgrading trees in place. ## Notable Changes * CI now runs with MySQL 8.2 instead of MySQL 5.7 * Bump golangci-lint from 1.51.1 to 1.55.1 (developers should update to this version) ## All Changes (ignoring dependabot) * Strip unused docker image manipulation from cloudbuild by @mhutchinson in https://github.com/google/trillian/pull/3278 * Switch from using unmaintained Google Cloud mysql db image to dockerhub official image by @patflynn in https://github.com/google/trillian/pull/3272 * Disable the OS package patches to bypass the mysql8 gpg key rotation issue by @roger2hk in https://github.com/google/trillian/pull/3270 * Disable race condition checking for beam code by @mhutchinson in https://github.com/google/trillian/pull/3249 * Make uninitializedBegin test accurately test its intention by @mhutchinson in https://github.com/google/trillian/pull/3244 * Fix deadlock in log_client by @n-canter in https://github.com/google/trillian/pull/3236 * Bump go-version-input from 1.20.11 to 1.20.12 in govulncheck.yml by @roger2hk in https://github.com/google/trillian/pull/3237 * Inlined storage/sql.go into both implementations that use it by @mhutchinson in https://github.com/google/trillian/pull/3235 * Support for skipping subtree revisions to increase read performance and reduce disk usage by @mhutchinson in https://github.com/google/trillian/pull/3201 * Updated Slack channel details by @mhutchinson in https://github.com/google/trillian/pull/3214 * Skip SELECTing revision that isn't used by @mhutchinson in https://github.com/google/trillian/pull/3207 * Increase some timeouts in integration tests by @mhutchinson in https://github.com/google/trillian/pull/3203 * Do vuln scanning with a version of Go not subject to GO-2023-2185 by @mhutchinson in https://github.com/google/trillian/pull/3202 * Bump MariaDB image from 10.3 to 11.1 in Cloud Build by @roger2hk in https://github.com/google/trillian/pull/3189 * Move golangci-lint from Cloud Build to GitHub Action by @roger2hk in https://github.com/google/trillian/pull/3188 * Updated all MySQL deps to 8.0 #3182 by @mhutchinson in https://github.com/google/trillian/pull/3183 * Bump golangci-lint from 1.51.1 to 1.55.1 by @roger2hk in https://github.com/google/trillian/pull/3177 The above was generated with the following command: ```bash gh pr list -s closed -S "NOT dependabot" --json url,title,author -t '{{range .}}* {{.title}} by @{{.author.login}} in {{.url}} {{end}}' ``` ## v1.5.3 * Recommended go version for development: 1.20 * This is the version used by the cloudbuild presubmits. Using a different version can lead to presubmits failing due to unexpected diffs. ### Storage #### MySQL * mysql: check for error when getting subtrees by @jsha in https://github.com/google/trillian/pull/3173 ### Documentation * Added comments to show how snippets were generated by @mhutchinson in https://github.com/google/trillian/pull/3048 ### Misc * Export logserver read counter metric together with logIDs by @phbnf in https://github.com/google/trillian/pull/3077 * Register DoFns by @AlCutter in https://github.com/google/trillian/pull/3083 * Add docker package-ecosystem to Dependabot config by @roger2hk in https://github.com/google/trillian/pull/3038 * Fix CVE vulnerabilities in mysql base Docker image by @roger2hk in https://github.com/google/trillian/pull/3037 * Fix db_server Docker image vulnerabilities by @roger2hk in https://github.com/google/trillian/pull/3049 * Add missing docker and npm Dependabot configs by @roger2hk in https://github.com/google/trillian/pull/3062 * Add govulncheck GitHub action by @roger2hk in https://github.com/google/trillian/pull/3089 * Pin Dockerfile base images by hash by @roger2hk in https://github.com/google/trillian/pull/3090 * Pin golang/govulncheck-action by hash by @roger2hk in https://github.com/google/trillian/pull/3091 * Pin Dockerfile base images by hash by @roger2hk in https://github.com/google/trillian/pull/3093 * Add top level read-only permission in govulncheck.yml by @roger2hk in https://github.com/google/trillian/pull/3092 ### Dependency updates * Bump go.etcd.io/etcd/etcdctl/v3 from 3.5.8 to 3.5.9 by @dependabot in https://github.com/google/trillian/pull/3003 * Bump google.golang.org/api from 0.121.0 to 0.122.0 by @dependabot in https://github.com/google/trillian/pull/3006 * Bump golang.org/x/tools from 0.8.0 to 0.9.1 by @dependabot in https://github.com/google/trillian/pull/3005 * Bump github.com/apache/beam/sdks/v2 from 2.47.0-RC3 to 2.47.0 by @dependabot in https://github.com/google/trillian/pull/3000 * Bump golang.org/x/crypto from 0.8.0 to 0.9.0 by @dependabot in https://github.com/google/trillian/pull/3007 * Bump go.etcd.io/etcd/v3 from 3.5.8 to 3.5.9 by @dependabot in https://github.com/google/trillian/pull/3004 * Bump actions/setup-go from 4.0.0 to 4.0.1 by @dependabot in https://github.com/google/trillian/pull/3008 * Bump google.golang.org/api from 0.122.0 to 0.123.0 by @dependabot in https://github.com/google/trillian/pull/3010 * Bump github/codeql-action from 2.3.3 to 2.3.5 by @dependabot in https://github.com/google/trillian/pull/3013 * Bump github/codeql-action from 2.3.5 to 2.3.6 by @dependabot in https://github.com/google/trillian/pull/3020 * Bump golang.org/x/tools from 0.9.1 to 0.9.3 by @dependabot in https://github.com/google/trillian/pull/3016 * Bump github.com/cockroachdb/cockroach-go/v2 from 2.3.3 to 2.3.4 by @dependabot in https://github.com/google/trillian/pull/3017 * Bump golangci/golangci-lint-action from 3.4.0 to 3.5.0 by @dependabot in https://github.com/google/trillian/pull/3021 * Bump golang.org/x/sys from 0.8.0 to 0.9.0 by @dependabot in https://github.com/google/trillian/pull/3025 * Bump golangci/golangci-lint-action from 3.5.0 to 3.6.0 by @dependabot in https://github.com/google/trillian/pull/3027 * Bump github/codeql-action from 2.3.6 to 2.13.4 by @dependabot in https://github.com/google/trillian/pull/3026 * Bump actions/checkout from 3.5.2 to 3.5.3 by @dependabot in https://github.com/google/trillian/pull/3028 * Bump golang.org/x/tools from 0.9.3 to 0.10.0 by @dependabot in https://github.com/google/trillian/pull/3029 * Bump github.com/cockroachdb/cockroach-go/v2 from 2.3.4 to 2.3.5 by @dependabot in https://github.com/google/trillian/pull/3035 * Bump github.com/prometheus/client_golang from 1.15.1 to 1.16.0 by @dependabot in https://github.com/google/trillian/pull/3030 * Update mysql Dockerfile base image from ubuntu:trusty to ubuntu:jammy by @roger2hk in https://github.com/google/trillian/pull/3036 * Bump golang.org/x/tools from 0.10.0 to 0.11.0 by @dependabot in https://github.com/google/trillian/pull/3044 * Bump ossf/scorecard-action from 2.1.3 to 2.2.0 by @dependabot in https://github.com/google/trillian/pull/3039 * Bump google.golang.org/protobuf from 1.30.0 to 1.31.0 by @dependabot in https://github.com/google/trillian/pull/3041 * Bump golang.org/x/tools from 0.11.0 to 0.12.0 by @dependabot in https://github.com/google/trillian/pull/3055 * Bump actions/setup-go from 4.0.1 to 4.1.0 by @dependabot in https://github.com/google/trillian/pull/3059 * Bump google-auth-library from 8.7.0 to 9.0.0 in /scripts/gcb2slack by @dependabot in https://github.com/google/trillian/pull/3069 * Bump golang from 1.19-buster to 1.20-buster in /examples/deployment/docker/db_client by @dependabot in https://github.com/google/trillian/pull/3064 * Bump alpine from 3.8 to 3.18 in /examples/deployment/docker/envsubst by @dependabot in https://github.com/google/trillian/pull/3067 * Bump golang from 1.19-buster to 1.20-buster in /integration/cloudbuild/testbase by @dependabot in https://github.com/google/trillian/pull/3065 * Bump golangci/golangci-lint-action from 3.6.0 to 3.7.0 by @dependabot in https://github.com/google/trillian/pull/3063 * Bump golang from 1.19-buster to 1.20-buster in /examples/deployment/docker/log_server by @dependabot in https://github.com/google/trillian/pull/3066 * Bump golang from 1.19-buster to 1.20-buster in /examples/deployment/docker/log_signer by @dependabot in https://github.com/google/trillian/pull/3071 * Bump actions/checkout from 3.5.3 to 3.6.0 by @dependabot in https://github.com/google/trillian/pull/3076 * Bump go from 1.19 to 1.20 by @mhutchinson in https://github.com/google/trillian/pull/3080 * Bump golang.org/x/sys from 0.11.0 to 0.12.0 by @dependabot in https://github.com/google/trillian/pull/3081 * Bump actions/checkout from 3.6.0 to 4.0.0 by @dependabot in https://github.com/google/trillian/pull/3082 * Bump golang.org/x/crypto from 0.12.0 to 0.13.0 by @dependabot in https://github.com/google/trillian/pull/3084 * Bump golang.org/x/tools from 0.12.0 to 0.13.0 by @dependabot in https://github.com/google/trillian/pull/3086 * Bump actions/upload-artifact from 3.1.2 to 3.1.3 by @dependabot in https://github.com/google/trillian/pull/3085 * Bump Go version in Docker base images to 1.20.8-bookworm by @roger2hk in https://github.com/google/trillian/pull/3094 * Bump golang from 1.20.8-bookworm to 1.21.1-bookworm in /examples/deployment/docker/db_client by @dependabot in https://github.com/google/trillian/pull/3100 * Bump gcr.io/kaniko-project/executor from 1.6.0 to 1.15.0 by @roger2hk in https://github.com/google/trillian/pull/3095 * Bump golang from 1.20.8-bookworm to 1.21.1-bookworm in /integration/cloudbuild/testbase by @dependabot in https://github.com/google/trillian/pull/3098 * Bump golang from 1.20.8-bookworm to 1.21.1-bookworm in /examples/deployment/docker/log_signer by @dependabot in https://github.com/google/trillian/pull/3097 * Bump golang from 1.20.8-bookworm to 1.21.1-bookworm in /examples/deployment/docker/log_server by @dependabot in https://github.com/google/trillian/pull/3099 * Bump golang from `d3114db` to `a0b3bc4` in /integration/cloudbuild/testbase by @dependabot in https://github.com/google/trillian/pull/3104 * Bump golang from `d3114db` to `a0b3bc4` in /examples/deployment/docker/log_server by @dependabot in https://github.com/google/trillian/pull/3105 * Bump golang from `d3114db` to `a0b3bc4` in /examples/deployment/docker/log_signer by @dependabot in https://github.com/google/trillian/pull/3106 * Bump golang from `d3114db` to `a0b3bc4` in /examples/deployment/docker/db_client by @dependabot in https://github.com/google/trillian/pull/3107 * Bump golang from `e06b3a4` to `114b9cc` in /examples/deployment/docker/log_signer by @dependabot in https://github.com/google/trillian/pull/3108 * Bump trillian-opensource-ci/mysql5 from `51cc6df` to `edf7def` in /examples/deployment/docker/db_server by @dependabot in https://github.com/google/trillian/pull/3110 * Bump golang from `a0b3bc4` to `114b9cc` in /examples/deployment/docker/log_server by @dependabot in https://github.com/google/trillian/pull/3109 * Bump golang from `a0b3bc4` to `114b9cc` in /integration/cloudbuild/testbase by @dependabot in https://github.com/google/trillian/pull/3111 * Bump actions/checkout from 4.0.0 to 4.1.0 by @dependabot in https://github.com/google/trillian/pull/3117 * Bump golang from `114b9cc` to `9c7ea4a` in /examples/deployment/docker/db_client by @dependabot in https://github.com/google/trillian/pull/3116 * Bump golang from `114b9cc` to `9c7ea4a` in /examples/deployment/docker/log_server by @dependabot in https://github.com/google/trillian/pull/3114 * Bump golang from `114b9cc` to `9c7ea4a` in /examples/deployment/docker/log_signer by @dependabot in https://github.com/google/trillian/pull/3115 * Bump nick-fields/retry from 2.8.3 to 2.9.0 by @dependabot in https://github.com/google/trillian/pull/3119 * Bump trillian-opensource-ci/mysql5 from `edf7def` to `f45c849` in /examples/deployment/docker/db_server by @dependabot in https://github.com/google/trillian/pull/3120 * Bump golang from `9c7ea4a` to `61f84bc` in /examples/deployment/docker/db_client by @dependabot in https://github.com/google/trillian/pull/3121 * Bump golang from `9c7ea4a` to `61f84bc` in /integration/cloudbuild/testbase by @dependabot in https://github.com/google/trillian/pull/3124 * Bump golang from `9c7ea4a` to `61f84bc` in /examples/deployment/docker/log_server by @dependabot in https://github.com/google/trillian/pull/3122 * Bump alpine from `7144f7b` to `eece025` in /examples/deployment/docker/envsubst by @dependabot in https://github.com/google/trillian/pull/3125 * Bump golang from `9c7ea4a` to `61f84bc` in /examples/deployment/docker/log_signer by @dependabot in https://github.com/google/trillian/pull/3123 * Bump ubuntu from `aabed32` to `9b8dec3` in /examples/deployment/kubernetes/mysql/image by @dependabot in https://github.com/google/trillian/pull/3127 * Bump distroless/base-debian12 from `d64f548` to `cc22d6d` in /examples/deployment/docker/log_signer by @dependabot in https://github.com/google/trillian/pull/3128 * Bump distroless/base-debian12 from `d64f548` to `cc22d6d` in /examples/deployment/docker/log_server by @dependabot in https://github.com/google/trillian/pull/3129 * Bump golang from 1.21.1-bookworm to 1.21.2-bookworm in /integration/cloudbuild/testbase by @dependabot in https://github.com/google/trillian/pull/3134 * Bump golang from 1.21.1-bookworm to 1.21.2-bookworm in /examples/deployment/docker/log_server by @dependabot in https://github.com/google/trillian/pull/3135 * Bump golang from 1.21.1-bookworm to 1.21.2-bookworm in /examples/deployment/docker/log_signer by @dependabot in https://github.com/google/trillian/pull/3136 * Bump golang from `0bd76fd` to `a44d05d` in /integration/cloudbuild/testbase by @dependabot in https://github.com/google/trillian/pull/3137 * Bump ossf/scorecard-action from 2.2.0 to 2.3.0 by @dependabot in https://github.com/google/trillian/pull/3139 * Bump golang from 1.21.1-bookworm to 1.21.2-bookworm in /examples/deployment/docker/db_client by @dependabot in https://github.com/google/trillian/pull/3138 * Bump distroless/base-debian12 from `cc22d6d` to `5be49de` in /examples/deployment/docker/log_signer by @dependabot in https://github.com/google/trillian/pull/3141 * Bump distroless/base-debian12 from `cc22d6d` to `5be49de` in /examples/deployment/docker/log_server by @dependabot in https://github.com/google/trillian/pull/3142 * Bump trillian-opensource-ci/mysql5 from `f45c849` to `99d6043` in /examples/deployment/docker/db_server by @dependabot in https://github.com/google/trillian/pull/3143 * Bump golang from 1.21.2-bookworm to 1.21.3-bookworm in /integration/cloudbuild/testbase by @dependabot in https://github.com/google/trillian/pull/3147 * Bump golang from 1.21.2-bookworm to 1.21.3-bookworm in /examples/deployment/docker/log_server by @dependabot in https://github.com/google/trillian/pull/3145 * Bump golang from 1.21.2-bookworm to 1.21.3-bookworm in /examples/deployment/docker/log_signer by @dependabot in https://github.com/google/trillian/pull/3148 * Bump golang from 1.21.2-bookworm to 1.21.3-bookworm in /examples/deployment/docker/db_client by @dependabot in https://github.com/google/trillian/pull/3144 * Bump go-version-input from 1.20.8 to 1.20.10 in govulncheck by @roger2hk in https://github.com/google/trillian/pull/3151 * Bump golang.org/x/net from 0.15.0 to 0.17.0 by @dependabot in https://github.com/google/trillian/pull/3150 * Bump @slack/webhook from 5.0.4 to 7.0.0 in /scripts/gcb2slack by @dependabot in https://github.com/google/trillian/pull/3130 * Bump google-auth-library from 9.0.0 to 9.1.0 in /scripts/gcb2slack by @dependabot in https://github.com/google/trillian/pull/3126 * Bump golang from `efde471` to `5cc7ddc` in /integration/cloudbuild/testbase by @dependabot in https://github.com/google/trillian/pull/3156 * Bump golang from `efde471` to `5cc7ddc` in /examples/deployment/docker/log_server by @dependabot in https://github.com/google/trillian/pull/3155 * Bump golang from `efde471` to `20f9ab5` in /examples/deployment/docker/db_client by @dependabot in https://github.com/google/trillian/pull/3152 * Bump golang from `efde471` to `20f9ab5` in /examples/deployment/docker/log_signer by @dependabot in https://github.com/google/trillian/pull/3154 * Bump golang from `5cc7ddc` to `20f9ab5` in /integration/cloudbuild/testbase by @dependabot in https://github.com/google/trillian/pull/3158 * Bump ubuntu from `9b8dec3` to `2b7412e` in /examples/deployment/kubernetes/mysql/image by @dependabot in https://github.com/google/trillian/pull/3157 * Bump actions/checkout from 4.1.0 to 4.1.1 by @dependabot in https://github.com/google/trillian/pull/3160 * Bump ossf/scorecard-action from 2.3.0 to 2.3.1 by @dependabot in https://github.com/google/trillian/pull/3164 * Bump google.golang.org/grpc to 1.59.0 fixing CVE-2023-44487 (https://github.com/advisories/GHSA-qppj-fm5r-hxr3) by @cpanato in https://github.com/google/trillian/pull/3166 * Bump distroless/base-debian12 from `5be49de` to `1dfdb5e` in /examples/deployment/docker/log_server by @dependabot in https://github.com/google/trillian/pull/3167 * Bump google-auth-library from 9.1.0 to 9.2.0 in /scripts/gcb2slack by @dependabot in https://github.com/google/trillian/pull/3168 * Bump distroless/base-debian12 from `5be49de` to `1dfdb5e` in /examples/deployment/docker/log_signer by @dependabot in https://github.com/google/trillian/pull/3169 * Bump trillian-opensource-ci/mysql5 from `99d6043` to `c079e4e` in /examples/deployment/docker/db_server by @dependabot in https://github.com/google/trillian/pull/3161 * Bump github.com/docker/docker from 24.0.6+incompatible to 24.0.7+incompatible by @dependabot in https://github.com/google/trillian/pull/3170 * Bump trillian-opensource-ci/mysql5 from `c079e4e` to `3f355be` in /examples/deployment/docker/db_server by @dependabot in https://github.com/google/trillian/pull/3171 * Bump @slack/webhook from 7.0.0 to 7.0.1 in /scripts/gcb2slack by @dependabot in https://github.com/google/trillian/pull/3172 * Bump @google-cloud/functions-framework from 1.3.2 to 3.3.0 in /scripts/gcb2slack by @dependabot in https://github.com/google/trillian/pull/3072 ## v1.5.2 * Recommended go version for development: 1.19 * This is the version used by the cloudbuild presubmits. Using a different version can lead to presubmits failing due to unexpected diffs. ### Storage #### CloudSpanner * Removed use of the `--cloudspanner_write_sessions` flag. This was related to preparing some fraction of CloudSpanner sessionpool entries with Read/Write transactions, however this functionality is no longer supported by the client library. ### Repo config * Enable all lint checks in trillian repo by @mhutchinson in https://github.com/google/trillian/pull/2979 ### Dependency updates * Bump contrib.go.opencensus.io/exporter/stackdriver from 0.13.12 to 0.13.14 by @samuelattwood in https://github.com/google/trillian/pull/2950 * Bump Go version from 1.17 to 1.19. * Updated golangci-lint to v1.51.1 (developers should update to this version) * Update transparency-dev/merkle to v0.0.2 ## v1.5.1 ### Storage * A new storage driver for CockroachDB has been added. It's currently in alpha stage with support provided by Equinix Metal. ### Misc * Fix log server not exiting properly on SIGINT ### Dependency updates * Switch from glog to klog by @jdolitsky in https://github.com/google/trillian/pull/2787 * Bump google.golang.org/api from 0.92.0 to 0.93.0 by @dependabot in https://github.com/google/trillian/pull/2800 * Bump cloud.google.com/go/spanner from 1.36.0 to 1.37.0 by @dependabot in https://github.com/google/trillian/pull/2803 * Bump google.golang.org/grpc from 1.48.0 to 1.49.0 by @dependabot in https://github.com/google/trillian/pull/2804 * Bump google.golang.org/api from 0.93.0 to 0.94.0 by @dependabot in https://github.com/google/trillian/pull/2802 * Bump cloud.google.com/go/spanner from 1.37.0 to 1.38.0 by @dependabot in https://github.com/google/trillian/pull/2806 * Bump k8s.io/klog/v2 from 2.70.1 to 2.80.0 by @dependabot in https://github.com/google/trillian/pull/2807 * Bump k8s.io/klog/v2 from 2.80.0 to 2.80.1 by @dependabot in https://github.com/google/trillian/pull/2808 * Bump github.com/google/go-cmp from 0.5.8 to 0.5.9 by @dependabot in https://github.com/google/trillian/pull/2809 * Bump google.golang.org/api from 0.94.0 to 0.95.0 by @dependabot in https://github.com/google/trillian/pull/2810 * Bump go.etcd.io/etcd/etcdctl/v3 from 3.5.4 to 3.5.5 by @dependabot in https://github.com/google/trillian/pull/2812 * Bump go.etcd.io/etcd/v3 from 3.5.4 to 3.5.5 by @dependabot in https://github.com/google/trillian/pull/2816 * Bump google.golang.org/api from 0.95.0 to 0.96.0 by @dependabot in https://github.com/google/trillian/pull/2813 * Bump google.golang.org/api from 0.96.0 to 0.97.0 by @dependabot in https://github.com/google/trillian/pull/2819 * Bump cloud.google.com/go/spanner from 1.38.0 to 1.39.0 by @dependabot in https://github.com/google/trillian/pull/2818 * Bump google.golang.org/api from 0.97.0 to 0.98.0 by @dependabot in https://github.com/google/trillian/pull/2820 * Bump google.golang.org/grpc from 1.49.0 to 1.50.0 by @dependabot in https://github.com/google/trillian/pull/2821 * Bump google.golang.org/grpc from 1.50.0 to 1.50.1 by @dependabot in https://github.com/google/trillian/pull/2823 * Bump google.golang.org/api from 0.98.0 to 0.99.0 by @dependabot in https://github.com/google/trillian/pull/2822 * Bump google.golang.org/api from 0.99.0 to 0.100.0 by @dependabot in https://github.com/google/trillian/pull/2824 * Bump github.com/prometheus/client_model from 0.2.0 to 0.3.0 by @dependabot in https://github.com/google/trillian/pull/2825 * Bump golang.org/x/tools from 0.1.12 to 0.2.0 by @dependabot in https://github.com/google/trillian/pull/2826 * Bump google.golang.org/api from 0.100.0 to 0.101.0 by @dependabot in https://github.com/google/trillian/pull/2827 * Bump github.com/prometheus/client_golang from 1.13.0 to 1.13.1 by @dependabot in https://github.com/google/trillian/pull/2828 * Bump golang.org/x/sys from 0.1.0 to 0.2.0 by @dependabot in https://github.com/google/trillian/pull/2829 * Bump google.golang.org/api from 0.101.0 to 0.102.0 by @dependabot in https://github.com/google/trillian/pull/2830 * Bump go.opencensus.io from 0.23.0 to 0.24.0 by @dependabot in https://github.com/google/trillian/pull/2832 * Bump cloud.google.com/go/spanner from 1.39.0 to 1.40.0 by @dependabot in https://github.com/google/trillian/pull/2831 * Bump github.com/prometheus/client_golang from 1.13.1 to 1.14.0 by @dependabot in https://github.com/google/trillian/pull/2838 * Bump google.golang.org/api from 0.102.0 to 0.103.0 by @dependabot in https://github.com/google/trillian/pull/2839 * Bump golang.org/x/crypto from 0.1.0 to 0.2.0 by @dependabot in https://github.com/google/trillian/pull/2841 * Bump golang.org/x/tools from 0.2.0 to 0.3.0 by @dependabot in https://github.com/google/trillian/pull/2840 * Dependabot: Also keep GitHub actions up-to-date by @JAORMX in https://github.com/google/trillian/pull/2842 * Bump actions/upload-artifact from 3.1.0 to 3.1.1 by @dependabot in https://github.com/google/trillian/pull/2843 * Bump golang.org/x/crypto from 0.2.0 to 0.3.0 by @dependabot in https://github.com/google/trillian/pull/2847 * Bump google.golang.org/grpc from 1.50.1 to 1.51.0 by @dependabot in https://github.com/google/trillian/pull/2845 * Bump github.com/cockroachdb/cockroach-go/v2 from 2.2.16 to 2.2.18 by @dependabot in https://github.com/google/trillian/pull/2846 * Bump go.etcd.io/etcd/v3 from 3.5.5 to 3.5.6 by @dependabot in https://github.com/google/trillian/pull/2849 * Bump github.com/cockroachdb/cockroach-go/v2 from 2.2.18 to 2.2.19 by @dependabot in https://github.com/google/trillian/pull/2856 * Bump golang.org/x/sys from 0.2.0 to 0.3.0 by @dependabot in https://github.com/google/trillian/pull/2858 * Bump cloud.google.com/go/spanner from 1.40.0 to 1.41.0 by @dependabot in https://github.com/google/trillian/pull/2857 * Bump actions/setup-go from 3.3.1 to 3.4.0 by @dependabot in https://github.com/google/trillian/pull/2862 * Bump github/codeql-action from 2.1.34 to 2.1.35 by @dependabot in https://github.com/google/trillian/pull/2861 * Bump golangci/golangci-lint-action from 3.3.0 to 3.3.1 by @dependabot in https://github.com/google/trillian/pull/2860 * Bump github.com/go-sql-driver/mysql from 1.6.0 to 1.7.0 by @dependabot in https://github.com/google/trillian/pull/2859 * Bump qs, body-parser and express in /scripts/gcb2slack by @dependabot in https://github.com/google/trillian/pull/2867 * Bump minimist from 1.2.0 to 1.2.7 in /scripts/gcb2slack by @dependabot in https://github.com/google/trillian/pull/2864 * Bump axios and @slack/webhook in /scripts/gcb2slack by @dependabot in https://github.com/google/trillian/pull/2868 * Bump json-bigint and google-auth-library in /scripts/gcb2slack by @dependabot in https://github.com/google/trillian/pull/2869 * Bump node-fetch from 2.6.0 to 2.6.7 in /scripts/gcb2slack by @dependabot in https://github.com/google/trillian/pull/2866 * Bump golang.org/x/tools from 0.3.0 to 0.4.0 by @dependabot in https://github.com/google/trillian/pull/2870 * Bump github/codeql-action from 2.1.35 to 2.1.36 by @dependabot in https://github.com/google/trillian/pull/2874 * Bump actions/checkout from 3.1.0 to 3.2.0 by @dependabot in https://github.com/google/trillian/pull/2873 * Bump golang.org/x/crypto from 0.3.0 to 0.4.0 by @dependabot in https://github.com/google/trillian/pull/2872 * Bump google.golang.org/api from 0.103.0 to 0.104.0 by @dependabot in https://github.com/google/trillian/pull/2871 * Bump cloud.google.com/go/spanner from 1.41.0 to 1.42.0 by @dependabot in https://github.com/google/trillian/pull/2877 ## v.1.5.0 ### Storage * Ephemeral nodes are no-longer written for any tree by default (and have not been read since the v1.4.0 release), the corresponding `--tree_ids_with_no_ephemeral_nodes` flag is now deprecated (and will be removed in a future release). ### Cleanup * Format code according to go1.19rc2 by @mhutchinson in https://github.com/google/trillian/pull/2785 * Delete merkle package, use [github.com/transparency-dev/merkle](https://pkg.go.dev/github.com/transparency-dev/merkle) instead. ### Misc * Fix order-dependent test by @hickford in https://github.com/google/trillian/pull/2792 ### Dependency updates * Updated golangci-lint to v1.47.3 (developers should update to this version) by @mhutchinson in https://github.com/google/trillian/pull/2791 * Bump google.golang.org/api from 0.87.0 to 0.88.0 by @dependabot in https://github.com/google/trillian/pull/2783 * Bump cloud.google.com/go/spanner from 1.35.0 to 1.36.0 by @dependabot in https://github.com/google/trillian/pull/2784 * Bump google.golang.org/api from 0.88.0 to 0.90.0 by @dependabot in https://github.com/google/trillian/pull/2789 * Bump golang.org/x/tools from 0.1.11 to 0.1.12 by @dependabot in https://github.com/google/trillian/pull/2790 * Bump google.golang.org/protobuf from 1.28.0 to 1.28.1 by @dependabot in https://github.com/google/trillian/pull/2788 * Bump google.golang.org/api from 0.90.0 to 0.91.0 by @dependabot in https://github.com/google/trillian/pull/2796 * Bump github.com/prometheus/client_golang from 1.12.2 to 1.13.0 by @dependabot in https://github.com/google/trillian/pull/2795 * Bump github.com/fullstorydev/grpcurl from 1.8.6 to 1.8.7 by @dependabot in https://github.com/google/trillian/pull/2794 * Bump google.golang.org/api from 0.91.0 to 0.92.0 by @dependabot in https://github.com/google/trillian/pull/2798 ## v1.4.2 * #2568: Allow disabling the writes of ephemeral nodes to storage via the `--tree_ids_with_no_ephemeral_nodes` flag to the sequencer. * #2748: `--cloudspanner_max_burst_sessions` deprecated (it hasn't had any effect for a while, now it's more explicit) * #2768: update go.mod to use 1.17 compatibility from 1.13. ### Dependency updates * Updated golangci-lint to v1.46.1 (developers should update to this version) * Removed dependency on certificate-transparency-go ### Developer updates * #2765 copies the required protos from `googleapis` into `third_party` in this repository. This simplifies the preconditions in order to compile the proto definitions, and removes a big dependency on `$GOPATH/src` which was archaic; `$GOPATH/src/github.com/googleapis/googleapis` is no longer required. ## v1.4.1 * `countFromInformationSchema` function to add support for MySQL 8. ### Removals * #2710: Unused `storage/tools/dumplib` was removed. The useful storage format regression test moved to `integration/format`. * #2711: Unused `storage/tools/hasher` removed. * #2715: Packages under `merkle` are deprecated and to be removed. Use https://github.com/transparency-dev/merkle instead. ### Misc improvements * #2712: Fix MySQL world-writable config warning. * #2726: Check the tile height invariant stricter. No changes required. ### Dependency updates * #2731: Update `protoc` from `v3.12.4` to `v3.20.1` ## v1.4.0 * Recommended go version for development: 1.17 * This is the version used by the cloudbuild presubmits. Using a different version can lead to presubmits failing due to unexpected diffs. * GCP terraform script updated. GKE 1.19 and updated CPU type to E2 ### Dependency updates Many dep updates, including: * Upgraded to etcd v3 in order to allow grpc to be upgraded (#2195) * etcd was `v0.5.0-alpha.5`, now `v3.5.0` * grpc upgraded from `v1.29.1` to `v1.40.0` * certificate-transparency-go from `v1.0.21` to `v1.1.2-0.20210512142713-bed466244fa6` * protobuf upgraded from `v1` to `v2` * MySQL driver from `1.5.0` to `1.6.0` ### Cleanup * **Removed signatures from LogRoot and EntryTimestamps returned by RPCs** (reflecting that there should not be a trust boundary between Trillian and the personality.) * Removed the deprecated crypto.NewSHA256Signer function. * Finish removing the `LogMetadata.GetUnsequencedCounts()` method. * Removed the following APIs: - `TrillianLog.GetLeavesByHash` - `TrillianLog.GetLeavesByIndex` - `TrillianLog.QueueLeaves` * Removed the incomplete Postgres storage backend (#1298). * Deprecated `LogRootV1.Revision` field. * Moved `rfc6962` hasher one directory up to eliminate empty leftover package. * Removed unused `log_client` tool. * Various tidyups and improvements to merke & proof generation code. * Remove some remnants of experimental map. ### Storage refactoring * `NodeReader.GetMerkleNodes` does not accept revisions anymore. The implementations must use the transaction's `ReadRevision` instead. * `TreeStorage` migrated to using `compact.NodeID` type suitable for logs. * Removed the tree storage `ReadRevision` and `WriteRevision` methods. Revisions are now an implementation detail of the current storages. The change allows log implementations which don't need revisions. * Removed `Rollback` methods from storage interfaces, as `Close` is enough to cover the use-case. * Removed the unused `IsOpen` and `IsClosed` methods from transaction interfaces. * Removed the `ReadOnlyLogTX` interface, and put its only used `GetActiveLogIDs` method to `LogStorage`. * Inlined the `LogMetadata` interface to `ReadOnlyLogStorage`. * Inlined the `TreeStorage` interfaces to `LogStorage`. * Removed the need for the storage layer to return ephemeral node hashes. The application layer always requests for complete subtree nodes comprising the compact ranges corresponding to the requests. * Removed the single-tile callback from `SubtreeCache`, it uses only `GetSubtreesFunc` now. * Removed `SetSubtreesFunc` callback from `SubtreeCache`. The tiles should be written by the caller now, i.e. the caller must invoke the callback. ## v1.3.13 [Published 2021-02-16](https://github.com/google/trillian/releases/tag/v1.3.13) ### Cleanup * Removed the experimental map API. ## v1.3.12 [Published 2021-02-16](https://github.com/google/trillian/releases/tag/v1.3.12) ### Misc improvements * Removed unused `PeekTokens` method from the `quota.Manager` interface. * Ensure goroutines never block in the subtree cache (#2272). * Breaking unnecessary dependencies for Trillian clients: * Moved verifiers from `merkle` into `merkle/{log,map}verifier`sub-pacakges, reducing the amount of extra baggage inadvertently pulled in by clients. * Concrete hashers have been moved into subpackages, separating them from their registration code, allowing clients to directly pull just the hasher they're interested in and avoid the Trillian/hasher registry+protobuf deps. * Moved some packages intended for internal-only use into `internal` packages: * InMemoryMerkleTree (indended to only be used by Trillian tests) * Removed wrapper for etcd client (#2288). * Moved `--quota_system` and `--storage_system` flags to `main.go` so that they are initialised properly. It might break depending builds relying on these flags. Suggested fix: add the flags to `main.go`. * Made signer tolerate mastership election failures [#1150]. * `testdb` no longer accepts the `--test_mysql_uri` flag, and instead honours the `TEST_MYSQL_URI` ENV var. This makes it easier to blanket configure tests to use a specific test DB instance. * Removed experimental Skylog folder (#2297). * Fixed a race condition in the operation manager that should only affect tests (#2302). * Run gofumpt formatter on the whole repository (#2315). * Refactor signer operation loop (#2294). ### Upgrades * Dockerfiles are now based on Go 1.13 image. * The etcd is now pinned to v3.4.12. * The golangci-lint suite is now at v1.36.0. * CI/CD has migrated from Travis to Google Cloud Build. * prometheus from 1.7.1 to 1.9.0 (#2239, #2270). * go-cmp from 0.5.2 to 0.5.4 (#2262). * apache/beam from 2.26.0+incompatible to 2.27.0+incompatible (#2273). * lib/pq from 1.8.0 to 1.9.0 (#2264). * go-redis from 6.15.8+incompatible to 6.15.9+incompatible (#2215). ### Process * Recognise that we do not follow strict semantic versioning practices. ## v1.3.11 [Published 2020-10-06](https://github.com/google/trillian/releases/tag/v1.3.11) ### Documentation Added docs which describe the Claimant Model of transparency, a useful framework for reasoning about the design and architecture of transparent systems. ### Misc improvements * Fixed int to string conversion warnings for golang 1.15 * Metric improvements for fetched leaf counts * Move tools.go into its own directory to help with dependencies ### Dependency updates * go-grpc-middleware from 1.2.0 to 1.2.2 (#2219, #2229) * stackdriver from 0.13.2 to 0.13.4 (#2220, #2223) * Google api from 0.28.0 to 0.29.0 (#2193) ## v1.3.10 [Published 2020-07-02](https://github.com/google/trillian/releases/tag/v1.3.10) ### Storage The StorageProvider type and helpers have been moved from the server package to storage. Aliases for the old types/functions are created for backward compatibility, but the new code should not use them as we will remove them with the next major version bump. The individual storage providers have been moved to the corresponding packages, and are now required to be imported explicitly by the main file in order to be registered. We are including only MySQL and cloudspanner providers by default, since these are the ones that we support. The cloudspanner storage is supported for logs only, while the Map storage API is being polished and decoupled from the log storage API. We may return the support when the new API is tested. Support for storage of Ed25519 signatures has been added to the mysql and postgres storage drivers (only applicable in new installations) and bugs preventing correct usage of that algorithm have been fixed. #### Storage TX Interfaces - `QueueLeaves` has been removed from the `LogTreeTX` interface because `QueueLeaves` is not transactional. All callers use the `QueueLeaves` function in the `LogStorage` interface. - `AddSequencedLeaves` has been removed from the `LogTreeTX`. ### Log Changes #### Monitoring & Metrics The `queued_leaves` metric is removed, and replaced by `added_leaves` which covers both `QueueLeaves` and `AddSequencedLeaves`, and is labeled by log ID. #### MySQL Dequeueing Change #2159 mysql will now remove leaves from the queue inside of `UpdateLeaves` rather than directly inside of `Dequeue`. This change brings the behavior of the mysql storage implementation into line with the spanner implementation and makes consistent testing possible. ### Map Changes **The verifiable map is still experimental.** APIs, such as SetLeaves, have been deprecated and will be deleted in the near future. The semantics of WriteLeaves have become stricter: now it always requires the caller to specify the write revision. These changes will not affect the Trillian module semantic version due to the experimental status of the Map. Map API has been extended with Layout, GetTiles and SetTiles calls which allow for more direct processing of sparse Merkle tree tiles in the application layer. Map storage implementations are simpler, and no longer use the SubtreeCache. The map client has been updated so that GetAndVerifyMapLeaves and GetAndVerifyMapLeavesByRevision return the MapRoot for the revision at which the leaves were fetched. Without this callers of GetAndVerifyMapLeaves in particular were unable to reason about which map revision they were seeing. The SetAndVerifyMapLeaves method was deleted. ## v1.3.9 [Published 2020-06-22](https://github.com/google/trillian/releases/tag/v1.3.9) ### Selected Dependency Updates * etcd from v3.3.18 to 3.4.7 (#2090) * etcd-operator from v0.9.1 to v0.9.4 * upgraded protoc version to latest (#2088) * github.com/golang/protobuf to v1.4.1 (#2111) * google.golang.org/grpc from v1.26 to 1.29.1 (#2108) ## v1.3.8 [Published 2020-05-12](https://github.com/google/trillian/releases/tag/v1.3.8) ### HTTP APIs The HTTP/JSON APIs have been removed in favor of a pure gRPC intereface. [grpcurl](https://github.com/fullstorydev/grpcurl) is the recommended way of interacting with the gRPC API from the commandline. ## v1.3.7 [Published 2020-05-12](https://github.com/google/trillian/releases/tag/v1.3.7) ### Server Binaries The `trillian_log_server`, `trillian_log_signer` and `trillian_map_server` binaries have moved from `github.com/google/trillian/server/` to `github.com/google/trillian/cmd`. A subset of the `server` package has also moved and has been split into `cmd/internal/serverutil`, `quota/etcd` and `quota/mysqlqm` packages. ## v1.3.6 [Published 2020-05-12](https://github.com/google/trillian/releases/tag/v1.3.6) ### Deployments The Kubernetes configs will now provision 5 nodes for Trillian's Etcd cluster, instead of 3 nodes. [This makes the Etcd cluster more resilient](https://etcd.io/docs/v3.2.17/faq/#what-is-failure-tolerance) to nodes becoming temporarily unavailable, such as during updates (it can now tolerate 2 nodes being unavailable, instead of just 1). ### Monitoring & Metrics A count of the total number of individual leaves the logserver attempts to fetch via the GetEntries.* API methods has been added. ## v1.3.5 [Published 2020-05-12](https://github.com/google/trillian/releases/tag/v1.3.5) ### Log Changes #### Potential sequencer hang fixed A potential deadlock condition in the log sequencer when the process is attempting to exit has been addressed. ### Quota #### New Features An experimental Redis-based `quota.Manager` implementation has been added. #### Behaviour Changes Quota used to be refunded for all failed requests. For uses of quota that were to protect against abuse or fair utilization, this could allow infinite QPS in situations that really should have the requests throttled. Refunds are now only performed for tokens in `Global` buckets, which prevents tokens being leaked if duplicate leaves are queued. ### Tools The `licenses` tool has been moved from "scripts/licenses" to [a dedicated repository](https://github.com/google/go-licenses). ### Bazel Changes Python support is disabled unless we hear that the community cares about this being re-enabled. This was broken by a downstream change and without a signal from the Trillian community to say this is needed, the pragmatic action is to not spend time investigating this issue. ## v1.3.4 - Invalid release, do not use. [Published 2020-05-12](https://github.com/google/trillian/releases/tag/v1.3.4) ## v1.3.3 - Module fixes Published 2019-10-31 17:30:00 +0000 UTC Patch release to address Go Module issue. Removes `replace` directives in our go.mod file now that our dependencies have fixed their invalid pseudo-version issues. ## v1.3.2 - Module fixes Published 2019-09-05 17:30:00 +0000 UTC Patch release to address Go Module issue. Some dependencies use invalid pseudo- versions in their go.mod files that Go 1.13 rejects. We've added `replace` directives to our go.mod file to fix these invalid pseudo-versions. ## v1.3.1 - Module and Bazel fixes Published 2019-08-16 15:00:00 +0000 UTC Patch release primarily to address Go Module issue. v1.3.0 declared a dependency on github.com/russross/blackfriday/v2 v2.0.1+incompatible which made downstream dependencies suffer. ## v1.3.0 Published 2019-07-17 15:00:00 +0000 UTC ### Storage APIs GetSignedLogRoot / SetSignedLogRoot now take pointers This at the storage layer and does not affect the log server API. This is part of work to fix proto buffer usages where they are passed by value or compared by generic code like `reflect.DeepEquals()`. Passing them by value creates shallow copies that can share internal state. As the generated structs contain additional exported `XXX_` fields generic comparisons using all fields can produce incorrect results. ### Storage Commit takes context.Context To support passing a context down to `NodeStorage.SetLeaves`, and remove various `context.TODO()`s, the following functions have been modified to accept a `context.Context` parameter: - `storage/cache.NodeStorage.SetLeaves` - `storage/cache.SetSubtreesFunc` - `storage/cache.SubtreeCache.Flush` - `storage.ReadonlyLogTX.Commit` ### Go Module Support Go Module support has been enabled. Please use GO111MODULE=on to build Trillian. Updating dependencies no longer requires updating the vendor directory. ### TrillianMapWrite API New API service for writing to the Trillian Map. This allows APIs such as GetLeavesByRevisionNoProof to be removed from the read API, and these methods to be tuned & provisioned differently for read vs write performance. ### GetLeavesByRevisionNoProof API Allow map clients to forgo fetching inclusion proofs. This dramatically speeds things up for clients that don't need verifiability. This situation occurs in some situation where a Trillian personality is interacting directly with the Trillian Map. ### GetMapLeafByRevision API New GetMapLeafByRevision API for fetching a single map leaf. This allows there to be a separate API end point for fetching a single leaf vs. the batch GetMapLeavesByRevision API which is much slower when many leaves are requested. This supports separate monitoring and alerting for different traffic patterns. ### Add Profiling Flags to Binaries The `trillian_log_server`, `trillian_log_signer` and `trillian_map_server` binaries now have CPU and heap profiling flags. Profiling is off by default. For more details see the [Go Blog](https://blog.golang.org/profiling-go-programs). ### Map performance tweaks The map mode has had some performance tweaks added: * A workaround for locking issues which affect the map when it's used in single-transaction mode. ### Introduce BatchInclusionProof function Added a batch version of the Merkle Tree InclusionProof function. Updated the map RPC for getLeaves to use the new batch function to improve efficiency. ### Google Cloud Spanner support Google Cloud Spanner is now a supported storage backend for maps. The admin API calls to list trees backed by Cloud Spanner trees are fixed. ### RPC Server Transaction Leaks Fixed There were some cases where the Log RPC server could leak storage transactions in error situations. These have now been fixed. If you have a custom storage implementation review the fixes made to the MySQL Log storage to see if they need to be applied to your code (`storage/mysql/log_storage.go`). The Map server had similar issues but these were fixed without requiring changes to storage code. ### GetLatestSignedLogRoot With Consistency Proof `GetLatestSignedLogRoot` in the LogServer will return a consistency proof if `first_tree_size` > 0. This reduces the number of RPC calls from logClient from 2 to 1 in `client.getAndVerifyLatestRoot`. ### Testing Support has been added for testing against a locally running mysql docker image, in addition to a locally running mysql instance. ### Deprecated Fields Removed From SignedLogRoot Proto *Important Note*: For use in Certificate Transparency this version of the logserver binary won't work properly with an older CTFE. Make sure to update the CTFE servers to a current version (built from a git checkout after March 20th 2019) before deploying logservers that include this change or deploy them together with this release. Failure to do this can result in 5XX errors being returned to clients when the old handler code tries to access fields in responses that no longer exist. All the fields marked as deprecated in this proto have been removed. All the same fields are available via the TLS marshalled log root in the proto. Updating affected code is straightforward. Normally, clients will want to verify that the signed root is correctly signed. This is the preferred way to interact with the root data. There is a utility function provided that will verify the signature and unpack the TLS data. It works well in conjunction with a `LogVerifier`. The public key of the server is required. ```go verifier := client.NewLogVerifier(rfc6962.DefaultHasher, pk, crypto.SHA256) root, err := crypto.VerifySignedLogRoot(verifier.PubKey, verifier.SigHash, resp.SignedLogRoot) if err != nil { // Signature verified and unmarshalled correctly. The struct may now // be used. if root.TreeSize > 0 { // Non empty tree. } } ``` ### MySQL changes #### Configurable number of connections for MySQL Two new flags have been added that limit connections to MySQL database servers: - `--mysql_max_conns` - limits the total number of database connections - `--mysql_max_idle_conns` - limits the number of idle database connections By default, there is no maximum number of database connections. However, the database server will likely impose limits on the number of connections. The default limit on idle connections is controlled by [Go's `sql` package](https://golang.org/pkg/database/sql/#DB.SetMaxIdleConns). #### Enfored no concurrent use of MySQL tx Concurrently using a single MySQL transaction can cause the driver to error out, so we now attempt to prevent this from happening. ### Removal of length limits for a tree's `display_name` and `description` Previously, these were restricted to 20 bytes and 200 bytes respectively. These limits have been removed. However, the underlying storage implementation may still impose its own limitations. ### Server validation of leaf hashes The log server now checks that leaf hashes are the correct length and returns an InvalidArgument error if they are not. Previously, GetLeavesByHash would simply not return any matching leaves for invalid hashes, and GetInclusionProofByHash would return a NotFound error. ### Map client A [MapClient](client/map_client.go) has been added to simplify interacting with the map server. ### Database Schema This version includes a change to the MySQL and Postgres database schemas to add an index on the `SequencedLeafData` table. This improves performance for inclusion proof queries. ### Deployments The Trillian Docker images now accept GOFLAGS and GO111MODULE arguments and set them as environment variables inside the Docker container. The [db\_server Docker image](examples/deployment/docker/db_server/Dockerfile) is now based on [the MySQL 5.7 image from the Google Cloud Marketplace](https://console.cloud.google.com/marketplace/details/google/mysql5), rather than the [official MySQL 5.7 image](https://hub.docker.com/_/mysql). This Dockerfile supercedes Dockerfile.db, which has been removed. There is now a [mysql.cnf file](examples/deployment/docker/db_server/mysql.cnf) alongside the Dockerfile that makes it easy to build the image with a custom configuration, e.g. to allow MySQL to use more memory. The `trillian-log-service` and `trillian-log-signer` Kubernetes services will now have load balancers configured for them that expose those services outside of the Kubernetes cluster. This makes it easier to access their APIs. When deployed on Google Cloud, these will be [Internal Load Balancers](https://cloud.google.com/kubernetes-engine/docs/how-to/internal-load-balancing). Note that this change **cannot be applied to an existing deployment**; delete the existing Kubernetes services and redeploy them, otherwise you'll see an error similar to `The Service "trillian-log-service" is invalid: spec.clusterIP: Invalid value: "": field is immutable`. A working [Docker Compose](https://docs.docker.com/compose/) configuration is now available and can be used to bring up a local Trillian deployment for testing and experimental purposes: ```shell docker-compose -f examples/deployment/docker-compose.yml up ``` Docker Compose v3.1 or higher is required. The Terraform, Kubernetes and Docker configuration files, as well as various scripts, all now use the same, consistently-named environment variables for MySQL-related data (e.g. `MYSQL_DATABASE`). The variable names are based on those for the [MySQL Docker image](https://hub.docker.com/_/mysql#environment-variables). Docker images have been upgraded from Go 1.9 to 1.11. They now use ["Distroless" base images](https://github.com/GoogleContainerTools/distroless). ### Dropped metrics Quota metrics with specs of the form `users//read` and `users//write` are no longer exported by the Trillian binaries (as they lead to excessive storage requirements for Trillian metrics). ### Resilience improvements in `log_signer` #### Add timeout to sequencing loop Added a timeout to the context in the sequencing loop, with a default of 60s. #### Fix Operation Loop Hang Resolved a bug that would hide errors and cause the `OperationLoop` to hang until process exit if any error occurred. ### Linting toolchain migration gometalinter has been replaced with golangci-lint for improved performance and Go module support. ### Compact Merkle tree data structures `CompactMerkleTree` has been removed from `github.com/google/trillian/merkle`, and a new package `github.com/google/trillian/merkle/compact` was introduced. A new powerful data structure named "compact range" has been added to that package, and is now used throughout the repository instead of the compact tree. It is a generalization of the previous structure, as it allows manipulating arbitrary sub-ranges of leaves rather than only prefixes. ### Storage API changes The internal storage API is modified so that the ReadOnlyTreeTX.ReadRevision and TreeWriter.WriteRevision entrypoints take a context.Context parameter and return an optional error. The `SubtreeCache.GetNodeHash()` method is no longer exported. The memory storage provider has been refactored to make it more consistent with the other storage providers. The `LogMetadata.GetUnsequencedCounts()` method has been removed. `NodeReader.GetMerkleNodes` now must return `Node` objects in the same order as node IDs requested. Storage implementations known to us already adhere to this requirement. ### Maphammer improvements The maphammer test tool for the experimental Trillian Map has been enhanced. ### Default values changed for some signer flags The following flags for the signer have new default values: - `--sequencer_interval`: changed from 10 seconds to 100 milliseconds - `--batch_size`: changed from 50 to 1000 These changes improve the signer's throughput and latency under typical conditions. ### Master election refactoring The `--resign_odds` flag in `logsigner` is removed, in favor of a more generic `--master_hold_jitter` flag. Operators using this flag are advised to set the jitter to `master_check_interval * resign_odds * 2` to achieve similar behavior. The `--master_check_interval` flag is removed from `logsigner`. `logsigner` switched to using a new master election interface contained in `util/election2` package. The interfaces in `util/election` are removed. ### `CONIKS_SHA256` hash strategy added Support has been added for a CONIKS sparse tree hasher with SHA256 as the hash algorithm. Set a tree's `hash_strategy` to `CONIKS_SHA256` to use it. ### Performance The performance of `SetLeaves` requests on the Map has been slightly improved. The performance of `GetConsistencyProof` requests has been improved when using MySQL. ### Logging Some warning-level logging has been removed from the sequencer in favour of returning the same information via the returned error. The caller may still choose to log this information. This allows storage implementations that retry transactions to suppress warnings when a transaction initially fails but a retry succeeds. Some incorrectly-formatted log messages have been fixed. ### Documentation [API documentation in Markdown format](docs/api.md) is now available. ### Other The `TimeSource` type (and other time utils) moved to a separate `util/clock` package, extended with a new `Timer` interface that allows mocking `time.Timer`. The `Sequencer.SignRoot()` method has been removed. ## v1.2.1 - Map race fixed. TLS client support. LogClient improvements Published 2018-08-20 10:31:00 +0000 UTC ### Servers A race condition was fixed that affected sparse Merkle trees as served by the map server. ### Utilities / Binaries The `maphammer` uses a consistent empty check, fixing spurious failures in some tests. The `createtree` etc. set of utilities now support TLS via the `-tls-cert-file` flag. This support is also available as a client module. ### Log Client `GetAndVerifyInclusionAtIndex` no longer updates the clients root on every access as this was an unexpected side effect. Clients now have explicit control of when the root is updated by calling `UpdateRoot`. A root parameter is now required when log clients are constructed. The client will now only retry requests that fail with the following errors: - Aborted - DeadlineExceeded - ResourceExhausted - Unavailable There is one exception - it will also retry InitLog/InitMap requests that fail due to a FailedPrecondition error. ### Other The Travis build script has been updated for newer versions of MySQL (5.7 through MySQL 8) and will no longer work with 5.6. Commit [f3eaa887163bb4d2ea4b4458cb4e7c5c2f346bc6](https://api.github.com/repos/google/trillian/commits/f3eaa887163bb4d2ea4b4458cb4e7c5c2f346bc6) Download [zip](https://api.github.com/repos/google/trillian/zipball/v1.2.1) ## v1.2.0 - Signer / Quota fixes. Error mapping fix. K8 improvements Published 2018-06-25 10:42:52 +0000 UTC The Log Signer now tries to avoid creating roots older than ones that already exist. This issue has been seen occurring on a test system. Important note: If running this code in production allowing clocks to drift out of sync between nodes can cause other problems including for clustering and database replication. The Log Signer now publishes metrics for the logs that it is actively signing. In a clustered environment responsibility can be expected to move around between signer instances over time. The Log API now allows personalities to explicitly list a vector of identifiers which should be charged for `User` quota. This allows a more nuanced application of request rate limiting across multiple dimensions. Some fixes have also been made to quota handling e.g. batch requests were not reserving the appropriate quota. Consult the corresponding PRs for more details. For the log RPC server APIs `GetLeavesByIndex` and `GetLeavesByRange` MySQL storage has been modified to return status codes that match CloudSpanner. Previously some requests with out of range parameters were receiving 5xx error status rather than 4xx when errors were mapped to the HTTP space by CTFE. The Kubernetes deployment scripts continue to evolve and improve. Commit [aef10347dba1bd86a0fcb152b47989d0b51ba1fa](https://api.github.com/repos/google/trillian/commits/aef10347dba1bd86a0fcb152b47989d0b51ba1fa) Download [zip](https://api.github.com/repos/google/trillian/zipball/v1.2.0) ## v1.1.1 - CloudSpanner / Tracing / Health Checks Published 2018-05-08 12:55:34 +0000 UTC More improvements have been made to the CloudSpanner storage code. CloudSpanner storage has now been tested up to ~3.1 billion log entries. Explicit health checks have been added to the gRPC Log and Map servers (and the log signer). The HTTP endpoint must be enabled and the checks will serve on `/healthz` where a non 200 response means the server is unhealthy. The example Kubernetes deployment configuration has been updated to include them. Other improvements have been made to the Kubernetes deployment scripts and docs. The gRPC Log and Map servers have been instrumented for tracing with [OpenCensus](https://opencensus.io/). For GCP it just requires the `--tracing` flag to be added and results will be available in the GCP console under StackDriver -> Trace. Commit [3a68a845f0febdd36937c15f1d97a3a0f9509440](https://api.github.com/repos/google/trillian/commits/3a68a845f0febdd36937c15f1d97a3a0f9509440) Download [zip](https://api.github.com/repos/google/trillian/zipball/v1.1.1) ## v1.1.0 - CloudSpanner Improvements & Log Root structure changes etc. Published 2018-04-17 08:02:50 +0000 UTC Changes are in progress (e.g. see #1037) to rework the internal signed root format used by the log RPC server to be more useful / interoperable. Currently they are mostly internal API changes to the log and map servers. However, the `signature` and `log_id` fields in SignedLogRoot have been deleted and users must unpack the serialized structure to access these now. This change is not backwards compatible. Changes have been made to log server APIs and CT frontends for when a request hits a server that has an earlier version of the tree than is needed to satisfy the request. In these cases the log server used to return an error but now returns an empty proof along with the current STH it has available. This allows clients to detect these cases and handle them appropriately. The CloudSpanner schema has changed. If you have a database instance you'll need to recreate it with the new schema. Performance has been noticeably improved since the previous release and we have tested it to approx one billion log entries. Note: This code is still being developed and further changes are possible. Support for `sqlite` in unit tests has been removed because of ongoing issues with flaky tests. These were caused by concurrent accesses to the same database, which it doesn't support. The use of `sqlite` in production has never been supported and it should not be used for this. Commit [9a5dc6223bab0e1061b66b49757c2418c47b9f29](https://api.github.com/repos/google/trillian/commits/9a5dc6223bab0e1061b66b49757c2418c47b9f29) Download [zip](https://api.github.com/repos/google/trillian/zipball/v1.1.0) ## v1.0.8 - Docker Updates / Freezing Logs / CloudSpanner Options Published 2018-03-08 13:42:11 +0000 UTC The Docker image files have been updated and the database has been changed to `MariaDB 10.1`. A `ReadOnlyStaleness` option has been added to the experimental CloudSpanner storage. This allows for tuning that might increase performance in some scenarios by issuing read transactions with the `exact_staleness` option set rather than `strong_read`. For more details see the [CloudSpanner TransactionOptions](https://cloud.google.com/spanner/docs/reference/rest/v1/TransactionOptions) documentation. The `LogVerifier` interface has been removed from the log client, though the functionality is still available. It is unlikely that there were implementations by third-parties. A new `TreeState DRAINING` has been added for trees with `TreeType LOG`. This is to support logs being cleanly frozen. A log tree in this state will not accept new entries via `QueueLeaves` but will continue to integrate any that were previously queued. When the queue of pending entries has been emptied the tree can be set to the `FROZEN` state safely. For MySQL storage this requires a schema update to add `'DRAINING'` to the enum of valid states. A command line utility `updatetree` has been added to allow tree states to be changed. This is also to support cleanly freezing logs. A 'howto' document has been added that explains how to freeze a log tree using the features added in this release. Commit [0e6d950b872d19e42320f4714820f0fe793b9913](https://api.github.com/repos/google/trillian/commits/0e6d950b872d19e42320f4714820f0fe793b9913) Download [zip](https://api.github.com/repos/google/trillian/zipball/v1.0.8) ## v1.0.7 - Storage API Changes, Schema Tweaks Published 2018-03-01 11:16:32 +0000 UTC Note: A large number of storage related API changes have been made in this release. These will probably only affect developers writing their own storage implementations. A new tree type `ORDERED_LOG` has been added for upcoming mirror support. This requires a schema change before it can be used. This change can be made when convenient and can be deferred until the functionality is available and needed. The definition of the `TreeType` column enum should be changed to `ENUM('LOG', 'MAP', 'PREORDERED_LOG') NOT NULL` Some storage interfaces were removed in #977 as they only had one implementation. We think this won't cause any impact on third parties and are willing to reconsider this change if it does. The gRPC Log and Map server APIs have new methods `InitLog` and `InitMap` which prepare newly created trees for use. Attempting to use trees that have not been initialized will return the `FAILED_PRECONDITION` error `storage.ErrTreeNeedsInit`. The gRPC Log server API has new methods `AddSequencedLeaf` and `AddSequencedLeaves`. These are intended to support mirroring applications and are not yet implemented. Storage APIs have been added such as `ReadWriteTransaction` which allows the underlying storage to manage the transaction and optionally retry until success or timeout. This is a more natural fit for some types of storage API such as [CloudSpanner](https://cloud.google.com/spanner/docs/transactions) and possibly other environments with managed transactions. The older `BeginXXX` methods were removed from the APIs. It should be fairly easy to convert a custom storage implementation to the new API format as can be seen from the changes made to the MySQL storage. The `GetOpts` options are no longer used by storage. This fixed the strange situation of storage code having to pass manufactured dummy instances to `GetTree`, which was being called in all the layers involved in request processing. Various internal APIs were modified to take a `*trillian.Tree` instead of an `int64`. A new storage implementation has been added for CloudSpanner. This is currently experimental and does not yet support Map trees. We have also added Docker examples for running Trillian in Google Cloud with CloudSpanner. The maximum size of a `VARBINARY` column in MySQL is too small to properly support Map storage. The type has been changed in the schema to `MEDIUMBLOB`. This can be done in place with an `ALTER TABLE` command but this could very be slow for large databases as it is a change to the physical row layout. Note: There is no need to make this change to the database if you are only using it for Log storage e.g. for Certificate Transparency servers. The obsolete programs `queue_leaves` and `fetch_leaves` have been deleted. Commit [7d73671537ca2a4745dc94da3dc93d32d7ce91f1](https://api.github.com/repos/google/trillian/commits/7d73671537ca2a4745dc94da3dc93d32d7ce91f1) Download [zip](https://api.github.com/repos/google/trillian/zipball/v1.0.7) ## v1.0.6 - GetLeavesByRange. 403 Permission Errors. Signer Metrics. Published 2018-02-05 16:00:26 +0000 UTC A new log server RPC API has been added to get leaves in a range. This is a more natural fit for CT type applications as it more closely follows the CT HTTP API. The server now returns 403 for permission denied where it used to return 500 errors. This follows the behaviour of the C++ implementation. The log signer binary now reports metrics for the number it has signed and the number of errors that have occurred. This is intended to give more insight into the state of the queue and integration processing. Commit [b20b3109af7b68227c83c5d930271eaa4f0be771](https://api.github.com/repos/google/trillian/commits/b20b3109af7b68227c83c5d930271eaa4f0be771) Download [zip](https://api.github.com/repos/google/trillian/zipball/v1.0.6) ## v1.0.5 - TLS, Merge Delay Metrics, Easier Admin Tests Published 2018-02-07 09:41:08 +0000 UTC The API protos have been rebuilt with gRPC 1.3. Timestamps have been added to the log leaves in the MySQL database. Before upgrading to this version you **must** make the following schema changes: * Add the following column to the `LeafData` table. If you have existing data in the queue you might have to remove the NOT NULL clause: `QueueTimestampNanos BIGINT NOT NULL` * Add the following column to the `SequencedLeafData` table: `IntegrateTimestampNanos BIGINT NOT NULL` The above timestamps are used to export metrics via monitoring that give the merge delay for each tree that is in use. This is a good metric to use for alerting on. The Log and Map RPC servers now support TLS. AdminServer tests have been improved. Commit [dec673baf984c3d22d7b314011d809258ec36821](https://api.github.com/repos/google/trillian/commits/dec673baf984c3d22d7b314011d809258ec36821) Download [zip](https://api.github.com/repos/google/trillian/zipball/v1.0.5) ## v1.0.4 - Fix election issue. Large vendor updates. Published 2018-02-05 15:42:25 +0000 UTC An issue has been fixed where the master for a log could resign from the election while it was in the process of integrating a batch of leaves. We do not believe this could cause any issues with data integrity because of the versioned tree storage. This release includes a large number of vendor commits merged to catch up with etcd 3.2.10 and gRPC v1.3. Commit [1713865ecca0dc8f7b4a8ed830a48ae250fd943b](https://api.github.com/repos/google/trillian/commits/1713865ecca0dc8f7b4a8ed830a48ae250fd943b) Download [zip](https://api.github.com/repos/google/trillian/zipball/v1.0.4) ## v1.0.3 - Auth API. Interceptor fixes. Request validation + More Published 2018-02-05 15:33:08 +0000 UTC An authorization API has been added to the interceptors. This is intended for future development and integration. Issues where the interceptor would not time out on `PutTokens` have been fixed. This should make the quota system more robust. A bug has been fixed where the interceptor did not pass the context deadline through to other requests it made. This would cause some failing requests to do so after longer than the deadline with a misleading reason in the log. It did not cause request failures if they would otherwise succeed. Metalinter has been added and the code has been cleaned up where appropriate. Docker and Kubernetes scripts have been available and images are now built with Go 1.9. Sqlite has been introduced for unit tests where possible. Note that it is not multi threaded and cannot support all our testing scenarios. We still require MySQL for integration tests. Please note that Sqlite **must not** be used for production deployments as RPC servers are multi threaded database clients. The Log RPC server now applies tighter validation to request parameters than before. It's possible that some requests will be rejected. This should not affect valid requests. The admin server will only create trees for the log type it is hosted in. For example the admin server running in the Log server will not create Map trees. This may be reviewed in future as applications can legitimately use both tree types. Commit [9d08b330ab4270a8e984072076c0b3e84eb4601b](https://api.github.com/repos/google/trillian/commits/9d08b330ab4270a8e984072076c0b3e84eb4601b) Download [zip](https://api.github.com/repos/google/trillian/zipball/v1.0.3) ## v1.0.2 - TreeGC, Go 1.9, Update Private Keys. Published 2018-02-05 15:18:40 +0000 UTC Go 1.9 is required. It is now possible to update private keys via the admin API and this was added to the available field masks. The key storage format has not changed so we believe this change is transparent. Deleted trees are now garbage collected after an interval. This hard deletes them and they cannot be recovered. Be aware of this before upgrading if you have any that are in a soft deleted state. The Admin RPC API has been extended to allow trees to be undeleted - up to the point where they are hard deleted as set out above. Commit [442511ad82108654033c9daa4e72f8a79691dd32](https://api.github.com/repos/google/trillian/commits/442511ad82108654033c9daa4e72f8a79691dd32) Download [zip](https://api.github.com/repos/google/trillian/zipball/v1.0.2) ## v1.0.1 - Batched Queue Option Added Published 2018-02-05 14:49:33 +0000 UTC Apart from fixes this release includes the option for a batched queue. This has been reported to allow faster sequencing but is not enabled by default. If you want to switch to this you must build the code with the `--tags batched_queue` option. You must then also apply a schema change if you are running with a previous version of the database. Add the following column to the `Unsequenced` table: `QueueID VARBINARY(32) DEFAULT NULL` If you don't plan to switch to the `batched_queue` mode then you don't need to make the above change. Commit [afd178f85c963f56ad2ae7d4721d139b1d6050b4](https://api.github.com/repos/google/trillian/commits/afd178f85c963f56ad2ae7d4721d139b1d6050b4) Download [zip](https://api.github.com/repos/google/trillian/zipball/v1.0.1) ## v1.0 - First Log version we believe was ready for use. To support CT. Published 2018-02-05 13:51:55 +0000 UTC Quota metrics published. Quota admin api + server implemented. Improvements to local / AWS deployment. Map fixes and further development. ECDSA key handling improvements. Key factory improvements. Code coverage added. Quota integration test added. Etcd quota support in log and map connected up. Incompatibility with C++ code fixed where consistency proof requests for first == second == 0 were rejected. Commit [a6546d092307f6e0d396068066033b434203824d](https://api.github.com/repos/google/trillian/commits/a6546d092307f6e0d396068066033b434203824d) Download [zip](https://api.github.com/repos/google/trillian/zipball/v1.0) trillian-1.6.1/CODEOWNERS000066400000000000000000000016021466362047600147450ustar00rootroot00000000000000# See https://help.github.com/articles/about-codeowners/ # for more info about CODEOWNERS file # It uses the same pattern rule for gitignore file # https://git-scm.com/docs/gitignore#_pattern_format # # These owners will be the default owners for everything in # the repo. Unless a later match takes precedence, # @google/trillian-team will be requested for # review when someone opens a pull request. * @google/trillian-team /*.proto @mhutchinson @AlCutter /storage/storagepb/storage.proto @mhutchinson @AlCutter # Mitigation for https://github.com/google/trillian/issues/1297 # Folks to watch out for hanges to DB schemas and ensure that # there's a note added in a sensible location about how to # upgrade schema instances. /storage/mysql/schema/* @mhutchinson @AlCutter /storage/cloudspanner/spanner.sdl @mhutchinson @AlCutter trillian-1.6.1/CONTRIBUTING.md000066400000000000000000000046651466362047600156170ustar00rootroot00000000000000# 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 trillian-1.6.1/CONTRIBUTORS000066400000000000000000000031131466362047600152310ustar00rootroot00000000000000# People who have agreed to one of the CLAs and can contribute patches. # The AUTHORS file lists the copyright holders; this file # lists people. For example, Google employees are listed here # but not in AUTHORS, because Google holds the copyright. # # Names should be added to this file only after verifying that # the individual or the individual's organization has agreed to # the appropriate Contributor License Agreement, found here: # # https://developers.google.com/open-source/cla/individual # https://developers.google.com/open-source/cla/corporate # # The agreement for individuals can be filled out on the web. # # When adding J Random Contributor's name to this file, # either J's name or J's organization's name should be # added to the AUTHORS file, depending on whether the # individual or corporate CLA was used. # # Names should be added to this file as: # Name # # Please keep the list sorted. Al Cutter Alan Parra Antonio Marcedone Ben Laurie David Drysdale Gary Belvin Roland Shoemaker Martin Smith Martin Hutchinson Paul Hadfield Pavel Kalinnikov Pierre Phaneuf Rob Percival Roger Ng Vishal Kuo trillian-1.6.1/LICENSE000066400000000000000000000261361466362047600143700ustar00rootroot00000000000000 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. trillian-1.6.1/PULL_REQUEST_TEMPLATE.md000066400000000000000000000011571466362047600171600ustar00rootroot00000000000000 ### Checklist - [ ] I have updated the [CHANGELOG](CHANGELOG.md). - [ ] I have updated [documentation](docs/) accordingly (including the [feature implementation matrix](docs/Feature_Implementation_Matrix.md)). trillian-1.6.1/README.md000066400000000000000000000307121466362047600146350ustar00rootroot00000000000000# Trillian: General Transparency [![Go Report Card](https://goreportcard.com/badge/github.com/google/trillian)](https://goreportcard.com/report/github.com/google/trillian) [![codecov](https://codecov.io/gh/google/trillian/branch/master/graph/badge.svg?token=QwofUwmvAs)](https://codecov.io/gh/google/trillian) [![GoDoc](https://godoc.org/github.com/google/trillian?status.svg)](https://godoc.org/github.com/google/trillian) [![Slack Status](https://img.shields.io/badge/Slack-Chat-blue.svg)](https://transparency-dev.slack.com/) - [Overview](#overview) - [Support](#support) - [Using the Code](#using-the-code) - [MySQL Setup](#mysql-setup) - [Integration Tests](#integration-tests) - [Working on the Code](#working-on-the-code) - [Rebuilding Generated Code](#rebuilding-generated-code) - [Updating Dependencies](#updating-dependencies) - [Running Codebase Checks](#running-codebase-checks) - [Design](#design) - [Design Overview](#design-overview) - [Personalities](#personalities) - [Log Mode](#log-mode) - [Use Cases](#use-cases) - [Certificate Transparency Log](#certificate-transparency-log) ## Overview Trillian is an implementation of the concepts described in the [Verifiable Data Structures](docs/papers/VerifiableDataStructures.pdf) white paper, which in turn is an extension and generalisation of the ideas which underpin [Certificate Transparency](https://certificate-transparency.org). Trillian implements a [Merkle tree](https://en.wikipedia.org/wiki/Merkle_tree) whose contents are served from a data storage layer, to allow scalability to extremely large trees. On top of this Merkle tree, Trillian provides the following: - An append-only **Log** mode, analogous to the original [Certificate Transparency](https://certificate-transparency.org) logs. In this mode, the Merkle tree is effectively filled up from the left, giving a *dense* Merkle tree. Note that Trillian requires particular applications to provide their own [personalities](#personalities) on top of the core transparent data store functionality. [Certificate Transparency (CT)](https://tools.ietf.org/html/rfc6962) is the most well-known and widely deployed transparency application, and an implementation of CT as a Trillian personality is available in the [certificate-transparency-go repo](https://github.com/google/certificate-transparency-go/blob/master/trillian). Other examples of Trillian personalities are available in the [trillian-examples](https://github.com/google/trillian-examples) repo. ## Support - Mailing list: https://groups.google.com/forum/#!forum/trillian-transparency - Slack: https://transparency-dev.slack.com/ ([invitation](https://join.slack.com/t/transparency-dev/shared_invite/zt-27pkqo21d-okUFhur7YZ0rFoJVIOPznQ)) ## Using the Code The Trillian codebase is stable and is used in production by multiple organizations, including many large-scale [Certificate Transparency](https://certificate.transparency.dev) log operators. Given this, we do not plan to add any new features to this version of Trillian, and will try to avoid any further incompatible code and schema changes but cannot guarantee that they will never be necessary. The current state of feature implementation is recorded in the [Feature implementation matrix](docs/Feature_Implementation_Matrix.md). To build and test Trillian you need: - Go 1.22 or later (go 1.22 matches cloudbuild, and is preferred for developers that will be submitting PRs to this project). To run many of the tests (and production deployment) you need: - [MySQL](https://www.mysql.com/) or [MariaDB](https://mariadb.org/) to provide the data storage layer; see the [MySQL Setup](#mysql-setup) section. Note that this repository uses Go modules to manage dependencies; Go will fetch and install them automatically upon build/test. To fetch the code, dependencies, and build Trillian, run the following: ```bash git clone https://github.com/google/trillian.git cd trillian go build ./... ``` To build and run tests, use: ```bash go test ./... ``` The repository also includes multi-process integration tests, described in the [Integration Tests](#integration-tests) section below. ### MySQL Setup To run Trillian's integration tests you need to have an instance of MySQL running and configured to: - listen on the standard MySQL port 3306 (so `mysql --host=127.0.0.1 --port=3306` connects OK) - not require a password for the `root` user You can then set up the [expected tables](storage/mysql/schema/storage.sql) in a `test` database like so: ```bash ./scripts/resetdb.sh Warning: about to destroy and reset database 'test' Are you sure? y > Resetting DB... > Reset Complete ``` ### Integration Tests Trillian includes an integration test suite to confirm basic end-to-end functionality, which can be run with: ```bash ./integration/integration_test.sh ``` This runs a multi-process test: - A [test](integration/log_integration_test.go) that starts a Trillian server in Log mode, together with a signer, logs many leaves, and checks they are integrated correctly. ### Deployment You can find instructions on how to deploy Trillian in [deployment](/deployment) and [examples/deployment](/examples/deployment) directories. ## Working on the Code Developers who want to make changes to the Trillian codebase need some additional dependencies and tools, described in the following sections. The [Cloud Build configuration](cloudbuild.yaml) and the scripts it depends on are also a useful reference for the required tools and scripts, as it may be more up-to-date than this document. ### Rebuilding Generated Code Some of the Trillian Go code is autogenerated from other files: - [gRPC](http://www.grpc.io/) message structures are originally provided as [protocol buffer](https://developers.google.com/protocol-buffers/) message definitions. See also, https://grpc.io/docs/protoc-installation/. - Some unit tests use mock implementations of interfaces; these are created from the real implementations by [GoMock](https://github.com/golang/mock). - Some enums have string-conversion methods (satisfying the `fmt.Stringer` interface) created using the [stringer](https://godoc.org/golang.org/x/tools/cmd/stringer) tool (`go get golang.org/x/tools/cmd/stringer`). Re-generating mock or protobuffer files is only needed if you're changing the original files. The recommended way to do this is by using the Docker image used by the Cloud Build: ```shell docker build -f ./integration/cloudbuild/testbase/Dockerfile -t trillian-builder . docker run -it --mount type=bind,src="$(pwd)",target=/src trillian-builder /bin/bash -c "cd /src; ./scripts/install_deps.sh; go generate -x ./..." ``` These commands first create a docker image from the Dockerfile in this repo, and then launch a container based on this image with the local directory mounted. The correct versions of the tools are determined using the `go.mod` file in this repo, and these tools are installed. Finally, all of the generated files are regenerated and Docker exits. Alternatively, you can install the prerequisites locally: - a series of tools, using `go install` to ensure that the versions are compatible and tested: ``` cd $(go list -f '{{ .Dir }}' github.com/google/trillian); \ go install github.com/golang/mock/mockgen; \ go install google.golang.org/protobuf/proto; \ go install google.golang.org/protobuf/cmd/protoc-gen-go; \ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc; \ go install github.com/pseudomuto/protoc-gen-doc/cmd/protoc-gen-doc; \ go install golang.org/x/tools/cmd/stringer ``` and run the following: ```bash go generate -x ./... # hunts for //go:generate comments and runs them ``` ### Updating Dependencies The Trillian codebase uses go.mod to declare fixed versions of its dependencies. With Go modules, updating a dependency simply involves running `go get`: ```bash go get package/path # Fetch the latest published version go get package/path@X.Y.Z # Fetch a specific published version go get package/path@HEAD # Fetch the latest commit ``` To update ALL dependencies to the latest version run `go get -u`. Be warned however, that this may undo any selected versions that resolve issues in other non-module repos. While running `go build` and `go test`, go will add any ambiguous transitive dependencies to `go.mod` To clean these up run: ```bash go mod tidy ``` ### Running Codebase Checks The [`scripts/presubmit.sh`](scripts/presubmit.sh) script runs various tools and tests over the codebase. #### Install [golangci-lint](https://github.com/golangci/golangci-lint#local-installation). ```bash go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.55.1 ``` #### Run code generation, build, test and linters ```bash ./scripts/presubmit.sh ``` #### Or just run the linters alone ```bash golangci-lint run ``` ## Design ### Design Overview Trillian is primarily implemented as a [gRPC service](http://www.grpc.io/docs/guides/concepts.html#service-definition); this service receives get/set requests over gRPC and retrieves the corresponding Merkle tree data from a separate storage layer (currently using MySQL), ensuring that the cryptographic properties of the tree are preserved along the way. The Trillian service is multi-tenanted – a single Trillian installation can support multiple Merkle trees in parallel, distinguished by their `TreeId` – and each tree operates in one of two modes: - **Log** mode: an append-only collection of items; this has two sub-modes: - normal Log mode, where the Trillian service assigns sequence numbers to new tree entries as they arrive - 'preordered' Log mode, where the unique sequence number for entries in the Merkle tree is externally specified In either case, Trillian's key transparency property is that cryptographic proofs of inclusion/consistency are available for data items added to the service. ### Personalities To build a complete transparent application, the Trillian core service needs to be paired with additional code, known as a *personality*, that provides functionality that is specific to the particular application. In particular, the personality is responsible for: * **Admission Criteria** – ensuring that submissions comply with the overall purpose of the application. * **Canonicalization** – ensuring that equivalent versions of the same data get the same canonical identifier, so they can be de-duplicated by the Trillian core service. * **External Interface** – providing an API for external users, including any practical constraints (ACLs, load-balancing, DoS protection, etc.) This is [described in more detail in a separate document](docs/Personalities.md). General [design considerations for transparent Log applications](docs/TransparentLogging.md) are also discussed separately. ### Log Mode When running in Log mode, Trillian provides a gRPC API whose operations are similar to those available for Certificate Transparency logs (cf. [RFC 6962](https://tools.ietf.org/html/6962)). These include: - `GetLatestSignedLogRoot` returns information about the current root of the Merkle tree for the log, including the tree size, hash value, timestamp and signature. - `GetLeavesByRange` returns leaf information for particular leaves, specified by their index in the log. - `QueueLeaf` requests inclusion of the specified item into the log. - For a pre-ordered log, `AddSequencedLeaves` requests the inclusion of specified items into the log at specified places in the tree. - `GetInclusionProof`, `GetInclusionProofByHash` and `GetConsistencyProof` return inclusion and consistency proof data. In Log mode (whether normal or pre-ordered), Trillian includes an additional Signer component; this component periodically processes pending items and adds them to the Merkle tree, creating a new signed tree head as a result. ![Log components](docs/images/LogDesign.png) (Note that each of the components in this diagram can be [distributed](https://github.com/google/certificate-transparency-go/blob/master/trillian/docs/ManualDeployment.md#distribution), for scalability and resilience.) Use Cases --------- ### Certificate Transparency Log The most obvious application for Trillian in Log mode is to provide a Certificate Transparency (RFC 6962) Log. To do this, the CT Log personality needs to include all of the certificate-specific processing – in particular, checking that an item that has been suggested for inclusion is indeed a valid certificate that chains to an accepted root. trillian-1.6.1/client/000077500000000000000000000000001466362047600146315ustar00rootroot00000000000000trillian-1.6.1/client/admin.go000066400000000000000000000071241466362047600162540ustar00rootroot00000000000000// Copyright 2018 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 client import ( "context" "fmt" "time" "github.com/google/trillian" "github.com/google/trillian/client/backoff" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "k8s.io/klog/v2" ) // CreateAndInitTree uses the adminClient and logClient to create the tree // described by req. // If req describes a LOG tree, then this function will also call the InitLog // function using logClient. // Internally, the function will continue to retry failed requests until either // the tree is created (and if necessary, initialised) successfully, or ctx is // cancelled. func CreateAndInitTree( ctx context.Context, req *trillian.CreateTreeRequest, adminClient trillian.TrillianAdminClient, logClient trillian.TrillianLogClient) (*trillian.Tree, error) { b := &backoff.Backoff{ Min: 100 * time.Millisecond, Max: 10 * time.Second, Factor: 2, Jitter: true, } var tree *trillian.Tree err := b.Retry(ctx, func() error { klog.Info("CreateTree...") var err error tree, err = adminClient.CreateTree(ctx, req) switch code := status.Code(err); code { case codes.Unavailable: klog.Errorf("Admin server unavailable: %v", err) return err case codes.OK: return nil default: klog.Errorf("failed to CreateTree(%+v): %T %v", req, err, err) return err } }) if err != nil { return nil, err } switch tree.TreeType { case trillian.TreeType_LOG, trillian.TreeType_PREORDERED_LOG: if err := InitLog(ctx, tree, logClient); err != nil { return nil, err } default: return nil, fmt.Errorf("don't know how or whether to initialise tree type %v", tree.TreeType) } return tree, nil } // InitLog initialises a freshly created Log tree. func InitLog(ctx context.Context, tree *trillian.Tree, logClient trillian.TrillianLogClient) error { if tree.TreeType != trillian.TreeType_LOG && tree.TreeType != trillian.TreeType_PREORDERED_LOG { return fmt.Errorf("InitLog called with tree of type %v", tree.TreeType) } b := &backoff.Backoff{ Min: 100 * time.Millisecond, Max: 10 * time.Second, Factor: 2, Jitter: true, } err := b.Retry(ctx, func() error { klog.Infof("Initialising Log %v...", tree.TreeId) req := &trillian.InitLogRequest{LogId: tree.TreeId} resp, err := logClient.InitLog(ctx, req) switch code := status.Code(err); code { case codes.Unavailable: klog.Errorf("Log server unavailable: %v", err) return err case codes.AlreadyExists: klog.Warningf("Bizarrely, the just-created Log (%v) is already initialised!: %v", tree.TreeId, err) return err case codes.OK: klog.Infof("Initialised Log (%v) with new SignedTreeHead:\n%+v", tree.TreeId, resp.Created) return nil default: klog.Errorf("failed to InitLog(%+v): %T %v", req, err, err) return err } }) if err != nil { return err } // Wait for log root to become available. return b.Retry(ctx, func() error { _, err := logClient.GetLatestSignedLogRoot(ctx, &trillian.GetLatestSignedLogRootRequest{LogId: tree.TreeId}) return err }, codes.FailedPrecondition) } trillian-1.6.1/client/backoff/000077500000000000000000000000001466362047600162245ustar00rootroot00000000000000trillian-1.6.1/client/backoff/backoff.go000066400000000000000000000076441466362047600201610ustar00rootroot00000000000000// 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 backoff allows retrying an operation with backoff. package backoff import ( "context" "fmt" "math/rand" "time" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) // RetriableError explicitly instructs Backoff to retry. type RetriableError string // Error returns string representation of the retriable error. func (re RetriableError) Error() string { return string(re) } // RetriableErrorf wraps a formatted string into a RetriableError. func RetriableErrorf(format string, a ...interface{}) error { return RetriableError(fmt.Sprintf(format, a...)) } // Backoff specifies the parameters of the backoff algorithm. Works correctly // if 0 < Min <= Max <= 2^62 (nanosec), and Factor >= 1. type Backoff struct { Min time.Duration // Duration of the first pause. Max time.Duration // Max duration of a pause. Factor float64 // The factor of duration increase between iterations. Jitter bool // Add random noise to pauses. delta time.Duration // Current pause duration relative to Min, no jitter. } // Duration returns the time to wait on current retry iteration. Every time // Duration is called, the returned value will exponentially increase by Factor // until Backoff.Max. If Jitter is enabled, will add an additional random value // between 0 and the duration, so the result can at most double. func (b *Backoff) Duration() time.Duration { base := b.Min + b.delta pause := base if b.Jitter { // Add a number in the range [0, pause). pause += time.Duration(rand.Int63n(int64(pause))) } nextPause := time.Duration(float64(base) * b.Factor) if nextPause > b.Max || nextPause < b.Min { // Multiplication could overflow. nextPause = b.Max } b.delta = nextPause - b.Min return pause } // Reset sets the internal state back to first retry iteration. func (b *Backoff) Reset() { b.delta = 0 } // Retry calls a function until it succeeds or the context is done. // It will backoff if the function returns a retryable error. // Once the context is done, retries will end and the most recent error will be returned. // Backoff is not reset by this function. func (b *Backoff) Retry(ctx context.Context, f func() error, retry ...codes.Code) error { // If the context is already done, don't make any attempts to call f. if ctx.Err() != nil { return ctx.Err() } // Try calling f while the error is retryable and ctx is not done. for { if err := f(); !IsRetryable(err, retry...) { return err } select { case <-time.After(b.Duration()): case <-ctx.Done(): return ctx.Err() } } } // IsRetryable returns false unless the error is explicitly retriable per // https://godoc.org/google.golang.org/grpc/codes, // or if the error codes is in retry. codes.OK is not retryable. func IsRetryable(err error, retry ...codes.Code) bool { code := status.Code(err) switch code { // Fast path. case codes.OK: return false // Debatable cases: case codes.DeadlineExceeded, codes.ResourceExhausted: // Retry with backoff. return true // Errors that are explicitly retryable: case codes.Unavailable, // Client can just retry the call. codes.Aborted: // Client can retry the read-modify-write function. return true } for _, c := range retry { if code == c { return true } } // Don't retry for all other errors, unless it is a RetriableError. _, ok := err.(RetriableError) return ok } trillian-1.6.1/client/backoff/backoff_test.go000066400000000000000000000106751466362047600212160ustar00rootroot00000000000000// 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 backoff import ( "context" "errors" "testing" "time" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" _ "k8s.io/klog/v2" ) func TestBackoff(t *testing.T) { b := Backoff{ Min: time.Duration(1), Max: time.Duration(100), Factor: 2, } for _, test := range []struct { b Backoff times int want time.Duration }{ {b, 1, time.Duration(1)}, {b, 2, time.Duration(2)}, {b, 3, time.Duration(4)}, {b, 4, time.Duration(8)}, {b, 8, time.Duration(100)}, } { test.b.Reset() var got time.Duration for i := 0; i < test.times; i++ { got = test.b.Duration() } if got != test.want { t.Errorf("Duration() %v times: %v, want %v", test.times, got, test.want) } } } func TestJitter(t *testing.T) { b := Backoff{ Min: 1 * time.Second, Max: 100 * time.Second, Factor: 2, Jitter: true, } for _, test := range []struct { b Backoff times int min time.Duration max time.Duration }{ {b, 1, 1 * time.Second, 2 * time.Second}, {b, 2, 2 * time.Second, 4 * time.Second}, {b, 3, 4 * time.Second, 8 * time.Second}, {b, 4, 8 * time.Second, 16 * time.Second}, {b, 8, 100 * time.Second, 200 * time.Second}, } { test.b.Reset() var got1 time.Duration for i := 0; i < test.times; i++ { got1 = test.b.Duration() } if got1 < test.min || got1 > test.max { t.Errorf("Duration() %v times, want %v < %v < %v", test.times, test.min, got1, test.max) } // Ensure a random value is being produced. test.b.Reset() var got2 time.Duration for i := 0; i < test.times; i++ { got2 = test.b.Duration() } if got1 == got2 { t.Errorf("Duration() %v times == Duration() %v times, want %v != %v", test.times, test.times, got1, got2) } } } func TestRetry(t *testing.T) { b := Backoff{ Min: 50 * time.Millisecond, Max: 200 * time.Millisecond, Factor: 2, } // ctx used by Retry(), declared here to that test.ctxFunc can set it. var ctx context.Context var cancel context.CancelFunc for _, test := range []struct { name string f func() error ctxFunc func() wantErr bool }{ { name: "func that immediately succeeds", f: func() error { return nil }, }, { name: "func that succeeds on second attempt", f: func() func() error { var callCount int return func() error { callCount++ if callCount == 1 { return status.Errorf(codes.Unavailable, "error") } return nil } }(), }, { name: "explicitly retry", f: func() func() error { var callCount int return func() error { callCount++ if callCount < 10 { return RetriableErrorf("attempt %d", callCount) } return nil } }(), }, { name: "explicitly retry and fail", f: func() func() error { var callCount int return func() error { callCount++ if callCount < 10 { return RetriableErrorf("attempt %d", callCount) } return errors.New("failed 10 times") } }(), wantErr: true, }, { name: "func that takes too long to succeed", f: func() error { // Cancel the context and return an error. This func will succeed on // any future calls, but it should not be retried due to the context // being cancelled. if ctx.Err() == nil { cancel() return status.Errorf(codes.Unavailable, "error") } return nil }, wantErr: true, }, { name: "context done before Retry() called", f: func() error { return nil }, ctxFunc: func() { ctx, cancel = context.WithCancel(context.Background()) cancel() }, wantErr: true, }, } { if test.ctxFunc != nil { test.ctxFunc() } else { ctx, cancel = context.WithCancel(context.Background()) } err := b.Retry(ctx, test.f) cancel() if gotErr := err != nil; gotErr != test.wantErr { t.Errorf("%v: Retry() = %v, want err? %v", test.name, err, test.wantErr) } } } trillian-1.6.1/client/log_client.go000066400000000000000000000245061466362047600173060ustar00rootroot00000000000000// 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 client verifies responses from the Trillian log. package client import ( "bytes" "context" "fmt" "sort" "sync" "time" "github.com/google/trillian" "github.com/google/trillian/client/backoff" "github.com/google/trillian/types" "github.com/transparency-dev/merkle" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) // LogClient represents a client for a given Trillian log instance. type LogClient struct { *LogVerifier LogID int64 MinMergeDelay time.Duration client trillian.TrillianLogClient root types.LogRootV1 rootLock sync.Mutex updateLock sync.Mutex } // New returns a new LogClient. func New(logID int64, client trillian.TrillianLogClient, verifier *LogVerifier, root types.LogRootV1) *LogClient { return &LogClient{ LogVerifier: verifier, LogID: logID, client: client, root: root, } } // NewFromTree creates a new LogClient given a tree config. func NewFromTree(client trillian.TrillianLogClient, config *trillian.Tree, root types.LogRootV1) (*LogClient, error) { verifier, err := NewLogVerifierFromTree(config) if err != nil { return nil, err } return New(config.GetTreeId(), client, verifier, root), nil } // AddLeaf adds leaf to the append only log. // Blocks and continuously updates the trusted root until a successful inclusion proof // can be retrieved. func (c *LogClient) AddLeaf(ctx context.Context, data []byte) error { if err := c.QueueLeaf(ctx, data); err != nil { return fmt.Errorf("QueueLeaf(): %v", err) } if err := c.WaitForInclusion(ctx, data); err != nil { return fmt.Errorf("WaitForInclusion(): %v", err) } return nil } // ListByIndex returns the requested leaves by index. func (c *LogClient) ListByIndex(ctx context.Context, start, count int64) ([]*trillian.LogLeaf, error) { resp, err := c.client.GetLeavesByRange(ctx, &trillian.GetLeavesByRangeRequest{ LogId: c.LogID, StartIndex: start, Count: count, }) if err != nil { return nil, err } // Verify that we got back the requested leaves. if len(resp.Leaves) < int(count) { return nil, fmt.Errorf("len(Leaves)=%d, want %d", len(resp.Leaves), count) } for i, l := range resp.Leaves { if want := start + int64(i); l.LeafIndex != want { return nil, fmt.Errorf("Leaves[%d].LeafIndex=%d, want %d", i, l.LeafIndex, want) } } return resp.Leaves, nil } // WaitForRootUpdate repeatedly fetches the latest root until there is an // update, which it then applies, or until ctx times out. func (c *LogClient) WaitForRootUpdate(ctx context.Context) (*types.LogRootV1, error) { b := &backoff.Backoff{ Min: 100 * time.Millisecond, Max: 10 * time.Second, Factor: 2, Jitter: true, } for { newTrusted, err := c.UpdateRoot(ctx) switch status.Code(err) { case codes.OK: if newTrusted != nil { return newTrusted, nil } case codes.Unavailable, codes.NotFound, codes.FailedPrecondition: // Retry. default: return nil, err } select { case <-ctx.Done(): return nil, status.Errorf(codes.DeadlineExceeded, "%v", ctx.Err()) case <-time.After(b.Duration()): } } } // getAndVerifyLatestRoot fetches and verifies the latest root against a trusted root, seen in the past. // Pass nil for trusted if this is the first time querying this log. func (c *LogClient) getAndVerifyLatestRoot(ctx context.Context, trusted *types.LogRootV1) (*types.LogRootV1, error) { resp, err := c.client.GetLatestSignedLogRoot(ctx, &trillian.GetLatestSignedLogRootRequest{ LogId: c.LogID, FirstTreeSize: int64(trusted.TreeSize), }) if err != nil { return nil, err } // TODO(gbelvin): Turn on root verification. /* logRoot, err := c.VerifyRoot(&types.LogRootV1{}, resp.GetSignedLogRoot(), nil) if err != nil { return nil, err } */ // TODO(gbelvin): Remove this hack when all implementations store digital signatures. var logRoot types.LogRootV1 if err := logRoot.UnmarshalBinary(resp.GetSignedLogRoot().LogRoot); err != nil { return nil, err } if trusted.TreeSize > 0 && logRoot.TreeSize == trusted.TreeSize && bytes.Equal(logRoot.RootHash, trusted.RootHash) { // Tree has not been updated. return &logRoot, nil } // Verify root update if the tree / the latest signed log root isn't empty. if logRoot.TreeSize > 0 { if _, err := c.VerifyRoot(trusted, resp.GetSignedLogRoot(), resp.GetProof().GetHashes()); err != nil { return nil, err } } return &logRoot, nil } // GetRoot returns a copy of the latest trusted root. func (c *LogClient) GetRoot() *types.LogRootV1 { c.rootLock.Lock() defer c.rootLock.Unlock() // Copy the internal trusted root in order to prevent clients from modifying it. ret := c.root return &ret } // UpdateRoot retrieves the current SignedLogRoot, verifying it against roots this client has // seen in the past, and updating the currently trusted root if the new root verifies, and is // newer than the currently trusted root. func (c *LogClient) UpdateRoot(ctx context.Context) (*types.LogRootV1, error) { // Only one root update should be running at any point in time, because // the update involves a consistency proof from the old value, and if the // old value could change along the way (in another goroutine) then the // result could be inconsistent. // // For example, if the current root is A and two root updates A->B and A->C // happen in parallel, then we might end up with the transitions A->B->C: // cur := A cur := A // getRoot() => B getRoot() => C // proof(A->B) ok proof(A->C) ok // c.root = B // c.root = C // and the last step (B->C) has no proof and so could hide a forked tree. c.updateLock.Lock() defer c.updateLock.Unlock() currentlyTrusted := c.GetRoot() newTrusted, err := c.getAndVerifyLatestRoot(ctx, currentlyTrusted) if err != nil { return nil, err } // Lock "rootLock" for the "root" update. c.rootLock.Lock() defer c.rootLock.Unlock() if newTrusted.TimestampNanos > currentlyTrusted.TimestampNanos && newTrusted.TreeSize >= currentlyTrusted.TreeSize { // Take a copy of the new trusted root in order to prevent clients from modifying it. c.root = *newTrusted return newTrusted, nil } return nil, nil } // WaitForInclusion blocks until the requested data has been verified with an // inclusion proof. // // It will continuously update the root to the latest one available until the // data is found, or an error is returned. // // It is best to call this method with a context that will timeout to avoid // waiting forever. func (c *LogClient) WaitForInclusion(ctx context.Context, data []byte) error { leaf := prepareLeaf(c.hasher, data) // If a minimum merge delay has been configured, wait at least that long before // starting to poll if c.MinMergeDelay > 0 { select { case <-ctx.Done(): return status.Errorf(codes.DeadlineExceeded, "%v", ctx.Err()) case <-time.After(c.MinMergeDelay): } } var root *types.LogRootV1 for { root = c.GetRoot() // It is illegal to ask for an inclusion proof with TreeSize = 0. if root.TreeSize >= 1 { ok, err := c.getAndVerifyInclusionProof(ctx, leaf.MerkleLeafHash, root) if err != nil && status.Code(err) != codes.NotFound { return err } else if ok { return nil } } // If not found or tree is empty, wait for a root update before retrying again. if _, err := c.WaitForRootUpdate(ctx); err != nil { return err } // Retry } } func (c *LogClient) getAndVerifyInclusionProof(ctx context.Context, leafHash []byte, sth *types.LogRootV1) (bool, error) { resp, err := c.client.GetInclusionProofByHash(ctx, &trillian.GetInclusionProofByHashRequest{ LogId: c.LogID, LeafHash: leafHash, TreeSize: int64(sth.TreeSize), }) if err != nil { return false, err } if len(resp.Proof) < 1 { return false, nil } for _, proof := range resp.Proof { if err := c.VerifyInclusionByHash(sth, leafHash, proof); err != nil { return false, fmt.Errorf("VerifyInclusionByHash(): %v", err) } } return true, nil } // AddSequencedLeaves adds any number of pre-sequenced leaves to the log. // Indexes must be contiguous. func (c *LogClient) AddSequencedLeaves(ctx context.Context, dataByIndex map[int64][]byte) error { if len(dataByIndex) == 0 { return nil } leaves := make([]*trillian.LogLeaf, 0, len(dataByIndex)) indexes := make([]int64, 0, len(dataByIndex)) for index := range dataByIndex { indexes = append(indexes, index) } sort.Slice(indexes, func(a, b int) bool { return indexes[a] < indexes[b] }) for i, index := range indexes { // Check index continuity. if want := indexes[0] + int64(i); index != want { return fmt.Errorf("missing index in contiugous index range. got: %v, want: %v", index, want) } leaf := prepareLeaf(c.hasher, dataByIndex[index]) leaf.LeafIndex = index leaves = append(leaves, leaf) } resp, err := c.client.AddSequencedLeaves(ctx, &trillian.AddSequencedLeavesRequest{ LogId: c.LogID, Leaves: leaves, }) for _, leaf := range resp.GetResults() { if s := status.FromProto(leaf.GetStatus()); s.Code() != codes.OK && s.Code() != codes.AlreadyExists { return status.Errorf(s.Code(), "unexpected fail status in AddSequencedLeaves: %+v, err: %v", leaf, s.Message()) } } return err } // QueueLeaf adds a leaf to a Trillian log without blocking. // AlreadyExists is considered a success case by this function. func (c *LogClient) QueueLeaf(ctx context.Context, data []byte) error { leaf := prepareLeaf(c.hasher, data) _, err := c.client.QueueLeaf(ctx, &trillian.QueueLeafRequest{ LogId: c.LogID, Leaf: leaf, }) return err } // prepareLeaf returns a trillian.LogLeaf prepopulated with leaf data and hash. func prepareLeaf(hasher merkle.LogHasher, data []byte) *trillian.LogLeaf { leafHash := hasher.HashLeaf(data) return &trillian.LogLeaf{ LeafValue: data, MerkleLeafHash: leafHash, } } trillian-1.6.1/client/log_client_test.go000066400000000000000000000155631466362047600203500ustar00rootroot00000000000000// 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 client import ( "bytes" "context" "fmt" "testing" "time" "github.com/google/trillian" "github.com/google/trillian/testonly/integration" "github.com/google/trillian/types" "github.com/transparency-dev/merkle/rfc6962" "google.golang.org/protobuf/proto" "github.com/google/trillian/storage/testdb" stestonly "github.com/google/trillian/storage/testonly" ) func TestAddGetLeaf(t *testing.T) { // TODO: Build a GetLeaf method and test a full get/set cycle. } // addSequencedLeaves is a temporary stand-in function for tests until the real API gets built. func addSequencedLeaves(ctx context.Context, env *integration.LogEnv, client *LogClient, leaves [][]byte) error { if len(leaves) == 0 { return nil } dataByIndex := make(map[int64][]byte) for i, l := range leaves { dataByIndex[int64(i)] = l } if err := client.AddSequencedLeaves(ctx, dataByIndex); err != nil { return fmt.Errorf("AddSequencedLeaves(): %v", err) } env.Sequencer.OperationSingle(ctx) if err := client.WaitForInclusion(ctx, leaves[len(leaves)-1]); err != nil { return fmt.Errorf("WaitForInclusion(): %v", err) } return nil } func clientEnvForTest(ctx context.Context, t *testing.T, template *trillian.Tree) (*integration.LogEnv, *LogClient) { t.Helper() testdb.SkipIfNoMySQL(t) env, err := integration.NewLogEnvWithGRPCOptions(ctx, 1, nil, nil) if err != nil { t.Fatal(err) } tree, err := CreateAndInitTree(ctx, &trillian.CreateTreeRequest{Tree: template}, env.Admin, env.Log) if err != nil { t.Fatalf("Failed to create log: %v", err) } client, err := NewFromTree(env.Log, tree, types.LogRootV1{}) if err != nil { t.Fatalf("NewFromTree(): %v", err) } return env, client } func TestListByIndex(t *testing.T) { ctx := context.Background() env, client := clientEnvForTest(ctx, t, stestonly.PreorderedLogTree) defer env.Close() // Add a few test leaves. leafData := [][]byte{ []byte("A"), []byte("B"), []byte("C"), } if err := addSequencedLeaves(ctx, env, client, leafData); err != nil { t.Fatalf("Failed to add leaves: %v", err) } // Fetch leaves. leaves, err := client.ListByIndex(ctx, 0, 3) if err != nil { t.Errorf("Failed to ListByIndex: %v", err) } for i, l := range leaves { if got, want := l.LeafValue, leafData[i]; !bytes.Equal(got, want) { t.Errorf("ListIndex()[%v] = %v, want %v", i, got, want) } } } func TestWaitForInclusion(t *testing.T) { ctx := context.Background() tree := proto.Clone(stestonly.LogTree).(*trillian.Tree) env, client := clientEnvForTest(ctx, t, tree) tree.TreeId = client.LogID defer env.Close() for _, test := range []struct { desc string leaf []byte client trillian.TrillianLogClient skipPreCheck bool wantErr bool }{ {desc: "First leaf", leaf: []byte("A"), client: env.Log}, {desc: "Make TreeSize > 1", leaf: []byte("B"), client: env.Log}, { desc: "invalid inclusion proof", leaf: []byte("A"), skipPreCheck: true, client: &MutatingLogClient{TrillianLogClient: env.Log, mutateInclusionProof: true}, wantErr: true, }, } { t.Run(test.desc, func(t *testing.T) { client, err := NewFromTree(test.client, tree, types.LogRootV1{}) if err != nil { t.Fatalf("NewFromTree(): %v", err) } if !test.skipPreCheck { cctx, cancel := context.WithTimeout(ctx, 50*time.Millisecond) if err := client.WaitForInclusion(cctx, test.leaf); err == nil { t.Error("WaitForInclusion before sequencing succeeded, want error") } cancel() } if err := client.QueueLeaf(ctx, test.leaf); err != nil { t.Fatalf("QueueLeaf(): %v", err) } env.Sequencer.OperationSingle(ctx) err = client.WaitForInclusion(ctx, test.leaf) if got := err != nil; got != test.wantErr { t.Errorf("WaitForInclusion(): %v, want error: %v", err, test.wantErr) } }) } } func TestUpdateRoot(t *testing.T) { ctx := context.Background() env, client := clientEnvForTest(ctx, t, stestonly.LogTree) defer env.Close() before := client.root.TreeSize // UpdateRoot should succeed with no change. root, err := client.UpdateRoot(ctx) if err != nil { t.Fatalf("UpdateRoot(): %v", err) } if got, want := root.TreeSize, before; got != want { t.Errorf("Tree size changed unexpectedly: %v, want %v", got, want) } data := []byte("foo") if err := client.QueueLeaf(ctx, data); err != nil { t.Fatalf("QueueLeaf(%s): %v, want nil", data, err) } env.Sequencer.OperationSingle(ctx) // UpdateRoot should see a change. root, err = client.UpdateRoot(ctx) if err != nil { t.Fatalf("UpdateRoot(): %v", err) } if got, want := root.TreeSize, before; got <= want { t.Errorf("Tree size after add Leaf: %v, want > %v", got, want) } } func TestUpdateRootSkew(t *testing.T) { ctx := context.Background() tree := proto.Clone(stestonly.LogTree).(*trillian.Tree) env, client := clientEnvForTest(ctx, t, tree) tree.TreeId = client.LogID defer env.Close() // Start with a single leaf. data := []byte("foo") if err := client.QueueLeaf(ctx, data); err != nil { t.Fatalf("QueueLeaf(%s): %v, want nil", data, err) } env.Sequencer.OperationSingle(ctx) root, err := client.UpdateRoot(ctx) if err != nil { t.Fatalf("UpdateRoot(): %v", err) } // Put in a second leaf after root. data2 := []byte("bar") if err := client.QueueLeaf(ctx, data2); err != nil { t.Fatalf("QueueLeaf(%s): %v, want nil", data2, err) } env.Sequencer.OperationSingle(ctx) // Now force a bad request. badRawClient := &MutatingLogClient{TrillianLogClient: env.Log, mutateRootSize: true} badClient, err := NewFromTree(badRawClient, tree, *root) if err != nil { t.Fatalf("failed to create mutating client: %v", err) } if _, err := badClient.UpdateRoot(ctx); err == nil { t.Error("UpdateRoot()=nil, want error") } } func TestAddSequencedLeaves(t *testing.T) { ctx := context.Background() for _, tc := range []struct { desc string dataByIndex map[int64][]byte wantErr bool }{ {desc: "empty", dataByIndex: nil}, {desc: "non-contiguous", dataByIndex: map[int64][]byte{ 0: []byte("A"), 2: []byte("C"), }, wantErr: true}, } { t.Run(tc.desc, func(t *testing.T) { c := &LogClient{LogVerifier: &LogVerifier{hasher: rfc6962.DefaultHasher}} err := c.AddSequencedLeaves(ctx, tc.dataByIndex) if gotErr := err != nil; gotErr != tc.wantErr { t.Errorf("AddSequencedLeaves(): %v, wantErr: %v", err, tc.wantErr) } }) } } trillian-1.6.1/client/log_verifier.go000066400000000000000000000065541466362047600176460ustar00rootroot00000000000000// 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 client import ( "errors" "fmt" "github.com/google/trillian" "github.com/google/trillian/types" "github.com/transparency-dev/merkle" "github.com/transparency-dev/merkle/proof" "github.com/transparency-dev/merkle/rfc6962" ) // LogVerifier allows verification of output from Trillian Logs, both regular // and pre-ordered; it is safe for concurrent use (as its contents are fixed // after construction). type LogVerifier struct { // hasher is the hash strategy used to compute nodes in the Merkle tree. hasher merkle.LogHasher } // NewLogVerifier returns an object that can verify output from Trillian Logs. func NewLogVerifier(hasher merkle.LogHasher) *LogVerifier { return &LogVerifier{hasher: hasher} } // NewLogVerifierFromTree creates a new LogVerifier using the algorithms // specified by a Trillian Tree object. func NewLogVerifierFromTree(config *trillian.Tree) (*LogVerifier, error) { if config == nil { return nil, errors.New("client: NewLogVerifierFromTree(): nil config") } log, pLog := trillian.TreeType_LOG, trillian.TreeType_PREORDERED_LOG if got := config.TreeType; got != log && got != pLog { return nil, fmt.Errorf("client: NewLogVerifierFromTree(): TreeType: %v, want %v or %v", got, log, pLog) } return NewLogVerifier(rfc6962.DefaultHasher), nil } // VerifyRoot verifies that newRoot is a valid append-only operation from // trusted. If trusted.TreeSize is zero, a consistency proof is not needed. func (c *LogVerifier) VerifyRoot(trusted *types.LogRootV1, newRoot *trillian.SignedLogRoot, consistency [][]byte) (*types.LogRootV1, error) { if trusted == nil { return nil, fmt.Errorf("VerifyRoot() error: trusted == nil") } if newRoot == nil { return nil, fmt.Errorf("VerifyRoot() error: newRoot == nil") } var r types.LogRootV1 if err := r.UnmarshalBinary(newRoot.LogRoot); err != nil { return nil, err } // Implicitly trust the first root we get. if trusted.TreeSize != 0 { // Verify consistency proof. if err := proof.VerifyConsistency(c.hasher, trusted.TreeSize, r.TreeSize, consistency, trusted.RootHash, r.RootHash); err != nil { return nil, fmt.Errorf("failed to verify consistency proof from %d->%d %x->%x: %v", trusted.TreeSize, r.TreeSize, trusted.RootHash, r.RootHash, err) } } return &r, nil } // VerifyInclusionByHash verifies that the inclusion proof for the given Merkle leafHash // matches the given trusted root. func (c *LogVerifier) VerifyInclusionByHash(trusted *types.LogRootV1, leafHash []byte, pf *trillian.Proof) error { if trusted == nil { return fmt.Errorf("VerifyInclusionByHash() error: trusted == nil") } if pf == nil { return fmt.Errorf("VerifyInclusionByHash() error: proof == nil") } return proof.VerifyInclusion(c.hasher, uint64(pf.LeafIndex), trusted.TreeSize, leafHash, pf.Hashes, trusted.RootHash) } trillian-1.6.1/client/log_verifier_test.go000066400000000000000000000041151466362047600206740ustar00rootroot00000000000000// 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 client import ( "testing" "github.com/google/trillian" "github.com/google/trillian/types" "github.com/transparency-dev/merkle/rfc6962" ) func TestVerifyRootErrors(t *testing.T) { logRoot, err := (&types.LogRootV1{}).MarshalBinary() if err != nil { t.Fatalf("Failed to create test signature: %v", err) } signedRoot := &trillian.SignedLogRoot{LogRoot: logRoot} // Test execution tests := []struct { desc string trusted *types.LogRootV1 newRoot *trillian.SignedLogRoot }{ {desc: "newRootNil", trusted: &types.LogRootV1{}, newRoot: nil}, {desc: "trustedNil", trusted: nil, newRoot: signedRoot}, } for _, test := range tests { logVerifier := NewLogVerifier(rfc6962.DefaultHasher) // This also makes sure that no nil pointer dereference errors occur (as this would cause a panic). if _, err := logVerifier.VerifyRoot(test.trusted, test.newRoot, nil); err == nil { t.Errorf("%v: VerifyRoot() error expected, but got nil", test.desc) } } } func TestVerifyInclusionByHashErrors(t *testing.T) { tests := []struct { desc string trusted *types.LogRootV1 proof *trillian.Proof }{ {desc: "trustedNil", trusted: nil, proof: &trillian.Proof{}}, {desc: "proofNil", trusted: &types.LogRootV1{}, proof: nil}, } for _, test := range tests { logVerifier := NewLogVerifier(nil) err := logVerifier.VerifyInclusionByHash(test.trusted, nil, test.proof) if err == nil { t.Errorf("%v: VerifyInclusionByHash() error expected, but got nil", test.desc) } } } trillian-1.6.1/client/mutating_client_test.go000066400000000000000000000066771466362047600214250ustar00rootroot00000000000000// 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 client import ( "context" "math/rand" "github.com/google/trillian" "github.com/google/trillian/types" "google.golang.org/grpc" "k8s.io/klog/v2" ) // MutatingLogClient supports applying mutations to the return values of the TrillianLogClient // for testing. type MutatingLogClient struct { trillian.TrillianLogClient mutateInclusionProof bool mutateConsistencyProof bool mutateRootSize bool } // GetLatestSignedLogRoot forwards requests and optionally modifies the returned size. func (c *MutatingLogClient) GetLatestSignedLogRoot(ctx context.Context, in *trillian.GetLatestSignedLogRootRequest, opts ...grpc.CallOption) (*trillian.GetLatestSignedLogRootResponse, error) { resp, err := c.TrillianLogClient.GetLatestSignedLogRoot(ctx, in) if c.mutateRootSize { var root types.LogRootV1 if err := root.UnmarshalBinary(resp.SignedLogRoot.LogRoot); err != nil { panic("failed to unmarshal") } root.TreeSize += 10000 resp.SignedLogRoot.LogRoot, _ = root.MarshalBinary() } return resp, err } // GetInclusionProof forwards requests and optionally corrupts the response. func (c *MutatingLogClient) GetInclusionProof(ctx context.Context, in *trillian.GetInclusionProofRequest, opts ...grpc.CallOption) (*trillian.GetInclusionProofResponse, error) { resp, err := c.TrillianLogClient.GetInclusionProof(ctx, in) if c.mutateInclusionProof { i := rand.Intn(len(resp.Proof.Hashes)) j := rand.Intn(len(resp.Proof.Hashes[i])) resp.Proof.Hashes[i][j] ^= 4 } return resp, err } // GetInclusionProofByHash forwards requests and optionaly corrupts responses. func (c *MutatingLogClient) GetInclusionProofByHash(ctx context.Context, in *trillian.GetInclusionProofByHashRequest, opts ...grpc.CallOption) (*trillian.GetInclusionProofByHashResponse, error) { resp, err := c.TrillianLogClient.GetInclusionProofByHash(ctx, in) if err != nil { return nil, err } if c.mutateInclusionProof { h := rand.Intn(len(resp.Proof)) if len(resp.Proof[h].Hashes) == 0 { klog.Warningf("Inclusion proof not modified because treesize = 0") return resp, nil } i := rand.Intn(len(resp.Proof[h].Hashes)) j := rand.Intn(len(resp.Proof[h].Hashes[i])) resp.Proof[h].Hashes[i][j] ^= 4 } return resp, nil } // GetConsistencyProof forwards requests and optionally corrupts responses. func (c *MutatingLogClient) GetConsistencyProof(ctx context.Context, in *trillian.GetConsistencyProofRequest, opts ...grpc.CallOption) (*trillian.GetConsistencyProofResponse, error) { resp, err := c.TrillianLogClient.GetConsistencyProof(ctx, in) if err != nil { return nil, err } if c.mutateConsistencyProof { if len(resp.Proof.Hashes) == 0 { klog.Warningf("Consistency proof not modified because len(Hashes) = 0") return resp, nil } i := rand.Intn(len(resp.Proof.Hashes)) j := rand.Intn(len(resp.Proof.Hashes[i])) resp.Proof.Hashes[i][j] ^= 4 } return resp, nil } trillian-1.6.1/client/rpcflags/000077500000000000000000000000001466362047600164325ustar00rootroot00000000000000trillian-1.6.1/client/rpcflags/rpcflags.go000066400000000000000000000033161466362047600205650ustar00rootroot00000000000000// Copyright 2018 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 rpcflags defines flags for configuring RPC clients. package rpcflags import ( "flag" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials/insecure" "k8s.io/klog/v2" ) // tlsCertFile is the flag-assigned value for the path to the Trillian server's TLS certificate. var tlsCertFile = flag.String("tls_cert_file", "", "Path to the file containing the Trillian server's PEM-encoded public TLS certificate. If unset, unsecured connections will be used") // NewClientDialOptionsFromFlags returns a list of grpc.DialOption values to be // passed as DialOption arguments to grpc.Dial func NewClientDialOptionsFromFlags() ([]grpc.DialOption, error) { dialOpts := []grpc.DialOption{} if *tlsCertFile == "" { klog.Warning("Using an insecure gRPC connection to Trillian") dialOpts = append(dialOpts, grpc.WithTransportCredentials(insecure.NewCredentials())) } else { creds, err := credentials.NewClientTLSFromFile(*tlsCertFile, "") if err != nil { return nil, err } dialOpts = append(dialOpts, grpc.WithTransportCredentials(creds)) } return dialOpts, nil } trillian-1.6.1/client/rpcflags/rpcflags_test.go000066400000000000000000000112601466362047600216210ustar00rootroot00000000000000// Copyright 2018 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 rpcflags import ( "context" "encoding/pem" "flag" "os" "testing" "github.com/google/trillian" "github.com/google/trillian/testonly/flagsaver" "github.com/google/trillian/testonly/integration" "github.com/google/trillian/testonly/setup" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials/insecure" ) func TestNewClientDialOptionsFromFlagsWithTLSCertFileNotSet(t *testing.T) { // Set up Trillian servers const numSequencers = 2 serverOpts := []grpc.ServerOption{} clientOpts := []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())} logEnv, err := integration.NewLogEnvWithGRPCOptions(context.Background(), numSequencers, serverOpts, clientOpts) if err != nil { t.Fatal(err) } defer logEnv.Close() dialOpts, err := NewClientDialOptionsFromFlags() if err != nil { t.Errorf("Got an unexpected error: %v", err) } // Check that the returned dial options can be used to connect and make // requests against the Trillian services created above. conn, err := grpc.Dial(logEnv.Address, dialOpts...) if err != nil { t.Errorf("failed to dial %v: %v", logEnv.Address, err) } defer func() { if err := conn.Close(); err != nil { t.Error(err) } }() adminClient := trillian.NewTrillianAdminClient(conn) if _, err = adminClient.ListTrees(context.Background(), &trillian.ListTreesRequest{}); err != nil { t.Errorf("failed to request trees from the Admin Server: %v", err) } } func TestNewClientDialOptionsFromFlagsWithTLSCertFileMissing(t *testing.T) { defer flagsaver.Save().MustRestore() if err := flag.Set("tls_cert_file", "/a/missing/file"); err != nil { t.Errorf("Failed to set flag: %v", err) } dialOpts, err := NewClientDialOptionsFromFlags() if err == nil { t.Errorf("Expected to get an error due to the file not being found") } if _, ok := err.(*os.PathError); !ok { t.Errorf("Expected to get an os.PathError due to the file not being found, instead got: %v", err) } if dialOpts != nil { t.Errorf("Expected returned dialOpts to be nil, instead got: %v", dialOpts) } } func TestNewClientDialOptionsFromFlagsWithTLSCertFileSet(t *testing.T) { defer flagsaver.Save().MustRestore() // Create new TLS certificates for the test services, and write the Client // certificate to a file (so we can refer to it using the flag). crtFile, cleanupCrtFile := setup.TempFile(t, "test.crt.") defer cleanupCrtFile() tlsCert := setup.NewTLSCertificate(t) // Certificate file and client dial options. err := pem.Encode(crtFile, &pem.Block{Type: "CERTIFICATE", Bytes: tlsCert.Certificate[0]}) if err != nil { t.Fatalf("Failed to encode the test TLS certificate %v", err) } clientCreds, err := credentials.NewClientTLSFromFile(crtFile.Name(), "") if err != nil { t.Fatalf("Failed to get credentials: %v", err) } // Set up Trillian servers (with TLS enabled) const numSequencers = 0 // we don't actually need any sequencers. serverCreds := credentials.NewServerTLSFromCert(&tlsCert) serverOpts := []grpc.ServerOption{grpc.Creds(serverCreds)} clientOpts := []grpc.DialOption{grpc.WithTransportCredentials(clientCreds)} logEnv, err := integration.NewLogEnvWithGRPCOptions(context.Background(), numSequencers, serverOpts, clientOpts) if err != nil { t.Fatal(err) } defer logEnv.Close() // Set up the flag. err = flag.Set("tls_cert_file", crtFile.Name()) if err != nil { t.Errorf("Failed to set -tls_cert_file flag: %v", err) } dialOpts, err := NewClientDialOptionsFromFlags() if err != nil { t.Errorf("Got an unexpected error: %v", err) } // Check that the returned dial options can be used to connect and make // requests against the Trillian services created above. conn, err := grpc.Dial(logEnv.Address, dialOpts...) if err != nil { t.Errorf("failed to dial %v: %v", logEnv.Address, err) } defer func() { if err := conn.Close(); err != nil { t.Error(err) } }() adminClient := trillian.NewTrillianAdminClient(conn) if _, err := adminClient.ListTrees(context.Background(), &trillian.ListTreesRequest{}); err != nil { t.Errorf("failed to request trees from the Admin Server: %v", err) } } trillian-1.6.1/cloudbuild.yaml000066400000000000000000000120461466362047600163700ustar00rootroot00000000000000# This file contains Google Cloud Build configuration for presubmit checks, unit # and integration tests, triggered by pull requests and commits to branches. timeout: 1800s substitutions: _CODECOV_TOKEN: "" # The auth token for uploading coverage to Codecov. options: machineType: E2_HIGHCPU_32 volumes: # A shared volume for caching Go modules between steps. - name: go-modules path: /go env: - GOPATH=/go - GOLANG_PROTOBUF_REGISTRATION_CONFLICT=ignore # Temporary work-around v1.proto already registered error. - DOCKER_CLIENT_TIMEOUT=120 - COMPOSE_HTTP_TIMEOUT=120 # Cache the testbase image in Container Regisrty, to be reused by subsequent # builds. The technique is described here: # https://cloud.google.com/cloud-build/docs/speeding-up-builds#using_a_cached_docker_image # # TODO(pavelkalinnikov): Consider pushing this image only on commits to master. images: ['gcr.io/$PROJECT_ID/trillian_testbase:latest'] # Cloud Build logs sent to GCS bucket logsBucket: 'gs://trillian-cloudbuild-logs' steps: # Try to pull the testbase image from Container Registry. - name: 'gcr.io/cloud-builders/docker' entrypoint: 'bash' args: ['-c', 'docker pull gcr.io/$PROJECT_ID/trillian_testbase:latest || exit 0'] # Build the testbase image reusing as much of the cached image as possible. - name: 'gcr.io/cloud-builders/docker' args: [ 'build', '-t', 'gcr.io/$PROJECT_ID/trillian_testbase:latest', '--cache-from', 'gcr.io/$PROJECT_ID/trillian_testbase:latest', '-f', './integration/cloudbuild/testbase/Dockerfile', '.' ] # Set up tools and any other common steps which should not be part of Docker image. - id: prepare name: 'gcr.io/${PROJECT_ID}/trillian_testbase' entrypoint: ./integration/cloudbuild/prepare.sh # Run porcelain checks, make sure the diff is empty and no files need # to be updated. This includes gofmt, go mod tidy, go mod generate # and a few more. - id: check name: 'gcr.io/${PROJECT_ID}/trillian_testbase' entrypoint: ./scripts/presubmit.sh args: - --no-build - --no-linters - --fix - --no-mod-tidy - --empty-diff waitFor: - prepare # Presubmit - id: presubmit name: 'gcr.io/${PROJECT_ID}/trillian_testbase' entrypoint: ./integration/cloudbuild/run_presubmit.sh args: - --no-linters - --no-generate env: - GOFLAGS=-race - GO_TEST_TIMEOUT=20m waitFor: - check # Codecov - id: codecov name: 'gcr.io/${PROJECT_ID}/trillian_testbase' entrypoint: ./integration/cloudbuild/run_presubmit.sh args: - --coverage - --no-linters - --no-generate env: - GOFLAGS=-race - GO_TEST_TIMEOUT=20m - CODECOV_TOKEN=${_CODECOV_TOKEN} waitFor: - check # Presubmit (Batched queue) - id: presubmit_batched name: 'gcr.io/${PROJECT_ID}/trillian_testbase' entrypoint: ./integration/cloudbuild/run_presubmit.sh args: - --no-linters - --no-generate env: - GOFLAGS=-race --tags=batched_queue - GO_TEST_TIMEOUT=20m waitFor: - check # Presubmit (PKCS11) - id: presubmit_pkcs11 name: 'gcr.io/${PROJECT_ID}/trillian_testbase' entrypoint: ./integration/cloudbuild/run_presubmit.sh args: - --no-linters - --no-generate env: - GOFLAGS=-race --tags=pkcs11 - GO_TEST_TIMEOUT=20m waitFor: - check # Try to spread the load a bit, we'll wait for all the presubmit.* steps # to finish before starting the integration.* ones. # Having too many "big" things running concurrently leads to problems # with timeouts and mysql issues. - id: presubmits_done name: 'gcr.io/${PROJECT_ID}/trillian_testbase' entrypoint: /bin/true waitFor: - codecov - presubmit - presubmit_batched - presubmit_pkcs11 # Integration - id: integration name: 'gcr.io/${PROJECT_ID}/trillian_testbase' entrypoint: ./integration/cloudbuild/run_integration.sh env: - GO_TEST_TIMEOUT=20m waitFor: - presubmits_done # Integration (Docker) - id: integration_docker name: 'gcr.io/${PROJECT_ID}/trillian_testbase' entrypoint: ./integration/docker_compose_integration_test.sh waitFor: - presubmits_done # Integration (etcd) - id: integration_etcd name: 'gcr.io/${PROJECT_ID}/trillian_testbase' entrypoint: ./integration/cloudbuild/run_integration.sh env: - ETCD_DIR=/go/bin - GOFLAGS=-race - GO_TEST_TIMEOUT=20m waitFor: - presubmits_done # Integration (Batched queue) - id: integration_batched name: 'gcr.io/${PROJECT_ID}/trillian_testbase' entrypoint: ./integration/cloudbuild/run_integration.sh env: - GOFLAGS=-race -tags=batched_queue - GO_TEST_TIMEOUT=20m waitFor: - presubmits_done # Integration (PKCS11) - id: integration_pkcs11 name: 'gcr.io/${PROJECT_ID}/trillian_testbase' entrypoint: ./integration/cloudbuild/run_integration.sh env: - GOFLAGS=-race -tags=pkcs11 - GO_TEST_TIMEOUT=20m waitFor: - presubmits_done # Integration (MariaDB) - id: integration_mariadb name: 'gcr.io/${PROJECT_ID}/trillian_testbase' entrypoint: ./integration/cloudbuild/run_integration.sh env: - GO_TEST_TIMEOUT=20m - MYSQLD_IMAGE=mariadb:11.1 waitFor: - presubmits_done trillian-1.6.1/cloudbuild_master.yaml000066400000000000000000000106231466362047600177420ustar00rootroot00000000000000timeout: 1800s substitutions: _CLUSTER_NAME: trillian-opensource-ci _MASTER_ZONE: us-central1-a _MYSQL_TAG: "8.0" _MYSQL_ROOT_PASSWORD: "" _MYSQL_PASSWORD: "" options: machineType: E2_HIGHCPU_32 steps: - id: build_db_server name: gcr.io/kaniko-project/executor:v1.20.0 args: - --dockerfile=examples/deployment/docker/db_server/Dockerfile - --destination=gcr.io/${PROJECT_ID}/db_server:${COMMIT_SHA} - --destination=gcr.io/${PROJECT_ID}/db_server:latest - --cache=true - --cache-dir= # Cache is in Google Container Registry - id: build_log_server name: gcr.io/kaniko-project/executor:v1.20.0 args: - --dockerfile=examples/deployment/docker/log_server/Dockerfile - --destination=gcr.io/${PROJECT_ID}/log_server:${COMMIT_SHA} - --destination=gcr.io/${PROJECT_ID}/log_server:latest - --cache=true - --cache-dir= # Cache is in Google Container Registry waitFor: ["-"] - id: build_log_signer name: gcr.io/kaniko-project/executor:v1.20.0 args: - --dockerfile=examples/deployment/docker/log_signer/Dockerfile - --destination=gcr.io/${PROJECT_ID}/log_signer:${COMMIT_SHA} - --destination=gcr.io/${PROJECT_ID}/log_signer:latest - --cache=true - --cache-dir= # Cache is in Google Container Registry waitFor: ["-"] - id: build_envsubst name: gcr.io/cloud-builders/docker args: - build - examples/deployment/docker/envsubst - -t - envsubst waitFor: ["-"] # etcd-operator requires that a ClusterRole has been created for it already. # Do this manually using examples/deployment/kubernetes/etcd-role*.yaml. - id: apply_k8s_cfgs_for_clusterwide_etcd_operator name: gcr.io/cloud-builders/kubectl args: - apply - -f=examples/deployment/kubernetes/etcd-deployment.yaml env: - CLOUDSDK_COMPUTE_ZONE=${_MASTER_ZONE} - CLOUDSDK_CONTAINER_CLUSTER=${_CLUSTER_NAME} waitFor: ["-"] - id: copy_k8s_cfgs_for_spanner name: busybox entrypoint: cp args: - -r - examples/deployment/kubernetes/ - envsubst-spanner/ waitFor: ['-'] - id: envsubst_k8s_cfgs_for_spanner name: envsubst args: - envsubst-spanner/etcd-cluster.yaml - envsubst-spanner/trillian-ci-spanner.yaml - envsubst-spanner/trillian-log-deployment.yaml - envsubst-spanner/trillian-log-service.yaml - envsubst-spanner/trillian-log-signer-deployment.yaml - envsubst-spanner/trillian-log-signer-service.yaml env: - PROJECT_ID=${PROJECT_ID} - IMAGE_TAG=${COMMIT_SHA} waitFor: - build_envsubst - copy_k8s_cfgs_for_spanner - id: apply_k8s_cfgs_for_spanner name: gcr.io/cloud-builders/kubectl args: - apply - -f=envsubst-spanner/etcd-cluster.yaml - -f=envsubst-spanner/trillian-ci-spanner.yaml - -f=envsubst-spanner/trillian-log-deployment.yaml - -f=envsubst-spanner/trillian-log-service.yaml - -f=envsubst-spanner/trillian-log-signer-deployment.yaml - -f=envsubst-spanner/trillian-log-signer-service.yaml env: - CLOUDSDK_COMPUTE_ZONE=${_MASTER_ZONE} - CLOUDSDK_CONTAINER_CLUSTER=${_CLUSTER_NAME} waitFor: - envsubst_k8s_cfgs_for_spanner - build_log_server - build_log_signer - id: copy_k8s_cfgs_for_mysql name: busybox entrypoint: cp args: - -r - examples/deployment/kubernetes/ - envsubst-mysql/ waitFor: ['-'] - id: envsubst_k8s_cfgs_for_mysql name: envsubst args: - envsubst-mysql/etcd-cluster.yaml - envsubst-mysql/trillian-ci-mysql.yaml - envsubst-mysql/trillian-mysql.yaml - envsubst-mysql/trillian-log-deployment.yaml - envsubst-mysql/trillian-log-service.yaml - envsubst-mysql/trillian-log-signer-deployment.yaml - envsubst-mysql/trillian-log-signer-service.yaml env: - PROJECT_ID=${PROJECT_ID} - IMAGE_TAG=${COMMIT_SHA} - MYSQL_ROOT_PASSWORD=${_MYSQL_ROOT_PASSWORD} - MYSQL_USER=trillian - MYSQL_PASSWORD=${_MYSQL_PASSWORD} - MYSQL_DATABASE=trillian waitFor: - build_envsubst - copy_k8s_cfgs_for_mysql - id: apply_k8s_cfgs_for_mysql name: gcr.io/cloud-builders/kubectl args: - apply - --namespace=mysql - -f=envsubst-mysql/etcd-cluster.yaml - -f=envsubst-mysql/trillian-ci-mysql.yaml - -f=envsubst-mysql/trillian-mysql.yaml - -f=envsubst-mysql/trillian-log-deployment.yaml - -f=envsubst-mysql/trillian-log-service.yaml - -f=envsubst-mysql/trillian-log-signer-deployment.yaml - -f=envsubst-mysql/trillian-log-signer-service.yaml env: - CLOUDSDK_COMPUTE_ZONE=${_MASTER_ZONE} - CLOUDSDK_CONTAINER_CLUSTER=${_CLUSTER_NAME} waitFor: - envsubst_k8s_cfgs_for_mysql - build_db_server - build_log_server - build_log_signer trillian-1.6.1/cloudbuild_pr.yaml000066400000000000000000000105321466362047600170670ustar00rootroot00000000000000# This file contains configuration for Cloud Builds triggered by pull requests # to this repository. timeout: 1800s substitutions: _CLUSTER_NAME: trillian-opensource-ci _MASTER_ZONE: us-central1-a _MYSQL_TAG: "8.0" _MYSQL_ROOT_PASSWORD: "" _MYSQL_PASSWORD: "" options: machineType: E2_HIGHCPU_32 steps: - id: build_db_server name: gcr.io/kaniko-project/executor:v1.20.0 args: - --dockerfile=examples/deployment/docker/db_server/Dockerfile - --destination=gcr.io/${PROJECT_ID}/db_server:${COMMIT_SHA} - --cache=true - --cache-dir= # Cache is in Google Container Registry. - id: build_log_server name: gcr.io/kaniko-project/executor:v1.20.0 args: - --dockerfile=examples/deployment/docker/log_server/Dockerfile - --destination=gcr.io/${PROJECT_ID}/log_server:${COMMIT_SHA} - --cache=true - --cache-dir= # Cache is in Google Container Registry waitFor: ['-'] - id: build_log_signer name: gcr.io/kaniko-project/executor:v1.20.0 args: - --dockerfile=examples/deployment/docker/log_signer/Dockerfile - --destination=gcr.io/${PROJECT_ID}/log_signer:${COMMIT_SHA} - --cache=true - --cache-dir= # Cache is in Google Container Registry waitFor: ['-'] - id: build_envsubst name: gcr.io/cloud-builders/docker args: - build - examples/deployment/docker/envsubst - -t - envsubst waitFor: ["-"] - id: apply_k8s_cfgs_for_clusterwide_etcd_operator_dryrun name: gcr.io/cloud-builders/kubectl args: - apply - --dry-run=server - -f=examples/deployment/kubernetes/etcd-deployment.yaml env: - CLOUDSDK_COMPUTE_ZONE=${_MASTER_ZONE} - CLOUDSDK_CONTAINER_CLUSTER=${_CLUSTER_NAME} waitFor: ['-'] - id: copy_k8s_cfgs_for_spanner name: busybox entrypoint: cp args: - -r - examples/deployment/kubernetes/ - envsubst-spanner/ waitFor: ['-'] - id: envsubst_k8s_cfgs_for_spanner name: envsubst args: - envsubst-spanner/etcd-cluster.yaml - envsubst-spanner/trillian-ci-spanner.yaml - envsubst-spanner/trillian-log-deployment.yaml - envsubst-spanner/trillian-log-service.yaml - envsubst-spanner/trillian-log-signer-deployment.yaml - envsubst-spanner/trillian-log-signer-service.yaml env: - PROJECT_ID=${PROJECT_ID} - IMAGE_TAG=${COMMIT_SHA} waitFor: - build_envsubst - copy_k8s_cfgs_for_spanner - id: apply_k8s_cfgs_for_spanner_dryrun name: gcr.io/cloud-builders/kubectl args: - apply - --dry-run=server - -f=envsubst-spanner/etcd-cluster.yaml - -f=envsubst-spanner/trillian-ci-spanner.yaml - -f=envsubst-spanner/trillian-log-deployment.yaml - -f=envsubst-spanner/trillian-log-service.yaml - -f=envsubst-spanner/trillian-log-signer-deployment.yaml - -f=envsubst-spanner/trillian-log-signer-service.yaml - --prune - --all - --prune-allowlist=core/v1/ConfigMap env: - CLOUDSDK_COMPUTE_ZONE=${_MASTER_ZONE} - CLOUDSDK_CONTAINER_CLUSTER=${_CLUSTER_NAME} waitFor: - envsubst_k8s_cfgs_for_spanner - build_log_server - build_log_signer - id: copy_k8s_cfgs_for_mysql name: busybox entrypoint: cp args: - -r - examples/deployment/kubernetes/ - envsubst-mysql/ waitFor: ['-'] - id: envsubst_k8s_cfgs_for_mysql name: envsubst args: - envsubst-mysql/etcd-cluster.yaml - envsubst-mysql/trillian-ci-mysql.yaml - envsubst-mysql/trillian-mysql.yaml - envsubst-mysql/trillian-log-deployment.yaml - envsubst-mysql/trillian-log-service.yaml - envsubst-mysql/trillian-log-signer-deployment.yaml - envsubst-mysql/trillian-log-signer-service.yaml env: - PROJECT_ID=${PROJECT_ID} - IMAGE_TAG=${COMMIT_SHA} - MYSQL_ROOT_PASSWORD=${_MYSQL_ROOT_PASSWORD} - MYSQL_PASSWORD=${_MYSQL_PASSWORD} waitFor: - build_envsubst - copy_k8s_cfgs_for_mysql - id: apply_k8s_cfgs_for_mysql_dryrun name: gcr.io/cloud-builders/kubectl args: - apply - --dry-run=server - --namespace=mysql - -f=envsubst-mysql/etcd-cluster.yaml - -f=envsubst-mysql/trillian-ci-mysql.yaml - -f=envsubst-mysql/trillian-mysql.yaml - -f=envsubst-mysql/trillian-log-deployment.yaml - -f=envsubst-mysql/trillian-log-service.yaml - -f=envsubst-mysql/trillian-log-signer-deployment.yaml - -f=envsubst-mysql/trillian-log-signer-service.yaml - --prune - --all - --prune-allowlist=core/v1/ConfigMap env: - CLOUDSDK_COMPUTE_ZONE=${_MASTER_ZONE} - CLOUDSDK_CONTAINER_CLUSTER=${_CLUSTER_NAME} waitFor: - envsubst_k8s_cfgs_for_mysql - build_db_server - build_log_server - build_log_signer trillian-1.6.1/cloudbuild_tag.yaml000066400000000000000000000017061466362047600172240ustar00rootroot00000000000000timeout: 1800s substitutions: _MYSQL_TAG: "8.0" options: machineType: E2_HIGHCPU_32 steps: - id: build_db_server name: gcr.io/kaniko-project/executor:v1.20.0 args: - --dockerfile=examples/deployment/docker/db_server/Dockerfile - --destination=gcr.io/${PROJECT_ID}/db_server:${TAG_NAME} - --cache=true - --cache-dir= # Cache is in Google Container Registry - id: build_log_server name: gcr.io/kaniko-project/executor:v1.20.0 args: - --dockerfile=examples/deployment/docker/log_server/Dockerfile - --destination=gcr.io/${PROJECT_ID}/log_server:${TAG_NAME} - --cache=true - --cache-dir= # Cache is in Google Container Registry waitFor: ["-"] - id: build_log_signer name: gcr.io/kaniko-project/executor:v1.20.0 args: - --dockerfile=examples/deployment/docker/log_signer/Dockerfile - --destination=gcr.io/${PROJECT_ID}/log_signer:${TAG_NAME} - --cache=true - --cache-dir= # Cache is in Google Container Registry waitFor: ["-"] trillian-1.6.1/cmd/000077500000000000000000000000001466362047600141165ustar00rootroot00000000000000trillian-1.6.1/cmd/createtree/000077500000000000000000000000001466362047600162415ustar00rootroot00000000000000trillian-1.6.1/cmd/createtree/main.go000066400000000000000000000105451466362047600175210ustar00rootroot00000000000000// 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 main contains the implementation and entry point for the createtree // command. // // Example usage: // $ ./createtree --admin_server=host:port // // The command outputs the tree ID of the created tree to stdout, or an error to // stderr in case of failure. The output is minimal to allow for easy usage in // automated scripts. // // Several flags are provided to configure the create tree, most of which try to // assume reasonable defaults. package main import ( "context" "errors" "flag" "fmt" "time" "github.com/google/trillian" "github.com/google/trillian/client" "github.com/google/trillian/client/rpcflags" "github.com/google/trillian/cmd" "google.golang.org/grpc" "google.golang.org/protobuf/types/known/durationpb" "k8s.io/klog/v2" ) var ( adminServerAddr = flag.String("admin_server", "", "Address of the gRPC Trillian Admin Server (host:port)") rpcDeadline = flag.Duration("rpc_deadline", time.Second*10, "Deadline for RPC requests") treeState = flag.String("tree_state", trillian.TreeState_ACTIVE.String(), "State of the new tree") treeType = flag.String("tree_type", trillian.TreeType_LOG.String(), "Type of the new tree") displayName = flag.String("display_name", "", "Display name of the new tree") description = flag.String("description", "", "Description of the new tree") maxRootDuration = flag.Duration("max_root_duration", time.Hour, "Interval after which a new signed root is produced despite no submissions; zero means never") configFile = flag.String("config", "", "Config file containing flags, file contents can be overridden by command line flags") errAdminAddrNotSet = errors.New("empty --admin_server, please provide the Admin server host:port") ) // TODO(Martin2112): Pass everything needed into this and don't refer to flags. func createTree(ctx context.Context) (*trillian.Tree, error) { if *adminServerAddr == "" { return nil, errAdminAddrNotSet } req, err := newRequest() if err != nil { return nil, err } dialOpts, err := rpcflags.NewClientDialOptionsFromFlags() if err != nil { return nil, fmt.Errorf("failed to determine dial options: %v", err) } conn, err := grpc.Dial(*adminServerAddr, dialOpts...) if err != nil { return nil, fmt.Errorf("failed to dial %v: %v", *adminServerAddr, err) } defer func() { if err := conn.Close(); err != nil { klog.Errorf("Close(): %v", err) } }() adminClient := trillian.NewTrillianAdminClient(conn) logClient := trillian.NewTrillianLogClient(conn) return client.CreateAndInitTree(ctx, req, adminClient, logClient) } func newRequest() (*trillian.CreateTreeRequest, error) { ts, ok := trillian.TreeState_value[*treeState] if !ok { return nil, fmt.Errorf("unknown TreeState: %v", *treeState) } tt, ok := trillian.TreeType_value[*treeType] if !ok { return nil, fmt.Errorf("unknown TreeType: %v", *treeType) } ctr := &trillian.CreateTreeRequest{Tree: &trillian.Tree{ TreeState: trillian.TreeState(ts), TreeType: trillian.TreeType(tt), DisplayName: *displayName, Description: *description, MaxRootDuration: durationpb.New(*maxRootDuration), }} klog.Infof("Creating tree %+v", ctr.Tree) return ctr, nil } func main() { klog.InitFlags(nil) flag.Parse() defer klog.Flush() if *configFile != "" { if err := cmd.ParseFlagFile(*configFile); err != nil { klog.Exitf("Failed to load flags from config file %q: %s", *configFile, err) } } ctx, cancel := context.WithTimeout(context.Background(), *rpcDeadline) defer cancel() tree, err := createTree(ctx) if err != nil { klog.Exitf("Failed to create tree: %v", err) } // DO NOT change the output format, scripts are meant to depend on it. // If you really want to change it, provide an output_format flag and // keep the default as-is. fmt.Println(tree.TreeId) } trillian-1.6.1/cmd/createtree/main_test.go000066400000000000000000000125701466362047600205600ustar00rootroot00000000000000// 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 main import ( "context" "errors" "testing" "time" "github.com/golang/mock/gomock" "github.com/google/trillian" "github.com/google/trillian/testonly" "github.com/google/trillian/testonly/flagsaver" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/durationpb" ) // defaultTree reflects all flag defaults with the addition of a valid private key. var defaultTree = &trillian.Tree{ TreeState: trillian.TreeState_ACTIVE, TreeType: trillian.TreeType_LOG, MaxRootDuration: durationpb.New(0 * time.Millisecond), } type testCase struct { desc string setFlags func() validateErr error createErr error initErr error wantErr bool wantTree *trillian.Tree } func TestCreateTree(t *testing.T) { nonDefaultTree := proto.Clone(defaultTree).(*trillian.Tree) nonDefaultTree.TreeType = trillian.TreeType_LOG nonDefaultTree.DisplayName = "Llamas Log" nonDefaultTree.Description = "For all your digital llama needs!" runTest(t, []*testCase{ { desc: "validOpts", // runTest sets mandatory options, so no need to provide a setFlags func. wantTree: defaultTree, }, { desc: "nonDefaultOpts", setFlags: func() { *treeType = nonDefaultTree.TreeType.String() *displayName = nonDefaultTree.DisplayName *description = nonDefaultTree.Description }, wantTree: nonDefaultTree, }, { desc: "mandatoryOptsNotSet", // Undo the flags set by runTest, so that mandatory options are no longer set. setFlags: flagsaver.Save().MustRestore, validateErr: errAdminAddrNotSet, wantErr: true, }, { desc: "emptyAddr", setFlags: func() { *adminServerAddr = "" }, validateErr: errAdminAddrNotSet, wantErr: true, }, { desc: "invalidEnumOpts", setFlags: func() { *treeType = "LLAMA!" }, validateErr: errors.New("unknown TreeType"), wantErr: true, }, { desc: "createErr", createErr: status.Errorf(codes.Unavailable, "create tree failed"), wantErr: true, }, { desc: "logInitErr", setFlags: func() { nonDefaultTree.TreeType = trillian.TreeType_LOG *treeType = nonDefaultTree.TreeType.String() }, wantTree: defaultTree, initErr: status.Errorf(codes.Unavailable, "log init failed"), wantErr: true, }, }) } // runTest executes the createtree command against a fake TrillianAdminServer // for each of the provided tests, and checks that the tree in the request is // as expected, or an expected error occurs. // Prior to each test case, it: // 1. Resets all flags to their original values. // 2. Sets the adminServerAddr flag to point to the fake server. // 3. Calls the test's setFlags func (if provided) to allow it to change flags specific to the test. func runTest(t *testing.T, tests []*testCase) { for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { // Note: Restore() must be called after the flag-reading bits are // stopped, otherwise there might be a data race. defer flagsaver.Save().MustRestore() ctrl := gomock.NewController(t) defer ctrl.Finish() s, stopFakeServer, err := testonly.NewMockServer(ctrl) if err != nil { t.Fatalf("Error starting fake server: %v", err) } defer stopFakeServer() *adminServerAddr = s.Addr if tc.setFlags != nil { tc.setFlags() } call := s.Admin.EXPECT().CreateTree(gomock.Any(), gomock.Any()).Return(tc.wantTree, tc.createErr) expectCalls(call, tc.createErr, tc.validateErr) switch *treeType { case "LOG": call := s.Log.EXPECT().InitLog(gomock.Any(), gomock.Any()).Return(&trillian.InitLogResponse{}, tc.initErr) expectCalls(call, tc.initErr, tc.validateErr, tc.createErr) call = s.Log.EXPECT().GetLatestSignedLogRoot(gomock.Any(), gomock.Any()).Return(&trillian.GetLatestSignedLogRootResponse{}, nil) expectCalls(call, nil, tc.validateErr, tc.createErr, tc.initErr) } ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() _, err = createTree(ctx) if hasErr := err != nil; hasErr != tc.wantErr { t.Errorf("createTree() '%v', wantErr = %v", err, tc.wantErr) } }) } } // expectCalls returns the minimum number of times a function is expected to be called // given the return error for the function (err), and all previous errors in the function's // code path. func expectCalls(call *gomock.Call, err error, prevErr ...error) *gomock.Call { // If a function prior to this function errored, // we do not expect this function to be called. for _, e := range prevErr { if e != nil { return call.Times(0) } } // If this function errors, it will be retried multiple times. if err != nil { return call.MinTimes(2) } // If this function succeeds it should only be called once. return call.Times(1) } trillian-1.6.1/cmd/deletetree/000077500000000000000000000000001466362047600162405ustar00rootroot00000000000000trillian-1.6.1/cmd/deletetree/main.go000066400000000000000000000040271466362047600175160ustar00rootroot00000000000000// Copyright 2018 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 main contains the implementation and entry point for the deletetree // command. // // Example usage: // $ ./deletetree --admin_server=host:port --log_id=logid package main import ( "context" "flag" "github.com/google/trillian" "github.com/google/trillian/client/rpcflags" "google.golang.org/grpc" "k8s.io/klog/v2" ) var ( adminServerAddr = flag.String("admin_server", "", "Address of the gRPC Trillian Admin Server (host:port)") logID = flag.Int64("log_id", 0, "Trillian LogID to delete") undeleteTree = flag.Bool("undelete_tree", false, "Undelete the specified Trillian LogID (bool)") ) func main() { klog.InitFlags(nil) flag.Parse() defer klog.Flush() dialOpts, err := rpcflags.NewClientDialOptionsFromFlags() if err != nil { klog.Exitf("Failed to determine dial options: %v", err) } conn, err := grpc.Dial(*adminServerAddr, dialOpts...) if err != nil { klog.Exitf("Failed to dial %v: %v", *adminServerAddr, err) } defer func() { if err := conn.Close(); err != nil { klog.Errorf("Close(): %v", err) } }() a := trillian.NewTrillianAdminClient(conn) if !*undeleteTree { _, err = a.DeleteTree(context.Background(), &trillian.DeleteTreeRequest{TreeId: *logID}) if err != nil { klog.Exitf("Delete failed: %v", err) } } else { _, err = a.UndeleteTree(context.Background(), &trillian.UndeleteTreeRequest{TreeId: *logID}) if err != nil { klog.Exitf("Undelete failed: %v", err) } } } trillian-1.6.1/cmd/flags.go000066400000000000000000000031251466362047600155420ustar00rootroot00000000000000// 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 cmd contains common code for the various binaries in this repository. package cmd import ( "errors" "flag" "os" "bitbucket.org/creachadair/shell" ) func parseFlags(file string) error { args, valid := shell.Split(file) if !valid { return errors.New("flag file contains unclosed quotations") } // Expand any environment variables in the args for i := range args { args[i] = os.ExpandEnv(args[i]) } if err := flag.CommandLine.Parse(args); err != nil { return err } // Call flag.Parse() again so that command line flags // can override flags provided in the provided flag file. flag.Parse() return nil } // ParseFlagFile parses a set of flags from a file at the provided // path. Re-calls flag.Parse() after parsing the flags in the file // so that flags provided on the command line take precedence over // flags provided in the file. func ParseFlagFile(path string) error { file, err := os.ReadFile(path) if err != nil { return err } return parseFlags(string(file)) } trillian-1.6.1/cmd/flags_test.go000066400000000000000000000054241466362047600166050ustar00rootroot00000000000000// 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 cmd import ( "flag" "os" "testing" _ "k8s.io/klog/v2" ) func TestParseFlags(t *testing.T) { var a, b string flag.StringVar(&a, "a", "", "") flag.StringVar(&b, "b", "", "") flag.CommandLine.Init(os.Args[0], flag.ContinueOnError) tests := []struct { name string contents string env map[string]string cliArgs []string expectedErr string expectedA string expectedB string }{ { name: "two flags per line", contents: "-a one -b two", expectedA: "one", expectedB: "two", }, { name: "one flag per line", contents: "-a one\n-b two", expectedA: "one", expectedB: "two", }, { name: "one flag per line, with line continuation", contents: "-a one \\\n-b two", expectedA: "one", expectedB: "two", }, { name: "one flag in file, one flag on command-line", contents: "-a one", cliArgs: []string{"-b", "two"}, expectedA: "one", expectedB: "two", }, { name: "two flags, one overridden by command-line", contents: "-a one\n-b two", cliArgs: []string{"-b", "three"}, expectedA: "one", expectedB: "three", }, { name: "two flags, one using an environment variable", contents: "-a one\n-b $TEST_VAR", env: map[string]string{"TEST_VAR": "from env"}, expectedA: "one", expectedB: "from env", }, { name: "three flags, one undefined", contents: "-a one -b two -c three", expectedErr: "flag provided but not defined: -c", }, } initialArgs := os.Args[:] for _, tc := range tests { a, b = "", "" os.Args = append(initialArgs, tc.cliArgs...) for k, v := range tc.env { if err := os.Setenv(k, v); err != nil { t.Errorf("%v: os.SetEnv(%q, %q) = %q", tc.name, k, v, err) } } if err := parseFlags(tc.contents); err != nil { if err.Error() != tc.expectedErr { t.Errorf("%v: parseFlags() = %q, want %q", tc.name, err, tc.expectedErr) } continue } if tc.expectedA != a { t.Errorf("%v: flag 'a' not properly set: got %q, want %q", tc.name, a, tc.expectedA) } if tc.expectedB != b { t.Errorf("%v: flag 'b' not properly set: got %q, want %q", tc.name, b, tc.expectedB) } } } trillian-1.6.1/cmd/internal/000077500000000000000000000000001466362047600157325ustar00rootroot00000000000000trillian-1.6.1/cmd/internal/serverutil/000077500000000000000000000000001466362047600201365ustar00rootroot00000000000000trillian-1.6.1/cmd/internal/serverutil/main.go000066400000000000000000000226411466362047600214160ustar00rootroot00000000000000// 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 serverutil holds code for running Trillian servers. package serverutil import ( "context" "errors" "fmt" "net" "net/http" "time" "github.com/google/trillian" "github.com/google/trillian/extension" "github.com/google/trillian/monitoring" "github.com/google/trillian/server/admin" "github.com/google/trillian/server/interceptor" "github.com/google/trillian/util/clock" "github.com/prometheus/client_golang/prometheus/promhttp" "go.etcd.io/etcd/client/v3/naming/endpoints" "golang.org/x/sync/errgroup" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/reflection" "k8s.io/klog/v2" grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware" clientv3 "go.etcd.io/etcd/client/v3" ) const ( // DefaultTreeDeleteThreshold is the suggested threshold for tree deletion. // It represents the minimum time a tree has to remain Deleted before being hard-deleted. DefaultTreeDeleteThreshold = 7 * 24 * time.Hour // DefaultTreeDeleteMinInterval is the suggested min interval between tree GC sweeps. // A tree GC sweep consists of listing deleted trees older than the deletion threshold and // hard-deleting them. // Actual runs happen randomly between [minInterval,2*minInterval). DefaultTreeDeleteMinInterval = 4 * time.Hour ) // Main encapsulates the data and logic to start a Trillian server (Log or Map). type Main struct { // Endpoints for RPC and HTTP servers. // HTTP is optional, if empty it'll not be bound. RPCEndpoint, HTTPEndpoint string // TLS Certificate and Key files for the server. TLSCertFile, TLSKeyFile string DBClose func() error Registry extension.Registry StatsPrefix string QuotaDryRun bool // RegisterServerFn is called to register RPC servers. RegisterServerFn func(*grpc.Server, extension.Registry) error // IsHealthy will be called whenever "/healthz" is called on the mux. // A nil return value from this function will result in a 200-OK response // on the /healthz endpoint. IsHealthy func(context.Context) error // HealthyDeadline is the maximum duration to wait wait for a successful // IsHealthy() call. HealthyDeadline time.Duration // AllowedTreeTypes determines which types of trees may be created through the Admin Server // bound by Main. nil means unrestricted. AllowedTreeTypes []trillian.TreeType TreeGCEnabled bool TreeDeleteThreshold time.Duration TreeDeleteMinInterval time.Duration // These will be added to the GRPC server options. ExtraOptions []grpc.ServerOption } func (m *Main) healthz(rw http.ResponseWriter, req *http.Request) { if m.IsHealthy != nil { ctx, cancel := context.WithTimeout(req.Context(), m.HealthyDeadline) defer cancel() if err := m.IsHealthy(ctx); err != nil { rw.WriteHeader(http.StatusServiceUnavailable) if _, err := rw.Write([]byte(err.Error())); err != nil { klog.Errorf("Write(): %v", err) } return } } if _, err := rw.Write([]byte("ok")); err != nil { klog.Errorf("Write(): %v", err) } } // Run starts the configured server. Blocks until the server exits. func (m *Main) Run(ctx context.Context) error { klog.CopyStandardLogTo("WARNING") if m.HealthyDeadline == 0 { m.HealthyDeadline = 5 * time.Second } srv, err := m.newGRPCServer() if err != nil { klog.Exitf("Error creating gRPC server: %v", err) } defer srv.GracefulStop() defer func() { if err := m.DBClose(); err != nil { klog.Errorf("DBClose(): %v", err) } }() if err := m.RegisterServerFn(srv, m.Registry); err != nil { return err } trillian.RegisterTrillianAdminServer(srv, admin.New(m.Registry, m.AllowedTreeTypes)) reflection.Register(srv) g, ctx := errgroup.WithContext(ctx) if endpoint := m.HTTPEndpoint; endpoint != "" { http.Handle("/metrics", promhttp.Handler()) http.HandleFunc("/healthz", m.healthz) s := &http.Server{ Addr: endpoint, } run := func() error { klog.Infof("HTTP server starting on %v", endpoint) var err error // Let http.ListenAndServeTLS handle the error case when only one of the flags is set. if m.TLSCertFile != "" || m.TLSKeyFile != "" { err = s.ListenAndServeTLS(m.TLSCertFile, m.TLSKeyFile) } else { err = s.ListenAndServe() } if err != nil { if errors.Is(err, http.ErrServerClosed) { return nil } err = fmt.Errorf("HTTP server stopped: %v", err) } return err } shutdown := func() { klog.Infof("Stopping HTTP server...") klog.Flush() // 15 second exit time limit ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) defer cancel() if err := s.Shutdown(ctx); err != nil { klog.Errorf("Failed to http server shutdown: %v", err) } } g.Go(func() error { return srvRun(ctx, run, shutdown) }) } klog.Infof("RPC server starting on %v", m.RPCEndpoint) lis, err := net.Listen("tcp", m.RPCEndpoint) if err != nil { return err } if m.TreeGCEnabled { g.Go(func() error { klog.Info("Deleted tree GC started") gc := admin.NewDeletedTreeGC( m.Registry.AdminStorage, m.TreeDeleteThreshold, m.TreeDeleteMinInterval, m.Registry.MetricFactory) gc.Run(ctx) return nil }) } run := func() error { if err := srv.Serve(lis); err != nil { return fmt.Errorf("RPC server terminated: %v", err) } return nil } shutdown := func() { klog.Infof("Stopping RPC server...") klog.Flush() srv.GracefulStop() } g.Go(func() error { return srvRun(ctx, run, shutdown) }) // wait for all jobs to exit gracefully err = g.Wait() // Give things a few seconds to tidy up time.Sleep(time.Second * 5) return err } // newGRPCServer starts a new Trillian gRPC server. func (m *Main) newGRPCServer() (*grpc.Server, error) { stats := monitoring.NewRPCStatsInterceptor(clock.System, m.StatsPrefix, m.Registry.MetricFactory) ti := interceptor.New(m.Registry.AdminStorage, m.Registry.QuotaManager, m.QuotaDryRun, m.Registry.MetricFactory) serverOpts := []grpc.ServerOption{ grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer( stats.Interceptor(), interceptor.ErrorWrapper, ti.UnaryInterceptor, )), } serverOpts = append(serverOpts, m.ExtraOptions...) // Let credentials.NewServerTLSFromFile handle the error case when only one of the flags is set. if m.TLSCertFile != "" || m.TLSKeyFile != "" { serverCreds, err := credentials.NewServerTLSFromFile(m.TLSCertFile, m.TLSKeyFile) if err != nil { return nil, err } serverOpts = append(serverOpts, grpc.Creds(serverCreds)) } s := grpc.NewServer(serverOpts...) return s, nil } // AnnounceSelf announces this binary's presence to etcd. This calls the cancel // function if the keepalive lease with etcd expires. Returns a function that // should be called on process exit. // AnnounceSelf does nothing if client is nil. func AnnounceSelf(ctx context.Context, client *clientv3.Client, etcdService, endpoint string, cancel func()) func() { if client == nil { return func() {} } // Get a lease so our entry self-destructs. leaseRsp, err := client.Grant(ctx, 30) if err != nil { klog.Exitf("Failed to get lease from etcd: %v", err) } keepAliveRspCh, err := client.KeepAlive(ctx, leaseRsp.ID) if err != nil { klog.Exitf("Failed to keep lease alive from etcd: %v", err) } go listenKeepAliveRsp(ctx, keepAliveRspCh, cancel) em, err := endpoints.NewManager(client, etcdService) if err != nil { klog.Exitf("Failed to create etcd manager: %v", err) } fullEndpoint := fmt.Sprintf("%s/%s", etcdService, endpoint) if err := em.AddEndpoint(ctx, fullEndpoint, endpoints.Endpoint{Addr: endpoint}); err != nil { klog.Exitf("Failed to add endpoint: %v", err) } klog.Infof("Announcing our presence in %v", etcdService) return func() { // Use a background context because the original context may have been cancelled. klog.Infof("Removing our presence in %v", etcdService) ctx := context.Background() if err := em.DeleteEndpoint(ctx, fullEndpoint); err != nil { klog.Exitf("Failed to delete endpoint: %v", err) } if _, err := client.Revoke(ctx, leaseRsp.ID); err != nil { klog.Exitf("Failed to revoke lease: %v", err) } } } // listenKeepAliveRsp listens to `keepAliveRspCh` channel, and calls the cancel function // to notify the lease expired. func listenKeepAliveRsp(ctx context.Context, keepAliveRspCh <-chan *clientv3.LeaseKeepAliveResponse, cancel func()) { for { select { case <-ctx.Done(): klog.Infof("listenKeepAliveRsp canceled: %v", ctx.Err()) return case _, ok := <-keepAliveRspCh: if !ok { klog.Errorf("listenKeepAliveRsp canceled: unexpected lease expired") cancel() return } } } } // srvRun run the server and call `shutdown` when the context has been cancelled func srvRun(ctx context.Context, run func() error, shutdown func()) error { exit := make(chan struct{}) var err error go func() { defer close(exit) err = run() }() select { case <-ctx.Done(): shutdown() // wait for run to return <-exit case <-exit: } return err } trillian-1.6.1/cmd/trillian_log_server/000077500000000000000000000000001466362047600201635ustar00rootroot00000000000000trillian-1.6.1/cmd/trillian_log_server/main.go000066400000000000000000000176611466362047600214510ustar00rootroot00000000000000// 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. // The trillian_log_server binary runs the Trillian log server, and also // provides an admin server. package main import ( "context" "flag" "fmt" _ "net/http/pprof" // Register pprof HTTP handlers. "os" "runtime/pprof" "strings" "time" "github.com/google/trillian" "github.com/google/trillian/cmd" "github.com/google/trillian/cmd/internal/serverutil" "github.com/google/trillian/extension" "github.com/google/trillian/monitoring" "github.com/google/trillian/monitoring/opencensus" "github.com/google/trillian/monitoring/prometheus" "github.com/google/trillian/quota" "github.com/google/trillian/quota/etcd" "github.com/google/trillian/quota/etcd/quotaapi" "github.com/google/trillian/quota/etcd/quotapb" "github.com/google/trillian/server" "github.com/google/trillian/storage" "github.com/google/trillian/util" "github.com/google/trillian/util/clock" clientv3 "go.etcd.io/etcd/client/v3" "google.golang.org/grpc" "k8s.io/klog/v2" // Register supported storage providers. _ "github.com/google/trillian/storage/cloudspanner" _ "github.com/google/trillian/storage/crdb" _ "github.com/google/trillian/storage/mysql" // Load quota providers _ "github.com/google/trillian/quota/crdbqm" _ "github.com/google/trillian/quota/mysqlqm" ) var ( rpcEndpoint = flag.String("rpc_endpoint", "localhost:8090", "Endpoint for RPC requests (host:port)") httpEndpoint = flag.String("http_endpoint", "localhost:8091", "Endpoint for HTTP metrics (host:port, empty means disabled)") healthzTimeout = flag.Duration("healthz_timeout", time.Second*5, "Timeout used during healthz checks") tlsCertFile = flag.String("tls_cert_file", "", "Path to the TLS server certificate. If unset, the server will use unsecured connections.") tlsKeyFile = flag.String("tls_key_file", "", "Path to the TLS server key. If unset, the server will use unsecured connections.") etcdService = flag.String("etcd_service", "trillian-logserver", "Service name to announce ourselves under") etcdHTTPService = flag.String("etcd_http_service", "trillian-logserver-http", "Service name to announce our HTTP endpoint under") quotaSystem = flag.String("quota_system", "mysql", fmt.Sprintf("Quota system to use. One of: %v", quota.Providers())) quotaDryRun = flag.Bool("quota_dry_run", false, "If true no requests are blocked due to lack of tokens") storageSystem = flag.String("storage_system", "mysql", fmt.Sprintf("Storage system to use. One of: %v", storage.Providers())) treeGCEnabled = flag.Bool("tree_gc", true, "If true, tree garbage collection (hard-deletion) is periodically performed") treeDeleteThreshold = flag.Duration("tree_delete_threshold", serverutil.DefaultTreeDeleteThreshold, "Minimum period a tree has to remain deleted before being hard-deleted") treeDeleteMinRunInterval = flag.Duration("tree_delete_min_run_interval", serverutil.DefaultTreeDeleteMinInterval, "Minimum interval between tree garbage collection sweeps. Actual runs happen randomly between [minInterval,2*minInterval).") tracing = flag.Bool("tracing", false, "If true opencensus Stackdriver tracing will be enabled. See https://opencensus.io/.") tracingProjectID = flag.String("tracing_project_id", "", "project ID to pass to stackdriver. Can be empty for GCP, consult docs for other platforms.") tracingPercent = flag.Int("tracing_percent", 0, "Percent of requests to be traced. Zero is a special case to use the DefaultSampler") configFile = flag.String("config", "", "Config file containing flags, file contents can be overridden by command line flags") // Profiling related flags. cpuProfile = flag.String("cpuprofile", "", "If set, write CPU profile to this file") memProfile = flag.String("memprofile", "", "If set, write memory profile to this file") ) func main() { klog.InitFlags(nil) flag.Parse() defer klog.Flush() if *configFile != "" { if err := cmd.ParseFlagFile(*configFile); err != nil { klog.Exitf("Failed to load flags from config file %q: %s", *configFile, err) } } klog.Info("**** Log Server Starting ****") ctx, cancel := context.WithCancel(context.Background()) defer cancel() go util.AwaitSignal(ctx, cancel) var options []grpc.ServerOption mf := prometheus.MetricFactory{} monitoring.SetStartSpan(opencensus.StartSpan) if *tracing { opts, err := opencensus.EnableRPCServerTracing(*tracingProjectID, *tracingPercent) if err != nil { klog.Exitf("Failed to initialize stackdriver / opencensus tracing: %v", err) } // Enable the server request counter tracing etc. options = append(options, opts...) } sp, err := storage.NewProvider(*storageSystem, mf) if err != nil { klog.Exitf("Failed to get storage provider: %v", err) } defer func() { if err := sp.Close(); err != nil { klog.Errorf("Close(): %v", err) } }() var client *clientv3.Client if servers := *etcd.Servers; servers != "" { if client, err = clientv3.New(clientv3.Config{ Endpoints: strings.Split(servers, ","), DialTimeout: 5 * time.Second, }); err != nil { klog.Exitf("Failed to connect to etcd at %v: %v", servers, err) } defer func() { if err := client.Close(); err != nil { klog.Errorf("Close(): %v", err) } }() } // Announce our endpoints to etcd if so configured. unannounce := serverutil.AnnounceSelf(ctx, client, *etcdService, *rpcEndpoint, cancel) defer unannounce() if *httpEndpoint != "" { unannounceHTTP := serverutil.AnnounceSelf(ctx, client, *etcdHTTPService, *httpEndpoint, cancel) defer unannounceHTTP() } qm, err := quota.NewManager(*quotaSystem) if err != nil { klog.Exitf("Error creating quota manager: %v", err) } registry := extension.Registry{ AdminStorage: sp.AdminStorage(), LogStorage: sp.LogStorage(), QuotaManager: qm, MetricFactory: mf, } // Enable CPU profile if requested. if *cpuProfile != "" { f := mustCreate(*cpuProfile) if err := pprof.StartCPUProfile(f); err != nil { klog.Exitf("StartCPUProfile(): %v", err) } defer pprof.StopCPUProfile() } m := serverutil.Main{ RPCEndpoint: *rpcEndpoint, HTTPEndpoint: *httpEndpoint, TLSCertFile: *tlsCertFile, TLSKeyFile: *tlsKeyFile, StatsPrefix: "log", ExtraOptions: options, QuotaDryRun: *quotaDryRun, DBClose: sp.Close, Registry: registry, RegisterServerFn: func(s *grpc.Server, registry extension.Registry) error { logServer := server.NewTrillianLogRPCServer(registry, clock.System) if err := logServer.IsHealthy(); err != nil { return err } trillian.RegisterTrillianLogServer(s, logServer) if *quotaSystem == etcd.QuotaManagerName { quotapb.RegisterQuotaServer(s, quotaapi.NewServer(client)) } return nil }, IsHealthy: func(ctx context.Context) error { as := sp.AdminStorage() return as.CheckDatabaseAccessible(ctx) }, HealthyDeadline: *healthzTimeout, AllowedTreeTypes: []trillian.TreeType{trillian.TreeType_LOG, trillian.TreeType_PREORDERED_LOG}, TreeGCEnabled: *treeGCEnabled, TreeDeleteThreshold: *treeDeleteThreshold, TreeDeleteMinInterval: *treeDeleteMinRunInterval, } if err := m.Run(ctx); err != nil { klog.Exitf("Server exited with error: %v", err) } if *memProfile != "" { f := mustCreate(*memProfile) if err := pprof.WriteHeapProfile(f); err != nil { klog.Exitf("WriteHeapProfile(): %v", err) } } } func mustCreate(fileName string) *os.File { f, err := os.Create(fileName) if err != nil { klog.Fatal(err) } return f } trillian-1.6.1/cmd/trillian_log_signer/000077500000000000000000000000001466362047600201445ustar00rootroot00000000000000trillian-1.6.1/cmd/trillian_log_signer/main.go000066400000000000000000000217071466362047600214260ustar00rootroot00000000000000// 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. // The trillian_log_signer binary runs the process which sequences new entries, // integrates them into the corresponding log, and, finally, creates a new // LogRoot with updated root hash. // // The naming of this binary originally came from the fact that it would // also sign each of the new LogRoots, but that functionality was removed. // Renaming the binary would likely cause some surprise for existing log // operators and so the decision was taken to leave it as-is for now. package main import ( "context" "flag" "fmt" _ "net/http/pprof" // Register pprof HTTP handlers. "os" "runtime/pprof" "strings" "time" "github.com/google/trillian/cmd" "github.com/google/trillian/cmd/internal/serverutil" "github.com/google/trillian/extension" "github.com/google/trillian/log" "github.com/google/trillian/monitoring" "github.com/google/trillian/monitoring/opencensus" "github.com/google/trillian/monitoring/prometheus" "github.com/google/trillian/quota" "github.com/google/trillian/quota/etcd" "github.com/google/trillian/storage" "github.com/google/trillian/util" "github.com/google/trillian/util/clock" "github.com/google/trillian/util/election" "github.com/google/trillian/util/election2" etcdelect "github.com/google/trillian/util/election2/etcd" clientv3 "go.etcd.io/etcd/client/v3" "google.golang.org/grpc" "k8s.io/klog/v2" // Register supported storage providers. _ "github.com/google/trillian/storage/cloudspanner" _ "github.com/google/trillian/storage/crdb" _ "github.com/google/trillian/storage/mysql" // Load quota providers _ "github.com/google/trillian/quota/crdbqm" _ "github.com/google/trillian/quota/mysqlqm" ) var ( rpcEndpoint = flag.String("rpc_endpoint", "localhost:8090", "Endpoint for RPC requests (host:port)") httpEndpoint = flag.String("http_endpoint", "localhost:8091", "Endpoint for HTTP (host:port, empty means disabled)") tlsCertFile = flag.String("tls_cert_file", "", "Path to the TLS server certificate. If unset, the server will use unsecured connections.") tlsKeyFile = flag.String("tls_key_file", "", "Path to the TLS server key. If unset, the server will use unsecured connections.") sequencerIntervalFlag = flag.Duration("sequencer_interval", 100*time.Millisecond, "Time between each sequencing pass through all logs") batchSizeFlag = flag.Int("batch_size", 1000, "Max number of leaves to process per batch") numSeqFlag = flag.Int("num_sequencers", 10, "Number of sequencer workers to run in parallel") sequencerGuardWindowFlag = flag.Duration("sequencer_guard_window", 0, "If set, the time elapsed before submitted leaves are eligible for sequencing") forceMaster = flag.Bool("force_master", false, "If true, assume master for all logs") etcdHTTPService = flag.String("etcd_http_service", "trillian-logsigner-http", "Service name to announce our HTTP endpoint under") lockDir = flag.String("lock_file_path", "/test/multimaster", "etcd lock file directory path") healthzTimeout = flag.Duration("healthz_timeout", time.Second*5, "Timeout used during healthz checks") quotaSystem = flag.String("quota_system", "mysql", fmt.Sprintf("Quota system to use. One of: %v", quota.Providers())) quotaIncreaseFactor = flag.Float64("quota_increase_factor", log.QuotaIncreaseFactor, "Increase factor for tokens replenished by sequencing-based quotas (1 means a 1:1 relationship between sequenced leaves and replenished tokens)."+ "Only effective for --quota_system=etcd.") storageSystem = flag.String("storage_system", "mysql", fmt.Sprintf("Storage system to use. One of: %v", storage.Providers())) preElectionPause = flag.Duration("pre_election_pause", 1*time.Second, "Maximum time to wait before starting elections") masterHoldInterval = flag.Duration("master_hold_interval", 60*time.Second, "Minimum interval to hold mastership for") masterHoldJitter = flag.Duration("master_hold_jitter", 120*time.Second, "Maximal random addition to --master_hold_interval") configFile = flag.String("config", "", "Config file containing flags, file contents can be overridden by command line flags") // Profiling related flags. cpuProfile = flag.String("cpuprofile", "", "If set, write CPU profile to this file") memProfile = flag.String("memprofile", "", "If set, write memory profile to this file") ) func main() { klog.InitFlags(nil) flag.Parse() defer klog.Flush() if *configFile != "" { if err := cmd.ParseFlagFile(*configFile); err != nil { klog.Exitf("Failed to load flags from config file %q: %s", *configFile, err) } } klog.CopyStandardLogTo("WARNING") klog.Info("**** Log Signer Starting ****") mf := prometheus.MetricFactory{} monitoring.SetStartSpan(opencensus.StartSpan) sp, err := storage.NewProvider(*storageSystem, mf) if err != nil { klog.Exitf("Failed to get storage provider: %v", err) } defer func() { if err := sp.Close(); err != nil { klog.Errorf("Close(): %v", err) } }() var client *clientv3.Client if servers := *etcd.Servers; servers != "" { if client, err = clientv3.New(clientv3.Config{ Endpoints: strings.Split(servers, ","), DialTimeout: 5 * time.Second, }); err != nil { klog.Exitf("Failed to connect to etcd at %v: %v", servers, err) } defer func() { if err := client.Close(); err != nil { klog.Errorf("Close(): %v", err) } }() } ctx, cancel := context.WithCancel(context.Background()) defer cancel() go util.AwaitSignal(ctx, cancel) hostname, _ := os.Hostname() instanceID := fmt.Sprintf("%s.%d", hostname, os.Getpid()) var electionFactory election2.Factory switch { case *forceMaster: klog.Warning("**** Acting as master for all logs ****") electionFactory = election2.NoopFactory{} case client != nil: electionFactory = etcdelect.NewFactory(instanceID, client, *lockDir) default: klog.Exit("Either --force_master or --etcd_servers must be supplied") } qm, err := quota.NewManager(*quotaSystem) if err != nil { klog.Exitf("Error creating quota manager: %v", err) } registry := extension.Registry{ AdminStorage: sp.AdminStorage(), LogStorage: sp.LogStorage(), ElectionFactory: electionFactory, QuotaManager: qm, MetricFactory: mf, } // Start HTTP server (optional) if *httpEndpoint != "" { // Announce our endpoint to etcd if so configured. unannounceHTTP := serverutil.AnnounceSelf(ctx, client, *etcdHTTPService, *httpEndpoint, cancel) defer unannounceHTTP() } // Start the sequencing loop, which will run until we terminate the process. This controls // both sequencing and signing. // TODO(Martin2112): Should respect read only mode and the flags in tree control etc log.QuotaIncreaseFactor = *quotaIncreaseFactor sequencerManager := log.NewSequencerManager(registry, *sequencerGuardWindowFlag) info := log.OperationInfo{ Registry: registry, BatchSize: *batchSizeFlag, NumWorkers: *numSeqFlag, RunInterval: *sequencerIntervalFlag, TimeSource: clock.System, ElectionConfig: election.RunnerConfig{ PreElectionPause: *preElectionPause, MasterHoldInterval: *masterHoldInterval, MasterHoldJitter: *masterHoldJitter, TimeSource: clock.System, }, } sequencerTask := log.NewOperationManager(info, sequencerManager) go sequencerTask.OperationLoop(ctx) // Enable CPU profile if requested if *cpuProfile != "" { f := mustCreate(*cpuProfile) if err := pprof.StartCPUProfile(f); err != nil { klog.Exitf("StartCPUProfile(): %v", err) } defer pprof.StopCPUProfile() } m := serverutil.Main{ RPCEndpoint: *rpcEndpoint, HTTPEndpoint: *httpEndpoint, TLSCertFile: *tlsCertFile, TLSKeyFile: *tlsKeyFile, StatsPrefix: "logsigner", DBClose: sp.Close, Registry: registry, RegisterServerFn: func(s *grpc.Server, _ extension.Registry) error { return nil }, IsHealthy: sp.AdminStorage().CheckDatabaseAccessible, HealthyDeadline: *healthzTimeout, } if err := m.Run(ctx); err != nil { klog.Exitf("Server exited with error: %v", err) } if *memProfile != "" { f := mustCreate(*memProfile) if err := pprof.WriteHeapProfile(f); err != nil { klog.Exitf("WriteHeapProfile(): %v", err) } } // Give things a few seconds to tidy up klog.Infof("Stopping server, about to exit") time.Sleep(time.Second * 5) } func mustCreate(fileName string) *os.File { f, err := os.Create(fileName) if err != nil { klog.Fatal(err) } return f } trillian-1.6.1/cmd/updatetree/000077500000000000000000000000001466362047600162605ustar00rootroot00000000000000trillian-1.6.1/cmd/updatetree/main.go000066400000000000000000000112131466362047600175310ustar00rootroot00000000000000// Copyright 2018 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 main contains the implementation and entry point for the updatetree // command. // // Example usage: // $ ./updatetree --admin_server=host:port --tree_id=123456789 --tree_state=FROZEN // // The output is minimal to allow for easy usage in automated scripts. package main import ( "context" "errors" "flag" "fmt" "time" "github.com/google/trillian" "github.com/google/trillian/client/rpcflags" "google.golang.org/genproto/protobuf/field_mask" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/protobuf/reflect/protoregistry" "k8s.io/klog/v2" ) var ( adminServerAddr = flag.String("admin_server", "", "Address of the gRPC Trillian Admin Server (host:port)") rpcDeadline = flag.Duration("rpc_deadline", time.Second*10, "Deadline for RPC requests") treeID = flag.Int64("tree_id", 0, "The ID of the tree to be set updated") treeState = flag.String("tree_state", "", "If set the tree state will be updated") treeType = flag.String("tree_type", "", "If set the tree type will be updated") printTree = flag.Bool("print", false, "Print the resulting tree") ) // TODO(Martin2112): Pass everything needed into this and don't refer to flags. func updateTree(ctx context.Context) (*trillian.Tree, error) { if *adminServerAddr == "" { return nil, errors.New("empty --admin_server, please provide the Admin server host:port") } tree := &trillian.Tree{TreeId: *treeID} paths := make([]string, 0) if len(*treeState) > 0 { m, err := protoregistry.GlobalTypes.FindEnumByName("trillian.TreeState") if err != nil { return nil, fmt.Errorf("can't find enum value map for states: %w", err) } newState := m.Descriptor().Values().ByName(protoreflect.Name(*treeState)) if newState == nil { return nil, fmt.Errorf("invalid tree state: %v", *treeState) } tree.TreeState = trillian.TreeState(newState.Number()) paths = append(paths, "tree_state") } if len(*treeType) > 0 { m, err := protoregistry.GlobalTypes.FindEnumByName("trillian.TreeType") if err != nil { return nil, fmt.Errorf("can't find enum value map for types: %w", err) } newType := m.Descriptor().Values().ByName(protoreflect.Name(*treeType)) if newType == nil { return nil, fmt.Errorf("invalid tree type: %v", *treeType) } tree.TreeType = trillian.TreeType(newType.Number()) paths = append(paths, "tree_type") } if len(paths) == 0 { return nil, errors.New("nothing to change") } // We only want to update certain fields of the tree, which means we // need a field mask on the request. req := &trillian.UpdateTreeRequest{ Tree: tree, UpdateMask: &field_mask.FieldMask{Paths: paths}, } dialOpts, err := rpcflags.NewClientDialOptionsFromFlags() if err != nil { return nil, fmt.Errorf("failed to determine dial options: %v", err) } conn, err := grpc.Dial(*adminServerAddr, dialOpts...) if err != nil { return nil, fmt.Errorf("failed to dial %v: %v", *adminServerAddr, err) } defer func() { if err := conn.Close(); err != nil { klog.Errorf("Close(): %v", err) } }() client := trillian.NewTrillianAdminClient(conn) for { tree, err := client.UpdateTree(ctx, req) if err == nil { return tree, nil } if s, ok := status.FromError(err); ok && s.Code() == codes.Unavailable { klog.Errorf("Admin server unavailable, trying again: %v", err) time.Sleep(100 * time.Millisecond) continue } return nil, fmt.Errorf("failed to UpdateTree(%+v): %T %v", req, err, err) } } func main() { klog.InitFlags(nil) flag.Parse() defer klog.Flush() ctx, cancel := context.WithTimeout(context.Background(), *rpcDeadline) defer cancel() tree, err := updateTree(ctx) if err != nil { klog.Exitf("Failed to update tree: %v", err) } if *printTree { fmt.Println(prototext.Format(tree)) } else { // DO NOT change the default output format, some scripts depend on it. If // you really want to change it, hide the new format behind a flag. fmt.Println(tree.TreeState) } } trillian-1.6.1/cmd/updatetree/main_test.go000066400000000000000000000111241466362047600205710ustar00rootroot00000000000000// Copyright 2018 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 main import ( "context" "errors" "testing" "time" "github.com/golang/mock/gomock" "github.com/google/trillian" "github.com/google/trillian/testonly" "github.com/google/trillian/testonly/flagsaver" ) type testCase struct { desc string setFlags func() updateErr error wantRPC bool updateTree *trillian.Tree wantErr bool wantState trillian.TreeState } func TestFreezeTree(t *testing.T) { runTest(t, []*testCase{ { // We don't set the treeID in runTest so this should fail. desc: "missingTreeID", wantErr: true, }, { desc: "mandatoryOptsNotSet", // Undo the flags set by runTest, so that mandatory options are no longer set. setFlags: flagsaver.Save().MustRestore, wantErr: true, }, { desc: "validUpdateFrozen", setFlags: func() { *treeID = 12345 *treeState = "FROZEN" }, wantRPC: true, updateTree: &trillian.Tree{ TreeId: 12345, TreeState: trillian.TreeState_FROZEN, }, wantState: trillian.TreeState_FROZEN, }, { desc: "updateInvalidState", setFlags: func() { *treeID = 12345 *treeState = "ITSCOLDOUTSIDE" }, wantErr: true, }, { desc: "unknownTree", setFlags: func() { *treeID = 123456 *treeState = "FROZEN" }, wantErr: true, wantRPC: true, updateErr: errors.New("unknown tree id"), }, { desc: "emptyAddr", setFlags: func() { *adminServerAddr = "" *treeID = 12345 *treeState = "FROZEN" }, wantErr: true, }, { desc: "updateErr", setFlags: func() { *treeID = 12345 *treeState = "FROZEN" }, wantRPC: true, updateErr: errors.New("update tree failed"), wantErr: true, }, }) } // runTest executes the updateTree command against a fake TrillianAdminServer // for each of the provided tests, and checks that the tree in the request is // as expected, or an expected error occurs. // Prior to each test case, it: // 1. Resets all flags to their original values. // 2. Sets the adminServerAddr flag to point to the fake server. // 3. Calls the test's setFlags func (if provided) to allow it to change flags specific to the test. func runTest(t *testing.T, tests []*testCase) { for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { // Note: Restore() must be called after the flag-reading bits are // stopped, otherwise there might be a data race. defer flagsaver.Save().MustRestore() ctrl := gomock.NewController(t) defer ctrl.Finish() s, stopFakeServer, err := testonly.NewMockServer(ctrl) if err != nil { t.Fatalf("Error starting fake server: %v", err) } defer stopFakeServer() *adminServerAddr = s.Addr if tc.setFlags != nil { tc.setFlags() } // We might not get as far as updating the tree on the admin server. if tc.wantRPC { call := s.Admin.EXPECT().UpdateTree(gomock.Any(), gomock.Any()).Return(tc.updateTree, tc.updateErr) expectCalls(call, tc.updateErr) } ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) defer cancel() tree, err := updateTree(ctx) if hasErr := err != nil; hasErr != tc.wantErr { t.Errorf("updateTree() returned err = '%v', wantErr = %v", err, tc.wantErr) return } if err == nil { if got, want := tree.TreeState.String(), tc.wantState.String(); got != want { t.Errorf("updated state incorrect got: %v want: %v", got, want) } } }) } } // expectCalls returns the minimum number of times a function is expected to be called // given the return error for the function (err), and all previous errors in the function's // code path. func expectCalls(call *gomock.Call, err error, prevErr ...error) *gomock.Call { // If a function prior to this function errored, // we do not expect this function to be called. for _, e := range prevErr { if e != nil { return call.Times(0) } } // If this function errors, it might be retried multiple times. if err != nil { return call.MinTimes(1) } // If this function succeeds it should only be called once. return call.Times(1) } trillian-1.6.1/codecov.yml000066400000000000000000000010611466362047600155160ustar00rootroot00000000000000# Customizations to codecov for Trillian repo. This will be merged into # the team / default codecov yaml file. # # Validate changes with: # curl --data-binary @codecov.yml https://codecov.io/validate # Exclude code that's for testing, demos or utilities that aren't really # part of production releases. ignore: - "**/mock_*.go" - "**/testonly" - "docs" - "examples" - "integration" - "testonly" coverage: status: project: default: # Allow 1% coverage drop without complaining, to avoid being too noisy. threshold: 1% trillian-1.6.1/crypto/000077500000000000000000000000001466362047600146735ustar00rootroot00000000000000trillian-1.6.1/crypto/keys/000077500000000000000000000000001466362047600156465ustar00rootroot00000000000000trillian-1.6.1/crypto/keys/der/000077500000000000000000000000001466362047600164205ustar00rootroot00000000000000trillian-1.6.1/crypto/keys/der/der.go000066400000000000000000000056421466362047600175300ustar00rootroot00000000000000// 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 der contains functions for marshaling and unmarshaling keys in DER format. package der import ( "context" "crypto" "crypto/ecdsa" "crypto/rsa" "crypto/x509" "fmt" "github.com/google/trillian/crypto/keyspb" "golang.org/x/crypto/ed25519" "google.golang.org/protobuf/proto" ) // FromProto builds a crypto.Signer from a proto.Message, which must be of type PrivateKey. func FromProto(_ context.Context, pb proto.Message) (crypto.Signer, error) { if pb, ok := pb.(*keyspb.PrivateKey); ok { return UnmarshalPrivateKey(pb.GetDer()) } return nil, fmt.Errorf("der: got %T, want *keyspb.PrivateKey", pb) } // UnmarshalPrivateKey reads a DER-encoded private key. func UnmarshalPrivateKey(keyDER []byte) (crypto.Signer, error) { key1, err1 := x509.ParseECPrivateKey(keyDER) if err1 == nil { return key1, nil } key2, err2 := x509.ParsePKCS8PrivateKey(keyDER) if err2 == nil { switch key2 := key2.(type) { case *ecdsa.PrivateKey: return key2, nil case *rsa.PrivateKey: return key2, nil case ed25519.PrivateKey: return key2, nil } return nil, fmt.Errorf("der: unsupported private key type: %T", key2) } key3, err3 := x509.ParsePKCS1PrivateKey(keyDER) if err3 == nil { return key3, nil } return nil, fmt.Errorf("der: could not parse private key as SEC1 (%v), PKCS8 (%v) or PKCS1 (%v)", err1, err2, err3) } // UnmarshalPublicKey reads a DER-encoded public key. func UnmarshalPublicKey(keyDER []byte) (crypto.PublicKey, error) { key, err := x509.ParsePKIXPublicKey(keyDER) if err != nil { return nil, fmt.Errorf("der: could not parse public key as PKIX (%v)", err) } return key, nil } // MarshalPublicKey serializes an RSA or ECDSA public key as DER. func MarshalPublicKey(pubKey crypto.PublicKey) ([]byte, error) { der, err := x509.MarshalPKIXPublicKey(pubKey) if err != nil { return nil, fmt.Errorf("der: could not marshal public key as PKIX (%v)", err) } return der, nil } // MarshalPrivateKey serializes an RSA or ECDSA private key as DER. func MarshalPrivateKey(key crypto.Signer) ([]byte, error) { switch key := key.(type) { case *ecdsa.PrivateKey: return x509.MarshalECPrivateKey(key) case *rsa.PrivateKey: return x509.MarshalPKCS1PrivateKey(key), nil case ed25519.PrivateKey: return x509.MarshalPKCS8PrivateKey(key) } return nil, fmt.Errorf("der: unsupported key type: %T", key) } trillian-1.6.1/crypto/keys/der/der_test.go000066400000000000000000000061001466362047600205550ustar00rootroot00000000000000// 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 der_test import ( "bytes" "context" "encoding/base64" "testing" . "github.com/google/trillian/crypto/keys/der" "github.com/google/trillian/crypto/keys/testonly" "github.com/google/trillian/crypto/keyspb" "google.golang.org/protobuf/proto" _ "k8s.io/klog/v2" ) const ( // ECDSA private key in DER format, base64-encoded. privKeyBase64 = "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgS81mfpvtTmaINn+gtrYXn4XpxxgE655GLSKsA3hhjHmhRANCAASwBWDdgHS04V/cN0LZgc8vZaK4I1HWLLCoaOO27Z0B1aS1aqBE7g1Oo8ldSCBJAvee866kcHhZkVniPdCG2ZZG" // ECDSA public key in DER format, base64-encoded. pubKeyBase64 = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEsAVg3YB0tOFf3DdC2YHPL2WiuCNR1iywqGjjtu2dAdWktWqgRO4NTqPJXUggSQL3nvOupHB4WZFZ4j3QhtmWRg==" ) func TestFromProto(t *testing.T) { t.Parallel() ctx := context.Background() keyDER, err := base64.StdEncoding.DecodeString(privKeyBase64) if err != nil { t.Fatalf("Could not decode test key: %v", err) } for _, test := range []struct { desc string keyProto proto.Message wantErr bool }{ { desc: "PrivateKey", keyProto: &keyspb.PrivateKey{ Der: keyDER, }, }, { desc: "wrong proto", keyProto: &keyspb.PEMKeyFile{}, wantErr: true, }, { desc: "PrivateKey with invalid DER", keyProto: &keyspb.PrivateKey{ Der: []byte("foobar"), }, wantErr: true, }, { desc: "PrivateKey with missing DER", keyProto: &keyspb.PrivateKey{}, wantErr: true, }, } { test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() signer, err := FromProto(ctx, test.keyProto) if gotErr := err != nil; gotErr != test.wantErr { t.Fatalf("FromProto(%#v) = (_, %q), want (_, nil)", test.keyProto, err) } else if gotErr { return } // Check that the returned signer can produce signatures successfully. if err := testonly.SignAndVerify(signer, signer.Public()); err != nil { t.Fatalf("SignAndVerify() = %q, want nil", err) } }) } } func TestMarshalUnmarshalPublicKey(t *testing.T) { t.Parallel() keyDER, err := base64.StdEncoding.DecodeString(pubKeyBase64) if err != nil { t.Fatalf("Could not decode test key: %v", err) } key, err := UnmarshalPublicKey(keyDER) if err != nil { t.Fatalf("UnmarshalPublicKey(%v): %v", keyDER, err) } keyDER2, err := MarshalPublicKey(key) if err != nil { t.Fatalf("MarshalPublicKey(%v): %v", key, err) } if got, want := keyDER2, keyDER; !bytes.Equal(got, want) { t.Errorf("MarshalPublicKey(): %x, want %x", got, want) } } trillian-1.6.1/crypto/keys/handlers.go000066400000000000000000000056311466362047600200020ustar00rootroot00000000000000// 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 keys provides access to public and private keys for signing and verification of signatures. package keys import ( "context" "crypto" "fmt" "sync" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" "k8s.io/klog/v2" ) // ProtoHandler uses the information in a protobuf message to obtain a crypto.Signer. // For example, the protobuf message may contain a key or identify where a key can be found. type ProtoHandler func(context.Context, proto.Message) (crypto.Signer, error) var ( // handlers convert a protobuf message into a crypto.Signer. handlers = make(map[protoreflect.FullName]ProtoHandler) handlersMu sync.RWMutex ) // RegisterHandler enables transformation of protobuf messages of the same // type as keyProto into crypto.Signer by invoking the provided handler. // The keyProto need only be an empty example of the type of protobuf message that // the handler can process - only its type is examined. // If a handler for this type of protobuf message has already been added, it will // be replaced. func RegisterHandler(keyProto proto.Message, handler ProtoHandler) { handlersMu.Lock() defer handlersMu.Unlock() keyProtoType := keyProto.ProtoReflect().Descriptor().FullName() if _, alreadyExists := handlers[keyProtoType]; alreadyExists { klog.Warningf("Overridding ProtoHandler for protobuf %q", keyProtoType) } handlers[keyProtoType] = handler } // unregisterHandler removes a previously-added protobuf message handler. // See RegisterHandler(). func unregisterHandler(keyProto proto.Message) { handlersMu.Lock() defer handlersMu.Unlock() delete(handlers, keyProto.ProtoReflect().Descriptor().FullName()) } // NewSigner uses a registered ProtoHandler (see RegisterHandler()) to convert a // protobuf message into a crypto.Signer. // If there is no ProtoHandler registered for this type of protobuf message, an // error will be returned. func NewSigner(ctx context.Context, keyProto proto.Message) (crypto.Signer, error) { handlersMu.RLock() defer handlersMu.RUnlock() if keyProto == nil { return nil, fmt.Errorf("nil keyProto") } keyProtoType := keyProto.ProtoReflect().Descriptor().FullName() if handler, ok := handlers[keyProtoType]; ok { return handler(ctx, keyProto) } return nil, fmt.Errorf("no ProtoHandler registered for protobuf %q", keyProtoType) } trillian-1.6.1/crypto/keys/handlers_test.go000066400000000000000000000044211466362047600210350ustar00rootroot00000000000000// 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 keys import ( "context" "crypto" "errors" "testing" "github.com/google/trillian/crypto/keys/pem" "github.com/google/trillian/testonly" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/emptypb" ) func fakeHandler(signer crypto.Signer, err error) ProtoHandler { return func(ctx context.Context, pb proto.Message) (crypto.Signer, error) { return signer, err } } func TestNewSigner(t *testing.T) { wantSigner, err := pem.UnmarshalPrivateKey(testonly.DemoPrivateKey, testonly.DemoPrivateKeyPass) if err != nil { t.Fatalf("Error unmarshaling test private key: %v", err) } ctx := context.Background() for _, test := range []struct { desc string keyProto proto.Message handler ProtoHandler wantErr bool }{ { desc: "KeyProto with handler", keyProto: &emptypb.Empty{}, handler: fakeHandler(wantSigner, nil), }, { desc: "Invalid KeyProto with handler", keyProto: &emptypb.Empty{}, handler: fakeHandler(nil, errors.New("invalid KeyProto")), wantErr: true, }, { desc: "KeyProto with no handler", keyProto: &emptypb.Empty{}, wantErr: true, }, { desc: "Nil KeyProto", wantErr: true, }, } { if test.handler != nil { RegisterHandler(test.keyProto, test.handler) } gotSigner, err := NewSigner(ctx, test.keyProto) switch gotErr := err != nil; { case gotErr != test.wantErr: t.Errorf("%v: NewSigner() = (_, %q), want err? %v", test.desc, err, test.wantErr) case !gotErr && gotSigner != wantSigner: t.Errorf("%v: NewSigner() = (%#v, _), want (%#v, _)", test.desc, gotSigner, wantSigner) } if test.handler != nil { unregisterHandler(test.keyProto) } } } trillian-1.6.1/crypto/keys/pem/000077500000000000000000000000001466362047600164275ustar00rootroot00000000000000trillian-1.6.1/crypto/keys/pem/pem.go000066400000000000000000000050021466362047600175340ustar00rootroot00000000000000// 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 pem contains functions for marshaling and unmarshaling keys in PEM format. package pem import ( "context" "crypto" "crypto/x509" "encoding/pem" "errors" "fmt" "os" "github.com/google/trillian/crypto/keys/der" "github.com/google/trillian/crypto/keyspb" "google.golang.org/protobuf/proto" ) // ReadPrivateKeyFile reads a PEM-encoded private key from a file. // The key must be protected by a password. func ReadPrivateKeyFile(file, password string) (crypto.Signer, error) { if password == "" { return nil, fmt.Errorf("pemfile: empty password for file %q", file) } keyPEM, err := os.ReadFile(file) if err != nil { return nil, fmt.Errorf("pemfile: error reading file %q: %v", file, err) } k, err := UnmarshalPrivateKey(string(keyPEM), password) if err != nil { return nil, fmt.Errorf("pemfile: error decoding private key from file %q: %v", file, err) } return k, nil } // UnmarshalPrivateKey reads a PEM-encoded private key from a string. // The key may be protected by a password. func UnmarshalPrivateKey(keyPEM, password string) (crypto.Signer, error) { block, rest := pem.Decode([]byte(keyPEM)) if block == nil { return nil, errors.New("pemfile: invalid private key PEM") } if len(rest) > 0 { return nil, errors.New("pemfile: extra data found after first PEM block") } keyDER := block.Bytes if password != "" { pwdDer, err := x509.DecryptPEMBlock(block, []byte(password)) //nolint:staticcheck if err != nil { return nil, fmt.Errorf("pemfile: failed to decrypt: %v", err) } keyDER = pwdDer } return der.UnmarshalPrivateKey(keyDER) } // FromProto builds a crypto.Signer from a proto.Message, which must be of type PEMKeyFile. func FromProto(_ context.Context, pb proto.Message) (crypto.Signer, error) { if pb, ok := pb.(*keyspb.PEMKeyFile); ok { return ReadPrivateKeyFile(pb.GetPath(), pb.GetPassword()) } return nil, fmt.Errorf("pemfile: got %T, want *keyspb.PEMKeyFile", pb) } trillian-1.6.1/crypto/keys/pem/pem_test.go000066400000000000000000000204241466362047600206000ustar00rootroot00000000000000// 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 pem_test import ( "context" "crypto" "testing" . "github.com/google/trillian/crypto/keys/pem" ktestonly "github.com/google/trillian/crypto/keys/testonly" "github.com/google/trillian/crypto/keyspb" "github.com/google/trillian/testonly" "google.golang.org/protobuf/proto" _ "k8s.io/klog/v2" ) const ( _ = ` -----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEvuynpVdR+5xSNaVBb//1fqO6Nb/nC+WvRQ4bALzy4G+QbByvO1Qpm2eUzTdDUnsLN5hp3pIXYAmtjvjY1fFZEg== -----END PUBLIC KEY-----` ecdsaPrivateKey = ` -----BEGIN PRIVATE KEY----- MHcCAQEEIHG5m/q2sUSa4P8pRZgYt3K0ESFSKp1qp15VjJhpLle4oAoGCCqGSM49AwEHoUQDQgAEvuynpVdR+5xSNaVBb//1fqO6Nb/nC+WvRQ4bALzy4G+QbByvO1Qpm2eUzTdDUnsLN5hp3pIXYAmtjvjY1fFZEg== -----END PRIVATE KEY----- ` _ = ` -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsMB4reLZhs+2ReYX01nZpqLBQ9uhcZvBmzH54RsZDTb5khw+luSXKbLKXxdbQfrsxURbeVdugDNnV897VI43znuiKJ19Y/XS3N5Z7Q97/GOxOxGFObP0DovCAPblxAMaQBb+U9jkVt/4bHcNIOTZl/lXgX+yp58lH5uPfDwav/hVNg7QkAW3BxQZ5wiLTTZUILoTMjax4R24pULlg/Wt/rT4bDj8rxUgYR60MuO93jdBtNGwmzdCYyk4cEmrPEgCueRC6jFafUzlLjvuX89ES9n98LxX+gBANA7RpVPkJd0kfWFHO1JRUEJr++WjU3x4la2Xs4tUNX4QBSJP4XEOXwIDAQAB -----END PUBLIC KEY-----` rsaPrivateKey = ` -----BEGIN PRIVATE KEY----- MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCwwHit4tmGz7ZF5hfTWdmmosFD26Fxm8GbMfnhGxkNNvmSHD6W5JcpsspfF1tB+uzFRFt5V26AM2dXz3tUjjfOe6IonX1j9dLc3lntD3v8Y7E7EYU5s/QOi8IA9uXEAxpAFv5T2ORW3/hsdw0g5NmX+VeBf7KnnyUfm498PBq/+FU2DtCQBbcHFBnnCItNNlQguhMyNrHhHbilQuWD9a3+tPhsOPyvFSBhHrQy473eN0G00bCbN0JjKThwSas8SAK55ELqMVp9TOUuO+5fz0RL2f3wvFf6AEA0DtGlU+Ql3SR9YUc7UlFQQmv75aNTfHiVrZezi1Q1fhAFIk/hcQ5fAgMBAAECggEAcpuq5J2GjQqcVwCWjF3jalB4XsbIDUGArWAfdd47RT1TYHFeCDua5Nfgrv4XF1ZcNqFXavvNU+WA6ghIIRDCkOnLwOg1yR45pyuqRbPXolUGM5Xtu/e6lb/7gOKXI50bZVlDehzWGprJm5MqeRzLFub/3aFut4/S44bb6COU+Mo6bsm+/2hcuOtUeDR5fOc49tTAZZSG6kVAXdWG5raU4a/Qx6LCR5zMjhzqy8FMGkW+eww243WM/5RCW6pzgwjFVPyfrg/Jqc2IgAPuFEStvK6jAsPaZxb7t1ue79ku8+xLDpJSgLUF3jU9Qy8+VphnmbHrYSqDSNUyfj8+qcbI0QKBgQDc/GD7Yprw4zp3IqLoYd96dqJtlloUgd7kGebDfAftAgo2ooS8tpbAYGvgmeMDqAfqfTkOJUACCHptnpUWusXJqW6SW9bk17jGb/pPcQiXmaNPGYbpPlamueUmS9gdatvw6iXewRqjltNMng+mfbvAmaFe+qeqCq86R9BoUFVBCQKBgQDMwd+6DKGKH/hgChweUtNLMmeOmzYskcUL43cLeAAwwlL4DruLthBb/0SYeMQ+sXpYDL3b1/i03Ln5P5g8KFL8EgIayInlZJHiHjOn9LF+S5gv5snI0Fdk2O8eNHCSiS0+qqPU8ZKTKwnbt8M+OqLhJD0C7N35oYAoCj0uhSp2JwKBgQDBIxqn2tBMBGyOvwjeTNwCrjjbynJERhVGCpUy+O38aLIAeh3EyVgMHrlp/VT5VxxEBtmc0VWV8U7/C4CF8wr2a0ymQfoY26k0VZ3RXJsD1FV0xnyw0bjt0r7Br7vcSg6cCii6/M6Jd0KJTgOjoXQ8qojs9+kdpmTrbORqpvs78QKBgQC13ZW8CLAKoS7ZDuG+xU5LQi/c6FuL5sWgM59vHlz88f0DuwI1q7aIIAlrbAjSroy+XELeW8vZyRueGTA8boyWu+AGrgxdJaC1uKGlEp/8T2STV2fu565YMp7gsy8x2InJWYM/BnpsIRQWhffy893sH2XZjU30BdBwv/drtHfsjQKBgHQgInEA+pwo/laVgVkuIlL/0avlRRG06TsMUJYDP9jOfzdoWZrsCVr4uLXMR1zJ2I/tmKv+u+35luu2rVnItB7hSJgMy+6Bxj4DL5QE9BuLVARDMGrj05oZXPw9954HjN87b4dVvSKl2hPz6lcsKDlPJy+OvXdsZxfc9NaCCQNT -----END PRIVATE KEY----- ` _ = ` -----BEGIN PUBLIC KEY----- MIIDRjCCAjkGByqGSM44BAEwggIsAoIBAQDgLI6pXvqpcOY33lzeZrjUBHxphiz0I9VKF9vGpWymNfBptQ75bpQFe16jBjaOGwDImASHTp53XskQJLOXC4bZxoRUHsm8bHQVZHQhYgxn8ZDQX/40zOR1d73y1TXSiULo6rDKVlM+fFcm33tGv+ZOdfaIhW17c5jvDAy6UWqQakasvL+kfiejIDGHjLVFWwX0vLCG+pAomgO6snQHGcPhDO9uxEYPd9on7YTgBrpa2IcXk5jFeY8xOxMnMwoBojRvH97+ivdBR1yW8f+4FAGg5o1eFV5ZqoUAF8GO3BBEwluMGNeT7gMgl4PO8N8xBxJulHd3tLW5qkW0cBPwkbzzAiEAvdYeMPamsFAyd7s07dt78wxXyHGrwVl2AcQBo0QTATkCggEASH9Rp+EjNkL7uCqGJ78P4tjJM+2+xaEhZpJ/kTzq6DtdFhu5Rov6lN5NnZKPSUNYr9Vkmu88ru0iND1N37z0rJpImksXKxCv0AwBkwtqCwf9jjkTrZiGRzP8xf789wK+uG7Uud20ml9QzXKr9Af9WrRx3DtCq44PBaIlhPvpZS9znCZsuUZqYZFW3/oD4EhwPgVLSWeulh1t33ku3mYQwVS8ZTdJGPyFRoD1dcQ4EchR4ce0u0nTXlqErWhfnmb9msF6dFCV0Mx5yrqxkEHbJ/vZgB4zAdOke7XiJsWqIok/7IJpJuVOvkY9NHgBdlq3xU180+pEo2NrGm4pbrGm1wOCAQUAAoIBAAGbucHEfgtcu++OQQjYqneukv4zqcP/PCJTP+GuXen6SH25V2ZlHC88lG6qdZVBPWZidAb9BSoUQpW7BzauKRqH7rKOsIeqvEPCiWBKA781Zi5HAWGhC4INJJx54Q66F54DkGlTRVFkXlGpAIudhfAIG//MyO9TIsLSgRyqjKWVm+/XhWDIT5iMJZZ/IgmbICueaa7go8poHuTTyUDPHPIeL5d9Aru7qD4JtX+UVy6GYKhWx/guv+A7zyJ8d1kMLsmUAro80DLPDoais2I8YPpbu+xTSLLswIYddDdwg3P8mMAGzuWY/ZLumwpRr/fbI+t2Sm9KKGNGkGGIKAg43cs= -----END PUBLIC KEY-----` corruptEcdsaPrivateKey = ` -----BEGIN PRIVATE KEY----- NHcCAQEEIHG5m/q2sUSa4P8pRZgYt3K0ESFSKp1qp15VjJhpLle4oAoGCCqGSM49AwEHoUQDQgAEvuynpVdR+5xSNaVBb//1fqO6Nb/nC+WvRQ4bALzy4G+QbByvO1Qpm2eUzTdDUnsLN5hp3pIXYAmtjvjY1fFZEg== -----END PRIVATE KEY----- ` ) func TestLoadPrivateKeyAndSign(t *testing.T) { tests := []struct { desc string keyPEM string keyPath string keyPass string wantLoadErr bool }{ { desc: "ECDSA with password", keyPEM: testonly.DemoPrivateKey, keyPass: testonly.DemoPrivateKeyPass, }, { desc: "ECDSA from file with password", keyPath: "../../../testdata/log-rpc-server.privkey.pem", keyPass: "towel", }, { desc: "Non-existent file", keyPath: "non-existent.pem", wantLoadErr: true, }, { desc: "ECDSA with wrong password", keyPEM: testonly.DemoPrivateKey, keyPass: testonly.DemoPrivateKeyPass + "foo", wantLoadErr: true, }, { desc: "ECDSA", keyPEM: ecdsaPrivateKey, }, { desc: "RSA", keyPEM: rsaPrivateKey, }, { desc: "ECDSA with leading junk", keyPEM: "foobar\n" + ecdsaPrivateKey, }, { desc: "ECDSA with trailing junk", keyPEM: ecdsaPrivateKey + "\nfoobar", wantLoadErr: true, }, { desc: "Corrupt ECDSA", keyPEM: corruptEcdsaPrivateKey, wantLoadErr: true, }, } for _, test := range tests { var k crypto.Signer var err error switch { case test.keyPEM != "": k, err = UnmarshalPrivateKey(test.keyPEM, test.keyPass) switch gotErr := err != nil; { case gotErr != test.wantLoadErr: t.Errorf("%v: UnmarshalPrivateKey() = (%v, %v), want err? %v", test.desc, k, err, test.wantLoadErr) continue case gotErr: continue } case test.keyPath != "": k, err = ReadPrivateKeyFile(test.keyPath, test.keyPass) switch gotErr := err != nil; { case gotErr != test.wantLoadErr: t.Errorf("%v: ReadPrivateKeyFile() = (%v, %v), want err? %v", test.desc, k, err, test.wantLoadErr) continue case gotErr: continue } default: t.Errorf("%v: No PEM or file path set in test definition", test.desc) continue } // Check the key by creating a signature and verifying it. if err := ktestonly.SignAndVerify(k, k.Public()); err != nil { t.Errorf("%v: SignAndVerify() = %q, want nil", test.desc, err) } } } func TestFromProto(t *testing.T) { ctx := context.Background() for _, test := range []struct { desc string keyProto proto.Message wantErr bool }{ { desc: "PEMKeyFile", keyProto: &keyspb.PEMKeyFile{ Path: "../../../testdata/log-rpc-server.privkey.pem", Password: "towel", }, }, { desc: "wrong proto", keyProto: &keyspb.PrivateKey{}, wantErr: true, }, { desc: "PemKeyFile with non-existent file", keyProto: &keyspb.PEMKeyFile{ Path: "non-existent.pem", }, wantErr: true, }, { desc: "PemKeyFile with wrong password", keyProto: &keyspb.PEMKeyFile{ Path: "../../../testdata/log-rpc-server.privkey.pem", Password: "wrong-password", }, wantErr: true, }, { desc: "PemKeyFile with missing password", keyProto: &keyspb.PEMKeyFile{ Path: "../../../testdata/log-rpc-server.privkey.pem", }, wantErr: true, }, } { signer, err := FromProto(ctx, test.keyProto) if gotErr := err != nil; gotErr != test.wantErr { t.Errorf("%v: NewSigner(_, %#v) = (_, %q), want (_, nil)", test.desc, test.keyProto, err) continue } else if gotErr { continue } // Check that the returned signer can produce signatures successfully. if err := ktestonly.SignAndVerify(signer, signer.Public()); err != nil { t.Errorf("%v: SignAndVerify() = %q, want nil", test.desc, err) } } } trillian-1.6.1/crypto/keys/pkcs11/000077500000000000000000000000001466362047600167505ustar00rootroot00000000000000trillian-1.6.1/crypto/keys/pkcs11/no_pkcs11.go000066400000000000000000000020071466362047600210740ustar00rootroot00000000000000//go:build !pkcs11 // +build !pkcs11 // 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 pkcs11 provides access to private keys using a PKCS#11 interface. package pkcs11 import ( "crypto" "errors" "github.com/google/trillian/crypto/keyspb" ) // FromConfig returns an error indicating that PKCS11 is not supported. func FromConfig(_ string, _ *keyspb.PKCS11Config) (crypto.Signer, error) { return nil, errors.New("pkcs11: Not supported in this binary") } trillian-1.6.1/crypto/keys/pkcs11/pkcs11.go000066400000000000000000000030641466362047600204040ustar00rootroot00000000000000//go:build pkcs11 // +build pkcs11 // 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 pkcs11 provides access to private keys using a PKCS#11 interface. package pkcs11 import ( "crypto" "crypto/x509" "encoding/pem" "errors" "fmt" "github.com/google/trillian/crypto/keyspb" pkcs11key "github.com/letsencrypt/pkcs11key/v4" ) // FromConfig returns a crypto.Signer that uses a PKCS#11 interface. func FromConfig(modulePath string, config *keyspb.PKCS11Config) (crypto.Signer, error) { if modulePath == "" { return nil, errors.New("pkcs11: No module path") } pubKeyPEM := config.GetPublicKey() block, rest := pem.Decode([]byte(pubKeyPEM)) if len(rest) > 0 { return nil, fmt.Errorf("pkcs11: extra data found after first PEM block from %q", pubKeyPEM) } pubKey, err := x509.ParsePKIXPublicKey(block.Bytes) if err != nil { return nil, fmt.Errorf("pkcs11: error loading public key from %q: %v", pubKeyPEM, err) } return pkcs11key.New(modulePath, config.GetTokenLabel(), config.GetPin(), pubKey) } trillian-1.6.1/crypto/keys/pkcs11/pkcs11_test.go000066400000000000000000000016071466362047600214440ustar00rootroot00000000000000//go:build pkcs11 // +build pkcs11 // 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 pkcs11 import ( "testing" _ "k8s.io/klog/v2" ) func TestPkcs11(t *testing.T) { // PKCS11Config support is tested by integration/log_integration.sh (when $WITH_PKCS11 == "true"). t.Skip("Only integration testing is implemented for PKCS#11") } trillian-1.6.1/crypto/keys/testonly/000077500000000000000000000000001466362047600175275ustar00rootroot00000000000000trillian-1.6.1/crypto/keys/testonly/keys.go000066400000000000000000000063521466362047600210370ustar00rootroot00000000000000// 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 testonly contains code and data that should only be used by tests. // Production code MUST NOT depend on anything in this package. This will be // enforced by tools where possible. package testonly import ( "crypto" "crypto/ecdsa" "crypto/rand" "crypto/rsa" "crypto/sha256" "crypto/x509" "encoding/asn1" "encoding/pem" "errors" "fmt" "math/big" "golang.org/x/crypto/ed25519" ) // MustMarshalPublicPEMToDER reads a PEM-encoded public key and returns it in DER encoding. // If an error occurs, it panics. func MustMarshalPublicPEMToDER(keyPEM string) []byte { block, _ := pem.Decode([]byte(keyPEM)) key, err := x509.ParsePKIXPublicKey(block.Bytes) if err != nil { panic(err) } keyDER, err := x509.MarshalPKIXPublicKey(key) if err != nil { panic(err) } return keyDER } // SignAndVerify exercises a signer by using it to generate a signature, and // then verifies that this signature is correct. func SignAndVerify(signer crypto.Signer, pubKey crypto.PublicKey) error { hasher := crypto.SHA256 msg := []byte("test") digest := sha256.Sum256(msg) var signature []byte var err error switch pubKey.(type) { case ed25519.PublicKey: // Ed25519 performs two passes over the data and so takes the whole message not just the digest. signature, err = signer.Sign(rand.Reader, msg, crypto.Hash(0)) default: signature, err = signer.Sign(rand.Reader, digest[:], hasher) } if err != nil { return err } switch pubKey := pubKey.(type) { case *ecdsa.PublicKey: return verifyECDSA(pubKey, digest[:], signature) case *rsa.PublicKey: return verifyRSA(pubKey, digest[:], signature, hasher, hasher) case ed25519.PublicKey: return verifyEd25519(pubKey, msg, signature) default: return fmt.Errorf("unknown public key type: %T", pubKey) } } func verifyECDSA(pubKey *ecdsa.PublicKey, digest, sig []byte) error { var ecdsaSig struct { R, S *big.Int } rest, err := asn1.Unmarshal(sig, &ecdsaSig) if err != nil { return err } if len(rest) != 0 { return fmt.Errorf("ECDSA signature %v bytes longer than expected", len(rest)) } if !ecdsa.Verify(pubKey, digest, ecdsaSig.R, ecdsaSig.S) { return errors.New("ECDSA signature failed verification") } return nil } func verifyRSA(pubKey *rsa.PublicKey, digest, sig []byte, hasher crypto.Hash, opts crypto.SignerOpts) error { if pssOpts, ok := opts.(*rsa.PSSOptions); ok { return rsa.VerifyPSS(pubKey, hasher, digest, sig, pssOpts) } return rsa.VerifyPKCS1v15(pubKey, hasher, digest, sig) } func verifyEd25519(pubKey ed25519.PublicKey, digest, sig []byte) error { if !ed25519.Verify(pubKey, digest, sig) { return errors.New("ed25519 signature failed verification") } return nil } trillian-1.6.1/crypto/keyspb/000077500000000000000000000000001466362047600161705ustar00rootroot00000000000000trillian-1.6.1/crypto/keyspb/keyspb.pb.go000066400000000000000000000603451466362047600204240ustar00rootroot00000000000000// 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. // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.34.2 // protoc v3.20.1 // source: crypto/keyspb/keyspb.proto package keyspb import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // The supported elliptic curves. type Specification_ECDSA_Curve int32 const ( Specification_ECDSA_DEFAULT_CURVE Specification_ECDSA_Curve = 0 // Curve will be chosen by Trillian. Specification_ECDSA_P256 Specification_ECDSA_Curve = 1 Specification_ECDSA_P384 Specification_ECDSA_Curve = 2 Specification_ECDSA_P521 Specification_ECDSA_Curve = 3 ) // Enum value maps for Specification_ECDSA_Curve. var ( Specification_ECDSA_Curve_name = map[int32]string{ 0: "DEFAULT_CURVE", 1: "P256", 2: "P384", 3: "P521", } Specification_ECDSA_Curve_value = map[string]int32{ "DEFAULT_CURVE": 0, "P256": 1, "P384": 2, "P521": 3, } ) func (x Specification_ECDSA_Curve) Enum() *Specification_ECDSA_Curve { p := new(Specification_ECDSA_Curve) *p = x return p } func (x Specification_ECDSA_Curve) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (Specification_ECDSA_Curve) Descriptor() protoreflect.EnumDescriptor { return file_crypto_keyspb_keyspb_proto_enumTypes[0].Descriptor() } func (Specification_ECDSA_Curve) Type() protoreflect.EnumType { return &file_crypto_keyspb_keyspb_proto_enumTypes[0] } func (x Specification_ECDSA_Curve) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use Specification_ECDSA_Curve.Descriptor instead. func (Specification_ECDSA_Curve) EnumDescriptor() ([]byte, []int) { return file_crypto_keyspb_keyspb_proto_rawDescGZIP(), []int{0, 0, 0} } // Specification for a private key. type Specification struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // The type of parameters provided determines the algorithm used for the key. // // Types that are assignable to Params: // // *Specification_EcdsaParams // *Specification_RsaParams // *Specification_Ed25519Params Params isSpecification_Params `protobuf_oneof:"params"` } func (x *Specification) Reset() { *x = Specification{} if protoimpl.UnsafeEnabled { mi := &file_crypto_keyspb_keyspb_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Specification) String() string { return protoimpl.X.MessageStringOf(x) } func (*Specification) ProtoMessage() {} func (x *Specification) ProtoReflect() protoreflect.Message { mi := &file_crypto_keyspb_keyspb_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Specification.ProtoReflect.Descriptor instead. func (*Specification) Descriptor() ([]byte, []int) { return file_crypto_keyspb_keyspb_proto_rawDescGZIP(), []int{0} } func (m *Specification) GetParams() isSpecification_Params { if m != nil { return m.Params } return nil } func (x *Specification) GetEcdsaParams() *Specification_ECDSA { if x, ok := x.GetParams().(*Specification_EcdsaParams); ok { return x.EcdsaParams } return nil } func (x *Specification) GetRsaParams() *Specification_RSA { if x, ok := x.GetParams().(*Specification_RsaParams); ok { return x.RsaParams } return nil } func (x *Specification) GetEd25519Params() *Specification_Ed25519 { if x, ok := x.GetParams().(*Specification_Ed25519Params); ok { return x.Ed25519Params } return nil } type isSpecification_Params interface { isSpecification_Params() } type Specification_EcdsaParams struct { // The parameters for an ECDSA key. EcdsaParams *Specification_ECDSA `protobuf:"bytes,1,opt,name=ecdsa_params,json=ecdsaParams,proto3,oneof"` } type Specification_RsaParams struct { // The parameters for an RSA key. RsaParams *Specification_RSA `protobuf:"bytes,2,opt,name=rsa_params,json=rsaParams,proto3,oneof"` } type Specification_Ed25519Params struct { // The parameters for an Ed25519 key. Ed25519Params *Specification_Ed25519 `protobuf:"bytes,3,opt,name=ed25519_params,json=ed25519Params,proto3,oneof"` } func (*Specification_EcdsaParams) isSpecification_Params() {} func (*Specification_RsaParams) isSpecification_Params() {} func (*Specification_Ed25519Params) isSpecification_Params() {} // PEMKeyFile identifies a private key stored in a PEM-encoded file. type PEMKeyFile struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // File path of the private key. Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` // Password for decrypting the private key. // If empty, indicates that the private key is not encrypted. Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"` } func (x *PEMKeyFile) Reset() { *x = PEMKeyFile{} if protoimpl.UnsafeEnabled { mi := &file_crypto_keyspb_keyspb_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *PEMKeyFile) String() string { return protoimpl.X.MessageStringOf(x) } func (*PEMKeyFile) ProtoMessage() {} func (x *PEMKeyFile) ProtoReflect() protoreflect.Message { mi := &file_crypto_keyspb_keyspb_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use PEMKeyFile.ProtoReflect.Descriptor instead. func (*PEMKeyFile) Descriptor() ([]byte, []int) { return file_crypto_keyspb_keyspb_proto_rawDescGZIP(), []int{1} } func (x *PEMKeyFile) GetPath() string { if x != nil { return x.Path } return "" } func (x *PEMKeyFile) GetPassword() string { if x != nil { return x.Password } return "" } // PrivateKey is a private key, used for generating signatures. type PrivateKey struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // The key in DER-encoded form. // The specific format (e.g. PKCS8) is not specified. Der []byte `protobuf:"bytes,1,opt,name=der,proto3" json:"der,omitempty"` } func (x *PrivateKey) Reset() { *x = PrivateKey{} if protoimpl.UnsafeEnabled { mi := &file_crypto_keyspb_keyspb_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *PrivateKey) String() string { return protoimpl.X.MessageStringOf(x) } func (*PrivateKey) ProtoMessage() {} func (x *PrivateKey) ProtoReflect() protoreflect.Message { mi := &file_crypto_keyspb_keyspb_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use PrivateKey.ProtoReflect.Descriptor instead. func (*PrivateKey) Descriptor() ([]byte, []int) { return file_crypto_keyspb_keyspb_proto_rawDescGZIP(), []int{2} } func (x *PrivateKey) GetDer() []byte { if x != nil { return x.Der } return nil } // PublicKey is a public key, used for verifying signatures. type PublicKey struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // The key in DER-encoded PKIX form. Der []byte `protobuf:"bytes,1,opt,name=der,proto3" json:"der,omitempty"` } func (x *PublicKey) Reset() { *x = PublicKey{} if protoimpl.UnsafeEnabled { mi := &file_crypto_keyspb_keyspb_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *PublicKey) String() string { return protoimpl.X.MessageStringOf(x) } func (*PublicKey) ProtoMessage() {} func (x *PublicKey) ProtoReflect() protoreflect.Message { mi := &file_crypto_keyspb_keyspb_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use PublicKey.ProtoReflect.Descriptor instead. func (*PublicKey) Descriptor() ([]byte, []int) { return file_crypto_keyspb_keyspb_proto_rawDescGZIP(), []int{3} } func (x *PublicKey) GetDer() []byte { if x != nil { return x.Der } return nil } // PKCS11Config identifies a private key accessed using PKCS #11. type PKCS11Config struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // The label of the PKCS#11 token. TokenLabel string `protobuf:"bytes,1,opt,name=token_label,json=tokenLabel,proto3" json:"token_label,omitempty"` // The PIN for the specific token. Pin string `protobuf:"bytes,2,opt,name=pin,proto3" json:"pin,omitempty"` // The PEM public key associated with the private key to be used. PublicKey string `protobuf:"bytes,3,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` } func (x *PKCS11Config) Reset() { *x = PKCS11Config{} if protoimpl.UnsafeEnabled { mi := &file_crypto_keyspb_keyspb_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *PKCS11Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*PKCS11Config) ProtoMessage() {} func (x *PKCS11Config) ProtoReflect() protoreflect.Message { mi := &file_crypto_keyspb_keyspb_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use PKCS11Config.ProtoReflect.Descriptor instead. func (*PKCS11Config) Descriptor() ([]byte, []int) { return file_crypto_keyspb_keyspb_proto_rawDescGZIP(), []int{4} } func (x *PKCS11Config) GetTokenLabel() string { if x != nil { return x.TokenLabel } return "" } func (x *PKCS11Config) GetPin() string { if x != nil { return x.Pin } return "" } func (x *PKCS11Config) GetPublicKey() string { if x != nil { return x.PublicKey } return "" } // / ECDSA defines parameters for an ECDSA key. type Specification_ECDSA struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // The elliptic curve to use. // Optional. If not set, the default curve will be used. Curve Specification_ECDSA_Curve `protobuf:"varint,1,opt,name=curve,proto3,enum=keyspb.Specification_ECDSA_Curve" json:"curve,omitempty"` } func (x *Specification_ECDSA) Reset() { *x = Specification_ECDSA{} if protoimpl.UnsafeEnabled { mi := &file_crypto_keyspb_keyspb_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Specification_ECDSA) String() string { return protoimpl.X.MessageStringOf(x) } func (*Specification_ECDSA) ProtoMessage() {} func (x *Specification_ECDSA) ProtoReflect() protoreflect.Message { mi := &file_crypto_keyspb_keyspb_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Specification_ECDSA.ProtoReflect.Descriptor instead. func (*Specification_ECDSA) Descriptor() ([]byte, []int) { return file_crypto_keyspb_keyspb_proto_rawDescGZIP(), []int{0, 0} } func (x *Specification_ECDSA) GetCurve() Specification_ECDSA_Curve { if x != nil { return x.Curve } return Specification_ECDSA_DEFAULT_CURVE } // RSA defines parameters for an RSA key. type Specification_RSA struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Size of the keys in bits. Must be sufficiently large to allow two primes // to be generated. // Optional. If not set, the key size will be chosen by Trillian. Bits int32 `protobuf:"varint,1,opt,name=bits,proto3" json:"bits,omitempty"` } func (x *Specification_RSA) Reset() { *x = Specification_RSA{} if protoimpl.UnsafeEnabled { mi := &file_crypto_keyspb_keyspb_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Specification_RSA) String() string { return protoimpl.X.MessageStringOf(x) } func (*Specification_RSA) ProtoMessage() {} func (x *Specification_RSA) ProtoReflect() protoreflect.Message { mi := &file_crypto_keyspb_keyspb_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Specification_RSA.ProtoReflect.Descriptor instead. func (*Specification_RSA) Descriptor() ([]byte, []int) { return file_crypto_keyspb_keyspb_proto_rawDescGZIP(), []int{0, 1} } func (x *Specification_RSA) GetBits() int32 { if x != nil { return x.Bits } return 0 } // Ed25519 defines (empty) parameters for an Ed25519 private key. type Specification_Ed25519 struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *Specification_Ed25519) Reset() { *x = Specification_Ed25519{} if protoimpl.UnsafeEnabled { mi := &file_crypto_keyspb_keyspb_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Specification_Ed25519) String() string { return protoimpl.X.MessageStringOf(x) } func (*Specification_Ed25519) ProtoMessage() {} func (x *Specification_Ed25519) ProtoReflect() protoreflect.Message { mi := &file_crypto_keyspb_keyspb_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Specification_Ed25519.ProtoReflect.Descriptor instead. func (*Specification_Ed25519) Descriptor() ([]byte, []int) { return file_crypto_keyspb_keyspb_proto_rawDescGZIP(), []int{0, 2} } var File_crypto_keyspb_keyspb_proto protoreflect.FileDescriptor var file_crypto_keyspb_keyspb_proto_rawDesc = []byte{ 0x0a, 0x1a, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x2f, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x62, 0x2f, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x62, 0x22, 0x81, 0x03, 0x0a, 0x0d, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x40, 0x0a, 0x0c, 0x65, 0x63, 0x64, 0x73, 0x61, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x62, 0x2e, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x45, 0x43, 0x44, 0x53, 0x41, 0x48, 0x00, 0x52, 0x0b, 0x65, 0x63, 0x64, 0x73, 0x61, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x3a, 0x0a, 0x0a, 0x72, 0x73, 0x61, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x62, 0x2e, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x52, 0x53, 0x41, 0x48, 0x00, 0x52, 0x09, 0x72, 0x73, 0x61, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x46, 0x0a, 0x0e, 0x65, 0x64, 0x32, 0x35, 0x35, 0x31, 0x39, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x62, 0x2e, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x45, 0x64, 0x32, 0x35, 0x35, 0x31, 0x39, 0x48, 0x00, 0x52, 0x0d, 0x65, 0x64, 0x32, 0x35, 0x35, 0x31, 0x39, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x7a, 0x0a, 0x05, 0x45, 0x43, 0x44, 0x53, 0x41, 0x12, 0x37, 0x0a, 0x05, 0x63, 0x75, 0x72, 0x76, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x62, 0x2e, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x45, 0x43, 0x44, 0x53, 0x41, 0x2e, 0x43, 0x75, 0x72, 0x76, 0x65, 0x52, 0x05, 0x63, 0x75, 0x72, 0x76, 0x65, 0x22, 0x38, 0x0a, 0x05, 0x43, 0x75, 0x72, 0x76, 0x65, 0x12, 0x11, 0x0a, 0x0d, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x5f, 0x43, 0x55, 0x52, 0x56, 0x45, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x50, 0x32, 0x35, 0x36, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x50, 0x33, 0x38, 0x34, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x50, 0x35, 0x32, 0x31, 0x10, 0x03, 0x1a, 0x19, 0x0a, 0x03, 0x52, 0x53, 0x41, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x69, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x62, 0x69, 0x74, 0x73, 0x1a, 0x09, 0x0a, 0x07, 0x45, 0x64, 0x32, 0x35, 0x35, 0x31, 0x39, 0x42, 0x08, 0x0a, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x3c, 0x0a, 0x0a, 0x50, 0x45, 0x4d, 0x4b, 0x65, 0x79, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x1e, 0x0a, 0x0a, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x64, 0x65, 0x72, 0x22, 0x1d, 0x0a, 0x09, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x64, 0x65, 0x72, 0x22, 0x60, 0x0a, 0x0c, 0x50, 0x4b, 0x43, 0x53, 0x31, 0x31, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x70, 0x69, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x42, 0x2a, 0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2f, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x2f, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( file_crypto_keyspb_keyspb_proto_rawDescOnce sync.Once file_crypto_keyspb_keyspb_proto_rawDescData = file_crypto_keyspb_keyspb_proto_rawDesc ) func file_crypto_keyspb_keyspb_proto_rawDescGZIP() []byte { file_crypto_keyspb_keyspb_proto_rawDescOnce.Do(func() { file_crypto_keyspb_keyspb_proto_rawDescData = protoimpl.X.CompressGZIP(file_crypto_keyspb_keyspb_proto_rawDescData) }) return file_crypto_keyspb_keyspb_proto_rawDescData } var file_crypto_keyspb_keyspb_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_crypto_keyspb_keyspb_proto_msgTypes = make([]protoimpl.MessageInfo, 8) var file_crypto_keyspb_keyspb_proto_goTypes = []any{ (Specification_ECDSA_Curve)(0), // 0: keyspb.Specification.ECDSA.Curve (*Specification)(nil), // 1: keyspb.Specification (*PEMKeyFile)(nil), // 2: keyspb.PEMKeyFile (*PrivateKey)(nil), // 3: keyspb.PrivateKey (*PublicKey)(nil), // 4: keyspb.PublicKey (*PKCS11Config)(nil), // 5: keyspb.PKCS11Config (*Specification_ECDSA)(nil), // 6: keyspb.Specification.ECDSA (*Specification_RSA)(nil), // 7: keyspb.Specification.RSA (*Specification_Ed25519)(nil), // 8: keyspb.Specification.Ed25519 } var file_crypto_keyspb_keyspb_proto_depIdxs = []int32{ 6, // 0: keyspb.Specification.ecdsa_params:type_name -> keyspb.Specification.ECDSA 7, // 1: keyspb.Specification.rsa_params:type_name -> keyspb.Specification.RSA 8, // 2: keyspb.Specification.ed25519_params:type_name -> keyspb.Specification.Ed25519 0, // 3: keyspb.Specification.ECDSA.curve:type_name -> keyspb.Specification.ECDSA.Curve 4, // [4:4] is the sub-list for method output_type 4, // [4:4] is the sub-list for method input_type 4, // [4:4] is the sub-list for extension type_name 4, // [4:4] is the sub-list for extension extendee 0, // [0:4] is the sub-list for field type_name } func init() { file_crypto_keyspb_keyspb_proto_init() } func file_crypto_keyspb_keyspb_proto_init() { if File_crypto_keyspb_keyspb_proto != nil { return } if !protoimpl.UnsafeEnabled { file_crypto_keyspb_keyspb_proto_msgTypes[0].Exporter = func(v any, i int) any { switch v := v.(*Specification); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_crypto_keyspb_keyspb_proto_msgTypes[1].Exporter = func(v any, i int) any { switch v := v.(*PEMKeyFile); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_crypto_keyspb_keyspb_proto_msgTypes[2].Exporter = func(v any, i int) any { switch v := v.(*PrivateKey); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_crypto_keyspb_keyspb_proto_msgTypes[3].Exporter = func(v any, i int) any { switch v := v.(*PublicKey); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_crypto_keyspb_keyspb_proto_msgTypes[4].Exporter = func(v any, i int) any { switch v := v.(*PKCS11Config); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_crypto_keyspb_keyspb_proto_msgTypes[5].Exporter = func(v any, i int) any { switch v := v.(*Specification_ECDSA); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_crypto_keyspb_keyspb_proto_msgTypes[6].Exporter = func(v any, i int) any { switch v := v.(*Specification_RSA); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_crypto_keyspb_keyspb_proto_msgTypes[7].Exporter = func(v any, i int) any { switch v := v.(*Specification_Ed25519); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } } file_crypto_keyspb_keyspb_proto_msgTypes[0].OneofWrappers = []any{ (*Specification_EcdsaParams)(nil), (*Specification_RsaParams)(nil), (*Specification_Ed25519Params)(nil), } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_crypto_keyspb_keyspb_proto_rawDesc, NumEnums: 1, NumMessages: 8, NumExtensions: 0, NumServices: 0, }, GoTypes: file_crypto_keyspb_keyspb_proto_goTypes, DependencyIndexes: file_crypto_keyspb_keyspb_proto_depIdxs, EnumInfos: file_crypto_keyspb_keyspb_proto_enumTypes, MessageInfos: file_crypto_keyspb_keyspb_proto_msgTypes, }.Build() File_crypto_keyspb_keyspb_proto = out.File file_crypto_keyspb_keyspb_proto_rawDesc = nil file_crypto_keyspb_keyspb_proto_goTypes = nil file_crypto_keyspb_keyspb_proto_depIdxs = nil } trillian-1.6.1/crypto/keyspb/keyspb.proto000066400000000000000000000052311466362047600205530ustar00rootroot00000000000000// 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. syntax = "proto3"; option go_package = "github.com/google/trillian/crypto/keyspb"; package keyspb; // Specification for a private key. message Specification { /// ECDSA defines parameters for an ECDSA key. message ECDSA { // The supported elliptic curves. enum Curve { DEFAULT_CURVE = 0; // Curve will be chosen by Trillian. P256 = 1; P384 = 2; P521 = 3; } // The elliptic curve to use. // Optional. If not set, the default curve will be used. Curve curve = 1; } // RSA defines parameters for an RSA key. message RSA { // Size of the keys in bits. Must be sufficiently large to allow two primes // to be generated. // Optional. If not set, the key size will be chosen by Trillian. int32 bits = 1; } // Ed25519 defines (empty) parameters for an Ed25519 private key. message Ed25519 { } // The type of parameters provided determines the algorithm used for the key. oneof params { // The parameters for an ECDSA key. ECDSA ecdsa_params = 1; // The parameters for an RSA key. RSA rsa_params = 2; // The parameters for an Ed25519 key. Ed25519 ed25519_params = 3; } } // PEMKeyFile identifies a private key stored in a PEM-encoded file. message PEMKeyFile { // File path of the private key. string path = 1; // Password for decrypting the private key. // If empty, indicates that the private key is not encrypted. string password = 2; } // PrivateKey is a private key, used for generating signatures. message PrivateKey { // The key in DER-encoded form. // The specific format (e.g. PKCS8) is not specified. bytes der = 1; } // PublicKey is a public key, used for verifying signatures. message PublicKey { // The key in DER-encoded PKIX form. bytes der = 1; } // PKCS11Config identifies a private key accessed using PKCS #11. message PKCS11Config { // The label of the PKCS#11 token. string token_label = 1; // The PIN for the specific token. string pin = 2; // The PEM public key associated with the private key to be used. string public_key = 3; } trillian-1.6.1/deployment/000077500000000000000000000000001466362047600155335ustar00rootroot00000000000000trillian-1.6.1/deployment/.gitignore000066400000000000000000000000321466362047600175160ustar00rootroot00000000000000.terraform/ tf-patch.yaml trillian-1.6.1/deployment/README.md000066400000000000000000000042251466362047600170150ustar00rootroot00000000000000# Trillian supported deployments This directory contains the supported deployment types for Trillian. Currently, this includes: - [Kubernetes on GCP using Cloud Spanner](#kubernetes-on-gcp-using-cloud-spanner) Further deployment types (community-supported) can be found in the [examples/deployment](/examples/deployment) directory. # Kubernetes on GCP using Cloud Spanner ## Architecture The infrastructure is created using Terraform. It consists of a Cloud Spanner database, a Trillian-specific etcd cluster, the Trillian logserver and logsigner. A Workload Identity is set up by Terraform to give the services access to Cloud Spanner. The container images are based on v1.3.3. ## Prerequisites 1. [Terraform](https://www.terraform.io/), [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/), and [gcloud](https://cloud.google.com/sdk/gcloud/) are installed. 1. You have a Google account with billing configured. 1. Terraform should have access to the credentials to manage your Cloud Project. Follow the instructions from the [Terraform Google Provider documentation](https://www.terraform.io/docs/providers/google/guides/provider_reference.html#full-reference) to set this up. ## Installation 1. Create a new project and copy its [Project ID](https://cloud.google.com/resource-manager/docs/creating-managing-projects#identifying_projects). 1. Create the infrastructure: `terraform init && terraform apply -var="gcp_project=PROJECT_ID"`. It is ok to re-execute this command at any time. 1. The script will output a gcloud command similar to: ```shell gcloud config set project PROJECT_ID && \ gcloud container clusters get-credentials cluster --region=us-west1 ``` Execute this set of commands to get the credentials for your new cluster. 1. Deploy the containers: `kubectl apply -k .` This last command may fail with an [error](https://github.com/google/trillian/issues/1820) similar to: `unable to recognize ".": no matches for kind "EtcdCluster" in version "etcd.database.coreos.com/v1beta2"`. In this case, simply re-run it until the error is not reported anymore. ## Uninstall 1. Destroy the infrastructure: `terraform destroy`. trillian-1.6.1/deployment/deploy-config.yaml000066400000000000000000000007371466362047600211650ustar00rootroot00000000000000apiVersion: v1 kind: ConfigMap metadata: name: deploy-config data: STORAGE_SYSTEM: cloud_spanner STORAGE_FLAG: --cloudspanner_uri=projects/PROJECT_ID/instances/trillian-spanner/databases/trillian-db SIGNER_DEQUEUE_BUCKET_FRACTION: "--cloudspanner_dequeue_bucket_fraction=0.0078" SIGNER_BATCH_SIZE: "--batch_size=1800" SIGNER_INTERVAL: "--sequencer_interval=2ms" SIGNER_NUM_SEQUENCERS: "--num_sequencers=10" SIGNER_MASTER_HOLD_JITTER: "--master_hold_jitter=7200s" trillian-1.6.1/deployment/etcd-cluster.yaml000066400000000000000000000016271466362047600210230ustar00rootroot00000000000000apiVersion: "etcd.database.coreos.com/v1beta2" kind: "EtcdCluster" metadata: name: "trillian-etcd-cluster" annotations: etcd.database.coreos.com/scope: clusterwide spec: size: 5 version: "3.2.13" pod: annotations: # Do not inject an Istio sidecar, because Etcd nodes require a network # connection on startup and don't retry on failure. This is a problem for # Istio because it takes a moment to start its proxy sidecar and, during # that time, all network connections will be blocked. sidecar.istio.io/inject: "false" affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - podAffinityTerm: labelSelector: matchExpressions: - key: etcd_cluster operator: In values: - trillian-etcd-cluster topologyKey: kubernetes.io/hostname trillian-1.6.1/deployment/etcd-crd.yaml000066400000000000000000000006741466362047600201130ustar00rootroot00000000000000apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: name: etcdclusters.etcd.database.coreos.com spec: group: etcd.database.coreos.com conversion: strategy: None names: kind: EtcdCluster listKind: EtcdClusterList plural: etcdclusters shortNames: - etcd singular: etcdcluster scope: Namespaced version: v1beta2 versions: - name: v1beta2 served: true storage: true trillian-1.6.1/deployment/etcd-deployment.yaml000066400000000000000000000013371466362047600215200ustar00rootroot00000000000000apiVersion: extensions/v1beta1 kind: Deployment metadata: name: trillian-etcd-operator # Cluster-wide etcd-operator, so should always be in default namespace. namespace: default spec: replicas: 1 template: metadata: labels: name: trillian-etcd-operator spec: containers: - name: trillian-etcd-operator image: quay.io/coreos/etcd-operator:v0.9.4 command: - etcd-operator - --cluster-wide - --create-crd=false env: - name: MY_POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace - name: MY_POD_NAME valueFrom: fieldRef: fieldPath: metadata.name trillian-1.6.1/deployment/etcd-role-binding.yaml000066400000000000000000000004061466362047600217050ustar00rootroot00000000000000apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRoleBinding metadata: name: etcd-operator roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: etcd-operator subjects: - kind: ServiceAccount name: default namespace: default trillian-1.6.1/deployment/etcd-role.yaml000066400000000000000000000012131466362047600202720ustar00rootroot00000000000000apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRole metadata: name: etcd-operator rules: - apiGroups: - etcd.database.coreos.com resources: - etcdclusters - etcdbackups - etcdrestores verbs: - "*" - apiGroups: - apiextensions.k8s.io resources: - customresourcedefinitions verbs: - "*" - apiGroups: - "" resources: - pods - services - endpoints - persistentvolumeclaims - events verbs: - "*" - apiGroups: - apps resources: - deployments verbs: - "*" # The following permissions can be removed if not using S3 backup and TLS - apiGroups: - "" resources: - secrets verbs: - get trillian-1.6.1/deployment/kustomization.yaml000066400000000000000000000005731466362047600213440ustar00rootroot00000000000000apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - etcd-crd.yaml - etcd-role-binding.yaml - etcd-role.yaml - etcd-deployment.yaml - etcd-cluster.yaml - deploy-config.yaml - service-account.yaml - log-deployment.yaml - log-service.yaml - log-signer-deployment.yaml - log-signer-service.yaml patchesStrategicMerge: - tf-patch.yaml trillian-1.6.1/deployment/log-deployment.yaml000066400000000000000000000042561466362047600213650ustar00rootroot00000000000000apiVersion: extensions/v1beta1 kind: Deployment metadata: labels: app.kubernetes.io/name: trillian app.kubernetes.io/component: logserver name: trillian-log-deployment spec: replicas: 4 template: metadata: labels: app.kubernetes.io/name: trillian app.kubernetes.io/component: logserver spec: serviceAccountName: trillian-svc restartPolicy: Always containers: - name: trillian-logserver args: [ "$(STORAGE_FLAG)", "--storage_system=$(STORAGE_SYSTEM)", "--quota_system=etcd", "--etcd_servers=trillian-etcd-cluster-client:2379", "--etcd_http_service=trillian-logserver-http", "--rpc_endpoint=0.0.0.0:8090", "--http_endpoint=0.0.0.0:8091", "--tracing", "--alsologtostderr" ] envFrom: - configMapRef: name: deploy-config image: gcr.io/trillian-opensource-ci/log_server:v1.3.3 imagePullPolicy: Always resources: limits: cpu: "1.0" requests: cpu: "0.4" livenessProbe: httpGet: path: /healthz port: 8091 failureThreshold: 3 periodSeconds: 30 timeoutSeconds: 5 ports: - containerPort: 8090 name: grpc - containerPort: 8091 name: http-metrics - name: prometheus-to-sd image: gcr.io/google-containers/prometheus-to-sd:v0.5.2 ports: - name: profiler containerPort: 6060 command: - /monitor - --stackdriver-prefix=custom.googleapis.com - --source=logserver:http://localhost:8091/metrics - --pod-id=$(POD_NAME) - --namespace-id=$(POD_NAMESPACE) - --scrape-interval=5s - --export-interval=60s resources: limits: cpu: 20m requests: cpu: 20m env: - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace trillian-1.6.1/deployment/log-service.yaml000066400000000000000000000005651466362047600206440ustar00rootroot00000000000000apiVersion: v1 kind: Service metadata: name: trillian-log-service annotations: cloud.google.com/load-balancer-type: "Internal" spec: type: LoadBalancer ports: - name: grpc port: 8090 targetPort: 8090 - name: http-metrics port: 8091 targetPort: 8091 selector: app.kubernetes.io/name: trillian app.kubernetes.io/component: logserver trillian-1.6.1/deployment/log-signer-deployment.yaml000066400000000000000000000042761466362047600226540ustar00rootroot00000000000000apiVersion: extensions/v1beta1 kind: Deployment metadata: labels: app.kubernetes.io/name: trillian app.kubernetes.io/component: logsigner name: trillian-log-signer-deployment spec: replicas: 2 template: metadata: labels: app.kubernetes.io/name: trillian app.kubernetes.io/component: logsigner spec: serviceAccountName: trillian-svc restartPolicy: Always containers: - name: trillian-log-signer args: [ "$(STORAGE_FLAG)", "--storage_system=$(STORAGE_SYSTEM)", "--etcd_servers=trillian-etcd-cluster-client:2379", "--quota_system=etcd", "--etcd_http_service=trillian-logsigner-http", "--http_endpoint=0.0.0.0:8091", "--sequencer_guard_window=1s", "$(SIGNER_INTERVAL)", "$(SIGNER_NUM_SEQUENCERS)", "$(SIGNER_BATCH_SIZE)", "$(SIGNER_DEQUEUE_BUCKET_FRACTION)", "$(SIGNER_MASTER_HOLD_JITTER)", "--alsologtostderr" ] envFrom: - configMapRef: name: deploy-config image: gcr.io/trillian-opensource-ci/log_signer:v1.3.3 imagePullPolicy: Always resources: limits: cpu: "1" requests: cpu: "1" livenessProbe: httpGet: path: /healthz port: 8091 failureThreshold: 3 periodSeconds: 30 timeoutSeconds: 5 ports: - containerPort: 8091 name: http-metrics - name: prometheus-to-sd image: gcr.io/google-containers/prometheus-to-sd:v0.5.2 ports: - name: profiler containerPort: 6060 command: - /monitor - --stackdriver-prefix=custom.googleapis.com - --source=logsigner:http://localhost:8091/metrics - --pod-id=$(POD_NAME) - --namespace-id=$(POD_NAMESPACE) - --scrape-interval=5s - --export-interval=60s env: - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace trillian-1.6.1/deployment/log-signer-service.yaml000066400000000000000000000005111466362047600221200ustar00rootroot00000000000000apiVersion: v1 kind: Service metadata: name: trillian-log-signer-service annotations: cloud.google.com/load-balancer-type: "Internal" spec: type: LoadBalancer ports: - name: http-metrics port: 8092 targetPort: 8091 selector: app.kubernetes.io/name: trillian app.kubernetes.io/component: logsigner trillian-1.6.1/deployment/main.tf000066400000000000000000000072761466362047600170260ustar00rootroot00000000000000variable "gcp_project" { type = string } variable "region" { type = string default = "us-west1" } provider "google" { project = var.gcp_project version = "~> 3.0.0-beta.1" } provider "google-beta" { project = var.gcp_project version = "~> 3.0.0-beta.1" } # Enable required API in the project resource "google_project_service" "container-api" { service = "container.googleapis.com" } resource "google_project_service" "spanner-api" { service = "spanner.googleapis.com" } # Main cluster definition ## Force recent version for Kubernetes master data "google_container_engine_versions" "gke-ver" { location = var.region version_prefix = "1.19." } resource "google_container_cluster" "trillian-cluster" { provider = google-beta name = "trillian-opensource-ci" location = var.region node_version = data.google_container_engine_versions.gke-ver.latest_node_version min_master_version = data.google_container_engine_versions.gke-ver.latest_node_version initial_node_count = 3 node_config { machine_type = "e2-standard-2" image_type = "COS" workload_metadata_config { node_metadata = "GKE_METADATA_SERVER" } oauth_scopes = [ "https://www.googleapis.com/auth/logging.write", "https://www.googleapis.com/auth/monitoring", "https://www.googleapis.com/auth/devstorage.read_only" ] } workload_identity_config { identity_namespace = "${var.gcp_project}.svc.id.goog" } depends_on = [ google_project_service.container-api ] } # Spanner instance locals { trillian_ddl = file("${path.module}/../storage/cloudspanner/spanner.sdl") } resource "google_spanner_instance" "trillian-spanner" { name = "trillian-spanner" display_name = "Trillian Spanner Instance" config = "regional-${var.region}" num_nodes = 1 depends_on = [ google_project_service.spanner-api ] } resource "google_spanner_database" "trillian-db" { instance = google_spanner_instance.trillian-spanner.name name = "trillian-db" # Format the DDL (remove comment and split the lines) ddl = split(";", replace(replace(local.trillian_ddl, "/--.*\\n/", ""), "\n", "")) } # Create a GCP service account, give it access to Spanner and add a binding so that a # kubernetes service account can use that account. resource "google_service_account" "trillian" { account_id = "trillian" display_name = "Trillian service account" } resource "google_project_iam_binding" "trillian-sc-dbuser" { role = "roles/spanner.databaseUser" members = [ "serviceAccount:${google_service_account.trillian.email}", ] } resource "google_project_iam_binding" "trillian-sc-logwriter" { role = "roles/logging.logWriter" members = [ "serviceAccount:${google_service_account.trillian.email}", ] } resource "google_project_iam_binding" "trillian-sc-metricwriter" { role = "roles/monitoring.metricWriter" members = [ "serviceAccount:${google_service_account.trillian.email}", ] } resource "google_service_account_iam_binding" "trillian-sc-identity" { service_account_id = google_service_account.trillian.name role = "roles/iam.workloadIdentityUser" members = [ "serviceAccount:${var.gcp_project}.svc.id.goog[default/trillian-svc]", ] depends_on = [ google_container_cluster.trillian-cluster ] } output "next_steps" { value = < Credentials page, click Create credentials > API key. A key will automatically be created. 2. Restrict API key to only have access to: * Google Cloud APIs * Cloud Spanner API 3. Download service account key (we'll call that file `service-key.json`) 4. run: `kubectl create secret generic spanner-key --from-file=key.json=service-key.json` 4. Create Spanner instance & database 1. Click on menu > Cloud Spanner 2. Click on "Create Instance" (we'll call its instance ID `${SPANNER_INSTANCE}`) 4. Choose region (we'll call that ${REGION}) 5. Choose number of nodes 6. Click create Database 1. Fill in a name (we'll call the DB instance `${DATABASE_INSTANCE}`) 2. Click continue 4. In `Define your database schema`, click the `Edit as text` slider 3. paste contents of [spanner.sd](storage/cloudspanner/spanner.sdl) into the text box (you may need to remove the SQL comments prefixed with `--` at the top) 4. Click on create 5. Create kubernetes cluster 1. menu > Kubernetes 2. click on Create Cluster 3. Set cluster name to something (we'll call this `${CLUSTER_NAME}`) 4. Set zone to something inside ${REGION} 5. Click create 6. Start initial jobs 1. Edit [scripts/deploy_gce.sh](scripts/deploy.sh) and configure the environment variables for your deployment. 1. run: `./scripts/deploy_gce.sh` Setting up continuous integration ============================================= Now that you have a working Trillian-on-cloud instance, you can integrate it with CI/CD so that pushes to master update your Trillian instance. 1. Create service account credentials 1. In your Cloud Platform Console project, open the Credentials page. 2. Click Create credentials > Service account key. 3. Under Service account select New service account. 4. Enter a Service account name, e.g. trillian-pusher-ci 5. Under Role, select Project > Editor. 6. Under Key type, select JSON. 7. Click Create. The Cloud Platform Console downloads a new JSON file to your computer. The name of this file starts with your project ID. 8. Provide the service key to the deploy script that CI runs. You might need the output of the following command: `base64 service-key.json | tr -d '\040\011\012\015'` - Ensure that the key is hidden from build logs (example: [Cloud Build](https://cloud.google.com/cloud-build/docs/securing-builds/use-secrets)). trillian-1.6.1/docs/Feature_Implementation_Matrix.md000066400000000000000000000143061466362047600226150ustar00rootroot00000000000000# Trillian Feature/Implementation Matrix - [Overview](#overview) - [Functionality](#functionality) - [Log v1](#trillian-log-v1) - [Log v2](#trillian-log-v2-skylog) - [Concrete implementations](#concrete-implementations) - [Storage](#storage) - [Monitoring](#monitoring) - [Master election](#master-election) - [Quota](#quota) ## Overview This page summarises the various features which are present in Trillian, and their implementation status. The status of features is listed as one of: * Not Implemented (NI) * In Development * Partial * Alpha * Beta * General Availability (GA) * Deprecated (⚠) ## Functionality | | Status | Deployed in production | Notes | |:--- | :---: | :---: |:--- | | Log V1 | GA | ✓ | | ### Trillian Log V1 This is feature complete, and is actively used in production by multiple CT log operators, including Google. ### Trillian Log V2 (Skylog) Skylog is an append-only log with considerably higher throughput and lower integration latency than the v1 log. It currently exists as internal prototype. ## Concrete implementations This section lists the status of implementations for the _pluggable_ subsystems which Trillian supports. ### Storage Trillian supports "pluggable" storage implementations for durable storage of the merkle tree data. The state and characteristics of these implementations are detailed below. #### V1 log storage The Log storage implementations supporting the original Trillian log. | Storage | Status | Deployed in prod | Notes | |:--- | :---: | :---: |:--- | | Spanner | GA | ✓ | Google internal-only, see CloudSpanner for external use. | | CloudSpanner | Beta | | Google maintains continuous-integration environment based on CloudSpanner. | | MySQL | GA | ✓ | | | CockroachDB | Alpha | | Supported by [Equinix Metal](https://deploy.equinix.com/). | ##### Spanner This is a Google-internal implementation, and is used by all of Google's current Trillian deployments. ##### CloudSpanner This implementation uses the Google CloudSpanner APIs in GCE. It's been tested to tens of billions of entries and tens of log tenants. Performance largely depends on the number of CloudSpanner servers allocated, but write throughput of 1000+ entries/s has been observed. [Issue #1681](https://github.com/google/trillian/issues/1681) tracks this becoming ready for GA. ##### MySQL This implementation has been tested with MySQL 5.7. It's currently in production use by at least one CT log operator. Write throughput of 4-500 entries/s has been observed. ##### CockroachDB This implementation has been tested with CockroachDB 22.1.10. It's currently in alpha mode and is not yet in production use. ### Monitoring Supported monitoring frameworks, allowing for production monitoring and alerting. | Monitoring | Status | Deployed in prod | Notes | |:--- | :---: | :---: |:--- | | Prometheus | GA | ✓ | | | OpenCensus | Partial | | Currently, only support for Tracing is implemented. | ### Master election Supported frameworks for providing Master Election. | Election | Status | Deployed in prod | Notes | |:--- | :---: | :---: |:--- | | Chubby | GA | ✓ | Google internal-only. | | etcd | GA | ✓ | | ### Quota Supported frameworks for quota management. | Implementation | Status | Deployed in prod | Notes | |:--- | :---: | :---: |:--- | | Google internal | GA | ✓ | | | etcd | GA | ✓ | | | MySQL | Beta | ? | | | Redis | Alpha | ✓ | | | CockroachDB | Alpha | | Supported by [Equinix Metal](https://deploy.equinix.com/). | ### Key management Supported frameworks for key management and signing. | Election | Status | Deployed in prod | Notes | |:--- | :---: | :---: |:--- | | Google internal | GA | ✓ | | | golang stdlib | GA | | i.e PEM files, etc. | | PKCS#11 | GA | ? | | trillian-1.6.1/docs/MapHashers.md000066400000000000000000000162331466362047600166650ustar00rootroot00000000000000# Map Hashers This document describes the constraints on and requirements for hashing strategies to be used with a Trillian Map. ## Background A Trillian Map is a transparent key:value store based on an underlying sparse [Merkle tree](https://en.wikipedia.org/wiki/Merkle_tree). The leaves of the tree hold arbitrary values (`MapLeaf.LeafValue`), and the location of each leaf is specified by its index, a fixed size bitstring (`MapLeaf.Index`). ### Merkle Trees A Merkle tree is then formed by arranging these leaves into a binary tree, and assigning a cryptographic hash value to each point in the tree: - Leaves get a hash value that is derived from the value of the leaf (and other inputs). - Interior nodes get a hash value that is derived from hashes of the two children of the node (plus other inputs). This structure means that the hash value associated with the root of the tree cryptographically encompasses the whole tree: - If any leaf value changes, the root hash will also change. - An adversary can't 'fake up' a tree that reproduces a specific root hash (unless they are able to generate arbitrary hash collisions). ### Addressing The bit pattern of the leaves index also gives us an addressing scheme for all of the nodes in the tree: - The full index value for a leaf (i.e. level 0 in the tree) is its address. - The index value for an interior node is one bit shorter than its two children, truncated at the end. For a toy example with index values that are 8 bits long: - Leaves (level 0) would have addresses `0b00000000`, `0b000000001`, …, `0b11111111` (256 of them). - Level 1 nodes would have addresses `0b0000000x`, `0b00000001x`, …, `0b1111111x` (128 of them), and (say) `0b1010110x` has children with addresses `0b10101100` (left) and `0b10101101` (right). - Level 2 nodes have addresses `0b000000xx`, …, `0x111111xx`. - So on… - Level 7 nodes have addresses `0b0xxxxxxx`, `0b1xxxxxxx` (2 of them). - The root node (level 8) has an address which is the empty bitstring. ### Sparseness The example above used a toy 8-bit index, but a real example would use a much longer hash such as SHA-256 to generate index values. This means that the set of all possible leaves is huge (2256 for SHA-256), and the set of leaves which actually have values is much smaller – the Merkle tree is **sparse**. This is essential, because calculating the hash values for every node in a 256-depth Merkle tree is quite time-consuming: - 2256 hashes for level 0 - 2255 hashes for level 1 - … - 1 hash for level 256 for a total of 2257 hash calculations (less one). The sparseness property of the tree allows us to skip most of these calculations: if we know (in advance) what the hash value for a particular **empty subtree** is, we can use that hash value and skip the calculation of anything further down the tree. As long as we have this property, then a tree that has **`N`** leaves with values can be hashed much more quickly: - `N` hashes for level 0 - At most `N/2` hashes for level 1. - At most `N/4` hashes for level 2. - … - 1 hash for level 256 for a total of (at most) `2 N` hashes for the whole tree. Also, if the tree is incrementally updated with `M` new leaf values, a similar calculation indicates that at most `2 M` hashes need to be recalculated up the tree. ## MapHasher Interface In the Trillian codebase, the `Tree.HashStrategy` indicates the hashing strategy in use for a particular tree, and the implementation of the hashing strategy needs to satisfy the [`MapHasher` interface](../merkle/hashers/tree_hasher.go). The following sections describe the current implementations of this `interface`. ### Test Map Hasher Scheme Trillian includes a [simple hashing strategy](../merkle/maphasher/maphasher.go) for test trees, where: - The hash of a leaf is `HashLeaf(leaf) := HASH(0x00 || leaf.LeafValue)` for an arbitrary hash function (SHA-256 by default). - The hash of a non-empty interior node is `HashChildren(l, r) := HASH(0x01 || l || r)`. - The hash of an empty subtree at level `n` is recursively build from the leaf hashes of an empty leaf: - `n = 0`: `E0 := HashLeaf(nil)` - `n = 1`: `E1 := HashChildren(E0, E0)` - `n = 2`: `E2 := HashChildren(E1, E1)` - … This hash strategy has some simple properties: - The hash of any node is independent of its address/location in the tree - The hash of a zero-length leaf value is the same as the hash of an empty (never set) leaf (i.e. `HashLeaf(nil) == HashEmpty(_, 0)`). - More generally, the hash of the top of an empty subtree (whose leaves are all empty/never-set) is the same as the hash of the same size subtree with leaves with zero-length values. However, this means that this hash strategy is more vulnerable to attacks where subtrees are transplanted between different locations in the tree. ### CONIKS Hasher Strategy The CONIKS hashing strategy ([original paper](https://www.usenix.org/system/files/conference/usenixsecurity15/sec15-paper-melara.pdf)) is more secure, but is more complicated because: - Hash values always depend on the location / address of the node in the tree - Empty subtrees have a different hash value than the equivalent subtree with leaves that have zero-length values. This has to be the case to preserve the efficiency of the sparse Merkle tree: given that all hashes are location dependent, if the hash of an empty subtree were the same as that of a zero-length-leaves subtree, the hasher would need to calculate all 2257 hashes for the whole Merkle tree. The details of the hash strategy are that: - The hash of a leaf that exists is `HASH('L' || treeID || index || depth || leaf.LeafValue)`. - The hash of a non-empty interior node is `HASH(l || r)`. - The hash of an empty node is `HASH('E' || treeID || index || depth)` ## Trillian Map API As described above, the map hashing strategy includes a specific hash calculation that applies to subtrees where all of the enclosed leaves are empty (have never been set). This allows hash calculations to be more efficient for a sparse tree, but also allows for a more efficient Map API. In particular, any values in a leaf inclusion proof that match the empty hash value for that node need not be sent on the wire. Instead, a `nil` value is used to indicate that this node in the proof is the top of an empty subtree (a subtree all of whose leaves are empty / never-been-set). Also, the distinction between empty / never-set leaves, and leaves whose values have been explicitly set to a zero-length value, means that two possible hash values (`leaf.LeafHash`) apply for leaves with `len(leaf.LeafValue)==0`. So: - A `trillian.MapLeaf` with `len(leaf.LeafValue) == 0` may have two potential `leaf.LeafHash` values: - `len(leaf.LeafHash) == 0` if it has never been set (a shortcut equivalent to `hasher.HashEmpty(leaf.Index, 0)`) - `leaf.LeafHash == hasher.HashLeaf(leaf.Index, nil)` otherwise. - The inclusion proof in a `trillian.MapLeafInclusion` may include `nil` values for `inc.Inclusion[level]` values, which indicates that a value of `hasher.Empty(leaf.Index, level)` applies. trillian-1.6.1/docs/Personalities.md000066400000000000000000000273661466362047600174640ustar00rootroot00000000000000# Trillian Personalities - [Overview](#overview) - [Conceptual Responsibilities](#conceptual-responsibilities) - [Admission Control](#admission-control) - [Canonicalization](#canonicalization) - [Auditability](#auditability) - [Practical Responsibilities](#practical-responsibilities) - [External API](#external-api) - [Storage](#storage) - [Traffic Control](#traffic-control) - [Monitoring](#monitoring) - [Examples](#examples) - [Certificate Transparency](#certificate-transparency) - [Gossip Hub](#gossip-hub) ## Overview The core Trillian service provides a Merkle tree store (and primitives to interact with it), but a full transparency application requires additional functionality on top of that base, which needs to be provided by a separate *personality*. The following sections describe the conceptual responsibilities of a Trillian personality, followed by the practical responsibilities that are likely to be needed for real-world use. Overall design considerations for transparent Log applications are also [discussed in a separate document](TransparentLogging.md). ## Conceptual Responsibilities ### Admission Control The primary purpose of a personality is to implement **admission criteria** for the store, so that only particular types of data are added to the store. For example, a Certificate Transparency Log only accepts data items that are valid certificates; a "CT Log" personality would police this, so that the Trillian service can process all incoming data blindly. It is common for these admission criteria to be broken into two distinct but related areas: - **Structure**: Submissions to the personality have to be in a well known format, thus limiting the transparent data to a particular field of interest. (For example, for CT the structure must be a DER-encoded X.509 certificate.) - **Authentication**: Submissions to the personality may have to be signed by a known authority, in which case the personality must be configured with a set of acceptable authorities. (For CT, each submission must chain to one of a set of accepted web PKI root certificates.) ### Canonicalization A personality may also perform **canonicalization** on incoming data, to convert equivalent formulations of the same underlying data to a single canonical format, avoiding needless duplication. For example, keys in JSON dictionaries could be sorted, or Unicode string data could be normalised. One particularly important form of canonicalization is **de-duplication** of timestamped data: if the Merkle leaf contents includes a timestamp indicating when the leaf was added, subsequent submissions of the same data with a new timestamp should not be treated as a new leaf. This use case is supported by the Trillian API, which includes the concept of an *identity hash* associated with a data item: items with the same identity hash are assumed equal, even if the full Merkle leaf structure differs (e.g. because it has a different timestamp attached). This means that a personality that wants to prevent duplicate entries can do so by setting the `LeafIdentityHash` value on new Merkle tree leaves appropriately. The personality code also needs to cope with duplicates: the Merkle tree leaf in a successful `QueueLeaves` response may not be the same as the tree leaf in the corresponding `QueueLeaves` request (it will have the same `LeafIdentityHash` but may differ in fields that are not covered by this hash, e.g. it may have an earlier timestamp). ## Practical Responsibilities ### External API In many environments the personality provides an externally-visible API. The design of such an API depends on the application, but key points to consider include: - **syntax** – the mechanism used to access the API, where possibilities include: - HTTPS + JSON: A web-based interface has the advantage of being accessible using standard tools, ranging from [standard](https://golang.org/pkg/net/http/) [libraries](https://docs.python.org/3/library/http.client.html) to command-line tools like `curl` and `wget`. (The Certificate Transparency API described in [RFC 6962](https://tools.ietf.org/html/rfc6962) takes this approach.) - gRPC: An RPC-based interface allows for additional tooling (such as mechanical checks of schema adherence) and may allow features that are not easily achieved in a RESTful web API (such as [continuous streaming](https://grpc.io/docs/guides/concepts.html#server-streaming-rpc) of data). - **semantics** – the set of primitives and data structures exposed by the API, for example: - What submission formats are accepted? - What cryptographic promises/guarantees does the personality generate? - Which Merkle tree primitives are exposed to external users? ### Storage A Trillian personality may need an additional storage mechanism over and above the Merkle tree storage provided by Trillian. For example, if Trillian is run by a separate operator, then [audit verification data may need to be stored](#auditability). However, note that the Trillian APIs allow a personality to store additional data that is associated with a leaf in the underlying Merkle tree, in the `ExtraData` field. This extra data is immutable but is not covered by the Merkle tree hashing algorithms, and so cannot be used for verifiable/cryptographic purposes (as it could be modified without affecting tree hash values). ### Traffic Control The core Trillian service can be configured to provide traffic control limits using its [quota system](https://godoc.org/github.com/google/trillian/quota); out of the box, this can be governed by: - global rate limits (for both read and write operations) - per-Merkle tree rate limits (for read and write). A particular application may also want to provide other kinds of limits. This is supported by the per-*user* quota limits, where the *user* is entirely specified by the Trillian personality, using the `ChargeTo` field in each Trillian API request. A typical use case for this is to add a `ChargeTo.User` string that identifies an external requestor, so traffic from a single source can be limited without affecting other users. However, the user quota system can also be used for more flexible limits – for example, by applying limits to particular authentication keys. ### Monitoring A personality that is in real-world use should probably be monitored, for performance, alerting and debugging. Trillian uses a [metrics interface](../monitoring/metrics.go) to support this, which can also be re-used in an associated personality. ## Examples This section describes two Trillian personalities, together with their choices for the various design decisions described above. ### Certificate Transparency Certificate Transparency (CT) is the original transparency application, and has been successful in improving the security of the web PKI ecosystem. It is also the most mature and widely deployed Trillian use case, built on the [CTFE personality](https://github.com/google/certificate-transparency-go/blob/master/trillian/ctfe). The [conceptual responsibilities](#conceptual-responsibilities) of the CTFE personality are given by: - Admission Control: Submissions have to take the form of a chain of valid X.509 certificates, each certificate signed by the next certificate in the chain, until a acceptable root certificate is reached. The set of accepted roots is configured in the personality at startup. - Canonicalization: Submissions have a `LeafIdentityHash` which covers the leaf certificate, but also have a timestamp generated by the CTFE. A re-submission of a previously-seen certificate will return the original leaf with its earlier timestamp. - Auditability: The CTFE personality assumes that the Trillian service is run by the same operator, so does not store personality-to-Trillian audit data. In terms of [practical responsibilities](#practical-responsibilities): - The external API to the CTFE personality is the HTTP(S)+JSON interface specified by [RFC 6962](https://tools.ietf.org/html/rfc6962), in both syntax and semantics. The CTFE personality is configured with private key material for each CT Log instance, and uses this to generate those API data structures that are signed – the Signed Tree Head (STH) and Signed Certificate Timestamp (SCT). - The CTFE personality does not require storage independent of the core Merkle tree. The chain of certificates used to validate the original submission of a leaf certificate are stored as the `ExtraData` associated with a Merkle tree leaf. - The CTFE personality can be configured to charge API requests to per-user quota for: - The IP address of the original requestor. - The set of intermediate and root certificates used to validate a submission. - Metrics are exposed using Trillian's metrics library, and sample Prometheus consoles and scripts are provided. ### Gossip Hub Users of individual transparent Logs are theoretically susceptible to a *split-view* attack, where a Log prevents a different view of its Merkle tree to different users – each user's view of the tree is internally consistent, but differs between users. A *gossip protocol* is a key mechanism to prevent this: different observers of a transparent Log share their views of the Log, allowing cross-comparison and eventual detection of split views. The [Gossip Hub](https://github.com/google/trillian-examples/blob/master/gossip) is an **experimental** implementation of a repository for information about a Log's Merkle tree, in the form of (another) transparent Log. This allows cross-comparison of the source Log's Merkle tree: different observers can submit their view of the source Log, and retrieve other observers' views of the same source Log. (Aside: in practice, split-view attacks are less of a concern for Certificate Transparency and the web PKI because browser policies typically require individual certificates to be logged in multiple independent CT Logs, limiting the scope of any individual Log's split-view attack.) The [conceptual responsibilities](#conceptual-responsibilities) of the Gossip Hub personality are given by: - Admission Control: Submissions take the form of signed blobs of data, and the data may be required to take the form of a valid RFC 6962 signed tree head structure. The set of accepted public keys from the source Logs, and the expected submission structure, is configured in the personality at start-of-day. - Canonicalization: Submissions have a `LeafIdentityHash` which covers the submitted data and signature, but also have a timestamp generated by the Hub. A re-submission of a previously-seen entry will return the original leaf with its earlier timestamp. - Auditability: The Hub personality assumes that the Trillian service is run by the same operator, so does not store personality-to-Trillian audit data. In terms of [practical responsibilities](#practical-responsibilities): - The external API to the Gossip Hub personality is an [HTTP(S)+JSON interface](https://github.com/google/trillian-examples/blob/master/gossip/api/types.go) analogous to that specified by [RFC 6962](https://tools.ietf.org/html/rfc6962), in both syntax and semantics. The Hub personality is configured with private key material for each Hub instance, and uses this to generate signed API data structures – the Signed Tree Head (STH) and Signed Gossip Timestamp (SGT). - The Hub personality does not require storage independent of the core Merkle tree, and the `ExtraData` field is not (currently) used. - The Hub personality can be configured to charge API requests to per-user quota for: - The IP address of the original requestor. - The public key used to sign a submission. - Metrics are exposed using Trillian's metrics library. trillian-1.6.1/docs/TransparentLogging.md000066400000000000000000000362661466362047600204520ustar00rootroot00000000000000# Transparent Logging: A Guide - [Introduction](#introduction) - [Running Examples](#running-examples) - [Ecosystem](#ecosystem) - [Log Contents](#log-contents) - [Leaf Hashing](#leaf-hashing) - [Leaf Size / Accessibility](#leaf-size--accessibility) - [Admission Control](#admission-control) - [Inclusion Proofs vs. Promises](#inclusion-proofs-vs-promises) ## Introduction The [Trillian project](https://github.com/google/trillian) generalizes the ideas behind [Certificate Transparency](https://certificate-transparency.org), to allow transparent, append-only logging of arbitrary data. This document works through some of the high-level design decisions involved in creating a transparent Log; it's a good idea to have a clear understanding of these decisions before writing any code. We assume the reader has a rough idea of the concepts involved in transparent Logs: [Merkle trees](https://en.wikipedia.org/wiki/Merkle_tree), [inclusion/consistency proofs](http://www.certificate-transparency.org/log-proofs-work), [signed tree heads](https://tools.ietf.org/html/rfc6962#section-3.5) etc. ### Running Examples Throughout this document, we will use three example scenarios for the use of a Log, to illustrate the considerations involved: - **Certificate Transparency**: The original transparent Log application, which allows logging of X.509 Certificates from Web PKI infrastructure. - **Binary Transparency**: A (so far) theoretical example where binary installable packages for a particular environment (e.g. OS+architecture) are logged. - **Pastebin**: Another theoretical example, of an open site that allows logging of arbitrary text blobs. Two of these examples will turn out to be a good match for transparent Logging; the other, not so much. ## Ecosystem The first fundamental question for designing a transparent Log is: **Why are you logging?** Understanding what the Log is intended to achieve allows you to check whether the transparent, append-only characteristics of the Log actually achieve those goals. It also helps you to understand how much of a wider ecosystem – auditors, monitors, indexes – also needs to be built up around the Log. - The [rationale for Certificate Transparency](https://tools.ietf.org/html/rfc6962) is to log "certificates as they are issued or observed, in a manner that allows anyone to audit certificate authority activity and notice the issuance of suspect certificates". ## Log Contents The second fundamental question for designing a transparent Log is: **What are you logging?** Ideally, you will have a single sentence that answers this question, and answers it sufficiently clearly that there is little ambiguity left in the implementation – the definition of the **leaf contents** for the Log should be mostly covered by this sentence. For example: - "*Certificate Transparency logs publicly trusted X.509 certificates*": The [leaf contents](https://github.com/google/certificate-transparency/blob/master/docs/images/RFC6962Structures.png) are essentially a certificate (plus a timestamp). - "*Binary Transparency logs binary packages that can be installed on XYZ architecture devices running ABC operating system*": The main part of the leaf contents would be the binary blob itself (or a hash of the binary blob to reduce leaf size). - "*Pastebin logs arbitrary text*": The leaf contents are just text. Reality soon intrudes and dilutes the clarity of these statements, but they remain useful as a guiding principle for what is and is not allowed as a variation on the log contents. - For Certificate Transparency, it's helpful to allow transparency proofs-of-logging to be included in the certificates themselves. However, this gives a chicken-and-egg problem: a certificate can only get a proof-of-logging after it's logged, and adding the proof changes the certificate. The solution to this quandary is [precertificates](https://github.com/google/certificate-transparency/blob/master/docs/SCTValidation.md#precertificates): a commitment to certificate issuance that can be logged in place of the real certificate. - For Binary Transparency, it might be the case that packages can arrive as either complete images, or deltas on a previous image. For the latter case, the leaf contents could include a reference to an earlier (complete) leaf that this delta applies on top of. ### Leaf Hashing A Trillian-based transparent Log deals with two distinct leaf hashes, which need to be defined for each Log application. The first hash for a leaf in Log is the **Merkle Hash**; this is the hash value that percolates up the Merkle tree and is therefore incorporated into the root hash for the Log; the cryptographic guarantees of the Log's Merkle tree only apply to data included in the Merkle hash. The default Merkle hash for a Trillian Log leaf is `SHA-256(0x00 | leaf.LeafValue)`. - For Certificate Transparency, this hash is defined by [RFC 6962](https://tools.ietf.org/html/rfc6962#section-3.4) to be the SHA-256 hash of a zero byte[1](#note-1) followed by a TLS-serialized `MerkleTreeLeaf` [structure, which includes](https://github.com/google/certificate-transparency/blob/master/docs/images/RFC6962Structures.png) the certificate together with a timestamp indicating when it was logged. A Trillian CT Log therefore sets `leaf.LeafValue` to be a TLS-serialized `MerkleTreeLeaf`. - For Binary Transparency, the data being logged might analogously be a structure holding both the (hash of the) binary package and a timestamp identifying when it was logged. A Trillian application may also specify a separate per-leaf **Identity Hash**; this identifies which leaf values should be considered equivalent, in the sense that an existing leaf with a given (application-provided) identity hash prevents Trillian from accepting any new leaves with the same identity hash. This feature is primarily designed to allow applications to detect (and squash) semantic duplicates, where two different leaf values actually represent the same underlying object. One particular example of this is for applications where it is important to record the time of logging a thing, together with the thing itself – where a later attempt to log something that is already logged should be rejected. This in turn is related to inclusion promises, see [below](#inclusion-proofs-vs-promises). - For a Trillian-based Certificate Transparency log, the identity hash is a hash over the logged certificate itself, without the tree leaf structure that includes the log timestamp. This means that a certificate only gets logged once; a second attempt to log the same certificate will have a duplicate identity hash even though the Merkle hash is different (because it has a different timestamp). - A Binary Transparency log might use an identity hash to weed out identical package versions, which just covers the binary package blob. - A Pastebin log might choose to not set the identity hash, in which case the Merkle leaf hash will be used for duplicate detection instead. ### Leaf Size / Accessibility Another consideration for designing a new transparent Log is the size of each leaf; Trillian can accommodate fairly large leaves, but would struggle with storing multi-gigabyte leaf contents[2](#note-2). In this situation we can appeal to the [fundamental theorem](https://en.wikipedia.org/wiki/Fundamental_theorem_of_software_engineering) of security engineering: > "We can solve any security problem by introducing an extra level of hashing." A large leaf blob can be stored in a separate (non-transparent) data store (e.g. a content-addressable store which allows retrieval of the blobs via their hashes), and the transparent Log carries the cryptographic hashes of the blobs rather than the blobs themselves. This approach may also be useful if there are situations where full public access to the logged data is not appropriate. - A Binary Transparency log might include pre-release packages that should not (yet) be available to the general public. - A Pastebin log might end up containing content that is not legal to distribute in some jurisdiction. ## Admission Control The general idea of transparency can be broken down into two constituent parts, which we'll call **read-transparency** and **write-transparency**: - A read-transparent Log allows any external client to read the contents of the Log, and to request cryptographic proofs of the append-only properties (inclusion proofs and consistency proofs). - A write-transparent Log allows any external client to submit new entries to the Log, and receive either an inclusion proof for the entry or a signed promise that it will be incorporated in future. Allowing write-transparency is vital for encouraging an ecosystem to grow up around the Log(s), and helps to reassure external (read-transparency) users that no "filtering" has been applied to content before it even reaches the log. However, it's worth making clear: a write-transparent Log allows arbitrary people on the Internet to write data into your append-only Log – and that **data can't be removed[3](#note-3) without destroying the Log**. That's a sufficiently terrifying prospect to motivate having strict **admission criteria**: checks that submitted content has to pass before being included in the Log. (However, this isn't specific to write-transparent Logs; even if submissions are restricted to a whitelist of clients, the admission criteria still need to be detailed.) Examples: - For Certificate Transparency, each submission has to take the form of an X.509 certificate in ASN.1 DER format, together with a chain of CA certificates that leads to one of a set of accepted root certificates. - For Binary Transparency, each submission might need to be of the correct layout/format to allow installation on a device, and might need to be signed by one of a set of acceptable package signing keys. - For a Pastebin Log, the admission criteria might merely be a size limit, or a UTF-8 encoding correctness check. These examples illustrate that good admission criteria typically include two key aspects: - Structure: The submissions have to be of the correct format to be of interest (so that the submissions match the subject of the Log), and that format has to be machine-checkable (but need not be strict – see below). - Authentication: The submissions are signed by one of a limited number of private keys, so the signature can be checked with the corresponding public key. The Log is thus implicitly: - trusting the key holders not to sign anything that might be problematic for the Log to host - assuming that it will be able to keep up with the numbers of objects signed with those private keys. For our examples: - A Certificate Transparency log requires entries to have X.509 structure, and trusted-roots authentication. - A Binary Transparency log requires entries to have the structure of an installable binary package, and vendor-signing key authentication. - A Pastebin log has little structural requirement and no authentication requirement, and so is much more vulnerable to different attacks (swamping, uploading illegal content etc.) Finally, note that a transparent Log normally acts as an **observatory, not as a police officer**, in the overall ecosystem. With this in mind, it's often sensible for the structure checks on submissions to be lax, so that technically-invalid objects that are still signed and distributed can be monitored and attributed. - A Certificate Transparency Log may well accept X.509 certificates that fail various sorts of validity check, as long as the signature validation is correct. This means that certificates that might be accepted by lax clients are still logged, and certificate authorities that issue invalid certificates can be held to account more easily. ## Inclusion Proofs vs. Promises Transparent Logs have two distinct mechanisms for guaranteeing inclusion of a particular entry in the Log. - An **inclusion proof** gives cryptographic proof that a particular Merkle tree head includes a given entry: the entry's hash and the proof hashes can be combined to recreate the root hash of the tree. - An **inclusion promise** is a signed commitment from the Log to incorporate a particular entry within a defined time period. This requires a separate subsequent check to confirm that the Log does indeed fulfil this promise. A related factor is the number of entries that are incorporated into each new tree head issued by the Log, which we'll call the tree head **batch size**. - Logs that issue inclusion promises can batch together multiple submissions (e.g. an hour's worth) into a new tree head. This is more likely to produce large batch sizes, but is not guaranteed to (e.g. if submission rates are very low). - Logs that only issue inclusion proofs are more likely to have smaller batch sizes, either because they issue a new tree head per submission (batch size = 1), or because the amount of batching possible while still responding to submissions quickly is small. The batch size per new tree head may be important for privacy reasons: a small batch size, particularly a batch of size 1, means that a new signed tree head correlates directly with a single submission. This in turn means that a user requesting proofs to/from that tree head, or gossiping that tree head, is likely to be interested in that specific entry in the Log. So, if privacy is a concern: - if submission rates are very low, then using inclusion promises with a large batch size is more likely to preserve privacy - if submission rates are very high, and some latency on submissions is acceptable, then just issuing inclusion proofs may be feasible. A new transparent Log should consider whether it (and the surrounding ecosystem) requires both inclusion promises and proofs, or just the latter, based on assessing the concerns above together with other factors: - Inclusion promises allow more operational flexibility; a Log can continue to issue promises even if its Merkle tree storage is temporarily unavailable, and the (potentially) larger batching involved may allow for a more efficient implementation. - However, issuing a promise that cannot subsequently be fulfilled (e.g. because Merkle storage is unavailable for longer than expected, or the backlog is too large) may be worse that simply rejecting the write operation in the first place. - Inclusion promises require separate monitoring to confirm the Log performs correctly, making the overall system more complicated. ------ 1: Prefix included for [second-preimage attack resistance](https://en.wikipedia.org/wiki/Merkle_tree#Second_preimage_attack). 2: The exact limits depend on the specific storage implementation in use (e.g. ~10MB for CloudSpanner). 3: This isn't strictly true – the Log could replace a removed leaf with a new leaf type that just holds the Merkle hash value of the full leaf (and define that the hash value for such a leaf is the identity).  However, the omission/replacement would be visible to a monitor that retrieved the log contents. trillian-1.6.1/docs/VerifiableDataStructures-Latest.md000066400000000000000000000142641466362047600230340ustar00rootroot00000000000000 # Verifiable Data Structures The Google TrustFabric team *Last updated in May 2021* ### Introduction Much of life relies on trust in various authorities. In the internet realm, site­ owners trust Certificate Authorities to issue certificates, mail clients may trust a key server to return the right public key for a recipient they wish to mail to, server administrators trust their package distributors to send them binaries for their software. In the real world, citizens trust land registries to accurately record who owns which land. This paper describes a number of data structures and their applications that allow adding transparency to the trust model, allowing an ecosystem to evolve from pure trust, to trust but verify. By adding transparency to services, trust can be verified by the ecosystems that depend upon them. ### Verifiable Logs The first structure we describe is an append­-only log. It begins empty and is mutated only by entries being appended to it. Once an entry has been accepted by the log, it can never be removed or changed. Periodically the log publishes a *checkpoint* that includes a commitment to all entries for a given log size. Clients of the log can: 1. Enumerate all entries held in the log. 2. Verify that a specific entry is included in the log, given just a checkpoint and an inclusion proof. 3. Verify the append-­only property of the log, given two checkpoints and a consistency proof. Furthermore, the fact that it is possible to efficiently check that a log is append-only makes it efficient to detect *split-­view attacks*, assuming a sufficient *gossip protocol* is in place for the log checkpoints. One common instantiation of a verifiable log is a Merkle tree, for example as described in [RFC6962](https://tools.ietf.org/html/rfc6962) and implemented in [Trillian](https://github.com/google/trillian). ![Verifiable log](vds/verifiableLog.png) ### Verifiable Maps The next structure we describe is a verifiable map. A verifiable map is a map from a set of keys to a corresponding set of values. Periodically the map publishes a signed map checkpoint that includes a commitment to all of its entries. Clients of the map can: 1. Enumerate all key/value pairs held in the map. 2. Retrieve the value (or indication of non­presence) for a key, and verify that this is included (or not present) in the map at a point in time, given a map checkpoint and an inclusion proof. Unlike for a verifiable log, there is no inherent way to efficiently verify that a map is append-only (see [this article](https://transparency.dev/articles/logs-vs-maps/) for more discussion). This in turn makes it hard to innately detect split-view attacks on maps that evolve over time, but we discuss below ways to address this. One common instantiation of a verifiable map is the Sparse Merkle Tree described in [Revocation Transparency](http://sump2.links.org/files/RevocationTransparency.pdf) and implemented in [Trillian](https://github.com/google/trillian/tree/master/experimental/batchmap). ![Verifiable map](vds/verifiableMap.png) ### Verifiable Log-Derived Map (VLDM) A verifiable log-derived map is a verifiable map created from a verifiable log in a deterministic and verifiable manner. The signed map checkpoint incorporates a log checkpoint, attesting that the map has included entries committed to by that checkpoint. In this manner a client gets the convenience of verifiable answers provided by a map, and the consistency guarantees provided by the underlying log. Any party willing to download and replay the entire log can verify the correct behavior of the map over time. This data structure can be used for arbitrary log entries and derivation functions, as long as these functions are deterministic. Importantly, the verifiable map may in fact be operated by an entirely different party than the backing log. For example, a third party Certificate Transparency monitor might choose to expose a verifiable map that maps domain names (as keys) to a set of X.509 certificates (as values). Such a monitor may expose a verifiable map based upon a Certificate Transparency log operated by another party. In order to address the difficulties of efficiently detecting split-view attacks on maps, all signed map checkpoints can be published to a separate verifiable log, called the map checkpoint log (MCL for short), which makes all checkpoints published by the map available. This log ensures that clients can verify that the version of the map they have been shown is globally visible. As with the map, the MCL may be operated by another party. 1. Entries in the log are used to populate the map. ![VLBM1](vds/VLDM1.png) 2. The log periodically publishes checkpoints. 3. The map consumes log entries in order and periodically publishes a new map checkpoint, incorporating a specific log checkpoint. ![VLBM2](vds/VLDM2.png) 4. Published map checkpoints are written to a verifiable log. 5. This map checkpoint log also periodically publishes its own checkpoints. ![VLBM3](vds/VLDM3.png) ### Summary The following table summarizes properties of the data structures laid out above. “Efficiently” means that a client can and should perform this validation themselves. “Full audit” means that to validate correctly, a client would need to download the entire dataset, and is something that in practices we expect a small number of dedicated auditors to perform, rather than being done by each client. Operation | Log | Map | Log-derived Map ------------- | :-----------------: | :-----------------: | :----------------------------------: Prove inclusion of value | Yes, efficiently | Yes, efficiently | Yes, efficiently Prove non-inclusion of value | Impractical | Yes, efficiently | Yes, efficiently Retrieve provable value for key | Impractical | Yes, efficiently | Yes, efficiently Retrieve provable current value for key | Impractical | No | Yes, efficiently Prove append-only | Yes, efficiently | No | Yes, efficiently, although full audit is required to verify complete correct operation. Enumerate all entries | Yes, by full audit | Yes, by full audit | Yes, by full audit Prove correct operation | Yes, efficiently | No | Yes, by full audit Enable detection of split-views | Yes, efficiently | No | Yes, efficiently trillian-1.6.1/docs/api.md000066400000000000000000001016761466362047600154110ustar00rootroot00000000000000# Protocol Documentation ## Table of Contents - [trillian_log_api.proto](#trillian_log_api-proto) - [AddSequencedLeavesRequest](#trillian-AddSequencedLeavesRequest) - [AddSequencedLeavesResponse](#trillian-AddSequencedLeavesResponse) - [ChargeTo](#trillian-ChargeTo) - [GetConsistencyProofRequest](#trillian-GetConsistencyProofRequest) - [GetConsistencyProofResponse](#trillian-GetConsistencyProofResponse) - [GetEntryAndProofRequest](#trillian-GetEntryAndProofRequest) - [GetEntryAndProofResponse](#trillian-GetEntryAndProofResponse) - [GetInclusionProofByHashRequest](#trillian-GetInclusionProofByHashRequest) - [GetInclusionProofByHashResponse](#trillian-GetInclusionProofByHashResponse) - [GetInclusionProofRequest](#trillian-GetInclusionProofRequest) - [GetInclusionProofResponse](#trillian-GetInclusionProofResponse) - [GetLatestSignedLogRootRequest](#trillian-GetLatestSignedLogRootRequest) - [GetLatestSignedLogRootResponse](#trillian-GetLatestSignedLogRootResponse) - [GetLeavesByRangeRequest](#trillian-GetLeavesByRangeRequest) - [GetLeavesByRangeResponse](#trillian-GetLeavesByRangeResponse) - [InitLogRequest](#trillian-InitLogRequest) - [InitLogResponse](#trillian-InitLogResponse) - [LogLeaf](#trillian-LogLeaf) - [QueueLeafRequest](#trillian-QueueLeafRequest) - [QueueLeafResponse](#trillian-QueueLeafResponse) - [QueuedLogLeaf](#trillian-QueuedLogLeaf) - [TrillianLog](#trillian-TrillianLog) - [trillian_admin_api.proto](#trillian_admin_api-proto) - [CreateTreeRequest](#trillian-CreateTreeRequest) - [DeleteTreeRequest](#trillian-DeleteTreeRequest) - [GetTreeRequest](#trillian-GetTreeRequest) - [ListTreesRequest](#trillian-ListTreesRequest) - [ListTreesResponse](#trillian-ListTreesResponse) - [UndeleteTreeRequest](#trillian-UndeleteTreeRequest) - [UpdateTreeRequest](#trillian-UpdateTreeRequest) - [TrillianAdmin](#trillian-TrillianAdmin) - [trillian.proto](#trillian-proto) - [Proof](#trillian-Proof) - [SignedLogRoot](#trillian-SignedLogRoot) - [Tree](#trillian-Tree) - [HashStrategy](#trillian-HashStrategy) - [LogRootFormat](#trillian-LogRootFormat) - [TreeState](#trillian-TreeState) - [TreeType](#trillian-TreeType) - [Scalar Value Types](#scalar-value-types)

Top

## trillian_log_api.proto ### AddSequencedLeavesRequest | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | log_id | [int64](#int64) | | | | leaves | [LogLeaf](#trillian-LogLeaf) | repeated | | | charge_to | [ChargeTo](#trillian-ChargeTo) | | | ### AddSequencedLeavesResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | results | [QueuedLogLeaf](#trillian-QueuedLogLeaf) | repeated | Same number and order as in the corresponding request. | ### ChargeTo ChargeTo describes the user(s) associated with the request whose quota should be checked and charged. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | user | [string](#string) | repeated | user is a list of personality-defined strings. Trillian will treat them as /User/%{user}/... keys when checking and charging quota. If one or more of the specified users has insufficient quota, the request will be denied. As an example, a Certificate Transparency frontend might set the following user strings when sending a QueueLeaf request to the Trillian log: - The requesting IP address. This would limit the number of requests per IP. - The "intermediate-<hash>" for each of the intermediate certificates in the submitted chain. This would have the effect of limiting the rate of submissions under a given intermediate/root. | ### GetConsistencyProofRequest | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | log_id | [int64](#int64) | | | | first_tree_size | [int64](#int64) | | | | second_tree_size | [int64](#int64) | | | | charge_to | [ChargeTo](#trillian-ChargeTo) | | | ### GetConsistencyProofResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | proof | [Proof](#trillian-Proof) | | The proof field may be empty if the requested tree_size was larger than that available at the server (e.g. because there is skew between server instances, and an earlier client request was processed by a more up-to-date instance). In this case, the signed_log_root field will indicate the tree size that the server is aware of, and the proof field will be empty. | | signed_log_root | [SignedLogRoot](#trillian-SignedLogRoot) | | | ### GetEntryAndProofRequest | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | log_id | [int64](#int64) | | | | leaf_index | [int64](#int64) | | | | tree_size | [int64](#int64) | | | | charge_to | [ChargeTo](#trillian-ChargeTo) | | | ### GetEntryAndProofResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | proof | [Proof](#trillian-Proof) | | | | leaf | [LogLeaf](#trillian-LogLeaf) | | | | signed_log_root | [SignedLogRoot](#trillian-SignedLogRoot) | | | ### GetInclusionProofByHashRequest | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | log_id | [int64](#int64) | | | | leaf_hash | [bytes](#bytes) | | The leaf hash field provides the Merkle tree hash of the leaf entry to be retrieved. | | tree_size | [int64](#int64) | | | | order_by_sequence | [bool](#bool) | | | | charge_to | [ChargeTo](#trillian-ChargeTo) | | | ### GetInclusionProofByHashResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | proof | [Proof](#trillian-Proof) | repeated | Logs can potentially contain leaves with duplicate hashes so it's possible for this to return multiple proofs. If the leaf index for a particular instance of the requested Merkle leaf hash is beyond the requested tree size, the corresponding proof entry will be missing. | | signed_log_root | [SignedLogRoot](#trillian-SignedLogRoot) | | | ### GetInclusionProofRequest | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | log_id | [int64](#int64) | | | | leaf_index | [int64](#int64) | | | | tree_size | [int64](#int64) | | | | charge_to | [ChargeTo](#trillian-ChargeTo) | | | ### GetInclusionProofResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | proof | [Proof](#trillian-Proof) | | The proof field may be empty if the requested tree_size was larger than that available at the server (e.g. because there is skew between server instances, and an earlier client request was processed by a more up-to-date instance). In this case, the signed_log_root field will indicate the tree size that the server is aware of, and the proof field will be empty. | | signed_log_root | [SignedLogRoot](#trillian-SignedLogRoot) | | | ### GetLatestSignedLogRootRequest | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | log_id | [int64](#int64) | | | | charge_to | [ChargeTo](#trillian-ChargeTo) | | | | first_tree_size | [int64](#int64) | | If first_tree_size is non-zero, the response will include a consistency proof between first_tree_size and the new tree size (if not smaller). | ### GetLatestSignedLogRootResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | signed_log_root | [SignedLogRoot](#trillian-SignedLogRoot) | | | | proof | [Proof](#trillian-Proof) | | proof is filled in with a consistency proof if first_tree_size in GetLatestSignedLogRootRequest is non-zero (and within the tree size available at the server). | ### GetLeavesByRangeRequest | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | log_id | [int64](#int64) | | | | start_index | [int64](#int64) | | | | count | [int64](#int64) | | | | charge_to | [ChargeTo](#trillian-ChargeTo) | | | ### GetLeavesByRangeResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | leaves | [LogLeaf](#trillian-LogLeaf) | repeated | Returned log leaves starting from the `start_index` of the request, in order. There may be fewer than `request.count` leaves returned, if the requested range extended beyond the size of the tree or if the server opted to return fewer leaves than requested. | | signed_log_root | [SignedLogRoot](#trillian-SignedLogRoot) | | | ### InitLogRequest | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | log_id | [int64](#int64) | | | | charge_to | [ChargeTo](#trillian-ChargeTo) | | | ### InitLogResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | created | [SignedLogRoot](#trillian-SignedLogRoot) | | | ### LogLeaf LogLeaf describes a leaf in the Log's Merkle tree, corresponding to a single log entry. Each leaf has a unique leaf index in the scope of this tree. Clients submitting new leaf entries should only set the following fields: - leaf_value - extra_data (optionally) - leaf_identity_hash (optionally) - leaf_index (iff the log is a PREORDERED_LOG) | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | merkle_leaf_hash | [bytes](#bytes) | | merkle_leaf_hash holds the Merkle leaf hash over leaf_value. This is calculated by the Trillian server when leaves are added to the tree, using the defined hashing algorithm and strategy for the tree; as such, the client does not need to set it on leaf submissions. | | leaf_value | [bytes](#bytes) | | leaf_value holds the data that forms the value of the Merkle tree leaf. The client should set this field on all leaf submissions, and is responsible for ensuring its validity (the Trillian server treats it as an opaque blob). | | extra_data | [bytes](#bytes) | | extra_data holds additional data associated with the Merkle tree leaf. The client may set this data on leaf submissions, and the Trillian server will return it on subsequent read operations. However, the contents of this field are not covered by and do not affect the Merkle tree hash calculations. | | leaf_index | [int64](#int64) | | leaf_index indicates the index of this leaf in the Merkle tree. This field is returned on all read operations, but should only be set for leaf submissions in PREORDERED_LOG mode (for a normal log the leaf index is assigned by Trillian when the submitted leaf is integrated into the Merkle tree). | | leaf_identity_hash | [bytes](#bytes) | | leaf_identity_hash provides a hash value that indicates the client's concept of which leaf entries should be considered identical. This mechanism allows the client personality to indicate that two leaves should be considered "duplicates" even though their `leaf_value`s differ. If this is not set on leaf submissions, the Trillian server will take its value to be the same as merkle_leaf_hash (and thus only leaves with identical leaf_value contents will be considered identical). For example, in Certificate Transparency each certificate submission is associated with a submission timestamp, but subsequent submissions of the same certificate should be considered identical. This is achieved by setting the leaf identity hash to a hash over (just) the certificate, whereas the Merkle leaf hash encompasses both the certificate and its submission time -- allowing duplicate certificates to be detected. Continuing the CT example, for a CT mirror personality (which must allow dupes since the source log could contain them), the part of the personality which fetches and submits the entries might set `leaf_identity_hash` to `H(leaf_index||cert)`. TODO(pavelkalinnikov): Consider instead using `H(cert)` and allowing identity hash dupes in `PREORDERED_LOG` mode, for it can later be upgraded to `LOG` which will need to correctly detect duplicates with older entries when new ones get queued. | | queue_timestamp | [google.protobuf.Timestamp](#google-protobuf-Timestamp) | | queue_timestamp holds the time at which this leaf was queued for inclusion in the Log, or zero if the entry was submitted without queuing. Clients should not set this field on submissions. | | integrate_timestamp | [google.protobuf.Timestamp](#google-protobuf-Timestamp) | | integrate_timestamp holds the time at which this leaf was integrated into the tree. Clients should not set this field on submissions. | ### QueueLeafRequest | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | log_id | [int64](#int64) | | | | leaf | [LogLeaf](#trillian-LogLeaf) | | | | charge_to | [ChargeTo](#trillian-ChargeTo) | | | ### QueueLeafResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | queued_leaf | [QueuedLogLeaf](#trillian-QueuedLogLeaf) | | queued_leaf describes the leaf which is or will be incorporated into the Log. If the submitted leaf was already present in the Log (as indicated by its leaf identity hash), then the returned leaf will be the pre-existing leaf entry rather than the submitted leaf. | ### QueuedLogLeaf QueuedLogLeaf provides the result of submitting an entry to the log. TODO(pavelkalinnikov): Consider renaming it to AddLogLeafResult or the like. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | leaf | [LogLeaf](#trillian-LogLeaf) | | The leaf as it was stored by Trillian. Empty unless `status.code` is: - `google.rpc.OK`: the `leaf` data is the same as in the request. - `google.rpc.ALREADY_EXISTS` or 'google.rpc.FAILED_PRECONDITION`: the `leaf` is the conflicting one already in the log. | | status | [google.rpc.Status](#google-rpc-Status) | | The status of adding the leaf. - `google.rpc.OK`: successfully added. - `google.rpc.ALREADY_EXISTS`: the leaf is a duplicate of an already existing one. Either `leaf_identity_hash` is the same in the `LOG` mode, or `leaf_index` in the `PREORDERED_LOG`. - `google.rpc.FAILED_PRECONDITION`: A conflicting entry is already present in the log, e.g., same `leaf_index` but different `leaf_data`. | ### TrillianLog The TrillianLog service provides access to an append-only Log data structure as described in the [Verifiable Data Structures](docs/papers/VerifiableDataStructures.pdf) paper. The API supports adding new entries to the Merkle tree for a specific Log instance (identified by its log_id) in two modes: - For a normal log, new leaf entries are queued up for subsequent inclusion in the log, and the leaves are assigned consecutive leaf_index values as part of that integration process. - For a 'pre-ordered log', new entries have an already-defined leaf ordering, and leaves are only integrated into the Merkle tree when a contiguous range of leaves is available. The API also supports read operations to retrieve leaf contents, and to provide cryptographic proofs of leaf inclusion and of the append-only nature of the Log. Each API request also includes a charge_to field, which allows API users to provide quota identifiers that should be "charged" for each API request (and potentially rejected with codes.ResourceExhausted). Various operations on the API also allows for 'server skew', which can occur when different API requests happen to be handled by different server instances that may not all be up to date. An API request that is relative to a specific tree size may reach a server instance that is not yet aware of this tree size; in this case the server will typically return an OK response that contains: - a signed log root that indicates the tree size that it is aware of - an empty response otherwise. | Method Name | Request Type | Response Type | Description | | ----------- | ------------ | ------------- | ------------| | QueueLeaf | [QueueLeafRequest](#trillian-QueueLeafRequest) | [QueueLeafResponse](#trillian-QueueLeafResponse) | QueueLeaf adds a single leaf to the queue of pending leaves for a normal log. | | GetInclusionProof | [GetInclusionProofRequest](#trillian-GetInclusionProofRequest) | [GetInclusionProofResponse](#trillian-GetInclusionProofResponse) | GetInclusionProof returns an inclusion proof for a leaf with a given index in a particular tree. If the requested tree_size is larger than the server is aware of, the response will include the latest known log root and an empty proof. | | GetInclusionProofByHash | [GetInclusionProofByHashRequest](#trillian-GetInclusionProofByHashRequest) | [GetInclusionProofByHashResponse](#trillian-GetInclusionProofByHashResponse) | GetInclusionProofByHash returns an inclusion proof for any leaves that have the given Merkle hash in a particular tree. If any of the leaves that match the given Merkle has have a leaf index that is beyond the requested tree size, the corresponding proof entry will be empty. | | GetConsistencyProof | [GetConsistencyProofRequest](#trillian-GetConsistencyProofRequest) | [GetConsistencyProofResponse](#trillian-GetConsistencyProofResponse) | GetConsistencyProof returns a consistency proof between different sizes of a particular tree. If the requested tree size is larger than the server is aware of, the response will include the latest known log root and an empty proof. | | GetLatestSignedLogRoot | [GetLatestSignedLogRootRequest](#trillian-GetLatestSignedLogRootRequest) | [GetLatestSignedLogRootResponse](#trillian-GetLatestSignedLogRootResponse) | GetLatestSignedLogRoot returns the latest log root for a given tree, and optionally also includes a consistency proof from an earlier tree size to the new size of the tree. If the earlier tree size is larger than the server is aware of, an InvalidArgument error is returned. | | GetEntryAndProof | [GetEntryAndProofRequest](#trillian-GetEntryAndProofRequest) | [GetEntryAndProofResponse](#trillian-GetEntryAndProofResponse) | GetEntryAndProof returns a log leaf and the corresponding inclusion proof to a specified tree size, for a given leaf index in a particular tree. If the requested tree size is unavailable but the leaf is in scope for the current tree, the returned proof will be for the current tree size rather than the requested tree size. | | InitLog | [InitLogRequest](#trillian-InitLogRequest) | [InitLogResponse](#trillian-InitLogResponse) | InitLog initializes a particular tree, creating the initial signed log root (which will be of size 0). | | AddSequencedLeaves | [AddSequencedLeavesRequest](#trillian-AddSequencedLeavesRequest) | [AddSequencedLeavesResponse](#trillian-AddSequencedLeavesResponse) | AddSequencedLeaves adds a batch of leaves with assigned sequence numbers to a pre-ordered log. The indices of the provided leaves must be contiguous. | | GetLeavesByRange | [GetLeavesByRangeRequest](#trillian-GetLeavesByRangeRequest) | [GetLeavesByRangeResponse](#trillian-GetLeavesByRangeResponse) | GetLeavesByRange returns a batch of leaves whose leaf indices are in a sequential range. |

Top

## trillian_admin_api.proto ### CreateTreeRequest CreateTree request. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | tree | [Tree](#trillian-Tree) | | Tree to be created. See Tree and CreateTree for more details. | ### DeleteTreeRequest DeleteTree request. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | tree_id | [int64](#int64) | | ID of the tree to delete. | ### GetTreeRequest GetTree request. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | tree_id | [int64](#int64) | | ID of the tree to retrieve. | ### ListTreesRequest ListTrees request. No filters or pagination options are provided. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | show_deleted | [bool](#bool) | | If true, deleted trees are included in the response. | ### ListTreesResponse ListTrees response. No pagination is provided, all trees the requester has access to are returned. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | tree | [Tree](#trillian-Tree) | repeated | Trees matching the list request filters. | ### UndeleteTreeRequest UndeleteTree request. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | tree_id | [int64](#int64) | | ID of the tree to undelete. | ### UpdateTreeRequest UpdateTree request. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | tree | [Tree](#trillian-Tree) | | Tree to be updated. | | update_mask | [google.protobuf.FieldMask](#google-protobuf-FieldMask) | | Fields modified by the update request. For example: "tree_state", "display_name", "description". | ### TrillianAdmin Trillian Administrative interface. Allows creation and management of Trillian trees. | Method Name | Request Type | Response Type | Description | | ----------- | ------------ | ------------- | ------------| | ListTrees | [ListTreesRequest](#trillian-ListTreesRequest) | [ListTreesResponse](#trillian-ListTreesResponse) | Lists all trees the requester has access to. | | GetTree | [GetTreeRequest](#trillian-GetTreeRequest) | [Tree](#trillian-Tree) | Retrieves a tree by ID. | | CreateTree | [CreateTreeRequest](#trillian-CreateTreeRequest) | [Tree](#trillian-Tree) | Creates a new tree. System-generated fields are not required and will be ignored if present, e.g.: tree_id, create_time and update_time. Returns the created tree, with all system-generated fields assigned. | | UpdateTree | [UpdateTreeRequest](#trillian-UpdateTreeRequest) | [Tree](#trillian-Tree) | Updates a tree. See Tree for details. Readonly fields cannot be updated. | | DeleteTree | [DeleteTreeRequest](#trillian-DeleteTreeRequest) | [Tree](#trillian-Tree) | Soft-deletes a tree. A soft-deleted tree may be undeleted for a certain period, after which it'll be permanently deleted. | | UndeleteTree | [UndeleteTreeRequest](#trillian-UndeleteTreeRequest) | [Tree](#trillian-Tree) | Undeletes a soft-deleted a tree. A soft-deleted tree may be undeleted for a certain period, after which it'll be permanently deleted. |

Top

## trillian.proto ### Proof Proof holds a consistency or inclusion proof for a Merkle tree, as returned by the API. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | leaf_index | [int64](#int64) | | leaf_index indicates the requested leaf index when this message is used for a leaf inclusion proof. This field is set to zero when this message is used for a consistency proof. | | hashes | [bytes](#bytes) | repeated | | ### SignedLogRoot SignedLogRoot represents a commitment by a Log to a particular tree. Note that the signature itself is no-longer provided by Trillian since https://github.com/google/trillian/pull/2452 . This functionality was intended to support a niche-use case but added significant complexity and was prone to causing confusion and misunderstanding for personality authors. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | log_root | [bytes](#bytes) | | log_root holds the TLS-serialization of the following structure (described in RFC5246 notation): enum { v1(1), (65535)} Version; struct { uint64 tree_size; opaque root_hash<0..128>; uint64 timestamp_nanos; uint64 revision; opaque metadata<0..65535>; } LogRootV1; struct { Version version; select(version) { case v1: LogRootV1; } } LogRoot; A serialized v1 log root will therefore be laid out as: +---+---+---+---+---+---+---+---+---+---+---+---+---+---+-....--+ | ver=1 | tree_size |len| root_hash | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+-....--+ +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | timestamp_nanos | revision | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+---+---+---+---+-....---+ | len | metadata | +---+---+---+---+---+-....---+ (with all integers encoded big-endian). | ### Tree Represents a tree. Readonly attributes are assigned at tree creation, after which they may not be modified. Note: Many APIs within the rest of the code require these objects to be provided. For safety they should be obtained via Admin API calls and not created dynamically. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | tree_id | [int64](#int64) | | ID of the tree. Readonly. | | tree_state | [TreeState](#trillian-TreeState) | | State of the tree. Trees are ACTIVE after creation. At any point the tree may transition between ACTIVE, DRAINING and FROZEN states. | | tree_type | [TreeType](#trillian-TreeType) | | Type of the tree. Readonly after Tree creation. Exception: Can be switched from PREORDERED_LOG to LOG if the Tree is and remains in the FROZEN state. | | display_name | [string](#string) | | Display name of the tree. Optional. | | description | [string](#string) | | Description of the tree, Optional. | | storage_settings | [google.protobuf.Any](#google-protobuf-Any) | | Storage-specific settings. Varies according to the storage implementation backing Trillian. | | max_root_duration | [google.protobuf.Duration](#google-protobuf-Duration) | | Interval after which a new signed root is produced even if there have been no submission. If zero, this behavior is disabled. | | create_time | [google.protobuf.Timestamp](#google-protobuf-Timestamp) | | Time of tree creation. Readonly. | | update_time | [google.protobuf.Timestamp](#google-protobuf-Timestamp) | | Time of last tree update. Readonly (automatically assigned on updates). | | deleted | [bool](#bool) | | If true, the tree has been deleted. Deleted trees may be undeleted during a certain time window, after which they're permanently deleted (and unrecoverable). Readonly. | | delete_time | [google.protobuf.Timestamp](#google-protobuf-Timestamp) | | Time of tree deletion, if any. Readonly. | ### HashStrategy Defines the way empty / node / leaf hashes are constructed incorporating preimage protection, which can be application specific. | Name | Number | Description | | ---- | ------ | ----------- | | UNKNOWN_HASH_STRATEGY | 0 | Hash strategy cannot be determined. Included to enable detection of mismatched proto versions being used. Represents an invalid value. | | RFC6962_SHA256 | 1 | Certificate Transparency strategy: leaf hash prefix = 0x00, node prefix = 0x01, empty hash is digest([]byte{}), as defined in the specification. | | TEST_MAP_HASHER | 2 | Sparse Merkle Tree strategy: leaf hash prefix = 0x00, node prefix = 0x01, empty branch is recursively computed from empty leaf nodes. NOT secure in a multi tree environment. For testing only. | | OBJECT_RFC6962_SHA256 | 3 | Append-only log strategy where leaf nodes are defined as the ObjectHash. All other properties are equal to RFC6962_SHA256. | | CONIKS_SHA512_256 | 4 | The CONIKS sparse tree hasher with SHA512_256 as the hash algorithm. | | CONIKS_SHA256 | 5 | The CONIKS sparse tree hasher with SHA256 as the hash algorithm. | ### LogRootFormat LogRootFormat specifies the fields that are covered by the SignedLogRoot signature, as well as their ordering and formats. | Name | Number | Description | | ---- | ------ | ----------- | | LOG_ROOT_FORMAT_UNKNOWN | 0 | | | LOG_ROOT_FORMAT_V1 | 1 | | ### TreeState State of the tree. | Name | Number | Description | | ---- | ------ | ----------- | | UNKNOWN_TREE_STATE | 0 | Tree state cannot be determined. Included to enable detection of mismatched proto versions being used. Represents an invalid value. | | ACTIVE | 1 | Active trees are able to respond to both read and write requests. | | FROZEN | 2 | Frozen trees are only able to respond to read requests, writing to a frozen tree is forbidden. Trees should not be frozen when there are entries in the queue that have not yet been integrated. See the DRAINING state for this case. | | DEPRECATED_SOFT_DELETED | 3 | Deprecated: now tracked in Tree.deleted. | | DEPRECATED_HARD_DELETED | 4 | Deprecated: now tracked in Tree.deleted. | | DRAINING | 5 | A tree that is draining will continue to integrate queued entries. No new entries should be accepted. | ### TreeType Type of the tree. | Name | Number | Description | | ---- | ------ | ----------- | | UNKNOWN_TREE_TYPE | 0 | Tree type cannot be determined. Included to enable detection of mismatched proto versions being used. Represents an invalid value. | | LOG | 1 | Tree represents a verifiable log. | | PREORDERED_LOG | 3 | Tree represents a verifiable pre-ordered log, i.e., a log whose entries are placed according to sequence numbers assigned outside of Trillian. | ## Scalar Value Types | .proto Type | Notes | C++ | Java | Python | Go | C# | PHP | Ruby | | ----------- | ----- | --- | ---- | ------ | -- | -- | --- | ---- | | double | | double | double | float | float64 | double | float | Float | | float | | float | float | float | float32 | float | float | Float | | int32 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | | int64 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead. | int64 | long | int/long | int64 | long | integer/string | Bignum | | uint32 | Uses variable-length encoding. | uint32 | int | int/long | uint32 | uint | integer | Bignum or Fixnum (as required) | | uint64 | Uses variable-length encoding. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum or Fixnum (as required) | | sint32 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | | sint64 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. | int64 | long | int/long | int64 | long | integer/string | Bignum | | fixed32 | Always four bytes. More efficient than uint32 if values are often greater than 2^28. | uint32 | int | int | uint32 | uint | integer | Bignum or Fixnum (as required) | | fixed64 | Always eight bytes. More efficient than uint64 if values are often greater than 2^56. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum | | sfixed32 | Always four bytes. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | | sfixed64 | Always eight bytes. | int64 | long | int/long | int64 | long | integer/string | Bignum | | bool | | bool | boolean | boolean | bool | bool | boolean | TrueClass/FalseClass | | string | A string must always contain UTF-8 encoded or 7-bit ASCII text. | string | String | str/unicode | string | string | string | String (UTF-8) | | bytes | May contain any arbitrary sequence of bytes. | string | ByteString | str | []byte | ByteString | string | String (ASCII-8BIT) | trillian-1.6.1/docs/claimantmodel/000077500000000000000000000000001466362047600171145ustar00rootroot00000000000000trillian-1.6.1/docs/claimantmodel/CoreModel.md000066400000000000000000000122601466362047600213100ustar00rootroot00000000000000# Claimant Model This paper introduces an intuitive, standalone model for a common scenario where one party needs to perform an action which is reasonable only if a claim made by another party is true. The Claimant Model allows formally verifiable constructs to be bound to a messy reality. ## Model *This model describes roles, not actors. In a concrete deployment of this model, the same actor may play multiple roles, or multiple actors may need to work together to cover a role.* There is a **Claimant** that makes a **Claim** that is relied upon by a **Believer** as a precondition to take an action they would not have taken if the claim was false. Claims are represented by signed **Statements**. The veracity of a Claim can be verified by a **Claim Verifier**, who will notify a **Claim Arbiter** of any false Claims. ## Example: Certification Claims As a concrete example of the Claimant Model in action, we'll model one of the core claims made by [Certificate Authorities](https://en.wikipedia.org/wiki/Certificate_authority) (CAs). A CA should create and sign a Certificate, *only at the request of the Domain Owner*. A user's web browser receives this cert when attempting to establish a secure connection to a domain, and proceeds with the connection only if it was signed by a CA that the Browser Vendor has blessed. In the event of a CA issuing bad certificates, the Browser Vendor can protect their users by removing the CA from the blessed set. The only party which can verify that a certificate was issued under authorization is the Domain Owner.
ClaimCERT
"I, ${CA}, am authorized to certify $pubKey for $domain"
StatementCERT
X.509 Certificate
ClaimantCERT
Certificate Authority
BelieverCERT
Web Browser
VerifierCERT
Domain Owner
ArbiterCERT
Browser Vendor
The Browser has sufficient trust in the CA to employ a strategy of [Trust But Verify](#trust-but-verify), however this model alone does not provide a mechanism to ensure that any StatementCERT relied on by BelieverCERT can be discovered by VerifierCERT. This is why Certificate Transparency was created; to allow verifiers to discover all of the certificates that have been believed. Using Logs to provide this discoverability is described in [Claimant Model: Logs](Logs.md). # Claim Requirements * The Statement for any claim must be signed by the Claimant to ensure that it is immutable and non-repudiable. * It must be possible to generate the Claim from the Statement without any additional context. * Claims must be [falsifiable](https://en.wikipedia.org/wiki/Falsifiability). ## Compound Claims When the Claim is composed of multiple subclaims, the Verifier role must be covered by actors that can verify all components of the Claim. For example, imagine a ClaimantPRIME that provides BelieverPRIME with an integer covered by ClaimPRIME: *"This is prime, and is uniquely issued to $believerID"*. This is a compound Claim: verification would need to confirm 1) the primality of the integer, and 2) that no other Believer had been issued the same value. # Believer Strategies ## Trust But Verify If the Believer has sufficient trust to believe the Claimant for some action, Trust But Verify allows the Believer to take the Claim on face value, and perform the action before the Claim Verifier verifies this Claim. This system requires all Claims trusted by the Believer to be discovered by a Verifier, and this is what is meant by transparency. Such systems generally include a Claim Arbiter that closes the feedback loop by notifying Believers of bad Claimants, and of new Claimants that can be trusted. ## Verify Before Use If the Believer does not have sufficient trust in the Claimant to perform an action without verifying the claim, then the Believer must wait until the claim is verified. It is attractive to imagine that the veracity of a Claim starts UNKNOWN and then is verified to be either TRUE or FALSE, and this will hold for all time. Reality isn't as kind as this, and the veracity of claims is best considered a degree of confidence that can be very volatile as new data points are brought to light. Believers need to determine what degree of confidence they require before proceeding with an action. The role of the Arbiter is less critical in this strategy, but they can still be included to prevent bad Claimants from wasting other actor's time. ## Discussion A Believer can be flexible about which strategy they employ. For example: Trust But Verify might be reasonable for a low-stakes action based on a Claim from a Claimant with a good reputation; allowing the action to be taken without blocking. As the potential loss involved in the action increases, the Believer will demand increased trust in the Claimant and/or a higher degree of verification. Some systems may also provide other incentives, such as some promise of compensation from the arbitration process (e.g. FSCS). trillian-1.6.1/docs/claimantmodel/Logs.md000066400000000000000000000146041466362047600203470ustar00rootroot00000000000000# Claimant Model: Logs This doc presents a model for transparent ecosystems built around logs. This doc requires understanding the core [Claimant Model](CoreModel.md). Log Transparency as described here should be applied to situations where the Claimant Model has already been established for the domain claims. It does not cover situations where you “just want to write to a log”. ## Purpose of Log Transparency **The main purpose of transparency built around logs is *discoverability of Claims***. A Claim is discoverable if it can be found by the Verifier without information being passed to them by the Believer. This property is essential when Believers are employing Trust But Verify and the identity of all Verifiers does not form part of the Believer’s [TCB](https://en.wikipedia.org/wiki/Trusted_computing_base). More formally: *Any claim believed by any honest Believer must be eventually verified by an honest Verifier*. Another way to conceptualize this is that Logs provide a verifiable transport mechanism to ensure that any Claim that a Believer relies on will be eventually discovered by the Verifier. # Model Log Transparency is another application of the Claimant Model to solve a transport problem in what we will refer to as SystemDOMAIN; it provides a mechanism for all StatementDOMAIN relied upon by BelieverDOMAIN to be discovered by VerifierDOMAIN. ## SystemLOG A log operator maintains an append-only list of StatementDOMAIN, and presents all clients with the same list. The log produces and signs checkpoints to commit to the state of the list as it grows, and committments to data to by a checkpoint should be cryptographically verifiable. A StatementDOMAIN should only be believed once its inclusion in the log has been verified. Clients of the log should verify that any new checkpoints they receive are consistent with any previous checkpoint that they have relied on, and checkpoints can be shared amongst clients to detect any inconsistencies in the list of data being presented.
ClaimLOG
"I make available a globally consistent, append-only list of StatementDOMAIN"
StatementLOG
Log Checkpoint
ClaimantLOG
Claim Log
BelieverLOG
BelieverDOMAIN and VerifierDOMAIN
VerifierLOG
Log Verifier
ArbiterLOG
Log Arbiter
### Artifacts
Log Checkpoint (aka STH)
a signed statement by the Claim Log which declares the number of Claims in the log, and represents a verifiable commitment to the contents of the log. The signature on a Checkpoint states nothing about the veracity of any ClaimDOMAIN, but asserts that:
  • This Checkpoint is consistent with all earlier Checkpoints; and
  • All StatementDOMAIN committed to by this Checkpoint are immutable and discoverable
### Roles
Claim Log (ClaimantLog)
maintains the log, appends well-formed Statements, and periodically produces Checkpoints. All Statements must be made available to VerifierDOMAIN, and commitments made available to all roles.
Log Verifier (VerifierLog)
watches a Claim Log, verifying that all Checkpoints seen by this verifier are consistent (i.e. represent a single lineage of the Log). Effectively verifies inclusion for all leaves that Checkpoints commit to. Note that this role is only concerned with the commitments to the Claims, not the Claims themselves.
Log Arbiter (ArbiterLog)
acts upon evidence of Claim Log misbehaviour (e.g. distrusts it, publishes the evidence so others can make their own decisions). Any actor can appeal to this entity if logs misbehave. Example situations where a report will be investigated:
  • Log Verifier appeals with proof of inconsistent Checkpoints
  • Claim Writer appeals if the Log won’t accept valid Claims
  • Believer appeals if the log is not available with sufficient uptime
This mechanism doesn’t always require proof, e.g. you can’t prove that a log was not serving inclusion proofs to you. It will probably contain human judgement.
Checkpoint Witness
any party that shares a Checkpoint they have seen. Sharing Checkpoints provides a mechanism to allow them to be more broadly checked for consistency. Only entries committed to by a Checkpoint obtainable from a different security domain than the Log are tamper-evident, and third party Witnesses support this resilience. This role does not map to a role in SystemLOG, but supports VerifierLOG.
Claim Writer
writes Claims to the Log. This role doesn’t map to a role in the Claimant Model, but someone has to do it. In many systems this will be the Claimant, but it could also be the Believer, or another third party.
### Relationship Graph This shows which roles must know about the actors playing the other roles before any of the machinery starts moving. For example, the Believer must know about the Claimant ahead of time because they need to have some basis on which to trust a Claim and verify its authenticity. Believer, Claimant, Claim Verifier, and Claimant Arbiter are all for SystemDOMAIN. ![Relationship Graph](cmrels.png) # Interesting Edge Cases ## Transitive Signing The signature on each individual StatementDOMAIN can be omitted if both of the following hold: * ClaimantDOMAIN and ClaimantLOG are the same actor; and * Every actor that is BelieverDOMAIN is also BelieverLOG The signature on the Checkpoint effectively transitively signs each of the Claims within the log. This is what the [Go SumDB](https://blog.golang.org/module-mirror-launch) does. The Claim here is “*I, ${SumDB}, commit to $hash as the checksum for $module at $version*”. This is falsifiable; correct behaviour can be verified by confirming that no two entries committed to by the log have the same module & version, but different checksums. trillian-1.6.1/docs/claimantmodel/Maps.md000066400000000000000000000156621466362047600203500ustar00rootroot00000000000000# Claimant Model: Maps This doc presents a model for transparent ecosystems built around key value maps. This doc requires understanding the core [Claimant Model](CoreModel.md), and it is recommended that [Claimant Model: Logs](Logs.md) is also read first. ## Groundwork: Discoverability A Claim is discoverable if it can be found by the Claim Verifier without information being passed to them by the Believer. The Log Claimant Model states that the purpose of Log Transparency is to enable discoverability of Claims. To understand whether this statement applies to Maps, we need to understand what it is about Logs that enables discoverability. A Log can be thought of as a Map where the key is the leaf index, and new entries need to be written into the next available leaf. Maps and Logs both have a single root hash which precisely commits to key/value data, and given this root hash and a key, there is only one verifiable value for the point lookup. The key difference (pun intended) is that in Maps the writer gets to choose any arbitrary key, where in Logs the key is the next available sequence number. A Log Checkpoint commits to a range of keys by stating the tree size. Tree size is the only input required to generate all keys for a log, and it is this property of key enumerability that makes Claim discoverability simple; given a Log Checkpoint, a Verifier can discover all Claims by requesting all entries from [0..treeSize). Thus, providing the Verifier is getting recent Checkpoints and is seeing Checkpoints consistent with the Believer, each and every Claim will be discovered in time. While simple, this is not efficient, especially for Verifiers interested in a tiny subset of Claims. Map keys cannot be generated/discovered from a Map Checkpoint, consequently Maps do not allow for efficient discoverability through key enumeration. However, if the expected location for Claims in a Map is precisely understood ahead of time by the Believer and Verifier, Maps can provide Claim discoverability. ## Claim Subject Maps (CSMs) ### New Terms
Claim Subject
the entity to which a Claim relates (also referred to as ‘Subject’, for brevity)
Mog
a Map where the values are Logs
Claim Subject Map/Mog (CSM)
this is a Map keyed by the Claim Subject. For Mogs, the value is a Log of Claims, for Maps the value is a single Claim. In either case, the Claims keyed under a Subject must relate to the Subject.
### Introduction CSMs allow for more efficient discoverability of Claims, particularly in ecosystems where there are many independent Claim Verifiers with distinct Claim Subjects. Claim Verifiers can become somewhat ignorant and selfish, looking after only their own Subjects and trusting others to look after their own. The trade-off for this is the risk that any Claims placed in the map at locations the Verifier does not expect will not be discovered by the Verifier. This risk can be eliminated with careful design of Claim Subjects and how they are extracted from Claims. ### Requirements To use a CSM for discoverability, these properties must hold: 1. Claim Subjects can be derived from only the Claim. Examples (Claim Subjects in bold): * Certificate Transparency: the Claim (cert) contains the **domain(s)** the certificate is for * Go SumDB: the Claim contains the **module name** 2. Claim Subjects must be keyed only under their canonical form 3. Claim Verifiers must have a complete set of all Claim Subjects they will verify #### Canonicalization & Discoverability A canonical form gives a single location in the map for a Claim Subject. This means that all users looking up Claims related to this see the same value. If there are multiple locations that users can look in, then it’s possible that Believers can be convinced of Claim inclusion in the data structure, but Verifiers are looking elsewhere. This means the Claims are not discoverable, and this undermines the core transparency goal. The spec for canonicalization should be precise and unambiguous so that it can be implemented in precisely the same way on all clients. Any discrepancy in implementation is a vector for presenting different Claims to different actors. This type of issue is insidious because the data structure verification would pass, giving a false sense of confidence. An attacker could effectively use this to provide a split-view without all the pains required to maintain root commitments that could not be reconciled. Non-canonical forms are not uniquely a problem for maps. Having ambiguous forms could be a problem for Claim Verifiers in the Log Claimant model too, but there Verifiers have the raw Subject and can canonicalize it, or query for substrings/prefixes, etc. ### Map Functions (deriving Claim Subjects from a Claim) Using a CSM is simplest in the case where there is a single Subject for every Claim. However, in the general case there are multiple Claim Subjects entailed by a Claim. This apparently minor difference has a large impact on the amount of work required in order to maintain discoverability. ``` mapFn(Claim) []ClaimSubject ``` We’ll use the signature above when describing how Subjects are derived from Claims. For a Believer to trust that a given Claim is discoverable, they must check that the Claim is committed to under all Subjects returned by mapFn. The reasoning behind this is best understood through example. If we look at the Subjects entailed by a Certificate for `*.foo.example.co.uk` we might derive: * `foo.example.co.uk.` * `example.co.uk.` * `co.uk.` * `uk.` * `.` This set of Subjects must have a non-empty intersection with the set of subjects that the Domain Owner is expecting. If this Domain Owner does not have any subdomains and expects to be checking only `example.co.uk.` then a believer convinced only of the inclusion of `foo.example.co.uk.` and no others will have trusted a Claim which cannot be discovered. This example also highlights that mapFn should not return too many Subjects. While the empty domain `.` may be derived from all Claims, doing so entails a map key which is effectively committing to all entries that would have been in the log. If this is the only mechanism that permits discoverability then a log should have been chosen instead of a map. ### CSM in the Claimant Model #### Log-Backed Maps & Mogs If the map is constructed from a Claim Log then we have the following:
ClaimCSM
"Applying $mapFn to the logged data committed to by $LogCheckpoint results in the map with $rootHash"
StatementCSM
Map Checkpoint (sometimes SMR, or Signed Map Root)
ClaimantCSM
CSM Operator
BelieverCSM
BelieverDOMAIN and VerifierDOMAIN
VerifierCSM
A VerifierLOG,DOMAIN with the power to compute the map
ArbiterCSM
CSM Arbiter, possibly the same as ArbiterLOG
trillian-1.6.1/docs/claimantmodel/README.md000066400000000000000000000010011466362047600203630ustar00rootroot00000000000000# The Claimant Model: Index The Claimant Model describes the set of relationships involved where *trust* is a factor. The concepts are described over a number of documents, which are best read in this order: 1. [Claimant Model](CoreModel.md): The core Claimant Model forms the foundation for the other documents 2. [Claimant Model: Logs](Logs.md): Verifiable Log Transparency described using the Claimant Model 2. [Claimant Model: Maps](Maps.md): Verifiable Map Transparency described using the Claimant Model trillian-1.6.1/docs/claimantmodel/cmrels.png000066400000000000000000000745551466362047600211270ustar00rootroot00000000000000‰PNG  IHDRp˙×ā3y4IDATxÚė xTUšŋ}FÜQVdGö%,BIHHB°ī;˛ƒ€(ĸ(›€ *‹€Đ0ĸ -Šģĸ´(.č¸ĐÚļíÚļ­=:ãt÷ĖØ3ū{Ļ{ž~ˇsŌ7Ą*ŠJ*I%yßįųžÔrëÖ=UuĪ}ķíœž={žŪ§OŸĪĸ2ÄÕW_Ŋ˙€ę΁ŋ}įwŒ *C´mÛöUÎZ@ā8@ā@ā@āŽr͏ÅÕO4mĶņU":âęn}æW A pE“ūí×ŋ3":"vPöü*#Cā8Cā8‚@ā¸Į1S-sŌ|[ļõˆí=ųP•nßž}vË-ˇØ‰'ÎznãÆļ}ûöRí˙ÔŠS߲e‹?~ŧÂĘ­2z\ĮTÚ2+ŪxãüĪ÷đáÃg=‰xøá‡möėŲA˂ĀU.ÛūėGļzī3ļhĶA›Ŋz›Íŋi“M[˛Ęû;sõ=ŪãĢî}Âļ;Ōū¯žŅ–MīfwoiIƒúŲ áŧũ#pU@ā†jš‡o˖-+đøącĮŧĮ{ôčQâ}Kŗŗŗ>wå•Wڞ={*ŦÜz˙@ë˜JSf…ĘėĘ6iŌ¤üĪvŪŧy%’­`ŅĒU+ËÉÉ)‘p"p+pûO}ãÉØ¨™ -epŧĨĻô´“líŠT;°%ÍNĘĖį÷gØÁMmĶōŪļ|V/›0ŧ‹%č`ii},)5Ņ&Ī_âeŲvŸøâŦ÷ILčb|{ĒŲ'ŗíĢW&ŲŌyiÖwĀ[rû@E œ2gĪ>ûlŠŽ[ˇn^ø—lä^äC–eڞ|ōÉ&e‡´˙’Üɓ'–Kû×ûËė•4”yĶgHāô\°Ī8ĐqęõĘĻĪĸ$ŦĖl=Njå¸5ûÛā‘“-qP[wũûÅĶ#=š*iüpfš'y—õĩ!Š],.ž—œv÷>zŋu?yŌæNčZā5?~0ÃÖ_Ÿá‰œÛޜCĸ  yŖFlΜ9a˜¤bƌVģví¯•ŧIâœĖh; Y 9[¸pĄ5mÚÔÛļ^ŊzvįwځėŌK/ĩ‹/žØËB8I‰äqüøņųōŖÛ͛7ˇ XįΝķĨP=íßŊĪu×]į=ŽmwíÚåŨžëŽģŧĪCbåŽSå+üūÚFßzë-KMMõŽGeîŨģw~™ulũúõķö¯įt,ŽŲWÛ$''{Īéĩ:tđļWļíüķĪ÷ŽOÍÄîsĶãú,ô9ëļĘæ—<}FS§N ØÔė/ŗËæé3S´mƒĀEˇĀ­Üņ˜õg‹į$Û/M(•´gĪąëæöĩž=ÛۈÉ3­GīîööŖÃĪÚN™škĻ&XJÖ¯ų.HĻFŲ’HĮ† ŧ š?$S‹- Iæœ`¨ŲĪɁúVI”- Eā$,NlÔœį„Š¸ œ¤K‚ää͉‘{äJe‘Ē,z›ËŠI†$Šz?'?ŖGöÄQ˛§û+õC &p7Ũt“'fz/WNWfI•äÎŊFīᚄĩ{N¯•Čš÷ôgāüŸ›?§cvûŌë%bG-pŒ*ŗ¤Ī_fIœ+¸č¸ÍŸļ>IivËĘl/[V–â(~õÜh›7ĩŋ];Ŋ{Đm~ųÜXĐÍn܋ĀW8t/,Zå’Œĸ 8Áp¸fTÉÅĒUĢB8e­ÔK¯ķËRqį˛lNžœü(‹æŋ¯ãI´üûČČČđ˛„zOeČ\6NŠo˜Ę­ũz'pÚĮ’%Kō—Tē2ĢLúüTW×wΗ˙XÜũPN} %gĘ.Jz —Íeƒ•‹~ŗ`ĨMąßž:ĢÜÅ-Üøßfچë-93+`_:Ē­ĀéBĢ‹z¤CY3'kįž{Ž÷ˇnŨē^V+Pæ)˜ĀéļdGYIúƒ…*p’Ɇî++&Ą Eā”-“ ¨ 6X˙1'Jūc tėĘL)'‘S&KePv-Ø 'pšß›'́1č•usīī"Āųå,sÛŠųSRæ?˙HŲĸƌĀE§ĀI€âR†ØŨ›&DŊ¸ގÍąžũē{™C¸rčWĢV-;vlHŌL$}ēīĖ/ĘŽI¸ÜëÔ˙KÛĒiXâ˛hĘzéxÔ,”A ’-eĸÜTÁîūûī÷äĐuä×û)Cæš-%j:n×Ģũ+ŖčúÆ8eŗtüîq‰”+ŗäÎ/—Ú—k.NāÜû%pē­÷Pš\ŸŊÂĶ„øË랂ëÖ­CāĸXāz$¤YL÷6ļæÚ$;zOŠ×'­"šOKßŋ9ŲŌRēÛēƒ/"p€Ā•U(cŽ´8íGRāÁ/p‰–2Rz2\NδM||ŧ'?;õkĶãڏ2c~ 4 Սx•”8ŨVsĻ2vŧ ÷ 9qtƒܔ:6×DY”ĀIœT5šę8”…te–4Ē?úŠ™UĪšĪĻ(Ķg ãÔgVXUN7č@bĻcô÷ŗ+zNũøTf}Æū2#pŅ)p. wëÁļxã.›ž`žeO˛^1-íĻ}*…ÄI8ôoo7ī} .Z'ōõO4+ qõĶōĪ1&Ą‘Œé1=įÄQ’$)‘¨h ũÕ}EQųj;Ŋ¯dTûö÷Ųsīåŋ/Ņ’°ųûÎéüĮŽėUQķŖųåK'ųT.ŗŽEō$ķN…KŸ…ģĩŊ˙ķÕąIũÂ-Ņ+nˇ`eö—‹.+ŋfbzڗ¯Í‰ˆ`}ųŌ8{õp–}ũJŲ5ĶjēÍ#wד p€Ā„ËœJl5ʖĨ´ĒļĀIŪ4įÚo_›^*ĄŌ´Ëgtą´øĻ^,˜ØŅ–Lél}ē5°ģnėį Dˆ´ÄIãöķ&Fā+EŧûÎÛöÅĪļŪ{ĒÄĄŒššnKĶüĀEŋĀi0@īūũíī•.ķvĮĘ>6vH+¯?] ¤mKļėAÍˤ¯ŨÁ-ƒmú˛[8@āJžwÂūįãÅöÃG7Ûoß?€Ė\ œšK›y“MŅÖ¸#)¤9ßõkœŋtV$#=Ĩ[š5Ĩ"pPeÎ/qų=2G pQ#pÄĐŊOûÕĪJŪŦųíë-3ņĒ€Yˇĸ$NŲ¸˛˜8mXÕSā>>ķ´}ųūáˆÅ7ėŗŋ|˛āė˙ڑ9ĢP0$ĮŪ86štSyÄ7õ$.ZFĻΙÜĮY‹Ā@ĩ¸ûđÎr­p˙ī“9ö›÷DXŽ.göJÛĩŠt‹Ôk€BiētH&S“8¨~÷ųĪĩß}°'ĸņĮ_n (nŋ˙đûÅ{?CVŽnŲÖ#6ubRŠ›+5ē4ᇛ1>ļĖWi@ā ę.ŌĄ&Ōŋ|ŧ°€¸)ËޏžÎÍ˙Ļ9ƊZCĩ2…æJķĪûVUŽ/Ø$Å\ÅÜíGßâK=‡,<Ŋ'-*NũņFOŸ…ĀWšø÷_n(ą¸Z‰AĄĨ­´z‚V\¨*Ų&­VpNŪĘ Uåø4 Ii¤‹ŧĀŧ¤Ų—/—~¨ä-”Q§ē#p€Ā•44ˆĄ4âHāœŧ-[ļ,`ļGĸá_ Ā/ Öō uBÛP2}ÚNč9­|h:&=^œ ŠLÚFû &X˛]E}&zß`åŌküĪ:žâ>ĶÂëĒēĪ!Øg„Ā•­ĀÍZ}—mYAROĘjũT­ā  5=ÉĖQíŧųã&fĩņnŸ~8ģØ×ĪßÛv˙.Üx˙ŨWí_>Ü‘>nNā‚É›žĶzœZ1@ Šk=Pˇd”–ÖŌãZJkAyɌū*Cä¤âßĨZTëyęq­ņŠ×jŋz,вPČ_˜Ūƒ-=ŽuJõ˜}÷¯)ĒEįuLÚŋš&pZ&KûŅÚĻڇ[Ø^Ĩũęõœļqk˜Jœ´ĻŠ;n—>?'nÚ^Ī)´~Š+—^īÖZÕ_—“@w|úlĩ?íWīé֛õ‡ž#­ĄĒãÕmí?÷÷įŊNĮĨcö/ķ…Ā•­Āmö#KLėņi;bģ6đūFJ nčɚVm´—FžŪ˛°‡]3ރˇŒV°}ŨsS‚­ē÷ ¸Š ‚ÄGō&)(ŧ8ēNRā2PɑnK\HĪK–$Z"Ę-ŋqãÆüáuôčŅŪ67Ũt“ˇ/÷>ZL^BXøø´0ŧ§Â‹ČKp\öPŲ*ŋÖ@•ôŠ<.ËĨuL œÄGepY+íãüķĪ÷î;ŠÚŗg÷œú ĒŽŋšäÉí_k¨Ēėē-yÕĸõūĪ×5GKŦtl.ë§ãWDŋĀéķqÛk}F˛jū œ>[}N.¨ĪDrˆĀ•Ā LbŸŧ0.â}͖ĪčjĢįÅx™ąŌŽHŨ}kŧŊ'%ä ]QÛJWîx ŽĸîÜsĪõDKōĄĖŋIP2āī§ûz¤B¯ķo+‘éׯŸ's’)ɎļU6Nč¤OŲ!‰ŒdIō(™+ĒUÛKâ´ŋ4J€\æËŨ—p-\¸0?“æÄėœ"šP%ŽZd^"ä$VQxReÔ$\z˙ūUNí_%v.Ģ鲗Žė Ļ>kI°$SĮë8•SĮ }hģ`‹ÕûNĮĨ…îũ͡įͧ‹ŒĀ-šũ[Ŋtpě97-ī퉔[¸^™ą‘ƒ[Úú%ŊŧfβX÷4Ô¸fB7Ûzė \E œËŽšŒTaaķrp'éPFĘŋ/ …ËŧŠéPR!ŠŌ~ĩ­¤Í/3ڇ¤L¯‘Ėų%ą2R:&ũ• jŋ~ķg Āšcô÷q $4MŪ_ŸäIRåÎeÕ\(ƒĻũëũí_Mģ’ZĢûÜŽYZrĢ÷S†Î5Ã8—í“āéũôšIúŠ8}>.SčJŽėŽwŋ^E67nÆ||WjHÛjŨĶ@’ĻLœš@Õíųũ"pƒ“:‘.Î/h’,IˆúĒ%p.Ëæ— eđœ *Ģ&1r¤L”nģæWeŧüđõēÂũŊô.e›Š85áj;˙T)„ÆõI+œšr§ōšމVÛčũũr§ĪJīí$OewĪéX”Ôį¤}ģŒĨĘ&9+,p’=ŋČĒ Õõ' &pj>õGzŊŋ\\ŲÜâMlĶ ƒÂ]ĒĻĖPļÕ †h…*a]9 Åėg7ė8ŠĀ į$Íõ! &pūúĢ>l’'TN†Ük•=Ķ}7įœDQ#9‘´čĩN 7[:Y‘¨(Ûä$0˜ĀI’ÔŦíul’­@ឈ:Ÿ^Ŗ&P×/MYA=Ļ}HĖ\fRĮ"™ĶūŨąĢšSĪi°†öĄ}*´=ĻŦ˜>S‰ĒO}Ôô:íË/pڏļSŋ=Ŋ^ˇeā”ÅSOęúįéøô:íWâˆĀ•­ĀĨ Ž9ûæ˛jĄŒ,ũė…ą^†-ZÎÅߞj7ĖˇôĄ)ļîā‹ på&ō•)CĻĮ]ø›÷ôÆN&‰Hái/$N°Ôŧ¨}îÛ&’t’÷:ɕÛF}Ū\sĄöīī#ĻûŽ/Ę cŌët ÁæLĶū´ŪC¯•Dę1ŊFB¨ōJ¤Ü(Z'p’J=ϞšŲ•ËßGOũõ¸BûWß;Eá‰|õ˜^Ģí‚M ĸ×čķt߅Ž]ĸŠ×7+ŊĀ)ķNöM#Q/p~‘›7š—%`Ŋ„ĀGTėäēŽY´pîĮRZÕOā´&h8Ų75ž88$ä %Y‰Aũë*rpƒÖJ]4-Ö˛Gą-ŋĀG”(ŖåŸWÎ.ķ†ĀUOĶZ ŗ'õ Kn4‚4TšŌB õ{ §O›d¯¤ō´oCB…N 4ņˆŒN6z☰Vn¨ė—[övšq[nŧ­ÛœaA pQ p{…Ũ´ŠÉoCŨž´´,–úÜEK9MJœ‘ÜÎĻĪa{O~U%ΜZÛ>=Į>™s:¯Üß"oA pQ"pZ6kŌȘ°fɔÎa5‰–Vā$LęGmƒNĘ´¤ūmmؚ›Ē„ĀyM¤ŋš58ˇlGrãG_Y‘7Ž ¸h¸šĢևŨDŠõGÃŲ>Sˆ,˜Øą\4,žŌÅ2S;Ų¸á]‹ŒąÃēZ÷NWZīƒ*­ĀųšHŋđY oA pŅ&pIЉa>uMšå-pjļ uՇH \Hofo==ˇØÕ($'ØÆÃ¯U* ĐDjČG\%¸Œ´Ø°ÅæúŲŨÂz&ü-­PI”ÂĮHĖ7d`s;ē?ødÅ9#SmÅŨTŠ&Ô"šH‘7€ĒDŋ~ũūEsƒDeˆN:ŊČYžĀÚ%,ŠQsĢF††Ûü‰AąšƒÄqƨövįÚg=ˇjÅX›˛bKÔb(ωy¨LwûҎíēŲ=>Uįũp^ŗã渰Ļ) Zô^“ëVĀÍGˇ`æßGŪÚ=ÛåL‹ÚiDBl"EŪ*›Ā­ž÷1OŽÂ w´f¨‹Ū§ÎŽĐ9á´$XNfw;ņČ\ë‘0ØöŸú&Ē.Ė&Rä  2 ÜŌu[ÃpËÂa¯Ē ‰#ŅîëW&xY¸Šœ:Dˇ5ŧ˛QTMä[‚&Rä  ˛ ÜĖŋėíG‡—šĀ)–Īčâ­d‰éD*RāTūĔø _JĢM¤Č@e¸qͧ…-cjr ˇœBīŖ €K+‘Čä•4VĪ‹ąw°¤ôô ¸4‘"o•]āÆĪšļĀŠØŅ{RJ<Š´´Y¸ōžJÄɧÖsÕ@ŒëĨ;mH¤.BM¤Č@U¸Ņ3„-TĘž…;ˆ M%’™xUŠF’jEQÍžܑämŖĨ]šAŸš~%ę'Ë=*,y+ŠĀE¸‰y¨*§e´Âm•Ô¨XIÅAō–ßԛ$ˇ¤” +níT vPsĢ–ũRĶ­FÁJĀ‚eĩ…žĶ MTė^įW$t iņú’ \5‘žŸÎū÷Üŋ×ᯂ¨’ņéŦû|FkŽP%nųÖÃļw}bX‰Ni'ԕ IJš‰S?ŧpˇ×ûhúŊN5ŋŪ_•]“ęļú¸)ƒ§Õ& ŋ>=šŊŨķäĪÖˇPŽ ›H ĸšĮœ§r˙ÖįĒUJā4‘īüiũ‚NũL’ŠË€…šISsǚ:Kōz5ãJēĘzb_íč Öļ÷é×K$oÁŽšH ‚Č“8Ž PĨN‘œØ=`Ĩ7ixKLŧĖ–ÄŽ¤âU8›7sTģ÷ŠûÅĶ#Ŋ Ú5ã:x6Ig¤å-kPK;đĖK%–7ŋĀ•[)AƒæT¨j78#éŦÎūi:{ņ|<¸_Đ>f(ŠĘUM›’°’:P6OK|)#§~o Ũö‡˛†ƒúˇ°ÄžÍlBÖßS_>Éc š°WōöāsĪ–JۜĀŲg3ûäî÷3.ĻQņéŦŽ PĨnæō՞Ŧø§Ė4(Ö{.ep˙ ĸ"D˛‚Õ` IV$›D•‘sYš;VöąÃÛŗmđ°avëÁŪ2X÷-k+p+}+@U8­\Đ/>6¤ĻĖ'îlą=ÛØƒn°ŋ~rMDî;’ōŗ{j ÕÜnZš!Ô5YˇŨgĢîÜUĨŽĐ­¤MŦH pUEā܀†Ô”^Åf׎íJķæZKΙnS,°?~¸ŧÔ§õNÕDĒ ”åSö-œ•’+(ûVQ—a+Y+@U8ŒÛ°Œ´Ø"%.3­§7ˇœÛ>6)ŨŪųŲĒRKœšJKōēĨĶclãOŽUK+p‘ ¯‰‰ Ēœbū†}A%îũ§GÛđÉ3ĪŅ#!Íöī]Q*S“i¸¯ŅČØISGV˜ŧE“Āēā…ŌĊÄPUNąrĮc–™•h˙õÁŦå¤q=a ԇNMĒK–Ī*qŋ¸‰YmÂÚūÕÃY–••PĄō­—á+ž‰‰ ĒœbÍūã͎Ÿüé4¯’üũÛĶ,!mh‘¯™˛b‹ =Âūû—ķ’ąÎLķ.„ĩWJL~S.ÂE0x+@U8…ÖOM>Ņ/nËįØĒ{ŸŠ]RzēũáŊ9! ™Ļ Ų´ŧwHÛ>ŋ?Òt˛Ī~XáōV™ŽĐąp+@U8‹6´nq)!oŋzī3Ö7)Õž>=7$)ĶDžš>¤¸U4Buđā~Q‘yĢĖ—a,ØÄz‰  \Ibãá×ŦSL/ûōĩ9ĨĀ ųéâz4˛Éŗ§E]9+ŗĀ¸HljõW× ä   œĄ; šH‰SļÕķb‚>âāëÚąąŨpĮî¨,cU8ËģžüĀzįJÜ'§Ŧ|Lėpâ^5™Ū´ ˇĨ ގĮ?‰Úō!p@•8'qbbí‹W ö‰ûūÍɧy|WĒ%Äu°eī‰ú˛!p@•8'q…›S5xAķššû'eZBßÖ6ņšųQ5P@āĒĨĀ9‰ëŪ§ŋ'q?~0#đ‚F IíbŲãÆzÛTĻ2!p@•87:ĩ[īXÛ¸ŧŸŨ˛¨ˇˇļęđ “+¸!p@ĩ8'q-¯noãæ]Õ8Ģ‚Ā p p€Ā p€Ā p p€Ā€Ā€Ā!p€Ā§XŊ÷ËŨÎę7jęEÂĐ ļũŲŧįn=xBŌQŦ˜¤Ž™mËļ) *î}ŗ§XrÎôb÷ĶŽ{ŋ€+:xë°æ•y˙ŠolåŽĮ8 zNrrqÍZ6eÅģũčÛŪĒ’šÆ-Ûåˋ¤Ž81™šúOöĘZŪtŒĒŖ‹z^Į[ŗV]Ûüđé"÷Ĩũhûkĩ‡ņ‹×å>=ˆ“¨9Yqą÷äWÖĄW‚'d…nũĄ—mÔÜՖ=cš-šũY1'L’š­ĮÎxÛMŧö6Û}â [ŗ˙¸÷ššk÷äŋF‚¨}čqm+ytĪÍß°Ī“+=>fÁšüŒ DSu´ŪCû-\íKDeāŌÆĪ+đœöŠãĐ>u|ڏĘįŽĶ-ĶĨũj[Ũ2iĄ5jÖ:˙žŽYepeŅ}—ĩ[uīŪvڗ>Cˆ¸ĀI:Tßĩ0ŧ_āVÜũˆÕiĐČ…×_×)Šr™­6]b=1lÕą‡û$y÷kÕk`ŗ×ėôļ“hIõ:‰Ō]’ŸSVP¯Ķū]FĐ šö¯ŋÖEÕņI¤$jĘÂųEJ÷›ˇëę5JĩíWRØ?cŒw[Ûģ,ž^ŖĮuÛIn׸”ücÖm×Ôę>'•[¯!€Ā”‰ĀŠīÛ9E4G8eËüũÁ”s˛RXā$QNú$c.S5tęROÜt[™/—YSHØÜūĩeû\FЉfQM¨Ę6hŌĸ@vŅÉĸÛ§ÄÎß_Ŋŋ˛j~ķ7ĄēĪ•E%ŒÚ§ž“œēį8 LÎ5#Ęd9ņ œDJŌÖ31ĶŽjĶÉŗ`į2i…›`•9s¯QL͜ĘÎIŧ”ķ \ ūjE œ˛ajîÔūē-)ķīÃ/X…÷Ŗ×čø‚ œĘwŪųäöPčžĩ¯€Ā”Jā’–ĒpöĘ/`5*ģ&éķËX¸§~fjŌ”ǝäP͛%8eō$SęƒĻ}(”‘û‡skägōíĶߏN¨ã)Jā$„î8ę÷§} p@š Ü´ļzMŽiQ*5sĒiP’æ0IļwŲ8ÉLINRå(IœdË Œ&pÚž°x)ÔWNũĪ —Oũį4­H°}ē>|ĘFJ(u\…NŲF7ÂUYB7ÚV÷•…tƒ=8 \Îõe“ŧHä$(37"Ô/`‹6ô„Eĸĸm4ŌĶÉMIšPû™ßņ_™=…ŽĨ(s}ÕÎņõ‘Sč84ĄpŲ$„*“ŋÂûÔ1(ķ§˛ģ×ûNũî$ŗÚ‡îĢœîëô'€PŽįīįT(”yĶv‘šMYž@}đĘ3Tæ`Ķ~5ß\q8 ĖŽ`)-#8CāŽ@ā8Ž@ā88@āĸ’FÍÛžŲĻKī3•9šĩëüË:—7üļeĮ˜*{YÚví{Š_%P¨z97šķQ€Ā p€Ā€Ā p€Ā€Ā p€Ā€Ā‡Ā p€Ā€Ā p p p€Ā p€Ā!p p€Ā€Ā p p€Ā€Ā p pPZjįIÛØ<ëŸwŋ& pŅIrž¸ŽÖ|4€ĀD'5rãß ÉÛoųXˆnîˍķäí˙rc pŅMáfTšOˆrԌú‡ @ā*Ž•æS@ā* jF}‘8€b¨S˙Š:öøY4Dë.Ŋ-ĮŌ´Mįũü:8€¨äōF͞;đúwFŒĻm:žĘ¯ƒČŗ=7gÅ~€Ā!pD%Ē\šČœēķë@āCāC⁁#8@ā8Cā8@ā8@āCāCāCā8Cā88@āCāC⁁@ā8Ž@ā8@ā8@ā8@āCāCā8CāCā8888@ā8@ā8@ā8Cā8@ā8@ā8@āCāCāCā8€ ÜÎãŸØíGßF⁃˛¸vŨû™ž{u4˛ėËCzmũFMŊ‹•BˇŖņĸĶžC›7õí–ņéŦû|FkΎ˛¸5û{įŌÅ5kyįPÍZumÚ [Ī:WŠÚĮĖÕ÷xû(ķbÅŨxĀ pP‰Î Ûî_ØęŊĪxŸe[„,p{O~åŊ..y`įoŠųQWƒ˜ķTîßúœ%úšDRāt\pŅ%ž°í?õ÷˜Î=æÎŖPîŽ'?°[ž(—ķBįŊ„1§jō@⠜ÎEį>I6~ņēüûs×„t‹”m+wa}ô^3{ÍNīą‡_+°bĖ‚5ŪļēŊäöŦgbĻēíļ5wĩ-ÚtĐۗ˙q.|‰ã,ņ˜—mū×ÜØ“ÍK+p‰ŲSŧs#PFmū†}g ÜúC/[rÎtīÜĶk7?|:˙ŧŅī]ˇ%ƒ:×´]׸,ŸŪGĄķÉŊÎ17ÚnÄėëŊÄÜšŖ,›Û‡Ëę|ĒU¯ĩé›ū•BāŽÎƒšņ‡ŧĪĩ9?1Ž*ü§ßŧŧĸqĢv§K*pũ3Æx ]$Té7hŌÂË8ÉjŪŽĢWŅ+› æ!'qšPõœļŅž”…ĐEBûPfB —aĐEF ɟ.@šĩö.6zŊnģ ŸŽ¯UĮ6ņÚÛJ”勏k˙Ī_ž4ΈqvĶÂîĘķ7Ĩ1ö_—ŧø]nÜá2sá \ã–ílƊ-!eĢu^)íßŧ΅!“ÚUm:Õ„ĒsRįĄdKŸwūŪ?V:G$}:¯´Î w~*t[įΝKڇū SķŽļŅqč=õŪî<Sā”iۑ'Á…?ËūüÆĸü㍇˛ļųëû?ž=+!Ēā~Pš•YÔŦ]īĢ’ œ..úĢĐÅHáū׌ų˙K×C˙í8=įĪŪIēÜsĘĀéĸá2 ĘĒéļ.:NØēí.dúë˛x%‰†WÔū¯ōüˆJGę]ŪøĨp~cú}×éĪVûŗØē­÷ $pNÄúįĮũŖsĶSĘæšsU]$wz­;wtžē}čÜtĮY’&ԆWĩz;÷X_ô}V˙Įī… ĸ3Öŋ¸°Ä!pa6͌Éģ]æŅ°Yëˇ#Մ*Ņr’sō6č‚áÂ58ũĮ¯‹{˙ÅFûrīĢĖ€nģŽßūí]†AÛų/xáFl¯6ߝ<”iDĻm\Úëļōú=Fq,ņUr?æũũįÜØ–—= ;§ßĒ_”ü}Ú\†Ë+ĘDë|Ķ?IúÍëĩÁÎ/Xū&X˙ųĻÛ:•­Sčüķ œū}–ĸ\ÜH΍ûrãßō>ŋ?ú>ĶąüÆĸüc÷ÚøŽŽŋëÆŋ NT&+šĀ•[HöSvLŨV3§ŋcĩ.HŽĪN S3˙ ĻRõŨq÷Õnjš$Īuøvͧne)${‘8úĀ‘Brnū‹_Úü„+pęvāūá(Ü7ŽpļZM­’7'cʞ•Fā´Ŋ˛ÚŽßÛĐŠKËZāüøeî÷įĐ Âđ÷“Ä!pÕDā\8…úĘH¨\S§.Bę­ų­0'|Îɟ$OM: C'x¯wī§ ˜äÍ? AíWr¨÷Đöz_+š’ļŌœ~ŗúũë<Ō?úíKę”Yv˙ˆ¸sEũAõ[ך!éŌ`ŸŌœú˛š. z_wÅ \‡^ ŪyŦcĐ(T'sĩų‰ p\9\ę˜Ųųũß’']\vLɖkĸŅm÷œšn\3‘ž.RĘ2¸‹‰ŋŖ´.zwaSh’8‰ŖŪCōæ2 :žŌL­ĐĨËÕīæũ°Ģ~|:ë$WzJ2œ~ãúgEŋ_ũîuų3Īî\ŅīZˇõ[w}?•ĩŪzėŒ7HHŋw—ÕķOåã^īŪ˝onā‚ö§lŸ˛ÛîŸíËŋ˙>õž:GũsÕ1•HāX‰ĄjžĀ\ų Ki‡Ā!p‡Ā!p€Ā!pĮ ŒĀ!p p‡Ā!p€Ā!p‡Ā!p pĀ!p€ĀQ˙#p‡Ā!p€Ā!p‡Ā!p pĀ!p€ĀQ˙#p‡Ā!p‡Ā!p‡Ā!p€Ā!p‡Ā!p‡Ā!p‡Ā‡Ā!p‡Ā!p€Ā!p‡Ā!p‡Ā!p‡Āqū p‡Ā!p‡Ā‡Ā!p‡Ā!p€Ā!p€Ā!på(p_Zû)U° ›ĩ~ģfíz_5nÕît4ĪŗÖڏĀq#pQ)p”8 ūGāʎĀUKę]Ņøgũ3ÆX4Dīäa-ĮŌ˛C÷ųu pH eŠį pH eGā ōĐ?ī\jÂGCŲ8¨lË;—ķQ‡ÄPv*ßäÆ˙åÆÛ|€Ā!1”ƒčĮ5Ÿē 8$†˛#på¨ųô?ōÎĨŋœC3* pH eGā ęų]nüõœŋgāŪį#‰ĄėD/…›OiF‰ĄėD9MōÎŖ%yįŌØŧûõųhCb(;œKPqlײmU1.­[˙vž^ŽŠ—˛G=U‘Ŧ› /eŽs 8ŽúŖâĨėp.‡Ā!pTŧ”8ā\8*^Ęį põ?GÅKŲ8ā\Cā¨x); pĀš pTŧ”8Î%@ā8@ā¨x);œK€Ā!p/ĮEÎ%ˆ°Āí?õÍ^ŗĶz&fZ‡^ 6dŌBÛzėLūķiãįŲÆÃ¯šU÷>aŖæŽFā8 âĨėp.AY œä­sŸ$kŪŽĢÍ]ģĮ–m=b‰ŲSŦVŊųWŋQS[šã1Cā¨x);œK 7eÅkФ…í>ņEĮģÆĨx™¸@ˇf˙q[rûŪ_÷˜^דxˇˇ?û‘í=ų•­?ô˛'„ē-Q”ä)8ŽŠ—˛œKP S“iq™3'p’°vŨûy¯éŸ1Æ?ũÕ63Wßã=§ÛzLÛ(ŗ§Įˇlį aė lo_É9Ķ8ŽŠ—˛œKPR“PIžB8õƒë;xdūãē˙įÖ(pn;IßÅ5kåŋ‡˛vÚ‡ĀQņRv@ā€s J(pWĩédĶnØ’ĀéöíGßļ‰×ŪfŠcf[›.ąŪE=Āi˙ëŨ ŊCā¨x); pĀšĨ¸„Ąŧ(ü¸¤N’æ8eĪjÖĒkŲ3–Ûü ûŧūlÁΟÕĶë%nGÅK؁Î%ˆ€ĀŨzđ„]pŅ%)hôŠ$ËeҜĀIÜ4ՈÛNŖV8¨x);œKPÎįäK×Ēcoānû8“ėŠ?›Ļ‘¤Å$¤Ûyį_ā:Eā8 âĨėp.A9 œ›D’ĻđOâë2rš Dˇ5UˆļŅ!ūį O#⟖DÛh0ƒÔPx˙GÅK؁Î%ŽĨ´ŖâĨėœĀ\•âęËę\ūeîßÚ|CŲ8¨4mŨ~W\ƘŋÆģOCāCb(;ŅOķîņißëbØ)vā}3'7æ#Aā8@āʎĀAͤeûWÜHĀÕ{Ÿą˜„´×ųT¸’„-ˇ¸=‡ĀƒĀ!pPJę4l’ž>~ūū b\účŋ¤M˜ŸĖ§ƒĀ…úGāœŧųß4ōÔŋŧ‡ĀCŲ8ˆ 5šļîđ™›….ŧz&|ÍĮƒĀ•&”‰+Éŧn‡ÄPv@ā ŽlŪö–knŊ÷]GÍ]mņCƯâSĒž7böõۊ ŽiT“ņlj]÷5כ–Ų’ėkb_MÜÛ¨YkoB_7i¯ÖRÕdžąƒ˛ķ¸×dĀ’:­•ęöĨ×hāŽq)Ū>üķÄ!pC؁ƒ‚ÔoßŖ˙?ģ(jRÕ=âč8pTM>Ēę)pŗ×ėô„Kˇ—m=âɘ–ĖŌ}ũ:uŠ×dúįÖđ„_7Ē?'ŦĶ ‘ˇŽĒ{ŽVŊŪÄžē­×LYąĨDũį8‰Ąė€ĀUŽlŅæi7c~°ĐEģGBÆĶ|ZÕSā$WZKō•6~ž—%sŲ5-­Ĩ š„MÛęį8ũ–$lnE…2tĘđ9™Ŗ Cā(; pP]tYė€a“~ĘÅągâĐ?Į§ë§VũN!a“`5nŲΓŗšĩęÚæ‡O{Ų´@ë—8­*IĶZ¨ūX´é`ŠûĘ!pC؁Ģ4iŲî;ŌÅQëÎą‰ķŠUOŋx'Zę›ĻûjR환éeãÂ8Ũ–ôšuO]­~_‡Ā!p€ĀA14hÚrŪ”ëļüW8ČŦ)Kū:pØÄ™|zÕOā$X:Ũ°Y°ÆëķļâîGŠ85Ņ_pŅ%^ŋ7‰›šL%ƒļ‰×Ūæ5Ŋj‡Ā!p pP45[´īū[$”ШĀvŨûũ!÷õ5øĢ—Ā9iĶHQ'hĀā~CĘä*KįļÕ}7ĐÁd•´i{=§•Ǒ̕ęöé߇Ā!1”8đҍyÛtS7„ęlž{áŨΧXũŽĨ´Cb(;Gë2ūĩ4ĘNŊ˙ë¤"p pH eG⠜hxUĢRŖŌ\(ՌÖ96ņ>MŖūGāʎĀAĶ I˜á3¯ûĪH\,“FLûK°Š|ĒGũĀ!1”ƒ˛ãÂ&-Ú}îĀ…`ĄŽč­;÷úŽCā¨˙8$†˛#pPF4hŌl˲­GūÉ Ļ–<ŠMą‘OCā¨˙8$†˛#pyštė5āŸ#}Átë¤6ī6°61‡Ā!pCŲ8ˆ ›ĩ~Ną–ÅEsÕŊOXˇ¸Ôų”8CāʎĀA„¨UĢ^RęčY(Ë gß´‘î—9Ē/ŸvĨāHė ėĘ*úĻf˙w\Úđ?+z HũŸĻ­Úũ%vPæŸŨc}R˛˙ĢŦŪģYÛÎĪōõ"pH eŽ*PŖIĢöj…˛¸­ĮÎX‡ũ?įã.č|ß|ßH eGā ”ÔoØdÅŦ›ļ˙XÍWZ*ŠīāœÅ|ęÔ\Đųžųž‘ʎĀAÉŠŨēSĪߖW˙ŖŊ'ŋbTā‚Î÷Í÷ÄPvJCƒÆ-q —W,šũë—ōS>}ę.č|ß|ßH eGā |:ÅĨú—Š Øsû'iËW@}Āī›ī‰Ąė„AÃĢZŊˇũŲ*d*‡‡_ŗv1q?į[ >ā‚Î÷Í÷ÄPvB¤îåWNŸ°dÃTä|\C&.økĪäa#ų6¸ sAįûæSBb(;ÅsaĶVíŋŒÔz§ĨY'ĩM—Ū˙Ę×Á :ß7ŸCŲ8(†úšîēa׉†Yņg¯Ųi=dÜ͡ :ß7 1”ƒā´îŪđīĸiiŖŽ=ūÔ)6ų ž.čÔ|߀ÄPvĐ I‹SZ!šnÍūãÖĄg<ëSrA§>āû$†˛#pP˜Ë.Ģ“>tōâ‹ÆÆdMüŸƒ†öį[â‚N}Ā÷ H eGāāīÔhܲŨĮZ !NĶ™\Ũ­ĪW|M\ĐŠøž‰ĄėäQˇaŖĩ 6ūäĮh”7ã¯ŗņ7đmqA§>āû$†˛#ppÎ9 •ŨŠfyShZ“ļ]c˙=÷x/ä+ã‚ΧÄ÷ H eGāĒ5 Žl~L+DģĀ)VÜũˆuí›üßt>%žo@b(;WmščĸKãfMúļ2ț‹ŪIÃū;&6­ ßt(šv1ũ^IH˙6Ú#6)ũ÷ũSŌPôMNûSĪ„Ô?Į J˙“{Ŧ×Āôī+C9šû$†˛#p\™Ķ¨Yë3Zņ 2 ÜíGßļö1qŋäÛCā h:öøYe:ˇĢBä~ėʎĀ!peĘeĩ/_8íú;˙ŗ2V’ŲĶ—˙ĩ[|ĘTžE8‰Aā¸ęDíē}QY+IMwŌļkė÷šå¨ÁW‰Ā‡Ā!1W-¨×°éÕ{žūKeŽ(įŽŨcŨú§îáÛDāC␁ĢĖÔĪ&!l׎wbÖ×UĄ˛ė—ōCÛŽ}‡PæÖšQ“Ÿ‡Ā\´ū~˙57öû7hÜüģžü JT–ëŊlWwīûVŌļ77žįŧFā8ĢĀūyå'Ę)Ūx(k›*č`ņüūôe|NÅÆØŧ߯?~—w¸Ė\͚ĩFēfõīĢR…9xėœ˙é—’á“ļ­šņm€Ī‚ķēÖmKFā8+–Nšņŋ*~‚¨ ņĶ+šļøB+TĨ SĶ \ÕēÃwšå{ŅWÖ˙ãûŽ:Qķâķėû7'#pÜYqíXröDKČiÃĻ-EāĒysC§ŧĪ€(ĮØŊ6ū€šJ‚ÅÆĨŊnãs*6–ø.z˙åËĀmSöŠNũ†›—ßõĶ?WÅJ|Ú [­KŋÔõšåL΍ûrãßōĘ˙Gßg2–ßHåŦ>{a,]*ǁĀ韱…ëîĩÉ o´!Ŗ&[úˆņ––‰™Ŗ-gæ [šã1oēļ]´ų Å§fŲō5›í­Īžˇ3ŋũÁâ“ĶlĶũ/ pįĐ_ĘúĀEôÜũÖI›īš&í{ô˙ĒĒūŽŦbģ˜~˙ŪŧÛĀÚyå­á“šßS§Q pŅ۟ũȆNœoŲãĻÛÎCĮėԇßxBæĮ^zז¯ŨfIYãŦ~Ã&ÖĸM;{ųũߨæÉSī[—}8*; ÂŽt4)$mų\Ū¨ŲsZÁ *7ĨŦŪûŒuč˙\€â;™ĢÍO„ú‹ŽĐ€Ē>‰ž –ļ`qōƒ¯mä”kŦ^ƒ†ÖĻ}'ģķžÃŪãÛö=äíËeé8*ėJͅ^’/ąŧųãÍOŋˇ›ˇũÄb3lĐČéŪCvĨåŌZu—ĪžyįŸĒĶ0ūQsoük—~ÉķøöЏč8eÉē÷KĘo6}ããoíâKjZˇ}ėŠWQ"Ķ@† –[Ķæ-풚—Úų\hu/odã¯E⍰+õ[´īūëę6“4´îÔSŖP/ä'@}€ĀEŸŧõ4´@Ÿˇ˜Øū?(Ã’Ō‡Ų…]ė X΄i!ËÛä9‹­vŨz–’5Ę>q2˙ņW?üƤĩíēØÖcg8*ėĘAŨ+Ž<ŧáĐÉŋT7S,ÛzÄ:õpˆ_õ]ōÖ­_’=|ü­|ÉÚ}äiĢqŪyļfËN;ųķŋ*Ŋ˙ņ—ŦuģŽvŪųįÛW6ąuÛöˇī~áeÜzöh§~ņuPÁ{î͏ŊŒŸFģ"pTØŅNĪ~éŖžŽŽōæ"&!ũOM[wjÅOú‹ŽĖxŋÔáļī‘ țš:ī=ü¤7}HÖ¨ ^čö;ŋūŖˇÍĸnĩZuęZz—˛ŸžpÚ.Ģ]Į–ßzgHYēG_>c˛& pTØŅÍ卛˙“&ÅŦΡųáĶÖļKėÛü¨¸Š—ˇ„Ėąvß#ĮĪ’ˇŊ=]pАŸ˙ÆŽ_w§%δY‹ŽķævëÛߒ3˛ lwá…ŲļGÃę'7 c”•uŊˆĀ6v‰šđ’KĻNZļée1jL3ĄßzđÄYÍ"Ą4M¨Jy/ã•9yņ˙ļé?œ_õWqōÖ7e¸Ũs˙ą2uŪyįYûNŨŧ Á„ëĄį^ˇŽ=ûzMŦūĮ?˙†]pá…at˜wã[uīvTRŗIĢöŸFR”$hąƒ˛­f­ēÖŽ{?kФ…5jÖÚ6~Í{~æę{ŦƘb÷SŋQS+īɄuė­:Æh9ąü4¨¸ō´1ŗmķŊ‡Î’ŠŸŊû…õŠčeášĩlc÷?ņŌYÛtčÚÃÔđȋī3w$WŪę7hč5Ą†+pî}ØĢ¯8*덪Vũ†ģnØõxD×;í—ĸūdf7OΙîI\8W8Åė5;­cĪø;ųuP påæŲmģ+VSį/ŗ‹k^j—ÕĒm‹nXëM%rŅŗX›v lWyS,ߏͿo؇ĀPaG­ģõOũu¤û‘éëQķŠ˙qÉÜøÅëŧŋ~SSjbökܲiãįåK›˛xڏš`Ss˙+1ûzO%ˆzŸ„Ąŧėžä0Ōĸ׹÷€˙¸˛múüD¨¸ōËŧ­\WX‚ĩįĄ§ŦAŖ&VŖÆy6g鍛SŸ?í-URySĖXļÖÖė?ŽĀPaGu\ųJaҊDöĘeڂ…_ā$i8u–”éĩs×î)ЄĒ~t˙pn OātŋsŸ¤Ü˙žk؊ģņfO¯Ķ ‘7 H$Ë!ilŨš×I~%Ô\ŲĮ„%ëlŅęMYa!RōĻ8tl™¯“ŠĀ6vX\pÁÅYS—~éĘHrÖĒcNŌ&yS%)iRŸ9×įÄ/p6÷ú)+ļXĪÄĖüûū×D2rÅō˙ĩîÜģ/ŋęŽōČÛÃĮ##oo~ūīLĻ ÂŽ&.ŧĸIķ_–E˙2eÅü˛Ux˛\ đ ÜĸMŊŦ›dMũæŽjĶ) Āév ,K“X6o×õ7ü\¨¸˛‰IËnŗą3Fŧ)nŲū€Mģa+@…=\VģūÚ%w<đCYTFʤiôiáŽŋęGĸfP Lđ ˜dĪßüŠūmę+ ÷ˇlßæŋļëÖw9ŋę.˛ą`ã>3#:åíŊ¯~°Ž}“Ęŧų*l*ėphØēsĪ/Ęzį]bcŦņäK÷kÕk`C§.=KĀ$pj•¨éīyį_`Ų3–GĀ)KŲĻs¯ßŸÃ:ŠÔ\Ôʛ¤-RōĻXˇëĄü$8*덠Nƒ+myøt™¯wĒÉ/ÕDrĨŦš˜āžsMĢ÷>ã=ߥW‚'|Kn˙{ŗ…F¤j”ĒæĶí@¯WŒšģēL'ÛÔžÛvíķ(ŋęŽôĄs|`ƈüĨ¯ĸMŪ^úākë–['•×FPaSa‡B˙ÄėIÕzŊĶ’Flʈ?5ŧĒuG~BÔ\éūJ>žLåM Ö÷MH)qĶiBÆ(ot{yÕ-PaSaG+š´xGƒ˛đC}÷Zuęų?#ęŽäō–”9ēĖ3om;võVc(É>‡MŧĻĖW^@†› ;,.ĒyŲĸĢļũ+y ŸuŨ˙ļíŪw2ŋ&ę.üy{Ä%ÛëŋúŽLåmÉęMÖĄKŒuėc;|*Ŧ}Ÿ<ˇÜúŊ!p@…M…*ĩ›ĩíô1Vúļ­:öüįsX'•ú 9ԇ5.%ĢĖå홷>ĩKkÕĩeˇÜnÛ˙ņQģ˛iŗö÷Ę/žąÄ!Ŗ ôĶE⍰ŖÃŪę54oŨÕŨûîæWE}€Ā…&o=Ríĩ",oĮ ĘÛ+ŋüÖ:ÄÄŲæŸžé‰Xlb†ÕŽ[ĪŽîØÕŪúėû€ûRSîuëī˛^ĶËĩĪTØTØĄŌŠWŌĐ/*Z|´–iy÷-)ĢčŪ?õßë6nŨ„Ÿõ<´L_§žqöâ;Ÿ—Šŧúåw›<ԓÅÂō›”iMšˇ˛›ˇė´ûɎ|×ļí{ČÆLgņiÃ+,ë†Ā6vąÔšŧŅkšŠŖĸ+)M'âæwĢėĄ CˎŨ_į×E}€Āfm;ÛÆ"*oZãÔ˙øÛ_üÁzL/rÁyMrŨ]ŲäĨˇÚčŲ+lūē=Ū~ŅRŸ p@…M…}įŸáøą oų6*ŠâNKpšyāüķ/i9+eî4ɯDTķÆECyŌÆĪũąUĮ˜4ÎDę.ȧK7XߤtK6Æyá­2ˇ?ZRÖØ2 ÂŽ.ŧĸi‹Ęk2ĘŌœ&ümŪŽĢ˜}Ŋ7Ą¯BĮ-yĶ:ŠZuAĢ8hT˙Š Ŧ“J}€Ā…ŌŨ Ŏūė=;}žežlĪŋõqØōV?€ŧ)ŌGMõú¤VöŒ>TØTبYˇūļëî~ä‡hФ‚ œš>´vĒ›ŸNâÖ¸e;o-Չ×Ūæ­Ōā@-į-éĩęîŋ\ŨŊĪ:ÎFę.pčü2vĻ'\Ožz߆äL° ŗŲŠŋ YŪŽ<˙ÆYĪåL]pÖzËTØUæz%|M•T0Sķ¨žķ?ό›ļuŨãZ5šNŅžGÜŋå~Ūĩ9#Џ‘œ3ŨöĒÚܒPaSa‹—_Ųė=­—WOä‡k:U3jŧûĩę5(Đ<ĸ ę#§į†NˆJĶ訿Wwųˆŗ’ú abßÄL›ļl­Ŋųų LĒģxõ&ĢS¯~@y[ŋãĨŒœ^%'G†› ûœ /žtÕŧu÷ũĄ˛.SĨ>nn0ƒĢėũS(#§ŅxüãŪú?­;÷œĪ™I}€Ā…ļĸIˇ¸Û°÷¨Ŋ÷ÕßEM O9˛į§Ī[üą-#ę8*ėHͰyģŽŋĒJ›šQ•‘›vÃV¯¯œĻQ߸h@āBõƒU÷ˆáSÛÉŋĩ)™ŪčÔG~öŽÅæĘ›?+Ā!p@…]Õč—>ús›¯øč9`ČÔĒÕ g(õ^¨ģ„šU‡Ž›iĶ^o{ÄUyyC†ģšWØu\ųēFy!PęĮ×ĸCˇˇ8CЏ’uE˜ŧb‹]ÕĻcūčs*ė*Éšįž7kƊÍß!OŅC§.ũąE‡Ŗ8KŠ8 ÂDí&­ÚH%}#j[´īöUî÷Sƒ3•ú#8*ėÔŦUg׍÷>ų•`ôÅÜĩ{ŦeĮ˜ÍœŠÔĀPaûi×-.õc*ĀčNą‰ŋĪũžęsļR p@…íQĢ^ƒŸU—Nž•5Özؚĩír‚ŗ•ú * œĻúŅ|nZ*OôúįlĶ*+ō$Ŋõā‰v‡ķ7ėķÖI´BŠæ w_îxĩO•IeXļõH™ Ē@†ģzUØÃ˛gŦøIŠū4jæŸ5m“ĀK}PÕNĸÖwđHoyģ!“z˛Ŗ•R´$ž“8}5™]Ô~´tžæz,é9Ļ9ä…—TjŲŽpöÕĻKŦ7/nkâp•OŨ!be{ĸŠĀPa—ŠķÎģāDnEųzĶ6_­JҍEÛˇĒZ™^ÕęŸjÖĒûSÎXęƒĒ&p¯ŊÍ5k] ÃĻÛ š´đž $pz^÷‹[¯YĶ"ų3^ēlĒ$‰Õ]rÖ>uƜų' ļ'mū˜“Oˇô_ "TÆpĻuB†ģzUØG(å惊¸æíēzŲŗ@2ä¤Î œD§ÆOø”åē¸f-o‰}&z,TyC†ģzUØÛōΡMU´\7UąríČ+×bÎZęƒĒ$p—PÎ5-j{ɓ˛l¤@įΞų_¯×ē×° ąŌ_ɓ˛w.¨}Hđ´_…ŅõĶsūÅ œdRâéöĨPS­˛pēŨĒcúĀPaåŸķΡ¯ĢXšū%¯\_Tąrũ!¯\ŋāŦĨ>¨J'Š‘šŅI˜°5û{M‹' ›DRāŧuˆs÷-ÁŌûwߜĢ}č=üQøøB8•WWxIëšP8*ė@¸æ8M(åŽ"NÂ"Éņ6PÆKų›(%AjŌLɞŋĻ÷ˆ´ĀŠ›šXՔęī§lœšpũĮ]RĶ~ô¸k Ö_•KÍĻvQ¨™ņGŸėŦ"åē;7ū×WŽĒŌ<|Ÿ¯L˙—Ë9sŠNJĀ)ÔŠĖZę˜ŲŪT ’MšQxeÆÔá_b7jîjo@š`#)pzO=ī,…Ū[Į¨ė™ŪKī̌YINˇ•ŨS?:íW˛(iÔû!pTØEņ]ĄŒÎwU¤\ß*WUiūBåú˜3—ú * œ›ÆCr¤fQĻĢđDžšŌCĨė˜„GY8MˇáŸČWÛøû¤ų_¯LŸ?ŗ($Yúåé=$ްāŸ2¤đDÃū‰|ũˇũųęØ5aąĘĸæZ7`AĮíŸļ ÂvnŽĢ*Ír” ¨*ąĀLä @…]4MōÎĩ%yįÛØŧûõ)åĸ>@ā8*ėčĻĒžo” ¨8Ž ¨°8ĘEš¨8Ŗ‚*l„€rQ.ęŽ@⍰9ß(P pPa#p”‹rQ pGTØåĸ\ԁĀPasžUŋrõĪ+QņÆCYÛž|iœ‹į÷§/ãs*>Zvčūĩ&ˇõOĻëMTĢįũËgųÃ-ęîŸˇđÄžÁ&ãÕkô|°ÉpuLzŪMúh˛^=īVY(z]QĮîĘVÔąûW‰tėÁĘĻ÷ VļÜĪũÁR~oĩŠxCt(WäétNÁeÂ"jŖ~ÃĻ˙­ŋÁDEKEéų@̏å§Î)´Đķz]QĮîĘVÔą+Š:ö`eĶ{+[îãgJųŊ}KÅ ĸCšĘNâeģׯ8y(͂ůĨŊnãs*>š]Ũų[ P°,–$JĪ“-UĨįĩDV0ÉŌ6žĶkô|0yÔ1éy-cL’ô|0AsË_;vWļĸŽ=˜ēcV6Ŋg°˛å~â;[œWoRņ‡čP. >¨ŽĐŽŌõˆĀ6ĸCš€úCā8*lD‡rõG pTØõ‡Ā!p@…čP. > ÂFt(P p@…ĀPT^ÛzėŒ ™´ĐZwęa{%ؚũĮ‘7¨°8ĘÔ\4 ÜūSߨ¸…k,>5ËvyÆ^˙Õw6uÁJë50Ãŗ§Øög?Bā8 ÂFā(P pŅ"pšÜļeģŽvnV§^}kÚŧ•Ŋúá7öčKīYĪžņöOŸļ>ÉCmâĩˇyĸ‡Ā!p@…ĀQ. >@ā*PāÖzŲ7kmÉŲöōûŋą3ŋũÁÖmۛ+q-mßŅ,.1Å{LqķļŸX÷ū)AWP@ā8 ÂFt(P@ Üĸû-eØ;õá7ų’æbĸ)Ķ'Ū–¯ŲTāņ7?ũŪ&Î]n CÆzũå8Žŧ¨ŸWvĸ㍇˛ļ}ųŌ8 ĪīO_VEË>@āĸQā6>ø˛ ĘĖ9KÜ\¨ õÜskx=˙ôëÚŒ6ōšlīɯ8ŽLi?䕟 Ę+~ČûĮ@āĸBāv˙ė ‹Mä T&p'?øÚj^zYĐį]ėzđ)‹0ؖl>ˆĀ!pe~Ņ“w›(§ØŊ6ūĀÉC™,6.íu[.ë*|.!p€ĀUBË7Ķzîõ"ÅŦ]įģaũÖbNņί˙h‹oÜ` i#ėļ#¯!p*lā\ęˆ¤Ā­ŋ˙„ž2§H!›8k‘uīÕ7$yķĮ‰w?ˇacĻXÎô%ļë…O8Ž‹6p.õDBā’˛ÆŲ“§Ū*awî=l ¯ljo}ö}ØįâūĮ_˛¸¤4[˛ū^ãĸC… œK@}Ĩ¸Ví:¯ŸžpÚęÔģÜNŧûE‰åÍkļė´Ŧņŗ8Ž‹6p.õ”Tā6˙ô´ÕģüŠ ͟_Xíēõm׏GDŪ\@āBŒk×<ú‚7@aÁukwEm÷ā͝X\bšÍŊé.ŽŠ—˛SaįP pĄÄŽg~nCFMļ‘gx+)DJāf]{“õė֜qICFØūSß pTŧ” 8—€ú %ļ{˒‡äx#SÕŧYy{āŲ7ŦnũËÃî_ˇī‘,{ÆrŽŠ—˛SaįP páćOZBJĻ]ŗtU‰8<˙öįÖ á•ļįČS%’ŋø!c8*^ĘN… œK@}€Ā•hŅûûž°ūIiļâ–Íöί˙’|=úŌ{ۍ͛7ī(‘ŧŊųé÷våU-l͞c/e§ÂÎ% >@āJßøÎVßuČâ“ĶlíÖŨEŠÜÆŧ ‚wücÉ'ę5áJ“†äØÍ÷Cā¨xščPaįP p%‰ũ¯ūΛx`jĻmÛ÷PéR3kß)Ö´yKo=ՒĘےUëŊüû]žfŗ%fŒ°ÕQ"r*l*lÎ% > >¨4—/r¯|esŽßčeäŽ]ŊÁĻĖ]jĩëÖˇ”ĖœR zPÖ.Єŋ~‘˜+rĢvCā8 ÂÎ% >@āJ÷ŸūÎÖ8n7Ũ÷Œ7 oöŒ–6ŽŊū|rĘÚŠéõé׋^ÎK"ˇėæÍ–3uĮE¨°s ¨¸HÄ]O~`Içx‹ÖŋøÎg! \ģŽ]íÆMÛCŪ~ĘÜkmûŗ!p\t€ 8—€ú‹Tl<üšÅĨ´IķWÚk}W¤Œ-ēaĩīVÆ.säÛ{ō+Ž‹PaįP p‘Ž•;ŗžŌmé-[Ž^ŨûČqģŦV{5ŒIƒĩŸF¯˛Ûö?ißøŽ‹PaįP peķ7ėŗŽ}“lÃŽÃyk¤ū§mÜ{Ôj×ŊÜŽßpWXŲˇQ“fÚ Ėo’á´ė1vĪŅ×ŧŠN8.:@… œK@}€ĀEzôęŠolâĩˇYī¤Ą^ŒY°Æv˙ÔÆ/Yī ~Ø}ä™båí§/œļúW4ĘŠĒu['˘oYcĻØ}ĪŊĀqŅ*lā\ęŽŧBũŲ&,YgŌG؞‡ž *o—^VËî}đė嚞<õžeš`ãg.˛ƒ/ŽĀqŅ*lā\ęŽmIYcmÂŦEvLjiH4íȊ[6[Ã+›ÚÛŋųOģûū'ŧ ÂF⍏ Šĩ˙xÂú$fØü•ˇ\GÕÅčI3mÚ•Ū6ŨRŊŽmמ§Úté}Ļ2EŗvYįō†ßļėķAe;öļ]cPaWļįU•9ɍ3šņ`(‹b :@}€ĀUŪ¸îžŖÖ#.ŲÖÜš;ā¤Á ƒŌíĻÛīĩ-÷>`ËļŠŪ;(û~4å:QаĢũ3Æđ›Ž˛¸ŧQŗįĐęŽōĮ5ˇė´ØƒíÎûʗˇ˙ø¨ÕģŧĄ=øô)ëû\i–áBā#8Žú+ĢIƒ—Ŧˇ†¯˛ĢZļą&ÍZÚėÅ×[ëöíî§>`*‡ĀQa#pP pQ{ŨŒéc=ûÆ[|RšM˜ģŌvŊĀ4"G…Ā!p@}€ĀE}Üš‰|+ĩĀí>ņ…œžÄR†æØāŦ‘ŪB¸“ŨTĒvpŽ +ÃĮą‰SĮ؍wíĢTįG}€Ā\”Nú—<ÄîüĨ#Rv:fq)YžÜ!pTØÕIā$Wköˇ)+ļØč™ ,%sˆÅėgƒõąÁƒûYlŋž–>4ÅR3ĶmÜôiļøæĩvãŊÛög?*rŋŗVm˛ĨĶcėāρ–žÜŪ†Lˇ›w?‚Āõ‡Ā!páË[ß\y;ņîįį…9vō]ëWI$CāJˇ{aŦũøÁŒŗž¯_™`gĪą“‡2=!ģ}e_ģfrKLčlÆÚ´ķí–ũĪõ>qņŊėoOõöņíëmÃ˛8Đ͖oŪ]ĒŲĪ88Ž’ ÜęŊĪXę˜ŲÖwđHĶ…/œ¸ĒåÕAåÍÅc/ŊkMrˇ wß:ž´ņķlãá×8*ė¨8eÚ&/[o}ãûØŌyiöūSc­˜ī ¤ IūŽ›ŨÃR“ēxŲēe›÷xī'Š›9žwí%ˆ{×'æŠ\w[ĩũ¨ĸDā”ä˜ŋa_™œS[ąŲkv|nåŽĮŧV€pŽsŲÖŋĪŨ6톭–˜=Å{ŦŦށ+eŒœsŊåLžkĪŧõY‘,š6oyÖc÷=ülHۅžō K:ÚfŽžŖÂށķ‚^rŗ%'vˇ§÷eEDڊŠīߜl÷ۚ`ƒ´ˇĄ9C­K{ûŅágm'‘[ŗ$Β°ÛžĀ!pÔ,pŠúš–Yßķ .ēÄVŨûÄYS|ÔiĐČ{īpZ”ČqĮ\ŗV]¯ ȒÛ°\AāĸMānØų˜ˇÆYIÄ*\1+ŠĀšH6ĻĖ3qjÆēgß>öЎė2ˇ@ĄĻ×ëįÆzŲšĸļÉJīn3–­Aā8.ĘN™4E õXQ–T×%įL/đ˜˛fšĩ. zÁöŗķø'^øS¤]÷~Efë¯+KiX!p!DRæč"×5‹&{æ_Ú°Éķ8*ė 8ũW›1ū›:%#`ŸļhŒļĻۀä„bG p”ŋĀI„šˇëjWĩéäEĢŽ=ŧĮôœ˛jʂĩék š´đžÔåļķ÷UÆlÔÜÕŪí‰×Ūfĩę5đ„LYšE›æKšŪ[<=¯ãÔ6ĘÄIū.ŽYËģī?~‰`į>IŪķ:VŊŪ¯ļíĐ+ÁūáÜ^×'ތ.eؘÄKŅDŅŦĨ5΋&yŅĻ}§ŦQãĻ_ß$o˙ģÆģ5Ĩ"pTØ!púĩĪ€D{dīÄJ!nūĐ r4 Cā âŽgbfŲQŸ3×\)Ųr}Ō$IĒ`]‰$Tjęô7Ģę5’1ə“Ŧõ‡^ödOÛh_ē­į”1sW8į?~Ģŋ95{ÆōüûÚ^åq™Cތ.}Äø"…é†õ[­gŋÄReÎBf-ÛØOŊ\ä6ƒ‡G⍰+Dāz&¤ZŸ^­ŊŸGīIņFūpfZĨ‘8ëĐôoǁCāĸCā$e’*?4I•­Â˛Lā”mĶ ?7øĀ‰”—ÜI´\č=uLڗ˛iūã,Nā”ÔžŨž†NČ?NmŠžę\Ž]ĮŽļ큧ĘEāŦ\gIiC8*ė¨mBÕ­Ē`WÜļÍŽY8ŨFˆˇ}[Úęy1•Bâū÷Ŗ™6jXo[}īc‡ĀEĀwūļųáĶų÷ÕĪ[Ų3'rūm•é &HĘĸéuĒŖÔäę˛q’,Ũ×ëüĄíõ×_O†"pjĘ2iáYûCāĸ@āFOžm™#'xŅ;>Ņ.ž¤fšČ›B}ņô~îũ3߀ĀQaGĀĒ4c$ۗ¯Í‰Č(Seö4ÜãģRŊûe%q‰ ]‚v@Fā8(?“\)cæîkÔ§S¤2en‰™šT‹¤˜„t1ûzo;×NũŨ”s÷ĩ_‰ 4”Dā”}:ui×Hč¸(¸ŦQōoKĻÎ;˙|줿ĨåĩęÔ-p<’8Ž ;NōÖ)Ļ—ũęg3K%Tŋzn´MĖjciņMŊLžämĮÍqŪũkÆuȟ°7ŌŖXĩ*‡Āņ)•Ā)Ķ&Áņ‡ęhW5I‘ú—éļĶë4ŋ›îĢ.Ķ`eäŠ$eŨô>Ęēų^i`BÍŠz_‰ž“´pÎeÕWĪÉĸ›‡‹"‹†@⍰ŖQāôß°2ogžZĒLØõŗģy’,ÛĻyŪõkė Bˆ´Äí^?Čf¯Ú„Ā!pPƧP’ Âá:ú+ŽĖ›2qn°ĒmĢÛĘĖ5ĄŽdMÛžDKîÆ/^įũuŲ8íĶ?5—^'AsĪšÛzÜIĨ{NĸĻŅ­…ûīųCā8*덏„´ĄöōÃãJĩĘBö æŪJ ÅmĢĨ˛´mY4ŠJęvVeĀ!pY+iøŗ[/5ŠFJXJ Cā¨°ĢĀeO[`?šŗäøJČ2¯ōšNÃif}h[rÄî•ÃŲ6nÖ5Q(pĘzŠéS͗n>6ÖBEā8+AĖ[ģÛŽ_”RĒfSõm“ÄEËČÔQÃz•[Ŗ>@āC⍰ËUā4jkpJéĻ šce{õpVTM-ĸųė&Κ‰Ā!p€Ā!pG…]ĩN~ûÅĮ–*sĻ~o°ķà Nę\ę™Ķ8ęŽ@ā8Ž ;Ē.gĘ4{v_fŠ$I4Į[4 ÜĄ;’méē­‡Ā!pTØUCānØņˆ-˜Ö¯ôĶvÜo'eFí2[ŲÓ88C⍰+ŋĀŠét@BWûņƒĨ–¤ĶgÛĻåŊËUĘÂŲ~xzgƒ´îÔãWēG{ôMÍūá}R˛˙Ģ2”C‘ûąoGā8+qL]° ¤šÚB MR#P5ēU+8LŅÖëg§ĐíąCZŲČÁ-íé=iÅîcŲônvįҎ8øž+;Į \N23zhįˆŠÖ—/ŗž/ØĘ 75ÍjĸßîH ˜uSöPŖ_%tEeåÔ?ī–÷#p\ЁīCā8NāĘ+pЃã<ኤĀũâé‘ļ|FWo ­å3ē”z˙û6$x™ˇP›pŪ“‚ĀqAGāøž8ãޚˇúžûmÃ˛~oęT&Ė dĐę 95sęqÍĮV‘æŒíh;Ÿzã‚|߇ĀqWNKNŠ kāB(}Ė’55{ʎIâ&fĩ y_‘ŽĖäļļzįŽ :đ}#pĮ \ųîŽûĩįö k~ˇ7Į…´­–ŅŠÆiD\_š›ôą‰ŊŊŠS8.čĀ÷Ā!p'pĨ¸Ŧ!}Â)ĒÁßŋ9šØíž~e‚—a‹Vķ¯qゞ–ž1ĀÖ|Ŗ>žoCā8Ŗ[āļ=rŌæOčÖ\kj e[ 6(Īyā"!rķ&÷˛äÁlÃĄ—8ęāûFā8Ž8:nüøĖ°ĻøĐÔŠP0„:b4šBŲȅĶbmhN–m~ø4G}|߇ĀqGĀí9ņ™en–ܨI4Ô–LéėM#Ž@ię@Ę;$ļ“FÆØ#ėŽ'?@⍀īCā¸h9íĶYŨĒĢĀ­ŨvGX‹ÍKĒ´ĘA¨Ûkےˆ˜›n$Z2ršėô.6lėxÛyüŽ :đ}#pWá÷ÉėūšqÆ>}Ŗ}6ŗIu¸Q#ú‡4Á…æsģëÆ~a v(Š4i¤Ģ&íĻĻUÍ[—žÚկ˜o{O~UåÎ>™“aÍ]Âøž8‹Ę8Oâ~ČÛūEûxÖûrę…U]āÂm>Հ„p&ß-Ā)fŽją%¸"/ʲø¸N6į†Ûl˙ŠoĒ”ĀŲ¯g4ŗOg=`ĪÉ=fũÉ~ąø .čĀ÷Ā!p\ÔžĀ…$Îōnß§ĮĢĸĀíx␭˙˙í{pÕ}Į™6múȔĄ!´“’ˆÁÆOŲ€$˒eŊ,ɖå‡lŲFÁļ$ËØ`›‡ņđ#ÄĻ&XV° $&ĘËP×w<5”&´%ĶxÆĪĻ´e:tš?˜N™é´i8Ŋߍޞw}ģ÷š{õųÎüFÚŨģģ÷ž{>ûûķû­™\üÎ>ÍU8M&Ø´rBÁmYį­NČ´ŖmBR›?kŧ;úZS1uJäΜ|øˇĖŲåÍšžēįzūלîC‡Žøŧ8€ }NqÖ.F!Äāúîę4īŊ:;Ø˙&ĢžüÅŦaJõSƒTˆČ…ũũŅ>3ģ!õd íkŽŊŅė~ņDdÎ ‘žī}7ö~ū/îũ]čų…ļŅĄ#>o€ā"Ķ€S@œ {ˆ5Āĩ6ķliŦÜĒÅŖ ꁓŊshĻŲ˙Ȕ‚{á~ttyJˆëYp‹y¸˙™Č‹‘&zīŨŸÆl%ŋˆĪ€ā¸Č5`Ęk€›;cd  QíŌ 2 š&ŗļé×eŦ› Ž­ūĢ—@ÜU˜ģ7<™I C¤I­w'ŋˆĪ€ā¸Č6`ŸĒĢ_€ûŪÛgO0PōŪ Eį• äƒŋZ˜5HõüåŌŪ?Öqš;ˇŖ9iD’†H“Ų…îčĐŸ7Āp‘oĀ!Žč!Vŋ÷ôĢo˜‡WO 2ōž)œdå˜đd Q Ąæŗ¸Yõ7™ŋÖnĻO—væi1.}ˆ4‰ë~‡ņypW2 8Cˆ+JˆÕ/Āízj_āqež Ĩ°ĢĘie Pšl‘ Ėę^_`ŽūÃ+ĖSĮĪ….‘o°ib2:âķā8ŽäpWĐĢ_€ÛŧũĀáĐLNÖRķĨŦáéÃˇ—kĶlØōÉ7†Ē”Vāibû™ņypW’ 8—÷Ģ_€[ŗöŽĀ)D28%˙ zMd+Ū\4x<Ē>kKcYŅ.ãiâ§‘KÔK‡Žøŧ8€+&Äå%Äęā:;[ÃØž-N)­ īSéG˛­Č [<톂ƒÛĮī}ÍŠ!xSŪˇęéSŠp9‘&ķÍDŊtčˆĪ€ā¸0@\NCŦ~Žmns`€Ķ,Ô#ßĒĪxŠÆÃe p‚¨T&TY"¨g1‘iÆŠÆĒ „-åõ§ŗĖ˛wār"M0Û4ē‰zéĐŸ7Āpaƒ¸ŦCŦ~niīâĀ'ī› )S R‡lęšúŠÃ*HÔë”N&tžũ‹ų)áOĻ×čũÉ[(ķ^kÍô ß뺏܆HK/Q/:âķā¸áp SvåÁvÄėŋķ bõ p}ËĨ—>:7pęo]S%äÕßLö×õŽéē%đ5ŋøD­rōāÉ’Ōt=Z'ØSÎ:ÍtUØÔ{œÃî4ķûʨ}ų¸ŧ„H“ÛŠÁīUiڅžé÷+ŽŊĀāŽŒŲ€+ Ā•‚ų ąú¸{ŲxĒę‘f;M^­l N3A3‡—ũįûĢĖĊęŒŧoé.o!R ø<œ}ÍOcļ1Ėp—YˆÕ/Āmųö›ĻKeRPS2Åė™BˇĒ‘Ôh¯MX¨œpŋ8ŋÂĖ[ŧĐŦß÷ZÆíË pų‘bĀe~€ĘAˆKd _ãļŗ1ģĪÂĀp™…XũÜĀņķæÎޞ„ĮŊˇûVS[y}ŌqhŲNF°câļĖdR„Æ˛)ÜŠ‰ųŽÎ°w`ŗiė\™UûĀ8DŠaXŖcö퀖Î>uũx|Uã'įāÎõŽįGĒį”ųĮe×)f_7mė%Įų‡Ŗķ͌övĶŅ6!iE„dŪšLAL3G3Ų_ãÛqAÍÕ$„\ŪĶŖGvš˛š–@eŗYŲ𧝔|G1ŦÖÛĀƒ¸ęļfđ$Ŋ˙˙0fO zîđĀpÁîBOķ0ūŠ §¸™mõCÚ­UUwŧs-ķZYÁ›ūC ŌÍMVÕ¸8M°ĖiœžŽ§ëœ^~c“Æ^cʧN1uÍfZS“Ški6K{™íÛzĖ‘īßíŒs:ŪŲ5fßĶ;̈́Ē&ĶėLÖíËņĀ6Å°âØ™Ŗ¸ÜŸäßÜĐÆ8.c€‹ÁKŦĄ~0Œ~”’NhpĢļū‰yyī¯B˜ģļĖ2+ļîsļ56ܞ¨”Đ7Ķ|pÉL•˛¸tU^ŨWgúˇÍ0ß}ëĸķū4!a÷+ī™ûŸ|Ų,^÷u3ĩĩĶT5Î4ÕÍŗÍÄĒĶŪŗ!ãI iĮĀ1qà å}{weT)ĀHm—Ž{\‰Cœ¯”"ANŪĨ%ķYhū_ßé6ˇMkÚÖģzYŌ„¸ōxåb2ƒ×Tí@´\Bœ<Œšô`Ģ*,é^š×ļd*ãâ0,ĪÃJÎŽ¸€+°8.cOœÂŠŋˇĩ֟Įdž%õ p˛)Õå5ļ6˜oŧôîĐúmûŋŸ˛xŧÆ­8ØzˆķĻi_8/4W´™Šz/ÄÎsrØØ…žãæ|īsælī#ú]Šk÷Æ>īÚØŌg.^ā8äsæëȘ}T˜oš¸U˜ļY•NČĐŊ^áÃ3GĨwĻT Ū1tš‚8WļĮQé/wâaMzč]ˇ.”WĐk —ŅBh €āP‘ā-'…태@m\E]™–sÚĢBôŠĘV­Œ¤l–@.Û1uîuĒȰîO‡ā b-ŅBöp‡ o×=ÍĀĨ˛ßožŲQ•ōúJM÷šLSŒhœ]&yŪ4ĢUaXy Ũë;gufØFāōbíūĜžįhÕp‘¸Ú–9Îē°Øœ%=\iÃ[Æ!ŌBœŧrĩŖĶŽGS.ļ|TFøđíEÎŋãálr`yíĶP7Ąāđ–+€‹˙æ4Äú3yúhŨp‘¸°Wđ–“i!Ί™ēuŗãŅJį-SîĩlBžÉLŪ=?)KT–kÜMŸOę THĩ̝§$nč;™ģ+í!.WÛ<'RW3Ŗ€‹.ŧå4DZh€“ŽĒb”//˜­ĒË4 éR–hûãëoKn]uĮxŗëĨ–ĀÅ}Gŗ ąžë~‡–Žz€›ÖÜx;ųū?›™Ũ\´ā-o!ŌBœėĮŸ5úü•Ī:z ŅLüŜ–ĩŌLRoN:åt,j&l:€rú´qEˇB\üw6Ãk iņpĄ¸u;÷›G÷ėĀ­XģŅl{ö(~x+Hˆ´'›×9Û CĻģĘ ˇtų‚ŠĻÖzķöĄļĀŠDŧeąŌ%ô­ŠŧŅė}ķũP´¯(\|[ˆ ą’č!€āB oŖ" ĀŲq­‹–™Õ˧šåK›Í#OøĢîĐÚ`^˙VŖo ĶĖVŋåē4ÖnZåõf÷‹'BĶžĸpCí†XcF߈Āpaô8ÔF%D&€ŗvߞCĻŦĒ>ø)ÍČË{ũĨŅĖS…Dũ¤ŠŽiöųģPĩ¯¨B€ā8T—Šų…8ÕCõ“@¸ļfŧ9pâbčŪ'‡ā0• ĀÉ7­žÆü؁ϔáĶo>Tžrŧ[Ī‚ŅĻ{eWŌ B€ā8Āåâ4&.ŲÄĨI–<øäķ-Îd…ĮžķJ¨ß#‡āÚÆ§Ū0ęĐFŽ/7c+ę˚Ũ/ mģûąƒæÎ÷äŽÔPß&'Ņi.ŽĨ” +}'WāPTÎBœf§zSŒhÜۊ…7_n*Zß9ķĢĻģˇÃ“ËÎN¨ķā:VmÍÉ{ā¸b›RŒL­ž5.Ųī}ËÆ˜SGæÄÍ0Ũ´r‚iŸuģéõ­Čŧ7!Ā ßøÍΚMO˙yÜúûŸ|Ų\5âËÎŋ¸íĪŋe–oÚëüõŖīŅާNu Ŋ9°´ØÜ§}õŋÎŖÔ Z–÷īŽȁãį/ņ yĨķŲk˙æ›?1ģ_yočuö:u]˙-ˇMķ•ž€āĸq“ĘF: Ļąmsž2nš‰ÚÖ4Æ<ųüáČŊ/!Āų4yÛŽūŖëSžF'Ü cn5ĩs–šĪūöī:€Ĩm‚'mĶzuˆ:– LÛ¯ûēšōękMķ’ÕΞąk8—^o=}Z¯åĒÖEŽiß=¯Ÿrļĩ÷l:֏šhnžTå@Ÿ€MëõA›ļé\ē]§ĀR@÷ĨF;@Ēđ-‡ĸpöĨĒâfŗcí$ŗwsšY>o¤™ßVf^ø^d߇ā|šāLā”î5׍7†Ív~Z/H˛Ûr‚,yäŲ“Îz 4ļNu>íã†7 pÚß.×´u9áQAœ`ĖœĀM@Ļqy^€Ķ1í ;…M'T5å4 Āpa2•ęēeÔĩfu_Ģ9xėDä߇ā|šŧdōZĨ8wg'P˛Ëē e5-βLŪ/˜{܁)īx3-˛Ĩ0M:€“7M!QkgĻãzN°Ĩ1k‚*c}Gã•Kp •j•éXō° ä, \v œÂŠ‚//QUž=…k5ĻNûÚI ÚOãŲŽ~Ncč8Āp!ŽdN^6…>•JDĀ#Ī™ĀLëĶœ J¯—'MŪ2yíUÚωšā 8pé8ŪY¨ōŽŲPĒÄ Ųĸ6+XŗįtK“ŦWĪĀiV­Æä 8Āp!.ōįNų!°ō&ŧ•÷ĖÎ,ĩ0÷˛Må!o›ˇDöÕ1ŨcŨ†ît#Úf'7Øô‰Ō}ØcY@´ŪB{līuiŲB¨=¯û:8hāB\äŽØfŽRZ€āB€‹ˆ Ū˛M´ Ā!€C!ā8 €Cp‡8 €CpyūQÕÄ÷¤;™Ā;N“ ėëŧ¯ā‡p!ŽĀ§ôîō*aĨ7¯dŧv­‰j“ûڙĸJ ĀĄápšŒãžũœSJ?)qT8ҏR]Ÿģ„žŌėp!ŽÄN€ģŧ–en8ĶvåhķîĢ›ĀĄápî˜|˜Ōã(wâ5_÷`•Č”#1Ņkä=ˇyŨš8„WB§ŲĄÖ*UÂ_ũčĢ:‚ •j{ĪցĄNCų֔ŒW7ÉŊŦŠōÔi_•Ōr{đ”GGĮPÛĶ:UK‘YĪ  ĐĻėŅzĩiw ÕÖ5ęZlמJŧ-Ķ>Ūō{B€ ĀŠ3ŌŋūW§"ŗe´ôc.pŲÎÁŨš=pęLöQ‡  Ķ>JūĢŽ@ˆõ蝖ĩ^”íhĩ,¸Sĸ_Áĸ…JOU ā¸LNëô°Ą:ž‚0wuĩ#mëŧg›SŠNí-ŲõŠŨЌœÚĒ‹¸Ãĩjcļ4žÚĒ–uWįÕ’ R×bÛϤt>{-5m]Î1o:ŽÚģãĒũĩ,pĖĮ8W!ĀåØÔ ČC ˜ũĄ×_u ‚0wąųTį†,uęä!P -ũoMá!­¨ŠĶGÁÖUĩcvl§#o]!ŧo— ĀŠ­¸Ī)O™LŪ0ĩA™ŨĻRq‰ŽO/úŪģ=ßîá jcw?v0nŲzÆm}ayĪÜįĄęšô`eKØY`”GNíō×~ũ3yņŧp!.Oϧry ÜáMũ¸Y¯€sĮą5QmSuXnŗ!$œ:u\Iu2Ök!ö•ŲëāPN^boĨyŨô`"sīã­ëë†@ícÛˆÚŖÚ•}€Ņ˙îãx—m]ãd'ø¤iؚ€NûŠ­æ{Ŧ‡ārl‚(EŗĄT÷x8yܞĩ '˜šŊh‚BCŠõŧiģÎĨÎDįsŸS [ā€Ca8=„č{ín?‚#=ĘÜ îĄî×ëAEĄMˇˇZmP˛d'¯]փ–BĻÉNmQŪpīyíĐ!ĀE ā6Ņ“šwĐ´:%÷doĻ}ūÔūÉN„õĐĄ™ĸöû¯á‚'­y¯Oëė,līėU=(YOšāôĐ%pÔyŨŠC,Ā ĩ]Ëjˇ‚7í§kØYāB\Dā¸ėMĀäMô+īœŧzļråA:kTQ…ލĀ!„8 €CĄ¸DĻ1oōxɛ-p͏6;Y‡RZ!Āa !Āš“įēîp!Āa 1ĀaB€Ã8Āp!Āa‡p!€ā‡pĄápǚ>ņ– Š‚MŽeĸxŨąû}†ī>‡p!n¸ę0ˇS_žiÜéÆL>……Į~īŠ/<Į7!Ā…Øų1xķFp+B!ĀECO Ūŧ{¸!„āĸĄböiĖ~Ā­@!„~ŲđŠ5¨!„āB.…O˙gđæé/aT„Bp!×\ö+ī›Â¨Ë-A!„^yç„QB!Ā…\#oܚÁ›ˇ`pųJn B!„¸hÜŧë¸!„ā8„B!€C!„‡8„BpB!„Ā!„B€C—ÜŧĘAˆÃ0 Ã0 +„up™ktĖ~~Y⤞†a†aų´˙ú`Ėō˛‹Čž™IENDŽB`‚trillian-1.6.1/docs/claimantmodel/experimental/000077500000000000000000000000001466362047600216115ustar00rootroot00000000000000trillian-1.6.1/docs/claimantmodel/experimental/cmd/000077500000000000000000000000001466362047600223545ustar00rootroot00000000000000trillian-1.6.1/docs/claimantmodel/experimental/cmd/render/000077500000000000000000000000001466362047600236335ustar00rootroot00000000000000trillian-1.6.1/docs/claimantmodel/experimental/cmd/render/README.md000066400000000000000000000056101466362047600251140ustar00rootroot00000000000000# Claimant Model Render Tool :warning: This is a prototype and has no guarantees of stability! This tooling takes a definition of a Claimant Model in yaml format, and renders it in ways that are useful for evaluating the design. Once the authors are happy with the design, these rendering artefacts can be used as documentation for the project. The markdown snippets that are generated will look at their best when rendered in GitHub. ## Prerequisites & Assumptions This tooling assumes that you are using the Claimant Model as a way of describing participants in an ecosystem around logs. Users are required to have an understanding of the roles in the [Claimant Model](../../../CoreModel.md) before starting to populate a custom yaml file for input into this tool. ## Usage Start by copying the [example: CT Claimant Model](./internal/models/ct/model.yaml) into your working directory. This model is the simplest form as it has a single claim. If you know that your model has multiple claims with different verifiers, then starting with [example: Armory Drive Claimant Model](./internal/models/armorydrive/model.yaml) is a better call. The first step is to turn this domain-level Claimant Model into a "full" model, i.e. one that also includes the log and its verifiers. ```bash go run ./docs/claimantmodel/experimental/cmd/render --domain_model_file /tmp/cmfun/model.yaml ``` This will output some snippets to stdout. If you'd written the input file yourself, this would be a good opportunity to check that the output looked reasonable and to iterate on the input yaml file as needed. Part of the output will be a complete model. Copy this and save it into another file in your working directory called `full.yaml`. If you reviewed the output you'll have seen that there are a few `TODO` entries with some guesses in this output, specifically when defining the actors for the Log Claimant and Arbiter. The Claimant and Arbiter from the base model are suggested as defaults, but it could be that a new actor (that doesn't participate in the base model) is introduced to perform either or both of these roles. Edit the file to resolve the `TODO` entries, and then run the tooling again against this file: ```bash go run ./docs/claimantmodel/experimental/cmd/render --full_model_file /tmp/cmfun/full.yaml ``` The rendered output will include markdown snippets for the full claimant model, and a mermaid sequence diagram. ## Known Issues A non-exhaustive list of issues or limiting assumptions: - the sequence diagram assumes that an offline proof bundle will be generated by the Claimant and distributed to the Believer; this is a best practice, but somewhat rare. Supporting other flows should be possible. - the sequence diagram doesn't show anyone relying on the output of the witness checks; this should be included because that's the whole point of the witness countersigning checkpoints - the tool doesn't support SCTs/promises trillian-1.6.1/docs/claimantmodel/experimental/cmd/render/internal/000077500000000000000000000000001466362047600254475ustar00rootroot00000000000000trillian-1.6.1/docs/claimantmodel/experimental/cmd/render/internal/model.go000066400000000000000000000204051466362047600270770ustar00rootroot00000000000000// Copyright 2023 Trillian Authors. 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 claimant is a code model for the Claimant Model. package claimant import ( "bytes" _ "embed" "fmt" "regexp" "sort" "text/template" ) var ( // TemplateModelMarkdown is the markdown template for a model. //go:embed tmpl_model.md TemplateModelMarkdown []byte // TemplateQuestionsMarkdown is the markdown template for a model questionnaire. //go:embed tmpl_questions.md TemplateQuestionsMarkdown []byte // TemplateSequenceMarkdown is the markdown template for the sequence diagram. //go:embed tmpl_sequence.md TemplateSequenceMarkdown []byte ) // Claim represents a falsifiable statement along with an actor that can verify it. type Claim struct { // Claim is a falsifiable statement, e.g. "The number 7 is prime" Claim string `yaml:"Claim"` // Verifier is an actor that can verify the claim, e.g. "Primal Calculator" Verifier string `yaml:"Verifier"` } // Model represents a Claimant Model, mapping the roles to actors. // See https://github.com/google/trillian/blob/master/docs/claimantmodel/CoreModel.md for // descriptions of these roles. Repeating the definitions here will only lead to stale // documentation. type Model struct { // System is a short upper case name that models the essence of this model, e.g. "FIRMWARE". System string `yaml:"System"` // Claimant is the actor playing the role of the Claimant. Claimant string `yaml:"Claimant"` // Statement is the concrete type that the Claimant issues, and is likely the thing that is logged. Statement string `yaml:"Statement"` // Believer is the actor playing the role of the Believer. Believer string `yaml:"Believer,omitempty"` // Believers are the actor playing the roles of the Believer. // This should only be provided if there are multiple Believers, and if // provided then Believer should be left empty. Believers []string `yaml:"Believers,omitempty"` // Claim is the claim made by the Claimant. Claim Claim `yaml:"Claim,omitempty"` // Claims are the claims made by the Claimant. // This should only be provided if there are multiple Claims, and if // provided then Claim should be left empty. Claims []Claim `yaml:"Claims,omitempty"` // Arbiter is the actor or process that fulfills the Arbiter role. Arbiter string `yaml:"Arbiter"` } // Markdown returns this Claimant Model in a definition table that renders // clearly in markdown format. func (m Model) Markdown() string { t, err := template.New("model").Parse(string(TemplateModelMarkdown)) if err != nil { panic(err) } w := bytes.NewBuffer([]byte{}) err = t.Execute(w, m) if err != nil { panic(err) } return w.String() } // Questionnaire returns some questions to guide the designer to ensure that // the claimant model is sound. func (m Model) Questionnaire() string { t, err := template.New("questions").Parse(string(TemplateQuestionsMarkdown)) if err != nil { panic(err) } w := bytes.NewBuffer([]byte{}) err = t.Execute(w, m) if err != nil { panic(err) } return w.String() } // ClaimTerms finds all of the terms used in the Claim that must be // present in the Statement. func (m Model) ClaimTerms() []string { re := regexp.MustCompile(`\$[\w\@]*`) return re.FindAllString(m.ClaimMarkdown(), -1) } // ClaimMarkdown renders the Claim(s) in markdown. func (m Model) ClaimMarkdown() string { if len(m.Claims) == 0 { return m.Claim.Claim } r := "
    " for _, c := range m.Claims { r += fmt.Sprintf("
  1. %s
  2. ", c.Claim) } r += "
" return r } // VerifierList returns all of the verifiers mapped to the claim they verify. func (m Model) VerifierList() map[string]string { if len(m.Claim.Verifier) > 0 { return map[string]string{m.Claim.Verifier: m.Claim.Claim} } r := make(map[string]string) for _, c := range m.Claims { r[c.Verifier] = c.Claim } return r } // VerifierMarkdown renders the Verifier(s) in markdown. func (m Model) VerifierMarkdown() string { if len(m.Claims) == 0 { return fmt.Sprintf("%s: %s", m.Claim.Verifier, m.Claim.Claim) } r := "
    " for _, c := range m.Claims { r += fmt.Sprintf("
  • %s: %s
  • ", c.Verifier, c.Claim) } r += "
" return r } // BelieverMarkdown renders the Believer(s) in markdown. func (m Model) BelieverMarkdown() string { if len(m.Believer) > 0 { return m.Believer } r := "
    " for _, b := range m.Believers { r += fmt.Sprintf("
  • %s
  • ", b) } r += "
" return r } // LogModelForDomain proposes a template Claimant Model for human // editing based on a domain model provided. func LogModelForDomain(m Model) Model { verifiersString := m.Claim.Verifier if len(verifiersString) == 0 { verifiersString += "{" for _, c := range m.Claims { verifiersString += c.Verifier + "/" } verifiersString = verifiersString[:len(verifiersString)-1] verifiersString += "}" } believers := m.Believers if len(believers) == 0 { believers = append(believers, m.Believer) } if v := m.Claim.Verifier; len(v) > 0 { believers = append(believers, v) } else { for _, c := range m.Claims { believers = append(believers, c.Verifier) } } return Model{ System: fmt.Sprintf("LOG_%s", m.System), Claimant: fmt.Sprintf("TODO: %s/$LogOperator", m.Claimant), Claims: []Claim{ { Claim: "This data structure is append-only from any previous version", Verifier: "Witness", }, { Claim: "This data structure is globally consistent", Verifier: "Witness Quorum", }, { Claim: fmt.Sprintf("This data structure contains only leaves of type `%s`", m.Statement), Verifier: verifiersString, }, }, Statement: "Log Checkpoint", Believers: believers, Arbiter: fmt.Sprintf("TODO: %s/$LogArbiter", m.Arbiter), } } // Models captures the domain model along with the log model that supports it. // This can be extended for more general model composition in the future, but // this is the most common composition and motiviation for Claimant Modelling. type Models struct { Domain Model `yaml:"Domain"` Log Model `yaml:"Log"` } // Actors returns all of the actors that participate in the ecosystem of logging // the domain claims and verifying all behaviours. func (ms Models) Actors() []string { am := make(map[string]bool) for _, model := range []Model{ms.Domain, ms.Log} { am[model.Claimant] = true for v := range model.VerifierList() { am[v] = true } if len(model.Believer) > 0 { am[model.Believer] = true } else { for _, b := range model.Believers { am[b] = true } } for v := range model.VerifierList() { am[v] = true } } r := make([]string, 0, len(am)) for actor := range am { if len(actor) > 0 { r = append(r, actor) } } // TODO(mhutchinson): put these in a more useful order than alphabetical sort.Strings(r) return r } // Markdown returns the markdown representation of both models. func (ms Models) Markdown() string { return fmt.Sprintf("%s\n%s", ms.Domain.Markdown(), ms.Log.Markdown()) } // SequenceDiagram returns a mermaid markdown snippet that shows the // idealized workflow for this log ecosystem. This can be changed in the // future to support other variations in the workflow, e.g. this generates // a sequence that shows the claimant awaiting an inclusion proof and then // creating an offline bundle, but not all ecosystems do this and so perhaps // this should take some kind of Options that allows these cases to vary. // For now, this is out of scope and this generated sequence diagram should // be taken to represent the current best practice, and designers can modify // it to reflect the deltas in their world. func (ms Models) SequenceDiagram() string { t, err := template.New("seq").Parse(string(TemplateSequenceMarkdown)) if err != nil { panic(err) } w := bytes.NewBuffer([]byte{}) err = t.Execute(w, ms) if err != nil { panic(err) } return w.String() } trillian-1.6.1/docs/claimantmodel/experimental/cmd/render/internal/model_test.go000066400000000000000000000044631466362047600301440ustar00rootroot00000000000000// Copyright 2023 Trillian Authors. 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 claimant_test import ( "fmt" "os" "strings" "testing" "github.com/google/go-cmp/cmp" claimant "github.com/google/trillian/docs/claimantmodel/experimental/cmd/render/internal" "gopkg.in/yaml.v2" ) func TestModelAndQuestionnaire(t *testing.T) { testCases := []struct { system string terms []string }{ { system: "ct", terms: []string{"$pubKey", "$domain"}, }, { system: "armorydrive", terms: []string{"$artifactHash", "$platform", "$revision", "$git@tag", "$tamago@tag", "$usbarmory@tag"}, }, } for _, tC := range testCases { t.Run(tC.system, func(t *testing.T) { saved, err := os.ReadFile(fmt.Sprintf("models/%s/model.yaml", tC.system)) if err != nil { t.Fatalf("failed to read model: %v", err) } model := claimant.Model{} if err := yaml.Unmarshal(saved, &model); err != nil { t.Fatalf("failed to unmarshal model: %v", err) } if got, want := model.ClaimTerms(), tC.terms; !cmp.Equal(got, want) { t.Errorf("got != want; %v != %v", got, want) } saved, err = os.ReadFile(fmt.Sprintf("models/%s/model.md", tC.system)) if err != nil { t.Fatalf("failed to read file: %v", err) } if diff := cmp.Diff(model.Markdown(), string(saved)); len(diff) != 0 { t.Errorf("unexpected diff: %v", diff) } saved, err = os.ReadFile(fmt.Sprintf("models/%s/questions.md", tC.system)) if err != nil { t.Fatalf("failed to read file: %v", err) } // We allow the questions to be completed, but for the sake of diffing // we uncheck all of the boxes. unchecked := strings.ReplaceAll(string(saved), "[x]", "[ ]") if diff := cmp.Diff(model.Questionnaire(), unchecked); len(diff) != 0 { t.Errorf("unexpected diff: %v", diff) } }) } } trillian-1.6.1/docs/claimantmodel/experimental/cmd/render/internal/models/000077500000000000000000000000001466362047600267325ustar00rootroot00000000000000trillian-1.6.1/docs/claimantmodel/experimental/cmd/render/internal/models/armorydrive/000077500000000000000000000000001466362047600312755ustar00rootroot00000000000000trillian-1.6.1/docs/claimantmodel/experimental/cmd/render/internal/models/armorydrive/full.md000066400000000000000000000037671466362047600325760ustar00rootroot00000000000000
ClaimFIRMWARE
  1. The firmware with $artifactHash is unique for {$platform, $revision} tuple
  2. The firmware was built from $git@tag using $tamago@tag and $usbarmory@tag and REV=... (with -trimpath)
  3. The firmware is functionally correct and without known attack vectors
  4. The firmware was knowingly issued by WithSecure
StatementFIRMWARE
Firmware Manifest
ClaimantFIRMWARE
WithSecure
BelieverFIRMWARE
Firmware Update Client
VerifierFIRMWARE
  • Third Party: The firmware with $artifactHash is unique for {$platform, $revision} tuple
  • Third Party: The firmware was built from $git@tag using $tamago@tag and $usbarmory@tag and REV=... (with -trimpath)
  • Malware Scanner: The firmware is functionally correct and without known attack vectors
  • WithSecure: The firmware was knowingly issued by WithSecure
ArbiterFIRMWARE
Ecosystem
ClaimLOG_FIRMWARE
  1. This data structure is append-only from any previous version
  2. This data structure is globally consistent
  3. This data structure contains only leaves of type `Firmware Manifest`
StatementLOG_FIRMWARE
Log Checkpoint
ClaimantLOG_FIRMWARE
WithSecure
BelieverLOG_FIRMWARE
  • Firmware Update Client
  • Third Party
  • Malware Scanner
  • WithSecure
VerifierLOG_FIRMWARE
  • Witness: This data structure is append-only from any previous version
  • Witness Quorum: This data structure is globally consistent
  • Third Party: This data structure contains only leaves of type `Firmware Manifest`
ArbiterLOG_FIRMWARE
Ecosystem
trillian-1.6.1/docs/claimantmodel/experimental/cmd/render/internal/models/armorydrive/full.yaml000066400000000000000000000021331466362047600331220ustar00rootroot00000000000000Domain: System: FIRMWARE Claimant: WithSecure Statement: Firmware Manifest Believer: Firmware Update Client Claims: - Claim: The firmware with $artifactHash is unique for {$platform, $revision} tuple Verifier: Third Party - Claim: The firmware was built from $git@tag using $tamago@tag and $usbarmory@tag and REV=... (with -trimpath) Verifier: Third Party Builder - Claim: The firmware is functionally correct and without known attack vectors Verifier: Malware Scanner - Claim: The firmware was knowingly issued by WithSecure Verifier: WithSecure Arbiter: Ecosystem Log: System: LOG_FIRMWARE Claimant: WithSecure Statement: Log Checkpoint Believers: - Firmware Update Client - Third Party - Third Party Builder - Malware Scanner - WithSecure Claims: - Claim: This data structure is append-only from any previous version Verifier: Witness - Claim: This data structure is globally consistent Verifier: Witness Quorum - Claim: This data structure contains only leaves of type `Firmware Manifest` Verifier: Third Party Arbiter: Ecosystemtrillian-1.6.1/docs/claimantmodel/experimental/cmd/render/internal/models/armorydrive/logsequence.md000066400000000000000000000031531466362047600341330ustar00rootroot00000000000000 ```mermaid sequenceDiagram actor WithSecure actor Firmware Update Client actor Malware Scanner actor Third Party actor Third Party Builder actor Witness WithSecure->>WithSecure: Add new Firmware Manifest WithSecure->>WithSecure: Integrate Firmware Manifests and issue Log Checkpoint WithSecure->>WithSecure: Log Checkpoint and inclusion proof WithSecure->>Firmware Update Client: Firmware Manifest with proof bundle Firmware Update Client->>Firmware Update Client: Verify bundle and install firmware loop Periodic append-only Verification Witness->>WithSecure: Fetch merkle data Witness->>Witness: Verify append-only end loop Periodic Firmware Manifest Verification Malware Scanner->>WithSecure: Get all entries Malware Scanner->>Malware Scanner: Verify: The firmware is functionally correct and without known attack vectors Third Party->>WithSecure: Get all entries Third Party->>Third Party: Verify: The firmware with $artifactHash is unique for {$platform, $revision} tuple Third Party Builder->>WithSecure: Get all entries Third Party Builder->>Third Party Builder: Verify: The firmware was built from $git@tag using $tamago@tag and $usbarmory@tag and REV=... (with -trimpath) WithSecure->>WithSecure: Get all entries WithSecure->>WithSecure: Verify: The firmware was knowingly issued by WithSecure end ``` trillian-1.6.1/docs/claimantmodel/experimental/cmd/render/internal/models/armorydrive/model.md000066400000000000000000000021371466362047600327220ustar00rootroot00000000000000
ClaimFIRMWARE
  1. The firmware with $artifactHash is unique for {$platform, $revision} tuple
  2. The firmware was built from $git@tag using $tamago@tag and $usbarmory@tag and REV=... (with -trimpath)
  3. The firmware is functionally correct and without known attack vectors
  4. The firmware was knowingly issued by WithSecure
StatementFIRMWARE
Firmware Manifest
ClaimantFIRMWARE
WithSecure
BelieverFIRMWARE
Firmware Update Client
VerifierFIRMWARE
  • Third Party: The firmware with $artifactHash is unique for {$platform, $revision} tuple
  • Third Party Builder: The firmware was built from $git@tag using $tamago@tag and $usbarmory@tag and REV=... (with -trimpath)
  • Malware Scanner: The firmware is functionally correct and without known attack vectors
  • WithSecure: The firmware was knowingly issued by WithSecure
ArbiterFIRMWARE
Ecosystem
trillian-1.6.1/docs/claimantmodel/experimental/cmd/render/internal/models/armorydrive/model.yaml000066400000000000000000000011461466362047600332630ustar00rootroot00000000000000System: "FIRMWARE" Claims: - Claim: "The firmware with $artifactHash is unique for {$platform, $revision} tuple" Verifier: "Third Party" - Claim: "The firmware was built from $git@tag using $tamago@tag and $usbarmory@tag and REV=... (with -trimpath)" Verifier: "Third Party Builder" - Claim: "The firmware is functionally correct and without known attack vectors" Verifier: "Malware Scanner" - Claim: "The firmware was knowingly issued by WithSecure" Verifier: "WithSecure" Statement: "Firmware Manifest" Claimant: "WithSecure" Believer: "Firmware Update Client" Arbiter: "Ecosystem"trillian-1.6.1/docs/claimantmodel/experimental/cmd/render/internal/models/armorydrive/questions.md000066400000000000000000000003241466362047600336500ustar00rootroot00000000000000- Firmware Manifest contains all of the terms: - [x] $artifactHash - [x] $platform - [x] $revision - [x] $git@tag - [x] $tamago@tag - [x] $usbarmory@tag - [x] Firmware Manifest is signed by WithSecuretrillian-1.6.1/docs/claimantmodel/experimental/cmd/render/internal/models/ct/000077500000000000000000000000001466362047600273405ustar00rootroot00000000000000trillian-1.6.1/docs/claimantmodel/experimental/cmd/render/internal/models/ct/full.md000066400000000000000000000024341466362047600306270ustar00rootroot00000000000000
ClaimCERT
I am authorized to certify $pubKey for $domain
StatementCERT
X.509 Certificate
ClaimantCERT
Certificate Authority
BelieverCERT
Web Browser
VerifierCERT
Domain Owner: I am authorized to certify $pubKey for $domain
ArbiterCERT
Browser Vendor
ClaimLOG_CERT
  1. This data structure is append-only from any previous version
  2. This data structure is globally consistent
  3. This data structure contains only leaves of type `X.509 Certificate`
StatementLOG_CERT
Log Checkpoint
ClaimantLOG_CERT
Log Operator
BelieverLOG_CERT
  • Web Browser
  • Domain Owner
VerifierLOG_CERT
  • Witness: This data structure is append-only from any previous version
  • Witness Quorum: This data structure is globally consistent
  • Domain Owner: This data structure contains only leaves of type `X.509 Certificate`
ArbiterLOG_CERT
Browser Vendor
trillian-1.6.1/docs/claimantmodel/experimental/cmd/render/internal/models/ct/full.yaml000066400000000000000000000012421466362047600311650ustar00rootroot00000000000000Domain: System: CERT Claimant: Certificate Authority Statement: X.509 Certificate Believer: Web Browser Claim: Claim: I am authorized to certify $pubKey for $domain Verifier: Domain Owner Arbiter: Browser Vendor Log: System: LOG_CERT Claimant: Log Operator Statement: Log Checkpoint Believers: - Web Browser - Domain Owner Claims: - Claim: This data structure is append-only from any previous version Verifier: Witness - Claim: This data structure is globally consistent Verifier: Witness Quorum - Claim: This data structure contains only leaves of type `X.509 Certificate` Verifier: Domain Owner Arbiter: Browser Vendortrillian-1.6.1/docs/claimantmodel/experimental/cmd/render/internal/models/ct/model.md000066400000000000000000000007001466362047600307570ustar00rootroot00000000000000
ClaimCERT
I am authorized to certify $pubKey for $domain
StatementCERT
X.509 Certificate
ClaimantCERT
Certificate Authority
BelieverCERT
Web Browser
VerifierCERT
Domain Owner: I am authorized to certify $pubKey for $domain
ArbiterCERT
Browser Vendor
trillian-1.6.1/docs/claimantmodel/experimental/cmd/render/internal/models/ct/model.yaml000066400000000000000000000003451466362047600313260ustar00rootroot00000000000000System: "CERT" Claim: Claim: "I am authorized to certify $pubKey for $domain" Verifier: "Domain Owner" Statement: "X.509 Certificate" Claimant: "Certificate Authority" Believer: "Web Browser" Arbiter: "Browser Vendor"trillian-1.6.1/docs/claimantmodel/experimental/cmd/render/internal/models/ct/questions.md000066400000000000000000000002111466362047600317060ustar00rootroot00000000000000- X.509 Certificate contains all of the terms: - [x] $pubKey - [x] $domain - [x] X.509 Certificate is signed by Certificate Authoritytrillian-1.6.1/docs/claimantmodel/experimental/cmd/render/internal/tmpl_model.md000066400000000000000000000006351466362047600301310ustar00rootroot00000000000000
Claim{{.System}}
{{.ClaimMarkdown}}
Statement{{.System}}
{{.Statement}}
Claimant{{.System}}
{{.Claimant}}
Believer{{.System}}
{{.BelieverMarkdown}}
Verifier{{.System}}
{{.VerifierMarkdown}}
Arbiter{{.System}}
{{.Arbiter}}
trillian-1.6.1/docs/claimantmodel/experimental/cmd/render/internal/tmpl_questions.md000066400000000000000000000002051466362047600310540ustar00rootroot00000000000000- {{.Statement}} contains all of the terms:{{range .ClaimTerms}} - [ ] {{.}}{{end}} - [ ] {{.Statement}} is signed by {{.Claimant}}trillian-1.6.1/docs/claimantmodel/experimental/cmd/render/internal/tmpl_sequence.md000066400000000000000000000016641466362047600306440ustar00rootroot00000000000000{{ $logClaimant := .Log.Claimant -}} ```mermaid sequenceDiagram {{- range .Actors}} actor {{.}} {{- end}} {{.Domain.Claimant}}->>{{.Log.Claimant}}: Add new {{.Domain.Statement}} {{.Log.Claimant}}->>{{.Log.Claimant}}: Integrate {{.Domain.Statement}}s and issue {{.Log.Statement}} {{.Log.Claimant}}->>{{.Domain.Claimant}}: {{.Log.Statement}} and inclusion proof {{.Domain.Claimant}}->>{{.Domain.Believer}}: {{.Domain.Statement}} with proof bundle {{.Domain.Believer}}->>{{.Domain.Believer}}: Verify bundle and TODO(TRUSTED ACTION) loop Periodic append-only Verification Witness->>{{$logClaimant}}: Fetch merkle data Witness->>Witness: Verify append-only end loop Periodic {{.Domain.Statement}} Verification {{- range $verifier, $claim := .Domain.VerifierList}} {{$verifier}}->>{{$logClaimant}}: Get all entries {{$verifier}}->>{{$verifier}}: Verify: {{$claim}} {{- end}} end ```trillian-1.6.1/docs/claimantmodel/experimental/cmd/render/main.go000066400000000000000000000055151466362047600251140ustar00rootroot00000000000000// Copyright 2023 Trillian Authors. 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. // render is a command to take a Claimant Model specified as yaml and // output markdown representations of it. package main import ( "flag" "fmt" "os" "strings" claimant "github.com/google/trillian/docs/claimantmodel/experimental/cmd/render/internal" "gopkg.in/yaml.v2" "k8s.io/klog/v2" ) var ( dmf = flag.String("domain_model_file", "", "path to domain model yaml file") fmf = flag.String("full_model_file", "", "path to full model yaml file") ) func main() { flag.Parse() if (len(*dmf) == 0) == (len(*fmf) == 0) { klog.Exitf("--domain_model_file OR --full_model_file are required") } if len(*dmf) > 0 { saved, err := os.ReadFile(*dmf) if err != nil { klog.Exitf("failed to read model: %v", err) } domain := claimant.Model{} if err := yaml.Unmarshal(saved, &domain); err != nil { klog.Exitf("failed to parse Model: %v", err) } handleSingleModel(domain) } else { saved, err := os.ReadFile(*fmf) if err != nil { klog.Exitf("failed to read model: %v", err) } models := claimant.Models{} if err := yaml.Unmarshal(saved, &models); err != nil { klog.Exitf("failed to parse Models: %v", err) } handleMultiModels(models) } } func handleSingleModel(domain claimant.Model) { fmt.Printf("Domain Model as markdown:\n%s\n\n", domain.Markdown()) models := claimant.Models{ Domain: domain, Log: claimant.LogModelForDomain(domain), } mbs, err := yaml.Marshal(models) if err != nil { klog.Exitf("failed to marshal models: %v", err) } fmt.Printf("Complete model template as yaml:\n%s\n\n", string(mbs)) } func handleMultiModels(models claimant.Models) { generateCommand := getGenerateDocs() fmt.Printf("All actors:\n%s\n\n", strings.Join(models.Actors(), "\n")) fmt.Printf("Models as markdown:\n%s\n%s\n\n", generateCommand, models.Markdown()) fmt.Printf("Sequence diagrams:\n%s\n%s\n\n", generateCommand, models.SequenceDiagram()) } func getGenerateDocs() string { builder := strings.Builder{} builder.WriteString("") return builder.String() } trillian-1.6.1/docs/howto/000077500000000000000000000000001466362047600154435ustar00rootroot00000000000000trillian-1.6.1/docs/howto/freeze_a_ct_log.md000066400000000000000000000127121466362047600210770ustar00rootroot00000000000000# How To Freeze a Log (CT Example) ## Prerequisites Some of the tools and metrics that will be used were added in the `v1.0.8` release. Ensure that your have upgraded to this release or later. If using MySQL storage ensure that the database schema is updated to at least that of `v1.0.8`. The `log_signer` process(es) must be exporting metrics so the queue state and sequencing can be monitored to [check](#monitor-queue--integration) that all pending entries have been integrated. Check that their `--http_endpoint` flag is set to an appropriate value. If it's empty then update the configuration appropriately and restart them before proceeding. We will assume that the log tree to be frozen is one that's being used by Trillian CTFE to serve a Certificate Transparency log. If this is not the case then consult the documentation for the appropriate application. ## Preparation ### Find the Log ID Obtain the ID of the tree that is backing the log that is to be frozen. This can be found in the CTFE config file. Locate the section of the config file that matches the log to be frozen and pull out the value of `log_id`. For example with the following config the `log_id` to use would be `987654321`. ``` config { log_id: 987654321 prefix: "the_name_of_the_log" roots_pem_file: "... roots file name ...." public_key: { der: ".... bytes of the public key" } private_key: { [type.googleapis.com/keyspb.PrivateKey] { der: ".... bytes of the private key ...." } } } ``` ### Setup Environment Set environment variables to the correct log_id and metrics HTTP endpoint. For example: ``` LOG_ID=987654321 LOG_SERVER_RPC=localhost:8090 METRICS_URI=http://signer-1:8091/metrics ``` ## Set Log Tree To Draining State Use `updatetree` to set the log tree to a `DRAINING` state. `go run github.com/google/trillian/cmd/updatetree@latest --admin_server=${LOG_SERVER_RPC} --tree_id=${LOG_ID} --tree_state=DRAINING` Make sure the above command succeeds. At this point the log will not accept new entries but there may be some that have already been submitted but not yet integrated. ## Monitor Queue / Integration If you have monitoring dashboards showing signer mastership e.g. in Prometheus then this information might be easily available and you may already have a global view of the state of all the Trillian servers in the etcd cluster. For the rest of the document we will assume that this is not the case. The necessary information can be obtained from the raw metrics that the server exports. Note that it is possible for elections / resignations or cluster operations to change the signer responsible for a tree during the following process. So if you are using metrics directly from servers be aware that this could happen while you're watching the queue. Wait until you're sure that the log has finished integrating the queued leaves. This will be indicated by an incrementing count of signer runs for the tree, no increase in errors for the tree and zero leaves being processed for the tree by the signer. The following example should make this clear. ### Find The Signer Monitor the statistics available on ${METRICS_URI}. For example: `curl ${METRICS_URI} | grep ${LOG_ID} | grep -v delay | grep -v latency | grep -v quota` This might produce output similar to this: ``` entries_added{logid="987654321"} 54 is_master{logid="987654321"} 1 known_logs{logid="987654321"} 1 master_resignations{logid="987654321"} 7 sequencer_batches{logid="987654321"} 54 sequencer_sequenced{logid="987654321"} 54 sequencer_tree_size{logid="987654321"} 7.095373e+07 signing_runs{logid="987654321"} 54 ``` First check that `is_master` is not zero. If it is then one of the other signers is currently handling the tree. Try the command on `signer-2` or whatever the next cluster member is called until you find the right one. ### Wait For The Queue To Drain Next check that there is no entry present for `failed_signing_runs` for the tree. If there is do not proceed until you understand the cause and confirm that it has been fixed. If signing is failing and this number is incrementing then the other metrics will not be reliable. Then check that `signing_runs` is incrementing for the log along with `sequencer_batches` and then that `entries_added` and `sequencer_sequenced` remain static. These are counting the number of leaves integrated into the log by each signing run. While these values are increasing the queue is being drained. Continue to monitor the output from accessing `${METRICS_URI}` until you are sure that the queue has been drained for the log. Remember to ensure that `is_master` remains non zero during this time. If not you may have to go back and find the currently active signer. For additional safety keep watching the metrics for a further number of signer runs until you are are that there is no further sequencing activity for the log. Because some of the available storage options use queue sharding (e.g. CloudSpanner) it is not sufficient to rely on no activity in a single signer run. ## Set Log Tree To Frozen State **Warning**: Be sure to have completed the queue monitoring process set out in the previous section. If there are still queued leaves that have not been integrated then setting the tree to frozen will put the log on a path to exceeding its MMD. Use `updatetree` to set the log tree to a `FROZEN` state. `go run github.com/google/trillian/cmd/updatetree@latest --admin_server=${LOG_SERVER_RPC} --tree_id=${LOG_ID} --tree_state=FROZEN` Make sure the above command succeeds. The log is now frozen. trillian-1.6.1/docs/images/000077500000000000000000000000001466362047600155505ustar00rootroot00000000000000trillian-1.6.1/docs/images/LogDesign.png000066400000000000000000001007231466362047600201340ustar00rootroot00000000000000‰PNG  IHDRL]#ny€IDATxÚėŨ|ÕŊ˙˙“dwŗų˙‡!$(JTP”(V°ĐFj¸ bĨ žĸ‹)*ôĸB/øƒĒ-ŪâŊx̎Ú̎é•ļ´bÅ5miŠ5@  Ėo>›3f7›d“ĖĮy@’ŨÍf÷ĖėyĪ9ķĨ!]oĨz+Ķ[/€X :ķôļ#))鋄„„ÆÔÔÔrss_Đŋˇ@oy!îëŅ[ĨĮãų]bbâņŦŦŦŋįååíÕį}ũûõļVo…ŧÄj”Ūn×ÛķzĢÖ[ŅvémŪϝ%ãķųū~ÁŧųĖ3ĪÔ8p@ĢŠŠŅöī߯mÛļMûĘWžr$99ų¨Ū6čˇÍœœœ7G}hį͍õõõš•ūôûīŌØĮF ‹× Te„# F“õVb„ČbŊMĐÛ ŊíÖ[­Ūœ€Čʗ@âņxūSouú˙5™%JOO˙ĢŪž¯‚Īđ”§ĨĨ}ūŸ˙ųŸĮĩ$DMŸ>ŊNlЏ‘;6l^nnîņĮ\kÍk¯ŊöiFFÆÕ4ÃOn7BĐŧ0C„ĢgđÄŦĐA~Ŋ-KII94mÚ´ˇņ‹_?tčP ¤ÔÖÖj/ŧđ‚6wîÜÆĖĖĖã#FŒø‰q{Ķŋß_ŋoß>-\?ũéOɒŊÁƒočͧOãŪŊ{Þ¯<Ÿ=z| âgĻiģ|ōÛqßez;@h vlŗõļCoՉ‰‰o{&TÄWxšYqųņėĖô÷eŖ#IhšŪV'''?“đkÕT(@–uÉl…0gŌu˙XŗfÍąöũ~ZJJŠÖÚŦT°ŖéAKëY2¨˙ūc*vg™öč­"B5Ę_Ė2eĘĶRS>ŦũÛ/ņŲ6­˙Ž–‘žvˆĐÔacúôéķĸˆ /ŧđķ{îš§áÉ'ŸÔĒĒĒ´_ũęW[ļlŠŠŦŦ|977÷ ÕTĸ{EFFÆæI“&Õˇ7°Ė˜1CģûîģÛußĨK—ļ+hŲÉyUYYYˇÄāû)Ávo„S–åMfS zd'%%~ôĮ]!8ÄwÛ¸îļiŠo*Ž~ˇGĄîˇũû÷?ļ~ũúĀRˇÖŧņÆo9r—Īįkxå•WÚV$ôØËwĩ§žzJ+**úc ž¯Rz=Ō•—čm5› ŅcŲĨŸWC` I;oôp)a]Ąž%įjŦ ņsšvM‘åëia>Ž”Á6/ZŪĘī7o×iz÷î]!Uëd–§ĄĄĄÍaãŪ{ī pˆfÔôāטũcũ%CûĮęNø{ĘTĶÅn@4đx<ŋ{ūį7hŌžØz–×#ûWę^rŽÎÖ?¯1bœŪ´6 8a+TąŠė6>n› :tU~~~C[ĘqÛIČ3fŒÎõÜL^įe˖ĐCĶQŖDv ė"ëTä ĸHŋŨΧQ˜’ęãĄ^ũGŋ œ§3}j™6üôÁZéY§ks*¯ĐĒūûG-n÷Ôöûß§Ŧy!æ^§ũ¯îÔ˛ŗ2>p`’ębåFx1åĄ§ÂXWÁF“ãį2P-1~Vd LöYŖbãvrΉß68í”ĀÔ¯_ŋ;|<įIā’™ĻhöíoûË"Rą/55õãũ‹f đ˜…Ē“g=@ų|ۘ?wi×/ˇh…}{Ž€ę§MŊėB­ėÂҚßī |īúyå_Ūvåm×iÆģÕVķÚĪc˛j^ÜŦĪ#˜öMĒÃI‘ƒĮŒŸ­Ķ[Ŋj:Ą~ļņ¯ŧŽUFX’åē7ģûHAŠĘ iŪjpļËøY^gĻ´´´ Ŋ{÷>~āĀ Î6nÜxÂMųQŧ‹ŦWģö’ ‘Õ|ú%r˛3ÅrXĒ~ųI-=-UËīŨC{nį†?“ĸqcGÂĪ}ß_ô1äį°âeY^Īŧœē& 2æ’&syœšTËē$¯ĖlĒTˋ|N1B“ß!0ÉāĶzūĶFÕtžSgĻėĖĖĖĪž{î9RQë%ĪeddŧÅģČũ*ōŗdR!s˜ 0šŖÉl’ ˜íKīŦ3*ŲYrŪN`؁)âiģåkj^vN`Zh{ŧCFč˛Ļ F0Û`ĢN]’×ĢW¯ĩŗfÍj$…GĘĢ{ŊŪųQē‹ÜŦšĒÚERgTŪĻļˇÕŋ„1įœōv/üüamīo~Ē5|úSä“ŊčC[ĶlÛ}ˇŗ& b˛\o‡ĒY7ԁɟ––vt˙ūũ$Ą0íÛˇOĶ_ŗĪUä—ļu9'nŸŠ\Š}™m•ĨŠÅ|ú@`ęööäc?„[ožĶĄĮ!0uI`ã˜VYžÎŗÜ×˜Šæs–d`+ŗZuF`ōûũŗ&NœxœÔæYĻŖ*ü˛ņn#}qA„kĩ ]9˜ēŽmÛ´2vļ}úÔĮĀ.S–ĶIŲúCF˛_TYf6Ĩ ƒĖޚד€•Χ &Wļ‰Ÿ<{ūīQĮŸK)qš¨­\ÄVŽËD`rM`’ŖųŖ‚ü,ߐÎVĄĢ˛Ü.J† B`ŠīĀdíƒRld§jší”ũÄ Ŋ5¨æ 5KĄˆl>e 0šēíūß­šĮ“¤ęwĘ R]í‹Ú•Ķ'´ēlĀ3LíTUUĨqÆcŧÔ°‰@`ŠĘļiũwĄIšĖ8}ûēoh3ޜ¸X­„! MÁŽÁD`‚EßŦŦŦğöUɛu1ē;%0@` ŋIĐ^+ėÛK+ę_đeKOKũr×­7ΉĢĀ´mĶĘĀß]õß? |-˙Ę×ō}ķ6ë×,Ҋõ‹ĻĀKĘt¤šNTSS#ŠžĀLF`Ēyíį§ülßī˙= äįņ4ĶdL‡Tū_ģ˙—_ŪĻėÂҁ`I`ę%ƒ>NŦ!0˜@ˇ&ioYøųĘÛŽ;ågk^Đöūæ§-‚„SkøôđUWûb‹īËũšŋy;ų}­ũ=rų]rB=ų{%:ũn{`rjĻnå×5ÔÕՑl:qI^īŪŊ˜Š•ĀôÂĪüüÛ×}ãËīÉm'^|n‹ękcÎ9ķËs~Ė&ß_zĶ5ÚđĶūī÷ûAF‚Šų=ŗ•žuēļį˙mqųZžoŊ•ũ¯îlņ\äûŪw‹våô _ŪÎãI ümŽŦAI–fge´xĖ’ĶŠZĖ ĩļ$OŲ*ĪI˜œYqyā˙Nį5;ĒSÂU&Õ¯_ŋ}O=õÉĻ‹>Œ1"Vƒ Sä“%ųš‡0—§ÉųNy=˛ߓđōÔöûK÷ō{÷hä~r.Ô¨§BÅõķĘĩú~¸ŋ„ cō{ŸØzOāņäûfĀ‘%š¯ ųš„1ų}r;ëī1“ÜVBÜ3;ÖkĪíÜ ]6ņüĀ÷īûūâ/Ÿ„%3ÄI“™( ZäŦĻĩĀ$_Ëß$ĪCū/ĪÁŧÍŨwÜĐâõ3ŸŸĶ ŠũŌĶ͝ŸŲĄĮ¸÷Ū{ĩ‹.ēč9 0IB‰ úĨI‘pbΚH‘ #ˇ—Ęyō=šõq$8ÉŦŽ„"k`’€c]Š'ÁĮdĖĨ×Ξūå˛ģéSˏgŸĩ’Jurķ÷˜Du6I‚Ü_‚“uĻGf¨‚ŊæōŧpŠ>8-ɓ¯%Zŋ'A)T %0ĩ›_MŸîÚĩ‹„dŗ{÷nmÔ¨QÚĩ×^Ģ544´ë1ƍ'¯ũ 0a!X“`` -2ŗ"!H€ŊÉ2;k`ûKđąū> O2$Ëâ$LČR8kĐ1›Ėú8…iō;˰b& [öÛÉm‚=†4 2“5õ˛ [„šö&3Y—ļöLR.Ĩ¯YÎfŪ^“Ėf™įÉLO°ûšaÅü=Ą–ŧŲÊ3Y‚'ŗN2kfū~ŗ„zG“ų\ßpu‹Ų0ëũL‘åņxÖ 6듐’lęëë3M›6mjĶũnŊõVmüøņŋŠánC`€Āšs˜œBˆĖđXÕŊĩ˜Ėđ"ˇ•ķŠ¤`„2 5ČėŒ,§k-0IČik`2 UČŋrû'ûAāūæšM Læī0(ŸĖzÉķ´W$0E܆üüüãëׯ×N-UWWkyyyZ¸ŗprģÜÜܧŸ~ú&@`jG`’ķ€$˜į4ŲgŽdV%T`’b 2›e¯&'÷U–Ų ˛ÄĪ)hɌ”, lK`’ Ļ‚,Ũ›q夈&ŗ ģžĨ‹ÁB)b<ŲŲŲo¯Yŗ&pΎœ4ũk­¨¨HÛļm‰IˇxņbméŌĨaŨöúë¯×ƌķlŒ÷ą(%%ų°Ķú5KŠ*¤´w¨Ād–)—âNÅîŊëÆĀל”ąTĐz;ųš˛TŖ 70™o2k 9_Ē=I–õ9eVT­\ËŠ#Mf­L 2Í;vėQë ˙ĐĄC ¯rĻæk*Il­ÄsĪ=§õėŲS.V›M`Q'))ņŸNŗ:]˜d†Gf™ä>r.)=.!Af…ŦŖ‚,Ģ3ĪA’™šŋ”ų6˅›įMɲųŊÖĒzōŧå÷Ȓ>ûī‘@dÎX™áJŠKČ5Ŗäņd6L„y YFgÎFÉ×fu@ķįÖ×_ SH€“@fŸ-3gŅ:ãÚKÖĨC‡Ŋīۅ×ëmd&Šu˗/×VŽ\éø39ī+??ŋaĐ A7ĮpWņčmˇŪęm…ŪžįS€Øŗjūė¯Õu֜šf.[ėŒk/™mĘäq' ûô^įÛDQßž}‡Z÷ÔSOiͧOwŧfS=Žäåå-ņž˛DoU–đ´KoËøX ļäËyL9§ĩŋÉŦ—,û“ øĘė•ũúS‘lR~=7'밊Ũë䄘úõë×HjŨž}û´áǡ(9~ĶM7}šœœü…ū:NŽņ~"Kņdųj‘å{…Æ÷Jųh †xŊ‰72āpg•ŠĻĩŋ™ĨЕq]§}ŋ˙÷Nų=˛0ŋw†’!Eßd‹PEũû÷?AjÁ0`€öâ‹/~ß቉‰ë¯a^Œ÷ëR<ģiĒŠD:›1$;;㑒!Ž0Ķäž&į2IžÎ K{ķS)\Ņ0lč 5l ū¤¤¤“­UƒĻŊõÖ[šßīoĐ_ŗŊz[­ˇâ8é#ÖĨxN6ęm;›ąš22nNMņšaūURˆ 3ĒįŅÜŅd6QŠHT~ã+'ŗŗŌ.ę;‡- Ų AƒÉy8h}†)//ī‹8ëNKņN ŨzÛ§ˇŲlMĞüŦŒ´ÕŊzæŧéņ$5¤§ĨÖËuybŊIhHMõĪÉÎ<ëĢŠ''ûūŲˇ įßúõÍ˙žŠũëã´Ų%—\ō_á^”5žíŲŗG+((x'ŽēF¨ĨxvÍ`UĖb”–s…8" 5bĈĄššš'äbĩîÁÔüLuÖ–âŲ-PMË=lU€Ā„˜rîšįū–YĻĐĻNzÜī÷Ί“.ÎR<';õļŽ- ˜kōz÷î}äņĮ'9Ųˇ´´´Ŗ*ö+â‰ļ,Åŗ“%¯Tė—Y&ěҪGŸ““Së­ˇjTÍkéŽ;î8ž™™ųDœt…ļ.Åŗ§¸ - 0!ååå 4hĐßGŒҏ~ũzí1sūQ{Ék’’"9Ž—‚kUۗâ˜? põ°aÃ^6Ž;¸˜p~~ūÉūũûk[ļl‰ēĀ$Īŋ=dĻíĖ3ĪüÄãņÜF¯ 0NäŧĸŅŖG˙ž˛˛R‹Æjzí L–.Ŋôԃ>Ÿo]€Ā„¤Ļŧ>}úÔ?õÔSQ–<(œmĶ˘\¤vȐ!&$$<Š(“ @`ÂQ^^~…\ŗiīŪŊQ˜6mÚ¤õčŅã•aÆ}0tčГōĩ„¨`į+͛7ī#ŸĪ÷šūį.â 0m Mˇ÷íÛ÷äîŨģ;d¤¨Dmmm§Ļ‘#GJÁ†iōÜĮŒķõŗÎ:ëĨ´´´2¤~úôé‡g˘Q7qâÄ zŊŪĪTĶu„ōy§L&´Ëœ9snîÕĢ׉Ž€ĀT\\ŦUWWwZXĒĒĒŌ233?P§.Ģ“¯Gé­ÂØĘõVĘ; "bÁ‚†zø˛Ë.kWčŠ¯¯×***´‚‚‚N›i*--=â÷ûįōn€Ā„.ˇråJ˙Ė™3ˇËyMͧO×^xá…V/z+į Ũwß}šĖPy景žsÎ9˙#Ą+Ō6lØp"++kīLčîā”]QQņčȑ#eggkS§NÕnŊõÖĀcˇmÛĻŨ{īŊÚõ×_/įÔCĖ?Ī=÷ÜßžkÜŨ3|øđÚŋG,,íŲŗGËČȨĶģ„w&¸Æ%—\Ō÷Ę+¯ŧåōË/bܸqŋ3fĖËeeeĪč˙ŋoذa)‡2ŨúĪŗO;í´O%4ĩ6CÕY˜““#aiīLˆ gœqFūȑ#ߓåyÁĘ~ˇF*÷eeeIIđyŧĸ 0!Öxd6JΉ’ë%…;Û$$/^ü™ßī˙TŒ)ŧŒ 0!fūųc‡ öˇž={6ĘšPģví:%<É×2Ŗtã7~œ’’rØëõūLŋk¯Lˆ šššgčÁéĮī$&&žčÕĢ×á~š——÷…ŽĨĨĨũEŋŲjŊķj€Ā„x&Å"ŠTĶgåßt^˜€Ā& 0 L@`€Ā& 0 L@`˜€Ā& 0L@`˜€Ā& 0 â>0-ĶÛV‡ļYoķôæˇÜļ$Čm7ˇõ8<~‘ŪÖémŪjôļKo‹l Ž LUzĢuAUÆc?ošm™ņŊg-ˇÛގŨÆ÷ĢlĄŠ\ouzÛ¯ˇÕFP’ Ö`§tŪBnLU­<~™-0•9ÜvĄņŗiÆ×ÅzĢ×ÛNuęĖS™šÖōčÎĀ4Y5ÍIpY ˇRÕ4ĶN`n{üP)ÛøŲ ãëF`ĘōØ˛LīvŪBŨ˜?ˏí>Õ´üŽ&ĖĀ´Ú¸˙˜0Ķãg Œ¯eŪŗŧEܘäü z#ôXŋˇß!09Ã´×xėu–Ûši‘ņŗÍ6÷ŪōÛĘí6đpc`šfüŦÄöũU!ĶvKP’bcl÷5“SÛmģŊ|o#o7ĻدΜžßڒŧeĒy&É)0U¨Ļráfs*^­šÎ› Ffĸ¨’ [SšržaZĄÂ;‡IÂNƒjyž’˜ĘÂxn2ģT§‚_oik+?€N LyĒéĻe–īIyīŊa&šŋœ“Tm 5m LŖŒĀå´,oœņŗÍŧ…ē#0‰Û`"Ťz\,ļN…_%Ī\֎ǁI,RÍį7-4oä$ˆeķčŽĀ$*õö´j*â°HZôajY ĪnĢq_™qe„ĢQmxŽr()/~ĐxŽ”¤rį/čļĀ$ŗ7KTs™o“TÂÛÅK ž“œ¯$į Iņ†BÕ4Ŗ#Õíd‰ŪB^:ņ˜„W¨VÍ×JĒ3îq˜L˛,¯H5Í: L& 0 L@`˜€Ā& 0 L˜€Ā& 0 L@`˜€Ā 0@K•F82ÛN#0mļ|o2/€xd¤Pm/€x4.ŒĀ4Š— @ŧÚ",íįåĪ*ËņĀ‘GolAé¤b9,Q,ĮGŲz;ŦXŽŽÖ)Ēã€ŖbŊ5*–ã€Ŗ§ËņĀ‘y![–〃íŧÚ#Ooķ|))OøSS÷$§¤ĐÛÁXjúßöq,ũ=^ĘGŸ¯Öë÷ŋœ˜˜øūūM֛ŸŽũÛĄ×įû‰Ū_˙āKöŋkÛa,6Ų})НxŊŪMl‡Ä˜”ŒŒ¯Ĩeįėķú’—Œwė˛ų ĩËWiķÖ>¨-~dģvËOvŌ\Úm~\[°ūG÷ĢlÆŦcŊ‹}âņzëŧÉ)˙ĒšĘŠ#zLIIOE˙ꇞ;ö ļÃčÜ/žzΉüAC>÷&'IĪÉų7ļCĸ9(Ĩ¤œ—‘ÛŖ:ˇ īņé‹nŅnŪö3?1Đdāvö¤ËéøHJZÚÍôt×+• ”Ũ+˙0Ûalm‡Ŗ/›zÂįO9–›_p'Ũ€(Ķŗ_˙{RŌ3ūyųõ7iK}’N 6™™(<äXjVÖUĶ2/¸í EZÚ-ÉŠŠGŲc{;ė{Zɉė^}ūĘv@tđäö)xļgŋßÚ°…MŒ7„™~Õ _jÚ'ú{_B÷wĪv˜’™ųD^ßÂzļÃøØĮNŋJKÍĘ:ÜĢW¯‘t\ŦGßÂ_ }îIΉˆ¯&3zhú\5]ĀŨ,5#ë?,­g;Œŋí053ķXzzúl¸Pī7ô.t‚AZ|ļIs¯×üiéĩzWHgkč>>ŋ˙Žžũ–â84ĨdfÕ)–įā.š} /KĪÎa^œˇQ—LnLĪĘáZTŨg\JF&ËđâŧÉōŧŦžŊß`sĀE2ōz~$¸ŦÄw“Y zúqŊKŒbĢčzÉ)Šg;¤É9M…%§7dôčq[.Đģ¨hqŸÁCN0PĄI›pÍ<-ģWū˙ąet-¯×;ĢW˙ĸÃôAšY=Ī›ė?ǏNŨ/#'÷ãŠīŪÅ …öåE6}~ƒâ\Ļ.åOM{›ífmŖ'OmЃôzļēWąœģÄā„fmGžĨõî?đ:6ŽÛũiĖ.ŅNš¸m’×û…äi6ēIŪ€ĸ‡K/ģ‚Á ­Eģ¸rŽV8tØ/ØBēFbbâg_zųQúÍŪzö/úPī"SØJč&ŲŊûü­|ér&´MúDīĸAī°…t _jęnļCšSwÕÕrQé l%t“ÔĖŦCķx˜ ­E›ģæ‡ZVĪ^GŲBē(0%ûßc;¤959¯-))éwl%t×ÛĀ2iNįNdäöh` é‰II\¨–t[ÔûĮļē‰TCcPBsĒ”G`ęŌõô;Z°m‘Ā@7JËÉa FslĻŽ“œ–~ˆ>G ÖL˜h&}ŽF`€ĀD#0ĀD#0@`ĸ˜@`ĸ˜ 0ŅL 0ŅL˜h&˜h&L4‰ĀD#0ĀD`ĸ˜‰F`"0ŅL€ĀD#0˜h&@`ĸ˜L4 0ŅL& ˜h&F`€ĀÔÆ6üĸKÂj—Í_ōqžvķmÛŨ¸éĮ-žžˇöÁĀ×ō¯|}õ÷ž–ÛÉ×r;L4&WļŦžŊZ´äÔ4MęZZvv‹īŸ;ezČĮš |Fā~ Ö˙¨Å×3–¯ |-˙Ęח_Sākš|-ˇ#0ĀD#0@`ŠŠf:á6 @rŸ›ˇũ,ŦĀ$ˇ“ī™‹ĀĀ)Ļ͎6lųr ^kcLĄš„) RŌm~<äm—>ú䗷%0ĀD#0@`ęÖĀÔØđ@“ķävú`C+_ē<"Kō>´U6vœæņz?3ÛāŗJ?ŗžw%Ëåü'Y:hŪ.§wŸ/Ī‘"0ĀD#0@`ę–Āäķûĩ”ŒLmĖWŋŽ1n|`–)Š ø´@Xš8į:mÎ=h3īZŖš09pģ_Ú"0Éíä|Ģņ3Ž <ļÜGž—(sY  & S—&ųyÅwīŠhŅYŪ§‡ˆ@s*LŅŖ o‹Ā$÷ĩWî“Âō} Z&˜h&LŨ˜df§+Ēä-~d{ā~–$4Ų“„,ëíåąU; V˜L4 0E,0YÃK$“,í;ũ*­pč°į&Éō;§Ād&˜h&L1˜¤ž<Ž‘Ârn’uīK€"0ĀD#0@`ŠÛĀtq圠eĮ͋č˜@`ĸ˜ 0Åe`ēđĒĘĀ×2Ģd}ÜŠ7|'đ}â%0I…HŠ ™WØ?Đī{ ¨)ߡßVļ™%ø˜ 0Åx`’Á ,Į“Ų$™m’ xR˛\J…Ëõ•ä_b=0ÉŲäŧ=ķzg˛$Už'Í>+ũ]nGđ!0@`ęĸ&2€9͖Vz؁sŒ‚ŨĪŧĀŦũqä_ųúĒew~y‘ZųÚ:”Råf “ĘxrT}ū‘ōũšk~¸Qw$Ęc‡zî&“››T…” ”r€Ā^R0Čv!AĘzg98 ũĀb&0ŅLĻîiæ,Ŧ˰:ũ|ŌÜë?—ᄯYĢ֞ŧ‚ĩyk ¯P}–ŸÉmäĀÅŌGŸlõņÂũŨ&L‰ĀÔj“ "hHéyށD*FƌĢülIžÜO.ū,3QĘ(Ë/?—YYeY2k._•‹<ˌ–y[™á’`fũŊN)KgíËå÷H3/ ­‚q!0ĀD`jW0|d hHÁ)„"&ÔlŽ=0ÉuĖä{rî“ĖÉŦUAņiķŸœ“ Crģ¯Ū¸$đĩÜÖŧõœAYŽ+ĄNfŽäüBe›í’į‘’‘]ō˜R¸Âú8.LīéËļL4S\ōϤ|ŅÚ27Į$AÃ:›#˙8ōŦ@8ą˙ÖĀ$3OrÛÁg•žō˜æ… íIfƒœĒRš3CæŦ×Ų“.?åšJ“Į5Ÿ“<åPéŌíMÎĮõvBo[ôVˁ‰F`ŠŖôļJoû“ŧžŖŅÖ?$äȌ>‘(fx’Šyō3§Ā$AE9ĮIf‡œ“Y„Å~•˜ĖëŖÉcĘ}­Mf‘äg2ãd LĄÎƒr[“i„%ÍŌvęm›&)VCŌß-ƒßgcáÂĩRDAfŽ”mVČ˜B•Õˇ—ũv[{`’Ų.[˜8Ĩ™a.ɋē*y‰‰īëĮ?Œŋį¤åoÛ­ˇizķ°i@`ĸ˜ĸ=$Ŋé0˜—“ųķĸ%0Iaûr:{•:9?HÎo ˜Ę—.X`’ĨxōĩĖvÉ̚Y„"Ø5Úĸ¤čCžäī­ĶÛ§–~´_o‹ôægs€ĀD#0ESHz[Ÿų×7°Ŧ*Z“Y\!؅—å\!™Áé5` c`’Ĩqōĩ~°ßלjk`š8įēĀ×ĶŨrĘcĘī“īĮH`ézÛeôŸôvŸ–Ė>uČčwųl‚DI`’‘œ›`­Reũ:Ū[°‹ėļÖnÜôãĀQüÎŧ (ŠCvŠV–‰ém™yãh LfāŅû†võ÷œ2ģdž‡$3QÁĒäå*ĖB™y6Īm V%¯ĩĀ$×R’BŌŦįNÉ˙%Éī2ŋÉ M;TķL“„îiļ>W¯ˇÍz+aSĀŁIĘ Ë HŽËlđī­#8 Ŗ2HėŦęjĻގÆaéYëŖéĻ ×Ėû2ÜHąķÚFLä{RvÜÚ'íÛŧœë$•ë$äHŗ¤¸y­Ĩļ&ë,“<†zmÃ|<ëíb$0 -4•ßŖš B4¨–"ĘØ$pa`’Áŧ,á uMSûp˛ĖHŠ2X$0šÎ8c ë–ŪÕ[^´&i2ŗ)Õņd—ū+M–Ô9•—}€uÆIÚ‡ļÎY’ŸÉ9Hōxær?s6Hf°ä{°ėÅ%äûö. RCĪ=˙Ëį#˙ˇßFž‡ŊLy”&Ķ:ÕŧŧŗÂō})=žA5Í4™ũnŦ(€“Y>ØēėĻ=I–ųČ`JfĢĖķ‚;!ˇ“%Cí-,ËÜdĐ%ƒ9ë÷e†L^Öå>ö&˂äžÖŲ´p~G¨Ā$%ˇ•û{<lZ¯3C`r…eĒų˙1[XjTå cĄJ^¸!_ʀÛ÷ æ9Lr€)m Lb•Ĩ-˛ũL‚ų ŊÕ* DāŽĀ$ą”å:` ;0ÉĀĘ\æ#M–íH a{p‘Š[æE/ĨÉ}ĖĀ&GąCũ +gŒæ2#irô\B,/R–‹sZ—÷˜GģÍkģ˜Mūnû2"yÎrŽ’õo‘ß)K’ėIîkŊŽ4Lڃœy^9áĀÔådpúŦj>â/ÕY*ČyKņ˜$ØûüūĀĖ”õˆ\kÉÜž HmLĘ@fģŨáįŽ( DāŽĀ$!ü8w70]xUeāļ˛ŦFf$˜˜Ą”cŪNŽTËī’€!ƒ.ķvfø '0I’Ņ%dU|÷Ž/œØ.aJž'?3Īģ0›Ė*É÷¤I’s/$ŧÉs‘ßo^,ĶZLū.™9’į*įXČíŦIîcũ{ä1e™“T“"öŲ3sڞÂψ’YŖÆ@TŽæO°üĖ<˙…`wŽ—Ā$í˛ų }\ú­00Hˆr:(@ +0‰ŲĒyfs]ˆÛ9ˆØĒšÎģ]˜$4Čą ŒÚ˜$pI0‘“ÁíKÍĖ*\bäk Tr[ /öĨjá&ŧYī/Ë˙äžĸœBœüÜú\ėŗIfÕ.3ØI¨“ÛÉs˛ĪNÉ÷­Ifæd i8Ę R°×T^§Î8‘Ā6ëŧįÕŠGėe ZŖlį-Åk`2—›Ę ˛ĖäĘ9 ŅYÅKâ(0‰rK_”ę|%§O+ DĐųÉŦXåt­–p“šœÎИ>ĖŲ+ Ö'ëIéá&ëE6ĨÉŦŽÜ×~r¸Ŋj—ÜOfĄœwHéy &3BRUĖōŦMB™vdĀ(÷‘ŋGžƒĩÉS3nöĮY0ųŠË9-Á턏­"0Ņ:90)#đ˜Gv¨Ö‹ÁîkL˛L.ØßbVü’Į2_ŗ\˛ĩÉR:30™ŋ7Tsú}ÖßE`ę2Ą–āĩ‰ÁĀdöĪCF˙ŦRM×njMļj:˙ÉZ BfF…yL]˜dĻÉišž5$Š ŗ.]˜äž˛.T`2Ë'Ë˙íå‘Í%…öĀ$Ëōäw85§ ՚ŋ+T5=SDĩļĀDëîĀ$J,áGÎYĘ ķ~R bžŪĒUËĢ"ĻČ43XČšLí Læ9DNaGfägrūƒy­'§eq_ŊqI§&šŸ, t*c.KíĖÉæs‘â öÛIáëųGōxös§Ėō擿^ßĸ„uųŸœ3ŒŧNז%x&Zw&34™Á§ēG DT) DL‘ũp—AŊ5Ô´50ɲ; ˛äÍZü@BƒTՒķ|Ėë™įKIAëE1åū˜$Ā8ŨÎ Hf‰dŠĒ'áIÎy˛–D7og LrųžüĖŠā„œe˙äqB)ĸ"ēĀDëĸ¤Œd M%íxŒRÕt>”Ŋ@Äv S;¯ĩ"aGf>œ“ˆHHpjf:šžĖļČãHņ…ąĶ¯ {hß%Ëڔqũ#sÖĮ,UܙI~ˇY.\ū•ÛË9IčäšZ—ČÉōBŗ\¸ü-ŒäuŋĪ˜ä>fYry,ķ1åwHX´ĪfÉíågō˜ĻNņ%x&Z&!ŗŖģ,ŋ¤Ŧ@DĨĸ@€ĀÔļ&%´–ĢÉų6Ąšõ<ųŋ 2c$ÁH‚”Ķī“)Q,ˇ—ĨojTJ{Ö&alüŒkN !ō\ėįPÉīļ?G M2Ķ$•íĖsšäņė×5īožŗ$!M–ĘmíÕøä<-™Q2+čIP’Û9-ũ“ŋUūNŠH`ЏN[‚G`ĸuq`é–ĐtP5͚ļ—SyŽ‹ŒŸ@` į:+2Kb_ZÖįK9ũķēEN…#bŠIđ įBĀĻ6ëÔ%x&Z7&eūFŋŽë`hR B.˜ģOĩ,ąVQ @`jŊÉlLgœ[ãtZkE>špŦĖJÉyCN3=ąŌĖkM›u#0ĩ[§/Á#0Ņē)0™Ąé1Ճ‡ŧ%ŨØēOÎiȏļí0Éãũ5Û!ÍŠ¯¸fŋRÜd%4-pŅķ’%ĪĒ–"ļ+ D:"5#ãûŖ/›z‚ ÍÚF\|ŠV0dč—wß<ÛāhĩŠŪ‹\Ū>üĸ īĶ÷höÖŗŅĒiÅm–XļŊE.{nļ* D"¤853ķšĩĨgį4Jßpqŋĩ.Á;¨šJGõvčKMûœžGŗ|HHLüB5]pŖE–P˛Ö…Ī/_9ˆ˜§(h‹”ĖˎYD3ÛĖģÖhiYYģ¸ËZ—āIšã˜¸ĨĮë}ũĒ[îh¤ŌĖVöÍYoé]c§Ëģnše{”Ĩznœå V âvE@8|>ß×sķ ę—>ú$ƒšVXrzCjVΡ\ØUci ž“i9šŗŌ¤-Úü¸–äņ~¤÷‹ŌhčģĒŠĐ‚l—;\ŧ]:ˆ¨7‚^1Ŗ@HiŲ9{'\3 Õņ4ZúAxbm ž#)ūPvõœŖôEÚéį¯Qî+öĐÚ6j†Ļ§•{—šĘŒįi-ą#J* ›{|žcWßyƒ•8m ÚĒÉųlÉÉÉ_uYߌÉ%xÁļÃĤ¤Ãl‡ņŨĻ|kņĄ„ÄÄ÷UĶŦj4‘°QkŲVĶŖā9WM"ęUËĶNá÷ûĮûüūšP"ƒ–øj˛ Ŧ xčJĘjuÉX_‚Ė8×[Īv§ŗŧˇ¯:އf)RPĨũˇÄšdé[´”ųĪ7ö1ÖՊģėžųs’SS˙ųĩ›ocđ'íÆM?Öúv؟–ūœ›BƒŠƒ%x!”{“ũGŲãĢ]ú/ Ž%y<Ÿũ?šåaà Ņtm4™“5Š€`RRRÎKÉČüĸô˛+'3‰Ũ6kÕZ--;įhrJŠ”ŪuËėM<-Á ĨT_ĘuŌØc9ėiĨįöú|.bĨø€„¤Ŋ–Ām3f˛?ŦPˆ„Ũŗ_˙˙ķ§Ĩ7^P>ƒācM–{ YZīõ%îķųžá’>¯KđBn‡é99˙%ŗžl‡ą”$ËųŖú{ü ŠŊĨ_éÆA34ŠŌŋŖLZ BĘŊa¨PCķ ųƒĪŸŌ8`øHíâĘ9Y‰ëĀ'JÚÍÛ~xŋ*ž{—6vúUZ¯ūEĮ|~]JFÆ­. ÅûŧÖįö.ø9ĮpĀ#NÆōv(ũ5VĪ”÷K z\xUĨV0dhƒ×—|<#;W äĮpßMˇ„:ŨË Ĩ@„T.´ˆ@H@͇ۀag\[8tدsú|$˸OĘ TFn†XhŠ™ž””Š™Y1ķ7IĐMōxN¤dfÕe÷Ę˙GnŸ‚3,nšša ^ļ܂~ßė3hČ˙d÷îķAjVö‘XÚ͞˛“’Nz|ž“Ūd˙‰˜ŲSROČv˜š•u8§wÁģŊ‹mONNž¤â§˜€ėovXBS´ ¸ĢTËûõļ@Q ãV|ŗy)ēKđ`%AŪŦŽÖhü[ÎËSĄij^Ō ī­„ŖEFX˛ˆXĄĸ¯$<&ŽY‚“ ž­=ŊŲøŋÁgÆ1ļŦĩ„ĻE1ĨīQ- DlPˆ˜Đ,ÁƒÕ"KبšgÍķ^žį%ŠÉ÷Ü Kbėo+SM!( 0ĄÍX‚+ë-ÍŌöūRŖƒj4īcåu1ø÷Iõ`"ØīL8Kđ`%•ĶĖ"2hŽrģɖیâe‹9åĘyv1–+ąPQ @`‚%x° ÍkķR­—™Ū`ÜļšfL’PlžŋļCÅîė‹ßIÖĪ|ē€ĀŸX‚ģb#øH¨QM˖Âhî3îŗ—0&ŗ„&9g-=†˙VŲʲŧŨĒeˆŠSÜ €X‚+9éŨ<'Iljĩå¨ú(Õ|.Č^ʘTbéģb<4Y÷“Öšņõ8ē€ĀÛX‚;9ĸn-ŪžÁđ"Kg Sė†ĻÕŧ3^Ūg™YÚ¨ZˆØ­( 0Å–āÁÉBK€ŪÜÁ>ņŧ%t!6åĢæe›ōoQœũí+Œƒˆ˜b Kđād­eāw{¯Đ2˜\ČËĶÁÁ<ĮG–é•ÄŲßŦ@Ä*Åė*€Ā•X‚§Ÿĩlxe{šj>Q~8/uĖJWÍÕ%4Åã…_ƒˆØ‡!@`ŠJ,ÁC°~atåŧĨ˛NøĮßĢXĒëĄi‡Ĩ/Ås1 Œ;UķÁ)ŗ@DŨ@`r'–āÁ‰ĩl¸ôÎ: .isšŌ:^ö˜&aļĢæŲ•rļą@y}kˆ=ÆëÂ+É%X‚'ĨĒš,´ĖüäwÁī3ûá^ū˜ˇN5/ņŦāåĖ䎰lsfŠ&ÉŦ+€ĀԍĐ,Áƒš6’Y6\úHW]Cg™jžÍĘãmˆyĢ,ûŸEŧލ–") DL]Ž%xFk‘*ŪVōģĒTķųˆ}‹Td+/Æ)ąKQ @`ęr,ÁC0Ģ-ƒŗŨôŠTĶuÍoˆ}ŗ-û$Îa;•SšvY/ €ĀY,ÁC0Öņ\°ÍT¨æJjÅŧ=qĄÜļ˛oräT b¯ąŊđzLÄ<“­Z– wKÁ…ĮTsÅ0ƒņĄL5Ÿ;ˇƒ÷=(9ø%Ë­"jTĶōÆt^ŠíX‚‡`ŠTsŲp|šéÂąéÆ Đœ E|ƒ;-û+@pR bže6 DČöB) ,ÁC(Ö˛áû\:Āg„}ieŧeqŖÄŌ7w)*&†C DTŠ–"diãp^)ø@“%xFúƒšôéyåîŖøféi™mĘæ­‹ĢĐdΜT+fLÂ%Bv( DL!ąĄĖSŅurŊ<ŋŨÆķŨÎÛWōmĄ‰rÚá“Rq@“éõ"ÅŅB˜"Ŋ]ąĄX/ē"Šžˇ|^˜3b•ŧq7^0‹’ÔšÚLÆYÁ D0\Hn UËĢW›kįƒ]kc/Laa ZÛ˙šUįdvi^ū ķTs%ŋ"ŪŌ¸’n MũÚ&XˆĩŠåŽ€ĢlPÍ%bWƒØU–e¯‚d LĄą­ 6Ģ,a#šÃôK?gö4ūB˙K?.ã%iˇ)ĒeųüåšŖxi€îU¨šO.ļČÉQs}ēõ…[ L 0…Ä<„ŗīĩ– ö‘,!2gRoįíËĐ´Õ2Č/į%éŲlW- DČgĘ^ {”â ?¯0>͝ßË@Pî[ⰑË2ž%Ɔm$æĢæŖOåÆī-˛}č–÷—uŧ“ƒ 4=ÆãËí*ûíwø —ŋc™ņ/kƒ L‰%xg0džˇP­bgæąĖ2`ÃÛ—ÖYú&ī¸"ã5­S- DT*Â]ĒX5/Į įCģFĩ<ĪiĢņ}YZb’?¨œĢįĖVÍG ÍĮ0Ģ+É^ŗjLå˙r.Už-t™3_‡ŒÛ´9k+7nc>fƒņ5į`˜:KđĐkŲđ*{˙\kŲßraĶødũ|_ÄËŲÆį‹ĩ@ÄE [>ād ÷ŧąQŽ qôÂiIžų=š¯ß60¨ą|Ī L#$˛^ˇÔnæī7ÑĮ2°ļVzZĩI} ]"Žąô…]„ĻN•mloÖĩÆ÷(´“šŒ­"Ć'3=O‡LæyENį•Ģ–e˃&ķhŊ}c.UáŸÃ´×˜vĄÉū˜ÄdÉ!Õ{LmÅ<„c¸jY6ŧ(Ž_‹ÉĒy9"įVġËvą[Q¤ ŗyŒĪØŊĒeˆu|vmWd„Šzãčƒu6F5‚˜â˜Ŧˇ}Ūø@´.˓ rŋņøų­&ŗ$yŠ-ŦíR-K›ˇ5ĢäÉ!]9WÉ3Īw’Û[¯Í´J5Wô),ÁC¸&¨æåGģGt•e˙\­˜Y`ĖŅ|ÁæjBS—¸x^Q čë#{kp -?7gžŦËOö…zãh†5D LcŒÛ×g§qß§ĐĩĮ6xĩWô;` WÖ°9ƒ&GĩĒTķ9ROGhĀ+_Cks3×XŒ‚įZ̚g•õöī-H“ū|Ōč+‡õö¯I ÉëpÜx]>įõˆûöŽŪūicŧËkŌeí=cœuŌ2~:ĘįZĖ4VNu™‘ey2UģÕhKTđĨ$•Æ,Á<ÛcČ׍û/T-/8+ŠĐätTŠØ8rŋÕxãŒī—)į“Íī—ŋÛéúPf3WŽtNáíîvŅ2ÃÄ<„ËząÎÕŧ§ĨšĢĨąFēå §”*á%éRN"öŸÉ~^ úIВŲ'§ĸ2 ĩŸ—ˆĀ,ÁC¸¤_lVÍGËđ’e^Ėô b)N­"9Ž—¤Ë+ąLąœˆz˛1ËÔc™1X1”8]Ļļĸ Ú2ā{VÅoŲđö0ČĪōRĀø ßaŲ†Øßvßû +‡Ŧ"ꌱU/d@kŊ^“ydwb€ĀÔ1,ÁC¸ō-ƒ 9ˆ3†—$,˛MT-̧‚ÁúFËgy9/Iˇ’ķΟĩ¯¤đ"€(ŨÁJE=9""G¤ōxILĀ<´…œoaÎBJĨ¯b^’6™Ļš/N>œ—†uŠĨ­n"iĢj>ˆh^+“™tˆÃĀÄ<´E™jY6œƒ5ícÎ(Č,'™Ã´Ä28§â—;ČlēTĻ@Äi`b ÚĸŌŌ_v0Xč9˙Ë\ZŊŽ—‹,ÛŲZ^×Č6Ūëeh(1˜X‚‡ļ˛– gĨ–ņ^X”[úį'ģ‹Sˆzã}by2ÄH`b Ú:8°žNĄ‚ČZϚ/>ÎōFXÉšnæōׄ&Wr*!īU)/ bM™ŅÉWđR KđвlėiÕ\bw/I§Ō*ã5ŪÉË›q–Đô´ąMÂ}¤x‹ˆ¨W- D°Ī ˆĸĀÄ<´•œčŧGQ6ŧ+ŠæĘŠŽģQĒųÂĒģMŽßoŽV- DH%ŅyŠs>A`\˜X‚‡ļ’˛á5–û^’NWĄšgō8NÛ¤šös¸—„ÚE–ũ¨yāIÎĨ@â"0ÉFPntzšOĨ­ķ™äȃũgŲÆcČc-TÎW”.3ž?ÜøŊōû™ 0…ƒ%xhOĀ>dé3 ĖēÎcÆëž‡}<‚Œ!ĒUķ ļM÷“í¸BQ q˜Ļ‰㨁yĩvų^Šå6ōŊ%A> åčĄ?ČãÕ)į“ĒåņļĒæuĖ_S+X‚‡ö¨P-ˆŗô§kĨĢæ#ŌĢy9$4™ƒo™ą`ö7篛O+ D ÆSēnöŠ–GuĻXæŅ„Zc‡fŋŋUØl|]l Ŋ–đã7~.7Ų˜dÃÚh<_N"$0…Â<´Į2Ë9eŒģĪ8cß`ėī§ņČ.KhÅKUœ Dėbl‡X LEzÛ œ¯•!GĢ,_¯6Ķē›g|oœņõ:ãkûŅ!1Č­˛ĻZ0Ļ0Ŋ,ÁC[ØË†/â%év̌÷C>[8ßÁB“ĩ‚å8^’¨ãT B.f-…_(¨ LVyÆÎiļ|ęm§Dē¤ĸĘØL{ŒûÍvhÕÆĪŦéiŪ*S+}’%xhĪ k§eĐUÎKâšģÛx_ļķr D?ŲĄ(ûí$-2ƈÖ+×fC”ĻrÕ|ÂĨf J‡lIvŒ˙ˇŋŨōskå”`͘ļōV˜‚` Ú#ß2(¯UĄv›"Õ|Ūj%/B„&sÅJƒâ G´ŋ—ōūíą3eu"5É\WžĮčĐ%ļđcLæŧ FP˛jØg T­!0˜‚a ÚCö_æŅLʆģ—ų9R§œ+¨ĻĩŠeĩą66ŨŠZˆ¯š\˜Ė#8öj&Ų– eeys¤°ÃķļŸoUÎį0 )ü°šĀS*UËe—æŽpŗå{Úđx,ÁC{ÉÁŗÂ§Ė0QšØŨĖ%W쨯ҊE–Ī„%ŧ1ĄÄ'P Ž L[˙;5ŋjž˜gš¯ßōaVãđ؏Y&ö%Ĩ–‹uĒYŠj-)&=ĄZ¸įŅąĄ„T—[>x%´S6Üũ˛-ÛģuYwžą_áˆ3Ŧ¤`€šę`]÷p/9°%Å`(nLĄZ‘ŅYk‘„œgUs5;iuÖ|ėē zĄņxuÆs‰L•m C`Š~“Ãčgá\‹%xe”Ņ/œ‚ĐK_ÛĀĀ)*?§Œ€$Á÷3ã{,ŋ‚]šåsbŖe[—qĖEåÅhæT BÆĸ+ĢĐŠ}v+Í|ä!Į\2g.ĄnÜ.ĪĄc7¨æk/9)22r™Ušæ0‘Įæ„ėčĒĐGkįŗąáØj Ķæ~Ëc$–ęD7ķ•#ļ}Į^8ƒtu–>"ãŗČË^ž¨gˆØ­Zˆ€\܆Į\ĄÜčÄ,™€X"0… Õ,ÁC8ōÕŠëÜ{Ģæķåę´ĸŲ7TķŦA[ļ ~ŗ„Ļw-}Ļ‘qIĖŊĪ;mû…*ôv UöÕL@—›ĸšf…d ß^X´ AĶ”!‹%xĮ*‡~õš%h3KĖs•œö'ÕŠX+)đ™CߑbTĖ0Ä A2Ãd=p&3PN+—œV#]ÎŦĄ/'įâ倅ĶĀGvn~‡AKđ.ŋj.0ãÔŋÎæ%ŠJæšŗ­˙ČĖ!‚‘™¤Æ ũ†åšąģßXaûLsžŸŦŦo Mč֔_Ļ8ą§r*ūđ´í6,ÁC[-PĄg ø@Œ^rŽŦõ<§YęuŧL2p>"hs}¯Øæ7B’Ŋ@ÄTËj|FpĨÛ`ÖZĒž%xhŋ¨Ög!¸ŽOô‡bŗ¤°}Æ`7/l<ļ Ŧ=ÍK}ašĨ?œ°Œ?¤'4p#{ņ E,ÁC{MqP- Ęzu–G?™1ØŽNA<Ϗ Zōƒdšd =Í’Îøņ‹ }ĐĀ•ƒķąœīÆ"}EúΡ6lŅ*nģKģæ{k5kß ôŊĪÉũÍ~xöĨ—ŋR<úœWzôĒôUĪW+ŋ_ú°ņ|njįˇĖxž%ŧu]ß/,û”ôœÜ×õ÷kOņŲįė.<õĨ˛ĢįüŲÚ/ŽŧåΡ­ũBīĮĖ~Ņ´ŲtÄÚ/Žŧeų›Ö}JŲ7gī=ûŌ¯ŧ,û”žũŧœž“ķšô Ų§čĪá3ö)QÚŽ$§$ŋ›—ßŖēxø —ĮN8įĨ¯ÎŧėĨënU-íÛ+æŊ~įCK÷ŽÚr[­´õ?ûū{[ž[`ë 4iV=ø÷˙ûŸũTž˙Oŋ÷žÜîŽ-Û/÷ûÖōyCgîŌĢ˙8uÆĨ/7rw˙!…Čé™ŊĪ—ė{?Ī$÷}ÖHŸđûäôČúķĀĶė>wüYŋzõ¤ßÉ{hí+7ŪōkŸ>`íŌŦ}BšĩOH›rõ¤—Ī+;ûåâĶž*}P˙ŊōYÃ>ˆ1rÂëdãč‹lĐ{dđâMö¨‡”7†_tqõ¤š ŪüÆ­+õpķ°ļø‘íÚ-?ŲŲímŅæĮĩš÷ūëÁ+—Ūž˙ĸĢŽ~ĨøėsŸ‘×ķÕĤ¤” Ú`õ)SĖ,tIߑĒ$įŊŦžŊū8äœ1ģ/ޜû˛ Z¯ũÁCšĨīČķž,jéÛÃ/ŧäŧ~^KNI9`„ņŊÆŅC3Hq~]Gû…>¨ÕŅš} _6öÂW$ĀČë?˙ū‡šiŸ2īŽ\ĩėÎwĮķš×O;gė+9Ŋķ_×ûķ'Š}J÷~&5ĸú .|ŊėŠqžáΚûeāĒd˙ãÛ´_T?ŅímĮKhŸ^ûé]˙vëÛsž3ãOÚzöúŗĮÃgR'SKXíĶŋ÷Ū &÷{=¸ŧōŊ-{ķß~ąîS7õ‰ũ÷ŌW¯]6ķ]>v_ßĸ>Õ^Ÿ÷SŊONŸÜ-ŨØņŦMHLüŗ0ęŗ{å˙Ŗäŧ ūRvõėw*WŪ{Ü-˜Ž |äHôد]õ§#Îz9%3흯Ŧ‚ėœV;&ĻË;Đw$TH°–AŖJ_ē ŧbīŒåwŋí}įæm?“ŲĢãgĪûôķ/Ē–ƒI^ũÃ-!ámãƒē‚Ŗƒ!ö) ¯ęû•Ŗz`ŪZé˜?Ÿ1ëÍĘ÷ÔÅÂ>åˇŨõŲ¸+ŋųˇÁg•ū)#'÷ÍĀl%û”&%ü)1)ņh~a¯ŋ›|Ūës—Vžu˙OŋwÔ-āŽ œeĐüÍĘ÷•^4ꕜžŲ—H˙ ķŗF­dæFfqfŪx՟×<ļâũXč̎.?:{ņŒˇĮN(}C˙ûŪ’žŸäIz‰>tŲđV$%yvK@ęQPx`ė´+ߛy×méŖOFõ@Ļ-a QÃ/ēxOZfÖߌõŧ1‹PJ Ũw”œĢŋfdIĶÕwÜũ~ŧôiķÖ>¨É…‚!CĢõmH–ūUM6*/^û…ū:üF’t9įōé­\šúh<íS*ž{WŨY'WgäæÖŠ}J[úORÂoeØw`Ÿˇŋ1ÚÛl_Õøôk?‰ęp¸íŠWĶî~äöO&•_ŧ× âš˙|ųYc$Yę&á(žúÄŊ?žŖqúŦËô.ėõž ~Å>č\Er”"Éëũ(=ˇĮg]zųú‡{āC>^š­-˚vĶ-ä(¸Īī—5č1vJĖ}GoüiioĘǝ/]ū}§šÍžįÆ ž>ŖĻWŅ ŋJ€RM'ËÚtOŦ÷ = ŊŸ–ķۈņ˙ņ[WĨ_4īSž~ķm‡O?˙Âŋ'§Ļ}¨ŋNcŸrj˙ņx’>čŅ+ˇö+ߘđö÷šũ¸ ãa0ÜZ““Ûødf-5=õƒ8ųLjÚ§$$ŧ›‘•V#Ë-īÚ´Ŧ–>ŅÜ'VnŧĨqâô‹ŪËĖÎø$ŲīĢaŸD†LßVz}žŨ^_ō=$šģæ‡ fÂhWßqĪĮEgŽ’åD_č¯áĶĒéÜOŧõŊUéāō˜ŗzŨ§ôđĘf][›ÕĢ÷[z˙ųH5-#KũBĐü_’×ûňņ—ŧÅ>%ŧ6ķŽ5úvôĻž=IQ‹gâpŸŌü™äõüÆį÷ūƌ‰nüųZÃa4YŠxîøŗßHj: KŸI_~Öčáų3= žúĐ­ų‚÷ŧõöĀĢ´‹¯wĐëķMö'˙OœîS€I÷%'¯‘MaÉéíæÛ´xZ.ée6“į^˙–œƒĄr¤\ņ’ß!ĨGø>éQPøúßūÎÛôö7ŠÔ6zŌ”xũ)é¯éUĶ:ü¨íú`˙ŗüAƒßūÚw–ÕĶ/ÚŋOš|ÁM_ČRh)Jû”@˙ILLüžĮëų|ØY§Õ.pIÜ,ĩëŒeZ‹V]÷AÁ€üšÄ¤Ā™hí?MŸ5‰ Ÿ Rø—[īŋé}úDûûÄŌ,l<Ŧčc_˛÷ĪįYFpZáõzg郺O‡ŅŅ…me€Á6˙ū‡Žäö˙‹ą4br v™9xˇīiÃöę}įīydÛ´oųԟ–öžū:?ĢĸĢdye’ĮķáŌķ>`Ÿá}Ęk§•Ô&yŊ˙ˆŅ}J ˙$ûŊ/úƨΞkÜ6Š 8øô5‰MË='GÕgMbÂ{gž3lßO^ÜÔĀ{Á>ņËõÚ¨ągNMO}7†÷)@‡”úĶ3öå,>Ĩs[Åmw5Ļįäԃés1˛O ôŸÔ´”Wpč‡;îa0ۉíî-ß­Ëę‘Yk,õts˙)•Â0=ķ{ü}ŨĪîfŲ]'ļ{ļ.×úôë}83'ãÅÚ§“[X8/%#ŗ~ę ßađŅ…ß ¯Ē”ķęoÁ¸(î>åúßP+Ëy_ģļ,õ™eߐ=—Î6•ûRR?›ō­Å˙äũęē}Ę%3įÖy|žĪĸ|Ÿĸrō2+3˛ŌË2!¯]Ķd9Ûŧ˙7ķ#šÎ“KûOšĮëųđÆģæŋĮûÕu}â†;æjiŠG ú÷ē”Ņ2â>,Ĩeį4H™c]ßfÜžę“(Må˛ÔJ.øË{Ų=íōëoĒ‘*s. MåÉŠéuėSēŠāĖ÷h^_ōįŅš$,eõČ<öŖ˙~€Ak7´<ļō¨L>qY˙)÷&{?ÚôĖũGxēž­}|Ĩ–‘•~œĐÂBS{’×ûŅŧl8Â{ØŊmúwn{GM˙–M„ĨM^Ÿ÷SˇėSK„& Ûôë×o°ĪŸŌČĀÆ=Ą)!!A8Ņp%îByŽ„%w˝oëī‹„îîŦlTčņzëŲ§¸'4%yE :­čļKžz!Sĩ~ jģš˙Ė^ZRÍ{ážvÆč­tÜYŗE#nôė?ā]šžƒ ÷´ ž^!istŸį§.üΛŧgîjgMēü×ŨŲRŗ˛ūČ>Å]mÂ5ķ´´ĖŦŠéĨĸ‚ŋßųāRĨ.jķūßĖɉ‰tįgÍmëĖ{ážļāļŲÚĨ%{E#nx|ž“‹ŲΠÂM×Tšī!-!!áÛûŽūŌwÜ׿ūā_åhđĮŨ6Ãäņ4Đ/ܡOŅ÷õGĸá3ɗė=ņÜÆ ÔEmķŗhIž¤ĪģĢO$&&ÔĶ'Ü×'RĶSE#~“×ĢU|÷..j—Í_(Éõƒũ9§ī¸ŗ˙čoO]ˇ n’’éîëz= ŸI^¯į¤\û…AŠ{ÚĸģH`:ÖmŸ5‰ ĮéîëÉūäŒĸ7üééڈ‹/ePáĸ6`øH-!1ņcˇ÷}`ü9}ĮũG{>ėļo˛˙ũÂ}}B_GÃgRZFę‰ÉW^ ÔEíŦągjždoˇÍ0Éė}Â}}"3;ƒ&ďŒÜZJFĻļ𥭠,\Ф˛˜ĪīדŪu{ßIōx?ĸī¸l9ۚúūöŧÕmaŌŌĐ/ܡOINK˙,>“rzf7fædh?Ųĩ‰Š šT+LIõk)i)ŨvĪ›ėųˆ>áž>ŅŖWNŖhčŦžŊ'÷(čËĮ °ĶŨĸ%&%pũrNŸ¯–žãž6įž4ŲžĨ˙čoOMwõ‹”ĖŦ:ú…ûö)z`: ŸIyųš rByŋA ]00Ößíö~GKMK9Ø]}"ŲīĢĨO¸¯OČļĘ(q˜Ė*J pÜ1°‘¯Ŗ%0ŅwÜs­´ėėĀŋōuw&ú…ûö)Ņ˜Ė*\ Ũ10–¯ģ;0Ņ'Ü×'LˆËĀd☃.Z×´ōĨË[ lĸ-0Ņwē¯-}ôÉĀ ũÖ°ä–ĀDŋp×>%Ú“u€,ĘdĀÚumåĻe-Æn Lô wõ â60I“ë§äôîŖÉÅlŋĩa ƒN>0™MŽËQaų–c¨18éØy&Ã/ē$0h”×S9Án­‰žĶ9M.{ųõ7iũ‡ 9(vk`ĸ_t˙>%š“Ųd6Afd'ƒšwŪËāļMf .ũÚø@č×SÉÁnëļĀDŸčū>A`Éa#G‡åļ˛Ôcâœë´7ũ˜KM^ģņ3Ž ,I’9§#œ×.Ú}'2Mf$`Ȁxčšį–ˇ… JnLô‹îÛ§ÄB`˛’evĄwߞĨB7Ü1W{âw›đ†Ņäĩû—%W–´ Ō/pNP8¯[}ĸûú ĻmÆōU\rjš6¤ôŧĀ‘îų<Ė@Æv-ÄȲ)莚0Y›ĩjm›#V}§íKĢ$•^vEā¤ũüAÅí nLô‹Žß§ÄR`˛ļ{|g`†!-#M;áœĀōĄ-ŋ\Ī@ØŌ6ū|m`¸ØhN†vųŒKĩMáöĀDŸčú>A`Š K„d9ˆ ėäqä˙_ŊqÉ˙ßŪ™IU扖ÚWVŲÄFe/AÁbmYEpcPË­A‡–Öæ=ZGģÕ@Į=tz™™g"ĻcúE˙Ņņfâ͛™fZDmDPPl”–~ųîšÖ‡—4Ģ(ą–ŦĒs"~Q•yķ.™÷˛ŋķmˇÕM#ŧčĄ'âĪb`娏Ģ5ŋ$4ĖPuŧc5Zĸ0Yv2LŪpö%—Æ--Ü`”Aû´ $īv›“0Y.įšŌR…)Ä/˙ķ™Ô­?\w'b6/Zø˙ŽuU­nę§ũHüYŒŸ2:îĒFË 13œīXŸæ$L–‰ē— >‹ã- “(LĮ9;5ÃtĸĻ˜Š„Iv¨%!l) ‰ SöNŧâĒ8™áķ#ĄĄ†œ‡åõąŸ–,L­ĩėĐJDk -FŒ7éqZŋXh5@æŨ÷`ēÛĩTaōšŌ°×”–.L™f÷ĸeaėäQqKSQ“,SĢžzũŠ“0“3åķuËæÅÉ0R@BL 2Ëëc?ÍQ˜, [&&Q˜ęi&/’ē…n$ƒÖIYƀe’úJë+¨Áå¸?BrKÍ.É-I‰ĖŠÃF¤F^4-NfŌ§sޝhMÂԒĘĮÃqq|á=ĐbDšĄüPŽ(O$ũŧĻĄf‹k Âä5Ĩ~Ŗĩ Sz0Éō´ųSŽtC"†ŒngŪI&3Í֔A Įĩæé•ņØZ í'ũ$Â#'œ™šqÍÅq2Œ4Ä1´a˛LÔo(Lĸ05p ;É" C¯ūãÚÔHâćĖ$BÔ"WN›'tAa‚x’ŽĩÍö—|=ÉTØŨ|8ēBą?’.j°I`ō âãâXHÄ8ö}Ŧũ)L-ˇė°~me‡ũsĮÅņ1jŠÖ–(L^SĻúļ3Ū…ÎÁÃÄĩņyQų!qf<‰4­s_'ĻtkcbŨËĢã¤5Dm3Š…ũ%_O2ļE×(ŽcöŠņūHÚi!.,*ˆ‹c!‘į8Ø÷ąö§0Y&2&Q˜š¨ģc6¨Ĩ–•.JÔ$ĶE…ă`<Į‚„(z G‚$)ų˜šÛäëŠŅ Ûĸģ 5ētƒa$<Ôė6fŖ0eoŲayme‡íŗŸPvØ6ÍōÖZ„ÉkŠÂԐ‰3ãŠÁ§–žYÄh‰ ‹‰+Ҏâ”8i AB,/yi凚˙äëiÛĸģ-tŖb$Ė´ 4fŦ0Y&&‘,&Ca2&CaĘa2&CaQ˜&…IaR˜&…ÉP˜ …IDaR˜&…Ia2&Ca2&…IaR˜ …ÉP˜ …ÉP˜D&…Ia2&Ca2&CaQ˜&…ÉP˜ …ÉP˜< “ˆÂd(L†Âd(L “¤0)L" “Ą0 “Ą0)L “¤0‰(L†Âd(L†Â¤0)L “Â$ĸ0 “Ą0)L “¤0)L “ˆÂd(L 'Lˇ?÷‹Ôø9WĻ:÷ęĸLōwˌ9ņķ­á|OšūĻøũ+L†Â¤0)L “Â$ĸ0 Ķ×âėK.MĐŖgjZÕ˛Ô5>˙åņ‰įˇŠķVœsŽÂd(L “¤0)L" “Ą0}=:v;1uáÂĨG=wŲ÷Ĩ ‹‹jeēņņgSsVŽNÍ_ŊökÛ¸õ™ŸÅËŽZŗ.^gņ#Oy>ü‚Į7?õŌQë^~Κ8ø?<ĪkØ'kÚomĮ´pŨOãeüM_Æ1°Œŋ “Ą0)L “¤0‰(L†ÂTcô:,ÕĢ˙uŠĻ× TEĨeŠū#GĮ]özœÖ/Uĩū…x’ÔļĶ ņvzŦH\14Ę6R’^ÎyL78ūŸw߃ОâuļŽƒ×p\Ûe˙§qTW:žë;bT|LŧšBØ ļ;°rlüwäEĶŽz/Č ÛbÛŧ…ÉP˜&…IaR˜D&CaĘKōt,ŅfcaA2.šņļ#­K‹z‚Ī$–Ÿ°’2âÂīÅ˙#:I!A>ę*LËÄ+Ž:˛Œ˙ģŸrÚ!Ę/(ˆ÷ZŒ8FZ…hYb-aaVדûÄ2tņ’[â}„V,^‹4͸}å×Öã5ƒÂd(L “¤0)L" “Ą0ÕÉ×\ RD!"HHIÛvąĀ„`|ãœĒ?¯¸•)l‡VŖē-IФ+¯=˛]&_ā9Ä&´0ĨÉHŧÍ顎ˆ%(Ķû@搸äņԊtæS2ŽĮ.…ÉP˜&…IaR˜D&CaĘ8(´ö$ãÚĩÆŌ4kų=ŠqŗŽˆ%ŠH­JtËCbB+Ph‘Ē‹0ąŒu͎K„ãBÚ2 Ëjúū°­MéÛDü2­į&CaR˜&…IaQ˜ …)c 7H­BéSĶĒD‹ ŗæ!LÉ ˜deü^–!YIaJoŅ ­UtdßČY˛{ bÞj&"}R ēķ…Ö¯ÁcĮĩĮÄ1ķ>Y/9š-O “Ą0)L “¤0‰4sa"qLv{ Á8’L-ÆWAŗLŸÂô՘#¤†Žqˆc}„ˆÁØĻ3&MŽ…†Ī’etec}ēŅŅ=.vČcBŲBFĨË­Rtá e–îstģc=Æ&…îta SM„(ąOd‡Ö(fØcÆ&!E´Žą>Ë>öÉkØĮĮ{fŧWJaĒ{pžëķžUée‘ķČ_öÁųOe0)ØĮŠ0ŖÂ”}Âôčß=9áĖTĮÎâVyzęgīŠĶē/ūöņԐ‘ƒjÜ.Ëj –+LM/Lˇūpq­įŠž÷ˇzũŠÔø)Ŗk,Kü­ī}N›?%~Ÿ “H# SH3‰]ŠŖšƒĪ9Ķg§0}՚„,1Î‡Ī ĸ•9JŽob†<–#*•ĶfÕ礉ŽpHQ˛l#.ĖbĮvéŪ‡äĶâ&]ā1ËX‡ũ"9ĄU(L,‘Šâ ĮÄö™ė!Ųĸ„ˆą ABš’­Xa=äŒcōÆĩßLjęS0Cp^šÎ!8”öÁ9 ãĐhyDäÜLĶČg ÎmCĢÂôí‚ä´ŦmYjÆ5ĮCĖ^85•———zėÖsũ§ũH\VjÚ6I*qŨ˛yņëøžkˆÄXa:>aįdōĖsSí:ļ=ō¸!$ƒmvëŲĨÆ˛ÄßúŪ'"vÅ 3&‘l&ƓáŪ4é­*$¸Ôž‡ûΤo‹$¤Ļeáž6$›a?a LøŋĻûî„uͧ­æuŧžãšöË:,KŽ‘IÖŗ,yOŸä{ ÷õQ˜72[jMĄ0}õĪÔŽODcž§ĖĖØĻz<kÚ-Æa&Åôũ%īåd9NĘ?ÛûĪtíP˜^îXWˇ*Ĩ?O+ĶÕˇ]˙˙ōŋŽ#,{eã‹G’ڐäōÜÚVĨÖŊŧ:ūŋŽÉ0Ûũųŋ?uDÖÂķĪũæ§q+×Ã?_ķĩmũũ†įã}ąü—˙ųŒÂÔ2ÃųŸû“ŋzčČķH5į!ų\ōܲ,ŊL|aâü˛ öËū“¯g›lģĻrōÄ?­‹—q “H– šN¸o 5ąta 2E 5ę$t{ 7+%i÷ÎaėŨŗ¨™MŠgÜ ËØ6ĩžaŸ$4ÉŽôd™n[a]žgߥe‚Į/ĮEëŨĻBKIËÂ:lãŧĢūâȄt㘊qfYčvÃzŧwŽ“D‰–+L “ÂÔ8Âēxōį{šOÆw˜ī#ßMž§|įi ¤r‡ī*׹ЊXĶ~¨ôáuA‚¸>„k×öɲ0–Žc -•\Âũš.ރĩŨLaj˜ áå.šûšåãüéããČÔĒū0´oĒrŌYŠS|'$¨.ÂÄvYˇ¤´8nŲā­X˙Oˇ­“ûž'ģáØH‚IļyŽn„ŧŽdZajXaâšÁÃğ7‘ÎΟ{ɸXēiĨ ¯ŋëá[â–KÎįę¤Sz9÷uĻU/÷Į6xÖ bÆßč;o{ėäQņž8ް-Ž…uYÖŖw÷øĩ “H– É@hņa< AwĨd­,c7B!,Adhéa H“Â6BRA"Â>ę"L$,$$!ąá8č:ćב8…uéĘƯ0€Ÿ×†`’$ļÅcŪG?ē|‘$…ŽeŦŪ ĮPĶg§05Lpž“]āĻÖ#L\gø.†kßWžį´ņũ§"#9Á:a[aÖÄcí'Œ‹ãõl‹m†ëDÛÆČ%g@äēÆą%[˛ĢKŗÂôõ¸aÕuqÂI7<ä….yɃēSčēE"MBÍ6ę*L$ÂëŌĘTXT‹QØIņüĒËâĮáøÂúß˙Ņ q˛žŠUKaĒ_aBˆÃ9A°‘ ˛t¯ä<„V?ĘÂÖ˙Ū“cy û ŦąÍd 5ĄŒĐšÄ6’]‘ áã†Æ˙S—–ŅŌZ:ųŸ2DĢXhCČ&‘,ĻdmnH,øK-âàzîĩ“LÄ%yߚpCQä&Ŋû ãLę"LĩÆÉíŌ"Ä1¤ßĀ4}?­K™ēđđžŧ°MŪĪ!ulŸņ5éŗ˛)L†ÂÔđÂDËMrÜQ¸—VhéßaZ„ųŪRQsŧ”œĖ.yTŽPaB…N&aJvĻ2‰krĨ050…nn$ģ$•´‘І„ˇ.”ŦoÅ)uĻäļéH"žCCKFĢÛ@žÂ˛Ē,ŠŸkŦ $Zŗ0ņY‡Į´ú /ÉķÄ9Gj(7KrĪ#-aAŽ’A÷ĐPFxD^Ŗ0‰4˛0‘„¤˙°Ķí…ä#tĒM˜čēB7ŧ°ŒÚY–QÛJ’“ėâēÖ Ŧ“~ž˜„¤&ŲĸÃą„÷Åk¨ANn—šŨ /ĩ 5ÕČ]r ’pCÕdí2ŸĩÕ$DOÁĐ ĪHa2φ&žŗÉnļ/\ŌĮĨŋžŽÂÄëC×]*t¸6&¯O5 ×Ä*9ŅCM-Ų SÃÆ”9įĮ­7éĪ3ŽˆņCAž’RØĄôĻä¤s_z¤ûÕ7&Z§haJž†ũ!v´( gÉ)ĪiubÆšüAaúō1ㅘQ/ųZzoÎį)9Ž ĄĨûd]…‰IŌËÛĮ€‡IIˆđzÖEÚBŲu–<‘&&~ėIüi ÷Ę 5„YĄj&D&LŽĀcēÛņ˜uÃŊhx ËhŲá1Ũ\¸#–‘ˆĐÚ’ d‰V$Z¯Hˆxmō™+ÂtÔ Sà SzˇģpŸ-ēÅRĄÁš ׇY;ų.rāēÂ2Žá;ŸI˜’ûáēÄ52ÆTŌrDÅ'\—¸>%[ŨŲׅ°?*]&Ž‘cKVĖ(L#L$ĄÔäĶŊZ{d$Lē€ˆ‰Ą cCHL€Ÿ&ēI!HŒ{ĸ;ÛCpŽG˜H˛ŲŨ¯x-É73äd™ã`ŦKrėĻÆĻ EČ į âq˜­ŽÖIēÁQž(#įßdŌÆĢQ.Ų““đ8´Ҟ„°!Ņ,GˆÂēĄ !î<Ļü˛Laida ­>áŪ2CúŊrH*’÷“aY2á "°> iō^&HL¸ë$#yßä„uƒ0‘xpĄ[ĩÉŧ&9Ŋ7¯gyĪ”īėf˜~ßœpLė?t-$‰f˙|ᘒŸ’E7DŽ×yãÚãŸíŽMĸKg}‰/2Dšũ ņ!9&á ŗ+Öe{$Ė uŦ Ķ×o\›.¸Dø.SnxĖw“īorpŋ.ž›œŽĄ…)zMûĄ5ˆ šä9N^¸ļąךpDâÂú\7¨xaßá^b”ĩô{})L3~‰„“D$Ė@Z ēÄŅōC⋠…š2؟$Éb}–ĶŌPכ’ŌJl)ĮC˛Íą Cl;y,$ž<ĪröŨ˜÷sjéÂTĶMey.]‚yLëd˜A/9ÉįĄĄĖP.(?aZđēŪ¸–ÖĨ° ‚IBK"ōöÍ:ˆ>e&eˆqLa?äÉ׊40eK¤[jiĄ0Õ]˜HXT’åĐŌ™~ß/d<)įČ-‰4‚Ä˙5í‡uÚ¤ îgI8ÉpzĶä}xøËļŲcLŨZ„ÉP˜ o\k4m(Lĸ0)L S3&ä$  'nTĘ:áž]áŪ^aÜ 5útĸ–Ÿ˙kÛë‡Čũ1†ŽV ēS…–Œm𗖠d‹}Ō*ÁkŲ_ctŊR˜ …ÉP˜ …IDaŠåoŌÅMajyÂDëBÂ8dĒ0=­GÉņt´ĩŠžd$Ė‚ēgÖļļēl!JÉ.aZčL3BŌM+yķeē~!X “Ą0)L “¤0‰(L†ÂÔāÂēË!LÉqG<nZJ—8^G÷9faLnĢŽÂDëTrÛFؙĻ:ŒĢË$LĄû]ŲãØÚ$&P˜ …IaR˜&…IDa2Ļϰ,ũ~;3Õ1ωnp´8ņ\¸šđ7&ō‡îŸ3¤ą.Ũ됰š„‰mķZēč1šƒũ&CaR˜&…IaQ˜ …ŠQ„)Jčbž ]ô’7TNvÉû&žgÂ$ŦMÚėĢ&aB–˜U29sĸÂd(L “¤0)L" “Ą0Õģ0%§û&ÂôŅLô¤ Œgc˛ūãÂdt‹c>°­0)D&a ûĄģ-HHO¸Ą2Ûa ã–čęĮLyŒĄbĸ –Ąb]ēíąŒuy-Ũōë¤0 “¤0)L “ˆÂd(Lõ~ãÚôŗÖ!1üŒHĪą 1b†ēp_/‡Žyü 3åņĘ´Ÿpžäņđ˜–Ģpī-Dˆ×†1Stģc9û %*ÜËÉ"K…´aS˜ …IaR˜&…IDa2&Ã× “¤0)L “ˆÂd(L†Â¤0)L “¤0)L" “Ą0)L “¤0)L “¤0‰(L†Â¤0)L “¤0 “Ą0‰Âd2Ą0)L “¤0)L†Âd(L" “¤0)L “¤0 “Ą0‰(L “¤0)L†Âd(L†Â$ĸ0)L “Ą0 “Ą0 “ˆÂ¤0)L†Âd(L†Âd(L" “Ą0 “Ą0 “¤0‰d %åm˙ßíĪũ„"‹ĸjũ ÍB˜ĸcÜgŲÉÆōķü§M)L…ÅşY.˛īšR\Vļˇ9ü&•ˇ+;üĘÆMHŗ(~ūīOĨŠJŠv7Yå\~Ū~ËDö•‰ēuú“Y´´ēœÔ{ĪewŪgR‘EqŪ‚…īG§æųfP|~;sŲŨxβ+Æ\:įߚ˛ü”ĩkŋÉkJvŅ —ĻĘ:tüĮæđ›tâIŨļ¯yzĨIiÅMšđO/5U™ČÉËųß÷?uįAĪEöDÕĨ*F xŨ,ZZ ũFŒznČÄķM*˛(:õčĩ%:5S›AņŠ:šbČĢžŗėŠōŽ74eų))+[6dÂy‡=ŲŊ úc3šĻ´9uPŸMžyŽIiEߊS>hâōS5bü°Íž‹ė‰a•§§Î™Ry‡Y´´zõī?„nyKō´‰EÄĩk9œ““CrSŪ ŠĪitË[ú“ŋūÂs—ąāū3Îā@ÅMY. ‹K>÷š’-הGSyųų‡šÉ5%.?eíĘžxņˇ›˜fA<ņOëRyyyŸ5qų9-ŋ ˙cËDö”‰ĸâĸ?7ŖkŠH}IĶ€‡;ØãĪ&8M/Kų……{ĸS2ŖŸĒĸ’Ōw•ĻĻųĢ˙ĮžH`ß͆ōC+Sû.]zMizY*)o{°°°pvsúMęÜŊĶŨ'öîvČšéãŌ˛’}Yō›TÕļ}ųnËDĶ—‰N]:rÖ ›ĖžĨUŌŗo˙wėvâ&8ʒŌÔübÎ]Ģ?Šd‰ÖĨąŲR(”&eIiR–”Ļ–U&:vîđE¯>ŊŽ5k–VM—“z¯éĐ­ûĄËīYcÂ҈1ũ–7cY:JšæŽüSJ7b0“„d›,%ĨŠm§ÎŸxMiܘqûĘTQiųæ*KIięŪĢëgk_XeÂڈqīcË•”īÍŌߤĒōvåģ~ôü*'hÄXõøōT‡ÚR–DB‚S^>̤ŧ힁•c-~äI“nUęrŌÉ[rrsŸÉîq05''įíŪŋąø¯ÖÛÚÔĀ1ķö•JÛļ{/úĖ˙9úėdsšČ/*zˇßYŖözMiøVĨžũ|TPTôF šĻÄå§´ŧt÷¸īV|î7?5ymā„žƒûė,(Čߘåågjn^î–!gWŧe™hø21´˛âķÚm/..o–,r4ÅeíÛ˙ûЌžq؟o}æg&#õ|O”~Ŗ*7GĸÄĖC‹ĸČoIe'Šģĸ÷ļ÷ĖķŋģŲ˛Ķ;ŦYw¸s¯ŪÛŖĪxSôYOj.å"77÷îü‚‚•Sg°\Ô˙5eȄķöGŸīžxM‰ËOaaūŊEÅEŧ|éŒ/ū~Ãķ&ŗõ|O‰—Œ}/??oO3*?ņoM^^ŪžŠW^¸Õ2Q˙eââË/8\RZ| ŧ]ųM-đš"R¯ô*m×á•Â’Ō?4ųŗĢÖŦ39ų1wåũûœ~ÆæÜŧŧũŅgûHZrŲ‰âųŧüüúõƂûײ ÜüÔKŠ ¯[ōÉ =zíhæĸŨĢ °đo ‹>Ž8gâ‡^Sž]ĐÕąīˆQģŅHHŌ¯)qų))/ųI܅ŗ&xôī0šũąö…U‡Ī?l3Đ5ãߤøˇĻ °`ī¸īVžũWŋüá!Īíˇ*Š ų´¸¤č`‡Ú=Û Ž)"õ{AĘĪĪŋˇ ¨øũ]ģī?ÁÂ?ßøøŗ&,uē ŧxúÛE%Ĩģsrr^‹>ËĒ(:ˇĻ˛ˇ8åäl+m×~ûØY—ŋeŲŠ{Ė^qߥH˛ˇEâųqôūĸ͗÷BÉo)å"z_;ÛvîōÁšķŽ9`š¨û5eˌ9{KÚļũ(Ĩ7[á5åHų)*.ÜÕ­g׏ŽŋķĒĪ_ū×õ&ŧuē°Íš~ÚÎōöåææĮ­Ô-ĨüÄe"7/w{‡ÚīXpķœm–‰ē—‰ˇĖųĸcįŸ”–•lmĨבzgBAqņĪō öTņáųW/úĶÂu?5‘IÄÕ>|xĖĨsļv:ąįÛtM‹>ŗĮĸaŅi3šĀ(ņ˙¸ëwúŧ1~Îü-QŲņ§i]ĢĻßrĮĄAŖĮo-,)Ų—›Ÿ˙ģč3[ÚÂŧ&äææž›—÷ižũˇŸ7áĮ^SŽŽk|8uΜ+?ęŌûäwĢ[¨ŊĻ$“ ^Ę/Č˙cňģ–Ü}íįOūę!áD<ökSķĢfŋÛûÔ^īЅ­”Ÿøˇ&’§ONØįÍkŋÅvËÄ×ËÄÂåW~Ū¯âÔ=…EŠŠ žôš"Ō0pÃ˛ŅÅ÷šŧü‚ŠĘĘ÷6b䮋—Ūö§Ö6đĸ‡žH7˙ē-=Nëˇ)ÉŊL|PũƒÄ CÅ•Ėe'ЧŖĪj'-—‘|ŋváÂ%[[[ŲĄĢŨĖe÷2é‚må:íŠû`ô™ü*úl–GQŅZËE$ģ KJ÷ô:lËE‹oŪׯ)“¯ŊūÃŪ+ļäî>wŧĻÔ­üDōũl$Oī—ˇ+Û{öÄáī.[{Ãg­mę§ũHjÉ=מ7xø€ÍQ2ŧ Ziųųęˇ&7ggqIņûCF ŪtķšÅ;[c™¨úÁĸĪÎ3ôŊâ’ĸOŠŠ wDߕ'ĸĪæ2¯)" ŗt-ŠęoŖ÷eíÛī9uØđmįĖžˇ{Öō{R-%á!‘™vķōŨ#/šöZžũ_'ŲÜ÷ŠÍĒž0Įĸp|e'ŠŸˇÉÉŲ_TR˛Ēœ:k͌ÛîÚÛRĘ]ÎæŦ\?wÁîūŖÆŧŨžs—ՂôŋĒéė6ŦÍX.ĸĪ铒ōļģ8vÆÜí3ŋŋō`KēĻ\zëŠO+§Īz§×€A›Åęqj^SęĄüЕ5ú]:ĐžcÛÎ:ጭķĢfŋˇzũ‡[JÂL"ŧō‘ÛöÎēî’מŲī’Ōâ"9đ7é˜×”œËږî:jđksޟžiÕcËöļ¤2ŊŸÃķn˜škčŲÛŠ<`BZō-"ŲĮÕĻ“Fĩ/¯ ā“N'öØF˛8qŪÕģ°L˛ĀŊd˛)a/Žkæ÷īŪ5zúėWŋsúĘ;tx‹¤-z/ģŖ÷ôJkĢ/<<Õ Wvĸø—HĸöP~Úuîōæ)Æo;ķōÜ¨5ËĮÃqQļ)ãƒF{Ģs¯ŪoįîĢ.?˙§ēĻ—>âc­Ų;ūrAwWÆvĩīÚí­žÃGmš0wū;LŦ’ÍהYwÜ{`ÜŦ+ļœzÆđ×Úv괕ĘĨę ¯)û›ôëÜŧÜŊ…Ÿô8šûÖ1“ĪŪ|Ũ˛y;™dķ•/fUĖ p×ęõ+öĪģiÖFœsÆĢēv܂Vˑå§>Ž)Ņo ]ģöčüæČ Ã6˝ēlãĪÜķQ6—‰5}×áĢo›ģsôygŊŨĩg—‘}Ŋ‡Ŗ÷ō– ‘æ ã0&T'‹$ŋ†QB|˜Ä§¸Ŧ|GĮî=ßę=øôM§Ÿ´qėĖš¸xÉ-īĪZ~īŽËîŧīĀŧûŒ“ŽŒ÷8V~ōõ$Sl‹¸āšë_õŊéVŽû¯žũūŽm§^§eƒ¤6:žĪŖcÛ'ë_δ´ēößŲb˛¨ėÄį(*;ČTQié;í:w}s9xĖø˙¨œ>ëŋ/\xÃëá|_ąęCßĻėPö(ƒlkĘĸwžtöæŠqįnĸ%ŦCˇî[(ģņؒčxĸ2´Ŋúø‚MŽĸģ§°qĘŒT—‹ũœ—]O|ŗ×€Áŋ¯71’íšo\tũMÛ8ŗīXõÁˇŊĻ ÆĄŒŅĨ”VŅÁcĪų}¯ūƒ6´īÜåEĨe;‘"Ž)ņqyMiŋIšÛŖsu8J<÷—ˇ/ßŅķäî›O?kāĢįM˙{dåļ–žģúÉģÖ

Merkletree Fiddler

You can enter numbers directly into the form below to update the Merkle tree, or you can mouse-over leaf nodes to show the inclusion proof and click on them to show a consistency proof from a tree which includes that leaf at its highest entry.

Controls

TreeSize
Inclusion proof for
ConsistencyProofFrom
trillian-1.6.1/docs/merkletree/merkletree.js000066400000000000000000000317361466362047600211510ustar00rootroot00000000000000const colors = { regularNode: '#f1f3f4', regularNodeStroke: '#bdc1c6', regularLine: '#bdc1c6', inclusionTarget: '#1a73e8', inclusionProof: '#ea4335', consistencyProof: '#fbbc04', consistencyProofOpt: '#34a853', }; // Controls the relative layer depth of nodes at different levels. const levelPower = 1.3; // This is our SVG drawing area. let draw = null; // Whether to render the node key. let renderKey = true; /** * Initialises the SVG drawing area. * @param {string} element HTML element id to use for drawing. * @param {bool} key Whether to render the node key. */ function init(element, key) { renderKey = key; draw = SVG(element).size('100%', '70%'); } /** * Renders the node type key. */ function drawKey() { let index = 0; const add = function(name, fill, stroke) { draw.circle(30) .stroke(stroke) .fill(fill) .x(10) .y(2+35*index); draw.text(name) .x(45) .y(2+8+35*index) .font({family: 'Roboto', size: 14}); index++; }; add('node', colors.regularNode, {color: colors.regularNodeStroke, width: 2}); add('inclusion target', colors.inclusionTarget, {color: colors.regularNodeStroke, width: 2}); add('inclusion proof', colors.inclusionProof, {color: colors.regularNodeStroke, width: 2}); add('consistency proof', colors.regularNode, {color: colors.consistencyProof, width: 4}); }; /** * Returns the largest power of two <= n. * @param {number} n * @return {number} */ function largestPowerOfTwo(n) { if (n==0) { return 0; } let t = 0; for (let i = 0; i<32; i++) { const c = 1< n) { return t; } t = c; } console.log('That\'s a large tree you\'ve got there, mister.', n); return 0; } /** * Constructs a new Tree instance. * @param {string} id Tree base-ID. */ function Tree(id) { this.leaves = []; this.layerSizes = []; if (!id) { id = ''; } this.baseID=id; } /** * Applies the defaultNode stroke to n. * @param {object} n The node to modify. * @param {integer} height The node's height. * @return {object} The node. */ Tree.prototype._defaultStroke = function(n, height) { t = this; return n.stroke({ color: colors.regularNodeStroke, width: new SVG.Number((height+1)/(200*t.numLevels)).to('%'), }); }; /** * Adds a new leaf to the tree. * @param {string} name Leaf to be added. */ Tree.prototype.addLeaf = function(name) { this.leaves.push(name); }; let oldInclusionProofIndex = null; /** * Clears the currently displayed inclusion proof, if any. */ Tree.prototype.clearInclusionProof = function() { if (oldInclusionProofIndex == null) { return; } const f = function(n, h) { n.fill(colors.regularNode); }; const effective = this._dropAddressToFloatAddress(0, oldInclusionProofIndex); this._inclusionProof(effective.height, effective.index, f, f); oldInclusionProofIndex = null; }; /** * Displays the inclusion proof for the given leaf index. * @param {number} index The index of inclusion proof to display. */ Tree.prototype.showInclusionProof = function(index) { const f1 = function(n, h) { n.fill(colors.inclusionTarget); }; const f2 = function(n, h) { n.fill(colors.inclusionProof); }; const effective = this._dropAddressToFloatAddress(0, index); this._inclusionProof(effective.height, effective.index, f1, f2); oldInclusionProofIndex = index; }; /** * @param {number} height The height of the node. * @param {number} index The index of the node. * @return {number} A formatted node identifier for the given coordinates. */ Tree.prototype._nodeID = function(height, index) { return this.baseID + '-' + height + '-' + index; }; /** * Calculates the inclusion proof for the given leaf index, and applies f2 to * each of the nodes present, and applies f1 to the leaf node itself. * @param {number} height The height of the node. * @param {number} index The index of the node. * @param {function} f1 The function applied to the node the proof is for. * @param {function} f2 The function applied to the proof nodes. */ Tree.prototype._inclusionProof = function(height, index, f1, f2) { f1(SVG.get(this._nodeID(height, index)), 0); this.pathToRoot(height, index, f2); }; /** * Calculates the inclusion proof (merkle path) from the specified tree node * to the root. * @param {number} height The height of the node. * @param {number} index The index of the node. * @param {function} f The function applied to the node on the path. */ Tree.prototype.pathToRoot = function(height, index, f) { for (; height < this.numLevels; height++) { index ^= 1; f(SVG.get(this._nodeID(height, index)), height+1); index >>= 1; } }; // Somewhere to store the size of the currently displayed consistency proof. // Used to remove it when the uses asks for a different proof. let oldConsistencyProof = null; /** * Clears the currently displayed consistency proof, if any. */ Tree.prototype.clearConsistencyProof = function() { if (oldConsistencyProof == null) { return; } this._consistencyProof(oldConsistencyProof, this._defaultStroke); oldConsistencyProof = null; }; /** * Displays the consistency proof from the specified tree size to the current * tree size. * @param {number} fromSize The size of the smaller tree to show consistency * from. */ Tree.prototype.showConsistencyProof = function(fromSize) { const w = new SVG.Number(10/this.numLevels); this._consistencyProof(fromSize, function(n, h) { n.stroke({color: colors.consistencyProof, width: w.times(h+1)}); }); oldConsistencyProof = fromSize; }; /** * Converts a node address from "drop" (aka Trillian) to the "float" (C++) * addressing scheme. * * @param {number} height The node height. * @param {number} index The node index. * @return {number} The actual leaf index * Converts from an "effective leaf" (zero height) address to the actual node * address of that leaf (which, in the case of non-perfect trees may be a node * whose height is greater than zero). */ Tree.prototype._dropAddressToFloatAddress = function(height, index) { // If we've been asked for an internal node, first adjust the parameters to // reference a leaf-level node under the requested node: for (let h=height; h > 0; h--) { index <<=1; } // Walk up the layers under we find a level which would contain the leaf. for (let eh=0; eh < this.layerSizes.length; eh++) { // Would the leaf fit at this layer? if (index < this.layerSizes[eh]) { // If we were asked for an internal node, continue walking upwards // the requested number of levels: for (;height > 0; height--) { eh++; index >>=1; } return {height: eh, index: index}; } // Leaf wouldn't fit, so we'll go up again - adjust the index accordingly. index-=(this.layerSizes[eh]>>1); } return {height: this.layerSizes.length, index: index}; }; /** * Converts a leaf index from the "floating" addressing scheme to the * equivalent "drop" (aka Trillian) leaf index. * * @param {number} height The node height. * @param {number} index The node index. * @return {number} The "effective" leaf of a "floating leaf" node in the tree. * This is required because we often think about non-perfect size merkle trees * as having leaves which "float" upwards to be closer to the root. */ Tree.prototype._nodeToEffectiveLeafIndex = function(height, index) { if (height == 0) { return index; } let zeroes=height; let m=1<<(this.numLevels-1); let ei=0; const maxIndex = this.numLeaves-1; while (zeroes != 0) { if ((maxIndex & m) != 0) { ei += m; } else { zeroes--; } index &= m-1; m >>= 1; } ei+=index; return ei; }; /** * Calculates the nodes which form the consistency proof from the specified * size to the tree's current size, and applies f to each of them. * @param {number} fromSize The size of the smaller tree to show consistency * from. * @param {function} f The function to be applied to the proof nodes. * @param {number} height the starting tree level of the proof. */ Tree.prototype._consistencyProof = function(fromSize, f) { if ((fromSize <= 0) || (fromSize >= this.numLeaves)) { return; } let index = fromSize-1; let height = 0; while ((index & 1) != 0) { index >>= 1; height += 1; } const effective = this._dropAddressToFloatAddress(height, index); if (index != 0) { f(SVG.get(this._nodeID(effective.height, effective.index)), effective.height+1); } this.pathToRoot(effective.height, effective.index, f); }; /** * Renders a tree node. * @param {object} c The SVG container. * @param {number} index The index of the node to render. * @param {number} x X position of this node. * @param {number} height The height of the node to render. * @param {boolean} isLeaf Tree iff this node is considered a leaf. * @return {object} the SVG node. */ Tree.prototype._renderNode = function(c, index, x, height, isLeaf) { const depth = this.numLevels-height; const cx = x; const dia = new SVG.Number('25%').times(Math.pow(0.6, depth)); const cy = new SVG.Number('99%') .minus(this.layerHeight.times(Math.pow(height, levelPower)) .plus(dia.divide(2))); const node = this._defaultStroke(c.circle(dia), height+1) .cy(cy) .cx(cx) .fill(colors.regularNode) .id(this._nodeID(height, index)); node.mouseover(function() { }); if (isLeaf) { node.mouseover(function() { const idx = tree._nodeToEffectiveLeafIndex(height, index); document.getElementById('inclusionIndex').value = idx; updateInclusion(); }); node.click(function() { const idx = tree._nodeToEffectiveLeafIndex(height, index); document.getElementById('consistencyFrom').value = idx+1; updateConsistency(); }); } return node; }; /** * Recursively renders a subtree. * @param {object} c The SVG container to render into. * @param {number} prefix The node prefix. * @param {number} lx Left-hand edge of space to render into. * @param {number} rx Right-hand edge of space to render into. * @param {number} n Number of leaf nodes under this node. * @param {number} height Height of this node in the tree. * @return {object} the top-most SVG node to enable tree-lines to be drawn. */ Tree.prototype._renderSubtree = function(c, prefix, lx, rx, n, height) { const xPos = lx.plus(rx.minus(lx).divide(2)); const p = this._renderNode(c, prefix, xPos, height, n==1); if (n == 1) { return p; } let ls = largestPowerOfTwo(n); if (ls==n) { ls = n/2; } const rs = n-ls; const stroke = { color: colors.regularLine, width: new SVG.Number((height+1)/(400*this.numLevels)).to('%'), }; const middle = lx.plus(rx.minus(lx).divide(2)); if (ls >= 1) { const cPrefix = prefix<<1; const lp = this._renderSubtree(c, cPrefix, lx, middle, ls, height-1); if (lp != null) { c.line(p.cx(), p.cy(), lp.cx(), lp.cy()).stroke(stroke); c.add(lp); } } if (rs >= 1) { const cPrefix = (prefix<<1)+1; const rp = this._renderSubtree(c, cPrefix, middle, rx, rs, height-1); if (rp != null) { c.line(p.cx(), p.cy(), rp.cx(), rp.cy()).stroke(stroke); c.add(rp); } } c.add(p); return p; }; /** * Renders the tree into SVG on the page. */ Tree.prototype.render = function() { this.update(); this.layerHeight = new SVG.Number('100%') .divide(Math.pow(this.numLevels+1.5, levelPower+0.1)).to('%'); this._renderSubtree(draw, 0, new SVG.Number('0%'), new SVG.Number('100%'), this.numLeaves, this.numLevels); }; /** * Updates the tree for the new /set of leaves. */ Tree.prototype.update = function() { this.numLeaves = this.leaves.length; this.numLevels = Math.ceil(Math.log2(this.numLeaves)); const maxIndex = this.numLeaves-1; this.layerSizes=[]; let t=1<<(this.numLevels-1); for (let m = t>>1; m > 0; m >>= 1) { if ((maxIndex&m) == 0) { this.layerSizes.push(t); t>>=1; continue; } t += m; } this.layerSizes.push(t+1); }; /** * Sets the tree to a given number of leaves. * @param {number} n Number of leaves. */ Tree.prototype.setSize = function(n) { for (let i=0; i < n; i++) { this.addLeaf('hi'); } }; /** * Updates the tree with a new size. */ function update() { this.tree = new Tree(); oldConsistencyProof = null; oldInclusionProofIndex = null; this.tree.setSize(document.getElementById('treeSize').value); draw.clear(); if (renderKey) { drawKey(); } tree.render(); } /** * Causes the tree to show the newly requested consistency proof. */ function updateConsistency() { tree.clearConsistencyProof(); const cons = document.getElementById('consistencyFrom').value; if (cons) { const c = parseInt(cons); tree.showConsistencyProof(c); } } /** * Causes the tree to show the newly requested inclusion proof. */ function updateInclusion() { tree.clearInclusionProof(); const idx = document.getElementById('inclusionIndex').value; if (idx) { const c = parseInt(idx); tree.showInclusionProof(c); } } trillian-1.6.1/docs/merkletree/merkletree_test.js000066400000000000000000000060411466362047600221770ustar00rootroot00000000000000let logPanel = document.getElementById('log'); /** * Logs a message to the current log div. * @param {string} msg the message to log. */ function log(msg) { logPanel.innerHTML += msg + '
'; } /** * Logs a "PASS" for the specified test. * @param {string} t Name of test which passed. */ function pass(t) { log('[PASS] '+t+''); } /** * Logs a "FAIL" for the specified test. * @param {string} t Name of test which failed. * @param {string} m Failure message. */ function fail(t, m) { log('[FAIL ]'+t+': '+m); } const tests = document.getElementById('tests'); /** * Runs tests. */ function test() { testConsistencyProof(); } /** * Initialises a test in preparation for running. * @param {string} name The name of the test. * @return {string} The HTML element id to be used for drawing. */ function initTest(name) { const t = document.createElement('div'); t.id='test_'+name; t.style='outline: 1px solid black; margin-bottom: 8px;'; const h = document.createElement('h3'); h.innerHTML=name; t.appendChild(h); const d = document.createElement('div'); d.id=t.id+'_tree'; d.style='margin: 4px; width: 400px'; t.appendChild(d); const l = document.createElement('div'); l.id=t.id+'_log'; l.style='outline: 1px solid grey; background: #eee;'; t.appendChild(l); logPanel = l; document.getElementById('tests').appendChild(t); return d; } /** * Tests consistency proofs. */ function testConsistencyProof() { // This proof data generate by Trillian's merkle_path.go code, and manually // adjusted for "float" vs. "drop" tree addressing. const tests = [ { first: 4, second: 5, proof: [[2, 1]], }, { first: 70, second: 80, proof: [[3, 10], [3, 11], [4, 4], [5, 3], [6, 0]], }, { first: 31, second: 32, proof: [[0, 30], [0, 31], [1, 14], [2, 6], [3, 2], [4, 0]], }, { first: 32, second: 33, proof: [[5, 1]], }, { first: 65, second: 88, proof: [[1, 32], [1, 33], [2, 17], [3, 9], [4, 5], [5, 3], [6, 0]], }, ]; tests.forEach((t) => { const name = 'testConsistencyProof_' + t.first + '_' + t.second; const drawing = initTest(name); init(drawing, false); const tree = new Tree(name); tree.setSize(t.second); tree.render(); let i = 0; let failed = false; tree.showConsistencyProof(t.first); tree._consistencyProof(t.first, function(n, h) { id=n.id().split('-'); if (i >= t.proof.length) { n.stroke('#f00'); fail(name, 'got unexpected extra proof node: '+id.toString()); failed = true; } else if ((t.proof[i][0] != id[1]) || (t.proof[i][1] != id[2])) { n.stroke('#f00'); fail(name, 'got '+id.toString()+', want: '+t.proof[i].toString()); failed = true; } i++; }); if (i != t.proof.length) { fail(name, 'got '+i+' proof nodes, want '+t.proof.length); failed = true; } if (!failed) { pass(name); } }); } trillian-1.6.1/docs/merkletree/test.html000066400000000000000000000010321466362047600203030ustar00rootroot00000000000000

Merkletree Fiddler Test

trillian-1.6.1/docs/merkletree/treetex/000077500000000000000000000000001466362047600201225ustar00rootroot00000000000000trillian-1.6.1/docs/merkletree/treetex/README.md000066400000000000000000000045061466362047600214060ustar00rootroot00000000000000Treetex ======= `treetex` is a tool for drawing/visualising Merkle trees. Ensure you have `LaTeX` installed on your system if you want to use this; the tool outputs `LaTeX` commands to stdout, and so needs to be used in conjuction with that. For debian-derived systems, `sudo apt install texlive` should do the trick. Usage ----- ```bash go run github.com/google/trillian/docs/merkletree/treetex | pdflatex ``` (if you've not used that much tex - that'll create a file called `texput.pdf`) You can use `okular` (or your favourite PDF viewer) to view it. Use `--tree_size` flag to, er, set the tree size. Features -------- Treetex has a few tricks up its sleeve: ### Ephemeral nodes This tool draws trees with all the leaves at the same level as opposed to floating them up to their real level - mainly because this is how the trees in [RFC6962](https://tools.ietf.org/html/rfc6962) are drawn. Nodes which aren't yet final are displayed with no border. ### Large trees When drawing 'large' tree sizes, set the `--megamode_threshold` flag (default is 4) to "collapse" perfect subtrees with at least this many levels. ```bash go run github.com/google/trillian/docs/merkletree/treetex --tree_size=100023 --megamode_threshold=4 ``` ![large tree](images/large.png) ### Inclusion proofs Highlight an inclusion proof using the `--inclusion` flag. ```bash go run github.com/google/trillian/docs/merkletree/treetex --tree_size=23 --inclusion=13 ``` ![inclusion proof](images/inclusion.png) ### Explicit leaf data Choose-your-own-~~adventure~~-data. Use the `--leaf_data` flag to specify comma-separated leaf data text explicitly. Takes LaTex commands too. ```bash go run github.com/google/trillian/docs/merkletree/treetex --leaf_data='entry1,thing too,${\alpha}$,${\frac{bananas_{\beta}}{\delta}}$,{\LaTeX}' ``` ![leaf data](images/leafdata.png) ### Explain-Merkle-trees mode To draw diagrams which explain what each node hash is, use the `--node_format=hash` flag. ```bash go run github.com/google/trillian/docs/merkletree/treetex --tree_size=7 --node_format=hash ``` ![hash mode](images/hashmode.png) ### Compact Range proofs To highlight nodes which make up (multiple) range proofs, use the `--ranges` flag. ```bash go run github.com/google/trillian/docs/merkletree/treetex --tree_size=18 --ranges=1:4,7:14 ``` ![compact ranges](images/compactrange.png) trillian-1.6.1/docs/merkletree/treetex/images/000077500000000000000000000000001466362047600213675ustar00rootroot00000000000000trillian-1.6.1/docs/merkletree/treetex/images/compactrange.png000066400000000000000000001161211466362047600245420ustar00rootroot00000000000000‰PNG  IHDRÖ”I3ō &iCCPiccH‰••gP“YĮīķ<é…@B‡PC‘*%€”Z(Ō̍@čPElˆ¸+Šˆ4EE\•"kE ‹‚tƒ,ĘēqQAYpߝ÷?ŧ˙™{ĪoūsæŪsĪųp ˆƒeÁË{bRēĀÛɎĖß(ŒŸ–ÂņôtßÕģ­Ä{ēßĪųŽ‘iü常ŧrų)‚t ėeÖĖJOYáŖËL˙ÂgWX°\ā2ßXáčyėKÎŋ,ú’ãëÍ]~ )ú˙†˙sīŠT8‚ôبČlĻOrTzV˜ ’™ļŌ —Ëô$GÅ&D~Sđ˙•üĨGf§¯DnrĘ&AltL:ķ5204_gņÆëK!F˙ĪgE_ŊäzØs ûžzá•tî@úŅWOmš¯”|:îđ3™˙z¨• €č@(U  t0–Ā8ā|AØø $Čš`(E`8Ē@-hM œā<¸ރÛā.L‚—@ہ‚°ĸA2¤é@F˛† 7Č ‚BĄh( ʀrĄPT UAuPô tēŨ„Ą‡Đ84ũ }„˜ĶaXևŲ0v…}áõp4œ įĀųđ^¸އOÂđø6< á—đ"Â@”]„p$‰BČV¤)Gę‘V¤éCî!Bdų€Â h(&Je‰rFųĄø¨TÔVT1Ē uՁęEŨCŖD¨Īh2Z­ƒļ@ķЁčhtē]ŽnDˇŖ¯Ą‡Ņ“čw †aaĖ0Θ Lf3Ļs͆šŒÄL`æ°XŦ Vk…õ†aĶąØJėIė%ėvûGÄ)áŒpޏ`\.WŽkÆ]Ä áĻp xqŧ:ŪīĀo—āđŨø;øIüA‚Ā"X| q„„ B+áaŒđ†H$Ē͉^ÄXâvbņņqœøD%i“¸¤Ri/é8é2é!é ™LÖ Û’ƒÉéäŊä&ōUōSō{1š˜žO,Bl›XĩX‡ØØ+ žĸNáP6Pr(å”3”;”Yqŧ¸†8WJ)Hq¤"ĨöHĩJ IÍKËIÛJGJJˇIK”aĘ8ČÄËė—é”y"‹’Õ–õ’Í’="{MvVŽ.g)Į—+”;-÷H–×–÷–ß,Lž_~NAQÁI!EĄRáĒÂŦ"CŅV1NąLņĸâŒMÉZ)VŠLé’Ō Ļ$“ÃL`V0{™"eyegå å:åå–ŠŸJžJ›ĘU‚*[5JĩLĩGU¤Ļ¤æŽ–ĢÖĸöH¯ÎVQ?¤Ū§>¯ÁŌĐØ­ŅŠ1͒fņX9Ŧ֘&YĶF3Uŗ^ķžF‹­¯uXëŽ6ŦmĸŖ]­}GÖ1ՉÕ9Ŧ3¸ ŊĘ|UŌĒúUŖē$]ŽnĻn‹î¸CĪM/O¯Sī•žš~°ū~ũ>ũĪ&   І.†y†Ũ†iņĒî¯&¯v\Ŋmu×ęׯ:ƑÆGŒ˜ĐLÜMv›ô˜|253˜ļšÎ˜Š™…šÕ˜˛élOv1û†9ÚÜÎ|›ųyķĻé§-ū˛ÔĩŒˇlļœ^ÃZšĻä́•ŠU˜U•Đšij}ÔZhŖlfSoķĖVÕ6ÂļŅvЪʼnãœäŧ˛3°ØĩÛÍs-¸[¸—í{'ûBûǃŸC•ÃSGĮhĮG‘“‰Ķf§ËÎhgWįũÎŖ<Ÿ×Äš˜šlqéu%šú¸Vš>sĶv¸uģÃî.îÜĮÖǝMZÛé—ˆ˙2Â6ĸ,b&Ō*˛4r*Ę*Ē4j:Ú*ú@ôLŒMLyĖl,7ļ*öuœs\mÜ|ŧGüņøĨ„€„ļD\bhâš$jR|Ro˛brvō`ŠNJAŠ0Õ"õ`ĒHā*hLƒŌÖ§uĨĶ—?Åū ͌]ã™Ö™Õ™īŗüŗÎdKd'e÷oŌŪ´gĶTŽcÎO›Q›ų›{r•swäŽoálŠÛ m ßÚŗMu[ūļÉíNÛOė ėˆßņ[žA^iŪ۝;ģķōˇįOėrÚÕR V (ŨmšģöÔą? ėYŊ§rĪįˆÂ[EEåE‹Åüâ[?ūXņãŌŪ¨Ŋ%Ļ%Göaö%íŲoŗ˙DŠDiNéÄ÷eĖ˛Â˛ˇ7ŧYn\^{ˆp(㐰­ĸĢR­r_åbULÕpĩ]u[|͞šųÇ‡ŽØi­U¨-Ēũx4öčƒ:§ēŽzúōc˜c™Įž7ø7ôũÄūŠŠQļą¨ņĶņ¤ãÂŪ'z›Ėšššå›KZā–Œ–™“!'īūl˙sWĢnk]Ŗ­č8•qęÅ/ĄŋŒœv=Ũs†}ĻõŦúؚvZ{aÔąŠCÔĶ)ė ę<įrŽ§Û˛ģũWŊ_ŸW>_}AōBÉEÂÅü‹K—r.Í]Nš<{%úĘDĪÆžĮW¯Ūīõ揿zíÆuĮëWû8}—nXŨ8Ķâæš[ė[ˇMowô›ôˇ˙fō[û€é@Įŗ;]wÍīvŽŧ8d3tåžũŊë÷y÷o¯ņy02*|ņ`úaÂÃ׏2-<Ū>†+|"ū¤üŠüĶúßĩ~oš /ŒÛ÷?ķyöx‚?ņō´?'ķŸ“Ÿ—O)M5MMŸŸqœšûbŨ‹É—)/f ū”øŗæ•æĢŗŲūÕ/ Mžŧ^úģøĖ›ãoßöĖyÎ=}—ønažđŊĖûØú>|œZČZÄ.V|ŌúÔũŲõķØRâŌŌ?B,ž“sMT cHRMz&€„ú€ču0ę`:˜pœēQ<bKGD˙˙˙ Ŋ§“ pHYsHHFÉk>€IDATxÚí{XgÚ˙īQT"VÔĨƒĩ´*Fc‰å +RQpã´Ũ­õ…ˇŨjĢŽ›´îJąu ¯=ēÛíBOŋŨZؒUĢ­âB*n5-QD­V%ĸRpÆųũ‘ ”$“d&“ÃũšŽ\ĩd2ų>OfîįûÜķŠĸAAī`ŅĸEĸ?ūņá>úččuëÖĨŊųæ›'~ųË_†ũį?˙ų ’oâŽhŦAÄãdff~VTTÔĒVĢåBĄ„BáĪ*,,„đđpuRR’jŅĸEßîÚĩ̚oáâN ąFAfķæÍ;7lØ0L?ŖĶéāĖ™3×RRR^€BžË€ î ž ‚ Â>s jūüų‹ÁS  !%%eŦN§+øøãkmũ<‚x+hŦAÄķīŲŗ§ÄbąØî“…BXąb…đ… —/_ū,߅BW‡‚ ‚ ˆą|ųōg7mÚôˇÆP;ÂíÛˇ›FŽ)ÃGA5‚ ‚x .›8qâ.NūÜsĪ]˙ÛßūöāĘ!2 hŦAÄCxũõ×ŋéĨ—æøk  ›ī˛"ˆ+‚cŦAÄ3˙ú×ŋæÚTƒZ­ÎZˇnŨ2ž ‹ ŽfŦAÄÍ!BøÕW_JII뤯T@"ßåFW3Ö‚ âæP%xøá‡eĒAĢÕʔJåžË ŽkAqsvîÜų<“U@ōķķA­VCaáĪ÷|QĢÕ Ķé@­V[=P(„ŧŧŧ‡ø.7‚¸hŦAÄÍ!Ir6“ãhã,“ÉîųģVĢNBĄ°īߖPVV&įģÜâj ąFA7gŊŒ­–Ëå —ËĄv[ĨRõũM,˜ŅîO}}=ģ e#ˆ€ÆAAܘ1cÆŧ¨Õ2ÛŗE§ĶX,ĨR $éØRÔLÍ<‚xhŦAčš~ũú)Ļۖgeeõũ[ĨR9ôŊJĨ‚9tņ0ĐX#‚ ˆ“””4ÆÚ˜hÃÄEsC<AßŋI’&!ķōō€ĸ(ë3ċ@c ‚ nLyyųaSclzl5I’@’$Čår I""" ++Ģo5­V ršõy‰G­įģėâjā1‚ âÆ!Đh45ŅŅŅa֎%I´ZíĪVĄhÅsŧūúëg^zéĨGø.?‚¸hŦAč B2_ä˛eË"ļmÛ6ÃÉR”Īw} ˆ+áÃˇAAĖC„~2Ō @ *Šĸ”ÆÃäPâ,Mjĩ>ūøã3EEE|W‚¸˜ąFA‚ :MiņĨĻ(Jgæ3Â÷Ū{¯æšįž t’LDđ]Wâj`ÆAAxÄh¤M3ŌZãKIQŖÅĻ†û¯ āZ¯Z­†3gÎŽZĩŠŋJC3Ö‚ âDŒk?ĶFĀ`ĸÕl,]÷ /īžûn0ĮEP@"Į߁ n kAáˆ~ éÅĄéa\Ŧ-€rŽĘŗyķæļ 6ĖCgA~ ąFA–0N4¤ĮGĶFšÎH;ŌŽ]ģļāųįŸĪb˛É‹-$ ?ūøãĶSĻLųÎ(‚¸#hŦAÄNú­Ø!C6š6ŌÖˇCäŽ<ĩZ­`ē&ĩ5t:üöˇŋUéįį7>&&&@.—ƒX,î{_§ĶZ­†}ûöuÕÖÖŪ Ē:|øđ w7Ų¸58Â&Aä€Í5≠ąFą‚RМSZZē-999,//ņįT*lܸą911ņĩ­[ˇnáģL1™hHoÄĸƒŸ†v ‘F† ˆ,ŖšF< 4Ö‚ XģvmÁɓ'ŗōōōîÉN3…$IČÎΆÁƒW˙ë_˙JpÅėĩqĸ!=ŦˇGœškÄAc 2AĸŖŖKĨŌH[˛ÔæPŠT››Ģ |\ŖŅāšl¸bâ šF< 4Ö‚  ‘HÎlŪŧ9R&“ąvN’$!>>^ŋdɒ)šššuÎ(‡™;č ŅH#ŧc4×rXŠOHw5‚ H?ÖŽ][0dȐ,62ÕũQĢÕđĘ+¯č:Á…vãDCzX‡Š‘Æ;—ÅhŽŗ Í5âΠąF1Ą¤¤Dô÷ŋ˙ũÛũû÷ûrõJĨēēē ßzë-‡ãŠˆ§`ĸ¤Ė\#n kAâââjß}÷]Ą=™B’$Ė™3§ķäɓcm5¸58âɯī<@s¸)>| @q$Éī¤R)§Ļ@ Ā[oŊåˇiĶĻR˜eéX“‰†Ļ;j âiPĨ%B %A šFÜĖX#‚‘H$göíÛ)œō}ąąąÍ•••#L˙†+v fŽ÷eßA\ŸqLMu~~ū€WĢÕ};0Z#>>~xDDÄëAäQND  ĩŠĸ¨DãK‰Ļņ6Œm•Pe4Ųâ ąF€ĖĖĖÔøøøáLŽUŠTg­V :„Baßŋ-!—Ë!888~2ŌK)ŠĘĮÕ;¤Ī\/ð4׈[€ÆAΜ9“ÁdÍjÚ8„JĨę{O,CaaĄÅs‰ÅbhooFAÆxoĖ€<4׈;€ÆAšššĻ15ÖlNn6lØ0žËŽ ތqŒõR@s¸hŦA`čĐĄC­ŖÕjY5Õ‚0Í5â. ąF€žžžn&ĮiĩZPĢÕ@’$hĩ÷Žā0øH’¤Ų!#‚ØN?smũņ‚đkA Ôö7Ęũ‹Å “É€$Zũ‹$Iˆˆ0ėNž••Õ7ŠQĢՂ\.ˇúŊ----|—A܅~æ:‹o=Ō\ĮA–/_ūėÔŠS˙ĻP(>—Z­ĻãĩW­Zu`Īž=RžË îA(Ê:…ŽžAØ3Ö‚x=AČ?ûėŗūõ¯u°q>&ϰŠČwß}Ad‚  (Ф(*ĘšF\ 4Ö‚xAˆ ‚PQBD9ļ W 2¤Î™:*++[oŪŧų´ņéMbpü(‚0„ĸ¨l@s¸hŦņx‚!'ĸĀh¤å 3nȒHoĘŌÕÕUnnGEļŅétĐÖÖvĸ¨:Šĸ )ŠĘ6fāT`0 %Ɨ‚ œ‰ f@s¸8ÆAÄ¸$— [„“ 5EQˇC”H$ˇŠ‹‹C¸^Ņ#!!Ą[¯×'i4šĘ ™ą bĐ_jã$.AŒQ$EQJžĩ Ū kA<cVWf| ā'Ēļå<ëÖ­[VSSŗ­ŧŧœ3­ųųų ŅhŽîØąã1;ĘHwđSgwnDč3×tAœkAÜ‚ h“)CVZ †Uƿϧ§‘H$ŗØX!¤?:ž|ōÉæĘĘĘpŖfGĘOw$Ä cĢüâÎĄ!šk„ĐX#â6‡wĐF’ĶŒ­X,žŽRŠBŲ’žžŪÕÛÛ+ŲĩkW5ËuCgė鎆]{ņŒã­ÅhŽgƒÆA—ÅdŒą ~2‹ZŠĸT\ˇD"™ÛÜÜŧkĶĻMÙlôb ­V ëׯīîęęúŋC‡ũŅ uG1§{t6[g˙YÄ}@sđkA\ {'r¤E––ļ×ĮĮgVAAÁ=[–ÛB~~>ü÷ŋ˙%}}}ØÎT3- ĐAœ‰x8hŽgƒÆA^akŌ!—Ŧ^Ŋzũ^ũ͟ūägKöZ§ĶÁī˙ûŽāāāÃüąËėŽˆ“ oÂhŽŗ ;’× ąFÄép5éc͂„„„ĸĻĻĻ鉉‰Ąbą2ŲZ­Ôj5ėŪŊģš§§įę˜1c2øČRÛX6œ‰x4hŽgÆAÎqæ¤Cg ‘HæŽ7nÕÕĢWéččō÷÷ÚĶĶĶĶŪŪŪ>a„ēîîîĘĘĘļņ­Ķp$âŠãP,EspkAX‡ĪI‡ģā$Hē@sp kAXÁ•&"܀“ OÍ5Â%hŦą w˜tˆp N‚DÜs„MĐX#Âwœtˆ8œ‰¸Fs]†Ė5vV@c ˆYR\\üļ;?ZÆI‡ˆˇãŠ“ “’’–ßŧy3ĨŗŗsũˇaÆ] Ņ–––ūīqįAD’hŽF ąF<Žøøø(ŠJ9räčØØØ š\BĄ°ī}NjĩöíÛ×U[[{cĐ A‡ĒĢ̟sõ†'"ˆeÜy¤D"ų<īįį7>%%%@&“X,î{Ÿ$IPŠT ÕjĄĒĒĒ!88øøūũû3]=nyhŽ[@cx ™™™Šĩĩĩ§§§‡( ƟSŠT°qãÆæÄÄÄ×ļnŨē…īr˜‚“Ä~ÜadII‰hëÖ­Ûãââ„yyyŒ?§VĢaíÚĩ2™ėoŊõV6ßåđtĐ\#LAcx™™™{oŪŧ™\PPpOvš)$IBvv6 <¸ē¸¸x:_åĀI‡ Ž8 2;;ûÍoŋũvõ–-[|d2™ÍŸ'IōķķAŖŅ\™={öėÜÜÜ:žĘâ Í5IQ”’o-ˆë‚Æqk‚ÄÆÆV-\¸PhK–Ú*• ŪxãFø•FŖ9ā ũ€“Äéđ9 ’ AttôaŠTŠP(@ 8t>ĩZ /žøĸ~üøņ¯îŨģ7ŸķĘķbŒæ(ŠÂ§Ȁ ąFܚ¸¸¸Úwß}Wh:ŅQH’„˜˜˜æŗgΆsņČ'"ˆkáėI)))‹-𛕕ÅÚ9I’„E‹ĩ ‚Ø]ģvU;§æŧ‚ ˛@Œæ4ֈے’’R1{öėšldĒûŖRŠ`ĮŽŦ ÁI‡â^p9 rÆ ›z{{˙dËxjĻhĩZXŊzuseeågיˇæ1kÄ-YˇnŨ˛šššmåååœ}ĮŌĨKa˘1°gB#N:D΁­I999á<ˇ}ûv_G‡˜#??~øá‡üą”īzķtĐ\#ÆqK$É­âââ{&*2…$I˜3gNįɓ'ĮZk@qŌ!‚xŽL‚”ÉdĮ•JĨȞ‰ŠļžžŪÕÛÛ+Á!!ÜC›kPbâđá[‚ØJ||üéé霚j@ú͟üūū÷ŋ@˛é{&âÄ!ņ`Œs!ú–\ŖįL €™IIIIËE"Q$×Ļā7Ū’‘‘Q ķ]_žEQ…Æßžœ ˆD4×kÄí (*‹qÕ!—ËáoûÛdŗ“ķqŌ!‚x/ƧRZ€{:Ü cfģoä‚ žyîšįüĄI(Bddä8‚ hô¸ĮhŽJ‚XŠuîŨ â[‚ØA‚‘#GŽļvœZ­­V *•ĘėûôŒÖH$÷ûųųU€ÁTĢ)ŠJ¤(j)EQ…hĒĄĄ(Ф(JEQT6EQ‰`Čl  āŌĨKSŦ=eŗˇ ™íO;<66vßuâ-7ŽQ‚Á\s3€q ĐX#nErrrVlllĨc´Z-¨Õj‹ÅPXXZ­ögīët: …}˙ļ„X,†šsįūHQ”Wō@„)EéŒÃÃ”ąąąŖ,k-nŒˇ%ĶmŠL&˙žëĀ›0>Ŋ@síå ąF܊;wîĖ’Ëå …`ē>l˙Ų÷*•ĒowFēŗ„L&ƒĻĻ&öĘFÄ̈]amlĩĩ¸`ˆEL …ĐÛÛÂwŲŊ 4×kÄ­ IRdíqĒ@ @……… “ÉėÚâŧ˙ųZZZœ26AĪC¯×GX[^í¸ĐÖÖĀwŲŊ‘~æ“2^kÄ#••:ŽņŖSK 6lßeBÄŗa;n!üæÚ{AcxųųųŸoXõN ülŦĸiæˆ$IF™ĄŽŽŽvžË… ˆįb-n!î‡Ņ\/€<4×ŪkÄ­6lØe&“ …B!$ Z­˛˛˛€$Iˆˆˆ€ŦŦŦžÕ@´Z-Xŗ ĐÔԄKS"bAAA­$iy6kq ā§ÕŒT*X;@OOOße÷vĐ\{¸ķ"âV,X°ā­Å‹¯1ä3tãdi˛Z­f4H­VÖ-[>ûĪūŗœīō#â~!V(Uyyycˇ˜B’$˟?˙ŦFŖÁMb\ãDÆ0ėЈ#<ĖX#nEiiéĢĻĶÚqĀjãÄ´ņRĢÕĐÔÔô!ßeGÄ=Ą(J[SSc5ÅĖ$n1E­VÃ<đ ßeG 7ÁĖĩ€Æq+(Š"ĢĒĒn:ķ;<ØŦŅhđ]vAܗÖÖÖsֆąąÉž}û:U*U>ßåF~ĸŸšÎrô|ˆk‚Æq;FuĘY3æĩZ- <ø ßeFÄŊš}ûönĻģ&: I’PQQҌ;Ãē&æ: Íĩg‚cŦˇƒ ADDÄĨīžûn˜ĩĩa%&&Ļíȑ#“)ŠĒãģÜ‚¸/AČÆW˛{÷nXĖíH€ĨK—‚Z­ū’$ÉߍâbĮ\į€Ö¸:â!`Æq+‚@š@ ؕÍéw)•J6lX|€™Aė A% ›7oŪ“ëׯīæōûT*ôôô%Ir+‘‡;ēE‘Ee€ÛĪ5â!$ĸdøŨwß=9xđājކ„¨ÕjĐh4WūķŸ˙<†ĮvB‚ JŒÆAÄ"&1K†• ”˙üį?÷<đĀŸ+•JNž“$IxįwšwėØą€ĸ(5EQK@åA(øŽäį šöhŦÖņöq‰L!B†Ž‡Ō›3ųÂøDÍ~°îøÍĩkƒÆa |Œj;&“ ŗžąÎ„cúe]ącëũ˛ũ8IÛIÍuÚ |ÂâB ąF3Žƒã¯Ä9ā5nĀĊķ1šë0dŽŅ\ģhŦ‡0i¤ qŦãāøkáglî‚āŠ*ÎÆ¤žqn€‹0ˆoˆ{B„Œ ˆrR•ˆĻš(ŠRS5H‚ njFA;1U`XM5‡PĨŖ(*TP@DžņŠ&ÂƎËRČ3f°žÁŒ5bũļŧUâîč7ūŗb¸š‰k€ŋƒs0ļ%€mī ąvS,X ¸sįÎ,’$EôßüüüšĮŽûĩ¯¯ī?wíÚUÍæ÷ĄÉãŽ;3A“““Ÿollˇ´´Œ§˙.ĒGŒq´´´´CÄ‚ˆDIjiiwt´ö]SūūA—CC'ü÷đáŋÍÕ5…C\gm2“™™™zæĖ™ŒÎÎÎYĻ÷ķķ;úđÃíæģ.¸Íĩk€ÆÚX°`Á[uuui'N$“É@,ūéÉI’ VĢA­VCUUUƒĪß~ûíbG0“€ˆk–ōˆiæ‡ĸ(Ĩƒį$$$uvvNz衇~!‘Hüd2…žct:¨T*Đh4]ĩĩĩ7†žīāÁƒ˙Ãw= Ž A‚đđ¨ŋŨŊÛ'θoâę~SĻHaôčđžcnܨƒS§*ā…ĒNîØÍAƒēxņDKߏ“įÜ.&J$’š]]]ī„ĮĮĮ—Éd “Ũ;ŠŽn<ØÜÖÖV×ŅŅ‘sæĖ™/øŽļ1šërĀšŧÆÚ P*•sJKKˇ%''‡ååå1ūœZ­†ĩk×vĘd˛ŧõÖ[Ųļ~/›FaG;:ĢW¯^ŋ˙ūœœœœ@š\Îøsųųų°cĮŽÆ¸¸¸Ū|ķÍĪøŽÄuøõ¯7>ĢÕî}õņĮW‡H$iŒ?§Ņ넝žÚÚ(/øĶįŸozߞīÆåŪÜ7™¤§§ī€}ôҁ€ŲPn’$á™gžé€c;vėX†§šk~Acíâ¤ĨĨmommMĪËËģ';Í’$!??4Í•ŲŗgĪÎÍÍ­ŗö“Ą¸Á‹‹bĪĐ‚ O<ņÄūŪŪ^QAA0m„LŅét C† 9°gĪ)ßõ€đOttę‘Aƒ|f­\ų.ÛüyŊž >üđ¸{ˇįčˇßî~Œéįp™O÷Į‘ßpŨēuËžûîģÂUĢVؒ 0EĨRÁ_ūō—ļG}4Ë“A€!1†æÚ‰ ąvQ‚DGG–JĨ‘ļdŠÍĄVĢáÅ_Ô*4Í{æžpŗˇĸßãoŗã¯%É\Ŋ^˙•­YjsäįįCQQQ}MMMvŧŧ“‰gÎíííų×ĸEkCmÉR›ãË/߆#Gv4 ėķą Uˇ[j{ļ>uˆŨéįį—˛}ûv_{ϐ$ K–,éîėėÜSYYéø…ėb šv>hŦ]‰DrfķæÍ‘ũĮ‰9I’đøã75jN˙ɍ¸Ŋļ{ciy‚ 111u_}õÕpG!S´Z-ŧđ ēC‡Eđ]~Äš!ˆˆŸ_ĩę“Ķ1ԎrãFŧķΓ /V1ķŊ¸š‹Ãdœü‚ QQQyl$œLQ*•PSSŖÜģw¯Įí€æÚšā:Ö.ČÚĩk ¤R)ĢĻ@ ĀÖ­[‡_ŋ~ŊĪ8×x­ (j&šj÷ĸūõR0ëŖ€äääĘÜÜ\VM5€X,†…  333÷ō]~Äš<účÂŊ?žšUS 0zt8,Z´64::õˆéß ‚Č2Æ)qŨ|4ˆq ėlČÃØFŗ %%%ĸŽŽŽWŲ6ÕyyyĐŅŅņjII‰ˆīz`cŠ 4„c0cíbäää„īŪŊûüūũû}Ø6B4JĨ._ž|¨¸¸¸pmQÃtÜââŋ?ôĐCé\4D4‰‰‰pß}÷-ōäeŦŸ˜?˙×75Ũøŋ^øgßņîģŋ˜ō\IÉĢį×@öZú¯}ūoû[ˆ=ķ˜ ÕjášįžkÔh4Ŗø.;3×´ŅF8ĩ‹WûĘ+¯ŲÎV÷göėŲŨˇoßN;uęԞˌpAĸ‡zčč‘#Gü¸ę¤&4fddxlc„üA‚qã&ÕåæĒ‡Û3Q‘)z}ŧür\ë͛—?€Mh¨Ŋ‚ d÷Ũwß+VŦ˜Æe’xĒŦŦüĐS—%" O~Đ\sq!$Éīâââ87ÕoŋũļīŨģw˙Ęw™Ž~÷¯ũ+§Ļ@(BzzzH||ü|—á–É“įÉå85ՁÁ™ųjФI’p4Õh###äÚT†„‘éŠÛ°—ÎÕŌŲk„}ĐXģ~~~Ofee9~"ˆÅb˜8qâ(O LvF' @ĄP@WWWßF¸E¯ošÎÆ LHŌĀĮg7Īüˇ"999kųōåÎúžå˗ÄÆÆŽãģÜ\aœŖ€æš#ĐXģAAA“Lwžz÷(•Jeö}Njĩõ9ˆąąąAÉÉÉÎqōˆS‘H$sĨRiˆĩãŦ]O……Ė扅†††aGÍs!Blí¸S§*āÔŠ ĐhvÚõž)ŖG‡6ŧ†x'­­­iL’–â™Z­­Vk1ÖŅČd24hs˛<æš;ĐXģAˆŖĸĸ,š’ÂÂB “Éúˆ)Z­t:…ž[B.—ƒŋŋ˙bžËްΏqãVYkˆŦ]O`Õt›‚5ĪF$Jz~Ú4™ŸĨcöī˙0eŠNž<§NUØô~&Nœé'%=ĪwŲ~éėėœh-éd)žiĩZPĢÕ ‹Ą°°´ZËûi …BčėėœČwššÆÄ\—cR„=ĐXģqqq˙kmĻŗ@ č3ˁāgÁAĨR|čb ĄPĩĩĩa|—aŸ~øa†5cmíz0dn˜"“ÉāΝ;ŗø.; ­1SĻXŪl300nܸØ÷ī‹Ģmzŋ?Itt´Æđ]v„?‚ÆĮĮZ;ÎR< …`:Ė’Éŧ“øøø@oxZb4×*@sÍ>| @ ´ļļYģŲMwĖĶétĀÆD˙Ą|—a___Ģ÷6ÛדX,’$E|—ᆖ–ÆPk“MĮ_߸QķæåÚô~ƒĄĨĨ1”ī˛#ŧ"ôņņņˇvĨxFˇ­……… “ÉĀZöĀøB0ė āŅPUHŊÂRœ0ė˜ąvC AĄP0 ˆwbK‡ ¯'„möī˙RS׀š dŦŊ öb.ž ČĘʝNĮxx›7aÜN s™k@cíf¨T*‹Å ‹öčŪ4ãM’$%/ϧ§§‡Éq–Ž'ąf'L˜0&L 8ÔÃÚûb/æâY~~>äįv*77ė  (J hŽĩ‹Ôjí•JJĨ”J%$&&I’@’$DDD@VVV߄ ­V{ĪŖ1sttt´ķ]v„}ÚÛÛ­ūŽÖŽ'€ŸV™QŠT@’–Ÿęt:Õ|—á˙ ŊžÉâ1ÍN(.΁ââøķŸ^O‚^ß/žeö}KčõMāëë×ĖwŲ^!{zz:Ŧd)ž‰Åb …@’$hĩZ°aY[¯æÚqpįEÁ¸&$ wīŪŊÆwšųÄhĻ—‚!s5Ųkĸ§§įĒĩĮílRYYŲ\YYų&ßåFا¨¨h7[5ĻÔÖÖŪĀŲäžKU՞ę“'÷78ķ;uēc EyüĒ ˆe:::ö83Q RŠ0é}æz&䥚fkB¯×JO°āNĩĩĩˇĐy.mmmuΚ¤ŖRŠ`øđáûø.3Â-ƒûü`mSļ8uĒƒķ]f„*++?ūę̝ŦÎCb‹mÛļĩUVV~Ėwš]ŖGX hŽƒÆÚ…8uęԟKKKëa†VŽ\ŲŨŨŨũ ßeF¸#55uҚ5kÚ¸ū’$á7Ūh)ų 4×ÚÅHNN^ŽT*9ũŽüü|Öj4š|—áŽÜÜÜē™3gnãúzĘÎΆÔÔÔ?ņ]^„{(Š"gĖHūŋŨģßæô{Š‹sāĻž‚OÔš;w.ųúë¯km N_ũuÃΝ;—đ]^WŖŸšvŪŦx7ĩ‹‘——÷ß   \™!NģwīnŪącĮžËŠpĪ[oŊ•­ŅhŽpõ¤°°zzzŽnذá}žËŠ8‡;ˇlŧuër5WCB4šĐŪŪ\Ŋoßߡđ]VÄĩHIIÉZšre7Wį˙ũīߕ’’Âx->oŖŸšÆz2kdįΝK¸˛råĘžEā…ësz+ŗgĪžũ›ßüFĪöÄXN ;vėxŒī2"Îåȑí *՟›Ųĸ×7Áöí¯ëŲžĀw×ã…^Øíëë[ÂEâIŠT‚^¯WŊđ ģų.§+c4׉ Gs=0hŦ]”ššš9O<ņD[cĘ´Z-<öØcŨ---[qˆw‘››[×ÛÛģ\"‘´ą5ŗ^ĨRARRR‹Ī|—q>E‘ííw–ŊōJ’ž­ĖõŠSđĘ+Izx‡€ æ(++[õ駟ę”JĨÕM̘@’$(•JøôĶOueeeĢø.Ÿ;@QIQT"ˆŅ\˙4ÖŽ‹ė… ģžų曉‰‰āȸ2ĨR ĢW¯nŽ­­ŨPUU5žī‚!ÎįėŲŗÉ$Ižļyķæ+Žd{H’„ĨK—ÂŽ;Ēkkk?úöÛo'ņ]6Äų!¨¯?ûAĀ˙ū÷ëēââ°7{­×7AqqTTüŗúĮĪ>n8/îø†üãuQrõęÕĨõõõĪ-Y˛¤Û‘dZ­†„„„žļļļ?\Ŋz•^ŗ¯=†P• hŽkÄ8ëVĘ={öHįΝû\FFFŖ­ŲkNééé]—/_.­ŦŦqãÆ- &ĸ€ī2"΃ 7oŪ|Ŋĸĸb|WWWáėŲŗÛlj¤RŠ >>^?f˘?O§(j `Põ:hsĘ+WÎ|õÃG"z{ģ _}5šĶÖėõŠSđęĢɝmmMo>üīéEÜNĶëŽĸ(íļmÛŪŸôĘ+¯ØœŊĻŗÔ¯ŧōŠîĨ—^ztëÖ­[p+oû@sũspKsÃhĒķ°č=‘búôéŃ ’ŠDĸāųķįûÉd2îŊ˙Õj5¨Õj¨ŠŠ!¯]ģveüøņŋŲĩkWuŋīČ’ĸ(ÜzÚÃ1;ą1ø™ū]_ÚÛÛûp||üp™Löŗ]ÉH’ĩZ Z­ĘËË‚ƒƒīßŋ?Ķôēėߨņ]^„{‚(€BŠĸTũū.ˆˆáį0!"bæũBĄÚū\ŖŲ :]5ÔÖV]éėlģX[Ģ]Ü?Ö¯[EQKų./Â?ÖâŒD"ų<$—ËA(ŪsŒN§•J•••­ õđWFķŪßeļ FĖcLؑEqģ •€ÆÚ…`jR‚&''?ßÕÕ%Ŋzõj°é{cĮŽ=;xđāĪĘĘĘļYųŽĐR…[P{(ta g‰D2788xå•+WbM˙>lذŽ!C†9rä{ũ;gũžÍĩ—Ā4nLœ8sn``đJŊžIÜŅŅęO˙Ũß?¨#00XĢ×7}xáBÕ+ß5`§ņ.ŒņĨ ņEmíØäää,‚ æ_ŧx1ÜôŊ &ÔQĩ¯´´´Đša6ÆÎHDsÍúi¸ˇßŗhŦ]>Ė AUfČķpvÖŗ<žqã0&Ąˇ7ÔŪ A% vvČØą“[KL ÷‚÷,k—ÁŧšŪĐXķˆI&ˆ×Y´F–S†¸)ÆĻ˛6Á‡kŒ•4v7ÅtŲOžĩÉÃnobž… ÜáJĻšÆ›Mĸ#˜Ô[•7%îpŒ5OĐŗŽ)ŠšÉˇM.‘BlĮ—PtÅa†ĢŽ—Įy!ž1Ž \ĩ ru}ފŅ[d—Ŧ˛‚Æš\ĩŅ2jËā;‹Ž0Į•;D¸ōŒûáęK™LÎM¤(Ęū-i—•ãX?˜0°Wö=lƒCAœ Ÿ“˘`4ÔËč!ÃFŽÚ%@>žwŒņ) !—‹O÷Ė )đĻĮ˞Œģ˜jœLk/Ū´ŗ%k'âęϚÆdĸœo-ˆyŒf5 cO]ãuž h‚\wfáM´§ãNϚÍĩ}xË}‹ÆÚšđēbƒd@f]‚ „`x\īŌ4ããúlđđ`ęĐģÛšC|ÂI×€;šj4×öŅĪ\ =Ÿ+‚cŦ„;ŽË2ŲJv)ŽetÜ)ŗØOˇ ¸“™ëáŽņÉDģۚ3oÆSv6äkgHwĮ˜´ŖŸāģM;ƨlhŦ­ŗ`Áŝ;wf‘$)ĸ˙6lذŽ!C†ęęęúLŖŅ°ôywžČtÂA‚˜_ÅŦi¸Ø0§Ŗĩc<ũw_?ßæā1Á_öüĪĒ=UÕ|—‡o‚&''?ßÚÚsëÖ­PúīÆ ģĸ---ũĢĩNŒ;Odj‚-Z$ęîî~ęÚĩkķ:;;‡÷̧ŊĨĨĨ…ŽžŠw‰dî!C–uuuÅĩ´´øĶÕ#FŒ8ēwī^ĢĢĸŗŠ6)csíh<÷vĸĸæ-īėÔ/Ņë›DĻ÷õõ;:nܤâĘJÕnkįpõ ˛ļ`’€R2Yę4333õĖ™3ŗL˙.ǎ—••mãģLÎÂä:`ܞąķ8/끉‹‹{­ģģ{IhhhXlllL&ąøŪQ*• ´Z-;vŦĄŖŖŖN üīŽ]ģĒMņ„lŠĨĨ#c"?honŸ* =M6mČé>ēī}}“NUœ‚“N‚a°Īājĩĩ‹Ũ=˜ÚX‚„„„ĸģwīN …Ŗ%‰Ÿ\.ā§Q:Ôj5h4šÎ~øáG??ŋsû÷īĪė_Ob‚ėh!ˆŽŽūĸ§§įĄÄÄÄPąX 2™lĀzÚˇo_WmmíáÇī;xđā˙đ]&g˛hŅ"I’īėėœ(•JCÄb1Čå÷N‡Đjĩ VĢĄ˛˛˛õ… ˇ† ö†FŖy¯˙šÜšĶoKYâââ^ģ}ûö˛‰'Žr$ž{+QQķ–ˇĩŨŲāëë7ūÁ%ŗfĨÁ„ ĸ{Ž9uĒNž<gĪnîîn¯<Ø÷… Ēô?—'Žaí)ĸD"™ÛÕÕõn@@@x||üp™L2™ėžc´Z-¨T*8|øp[ggįå#Flö“Íä ,ķüũũÃg˘Ę$æ9ōŗC‡ũ‘—BQ…/“×įŸ.Š­U(”-TUUQRŠ´kņâÅÛés€ Jø./0L’+ ˙Áķ –Eˆ#neäfPEÍEŒ_/īz™?y|Įŧ§įŊÉw™œņZĩjÕúČČČ;%%%6]O%%%TddäUĢV­7ų ‹ī2ąt=ŨS–5kÖL:ĩŖŧŧÜĻzĘËËŖ¯+Š9|—ɯŋo—JĨ]UUU6ՓBĄ bcck?˙üs‘ÉopĪ=í ¯ū×›ņÜ_ééĘđG]xdáÂ5Ô\ϊŠšŊ>øā2õČ#ņ]>ēđHQQŗĀä÷ƒ!ģ+āģl\{tæZlōwAZZÚŠTÚU[[Ëøúģ}û6ĨP(¨´´´#7n įģl<Õë1ĪY/ĖX›°víڂęęꧡlŲâÛ?›ÁĨR lž~ũú˙ętēāYŊō,Æü*&Ēšą9yåģ+īÉN3Eߤ‡Ũoī†Žü ›2wĘŧí¯o¯ãģlԕā‰'žØßÛÛ+*((¸'ëĘ’$!;;zzzŽîÜšķpķ'ÔQyxxø˙ ]"‘ܯP(ėĒ'NJĨēģģwėÜšs ßåâ‚E‹‰nŨēõßøøøáyyyvCĢÕÂʕ+{ĸŖŖˇūv=ŅĐOuÖŦY#f+ž5jގe¯ãâ~ũl}ũŲ­‹¯ķ‘HŌė:Į—_ž UU{›|đŅįöîũëYđ°LuLŗ¯k׎yë­ˇœëĐXCß#čÃRŠ4ŌŪĐcÖáããķŪwß}÷{žËĮAF‡ūaŪĶķî[¸fĄÃį;Uq ūŠø§Ū/Đīņ Uđ]>ļH$s›››wmÚ´ix˙ĮUö RŠāå—_&GŽ™îIã?cccŗŽ_ŋūNAAÁĐūEí!??ŠŠŠękjjĸ<ŠáŽ‹‹{mȐ!pÄ Ō$ ųųųP\\|ũōåˏxR=Ņ>~üø#c؊įkÖŦiģ{÷îÛŧ=Zv2SĻĖ- 9åĘw!00ØĄsŨ¸Q……ŋëž|ųÔųÖV2ί9S‚Œ9ōĐ´iĶüđÃ}…BĮž ,ûžūúëdžËĮ%´šŽˆˆh7n\ÚÛoŋāhĖ0t+**Î~ûíˇ1θūĐX@JJJÅĸE‹æfeąģjNBBBˇ^¯Oō3ôĀ”ŽdŋŸ6A4ĩsę›ô›”Ģ^=Å2×A"##ë><ܞėĢ9H’„ųķį7j4šQ|—‘ rrrÂÕjõ‰¯žúŠÕzŌjĩ°bŊú'NÜĪwŲ 333õúõë˙Ūŋŋ/›į-,,„]ģvØŗg”ī2˛ —ņ|˘1ŋ***˛:9Ī™6MļmōäŲË.\Ãęy ×I’×sĒĢËxŸ\Æ% ,PŒ;6÷Ŗ>ōcķŧųųųpôčQ}*G3yōäĮ‡žãȑ#nķŧ~ë 6lš:u*ëAāÃ?ôõņņŲ†ņCnMŦûŒ“˜6wÆ ›¸.„W뜜œđ#GŽ(í˙d ĄPŠŠŠÃĶĶĶ÷ō]VGX´vŅœæÆæäŒÜ NÎ?E:"fFܟ˛*Ĩ€ī˛:ÂęÕĢ×÷ööŠØū1rš|||fšģJKKÛN¯úÁyyyĐÚښŽT*įđ]VGHOOßûâ‹/wôQ˛9 āāÁƒÎÉÉ įģŦl@Įs6† „§ÄsKčtĮ —-Û<„‹sÃ˛e¯úœ?¯9Ęw9šâ›ožųf˖->l' h>üđCßīžûÎmW„˛FzzúŪÔÔTÎb^^^9rDÉuĖķjc]YYšķå—_öåę&0ô2[ZZfeffĻō]^{šPuáSŽL5MFnčŽé–/yiI8ßåĩ‚ û÷īĪ)(āļoPPPåååt× -Z$ēsįÎBŽĖM^^”––ēíRU™™™Š>>>ŗ¸ę¤ž‚ŧüōË>'Ož,æģŧlpōäÉbgÄsˇŽįæ˜2enéôéŋ =:œÃīBx¸(46VÎyÖĐŲdffn’H$÷ŗ1_ÄBĄRRRæÍ›WĘwyŲ&333ĩĨĨeW‰N€ž˜į[YYš“˲x­ąNJJZ.‰"šŧ h āôéĶī9~&įķĐcŊ6M6í~ ēyOÛ{lße„„ĸœœœ@.uC`Ø´iĶđ„„„"žËlׯ_/Ū˛e ëųúC ™2eĘË|—ŲŽ=ú!ם4ÃS@0]"‘ĖåģĖŽđđÃ/ …"ŒįöA„°ˇˇ{6C@ú“šēęęj^āģĖlsüøņ¸4…4 …ēģģg{Úvā§OŸ~Ī1O&“H$ŠLJJZÎÕwx­ąîíí]öÜsĪų;~&ë…B˜9sæ}îx#ôv÷.ac&LMߥžá|—ŲšššĻs™]4E.—ÃŨģw'ķ]f{ álH >Éw™m… azzú0Ž;i4Ī=÷œ_PP÷Ž€Cƍ—‘‘á”x.Ü6ž›ãĄ‡{é—ŋĖpÆwÜ9ˆOœ8ķw|—›-’’’–˙öˇŋe}Έ9V­Z÷ßåf ‚ „3gÎŧĢ! ũÉČČđīííålŦē×kã.xV+,4?œI­V÷ígųķįûÉårˇkŧ|ũ|Į39n˙'ûzŸ&2&rxÔŧ(Îz’\` ĄLŽĩv=ŠÕjPŠTVĪ# Gģ[ޔ”´|Μ9ŒZkõDīRf @!!!Œ~W"))i“D"ad-ÕM~žåEÄb1455Mįģ܎p÷îŨÉL:l–ę+??Ôj5Ŗ:u×xnŽžžŽ¸)S˜-–°˙'ū]¯of'œ:Uz}“ÅsL™"…aÃB~ÅwšŲĸˇˇwͧ%]_ôZüôËÚ5(“É ĢĢ+Žīrŗ…\.W˟?ŸŅ„OKuCīžh͓‰ÅbN“S^iŦ ‚Nš4Éęr\–LŽVĢNBĄ°īߖÉdpéŌĨŲ|—Ũâ~÷ėô_NˇšÅ8Uq Ž~qÔî÷M™•6 û ~†ī˛ÛBrrōķL‚ĒĨ늰°Čd2FA"‘ø%''?ĪwŲmÁ××÷G뉜bą AĢÕZņú’$LęŪãš%‚‚F†2Y¯úÔŠ 8zô‹ßÛŊûmHŌāäÉpãFÅķL˜ ‚Ļφ‡ų.7[455MgŌą3w ętēžø/‹­n”åŽIs\ēti6mI’ “É€$IĢmäI“îį*9啯zōäÉO0š ,ũĐ*• čŒ7ŨČ[ÂxŖÜĮwŲmáĮŗ?Ξ"bõ8kĮ09Íҏyéf8ßeˇ…ÆÆÆdĻą9A_įL X ršZ[[cø.ģ-\ŧx1ÜŅûN(‚éRjÖ ™L7oŪLáģėļ0bÄ“GĘÖŽ9ēãĪąX “'O~–ī˛ÛņĀpOÉårFuæŽņÜAˆĮŸÂ¨Se.ĢŊ˙'§NU@ję˜0Adõ\ãÆ=8Č];sĻ!x衇y)s× X,…BŅg ™ +ŒŠŠÁũ¤įp1¯°°H’’$­ļ Ƙ÷…ņJcíëë;ÕYcyܙ}‡Ø“ûãä?Œī˛ÛBKK‹ŋŖcëčFĀ`ˆŦV@ˇnŨřŰaÃū](,,™LfÕÉd2ĐëõÎÔÍwîÜņaã<:˜Žg …āëë;•ī˛Û[ņœŽ/ĨR $éŅ›ۃ@0våÔŠs:GCC44ÔÁ”)R(.Îąšą š˙>pĢ{Ķ âņãĮ;ÔÉĸۏÂÂBFĻĀÛ~ņ‹_xôf1ļ ‹éųl@’¤ÕļË˜į•ÆaÆ`ŸÁœ¯Ū€ÜKaa!( ƙFoD @VVčt:FãŅ}||ŧî:ÖjĩŒM5bĀôI“ëĘS1âž`6Î#Š`ęÔšđõן8v2/…”ûîcįˇķča\åååŒį*qk;1Ŋø™ôŽÜ‘ŪžŪnž5x*• Äb1ˆÅbĢCAŧ•üüüžÉxL†ĖôôôxåuLGg2ŪĐÛÉĪĪg4iŅšsįf“Ŗį ī›°hmâ"20:ÎæûôæMĮ;O6ÖBĄĘËËyy^iŦk™<ęŖWũPŠT}ãv"""Ũ 'iĩZƏoÜ ˙@íÅę‹V;Uq n\ŧšĐ7éAߤ‡Ŗ^4ûž5:Z;Zø.ģ- 6ŦÃŅëIĨRõÍOLL´ú(š$I5jTßeˇ‘›L˛TOôã>Ú,ZÛēZĢÕB`` [šĘ#Fô°QOôxM&čt:čîî>ÉwŲíĄģģû¤ĩÉãÖę‹[M˙Ių䓷÷îŨģ–īō3%%%ĨbĶĻMsĸ@›j&÷RŠ„cĮŽ=YVVæ6ģ0J$’3ûöí‹t֚¸ŲŲŲPXXAQ”Ûļâš-ו;ÆsKˆÅ Z~˙û΃=ĪŠSĀtŲž×__ÔPSŗ ßegŠTzšĸĸÂęĘ4Ö°eÂqjjjëîŨģŨj>’9؊yļÜÃ\Æ<¯ĖXSĨĢĒĒb%ÛĮtíJĩZ ÁÁÁn•9;ôųĄ÷˙įx›3ŋķTÅ)číéũˆī˛ÛÂȑ#ßsöc'ĩZ ĨĨĨåģėļpëÖ­3YúÍô˛TLøūûī[ŨÉTkŲ¨'[8wîÜw4ÕėÅs[Ž+wŒį–¸sįF=C8˜šę‹Ģađ`ß3|—›-jŲhlRÚĐĐPĪwšŲ‚­˜gË=\UUÕĀUĖķJc 0f˘3ļ>zqFĶQVVļ‘īrÛJKcË g~_ÕŪĒæš¯kÜĘíÚĩĢēŧŧÜŠÃ2¸ \ĄŅhŪĢŦŦluæwēcãSVVöļ3;jZ­ tšīr;BppđqŒįöŦ=uĒÂißwęT´´4ū›īrŗ[IύT*ęؕ••mÔh4Îú>NcƌáŦcįĩÆēģģû#gMV!IvėØŅânFĀgˆĪ—_žũĨSžëbõE¸Ûs÷*ßeļ‡āāāãÎ ŦÆ zœī2ÛCCCCŊŗLc~~>øúúnįģĖļBQ”襤äĻŗ–|+..î¸zõĒããâxdđāÁŸa<ˇŸšš¯7–—č´Ižß~ģģņ…Ē÷ø.7[h4š÷vėØŅčŦī{˙ũ÷ģ=ŠcGQ”nĮŽ-Ίy………ĐŨŨÍŲ“q¯5ÖeeeÛ***Î:à eggÃĉŨf,Ŧ)u5uĢŋŨũmã:îןmøŦ;848ƒī2ÛÃūũû37mÚÔÆu` Irssõû÷īĪäģĖö0f˘Œõë×sŪ€ët:ØˇoߕC‡ũ‘ī2ÛÃĉ×fggsū=jĩ~ü?mL6wą—‹ĢĄŽŽēĄ˛Rå1ŲVšĸĸĸĮŽkāō‰œN§ƒ={ö´}ũõ×qΚRVVļ­§§į¨'Ä<¯6ÖšššuRŠôcŽĖI’đÎ;ī4īØącßeu„JUåîá!ÃKšrąú"|˙Í÷•ĒJˇŽ§­[ˇnštégfHĨRÁāÁƒĢˇnŨē…ī˛:ÂĮ,­¨¨häĒR*•āããSš——÷_žËę“&MJËÍÍÕseķķķaÁ‚¯įææÖņ]V6pV<Ÿ:uĒ[>Uc‚P8#̰đwœúh–ãgrMvėØąāwŪiæ2æIĨŌšŽy^šÜ^$ə͛7G2Mʔôôô.yQQŅnžËČ=öPíoūī7B6ˇ9×7éá˙ä˙×<>^ĪVæZĨRA||ŧ>00đqO0Ն!!AAA›bcc›ŲĘ\įįįÊ+ękjjæđ]>ļØļmÛû7nÜØ’žžŪÅÖĒJĨV¯^]ˇsįNˇ~Bd.ãš§›jАƒ‹ĩÅÅ9Ŧėĸ¨×7ÁÛo/ëŌéŽíķtS °wīŪüƒ~‘žžŪÅFæ•$IP*•đĪūSëéĻ ¨¨h7W1īÛoŋqFĐXƒÁ }˙ũ÷~øá‡‰‰‰m…™ŸŸĪ?˙<ŲÛÛ+q׉Sæ¨ÚSU}éÔĨû/T]8úįEG&4įÃo|q%zqô”ę˛j ļæĀéͧīßącGĩRŠdŧû]H’„ĨK—ÂŽ;ĒOŸ>}ŋFŖ9ĀwŲØdīŪŊų2™lÚK/ŊÔ∠Ōét˜˜æč‰'î÷´I=‡úŖŋŋŋüÉ'Ÿlvd˜ƒVĢ…ÄÄDøá‡v|˙ũ÷<Ĩ“Ö.âų¤I“’<-ž[âʕ33¯ŧúÚk÷8˛ ŸFŗ^}5šsȐĄ/ž:uĀãM!MeeeZ@@Ā‹sæĖét$ÁĸVĢ!!!Ąįōå˯ž>}z&ßår‡úcoo¯äųįŸ'Ũ1æáP~(•Ę9ÛĨRiˆ-rt:Ŧ\š˛;<<ŧōã?v›]íå×ũŦv¯öÕĮW?"I“0ūÜÅę‹đŠōĶ6á áļ=ŲÃũ`žŲ°aÃσŽÛ¸qc€-ĻÕj5Ŧ]ģļsîÜšr÷1ÕLHKKÛŪØØ8˙íˇß°egÆüü|ØącGcjjęŸ6lØđ>ßåāAzzúŪĻĻ&ņ‡~čkËfJĨĘËËíîcĪmAŠTÎ)//˙<111ԞxŦõÄá2Lyá…ˆöíûûö‡zL˜šēƒ}N¯o‚ÂÂßuĀąûą}ûëu|—…rrrÂOžúčŖ!L‡’$ ųųųpčĐ!ŨęÕĢ—,]級ī˛đEzzúw‹yhŦÍ0räČ3÷ßĐĖ™3ī“H$~2™ėgģ"ŠÕjzšĒæúúz˛ŖŖckCCÃ[|kw&#Føa丑#ÂŖÂƒ'Μč'I“@`pā=Įhvj@W­ƒīŋųžąåVKķK7Ō)ŠĒæ[ģŗ "|ôčŅ߄…… ILL ‹Å “ÉĀ4Ȓ$ jĩ´Z-”——7\ŋ~]õęÕyEÕņ­ßYL™2%Ĩ­­í/cĮŽ ‰.“É~ļ‹I’ RŠ@ŖŅtūđÃ?ž:uęnSS̓|kw&ĄĄĄkũũũWOž%%%@&“A˙‚N§•J•••­ õ'Ožlkoo—{Ú8{đķķĢxä‘GF„›k蘧Ņh:ĢĒĒn^ģv­ņæÍ›ŋ§(ĘšÛØë!B Šĸ˛ ‚$''˙Šĩĩ5æÖ­[ĄĻĮ ‚ęxā“ĸĸĸŨÆĪäQĩ”oũNŦ'9ˆ)ŠR!%‰žīhíˆiiléĢ'˙ ˙ŽĀā@­žI˙á…Ē ‚€Œĸ(îüt‚PIQTáĸE‹Dˇoßū]WWW\KK‹?}˰aÃ:† rhäȑīíÚĩ̚ ˆ,Šĸœŗë… @DRĨ•H$sƒƒƒW^šr%Öô˜QŖF5.--ũĢaí]"Ô|O늖RE&%%-×ëõKH’ PO;KKK )Š"u›ī´ąž˛×‹ ##cM}}}’Ĩxnüœ×][aZAĻMKĖęééšÛÔt=Ōô¸āā1g üYMÍ×یŸķē6ũë!))iyooī˛k׎ŨScĮŽ=ëįįwĀ䞕€œĸ(˛k‰ūž!333õŌĨKO›‰y‡KKK_5֟ (ŠJtēf4Ö?Į´ˇãs^Ķx™6đÎøœģBDš=77AUEyEƌ ‚ MSoëКvúmüœ×uhåļĢžŒŸ@  ŗĢ`ī}iōyģbŸ'áHQ†NĄW´•fęĀnŋ`¯—sœŧØc ØųC¨@ÁwœąÁŌŲyÃĢĀc×â4ŘՎ÷ĻV ‘76gX:;á (Āæ  1ë*6Æ7oBvÍ~2Æ6Ũƒa×õf‚šÎØz#Æ˛;ōÄŖŧÄS „ņŪŗ×gî}§_hŦŽėŧŒ—ĀK/ģŽņ‘˜ˇ4VY`gÃnüœˇU™ŒŊĸņ1ÆĄŲ¯éĐôuūÁÁ'ˆŪtۃņz“98Ļŧ蚀,p cbŦ{™—xЁp¤ũėģ÷xAcũsÄŽ Sƒ‡x­1›ëą!zÛ&3ÎÆ:šÕ1^‡ŪĐĄu({čeZC}9´˜ŧąaöø{Đ ŽfĢéæ•YkcûĻfa‡W$úcŧįH†Ö:}$kXxlã-—ÃxzDãdęmņ† *Į¯'īЂĄĶīčŽCߥčk”…,M<ôÖŦĩŖI&š|đü6q äāxü÷O1vã2Å„ÎėŖąž6xĪoŧnā==k’Õw¨ˇíéŲXcVŸt4Ģã鍝~#ŪĐĄ`§S }Yk§ÆĒ0^oŦlëcÕYܟBoĘúī5‹ A856bŧnāäƒĄˇęq°ØĀxv6–Ŧ><ˇžX3@`čĐzjãÃJ§ßÄä0߅ĮÍ0éÔ˛šLž'ĮǁŗŧLžˇÕ›ņŸNx¤§0ÛõGg­’ BcũŦ<ļčkŧHmŧäĀŌoĖÆ:íbw22Û@_Põ¸kÉäq=›Y ËŠqũōôŦĩÃcĢûã놙OŒSV—'3Ģîq÷gLîWļ—ŨõäÄAuŒœ8֍5pōØĀ/cĀвŧĻĻĮe2ŒÁíM}ēO“ņž(įj[ûbAr‘H”N˙˙ĩk×4 ģųĒ'‚ kjkkį´´´Œ7}/$$D}˙ũ÷YTT´Û\y8ŌÔwí!HNNÎęėėœ{íÚĩHĶãAõ<𠭏/,X 0§/00p{YYŲ6cšœ˛íŊq‹ęlúšZž|ųŗ/^L"IRdI_$%%-×ëõKŌ7a„˛mÛļŊo,— Ûŋsē…{˙靖žącĮžõķķ;PZZZČį–č™™™Š—.]zښ>ŽãIũŨ'$ÉÜ!C†,kllŧ'i5lذË˙-..~ÛÖúķ8c-‘H~ôøŨģw'Oš4é~ąX BáOÉ+­V •••­—.]j3fĖËĘĘĸ(ŠŠâJAYÃG Ÿ+œ!ÛÔĐô°p†0tęÜŠØwĖÅę‹pü?ĮÛē;ģ/jkžŽŲČuC&‘H~įįį÷dooīÃņņņÃÅb1?=áTĢÕđũ÷ߡ644Ô2äĐĄC‡&R%尞ō‚&L˜03((hRTT”` M‡nëėėŧėëëģŊ˛˛ōM.A‚ØØØuūūū)íííBsõtđāÁæĄC‡ęnŪŧYYSSHQÔĶjÚõŖŋŋ˙<ŸqņņņÃe˛ŸâI’ ÕjĄĻφlmm=w÷î]ĩ3ęI$å2$ŅĪĪo|LLLĀ@š<Ø%%%@,ū)yĸĶé@ĢÕBUUUØ1cÎ|÷Ũw×nŪŧy€ÃNŋžLJJĒģ~ũúœˆˆˆŅ‰dČ@šÎ;weĐ A§ü™3ŒÎôéĶ‹īŪŊ1zūüųCd2Ų=qĀpīŅ÷_[[[Ũɓ'ũzzz"íüJ[ę-k„ ōaÆE>đĀÁąąąAršÜĒž!C†ŧ Ņhp­ ..îĩîîî%ĄĄĄaąąąA2™ LWC[¨RŠ Ļφllll8}úôŊ^ŸÉuÛC„,(((įūûī›8qâ(KúÔj5ėŲŗ§­ŗŗķ2üUŖŅpzŌH$’ßĀķô}jI_eeeë… n]šrĨžĩĩ5—ë$AÂĀĀĀĸɓ' ŠŠČår‹úęCBBJ÷îŨģÖIõ7ˇĢĢëŨ€€€pē2m ąEĨRõųą–––ŗ/^TqŨ1đņņ9ĶDûsúÔj5ėÛˇ¯ĢļļöÆ Aƒ?~<ƒŅP寍7†ĮÆÆÖ* ĒĒNJbByy9õčŖļ-^ŧx;šRפΠ‹ ̟˙ė|Ꝛw¨ĸæ"̝ūņПųøLN4edd¤FEE]ÉËËŖjkk­ÖŅíÛˇŠ’’ęąĮĶgddlâJ͌3 lŌy‡+MĢV­Zy§  €ē}û6#MTDDDķĒUĢÖsĨ)**ęvII #MĩĩĩTAAygŲ˛eĪrôÛmš­á…P|p™**jļøzį*##—š4iVmjęš9\hZŧxņöøøx}II‰Õ:ĸëé…^ ĸĸĸŽ( N4%$$”>účŖmåååŒ4UUUQ …‚ŠŽŽ>ŗqãÆp.4­]ģvYttô­ŧŧ˙üsŨN3‰Ļú Åĩž7†§ĨĨąW_lllíįŸ.âZ_ZZZ§-ú(Šĸ §ņÄøØĢ///š1cÆuŽõAZZÚŠTÚÅÄkô×}kíÚĩËŦ}gâųZŗfMT*íbj¨ǰäääÛīŧķk }˝böN‘NĄ6ŗ™‘Ąî˙Z¸f!!ޏ•ē&•ĩ‹ė駟ސÉdŒĖë@( *11ņ:›---íˆŊšč`ÆĻ&:pÉår›ĢŠ&š\N=ņÄĮŲÔôÔSOą5Ø÷×ÄĻ騏qcxbbâu{5ÕÖÖR2™ŒUMééĘđŠSĨ×.\cÕL›3ØI5gÎō ļ4)Š9111wl5:4UUU”T*íĘČČØË–Ļĩk×.‹×ÛjM5‰Dĸî5kÖ°Ĩ‰ĸ(X°`Cņ‰ĸ ą<&&æļŋo—ÉdŒ7QPP@ÅĮĮë™4ÎļžÖŦYSā¨ž’’J$usŅĪĘĘzS$u3íČ Dyy9%•Jģ¸H^ŦZĩjŊT*írTŸH$ęÎĘĘz“m}˖-{V$u3íœDUU%“É(ļī]Š…¸bĒ‹d'‹ŅĮ´ŨbU¸ŗ_  {ŋŽR[[KĨĨĨu&%%msP“püäņWŽYh—Ą6}mūf3õHü#]L}`Ģ#šRSSEsįÎŊėČES^^NM:ĩ#99YáˆĻčččšödĻĖišÃ–>:{Pʖ>‘HtÂŪNø@ú˛˛˛(ļôQ”áéŊɔô) J$pôŪ0ÕgOØlŨģô+&&f§=Y`KúŒņŽ}qqq°÷hŒO]¯¤ĻĻØF°rQōõŠŠŠēÂFãŪ˙ĩ7ā€`ܤqwėÍR›{%<@ŲkŽ@0yōäVļ.(Š2Ž+VØmŽSSSE111wØÖ”––Öi¯šŽŽŽž;uęÔļ­I*•vŲk—KĨŌ.ļ59bŽ“““+VŦ`ĩžjkkȘ˜;悔ĩ×´i‰ŠšsŸėpÔP›ž6oū† ‹lĩ7°ĮÅÅ}••ÅZQ”!›3qâÄÛöjJHH(eË|Ņ”——SQQQWėŅCŋĸĸĸްŅųč#1Ęú ņųŽŽ>Ãv;HQ}Ų˙Žę[°`AEAAëú X1× Ĩ\č+))Ą,XP᨞¤¤¤mlu:M)//§ĸŖŖĪ8Ē/..îļã EâŖą…2iŗœŠĪm—ÛKOO?ōĮ?ū1Ŧ˙€sGÉËË??ŋ”uëÖ-ŗõŗ-ylŋ|ƒ|øŅV5­|w%ūīĸĩ‹æØúŲØØØĒwŪy'°˙ÄGđÆoø]ģvíĩœœœp[?___ŋoëÖ­ÃŲÖôŅG šxņbž=š|||v}ņÅ~ĻŲĐ´}ûv_Ÿ]`͉srr¯^Ŋú÷íÛˇû˛­éĶO?˙ļõŗ999á—/_ūĶoŧÁj= …BØēuëđúúú}ļ~vɒ—Âģģ;^ÍĘzĪ5A0a‚žz*?pŌ¤YUļ~öŨwßMŊīžûž*((`SˆÅbøķŸ˙,HKKÛkëg×­[ˇŒĸ¨yyyyŦj’Éd™™–’’RaĪįĶĶĶdff†õŸxÅŽÄ(š´´´íÉÉɜčËËË__ßß(•J›ã:ÍÚĩk ¤Ri$Ûí €BĄ°ģ-¤Ų°aÃĻŠS§ÎÍĘb5ÕŦŦ, ™īˆžuëÖ- ™Ī…>š\S§NģaÆMŽčëîî^ĒP°ŋBĄL&ŠTšvíZģ•RМãëëûļ㠀!Ū%''‡ĨĨĨmˇ÷999á׎]{í7Ū`ĩ} õeff†Ĩ§§é˙ž[ë͛7?ëãã3K.įf9Į?üĐ÷ģīžŗifjÚú´MŖÆIŌ$ļ|Œ1Ë6/ķ=søŒMË6ũņÜ'ä"č øđÃ}Ž9rÔ–Ī­Xąĸ"111”‹†J @nnîđĢW¯ÚÔȧ§§IMMeÕč›jzņҧ§§Ûd†Nž9rt˖->\´Y†ßˇĨĨeV˙Øįvƚ Aqqņ.oĄP)))LA‚ŗ‡ĪŽK]“Ę™Ļ ĸ 9üŅ…2 Â999á‡椧K#‹aƌĄ™™™ŒųE‹‰ęęębšl¨d2„„„„3Ք™™™Ęeãđ“JJJZÎäøå˗sÚqø) dff2ēh3337͘1#”‹NM^^ÔÕÕÅ.Z´HÄäøG]¸ũ‘Gf‡L˜ĀčpģHM]§Oŗbɒ—™?oŪŧŌ9sæ¸č¤ŅĀ—_~ųĻ;ϧ§ī]ĩjUW ­i÷îŨ¯Úō™ŨģwŋĘe0Ä[b”)ĮŽûœËX`ˆĄRŠ4ĞŦÜÖ­[ˇoŲ˛…Õ'ZũĄÛÂyķæ•ÚúŲĘĘʝ/ŋü2§úüéOō;yōdୟ=yōdņŸūô'VŸž ¤īå—_öŨˇoŸÍõ7oŪŧŌ”””.c €ĄsˇuëV›¯ŋ´´´íRŠ4„‹$™)pėØąĪmũœ3Ú,Z_˙ØįvÆ:!!ĄˆĢLž) …šššÄ‰dŽĩc#c"KĶéĻkSsAFnÜíŊ›43eĻČÚąûöí+å:čĖP}}ũ:&|cc#§Ŋ[…BĮ‰ĻͧOŋĮuã`¸ųŽ\šbu V‚ Z­6ĪYšNŸ>ÍHĶņãĮ_ā˛ķAŗeËßķįĪīd IØÚz{~FF.§zƒaŲ˛W}Nœ(ˇÚ0.Z´HDQÔ<Žë‰6 E֎5Ư\vŌ ė÷ŋ˙}Sģ3===„kĶ`ˆ555km؊|Á‚o͛7“'kũÉËËƒÆÆÆųļė*‘H~'t†>…BEÍcÚáĨõ͜9SÄĩŠ0$.`ÆÃ?ŧ˜égŒĮr~_ü4ä¸66#œK ģ¸¸8Ą-ú‚666Îįēã `ˆ-ķæÍ ]°`Á[6čsZ›% á™gž ‰íkˇÜÎXwttÄ8ãf0< ˛úËŒ˜€žú’ yŪφf˖-žˇnŨĘazŧŸŸß“ÎŦŋ7Ūxcȸqã˛™?nܸė 68åžč¯ū$ĶãoŨē•ķōË/;%–:wđ<Ķããââ^Zĩj•Sâ/€Ąū~üņGÆÆĪ™m€aŧGGĮcô˙ģ•ą–H$sããã‡[;N­V÷íšcĪû4BĄšššĻ[:fâˉŋ{hÖCAÖ4Ē87ęnĀŠŠSfŲ˙É~Ģu0A4:Z;,nųģ"66–•zRĢÕ RŠ,žG&“AWWWœĨc’’’ÖH$G4ŠÕęžŨÂŦ!“ÉĀßß?ÅŌ1ãÆËfŌIcrŊäįį3Ō4jÔ¨_Y:fÔ¨QŋrTS~~>¨Õj(,´>4R&“ĩÎŖŋŋŠŖšH’•JjĩHŌō&‰Ä?))ÉâŖ{ŊžI,‘¤Y<ĪŠSpãFœ:õķaÛ7nÔAqqNßk˙ūO,žkƌųAĶĻ%ZœátëÖ­)Ö:ŽÖŽ%zˇ4ką 55u¸ĩáEzŊ>ÜZĮ‘i`ĸI*•†Xë¨!œ9sf¨Ŋzāg×ļĩĪČd24hŖô)A‚ˆˆˆŅlę3÷7S}Öb¨)~~~ãÍŊgoũYŠõbą‚`ŧCqooīÃæLŊú,Å~&mĩ)wīŪlî^ĩ÷÷0ßčíí}˜Š>‚ ĸĖeûíŅĮ¤M°tMõ§ĢĢ+ŽM}LڇˆˆˆŅLŸ: 4HƖ>NJĨ˛īeŽgΜJ'„ÜĘX)Ŧ=ZŌjĩ Ķé@(öũۖ÷-UÖ@ ö+kŲę‹ÕáFŨ >ēīßũ9Uq Ž~ÁlŽM¨0ÔâæīīŸâh=‚@ ™LfĩQféûôzũcÖĖ™%M´á‹ÅPXXZ­Ö⹄B! 2äKĮ455Mˇf„˜\/t@°†X,†ļļļKĮÜēukĒ5#dM4˜<†•ÉdpëÖ-‹p{{ģĐZĪߚĻüü|Ëå}A͚ĻĻĻ&‹?Œ¯¯åFāâÅj¸qŖFīûˇ)7n\„  `˜:u.…" ļ¨iĘ)ôöv?aî}kfŒIiĩZ Id2Yß6đÖęÉ××÷sī/Z´H”˜˜jéÖ4ŠT*Đjĩ}q€Éo7nܸU–Ž‘Ëåfã8“û­ŋdōĄPūūūáĀ€äääŦųķį˜Í´GŸšŋ™Â$†Ō$%%-OII0[h>Ļą~ôhëOm ķVĖ%Āėũ}™Ä~km5 AÂI“&ŨĪ–>kí@||üp&sZ‚Œ=zĀkÁ^}Lڄ”””Ļķ€BCCÃjėÕĮ¤}˜?ūäädFˡøûû˜P°GŸN§ëģ?Äb1˜k e2Čår€›ëÖÖÖpkfAĨR]ĄôhËûũ1.ųböI[sÛxkËëŨyF‡ÚÜ ĸ đõ'_˙ė[†’Lœ9qH„8"ĶÜûƒ k͜Ý@ĐwŅ Ģü#<D„Ų' ĀętKš„B!˜.‰ÄäOTT”€ ˆ A‚ĐĐPĢ­Õ}“2E(Ž6ü ‚N™2ÅęĶkšär9ČårÆēBCCÍ>r$BĖä)‘%M………  A­VƒBĄkão{ŸM˛éĶiņ1äŅŖ;aôčp0,÷õ×÷f¤'L˜ ށ)S¤ ×7ĩė÷čŅáĐŪŪrŋš÷ŖŖŖ3͙1&udZW$II’V¯qąX õõõÍŊßŨŨũ”ĩēļĻIĢÕöũ^€ŅĶĢ~øa†Ĩc.]ē4Û\gRGũ?Ë4ĻΘ1#Ô\<0Ĩąąq›úĖũ­?Öb(Í͛7SØÔĮ4ÖK$’!ãĮ_dMߕ+WšģîėŅĮ4ö[kĢibccW°Š€Y; “Éāʕ+ ­é?~ü"‰D2`,ąW“6A&“Á͛7->å0ÄßGydĀvĘ}LÛ™L­­­–ĩAŸpƌ&ėŅ'‹AĄPô%<Ė%e2\ēti6€›k°ĐØr…\.‡Ķ§O›m(zģ{G8[ĶŅčíîđą!Aâ‰'Rl”›ž€t:XË6Ëd2‰DfĪ‚EEE9TO‚L&cdÅb1Œ9Ō\ GEE9<K§ĶY5ŠĻÜwß}~`NŧĐø>+š”JĨÕa}†cĀ–Z(.ut’N§N2™ ”JĨÕŦ'ĀŦYŗ›3AááQĪ8ēĄŪŋ˙ĢϚfüø)-æ4uuuÅ9:V_,ƒP(„™3gI’ŒŽq˙ĄæŪ;ūü,G;ĄPØ÷{҆ßC‡jéũ––g}4ÅJ<0Õ7Ūķ.ú#“É`Ō¤IV×dîėėœÅæ\ĻąŪXŋ´vžÛˇoOgsĶØ/ áÚĩkc­O¯×G°ũû2id2Üž}Ûęp•‘#Gū’íšHLÚąX ŗŦkŌ¤IËØœ”Ę´} …pëÖ­Pkį9r¤Õ„‚-ĐąĒ°°ĐĸĐŌŌâā~ÆÚé |Āņ39OōđáÃĮ°u˛ÂÂBP(6eeÂĮĮĮęøjĢ ++ t:ŖqցƏ˙[uŅĶlž+ašŨaRO–>|8ãqw– ëI&“1û=tčĐ`°qS{hh¨ŗ: ¤œiĸՖ——3šÛ` __ßąŽXúZb2ԉÆĮĮĮĮŌûÆ ÆVŲ×ņ€ Ž“öÂVŦ§(*„mmļÆ~gbK;ĀEŨ0Í6‹kÔÖöÁãĮˆ‹Ž;=$Ätlķ8cmZđ˛>ÖŪˆžžžnG4˙ôÔDߤ‡ĐpĢ.Ūë ĀpŠÅb‹ÅV‡‚p­)??ŋob“Ą)ÎĒ'zü“1ąÎĒ'Gۚ„Ba_–„IƓ ‚‚‚ûū­×7AhhøĪŽšqŖ.^Ŧv‰:øÉX …B(//įũžŖ˙&“ÉúrŊ"“=ņŲžĪ8SŸ3ąWŸŗbŊ=úœûí­?gĩö֟ŗÚ{ô9ŗ}°÷÷Õét6ũŽneŦ{zzzŦ“••Õ—]Ņjĩ —ˁ$Iˆˆˆ0ûž%´Z- 4čœ#ēžN€“N€ŽZ’4 č›ôđbԋ}ĮœĒ87.ŪÍN č›ôŽ|ŽļļļŪŅzRŠT}ŗ`­^đ$IBssķesšÚÛۛŅD?&§“-hu:TWW›ÛɏŅlM=îʙXŌDŖŖÛ31B===æęãÚĩk&C7úkJLLėģ–äryßã>NL–áē|ųōMđ‹)ŠēŠ×7Yü|BÂĶpōäĐéĒA"IŊž ^|ņ§… nܸhSŊßēuy¨9ML°vĪÉåōž°°°Ņ5n‰ģwīžļöÛYĶDOzĸ&™šŽŽŽvK´´ØĢā§Yũ*• H’dĶ­ÄFØŖĪÜßúc%†öáįį×lîöčŗ5Ö[Ã××÷‚šëÎ}öÄ~{ąW“v@§Ķ¯¯īgëŗĨMđķķkļĻĄššų2›×Ÿ=íƒ%ĒĢĢ+Øŧūčߎ }ąĸ(ˇyEGGŸš}û6ńōōr‡Ū§ÉËËŖ’““æ4ũâĄ_\,j.ĸ˜ŧ^Ūõ2ŖãŦŊŽYH€ØœĻ3f\gT8ęÁ …‚Ą…ßî–Ŗšnßžm“ŪŦŦ, æ4%&&:Ŋž’““o›Ķ‚… ļ8ģžŒŋ9MÂŦŦ,Æį˛ôŊ,j/\¸†**jļúzųå]ßįFį)*jĻÆŸ|ŜĻČČČÅ …Âá:˛ãˇ;cNSrr˛"//ĪaMĩĩĩTmm-ŖķÜž}›š7oŪqĘB_°`A…ĩķŲsŋYûˊ+:,ÅúŗŗĒNJu}Ö°CéW\\Ü%%%|é3ÛîP?]wo°ĒÉ}‘——GEGG˙ΚžčččßYģ/¸¨ŋ‚‚*99ų-kú@l-–pQ%%%T\\Ü ô ŲÖĮä3UUUTLLĖNú+VŦč`[Ÿĩ˜U[[K-X° ‚ĸ(÷ĘX?đĀß0ëgmp=ĶÁ÷ĻĢ´´Ôės”ąŽ=fimjSØÚDæĮŗ?ļReöšÄ˜1cÎ0}lÁÖ$„ƒ6Seļ[7lذ+L{}æ4ŅKŪ0ĨĒĒǁĸ(ŗi„;wîLŗ3lÕĶ?ūhöqEQäíÛˇo0=õd,˙M štįΝģâ¨&kī™ĸĶéĀĪĪĪlf‡ĸ(íå˧ũpSĻH-žO¯b‹ĢaذQfoô3gÎ|qøđá6GëȖßNĨRÁ¸qãž7÷~iiiĄFŖérT“P(´iHÁȑ#ŋĩt EQû˜Ŧ.b+Ö>S]]Ũd)ĐnįBŸ5ŦÅPšŽŽŽĪ˜,ÅČ6‡nŗÔîД––žĒŅh:ŲÔĮäžĐh4]ß~û­ÕŨH5Í{•••­lęc‚VĢ…ŌŌŌŋZ;Žĸ(­ĩXÂEũiĩZčęęúŒ>ŨÁƒ-fļš¸U*ZŨzĸ(˛ēēē‰m}LVZŖ(j€› QŠTųûöíëtüLĖšqãFŊĨ`L^#˙Bķpú&=ÜšqĮâPîîîl™lä(:|ÆŌ1{œŠIĢÕBppđqKĮkŠI­VØ1cūké??ŋsÎÖäëëk1X 4č´ŗÆĶš‚‚‚,nkŪŲŠ?gm8›=ē ląŅéėė´úŸM´Z-\Ŋzõ/æŪ§(ŠŦ­­eÜQcFĶŠRŠ,î–TZZZhÍØ° “x@SVVļ­ĻĻÆŠãģ˜ÄPFsāØąc ÎÔG’$ãë›ĸ(R§Ķ9õē°ŪV›réŌĨ&gë;wîÜ&'C,qöÃcĮŽ5h4šLŽÚQH’„ÜÜ\ũáÇWX;vÄčۋsŠ9פoŌÃŪ÷ö6_:y)ĮÚąūūū‡QO:ŠŠŠęO:õgKĮQEęõúCθ؍ÜęΜ9ķ…M:’$;#@¨Õj8uęÔEkR5́ŠŠŠŗÎčm‹c֌FŖy¯´´´ŪYš|}}ŋą–yúá‡#üöÛŨũwTä‚/ŋ|† úŌÚqÕÕÕ9oŋũvŗ3 ™RŠ„1cÆX},zøđák׎ít†Ļüü|čîî~‹ááuF|0Ä(&ņĀ”#Fl~æ™g Ŗq’$áŖ>j´CMéėėTŽ\šŌĄÕĒlŅ÷Æo4VWW[mwhöīß˙ģM›61ņžW_}ĩ“I[MsøđᯞúĒSî €M›6ĩŊÄôøĘĘĘ77nÜč”X°råĘîÎÎN%Ķã5Í{}ôQŖŗô=ķĖ3]#FŒØĖôø3gÎ|qčĐ!ŗ:Ÿųųų0lذ7č˙w;cŊwīŪüC‡é¸6CųųųËäŅŌŲÃg˙įûožoŧXmÛ*ļōá ÂTéÔטhúú믓wėØŅČõ…• “'Oū“c÷īߟų—ŋüĨë›qũúõŨÉÉɌļf:ujÆĻM›8ÕD’$Ŧ_ŋžgūüųÉLŽŸ?~ōúõëģšÖôę̝vîÜšs“ã“““—+•ŒãŽ]čt:øË_ūŌöõ×_3ǧĐĐ +>üđN5Ũ¸Q§Nh8qBmõzĸ(ŠLLL|-;;›SMZ­Ž;ÖpđāÁ˙aĸiʔ)˙Įĩ‰UĢÕpčĐ!FŖyÉņæŊššš3ÎčÔ*•J`hŒœ9# ŒM!€ĄŦuFį$;;$I>Ķa†¤EHHČ>Žc€Ą­ž2eĘ˙Ų¨Ī)÷€áú Ųˇk׎j[ô9#–ę/88XËtMDDÄ gčŖ@L‡ĐÄÅÅ­pÆõ§VĢĄĻĻæŒiės;c °zõę%ëׯīáĘx¨Õj¸téRõÖ­[ˇ0ũLėŌØŸmøŒŗ ‚f§îöÜ=ēīīûkŠ‹‹{Ë ߨK;ZTTÄč&EQä/ųËßsŠÉ˜ÉSįååũ—ÉņšššuņņņoryæįįƒT*ũ877ˇŽŠ&‘Hô —A?;;.\ȸ1ĘËËûīũ÷߀ËzĘÎΆ_ūō—ŋgz|eĨj÷ĐĄAGŋüōmĻą™ÂÂßuOŸ>Ÿņú^[ˇnŨ2xđāj. ŲúõëģWŽ\9ŸéņEEE5ÍŽL,Ũq\Ŋzõ[>÷čŖ.øķŸ˙Ėi2??‚‚‚0ĻL:5ãũ÷ßį\_OOãjʎ;ÛŊ{w3—É•JC‡=kK[HŗsįÎ%læōI—Z­†cĮŽ5m´õŗEEE;ÖĀeįNĢÕÂÁƒ›wîÜiĶŊ`ˆ%C‡=Ëe,Ņét°{÷îæ;vC|J¸ĸoŌÃŪŋímūv÷ˇ6]üožųægAAA;¸¨'N%%% ļې6lx_¯×ā"XĐC@ŠŠŠeai6oŪŧņæÍ›GšĐ¤VĢĄĸĸâė[oŊeSoâ­ˇŪĘæęɌJĨ‚žžžŖ›7oļŠ1úøãĨ\ ÉĪ·ģwīîÛ°aÃûļ|îÛow?ÆÕââņå?ūņ›LĪŋūõ¯„ÜÜ\=†LŠTƒ>ø—ĨK—VÛōšŲŗgĪ~ņÅ9Ņ”ŸŸ“'O^mĢĻÜÜÜēqãÆŊĀUgMĢÕž}ûŽ|üņĮR{>Ÿ››[7}úô§š∊Ą™5kÖs\ Ą‡C†‡‡3z‚4ŖFšŗ~ũzÎô­]ģļķąĮŗē ˇ9{ėąY\•Zŋ~}÷¨QŖæØûųđđđdŽb €aČŦYŗžŗ÷ķ\wîžy晎„„„ß2MJõįã?––””4pÕš3>-Ųđ3}ÖÖtåWttô™ŽËtÂøøx}bbâr{5…E†U-\ŗ•õĒ‹š‹¨ÍßlĻ”ø äÎæÍß0^“ÚÚ+##— :a¯ĻČČČÅ111wŦ­‡l …‚ŠŽŽū¯Ŋš—/\¸°…éšÔÖ¸}û6ĨP(¨„„„R{5Q ĨlÆŠ2Äō˜˜˜;ŽÄ(S}lļ3´žÉ“'ˇFGGÛ×éW\\Ülëģ}û6%•Jģi é—H$ÚēbŊĻ{P0ÕˇbŊK{L0}%''+ŌŌŌ:šĐ'‰ļ:Ē/11qšT*íbSEâ‰H$*vT_ttôÜɓ'ˇ˛WLõ9[(c›s‡m}yyyfõšeƚFŖŅĖš|ųōĢ ŨŽöHōķķá˙÷AŦ­cyLšræĘĖK5—>{-åĩnGĮ\įÃĢ>h2|Î…Ē ė=Z­ž~ôč҉‰‰Œw2‡RŠ„W^yEˇdɒ)ļŒëΉ'î/++Ûį¨&’$û4={6œb¸œQ(Š"kjjĸŠ‹‹.]ēÔĄŨĮH’„ĨK—BqqņŅ‹/>DŲ0ö¯ŋĻ‹/>T\\|TŠT˛ĸé˙ũŋ˙wāĉ÷ÛĢi׎]Õ§OŸž˙•W^Ņ9ĒI§ĶAbb"”••í;qâÄũöžį…ĒS§J§ũãĐ3ž_5 7nÔÁÛo/ë:ūģ/ž˜fīyΜ9ķŨQŖæ<÷ÜsŽfdĩZ-$$$t?~ü3FcwöĢŦŦl[CCÃÂ'Ÿ|˛ŲҧXFM=—/_~•é˜xs|ũõ×ÉeeeûēŲČ|åįįCFFFcOOĪ"Gb”ŠžĘĘĘŲhg ģj>ųä“́Û:Žu <ø?555JļôŠT*ˆ×ûøøŦp¤-¤9~üøęk׎åĖ™3§“'pjĩüņæÎÎÎ5{÷îuøqėŪŊ{ķ^|üņĮ›ŲŌ7gΜÎk׎å?~|ĩŖį+++Ûæããŗ">>^ĪÆSU:žÔÔÔ(?nķ‹ūh4šŗWLõ?~ü3Gc €ĄÍęééY”‘‘ŅȆ>N ŨeeeûĖęs´7ā ¯Ī?˙\”˜˜xŨž^{mm-%•Jģž~úé 65=•˙Tj„8â–=ŲëÍßlω¤kÎō9ŦjZģví˛ččč[öd‡ĒĒĒ(ŠTÚĩaÆmljzíĩמ×ۓ•-//§ĻNÚ‘••õ&›š^~ųåMS§NípDĶË/ŋŧ‰mMņņņz{2Å%%%œhZŗfMH$ęļGS^^sįĩ×^{–MM <_úČ#ņ]öd¯32rŠŠSĨןz*?•MMO?ũt…T*í˛'{mĖRßzįwXՔ––vD*•vŲ“ÅQ(TbbâõĪ?˙\ÄĻĻĩk×.‹×ۛŊŽ­­Ĩd2•––v„M]ôëwŪI‰‰šMÅõIDATc¯žÛˇoSršœ3} …bNttô-{ŗ×Ļú6nÜÎļž7†Ķí´=ŲWú Éʕ+/rĨoåʕ՗˜˜x+}iiiGärš]ú(ę§xĸP(æ°­2ÆGôŅíÛņÎTŸL&cŧ“ė@úâããõk׎]fé{XÎį+..îƒČČČ; …‚˛ÔˆŨž}›*))ĄÖ­[×u…Įqæ^=öĐa‘a­šÔæo6›5Ķ\ū€zá/PsŸœÛņ‹‡~qQŧ@ĖjŖeúJHH(ĨkõTPP@­Xąĸãá‡æ$˜™jĸUKšjkkŠ‚‚ꊧžē}†+M 0ÕdéF¤5-\¸°eÁ‚`į0 &šæÍ›w|á…-V5ŅA !!Ą”+M7n ŽŽ>ķÔSOŨąĻŠĒĒĒ/p˛ņˆĪÜK,^ úÅ/ē8wî“+WžK}đÁeŗfzķæo¨ŒŒ\ęÁ%w&M’ėäJSjjĒ(::úˊ+:JJJ,6H$Ę2dHĸŸŸßø˜˜˜€ūÛLĢÕj8xđ`sOOĪU__ßíÎÖģŽģģ{‰Ī¸øøøáé;|øp[ggį厎Žōęęęgëķ÷÷OiooÆĮĮ‹Å îŅ÷ũ÷ߡ644Ô2äĐĄC‡^w–ˇ˜2eĘË#GŽL š%č¯OĢÕž={ÚîŪŊ{=00đˆ3ũ€Á˙øųų=ŲÛÛûđ@õ§ÕjAŖŅtÕÖÖŪ3fĖmŅᑯúž„Đ Šĸœˇ]yMBX3ā ĘĘĻ!¨ 5YŅ$€ßĀ1ØîĖā„šÖ$cl­ŗf7Ô$‰Dé—/_Ž?~|MssķeNWā ŋĄQŸL$=sũúõˆĀĀĀÛEĐét%Žh}“&MZÖÜÜ<900đö°aÃÎUWW˙Åô‰'Mšôŧ+ë‰Dŋiii™¤×ëG>üôšsįūęĒúƌS[]]ũ‘+øZŸP(\JÄ4Õ' …ŲÇOĮ˜ęęęEŲ5¨Ũãu_A ĸœĸ¨Džu˜č‘€Œĸ(îW0GMŪ )ÔŽ¨P“Mš\*6šĒ&WÖeĸĪåŽ/ÔĮĒ>—‹ũ¨uĮˇ^AAA\4Ö‚ ‚ ÂhŦAA„ĐX#‚ ‚   ąFAA@c ‚ ‚ ,€ÆAAAX5‚ ‚ ‚°kAAaž؂q;iĄiÜõĮĖnëkÜ&Y`ãųĻĀž¤ÉŌnY.ĒÉŪēwEMĀt‚ ėų,iik^Ôd]AÛŠÉ‘ØäŠštm5î 6tŅXĒ3GĪíČõ…ú¸×go;Dãhģ æÛÔĮģ>bŒ[mi>}úôâččč'Ûëíúõë0fĖ›?§ÕjA­V'šû!ĮDŽšøČ‚GÂm=oOGtwvÃĐCmÖT_]į+Îģ”&íŋ´mwŽŪ 4÷ž+júÅ/~ĄōÉ'l=oGGttt@pp°Íš>ũôĶļü‘uMMMMāīīūūū6kÚŊ{wŨ÷ß?Á41N/?aŗĻöö;āëë>>ļkúūûŊuׯŸPA˛”–‡…‰l>oKK jķį\QS{{\šĸũW}ũ‰ 6õ°QWLęĖŪëę§ōÛ}Ą>îõ=ōČ#SSSÃí=ˇ#ąŸÆR€úøÕ`ŋW¤ŅjĩËå “9Ú)fŽRŠĩZmņ˜”ܧÖÁžœ=pžâŧKiĒũĻļŨÚ1ŽĻ),,Ŧ=//ĪîFĀ***\NĶîŨģ-žīŠš‚ƒĮĩ§¤ä:UĶ÷ßīĩø~X˜RRr)Éå4?_Wޘ}ĐĀKõĮRņq]Ą>įéČËËãSžÕ6õšˇ>ĨR‰cŦAA„ ĐX#‚ ‚   ąFAApģ1Öũ),,’$AĄPØüY•JZ­]Ÿ7ĮŅOŽB{S;H×Hmūė‰' žē‚ėú{ûâm €ĄÁC! 8āŖÉˇĻŖŸ…°éCģ˙íũ.Ąé\Å99a$Œ yųļļŽ'ú\:Î6j"Iär9dee9œ°¤É’žūščŽŦŊéLН¯†‘#ÃĀ0ĻsčĐ`¨¯?C‡ āčŅO ,LC‡C{{LštoÆööí‹bøl{; ))š0kÖĶ?ËėōĄéĉ0i’ôgÆÍ=ŽhÚŋ˙m˜4IÚg†øŽ#€ĄCƒ! ÚÛI8zô˜4I “&I]Rß@˙Ī…>úoæ4ZŌgøÜt Áūũoģœžsį*`äČ 0rd¸C÷[ņU ôcznGÛŽôąÕpĨ­ļËúËË˃ŦŦŦ÷Uqkc­VĢA&“õĨđisL÷0t:]ßC’äĪŌüĻYŗn {8_q&͝uĐÖÔ“æN‚ĨöeWë!lēa\q{S;´5ĩõ}ö܁s}īą [š¤k¤\]“æN˛K ۚĻĨM3œīĀų{ˎ=°u=Ņgãņ[šh-*•Ęá`oIĶ÷ßoVOMZ­H’´ēΟ¯€I“æBcc´ĩ5Á¤IsáÁĨ&‚††ŗ6 ™ŽļļĻ{>{î܁ž÷i3}ū|E_ŖÎ§ĻiĶŌ`Īžøį?ķ3ãæˆ°[S@@pßßúÃ×īfJ}}5  C‡ģ¤>6झŊŨ0ŒÂœFKú¤Ō5F\ “&Íu9}ĶĻĨĪĀĄû‚­øÚļÚŽôąÕpĨ­ļ+}´™VĢÕ÷$ÔhÜÚXütČårĐjĩ} ¯ōčĮô‘‚i…T1Ž@g]§ĨMƒúęz¨¯Ž‡;OŒ#ŲŪˇsbũņŸ†zÜŽģm÷d>gi:ąķ„ÁŗĐ`CSc]#„„‡Ā´ÅĶÎĸ°s=™ŽÍbã1šH’ė{´åčSKšD"‘Y=ũ5ҟĪĘĘę+#ĐY°iĶŌ žžēoŸqQĐŪNÂųķ†ĖV}ũņ{>wûvŨ=“ĢNœØé°ŠfKĶšsđÔS˙€„„YšBëqD“TēęëC{{Ķ=&›¯:ęiVž ØÖĮ6æô€ŅĀŦҚž'vM°c.ô56ÖAHH8L›ļØĄŒ:;ņĩ?lļ\čcŗ=āB}>6ÚŽôŠT*ŗŪŅ­Wéŋü ] ôåryß{tƒî‘˜VˆL&ëĢhGˇKīŋômé˙ŌV蛸G χFĐ̝އļĻ6›æĐ$=ļ4Øyjž¨‘á#!$<f==‹wMį+ÎCûtC]9Ú)aëz’Ëå ĶéX –&úīZ­öžĪpĄŠŋKõD?žsô‘_˙Ĩ¸čšÃl:ŧƒ›Ų߈Õ×W÷eÄøÖđĶŖsGMÚ@zėŅdčxL€úúã0kÖĶŧ×ũˇúújhkk‚xÔdÍ\—ĶGUĶ˙ˇwrž5}Ļ×1­‘‰žsį* Ļæ 92BBÂíūšŌwū|´ˇO‡úúj‡î ļâ+[éá lĩ\é3ũ›#í—õĮFÛĀ•>zH‰šēskcmôš†ĻžL&cõņŊ­ũähßpšĄÁCY]Ÿ™ MĶŌĻŨcx]AĶŦ§gA{S;„<Âú !Lčz0ܔUUUŧԓĨk<++Ëe4‰Åâ{ư9›ŖG?é{āl]f6ôM›–ÆJį’+}ŗf= ííMō´ĶãbŲ@ëķÕ0ŅĮg{ĀDŸmĶßWĄP˜ÕæuÆÚÜ"ß|4ė4|hOĐćĄĻájQ{.4ņyģĸ&sƆ/SmNŸz\QCęęúøÄSôņõģbˎG_ą×ôšũkAAqĐX#‚ ‚   ąFAApģ1Ö*•Š•uo™ÂdМ=9{œZLvctļĻĻĢMV:ģšĻúúúĄJĨŌŠšęëë]NÍŽĻŠŠéęĐ={rø–qôŽŽ„ŗ5YÛĪëČWŧŽPģđËŦĩ¨ĪŊõiĩZø˙‡Kä“#Zn%tEXtdate:create2019-11-29T19:38:03+00:00Ηīū%tEXtdate:modify2019-11-29T19:38:03+00:00ŋĘWBtEXtpdf:VersionPDF-1.5 \ 9IENDŽB`‚trillian-1.6.1/docs/merkletree/treetex/images/hashmode.png000066400000000000000000000632721466362047600236770ustar00rootroot00000000000000‰PNG  IHDRÎXÚŲî IDATxíŊ tV×u.ē,ņYØČØFi¨–OšU„€)Šm'Ļa#ÃcĢ 9B 4Čp†=eL¸FC´¸C3nŽ’Îļ›Čų◆jų’QCXB }ét›ĨĀŅ „K˛ĸ!Ûh —wލĮÉģ„ˆ>B@ŦĸŗCO=nr„@āP3pČ)CBĀéCĐq:PÜ-Šģ„Ä?õ8Cž˛%\ @wWŪ …¨×éB‘(Ē3¨Įé 7ŠE„‚ÍmŽ€z# !ŋ §ßSú„€ˇÔy›œđŠĄ×I=páÅ(d8ŒQK‘Šĸ_Ÿ6’#ōá2œ\‹‡ˆ#†Hī]ŒãžÍ!´ßjĶCØÚ_ɗđ2œJÉ>"PėcÚĸ'M=qŅ%(ũd8‘{|3MMMŌęÕĢ%<áļnŨ*mܸQjiitjTˆ"ŠĐI†3B$ĸ@z(Ō×aڂ‚Оrhē0™LJÅÅÂØŖ!ÂŖ¯ÄaȐá Y”=!`_ zšJ#‰žĻԈZ¤1Ė`ÉôM1aŌ@yĮŒ˜đIlĸ#āko††“ ÕâŲĐĐ uuuĨ†kņŊŽŽNB/”9|cáad•ß~ûöí,č°g}}ũ°°Ã>ēûŒ„[vĮ*Å&B€0D@–åm˛Ž¸¸øvęmmmrm-ĒĘōļmÛdüÆJˇyķfšŗŗ3õ‡w\\.ķ6Ôúč?Ôãôcʁđßzœč5*‡eņ› ÛÖÖÖJíí튞åæÍÃíz•Ŧ—‰Ū§Ō…Ôãę+‰ĄwBĀcČpz (%Gø„€/Ģt`ą‚ ƒ`ü˜‘d†ŋņ t˖-’ŌxÂ_Ī!>KC/Œūúų%_čŦÚøĘž8 ƒ†A.Œ* zĄ0”ėķđc=Mô<•ŊÖ0h•$Š)‘HT…”7e#ČpÆHØÄǏ„e8˜Ō8Ē4úĻÄīD"AuZ@Į<R˛˜+ąĪ?t˙Ļu‘á´Ž…tŽíãtŽÅ$„C=DÖKô’xĖ…Âą§i{™&ĨE¸A€ §ô(.! –ö&bÎŅėØ<ĖY˛•° ŨJ+,2ƒÉž,Žécnt“#xA€ '/’ :‰ÄđŊ:áā #Ŗ\¤#ÉVˇÂø`QÚÅQ‡uōۋô‘Œ°‰ŗÔĀ0Iƒ>ϐá4…ˆb ÃŒ$(Æ^JĨEoPŲÛDŗ8n9÷*}đaÁpZn`¸å‹âĮ2œņ–?q/ˇ& 腡„ƒaJCĒ5ˇiĮ ;KŸüN_EN ôĶčpĨT ¯€á4Üāȃ āĐģTMøŠ{›đĶ‹=ü)΍…?ëųĄ¨•fŠ€ô?ŊôYŪFy`xV̓2mwĶÆ…Fō"l#@=NېQB  įī`Ė”Ã˛ømftŒâ°ƒN būČf vŒŌGzzyĀãF×Ļ#Ãi0 î 2œÎpŖX„@ĐčZô.™c=E-#g‡Ũ`‚ĄŌ ãˇÃf–>hĶËÈn“od8MĸĪ„!@ÄÜ5éöö‘†††ÔM&VĶÁm(§žžūvtå{cccƟ=o2yŅĘQŽ2-õo­dcŖ ÄhčĐgč" s‰D‹,Ëču:žs•č™jmIQS€ģ4ņ‡yFļ„ Ŋ˛{6•=Qu|+ŋ•yäääHSĻLIŅÆzÍxb¨=\ôtŅC5pÆ]lƒˆô‰ B ĸȲܠÕ͞ãĮîĪ´GV+ ÜŲ Įžę8n~#Mäiâ†ßyQ ļø@€ÎĒåCD!`Š€,Ë8Ŋ Á4`<<„^y>"@†ĶGp)iBĀk‰VŌ Ō!`ˇ& Úŋ9„Ŋ€Î@Ļ, Ö CĮ ųŅ!ā+d8}…—'ŧG ‘HārJęeIõ6ŊW/JŅd8-€DAxšCš‚$ ‡APo3HÄ)¯Ûáŧ Ŋ⠐žëŒķ-õ6ÅQ×ČQJû8#'Rb(.ȲŒkÆÄp_g{"‘¸/.r&>ųC€zœüɄ(",!€Õ¤øÃžg)pDĨ¯[vˆ A Ã)¨āˆlB@’¤âŲŗgĄĨĨå帠ņÜsĪũD"si /õŽ Ä'!@„€u`<1L‹ŖødY>`rz>oKÃSœæŊÖ:\’đšãôKJ‰œW ‚Cn¯*Á9ļ8ĀŊ*}ė pėž1ā {ŅwBĄ<B€ øD 2Ŋ=Ž.}Ųĩéũ[v=Ņ›Öģ‡ŊNôŧ51y„!@Ä Íĸ§Šg@R@DĐxM&|ĖwÂxâÚ5r„€īĐâ ß!Ļ W0Ŗ€ĄHŦ&5’LßIYeÎEÁEVĪęåŒS”Ā3°Â}Ĩ† ŊDȟ B@|CF`ÁPŖÁđŦ‘d ‡ŗ"Ō7B€ ąĀĐŦĢŪSÚx68§ ŪŨ8ô81tËšI‹â„!@pŽÛjáŲ|,Ëu˛,‹°h4ĸĮ蕺4/ėUf”!@„@đĀXĸ§äųæ~Ī'Ëō6Ž{Ÿ›͚I‰ ŨziÍō¤ī„!@>#€ĄE Ëēĸ4%=:Y–Û82 ˜Ëô{+ đŊdßņ5 B€pš ôœôđm˜3č^ æ<ŅŖ§UˇŽÕ– B XEîųĐŦUvŌ=Đ ayųŨÃ4byãF™@*FŅ7B€ s¸:L¯ĀÅ""˃zšŊZKž U`CãæĒ@!B€ Ė`=ž ‡)Íčö=}QŊ,ËX´ƒaU+ÆFaF8´žô0fôøļK?KúB„!`,N~Ž-mTąČŧG3ų„2ĮlF}'B î`h6äķ̐5Žœā—=ĸŒ ą`ûÃ\#báQëøˆÃđHϜ B€ˆčŘŪh-–…įCĪđėä&á!B€Ē|Ųį,¨Ņã3Ā”!č2Ũ´"ŧí3°…b#H.]ētũ˜1cŠ;::fß˙ũ7;::r—,Y2:++kĸ:ôáÇģ._žÜ•Ũá…ūgkkëkę°ũfCŗí’$mäˆ."%\ X8ÔÅģ^¤Ī†ąĮčÆ~c<ĩŗĐsü/<[đ—H$đ{G†3ö*ā € 61¤ėR* Ģš¤“ĸ‹‹€'Ã˙X)Ëq/S¯ÚĀÂ/VôŠ+}ĸ<šÔÔÔŧZWW'c8T$‡ĄäŌŌŌnô”J&”…iĨhb#ājÁ†>eYĢ€U& r„@$HbÅ,æEuč!cĶÚĩkŋjC"˜Áü“ˇûil@Ac‹€íĄ[Ļ˛jĄ•ˇąUûč0žÄP'Īķ™ĘgôŽž2æ=—.]ú âĄM,€DA|E 7ŦÜ6í…Ĩˇ™ŠŋhßČxúĒZ”¸¯˜9ķ\û2ƒ(ŨØkj`<1T†ũu´`ÁWÍĸÄ-"}4ŧi%‚F“Ud<-* ã ĪFĄ§ÉJ!{ĸį‰aÛ5kÖ<Ą‚›†fU€ĐOnĐŧ8@đ9MV$žd<šQA"ā§žzę‘į4J"žÁxΝ;ˇGąÚVŗb2ŠÁ!0ŦaŖ™*Ē´×38Ŗœ\ €4˜ 43>ĸĮôĸE‹:ˆÂ\@IQ ¯HM%äääüWY–ƒ;Û2ÜŽŊoā›!Ā'Ø˛­ĸm9qZŽĶû<ßãSD! Ā^rĒ킯Ã%BTÃHŸ;ë•ú‹”ĻSČ|!€ÛEĸ<¯ŠWC wÃáų’QCŒD }*Įvé8įū8 A˜!ÛQ#ÅG>QDāŌĨK_ޝ_Ŗ.™LJßøÆ7r–-[öbåJëeš”!ā_ §čåŊN˜%#\(“ (ލ¨˜ėWĻ0”0œl¨Ά†ĮŲ!ŊíÛˇkÆĮ1^ĪĶĸ'ūī|§L3Cō$‚CĀWà z9åšĮœŠPN!P]]]“rürÅŘ~¸åpÛJmm-û™ēDēąąQŪŧysę}Āaë  <\kVUU…ģ:ÉĄ! ˲¯ēë•S”?Ŗ2ŠģlyqĄ Į$cęqš$âįîîî%^÷Ōč]*‡eÕÃAX(TWwkŊÖ-[¤Í›oÛŦôĮģōĀų {œāeܸqOô$BBŸšIJé•SeYT–QÔøëęęŌ„#Œr*Ërq"‘ānÕ!NMÛ3++k†˛ĐxÅM{{{j-æNQ¸đ[m8™AD!SŌ€ßĖ˜Ģ &†}•sĻ^Ņk”Μ9s˛õĢ_a¨ŒģBiD7}‹~. 2+§zeÔ Ų0Ę)¯{\Épši‹€ß{zzÆųA6 æļmC—ĩŖ )ŗ<1_ ŖŠ\œ¤|gáÂ|‚žĩkזŧôŌKd8Ãåí9VĘŠVõœoÄĒ[ûŪŧIĪ“TČpz#_‰äää„vę $ūPxņʆwá§ė‰†…hûéOú'’$ũ,,(ßX#āë #dõĘ(Íad#Bhŗ"Ŗ´č!JJJ.đ2𝤋xXš°8bá…/GFčĈPȲŧYY6č]./ˇĻ§PÅMlbŠ+ļüˆzB€¸…€@𠋆lÔ ytÛø€Å Ė)ߙŸÛ§iēĨ‰â~ @eÔTÍĶ$ÃiŽQ$C`ŪŅėŧY,[Wö­Äą–Ō°)ß׋<0 ÚÉ"#`Ĩ,P GÂd8ÃÁŨ×\sss­dĀī°°0’l[ˆr¯ûާQe87ī^ä4Pņ#8E@{ŗ¤ŠXŖ˛@eTV€?ÉpvPY]ž|Ų´P¨0# ēÔû.ŅTö6Æ,Žüy•[ÍĢGø›;wîč}'BĀgLˇA™•…¨—QŸņw•<NWđņ9;;ģߌ2J8Nö§4¤Zs›fqĖō´ō=ˆ<@*—^zém+4QB ĖĘBÔËhs.‡hUm%Âį<ûúúŽ755í—DĄc׎Áˆ(&ČS÷6á§ū0žVR§Ãž!>čŅJW ‡ŨЧ&>ŠīôJx‹@iiiˇîY>444Ø:å'ņāOíęëëo{ąwe8åûí€:/zyhųkųądqĩŲ˛eË^ôqJ°‡€Û̓ė–Qčŋ^š@™Āubėd/„cNųÎüôžZéãęA8|cuûm’ļķK~í‰ÂvhęqچLŒYYYíh!:u˜ë`g˚ĨĄėšâ]Ų2E “iŗĩ9,[Ŋ<Ôū,’Qëß._žü< KOB $ė[Lj´SFUYVđ,fĒåY:îhë:wîÜ) ™BÃQ===?ōĄ7B T\ ׆Jšŋ™ßēƒĐß<§N†Ķ1tüGĖÍÍũ_0䆨ŗgĪ寯FZ4 Ŋ…ˆ@"‘Ā-jŨŽ”ņŪĩ‘áõ!Ã(ÜÁfļcĮŽ˙įÅ_ė 6W~sÃb„ÁÁÁwųĨ(‹)Ôē.øí‰D‚ëÆÎá‹Ú¯öŒŒŒ˙0Û§5ĻõøŲ°aÃõˇß~ûĪõž“?!Ƨ„DTˆŲrŨÛ.tä^ˆÚDÖģwī^;88xǞ˛r|ųņš'NÄōvį{txeŽčD"Ņ.Ë2ŒEĐŒxC|Ž#ô&)˙RĄ§Øō’rWvvöÛė´ ^ˆ šŽūđ‡×.\ødĐųR~„€E¨×y (ÂÁĸÂP0˙H–——_ĩš<2ÁqÂJuuõn˙aĻį¸=I(v›sô‚I=Î`ņ+ˇŽ;î¸ŖÁėᰈķ;ßūįž¸sįÎĪųĨO¸DŊ-ŽŸäĪ(:øŪh€§od8y’†´ėØąã˙~ūųįģâļ¯sãÆRffæ?ŸBōQĢ(i/H¯$ÆxxÉģ$I[1×ëqšž%G†Ķ7hųKxʔ)ŸûĶ?ũĶŪ¸Oėa=pāĀģ{÷îũ[ū¤A#HīëŒÛö,Škƒa¤,]ēôuuu˜1fG땔”\ĀÜüI("ôe9‰cœ5<2_;Á¯>|~Ą'Ÿrņ*ôžNž<ųīQžīDú+_ųĘ`~~~% ŅúĻJ”°O¤‡lWĮDwWķ~؁ObĻdED ēēú(îā‹ĸ̍¨Xŋ~ũįE” ŅL0dYŽbųTđDûV™°é) É œÄf”Üēuëú1-ŒˆPBĀY–Ŗ:¯RoĀ6}"¸F e>0 YQQąķ†<PĖúúz†ŊĒĒę iLą: `Dĸˇ¤‡nyí}âėŖ&GDÜL0LŅkjjŠ{ėąƒ˜ûlk ÷  Ë↗ÚÚÚ )Œ ]# y‘Dā[ßú֋~øá%NĻW0ĪC ŲHj1ĨF°×ļaÆG aģGЇ4`ņzĀjĸŋŅ[n¤ãõˆĐk\€îã$͇=ŸaĩpS“†eãĸvÄ'&í-ŨƒWXXXS^^žŋ´´´CĻ~ž€áXô.a¤q@CYYŲĪ-Dô–-ņA"'"„F`†Ĩ hPĮ‚5Ļķ‹Õ|¸k``āã;īŧķW¯ŧōĘßép…Q#üšmāé$ī™7ގŒÕčNē3oŪŧoN˜0ዙ™™÷ē-HzŲŗ;*wíÚ5pæĖ™ö1cÆŧēwīŪŋÕ ¯đĮ° ”˜÷ÂĻ 9”W`dŠW^SSSÔŨŨũŖĢW¯>ŒģG™ąôšjQTŦožųæŲ‰'ÜŊ{÷ר'ę5ʖŌK.\¸đģŲŲŲ+§Nz÷ōåË3a,4ŽŒrS6”N\Ŋzõ…÷ß˙‡Š8ĐĪÕΊ$yEYB¯´’#†#°víگΜ9ķ\}}ŊÜŲŲ)åWCCƒ\RRraŊ˙0œĒaŋĐÕEa#g āĨģ ķąĮ;¸jÕĒkJÜŠ|åōōōĢČĶĻM3ŦąCĄÜ"€ō…r†ō¤CGŊRVVÖVUUõ…ôÁ"-ēŅ-GneBņEŠŒÂ´ÁT\V¸@ Œ¸ N´úx8üYE÷?Q9Š7$W­Zõ;L°0ōÕÕÕģuq°>ķĖ3fĪžŨ2Ļce+**vr.@Œz9[nĖ9cDžX„!B¯C̰chsŸ´Ä@ĀŸžzęŠ***0ü-’Ŋ ģļļ•9‹`ú=8Ū†â­č =q<ڀŠ"õz‹’Ą`B"€šB¨°ƒX)@zaXOD1ī)¤,‚"-x‘F´äúč‰%RÃ|ĐCGedAKŪŦŒ€!ŗá|Ĉ­Ž ûāseF“įš­B¤å‡žæf™ ^ØˇrLbž÷ųL-ųjųĄ'RRRB‡&hʃˆ# Zō†áG€Ķ2Ísčad>ąĄŅ†ę´ ”ŌÆ“Ķ‚ēîĀČDÅh2™ƒ2žÚĒ…­[0š Ģ(Ÿ9\ŧûÄOˈÛ‘ .üø[ßúÖØ¸ÉüūøĮ?Î<ūüĢqâ Ā–.]Zāôĸi‘ąBŊÖßß_ÆIc ÄĶļ‘ŠŅŽãôâØe­R įāX>†Gԟ¸**Ē[O˜L͞˜ËOßīuq§øÃŪFčy\Ļ$8Yeém)ąéqĸōX˛dÉŨql‰˛7ØWTTLŽËÕTÍÍÍõõõŧŦ—`Rö ū{{{ŋlŽáäŊūâŋ8zWW\\,ŨsĪ=…\ĐÂŽę‹Ģ,"Á7æ=âÜe-đ¸ô:Q‰ÆŊˇÉd/^üĩHd}&R+§Īq~bT ŖkúPö÷sFōļ”Xô8ŅÛÄŧGœ[ĸŦ¨ƒ/ų˓.\øß˜_Ÿ§NĒ{o“É8ôõõmdŋŖø,++û/ßøÆ7rĸț]ž0ĒöĐCŨÉÁ*zô:#9Ī ÃŲßß˙Wuuuvõ/˛ákkkĨqãÆUE•ATÕÕÕwSC閄*RNøĸvWŽ\YNe|ÚŋüËŋ›••ö<Åö¨ׯÂpŪ¸qŖ0Îs›CÅi¨"ÍĪĪĮJ$'ƒPa q@nå˗ceņˇ‡|ĸķ†ƒÎŅ0ˆGî9Á\įĩk×JŨ§D)h!yÚ~ũúĪ———ĶŽJú˗/ĪŦ­­ÅDä* Tä†@Câã?.ō‰ÎÛącĮžŋxņâXn92’"ę=N EŽyÃŲŪŪūW~õ>ššš¤ÕĢWKxÂmŨēUÚ¸qŖÔŌ‚Ą}ž0ééé)į›JûÔa8rŪŧyž5”D–ųŦYŗĻEq”áŖ>*Ĩ2>˛Ŧ`_į¨QŖūķČ/úDr¸6ō†ķôéĶ÷ûŲû°ō@Ė'ų™ŸW*:{zz>åUzŧ¤ĶÛÛûŦR~Đ%ĒĖËŠ+ĸ6\›œ:uǝķŲ"ËģĩĩõĶ~”iļGqž3憺ûmŲVPô>”F=Mŋ+m[š^šråø¨mS8yōd™_ŊĀ)˛ĖËŋĢMÔB¨ĪÕÕÕu‹/Îô‹h‘å L-Z4‘ƒ-!‚‹Ôļ” ŋއtËĘĘūFiØŧĻ †éŖpÁáŲЀuœ9¤ˇ};F6F:l)đz•(*Ōßüæ7Ÿ•$é'#sĶgƌš~R.˛ĖĄ?˛,GjMOOĪ*?J"Ëå‹ÂZZZū¯C‡ũĩŸåÂ$mļ-e‹I8úĖ8đØĪCŠ‹‹oīŗÆĻc\"ĖōÅ]˜ę[9˜?žųIŖÃėąƒß‹ũ>ô@OæLŽzōVû›Éůīëׯī‹RëúëVH׎ŧē€×q­YØez‡+Ö8¨ˇq@ƒg$DēĮyüøņ^÷Ōōč]*‡eÕC:Č]]],Ję‰DlŋŪ•›ôƒîq‚ ûīŋ˙fkkë0EũQUUåëļ##™ëÉ[Οa´Ė322Æ.hmmÅܓđnîÜšžÕaNä @•e|˖-ŌæÍC‹×ƒ–7FÄnܸ1›AcûÛđ ‘Âė’á›ŌŲ%ďđ~ĒöööTáĀĸFüVN=~PpP™ÂŠ*”ÜĪĄe-š.\¸0EË_Dŋsįέ„LüpndnDOĐ2GcīŊ÷Ūû“ÖÖÖ׌čåÛĩk×0‡įšs#oÖFYW6ŽAdĐōFžĶĻMķmØđŽÅĩTÚsQ6â!h¤ į‰'˛ü•ķļmC#( ĘßFyúUąåiô {Ŋ~ûÛßFĸ%XPP0Ã/|ŨČÜ˙ ŋĄŅvéŌĨ™AįëS~ÅsæĖÉö#mˇōÆZMŋôŅĪžÎû[¤'‘á´XXÁ –,Y2:ŦĖŅ›D‹OV<ŲĐ[¤n†Ekzšø­N!á6ë>ø /ŦŠJOŪX¸ĸÔ6Úā–W§ņĄ‹ Ũ9eƒÅK†…§žŧ1 ƒō?č"ūØÔ #:čįŊ÷ŪÛ÷öÛo­:?L ø3¤Î‰~ģB Ōī…"N%`Á@؋íĀ(=„â l"ûŊP„a&ú3* ÂĻOŸū3,Â!gŒGe“Ŋ‘8Ō+ęû8y¨ĪGЀVrX-åĐ! (SĻLņe~SP8D ›ÍsŠ@Ģ!d8 ášõÃ2øķÚasĘwæįæéuznh-ވōÆ$sᚿˇĖũi:GĐRLĖobđ.ö†ķŽfįÍbyš˛‡h%ŽÍP*žōŨ‹ô1ˇēÉ GĀ ļAËZĄk8'#ÅMæĮŽë‰ÂH+Øú-seų…VhÉÉpĢōė3Ô_č+S” gK__ß%+*T.ԁ‘Ä" 8(­É}Ŗ8Vō4 ãEúH”ÜpŒ° KŪ ĐˆŽáč˙˛"ķüü|KåB?>ž\ž|ų ,ŧ˛âŒ° KæF4Yá aŦČûã?ž[m5MŸÃEb¸6ʆŗk˙ūũ7Ė”†…I„UīŊBKQŲÛDŗ8fyš}÷*}4,Nkĩá!Ÿ1cÆquË^M’ļaČ4šŅĨæCˑ΅ >Ћ/˜ģ•)3lКMVå`&o¤ĶŨŨ=Æjz„‹Äpm” §„eØfŠĀZŦPdö§4¤ZĶ,ŽYžfßũN_™˙îŨģ/Få4Ë—/df8Ͱ Cې‡]J™š}Ÿ>}ú'nĶā$~ûž={.›Ņb†m27ŖÉŒ';ß{{{¯Ú īsØHlK‰ôGŽ4SvH4*]ĨŅD\uo~zqā㠇– ‹Ģ០¨ņO/}ÕK‹ųcčF̓FˇŊōōōēo˙üeüøņoĩ´´|Øë9#l‡ÉL_/Ãa•ōÆo|Cåˆ? õkĨë&ģéŗŧ ŸĮŽûö[ôg~~> §áũĢzōcŧkÉF/üĩĘ8K Gėą“ƒ˜ŸÖĶIúĐ%ėŊVtŠå;888ĀŪ9yb„ saŽtEēĮ™——÷Q@YÉⷙŅ1Šƒš¤‡?ŧ3§įĪž+ŸFé#œ^Z(LøC´ã>üđÃČč@cccĢ˙fØjáfGOLNĐ%üĄ25rNō@4’ŦĖq)ķÆB‘ÖÖÖˇ”~"ŋ›5Ž°ÕãÛ(Ž‘Ėa،ę–ŸĶô™ąĩc4AĶčŅŖyš~ž32•&SJås``āGËAÁQ⠐…6rfq†–ĶķWįe–>Â[MKļÖītZīj}Ô¯åwŪ9ĢEģlÕņĖâɂ]‡0ĘÆ™Wy°Qu%ŦN_ũ{Īž=ļŗ×ēR'ÂŅoŖÆą™ü´Ø0‹Ŗ'sÖ`Cų7rNĶGš,mô:‘ŽzGuÄJØÃDbž3@ŧĪĒ ŽŽÎøX “¯ –OųQžTdå]yę‰ōŨˆ$Ŋti(ĶQ˙V§‹´ĒĒĒž¸T|ĖpņâÅ­nNeōBŪ gv­ŖG-ÎčŠ'o\c‡4•aŒd:€đž4.bĮõ]nœ2‡Ŧ=ŽT^!ĮdΞft*eŠ|g˛† ‘>ûf$oÄYˇnļĸ[ķĀĨ–ʇ}ķH—%4B;ËÕuîuŨŧyķĢO?ũ´ãƒ 1Ôö /˜á2RŅÂ;zô¨4wîܔZ‡HCéá58´ĩŪYZzOeZÃÎÅüõ¯ĘųeeeĨüŋôĨ/é%#}į;ßšüÖ[o­Õ ā‡ŌŌŌũũũʼnķJې ū>ųä“ÔŌõRŪŦĮąoßž”Ž)õMOæĐã'Nŧđá‡ūÆ 6<Æ9}úôīŗ˛˛ž}ōÉ'ßâ…ĖæÅzüņĮĨüüü\LæėiC­ōÍôå2Į(ō€ŽąŧĩŌūîwŋ{ąŖŖãZßBöƒŅHGCσ˛×B—Y+[€f->­īhåą^ƒÖwĨŸ^X-Ök@|åģ2=­w­´ÔᐞÍøūØcÔÂLdŋššš"ˇŖ VđexÛ Ģ”ąōĨĨ÷ÔËCío&sô†DnåëéeYYY°pãÔXĨe',“3{ĨËžĨ¯äĶLŪč¯]ģv‡n!ûŖ÷Đ2 ”ŊkÖŦy‚ k0Ť§œēū/ūâ/žĶÃMd˙’’’ $ãá Â}ôŅGOˆ,W=Úkkk0ÜJn8h@nذá=Ü8đ监bˆĨĨĨŨÃUŠ~=ūøãWT0EægMMÍĢhm“Bx,[ļėÅČy8#ÉUĢV]â–Ū€ÃaâˇĨDzU-S“ÜÜÜw0o@îXxîÜšSÆãûÍÍÍ<Ī:Ôģvíēöæ›oū]脸C@W[[Û9’3UÔw&Lø=įÔcƒūĻkމ…á<~üø?žüōËT‘Ļû ¯^ŊúĮz銴×^{íŨũ×ŊÂļ¸J,‘Ãūđ‡ĶXv4YČÉÉų˙Øö2Í1ķüéOz­¯¯Īxqø˜Āp:[Å>íņ `Á‚'íLĐGu°s]ŗfÍęŒēÔąMæļoi1pˆÚļ#-ũÅФrņLT˰_¨įÚv$äļ”Xô8Qȿ˙ķ5ŗ\´ cÔü€AuuõĸƗšŸæææŸėŨģˇmÛPËođŋsįÎSQßŧ-Ę)SĻüw*ã’ô•¯|åú]wŨõäm`ø~v¸–oX=¤Žēēú¨Û­)f­=žŋŖ%ZUUeéCa-Šĩk×~ÕíÖžåi…6đ•åĄ !āŒãŪëā‚m3r[Jlzœ(ŋcĮŽ]ģaÆë—en˛KˇÆŸå† Ÿ yéĨ—~väȑØö:q_[[Ûģ¯ŧōĘë>CÍMōŗfÍúvœ{?ũéOĘËËWq#sB0ī.ė BæėE$D°zü••Ŋ(a›÷đLÛp BEEŀ(rō’ÎōōōĢāß30I¨°°đX×3`ËQEEÅNAĤ$SØm)J&ĸūžÄžÎ¸-"ˆk% e.))y+nû:q @iié/Ŗ^˜ĩø[˛dÉŖqÛ׉úlöėŲ¸"PÄŪļ¤Āx’ãŦ¸ŒSÁÂ<×ŌĨKy<¯2(5I.Z´¨#.ķÛāĢČ­D=҉˛˛˛ŸĮiU5FU_9Ũč‰ā)Ā)*q(Xčy”——ī÷MūSĮ%zŨQisįÎíŲ´iĶ ūĨâ/…X$‡ŖøPáØAŅô=u!ˇĨøŽ DŊ`ĄįžØ‡AĶúõë?õųNđ>ÃĀ—Į<ąĘ6Ę# hļŠVOMę$IÂ9V G`.—zÃĩ-ķ¨nQĄ!ųá˛Æ/č?zāQiP ɏd\<!ˇĨˆŗw'īžûîū(,đŖ‰EŪÁ”°X(jCx¸ĖYĐ•ž+ĘAÔ*F“D\ ¤'sšįÔC†G,Ī(ô°phūüųũaŸq‹š8ôŠhŋZ‚ {ÄÆģļļ65—ĮúĶRĻyNÕ=ØÔq¯!Œ W­Sä…<ËĘĘÚb4dŦpĩsKĸÅ˙øã_ ڀ"?Ėö8´%/ĘWe#ZEÂt5ŠSž$rGŲ$Ŋy¤ÜÜÜ˙rß}÷-7nÜĖōōōœÚÚZЏØ[9ˇ´´HÛˇo—~ûÛßövwwˇŸm8Õą ĘĘĘū&33ķŽŲsįÎÍ8qâDVyyų„ŒŒŒqĘƒƒƒũ{öėšš››;xęÔŠž3fŋ|ųōGûöíû'I’PaĒNÕ ŠTJ0ŋo7”ÔŲaąÎčŅŖ+oܸ1;###süøņĻL™2ęŪ{īŊSöĉį/\¸pŗˇˇ÷ęāāāĀčŅŖ?¸qãFSssķOÔa͆š*R `đēŨPŌČĢxáÂ…Īæįįį?~įāš–ņ{īŊˇīȑ#ƒyyy œÚˇoߏ$Ij×HōŪĸķM#x¤Ŋ€zœ4â1C vĮealq*†ōĪ^—T’8š`]§”5{ˇ›ōĩĢ'vķ đ#@oĮî8<äÄäĖžvĶ@<ēĐų–ļ˛!?ą—“æŊüŒĘÛ°í ôģUץîåʑá´& 6Y H(ĒHũ…™5”´öĶú›ŗvęĐ=ĒHĩąņʗ§†xĸ2~K˛(ƒ45啖œ+y¤)`ąø–/s]Jy¤IIŸČīh(ņ6$Č#MaČ8pˇzœæĒ€- <õ6ÅF¤á;††ˇO´pyĻeœĄ]$į=7đåÉą)™¸4Or‚!Āë .[b‚ÉV‹\ž÷ĘBã^‘jÉĖ­oŊMÆd ™ĮŨae-WÆŒ˜H i æÍ›W2zôčųāųƍīŊ˙ūûo§ĪÕ[ㄸ<, R‹ 4ãΈ>Đ_|øpׄ ŽžXMĮ¤ę Ü:88øyVÆ]Ôm|KXE]vvöŪ‡~ø ãÛAŨüôĒÜĖFÆpΛ7ī›ĶĻM̰¸?÷%*+Ms(¤T!cw`J’ôہÆôõõÁ8që222Ū¯ĒĒ:ßŨŨũi§wƒĸRßŋųÍoŽ÷ööļ3æÕŊ{÷>įĨĸy ā’%KÍĘĘĒ—eyîøāƒw3ž• #+ô@Ūh8ūū÷ŋ?;qâăģwīū[Ž{^`)štéŌõ˛,?9uęÔģ/^œēÕnĪøCŖ177÷ĻĻĻ1ׯ__jŗ°Âdeeĩ”––Žbu›“kė”WØĄƒĐĶĶķŧέúč  *ļ œŦĢĢķ•oāĀ čŌĨKˆP3Iĸb¯¨¨𲥠–7~766ĘķįĪīį¤pĨ &ø]~:TRȧēēz7 TęĨĨĨ)ƒé'ßĐ'”'4Čxh8 žŠĒĒęĒnC^ŧÔmdáwGŨŨâąá`\ :˙š,,,<æˇRŠ *‰VyQQŅĄ°*Tä ÁoĨRōÎPh :›ķ˜čų>ūøãWü6JžņŽÂ…ƆŒœSī<&æęŲhŠš6?gT*a5˜ gh†QÆŅpąÁ”ĒÛPÆũė ¨uy!OÔĢaÖmĢV­ēFŨžÃĒۜ×6bbņËÜšs{ŧėžĢ•Čė7*•E‹u ôĻM›f "AE–æ¨L׎]ûU"sŊLT a9T*¨Lƒn™.[ļėET$AV jŒ{yyų~×B´‘ĻE0dĒæå,h#‚úC§aÖmČ ĩ¸ÕmĐ5ÔmA#hõŠoŪÆLĐɀϤŧ‚öV:háņ8LɀU>YÁō¨5žÄ=ņ_TTtŨĢFŒ‡×{^?Ŋl4`!áãîՂ!4ŒE0āå‹Ŋ¨åĐ0æyT…ÉOÔm ÕŖm:I`(JŨŨäa›Ž `V°Ō+ņėđ9",ļ}ˆb<”ËmŖķØĸVą `šÂÃ\Ŗ*Œg/xD]͝æÎ‚Ūŗ§Ėé;Nã€p (Ž Íx+,ôōJŖ Nq+ Í8å Œ. ŅäåĻBA#S”ž‡R6ëׯīsĶ ÁQ‘Œã4ģŲß ĖD­Ûŧ蚨#ŦEE%*Ęđ S*å'Î8í}aņ…ˆ•(øĮ")§ zŲŧ­ŽSĘÔčŠĶy^Q+Q⁆­ĶÆôCŨ":”Īô")kš"æ÷p‡ˆ|ƒfĐîtžWäē ēę´nSˆßßWF{›Ŧ0€öUĢVũÎ.JØ?$bK”ņ ÃįdĩŠČ•(ãŨac)‰x, Ÿ¨P6lØđˆ]]Įoĸ6!'ôœœ4–ĸPˇ9i,+{›ŦLBWĄŗvõ<°đ˜/BĪ…,ęĶÁ0VđĸōËčFá°Û2ƒąĩˇÉøFƒĮîÖQ§#ĪxBnv‡ą07*r%ĘøĮģŠQô‘4Æ7FÁ‹ ŪSĶ,ž¨Oč,ĻŌlđ\P‘‡ė” ^§}@˜iA’Wå;Zfvļ+ĀČFĄÆ˛1D_ ✮RÖė ];gųB?Dîm2žŅX˛s{ƈŪ@īāÁÎĒj`$ōH“ˇēnœY4Í)‰{öœÜ¯ˇzõęÔ{ČaëÖ­ŌÆSwkšæčSܗ—‘‘ašU6jÔ¨ ģwIâNAŪøÆŸûÜįî°ēŌ´ŋŋ˙¯ęęØ=ŊօÁ#īŸũėgŗq_¤.–-[öŊgžyfŧ•°Ę0<ōũ­o}k,îLTŌŠ÷ŊXŊzõvīå‘očí•+W–ëņĒōONœ8qnę6đ^TüéūFvË8ō†ÎBwYŨƍáÄMö¨|t%`đ„ąbL*o‚gūA>Ÿxâ‰+GuaØãᇞæ„6ų^ŧxņ¸ŧŧŧg,ō3ßneÂŌåw4|pÉ2ŖĪčyõęÕ˙ÃnC‰ĨĮß  3úŒžÉdō;Qá|>ôĐCwZļŦŽŽŽ+++‹LŨ^Ŧœe l€‘‘Nč}ãMĪA't7+++u737†ķâŋÕN Z'J#‰›Î•FTO0~ûƒ—üü|+—ã~Û Ŋ<ķũņĮ—™á‹Íī+WŽ´ŨëBē<ōŽÆZffæ=f|c8#+Â#ß ˛ŧŧ<ĮĘ!/^œ¯,Ģ#Ôņā•īå˗3fŒémAkĸVˇ'qŨöîíí}Ũö°øÂĢŧĄģ‹ĀF†E^|–™™y¯Ũ!C †6ž RWW×m?&eÚFߐŪöíÛ5ų­¯¯–Žf ´'ZL§OŸžß( žĩˇˇ—8)Tz|#M=ū˜?hSW`^ņü-Z4ņāÁƒ`O˙ŽŽŽÕN HOwÆÂ(eÄÁ†”ēā%ߟųĖgƌ7Ž~įΝ[ôøÆČĘōåË3õžų{É7ōņŠwčī† `@^7 ŋ ¨¨h˛ÁwŨOvųV&´eË åVéŧâ:öÜsĪũ‰2m­÷žžž”9ģΠߌ7č¸_ē^Ā“?YqRÆõøF~ze߯ŊŊ]R×o -zíÔéˆ?sæLŒšōŽ•—į~ŌÄáN\qqņíh˜¸ÆĩDpHēøS§môívbŧ`á‹$IÅ€%ŋôĨ/u;ÉJo3ŪąpÉīí>Xčdļ\Go9áqôxד+ķß~.F˛˛p āœ.ŽąË7äšđô{ņ™ŲV$,qJƒ]ž™^á€ŋ÷‹š- ÃÂ)uũÃč3{:á:žę˜Mė;ʑÉĸ°¤Ķ=Ģz|#oV–Õõ:t ŧCßũZŦgVŠŌ™}ķĸĮ‰ü@Û{īŊ÷ų;v´(ķgī………Ė™3'‡ũļú4âiņ§—‡—­2đũ‹_ü"5¤Ą—ßüųķŗôžųņŽĮ7ëqāģR_—|ŖĨ›m(ĪiĶĻe2Ŋ4âSũÍ ßč ĸޏ›7oV'é)īͧOŸpøđáy0ÁÁÁĨNFVœđ~ü¸ŅM•wŨu—ĄŅĸ͌w#žĩ*äá%ßHīâŋSĩhg~.\˜ÂŪ­>ōxĀ…C–jãé%ī ,HîÜšDŗ‘˜‘‘a{xÚ)ßĀ0ĘŌ@ÅŠ,g^ō´ÖŦYķ'¯ŧōŠĻáĖĪĪĪSæmEænøF]ČōCũ¨”š—|C¯ĀÛĄC‡4YZąbÅ ¯ëtddTÆYà ģ*ˆã%ßH¯§§g\B“ë€=ą‰účŅŖF­mŠPpX‹€Ããūęovl‡˜%K–|ĐÜÜ\¨›āŸūųåFĘ ĪĖOÍ㊅Â\ėŽí›åŠū^[[{ę_ūå_4Ë`E۟˙ųŸofŠŽŽëôˇߐ? 'pŸŨåņvčyöŲgûŸ{îš|tz4âÔ×ס)+30ļŊôøFB¨4đ|{ˇ’Pæûˇûbccã‹JöŽ“WŪ|ķMGĢĮYę§ßLž0@Ø˛…5ƊTŽ›ß áŲgŸ}åĐĄCš+ĒèÛXy÷Ú`¨qš3gÎņÖÖV͎Ž“üîwŋ[ãuŨĒ'sđĒėúŠë˖-;ĢÆ"”ßögv’{ųÄ8¸ŪXŋŅ7/i0:ĸ …ĘËŧ”iş2OåģŅÉ*8đÁ¯yÖ°ųNĪkí^ĸ*Îw)ąÕz×ã[Ī_+ 7~§Ņa”q7ü؉kTÆĢĢĢÚIËjX#šâ[ŧ WīĄ_ĀÄ/ôx×ķ÷šč2Cĩ'Nœp4ß5$&í7ô4ôœŅ7Ŋ8Nü;;;īrĪmœ øĶŖsūüųWŪ~ûmÍ!,ŊxnũÃæÛˆūœœÛCķFé)ŋéņ­į¯ŒëÕ{OOŪœVå}÷Ũįh ŽmAō§G˙3g&ę}3ē׋gæoġŅ7ŗtí|7âÍ;yh…ÕãOĪ_+ 7~ŸúÔ§ĻpŗĶ #<Į3f ž1Š m3gÎĖ‹ ¯Ä'!U222ÆEÎpbūŽÍmú%8Ėđčüæøv'õ .\r—ÂđØ$īáxxų‹G]÷[ŪĀGžA—ßŧÛå› Ã™››;hEé11lv>+ŋ°.;ŪąhÁ‰ÃbŦÎԊÉw¤mÅŨqĮ'­„3 ÃīvøNŋģĻo#9ž>}úU,`°âĸÄ7†ëÛÚÚN‰Â7č´Ŗëųųųē ĸŧŧŧî¨ō=yōäszŧåääā^bKN4]‡.s1ŒxųōeŦ@´´â ­\Š#ÉVĖÁ˜ąuZ)ė›% *Ápb5žž¸’­°XēŦåŋ3foooŸ´Ŧ8xˇĘwnnîu=ž ?„lŦ:‘øNķ¤gÛŖĘ7ZėS§N}COĻvÖ1đ ođaE×Qôõõ×ãûÃ?´Ü9‰ođ{üøqŨĩ)ŖGū ĨĨeļÕú—Ū­Č|wwwą,T=ÅđÂ?;;ÛRëJĒZīJÅÂËz› KũņÕ-~ø!ž˛Bƒ~ĢĶSōŠ|ÎČ!¯ņãĮëĒ˗/„<Ŧ8äå%īZ|ƒ3Ū­đtŽ9ĸ;ŠđĘ+¯ü‡ZzxÍ7ãŅŽĖ­ōŊ{÷î‹:[Quûž={.ëņĒôoāŲØØØĒäAųnÔ;Q†ã…oĐdUæ'N<ĻäAųŽÆą˛nQ~Sž‹Æ7xoJ”īĀÄ ßˆã5īHO]žY>FõēUy÷öö^åÂp^¸pá0kæXe‹°ėOiLô˙§Ÿ~:ex”a˜zĒĘüŅō@ēøCxô>ņ§ŒkF+ž#üĀĀ€îUggįŒ'ŗôX8Æ7žNy×ã40ŪņĪ,_3úÔß§L™b´×Šé“O>ąd@Xū^đ õxg|3™#? ĶÛ•š™˜2eŠĨ EãûđáÃBĐëiK–öŒ‡Á7Ķ]­iöMī =ŲącĮŊīgΜé`<酁? ¤ž#O6eWĪŧéņLĀ‹į%īzåt(Ë8ōtZŋ pa8Įˇ—g4„‹­h0”áÕŊM„ƒaÄ7äÁÂĀ•$~#]¤ÉZ+ʸø†?Äąã 8ûöíû'Ŋ8­­­‡˙øĮ?^ĶûŽô÷Šw#ž‘ã¸0l”tXy‡BŪŧyķ=Ŗ°īŊ÷^ŸŅwöÍ+ž‘žīŒoe8臝Ęiôôô6Ξ=;žĖ\|C_Ą÷øŗZé1>NŸ>}•Ŋk=sss[ f. žAęČĪŽ;zôhQƒa``āōZˇ1Ŧ1ŊÅęEĢüƒ'đfž%A[ŸnëuŖōœX‡"OeĮÔČtÄÍČČØË…álnnūÉž}û €ēYiÜđۈq`$füŋáĪâã7ū˜SĻ͌­#ōÎ;ī ×ĨÛ ‡,8pžåĢ÷dŧ°īJú˜ŸōÉÂkņ~ôčQ]ž‘†YÚĘ|ôŪ‘Æž}û~¤÷ūyyy­ ĪČ1>X3ÚXx-žíȜĒŲ+uŽŅĄ÷DūŲŲŲŋÔû˙ɓ'ī43 Œ–NP|ŗĶVÔų3:ôžĀiōäÉīë}‡˙å˗ŸG8#§Î7(žYŲˇk<ĀËĮŒ…AC•‡ŠÁÖÖÖ×^ũuÃÆTX|3~ĄfeQŖžĀ›Ú_ņģĢŗŗSwņ į%īVë6č!dŽŧí:ÄEŨƅáņ™)°š•õöÄ[aš)*ÄÁĢ´˜eéāsȏUÖķdßŦ>‘ÆÍ›7?1 _PPđ6ŖA+Ŧ×ŧ S%ßČ_Éģ=VüŪ}÷]Ėķ[EIÚÅdĄ•Ļ×|#Ģ2GXĐŖÉô@‹Fĩߎ]ģvîÜi8,ącĮŽī)o;|sč;ʋž6°š6mÚĪÔx(īßŋ˙ßĶķŋJīÛīaĘō@ŊÔåá6/āåWãĶ0¯ŦŦŦv¤­åÂäaD°e…•ZPy!Īôrtŋ{Zđ¤†ŗP‰É7đŘ˙ŦYŗ:}ĒĶâ÷ļ†Œ`´ƒ^iŒŪ.öY†ĩ˛Œ6ŨëF~ĀÛ햓Û´ų=Ceôŧ'ô å+}F(eõKë6\ėƒ.ãĐ1čZXu›Íĸá>8ĢT‚(\PdT NŽsĪéđ@Žļ ĸp['ĮCK & •b8ÅīÂÁ…Yé^f¨RčITähûm@+đÎĀ[IDīh0A˙üiabT 8/; ^•yĸnC}DŨ†Ņœ0‡Jž}Put ēeį ~%­Âŋc/S2/{bH Š‹Ŋ[<ĩ ÖŦYķ=ôĄø^ō&” Grb8†ąŽ†č áāĩÅP &/†CÉ8k8 UîõœŌ[ŋ~}p r BɟÁ{z}ôڀB G¸ŅĮĢŊČ|Øū„zĮĪē iķXˇA‰eÜĢē-a[š|FH.]ētŊ,ËON:õîå˗gVVVĻn7ˇC.n6ĮāÍÍÍgΜi3fĖĢ{÷î}N’¤.;顑L›6­v``⁕+WŽĮÍæ¸ÕŨŽÃMđā{׎]mmmįōōōŪzķÍ7Ÿá™o´N§L™˛ņƍ…ååå9ā7ŲÛuāģŠŠIúũīvâĉwīŪãÔøšá]Å hww÷.]ē´čᇞ=īv]KKKJæ{öėšœ››ûÎņãĮ˙ąĩĩõ5ģéž`Ų˛eßëččxdɒ%wCÖā;™´7 ž!ī7ŪxŖ733ķ===Ī777˙$@>ėf•ĒÛŽ_ŋūæįį¸ŠÛĀ7ʸ(uNåĘÎÎū ĢÛ ëvË8ĢÛ ÷ũû÷ŸJ×mįļŒGÅp*•1šbŊo_ģv­âʕ+÷Ėž={Ė]wŨ•cĸ6(0”ø;zôhĪŲŗg¯;öŖėėė_îÜšs‹2AQŪ1×Ķͺǧ§gF^^^æ‚ ’¨XÔĘe‚"}ōÉ'—?øāƒë‰DâüäɓwîØąãû<K=9 —töėŲ¯ĸ€]ŋ~=ˇĸĸb˛߈Ęcpp°Īž=WÁwnnnËøņãüÚk¯ŊĢ—>¯ū0ĸŊŊŊĪžÜÕŅŅ1——‡ģ$w ZÆ1l_700°ÆjŨÆĘøwÜq233ķ•;wnąŒ§×,īčč(´Rˇ?ūÚûīŋßãWŨEÊUî+׎]{˙‘#GūäėŲŗ÷M˜0Ą3;;ģkęÔŠo466ĸ ĩhEŠ€_qUUUášsįVöôô$¯^Ŋ:iÚ´imųųųīîÜš<ßĒM#Š…‚“&MßŲ'Ož\pĪ=÷ž>}ú';v먑–7ˇŖ*^ė\X(¨]IDATüD÷ĢxŊ+NŸ>}ã;;;ģ§ŗŗķÖÖÖÃn[Úvˆ 8leuuuņ™3gŠXĪËË;9~üxÜ{å2nTˇáâ娖ņTŨÖÛÛûHGGĮ=1ĒÛ.VCŲm–$id“|č{TßĀ3xŖkŒ#Ķ’$ŕņ¸ōxŨ6*Ļ ąM„!@8B€ §#Ø(!@„@\ ÃWÉ߄!@Ž Ãé6ŠD„!WČpÆUōÄ7!@„€#Čp:‚"„!@Ä2œq•<ņM„!ā2œŽ`ŖH„!@qE€ g\%O|„!@8B ÃQ,>"9=ũįS’$-rČŽi ûx>œdnīdë[Ė‚gđîÂÉõöN¯ō$|CŪaĪ—:Foˆ[onøÆA÷avīTWE/ãađ Å ģŒ Sˇ {Vm~~ūÕ/~ņ‹ãmU#’$]ētI7n\ęĪnÜ×_ũxkkëLģņŧ KŧŸxâ‰vĶėīī—đ7qâDģQĨ^xĄ÷Ė™3lGô0.U^ŧxņõ!ŪV˛čččōōōŦ&}“FUČJeeeeŖú ūa„ęüpĘ7Eonn~åĐĄCOę$ˆw\Ëx|ķPÆãZˇR˜X&¸'wGé X–XOĐ$ĪČ X‡Å/ˆĶī‹œÕ¸â>@=UFēÛg%čŌgāí–pˇņãZÆÃ⛇2.RŨFsœnK7Å'B€ˆd8c%nb– BĀ-d8Ũ"Hņ B€ b…€ČĢjGjëÖ­ŠÛŪëëëG|3ķØž}ģ„Å X|â$žYú~~'žIŪVõKd=¤ë¤ë<čz¤ 'V:Y}A Bihh°*ŽÂßöÅAōļ1H×íKtŨ>ff1"5T‹ceåШĻĻĻT/R üÚÛÛS=Sæ?Hø;ŲîĀŌ ëI|ßB>Žō÷jŪŖ¨įāĶL×ĩøføPw˛ũûVŲ ëŋ™ŧ™lƒŽĶ#e8™pQ@ž~úéTīīĖ1? ÷ €1WPP ‹Ū*ÂŖ…†?e\–į§|CQEsnų†.lŲ˛E8yCNZŧ[Õķ¸đ œ”eœé7d.šĶ’7x°"s”í7 Šënø>āuēWõ[d 'AဃaDĪ~Ŧ ?Fü†”=S´V”qņ ˆÃģķŠoā°ágĐįßL/ "T¤JžƒZ×?L–zzŖ ĮŸ˛Éŗė•ŧÛá<)Ë8Ķø‰āŒøũVeŽz¯ļļVĒĢĢģ]/ōĖŋW|ŗNĶi<-Œ"c8QøQI°.;3~Ŧ Ė!.ûa‰dDŧâ›a!ĘĶ+žą ō†Ü•F†W”|ƒFÖíĐáŖGŪÖe#=G ĮŌã•_%]ŒV­2nÄ7㓕qVö!wœßāÅjŨÆø…!AŪW|Ŗl'¤į•‹Œá L1P),üAIāPh”āásJÅc=OöM„§|‹Ā§šF¯ø†Ž0ÃŖÎƒĮߌoĐĻÖõĸĸ"Kz‡Ž#ž=m&ÆģžWYÆQ2ƒŖŦX<>õø­Vë6đ ™#J)Hq¤"ĨöHĩJ IÍKËIÛJGJJˇIK”aĘ8ČÄËė—é”y"‹’Õ–õ’Í’="{MvVŽ.g)Į—+”;-÷H–×–÷–ß,Lž_~NAQÁI!EĄRáĒÂŦ"CŅV1NąLņĸâŒMÉZ)VŠLé’Ō Ļ$“ÃL`V0{™"eyegå å:åå–ŠŸJžJ›ĘU‚*[5JĩLĩGU¤Ļ¤æŽ–ĢÖĸöH¯ÎVQ?¤Ū§>¯ÁŌĐØ­ŅŠ1͒fņX9Ŧ֘&YĶF3Uŗ^ķžF‹­¯uXëŽ6ŦmĸŖ]­}GÖ1ՉÕ9Ŧ3¸ ŊĘ|UŌĒúUŖē$]ŽnĻn‹î¸CĪM/O¯Sī•žš~°ū~ũ>ũĪ&   І.†y†Ũ†iņĒî¯&¯v\Ŋmu×ęׯ:ƑÆGŒ˜ĐLÜMv›ô˜|253˜ļšÎ˜Š™…šÕ˜˛élOv1û†9ÚÜÎ|›ųyķĻé§-ū˛ÔĩŒˇlļœ^ÃZšĻä́•ŠU˜U•Đšij}ÔZhŖlfSoķĖVÕ6ÂļŅvЪʼnãœäŧ˛3°ØĩÛÍs-¸[¸—í{'ûBûǃŸC•ÃSGĮhĮG‘“‰Ķf§ËÎhgWįũÎŖ<Ÿ×Äš˜šlqéu%šú¸Vš>sĶv¸uģÃî.îÜĮÖǝMZÛé—ˆ˙2Â6ĸ,b&Ō*˛4r*Ę*Ē4j:Ú*ú@ôLŒMLyĖl,7ļ*öuœs\mÜ|ŧGüņøĨ„€„ļD\bhâš$jR|Ro˛brvō`ŠNJAŠ0Õ"õ`ĒHā*hLƒŌÖ§uĨĶ—?Åū ͌]ã™Ö™Õ™īŗüŗÎdKd'e÷oŌŪ´gĶTŽcÎO›Q›ų›{r•swäŽoálŠÛ m ßÚŗMu[ūļÉíNÛOė ėˆßņ[žA^iŪ۝;ģķōˇįOėrÚÕR V (ŨmšģöÔą? ėYŊ§rĪįˆÂ[EEåE‹Åüâ[?ūXņãŌŪ¨Ŋ%Ļ%Göaö%íŲoŗ˙DŠDiNéÄ÷eĖ˛Â˛ˇ7ŧYn\^{ˆp(㐰­ĸĢR­r_åbULÕpĩ]u[|͞šųÇ‡ŽØi­U¨-Ēũx4öčƒ:§ēŽzúōc˜c™Įž7ø7ôũÄūŠŠQļą¨ņĶņ¤ãÂŪ'z›Ėšššå›KZā–Œ–™“!'īūl˙sWĢnk]Ŗ­č8•qęÅ/ĄŋŒœv=Ũs†}ĻõŦúؚvZ{aÔąŠCÔĶ)ė ę<įrŽ§Û˛ģũWŊ_ŸW>_}AōBÉEÂÅü‹K—r.Í]Nš<{%úĘDĪÆžĮW¯Ūīõ揿zíÆuĮëWû8}—nXŨ8Ķâæš[ė[ˇMowô›ôˇ˙fō[û€é@Įŗ;]wÍīvŽŧ8d3tåžũŊë÷y÷o¯ņy02*|ņ`úaÂÃ׏2-<Ū>†+|"ū¤üŠüĶúßĩ~oš /ŒÛ÷?ķyöx‚?ņō´?'ķŸ“Ÿ—O)M5MMŸŸqœšûbŨ‹É—)/f ū”øŗæ•æĢŗŲūÕ/ Mžŧ^úģøĖ›ãoßöĖyÎ=}—ønažđŊĖûØú>|œZČZÄ.V|ŌúÔũŲõķØRâŌŌ?B,ž“sMT cHRMz&€„ú€ču0ę`:˜pœēQ<bKGD˙˙˙ Ŋ§“ pHYsHHFÉk>€IDATxÚė\TeöøĪE~‰Ŗ2ĸfJŠHâč (?Rp đ†6l’ģ•|ĘÕ6Í6KWKŋ˚fļî~ ´í“é.ã4S’qąb1% nĸĻ ČuøĄ÷ûĮĖő`æÎĖŊsg†ķ~Ŋæ•1÷Įyžš÷<įœį<į!hšAAgf˙ūK2P€d]ĸ hfĖĸZ^Až%‚ ‚ ÎĘūũ—˛@ +NŖ gƌ!ųBˏ âî C‰ ‚ ˆĶaœ‘ĖƒŽg#ŲB@ƌC´BˇAÄ]ņZAASöīŋ¤€"°Ī™0Ėjg9AĀJAAœŖķ—ĮÃĨŗ1A„{ĐĄDAÄ)0Ļš€˜§[$aÁAnA‡AAÁŲŋ˙’j?gĀPŦ'rƌ!¤ĐíEqp %‚ ‚ ΀øu&Áx}ĨĐ Eq'p†AAAŲŋ˙’*€‡’!g)A¸g(Aš,pœ3 €ŗ”‚ œ%‚ ‚ B#ˇtĀΝ›Ą˛˛ ˇuų}ee Ô××Aee '÷CA؁%‚ ‚ ‚aLwĩ¸ßdCÃy8qĸ¤Ōøß|WSSõõu4ŧãߐ+Ę"‚ v‚%‚ ‚ B"asPlė ˆAAÃķ]YŲūŽŋ‡†FĀW_}Ææ’8K‰ ÂčP"‚ "$Ŧģúú: €O>Y :]3÷uäšMAˇJAAœž””y˙.-ũBhqA#čP"‚ âÔėÜššÛb<}úôīøˇN× Ã„A¤Į€%‚ ‚ NM\œaí¤N× :]3ÄÅ͝Ž^z) ŗ—'NĒģÖÖVA\ÜLĄEFé14M -‚ ‚ =‚ d`(Æ#“Hūūû_=c靎jjĒēŦōĘPYYböûNdΘ1$_čž@quĐĄDA„‚ƒaKƁ?Z @ûÅŋRP+€x‘3f Ņ ŨG‚ Ž:”‚ ‚؍éŦŖņC™|4 ĨišęęÜũû/U‹Ŋ(šâæMę\fæããģ“Aa§Đ ‚ â:˜Ė:ĘáūÖ0Ė:j@MĶtŽ•—Uƒʲ˛/ŋ€<‚ Ā(¯ÚQ÷Fq7p†AA.1Î:šÎ<23Ž$H.fųöīŋ$CÚĢŖö† ™1citŽŗĀⓐOĶ4ĻÁ"‚X:”‚ ŌÃ!B§‘™u4]먒oGk˙ūKš t@sU3f ųÍ Ēą˛Āā8kÁā\’AÄĨA‡AAzAČÁā<šÎ:’Æ˙jišÖ!—q–˛øM}ĨĀ0;ivVÕØG ciĀā\âzKA.@‡AAܐNŗŽ“¯LĶUjn˙ūK208•|¤žRdmeW‚ `p. ÎĨKAû C‰ ‚ .ŽqFMGŒqIæ#ÔŦŖ-ėßIĀŊSiמ“Æõ–ŒsI€‹ų ‚ C‰ ‚ .ƒqÖŅ´P †Ų3ŌŲfmã™J 2fĖ™Smü`˜ũe*Ûb1Az$čP"‚ ˆŌÅŦ#S$ĮåfmÁ¸Ļ˛ N›­h ĮÚ4Wk0VÂeœK5œK—węA؂%‚ ‚ˆŅ!1y¤L>āhkWŘĢëK )Žuēë-™Jš¸ŪA:”‚ âŒkđLĶUMˇæ Áā8bÚd7ė߉I3í.å—IûUΘ1DĐB“õ–r0pŊ%‚ î :”‚ Â1ÆYGĶ™GÆÁÁYĮF§õ–$ļ ÁĀ‚ n:”‚ b#&ŗŽLšŖéŦŖ kŅy@āõ–20<ų¸ŪAWJAaAŗŽĖ:GĶ}qÖa…ąč’ Ī“ Î%>?‚¸čP"‚ ˆ ÆE f;¯ĶcGœUB8ƒ ˆ,¸_tHCĶ´Íûe"‚8t(A‹q–ˆq ™YGŌø_­ģó8&Å|`,2„Å|qvĐĄDAܞ.f)0ŦuÄYGÄ)1>ŗYpŊĨ×ã"⌠C‰ ‚¸ÆYGFf{fkgWŖS1fK € â C‰ ‚¸$Æœîö$ԀÁyDŖq+‚`ļ ‘€ Î%ķAD0ĐĄDAœ“­9ĮŅtkœuDz$¸ŪAgJAÄi0Ļö™ÎXņA@‡ACX؄æĨK7õ ÎËõUĒl ‹š•Ÿ˙Ö>GˇÍ˜ŽÆĖ<2ŗŽĖö8ëˆ ˆ]3˜™KfÖ×["‚%‚ =ƒ äÅááŅSž~z!o÷¨¯¯ƒÍ›sŽ˙:ˆ¯{˜lÍÁ8ŽĻ[s`XëˆF‚ ŧa `)Ā ‡pŊ%‚ôpĐĄDÄí9rüŸŪ‡øHuíĖΝ›Ą¤dßŋkkOÎĩ÷Zf™"9Ϻޏ5‚ ‚b\oÉôRގD:”‚¸=RiüÁ?ūņo)|Ĩēvæ7žžPUutÛãMfåpƑ™uԂ!]gqZ:­ˇ08–¸ŪAzžB € Â7:]ķ8ļÎäΝ›ĄĢ´ØĘĘ õõįA*7{‘#Į?2räø)gÎüp¤ķwÆYGĶ™GfÆ‘Š°ŠŗŽ‚¸FŊ•ųFį2‹ ˆ"0čļ| Š!ˆûâ!´‚ |B„äąĮÆ˛9ļ´t?œ8Qō›ŋ×ÔTA}} īøˇ9BB"ĀÃÃãOAČ ‚Č%" ˆŖqĨƒŠĻi:‰Ļé šĻŗišVŅ4­AgAW‡Ļiƍ͒@ ‚ ŠŒúPbīõq.ĐĄDÄ­‘É˙8vlŧÅã c×YĒeeû™á €¯žúĖėĩ¤Ōx¸y“ƂF‡1Ãč@æĐ4ÕéĐ4Mõ^˛0”FįRiœÉDÄÅÁ”WAܚ˗™h)EXĨ˛˛E$ę/ÖĒ„n?‚ ˆŗ`Ü÷VĐQĖ' €û•b1CA\t(qkzõō,õ7{LMM„†Fpzߖ–;~BˇAÄY1ėQ›ķ) ‚CVķAJA08•:]ķoĖ>}î;¤:]3˛.⊠‚˜ĄS1 ×[‚ĄÂ5.@×P"âÖÜģwī”Ĩ":ĄĄ •ƃN×Üņ7Ž^z)RRæu늭­‚¸¸™īÛÚÚrGčļ#‚¸Æõ–L15œË ãzK,æƒ N îC‰ ˆ[9uÃĉOž–’2ĪîkUV–°ZgŠĶ5Ãʕ™?9sŊ¸ŪAœ œĄDÄ­Ņj˙ûÍŠSå-\\‹mŅžĘĘ iē+"‚؏ą2v6d˙”g܎I!´l‚āJAÜ cZ”îGŗĩ$ųc#<ė(~øĄ¸åįŸOœ‚û IĀĩ@‚ vafŊ% ų¨cD0åA—ĮĨ–?$Rĸ4LJ”Tđé§˙˜ÂÕļ –X´H~ņ—_N?b"ãā2ōiō‘B÷‚ ˆĢC„ •be`Đ¯ų¨_Äq C‰ ˆËa4'‚ûšļ›ã%ááŅ'sswķž•Į'ŸŦ…+W~}ûë¯÷ŦčFĻDž $&˛k„éMA÷ÁĀS€AŋjĀā\âzKát(qz:9ab0ÎōYŗW™\ūģOÅâAķž{î ŪäŦŦ,íÛד§ObEÛērŽÕ]Gą‚ ˛Ā [ ģ|ĄeBwJAœ’NiĸÜOcĩŲҊŒLlš7īĪbĶ=&šB§k†åËíO?ũĮ *Õ˙TÚØfąI›ģLßEAŦÃ$(ŠŖ^ĩ& ‰ ˆyĐĄDÄ)čǘp\Č&3sipUUŲ)>R_-ĨēÚŅ'ĖĖ,÷Aą Ŗ^͂ûë-Q§"ˆ C‰ ˆ`X*ĻÑ‘‰JooßՋ¯÷‰úsrÍO>Y UUeߜ9s|2Īũ…Å}A8ĸS1fKÔ§b%čP"â0Ŧ-ĻÃãĮOžwíZũ˙Νģ´O\Ü ›¯SSS[ˇžŨæééUđÃGæ9˛ XÜA„;ŒN9ôŠ Î%.5@ C‰ opQL‡/23—˙úkíž{÷îJ.üX;[šsįf8qĸ´aÂyV~ū[û„n÷Aą\o‰ փ%‚ œÂG1>?~ōŧ†† ›bbωccg€š‚=õõuPZēĒĢŋ×ĩĩĩîuôŦ$[°¸‚ ˆũ˜Ŧc—ގDnA‡AģpD1GđČ#=”}īŪŨđ^ŊŧŧCCĮ2ß555ܸxąĻ­_ŋ€“7n4í<{öøvWr˰¸‚ ˆ}tZoŠÃū–N(EGƒ%‚ V#D1ˇO ÷/Ō ,îƒ b;¸ŪAJA,â,ÅtîÁâ>‚ ļCDÆFƒîĖZ&q4čP"ōœš˜Â/XÜAÄzŒã&ã\2Ë 00‡ôĐĄD\¯˜Â?XÜAÄzŒëÖŗāūzK\ŗŽ¸5čP"HÅ]Šé Ž‹û ‚X‡1ë# K 4€ˆ‚%‚ô ÜŊ˜âX°¸‚ {Œc°ÂøŋŒs‰ã/âō C‰ n ĶA÷Aa‡‰ždÆf Ö(@\t(čĀb:ˆŗ€Å}A,c˛”@÷g-1苸čP"ˆ‹ƒÅtg‹û ‚Xƈc‚¸ŪqĐĄD‹é Ž÷A1qŊĨ KԀë-'Jq°˜âÎ`qAŽé´ŪĀāXâ2ÄŠ@‡qY‚xÖĪËË÷v[›ūėĩkÜEÉb1¤§âîÅ}‚ Ëđķë7 €$Ü B„Fũ˜ۀ€|gļ æ%$ŧŧméԟ‹ĢĒÚ—/?AB÷JÄe B’˛úē^/ķņô3t¨Ÿ\"éø^{ų2”_ŧØZKQW‚Dĸ¯ÕÖžįˊļSÛ°˜‚tĢ÷!B6aéŨģms<<<‡<ūxtŋącã;ž¯¯¯ƒ~8ŌzåĘEJ$ęüΝ[Ē3g~8"´Ü‚87ÆĨY`°´`p.Ķ‹Aˆ?_ž–Ō[ŲRYY˙ûŸ¯64œ‰N%‚ ļ`’Í!˛Ŗž]ŋūŸGŽü.eT—–‚"..S`]t(§c^DÄË­wīūŖ@Ą°˙bČŅh õîŨü ĮŽq’F†ÅtÄ5āģ¸ĪĉOJoßÖ}ˇv­Ú‹kŲKK÷Ã7ß|ūíŅŖ&9´Ķq;Œēq.™ũ-ŲëÁũûŗÔĨĨyöÎLvĨĶÁ;˙ųĪŧõ{ö|&t?!ց%âTŦœ2%xߙ3?˙÷šį<ÅžžŧÜ#ņ“OÚū'22*C­Ž´å|,σ Ž Å}ÂÂ&4ggŋŨ/44‚™UĒl2Dōöūķū ē A7ø,‡Šë`qŊeR)•KĨÅúöõįCJ§ƒk7o6‡ü(˘.:”ˆS1{ôčcs|"›JŽļĸŊ|æīŨ{ņD}ũ#lŽĮb:âŪØ[ÜgŌ¤Ô]C‡†ĖļĨ[tēføË_洜;W=S_á“l+&ƒŖËõ–_ūõ¯ģ‚üũgËBCy•gMAÁįË˙ī˙Ō…î„=čP"NCrHČ<_ßOųHuíLŽFe.|TRW÷RWßc1éšXS܇ IxxôÉÜÜŨ~|ËUZē ?-ŦŦ,qhq1AzÖ[’`؂DģæxyyFÆ?!ƒļĻdĄĄ!0cf~šB € Í--ËķfÎtČŊrår¸ŅŌ’Âü?A2‚ ”AQ†"jšĻ“hšÎĻišŋ˛Û‚84MkhšÎĄi: rŒfôCŽŅူ°Čõ3fĖįŨ™ˆ‹›uÅöŌą‚  iš¤iZeÔų  ĸč‡ÚZN‹đ˜C {Ž{Kčž@Øã)´ÂĐģw Ĩu“’qīŪ@Rt•Ģ!IˆÅ@RČ%ŗ×Š2dA@¸_L'Ič~@Äy0’:Ē2Å}‚P2f|\Ü ŗ×¨Ŧ,‘¨?Ôן‡îŽ-,Ü))ķ,ʓđtŸņã'Īûᇯˇ Ũ7‚¸7Æ´Cę˙ūũM–Ž×TV‚X$˛žē*ÚŖŠŦÃZIKE}<"Fčö#ėÁJÄ)Č3&-"0Đėf“Ú˗AC’ <ōĩZĐ^žü›īIЉXÜņosD?ü°Ī¤ĄCīŅ4aœĀĘŦ‚˜…Ļi­IôūĪ™;žĻĻ Nœ(ĐĐøęĢmPSSõ›c*+K Ŧl?ĢûKĨņĐĢ—×ĄûAžÃš?üáeMeĨy­Ļ4'N€,4ōŋú ´55|Ÿ_Xb‘äR)hNœčp.ģcÖĉ#`˙~ķ3ˆĶ€%âT76Îĩ4Ŗ(‹!K&ëø˙Îŗ™ęS§@"6č;Æé4‡\"OpĄÛŽ ˆk2bĸéŖGGy›;&(h8<ųäŗ˙ßÕū”R)û,ÖĐĐhlŧÄoE A–gd —KĨf‘A֓OvüŋX$zā{fæ’ųwg‡ŗ3ښ˜ö×ŋūQčļ#ė@‡q ZîŪhÉĄûú‚¸woČ×jA.‘t8ļ"‹ĄŽšy¸ĐmGÄ5ioo‹ŗ´MˆHÔD"(,ÜcĮÆCPũ*ĮÛÛ§ˇĐmG¤į°tË–ą–Ž‹D ‰ ŋ°äcĮ‚$čÁä E\\Gškw)ąĻČBCáĀ_˙Ú*tÛv C‰¸b__Ȓɀ¤(PŸ>m÷õ‚D"Î7!G1E$ę))ķ Ąá<”–˛KmEqÖ/XĀĒæŠX$‚Ŧ” @]ZÚå1ų…… œ3į7gW°qdįJÄeP••ĒŦ ŽĨöŌĨž÷ž´§ôzV3˜í÷îĩ Ũ.Aܗ;7ÃΝ›ĀāXÖÖVŲyEAĮrčøq‹Į¨vîÕΝ`Li­­ũÍ1ęŌR…†‚,4ÔbĘ+âZ C‰8>ŊzŨ°tŒlđ`ˆÅ@éõ Ŋ|˛d2 ôzŲ´ ˛d2А†-‹´—."ÜōōČ;ííw„n;‚ îKhh ŽjjĒāÉ'ŸŽ^zé~ÃĘʨ¯7Ė^ętÍB‹Œ ō_ũđƒÅāģ,4$AA@ét ­ŠŦ'ŸJ§ƒ— Û}ĢKK!į“O į“O é­ˇ€Ōé,Ūwũ‚G„n;‚ĻiĄe@ˆ>üÃÅŅŅ/vĩˆ)”^ÚK—Ėn ĸ!I‹[†ĒÂŽ(.>ōåŲŗ BˇA×ã‘G{jâÄ'÷<÷ÜfcœIkŠī˜ã•WϜ;ūėŖBˇAÂūũJČĩtãLZ*āÃmM ŧĩmÛ{**–Ũ|Ä28C‰8­wī~Ö9…ĩ+ÄžžE6Î$€Áņ¤iēPčļ#âš\¸đķįÕÕˇ-'õįĖ™ŦŦ,ēí‚ô–nŲrąģ5‘Ļ0ۂpą(ĪÛBˇa:”ˆSP~ņâ‘._npä=ËΟ×øųg•ĐmGÄuikk9īČû8QwîÜBŊ… ˆÃXŋgĪgЏ8Ō‘÷üju5˘AŲ%Ä C‰8 úöös$åŨAéõPCQBˇAׯĶĶģԑ•[ĢĒĘŽ9ķÃĄÛ HĪbūûī;4xö…âĄÛŒ°JÄihš{7įÅ}ûÚq¯ė/ž€Ą}ûž%t›qmNŸūnMAÁFĘÅtvîÜ zũíĄÛŒ HĪ#eüø×u¯ķō…n3Ât(§€ ņwŋūšrY§#™­AøB}ú4T76’E$O„åŊEAē€ ˆ\Øéį×OŊyķŸyŊWMM;vāÚųķg¯QD„Lčö#Ō3 BöģŋũmŨ‚M›ŠŲŦĨ´—=ĮŽ•3gæÁŽ("8čP"‚CD€ļúęÕQ_Ÿ;Gņ•úJéõđö7ß´Ė › j(0…‚ Ŧ "‹ ˆ" išŽüņĮoŗ=}ú}‘Ÿ˙Ö>šĻIŖc™ƒ KA8ƒ ą1؞ jšĻ3hš&Er’ÖXŦnm ’  ĒĨ­íw4Mk6šŠ‚P Ũ/H÷ā>”ˆÃ1>š@€ŠĻéßDģ↠{įĄ>}–m™5Ë[ėëk÷=)ŊTĨĨđũĨK…‡I2ĩ ™Ä`ˆĀIŒ2i…î'Aœ‚ äft€a_JOO¯/_žÅ'(h8'÷Ūšs3íøų×_Éöʇ b c֘ Žd×ë÷īĪ€<.īûú–-úw,ˆƒ3ē´ŋŒreAĮŠ…î'äAĐĄD†‰Ķ&€lšĻ͖ žņrՕ+īŊõÄ>ŠŅŖmž¯†$aYQQû`‘čMKۄ]& Ϟ$#‚ î‹ŅQS€X:j#GŽŸâáŅkottrŋ§Ÿ^hķŊëëë`ķf%Đ4]XYY’ĘæŖŧš`ˆčŖc‰ kLõMĶ9–Ž?++-Đß˙ŗgžxBdĪ}ĩ55 Õ@vwΤ‰ŒĻv¤ŠĻiĐũ†@‡qÆTäXŖVN™|öÚĩ=Ãû÷—*ãâĀÚŲʍ~¸|šáÅņãS2ÔęJ+äň?‚ôP¸,ÅÄL;vįŽnâ…*°vļrįÎÍP^~词Q˛WöėųßĪlßō ‚ tčģ<°"pÖÁūũ’#'Onoŋ{7Z.•Út˙>üåķS§ūۚ='1øī| C‰đŠIÄ\MĶ´Í›qĪ‹ˆxšēąqe’D¨ŲāÁŨĢŊ|4$ ˙ĒŦŧ1.(čũí'OްC~4ˤ‡`b¤ˆÁü˛ËH™<9=íüų3›##‡ŽRi|ˇĮÖ××Aee 9˛ûļ§§×7lg%-´‡IËGũ… ˆ)ÆŲž\āBßíß/˙ąŽî¯õkÉą$ëëAúđÃĒ/Ŋ4ßŌŦ¤…6`đßI@‡áģ"^f4čŠ!}ûf_×ëĮôî ãdž;ßÜ|õĖĩk÷‚DĸęļģwˇĒ­ŨÆQ[L×WæcŠ‚¸|§Q9~JŸ>ũ^ÔëoOē{ˇ­˙čŅŅ}<==}Ž\ųõj}ũ9~ũNŌôŊB­ļ8ŸKŖSÄ1…OŖPLÎ}ĪŊ÷ŪķŸŧöÚ-MeĨL.•Y_?ÔÖūđtllí’-[¨ äX3#ÉĸM c›0x&čP"œbí:Iîeē›–ĪčĻX ˆ{!T°ČUg ĄK00† ˆŖŗŽ‚0ú 1ß3ˆļ.¯BėJ„3Œ/˛Ü< Ž)âú˜=*ĸm“ĪA?AœƒždˇtšØČÁĒũŽJÄnzjǁąŨY€ë+ÄeĀuŅũ€âæ˜lĶFÁšr[GÛ.,čP"6C„ /+§ë$] ãæŋ¸> Aœ“Ā—†MIüž‚‰'.q šüČŲéIŗŗB‚%b5œVs°OÄ9Ac‚ØOâƒÜrĀu„€Ų)ü‚%b8gœĩEįĀt“nĀw‘5&[=iû A\†žēüČZЎåt(V ĸ˛ė/\Č ÍG×ŗ ŦĮ$Ģ ‹“q:”ˆYpÆÍ>0† ŽÁđĴs1:–Y€Á1q*0xf?|í™ŪA‡éTT܁ë+„?øÜ¤1€}Œ În‹Á=ĻK$°h›m C‰<n~Í Cî@]åx°ĪDXLöĪUŅ4­ZwSũmĮChįÁø"IĶt ÜBĶ4IĶtŠ]AŦÄøîę*‡BĶ4eŒŪဂ ˆ"c  A!BNDMĶ‘čLōƒŅ‰Ė AƙK„8Cé„!1bÄŗ~~~ž•••Å`ˆ–đ2Ŗ… ē…Á$ŌČ[io㌂\*•θ}ûļūėŲŗŸĄŽØA iPĐlæ˙+ëëw;ÂĀÁčąsáčĨAH‚‚†eûųõpûöķõõįķpâŒãŠB*•&8f<Å:Â!Āē|™ņ^†‰—J'€ I\\Ü_Z[[㇎=Z$—ßА$ Z­***üũũët:Uyyų.î ¸NRPøØ|8::zŠH$Rļ´´Œx衇†FGG{Ëd˛ŽīĩZ-|ųå—ˇ››››‚‚‚ž>tčĐ üís!‹yä‘7u­­‘!bņCŅC‡zËîø^{ų2”_ŧØZKQWDŪŪG/\x‡Ëu=&U“5¸žÅų01zI0^œŊAˆ‡ ņrßžâ´ģwī†=ūxtŋ‰ú€N× ĩĩUđãå7zõęU}ķ&ĩīüųŗ˙DŖá f<Ŋ~ũú¸ČČČ@™LÉũIyF§OŸÖ544\ôöö.---ũ\Œ§hŸ9<:õ 0ė*Ã2‚ÎP ƒsŠ1ūŋĶ‚Ĩ¤§§īēvíZĘĸE‹üär9ˆÅbŗĮkĩZPĢÕP\\üSJJJęĒUĢÎY{O\ã|pąžråʕÁ'OžÜ!‹Į͝;×Į4(ŅÖŽ]ÛFÄáǧ ŨˆsA„8=,ėĐôøW&Lđ–K,g7jHūņũ÷­-wīũōėŲ;īŲ.—ŋ×äÉéi$ųã§ŗfŊÔO*‡  áf¯¯¯ƒĘĘØģ÷ÃĄĄ9ÅÅģū)t ŽËʕ+ƒ &$$ŒR(`”í Šĸ@ŖŅĀ|p;  pĪž=slš/Úg΋ÉŊjšĻUv\* îjd ų 'u,ŅĄˆœœœÉÅÅÅģrss­>_ŖŅžeËÚļnذ!›íy˜2æÜØĒ°–/_žúyã7< …Õ÷UŠTđå—_Ūž0aBÖúõë?ēáYķė÷—.å/ŠŠōSŒmõųęͧáƒīžģ=aȐŦõGZõL™TÜãtÆ q Ļ3Ę`ƒc3íX¯^Ŋ&.\øˇŽŲHļčtͰyķŸáîŨģß=z`’Đ}¸K–,É+..žŋnŨ:O6ŲÎäää@qqņĩ„„„9ššš_ŗ=ˇčq L~'k #ÉĀP§Äžuį$dƒĻÄĸC)ãÆÛ$‹˙gŨēu^–ĸ^æ ( T*˙ôŨwßŘ´M#Į˜2æü˜(,ŗë+ ‚O›6mwŸ>}ĻäååYœá6I’đâ‹/âl%S%’ƒŊ{§ä͜ b__›¯CéõũÅpíΝÂÃ$iņ™Â4/÷ÂÚæČ‘ã§xxôÚ;kÖKũââfØuīŌŌũ°wī‡7îŨģ;ëĖ™ŽŨˆķC„8**ęhBBÂ(ĨRi×xĒÕjaŲ˛emEũīņãĮ[¸/nYábØ0“lËŦ¤9rĀ0[é4 Cé`âãã?Œ}Ņ–YÉîĐh4°téŌ‹'Nœx¤ķwF- Säéw!Øė55zôč_V­ZlËŦdw¨T*8tčP!:•=“qƒŸx922"ˎ`WgTeeđ'NhO]šŲÕ÷&ޤ8ZKŒ8&Čēy9rüŊūÖūˇŪúWKé­lŅéšá/™Ķâí훂N%b‰ącĮ^Xŋ~ũP[f%ģ#''ĘĘĘ>*))yŠķw¸•˜ëÃ2š†ß™kT`p,t(ČûīŋŸöõ×_ĢwīŪíÍõĩķķķĄ°°đÛŨģwOĀoŨ‰î‚éééģFŽ9›ËāCFFx{{ŋ˛mÛ6\‡ÔƒČŽŒ\/öõ]’ËĄAŐŖŅĀųææˇˇŸ<š‚ų['=ĨÂõBî‹ĨßzäČqË–ũ#€+g’ĄžžÖ¯_|Ŗēú{ërg‘ÅėŲŗĨ¤¤LĖĘĘâãÚ­“'OVŧúęĢû:ŪfļĘÕCž1e†ƒ2¨āņÖŲ`X[)8¸Ĩųę̝ūõîģīrîLdee§§įÄ5kÖŧlL3*CúD:“Žq˙Ę 0Tû* "7''g˛N§ãřČË˃S§NmZšre°ĐíGCB!ũî×_+ãâxšž2.Ē˙R PH ‚‘ †õ$ŖžBgŌ1ŲÃ2äÆ=,å†5““&Mãܙ ŅŅÉũbbĻēįd͚5/{zzōâLŧûîģŪŸščŊ<0¤g 3éĐ4­1î3ŽÃ>ãY`ņ|ë\°oM&g Cé ĻNzpōäÉb ‹*‰ļ’—— n|ë~˜(,rįΝ_đåLˆÅbxã7†ËĸvæXˇn—H$âwfqøÉŸîĮ<Äf@‡Ō\ŋ~}œĨŲ$FÔjuˇß“$ å,‰øøø~aaaO Ũn„{‚Œ9ōaKĮYzž …œ,!‹ÁĪĪ/Xčv#üA„8°OŸĄ–R]5$ ’õéĶ6}Ī öõ…Pąx ĐíF„' (;6Öōö ••%PYYĨĨûģüŽĻĻĒËī:;‚Xīی¸7ŅŅŅSâããûY:ŽÍx `¨n™L÷îŨ ēŨˆC°8chéšRŠT ŅhXŲj€ĨûC„X*•ú›;&??Äb1ČåōŽĖ­V $I‚D"éøˇ9är9 2M7$55õŅŅŅ>æŽąô<ĢÁ‘!>>ž_ff&Ž=rSRCCŗb‡ ™;&_ĢqīŪ —H:GkžīLė°aĸi=†‘úŽN×<.44Âė1……Û@$ęRi<œ8ap,jjĒāĉ €¯žÚ55Uf¯:]ķ8ĄÛ8"‘Hii‹6ã)€Z­fđ—H$Ģŧ"î‹ Õ]ģ…ÍsÅL"ąÜÆF §ŊĸCÉ3ąąąK-9bą¸ÃI‹Å Õ>X”U­VSĖG&“YŒVČårĐétÁBˇážÖÖÖKé9–ž'`Ģ :ŽŊpáÂLĄÛŽđCËŨģS,­÷î $e¨‚.öõíåËV}ßšD-wīNēíˆp!ęcé8‘ČęëĪ˙Ũ˙§1(h8<ųäŗ&ĮZŪ$ ¨ô€N§ ļ4˛O™€?ĸŖŖ}bcc— Ũv„W,> lž+…B …‚õŗčPē7ˇnŨ ąô00 €A1q”Ī?Hčļ#ÜséŌ%KŸ~žär9444Lēí?\ÖéFÉ6{ŒbôhPŒ $Euü›í÷‘  —uēQBˇŲ°a#D–Š‹›qq†´ØúúēŽH‘Č ˇÁØąņĀf-ĻņžüV4C\‹v›ņ”$I`[$O"‘Ā­[ˇB„n8Â+;kžĢœœ (ĘŌ%J'"??”JĨ5Ҥ‡ŅˇoßžlåōyōôôtHQ)ÄšÉ×jA’n‚–žG[),Üsæ,üĶ(õ‡””yĐĐpžÕ:Ją…îÆS­VËڙDÎ˜ŗĶL÷EešDIĐJ'A­VƒL&™Lö›ŠoĶ)ŠĸĐáD,bîyB[PŸ> ˛!C@6xp—)­–žG[)-ŨĄĄņ@ĘëΝ›aįNÃN "Q¨­­˛õŌ-–ÆS­V (ŠÂņašįJĨRą-ÆcŠ Ķ˜čP:jĩrrr ''’’’€ĸ( ( BB YYYY vĩZ-ĢF??ŋÛBˇ ល7oŪ´tŒĨį ā~Õ`ĩZm1•‚ĸ( âšĐmG„C}ú4äh4ŖŅ@Ō§ŸuįPz=„lÚÔí÷ÂĨĨûá“OÖÂ'ŸŦ…ˇŪztēfĐéšáĨ—b 44‚‚†ƒN× 55UŦ§DK°ą“,§2™ äršK¤$"ÃâÃ`éšbÖN2gšt‰ds_4M yˇ'::záėŲŗ˙ŽTÚ_Аmĩ'Šĸ %%å§ōōō0ĄÛpËôéĶ‹?øāƒ)ŽœĨVĢÕđņĮŋwā%BˇážØaÃöŦš2å)K…y¸DC’°ōȑĪËΟOēũˆp„…Mh^ˇn¯ÅmĖÁ8“Ri<Ģã—-›uŖēú{ËÕ{ˇ'::ēē°°p”Ĩē\ĸRŠ`÷îŨ,//ß,tûې@‘Ŋafŧ­(ĸ˜*ļs ÎPōLyyųæ˛˛2×bûPi4đ÷÷Įŧ 7„ĻéBļÛ}p…VĢ//¯˙ēí?ôņōÚei›ŽŅ$ īß˙cĄÛŽ Aö^ƒŲR„-zũ­Bˇqüũũĩlļúā’˛˛2:“n'ö7ŗ­ˆ8öaî:” ŽŽîē#ī§ÕjáĐĄCī Ũn„{<˜ĪU€‚-EEE {÷î­ēí?Ē­ŨVÕĐāĐ|­’ķįol?yrŸĐmG„Å××ī˜# éTV–€XüĐ×Bˇq:´ÂŅk›ššŽŨn„w(pläߐî*čD:”@$U8rVЍ¨¨ĻiœĄtChšĻ.:jŊ†VĢOOĪ3Bˇá—Ë:ŨŌAĪIQpģ­íœĐmF„įøņ¯ßûá‡âGŨī›o>o9~ük ļ"@Ķ4YTTÔā¨ûŠÕj i+Gõ mƒ;ځũ čP:€ŖGÎ_ĩjÕ-G8999¸]č6#üŅŋ˙5ŲŲŲš×k¯Ŋv;((čUĄÛŒđ‹w¯^¯ž¸o_›#îõâž}mŪŊzá3…MĶÚēēŸ~ŠŦ,áũ^••%PW÷Ķ/lEL ܞ““Ãû}(Š‚UĢVŨ:zôč|ĄÛŒ8„|plÕUĢKÂr :”€Ļi*""bƒJÅīZY­V ååå°xŠ{sčĐĄmíííßō=띓“…˜îęū”_ŧxÄŋwo­ĒŦŒ×û¨ĘĘĀŋwomųŋG„n3âČd‰ŠüvģN×ĖÛ=tēføøãˇÛe˛ÄTĄÛ‹8XR^^~īÔW•JhšÆr°= Er Žđ €ĨÃØž}ûŠË—/˙Äįđe˖ĩŊüōËiBˇáŸ1cÆĖ]ģvm;_ŗŪZ­JJJnėŲŗgŽĐmEÃiûΜšÁWę+IQ°īĖ™ģ32Ļ ŨVÄyØž}ũšˆˆØ­ģvņW§d׎Íģuûöõį„n/â|ŧüōËi˖-ã-CCŖŅ@uu5š}ûöBˇq(ųĀę+ Vv5JœēråĘ|8999đØc}‘‘Q)t;ūYĩjÕšđđđÅ ,håãú˖-k8pādĄÛ‰8ŋū•š8tč+|Ĩžž¸o_Ûđūũũ+F葨ŗ'/ģĒĒė'>R_++KāÔŠīČ={ōŗNq9222*{ėąøH}Ĩ( VŽ\yC*•Nēˆ $Нdđx}ĢĀ}(Lrrō<Ŋ^Ÿ÷¯ũˏĢŊsrr ¸¸ø›ōōrtzS§N=8a„ĨR \ėĨEQ,X° õʕ+ëJKKßē}ˆã‰>üÃáūūŋ˙ 5ÕGėëk÷õ(Ŋū°{wëś7ķŽ_žŧXčö!Î Abąø!2+ëm˙¸¸œ\ŗ˛˛6lX|ĸŽH0Õą„D"9•––6zãÆœ\$I˜3gŽŪĪĪīÕ˛˛2Á׸!‚!ޔ\oxš N°v’JC„ØÛÛ{wddäø´´´~JĨŌækiĩZXļlY[mmíįΟ˙_šĻæÁBA2ąXŧyøđá˛uëÖyZšgҍÕjXĩjÕ­_ũõĖõë×ŗ°xEĪ„ ˆÜąø‘!ũú=ĩâ‰'üävž4$ Ģŋųæö9Š:táÆ34M;jM âb‘ˇ'MJøđ҉sæ,‘¨ŋM×Ōéša׎Íđë¯äˇĮŽüühšÆJ¤[‚@îäɓ)§Ö­[į%“ÉlžžJĨ‚}ûöŨ¨ĢĢ[öë¯ŋ* ƒ=ä˙k/f>ĘFÔWB„ Z[[_/++ë_^^ūmRR6l*ž““¯ŧōĘĩYŗf=]WW7˛Œ é!0 EQŠųË_&üíokČÉÉkSĒ)Š‚ŒŒ ØŊ{wåœ9sŋ~ũzäŸW¤ADˆkššžMßpėXuŽF”^oÕu(Ŋr4ØrüxeBppøųææt¯ Ā<4MŋvôčIŊŪ~įnے[YYų˜^o=z`M͝īa{ôqk˜ą2Ž9’1kÖŦ§_yå•kļ¤Ā’$ IIIP^^ūmYYY˙‹/æƒaŠĮԍÖĮĮį&Ø?Ŗ¨€p2gg(Š1Ģ5IĖĖĖL;uęÔæÔÔÔĄ2™  EˇįkĩZĐh4°oßž%%%/™\[ ƒ3Kîņ÷.CÔŗ#"‘™™šúøņã¯Îš5̟BĄsVF ކ††.9tčĐ6“ëw °UízYį™œĖ1cVéŌį„…‰å ˜›ąÔ$hHvUWSŅ?ŧ|[UÕ?;Ũã7:éŲt÷Ü!;6îŋA’°° ũ¤Ōx Ūå5ęëë ˛˛ĒĢŋŋqņbÍ%™,1ĩs|öŽ07ÖÅĮĮxīŪŊŒ´´´~ršÜėxĒVĢAĢÕÂÁƒ/†‡‡/Üž}ûžN÷éō9GzAä‚A˙¨@†gNaÅ%4`(žÃ_eO{ۈĨc°4˜!KNN~íÖ­[“ÚÚÚúĮĮĮ÷ņôôô¨­­ŊxūüųŪ<ŲØØ¸ŗŧŧ|s7א€ÁŠLB'Ā}a< {jȐ!Ųׯ_ãĮhoo×WUU5744´I$’ī.]ēôAyyų‘nîƒ` 9äŌ4ŲŨ1aƒ=5¤oßė{4îåáá=~đ`Ã3uīžžĒĄĄšíŪŊV‚8ĨkmU™Û„ ˆ ČÆĀÂFĮ! ›°ÔÛÛg:Ax 2$˜öķëĐŌĸŋ~áÂĪ-4}īrkk˗ÕÕßo5 °uq-t*‘ØNŖŖŖ8đ鯯Æ1Æ ģ2Ā0ž–””ÜōōōjîĶ§ĪąC‡ŊgN¯á˜Ú31ޝŠ.~w1œJœLÜ_gŠCjĢF$MēuëÖÛæt›3€Ĩ°E‰@-[g–Ü‚ @mŒtą9^ &yû4MŗŽpáčūØĸ3:?S`ŽÂl ÄfŨb|^Ŗ‹˛öB§č!ĪÚPˇg<5žcjÂ$›Ė扂  ĄiÚ)ļéVNt(ųEåa|ø4MgŨ~„[„0†Đs_„ @™ ˛8SŲafÄA lã,y>괞‰ĐA-t*{Æ €|k]\§Â\‘3€EyxÄhŦ)ĀĄ• 3WŖ#€¸ Æĸ”Ŗ ã '3*7ÁhTå‚Á¨r¨Qoŧ_6`ņ§‡q\T‚°K3’@…ėzB;“Ėn\W‡¸)F›´×™4ĸqv}…%OzjTXVļsŒM‰€Û.äVvœÄ¨Ō‚áš*@§˛g ô¸Č`ŧw¨ĶzΠ÷ŒZŦ|íĻõŠœC›M Öņq|›1å•{œIiaēĸëcTLyB§;tWYq=œŠ0Ž1ũQIĶt’в üá,Îd'™œfŦFøÅYk´ŅÜ‚ ˜%œŲJA9ķ8‰3”üā4JËÃÔÅÄ\‰˜¤)æáŒ’ëb4`ōA?tĩPcŠžûÂʼnœIœŠėa0ÅėœBī1˜,)Á™J7Á˜ĘŦæ!đŽíT°ĶŠ@‡’cŒF‘ŗ)-0]ŖĶ–†(—Sa&iŠEB˂XŗFÍōhŅŠt?œQ™b” S¯ŨģĖŠôƒŅŠ”ŖSéú> OĪZ>8qÚ+:”bŒJÎĻ´°†ëa’žÃiʝĘ|4ū] ĄŠ:ąÅ¤PŽûv:é1g ˛>€Qļ @§ŌípÖ ZdÖ)piŒēC †ß’sŒļ Dčvv:”aŒ,‰uŸトQX×!œ(-ą3ÆÁ™Ä*uށub…1R/ÁHŊëãŦkÖē‹Dš.äL2˙$Āl2W&T:”`üqåΞ§qĀTƒÁYAœ“AP-´,æ0O°J“ãj{ž™¤9堉XÆÕœIt*Ũã8ętcæĀ5ŊŽ‹qœåj‹s¨Ā)×QĸCi'Ɨ> xšâæ\ĢäÜ0Î™Ģ ‚hü;7Bí…˘ūåĸ˜TƒVš’3É`ēNJׄI›w֌1s Séz!…#2€˜´WgÔM¸mt]ĖC˛]ŗf|ˆ @Ø šmmŗÕŠ ĒKi]­ÍŽÂ´ŸŦ‰VšÚL’‰ÜVĪFĪé,ÕsIŒ:†Y3AYc ;ã6 VļŨĻY.[õ9bĀŪūsĨ4C íČCæQ†•įŲüÎ"ö÷ŸĢŽŖ]´C `ĐßÖč?[mÄØwrŠT:›ų[eeånИC[„8,ÃY'z¤CI„$66vž¯¯ītÁĄĄĄtŋ~ũ‚ÚÛÛõUUUÍ müąW¯^Ÿ:th[7×pɔžNm0;đ!Q(Ęēēē'`L&këׯ_Н;wŽWWWˇ444´I$’ī.]ēôAyyųĄÛ#AȒ““_ģ~ũē Mœ8ąWīŪŊũ™~jnnĻüũũĩ‡z¯ģgÅMŒŗ{TFGGO‰DĘÆÆÆĮŊÂÂÂ|z÷îíãÆz­VëW‡ūZ­Võd' 99yŪŨģwŸeú)""ĸŋ§§§ī7ękjjˆ{÷î]Öëõ_–••­īîY1eEéŠĪ“I;Ŗ*Ō\[íÕį=‚ Ä2YBAx¤Pԕ°  amƒmoo×_Ŋząņƍ&ÚĮĮ¯öÆĻgÎü°ŲÂĩÜ™4i+į$zčЅũüžŽ×éÂ$bqÛđūũzzxøÖRÔÅķÍÍŊ}zõĒy{ī9XS“īĘī#_XĄķļšÜř4i+ģ ::zʐ!C‘$…cĢu˜Ú%!!!EGG{Ëd÷'†I’„ÂÂÂÖēē:Ęßß˙¸N§S™ÚģÆ:”#gÙĘŲÖģx—Ģ'9”ŗf͒ūüķĪ{&Mš48::ÚG.—ƒDŌuÁ$­V jĩöîŨ{cā%%%/™~īLƒÛJwNqfffÚŠS§6GFFJIIņ‘Ëå w=ģŽŅh@ŖŅĀŽ]ģ¨ &ü}ûöí+„n—ŖHNNžWSSŗ!##cL&…ĸëŦOŠĸ@ŖŅ€VĢ…‚‚‚ĢĄĄĄKL [Ww&-ĩ#33sõ÷ß˙Į9sæˆår9Čå]§˙3ũTXXØRQQqÕ××÷÷=)P˙áŊ{÷2âããû) 0ÔL!I4 ”••Ũøé§Ÿ.Ĩ¤¤¤ŽZĩęķŊ;ģLéîšbôų„ ÄÆÆöcŖĪ÷īßK,īčŦĪ{"Aˆ##§îöđđˆœ0A.’Jã!(hx—ĮÖÔTAee ”—ēáãã{´˛˛$ĩ‹ëš•3iŌŽn”ŠÉA}{{LÚȑũä Čîō$E†$a˙ŲŗēÛmm5‡ų%Ņ•u=WÄĮĮØØØ˜1kÖ,V:¯ŧŧŧåØąc—{ėąôŊ{÷VšãnΤIģēĩl[ÃÃÃnßž}ŸĐm’•+WŸēmBđģßŊēúÔŠōĨĪ<횟ToÕšĨĨûaīŪoL˜05į˙ū/÷ŸîëL2tvVÖLúōŸÎM9˛Ÿ26ÖĒkiHVķÍíøaÃÖ¯ų曀5… 7jÔ¨/ūņĖčø}4M' Ũ6>čŦ˙VŽ\|øđáÃqqq[ĮVíîŨģ' Ũ6!Xž|ųęcĮŽåŧņÆ^Ũ9áæĐh4°lŲ˛ö;wîüxæĖA‚CÆēgZ'ėöĨé‹g­â2…Qbĩĩĩ?ž?ū?Îô#Ú A2ooīw###Į§ĨĨõS*mߎyŅüæÜĻæÍ›÷reeåē•+WöénF’ jĩV­ZuëęÕĢåWŽ\ųŗ›˙Yƒ šûđÃĮ­[ˇÎĶ…Í RŠāë¯ŋĻŧŧŧ;G¤Ũôôô]ÍÍÍ6ēLÉÉɁŌŌR˛ĸĸĸēĨĨåg¯l AdųųųM7nÜDŽôš——WÁW_}5Očļ9 ‚ ē&ĨčÕĢ׹ ˙"Q›ŽS__›7+ÁĶĶûHEÅárgߒÆ^ŒŠmâÄGæA)y3f€Ä CŪJ¯Ui)œmjúvOuõ4gšeā›'Ÿ|r[[[›MÁ SGQÔ̧OŸ~\<ÃĮŒS9yō䞿ææå\Œ­ûöíģŅŪŪ>̧d!NOO?0hĐ ‰šššV9âa&QΞ=ûíž={y‡ ‚(rĻ Š[;”ŅŅŅS<==÷nÚ´ŠŸ=ŠË”œœøâ‹/´§NŠē}\Ŋ0000wãÆ"kf%ģƒyŅž˙ūûÂǧÚ}A'!..zhŲ–-[ŧíQD EÁ‚ Z¯\š˛Ž´´ôMĄÛĮS§N=8a„k#§ŨA’$<˙üķˇ}}}ŗŨi \xxxÅĖ™3eö8FĻh4xå•Wn‹Åâéîh DGGOĄ(ęË;vøĄ>ˇ‚ āÃÎ<˙üōAqq38šæÎ›á‹/ļP׎Õē}Žāá~ũšGG‹­•ėõéͰĸ¸øÆOÁîė 1p­ķ´Z-dffŪöôôüŨŠS§ö Ũ>ž3fĖw“'OķöÛoûr5ļūéOŌĩˇˇŋ㎝;vė…7ß|s¨=“Ņh4°téŌ‹'NœxÄŅí1f†8ÍēXww(wėØĀ…“dJNN?~ü3wˆl¯\š2x׎]?–””ôáBA™2{öėV??ŋ?mÛļíŸBˇĶ^233ĶôzŊz÷îŨŪ\_;11ą­oßžQî07oŪŧ—oßžŊ‘ë~ĸ( f˘qC.—u‡tęôôô]#GŽœÍ•aÅ@’$(Š­V$tšõš}ŒU›™šTbmŠĢ%JK÷ÃáÃGž˙^ãÖŠéĶGŒ(~^*ĸ=šĶëjHūzäYzū|ˆĐmä“'Ÿ|rÛ¸qãžåCį͝;÷Zyyų@ĄÛČ'|Ž­ņņņˇæĖ™ķ¸;Œ­Ũ1}úôâ'žxbŠ=xŨ‘ŸŸ{÷î=ōå—_:TĶ^eΒâļûPΞ=ûØėŲŗ97>rssĄ­­-ãũ÷ßOēöōÍ7ß|ŗqãFΝI€-[ļxŸ:ujĶʕ+ƒ…n§ŊÔÕÕ}úîģīrîLŦ[ˇÎĢ­­íŋBˇŅ^VŽ\|éŌĨ÷ˇlŲÂy?‰ÅbXĩjUŋožųæĄÛi/īŋ˙~ZssķLŽ +‰DŗgĪ>&t;šõš}¤§gį…‡GqîLÄÅÍ_ßŪS~÷ģWW ŨNžX3uęË}ŧŧ8w&ä Ä &Y2i’Ûî ũūûī§ĩĩĩeđĨķfĪžān:Ī”•+WWUUŊĮרēqãÆ>î0ļvĮš5k^nmmåřČĘʂ>}úLYŗfÍˎl—qi‹ĶėUę–åŌĨKŸŊyķæDžƒ°}ûö­BˇÕ˛ŗŗ×GGG?bOž9Äb1ŧņÆžgĪžŨ#t[íaöėŲĮŌŌŌúņaĖČd2˜ˇĖĖĨÁĩĩ?ž0gÎBŪîąpáßāģīŠūėŒ›mÛ Aâ˙œ:õ^ŪĖ™ŧŨ#W.‡ĘúúVN™,t{ųuž}œēũ­'žđûúōzŸuÉÉ^ęͧ‹…n/×8RįÕÖÖē]P(99yž§§įD.×ũu…RŠ„cĮŽ-tÕąĩ;ĻNzĪ ąX ‹-ō›:uęA7Q ü>,q;‡2..î đ’ՙÜÜ\¨¯¯Ÿ#t›m!11qû† x‹x™’—— ˙Oč6ÛBkkëû}ôo‘USŪ}÷]oŠĸūWč6ÛÂíÛˇWņít”ö† |bbb\Ōp (j.i_aR_ãââŪēÍö÷_ŠŽÉÍÍ…ÆÆF§Ú(Ú^FŽ?%""vThhī÷Š‹›‘Ađ˙c9‚ Ä}ŧŧâøHuíŒlđ`H1"8lĐ §„n7—Ô××Īq”Î[°`A€ĢëŧÎ444ü?žj÷ĮÖÄÄÄíBˇ™Kâa›&PÚÚڞp¤SnÜ!g(ų ­­mߑSæĖ™#ŽŽŽž"tģ­Ĩąąņqžg“Äb1DFFrÅȗŸŸ_°#ŒYÀx÷îŨ0ĄÛl-AHFŽų°#‚†ôNįrU9ޚ1cFGŨ/++ <<<ķ’턝¯īôŦŦ,‡ŨoÖŦYũ\QŸwGīŪ}”ąąÜTteÄ rQX؄ųBˇ›+bydéŒ#DŽēŸ"<†ôí›-tģš"::zƜ9s6î z— ōwAâáÇû;rlmll|\čvsErrōŧß˙ū÷sĻOŸî•éāĻjzÅíJOOĪ!l^žüüîŗÁ4 $ Æâuär9ˆD"Į„?8‚ qddä 6Įšë'6ß3DGGû¤ĻĻ:Î2ä€ĖĖĖ´øøø~lŽĩôWŠT ŅhXõŠ+ęss47_{œíėdaa×;ōčtÍPZē*+K@§k6{¸¸™āíí3]čvs…¯§įt9ˀbžöˇ[ “9MĮ§ĢcL‘  ×õúqBˇ›+D"‘’māÚÜûŠÕj;ÆQsˆÅbđôô"tģš"555kƌŦll66­Ģ˙ģÂËËkĨįĪ\ŋuūŽo —ËaāO;¸Šų xđØ­J‚ dlsÆŊVĢ’$A"‘tüÛrš._ž€€Í3*—ËĄĨĨÅĨp‚ ă?ÄæØĘĘ(+ëzŋ]ģ6C\Ü 8qĸęëëĖ^G$ęwîčž_xÄ` ‹€††$A}úôoūNRˆ{÷šD˛!C@ÜģˇÅk īß_ė.ũå˗Gq16Prš(О86ÄĮĮ÷sU×™ÖÖÖgėí?ĩZ Z­ļÃądĶƒ˙o Ũv.¨¯¯37æšëˇÎßąõ d2´ĩĩ94ĶˏĨāK ÜĘĄŒ‹‹ûļFCw¨Õj`RãÖ}ûöí+tÛ­á×_co?ąųŪ‰D—.]ōēíÖ@QĢ"3掋ŊG,[ år9\ģvMđH“5čõzV ŪÍõ“D"ĶÔFKŗRršH’ä~ņõõĩlM‚å÷ŠÔØ “É@*•>'tÛmA*•>Į…žR( P(X÷Y[[Õk‹bĈqŦļčn;‘ÂÂm8 *+K`Μ…Āfļsôčč>î`Đ! 4ˆUæEwŗ˜˛!C@ r‰¨;w€ÍZĖ”ĐPop’"öÂÖ6b3‹DQPÅjlˆ‹‹ûĄÛΗ.]ōˇwlÕjĩĀ8UbąØb°R.—ïŋū:FčļÛ A‰DŌfŠ­lŋŗÆ7čßŋ›kÉ5B§Ŋē•CŠĶéļÖÁ•ikk uÔē@S\ÍņæƘčØt^h‘8‡ \,ƒX,†üü|Ë卉DáBˇ]H’ļÆĢôv'„š™ūĘÉÉŠĸ„n“ÃxøaÉ${‹ņ44œ‡††ķ •ÆÃ'ŸŦĩ8C āééé î0Ã&îíééo׌•aķĩZP„ŗSWąFLēņ΂L&‰D‘‘‘@Q̐쨂\ØL‰¤#¨Í8喎okk ēí >|ø@!n2?c(xĩWˇr(ÄYÉĪĪĨRÉz–¤'"‹!++ H’dJŨĶ06#ė0ųîIĪ•'û\„„œŌącãá̝>ēY. IQ`Íļ#~^^üîQâB0éęEEEV-ŗA 0úÍ wÃÜęĶ^AČtyt(;ašNÁ6æjqMˆûˇ Ũv!PĢÕ “É@&“YLyuE¸ø]U*UG‘6ŠÁEA[[ÛeĄÛ.ĖzS6ë‰z:*•Šuá0wãęՋg,ŅąD`ā0ĐéŽtüą’ĸ@{™ŊĒĸôz8ßÜ|FhšÆĄ”H$PTTÔŖtž^¯ŋcī5˜õ§L '›,)ĄlDŽĄnܸQĪÕÅŦņ îÜšs„H‡Ņ€€ŗ”nåP655}ÅFŲ0 “ÕjuG @HHĸ9L$GĢÕ˛zų\ÍQ0`Āq6Q>sũÔÕ÷–đôôtŠ|ŗÁƒ˙diģĨ~RĢՐ““999””dąŸ´Z- <ø'ĄÛn wîÜa5č™ë'&­‰q’,mĄŅh ((č”ĐmˇļzÂR?1Å)Ø ÕjĄŠŠé+ĄÛn \čsfí$ķwwÔįŨqķæõ˙Ģ­­bulee Ôן‡ŌŌũ Ķ5ƒN× /Ŋqq3ĄĄá<Ô××ACÃy˜3gĄÅkÕԜl iÚå§ChšÖŸ;Įʰ֐$ęͧŌëŌë!dĶ&08”Ö Ŋt šîÜų?ĄÛĪ\č<…BŅĘĪΎ86¸˛Î댯¯¯ŽÍqæú)JÆčRK.jĩ p\čļÛ MĶښšÂÖ~ëü5žÁˇß~{׸?¤ŖQƒ€Õ^ šĻ…ē7÷!qzzzũîŨģY"0ÛĒ€$IÂĸE‹Ž|ųå— BˇŸ-ŅŅŅSŠąŲ0ƒZ­†íÛˇīŪŗgËė5mÚ4åSO=•ëČ}đT*|ũõ×9PŲ5Įžžžká…ŗíŨהq&Ų\'''ĒĢĢĮíŨģˇRčöŗE.—ĪĪĪ—:2ë!--Mˇoß>—]ģ<{öė{õš5ĪI’••UŠŅhÜb놑#Į5Ž_˙e€Ŋ׊Ŧ,éļpOg-’_üå—ĶnQéulPЅ˙ķ?CíŊIQĀĻZ,@ō§Ÿ6Ē­ ēí\0}úôâ>ø`ŠŊ:ΚwxöėŲ­{öė ĸiÚĨØ]‘žžž+33sļŊĩ˜Ā8›ß!''Š‹‹ĘËˏŨ~{‘ËåĮÕjĩ”Ë}<-ųEAJJĘOååå‚ė)ND¨˜XGâV3”4MSWŽ\šČÅĩØĮjĩhš.ēíÖP^^~¤ĒĒĘĄĘVĢÕÂĨK—>ēíÖpđāÁüũû÷ŗŠrEYY™îāÁƒ.•ŖwéŌĨ¸XŸÁl¯Â†âââkŽäLx{{˙ÛŅëXęęęŽ Ũn{¨­­Ŋbī5ŦyŽ4 x{{˙[čvs…——O ›B:–`ëLVV–€XüĐ×Bˇ›+‚Dĸ¯5,˛T,Á֙$) ôííį„n7WĐ4]ČŚGkŪá+WŽ\tgĀ0ļr‘â+‘HX/ß*))šáÎ$€^¯˙’ë5ˇ–žCFÇ˙FĀf –öęV%MĶUŽ4Ú\ŅĐétgYņ°ŧŧü‚Ģ))šĻ)Gä .7–——)..vؚ ’$ÁĮĮ§Fčv[‹Ŗjĩ‚‚‚\Ú¸‰DŽ,Âą˙~—ÔįŨŅÖÖúiee‰ÃîwâD Üēuã#ĄÛÍ×õú¸p(Ųĸ!I¸įé Ė/++s˜ÎĶh4@Ķ4ģøāƒÛ‡Z!tģíáčŅŖK?øāƒÛޏ—+ëķî8{öøöĸĸ×ė-ÎÆúú:¨¨øīÅ3g~8"tģšĸüâÅ#kj.ZģŌ(ŊŪûöÛe.ŦēŨ\áhˇiĶ&ŨŅŖG— Ũn.immÕ2ëøfÁ‚­žžžēÍ\AĶ4yėØąËŽ*äD’$TTT\"Ũ´ŗ(ėƒé~%MĶd¯^ŊņíPK–,iIIIIēÍļpčĐĄmđßNI’°{÷îkeeeéBˇŲ8°äāÁƒųVHZ­JKKIWZ;iĘáÇS7nÜxƒīYoãķúCuuõįBˇŲRRRR—-[ÖÎw?ƒ…N0°ŲMĶd@@@!ęsÛ iš1bŧj׎Íŧßkķf% 6ŌrÕ#|Đ …/îÛ×f˙•ĖŖ*-…$‰äw htŪ’%KZĄķzõęuČÕu^gĘĘĘŌwīŪ}M@{`ÆVŖmč6<öØcé˖-ãũũ0ģ}}}/t›ÁPœĮqÅ?Œ¸C °gĪž9%%%7øtT*Lš4iķĒUĢÎ Ũ^[3fĖÜĩk×ōjÜfggCZZÚ[BˇÕRSSįņА(Š‚e˖ĩ-^ŧØe uÅ´iĶr˛ŗŗyģ>EQ°víÚö1cÆĖē­ļ˛jÕĒs [ųŒ8kĩZ())šáJ°Ėá(}.—Ë?qe}Ū_|ąe]SSÃOĨĨûyģĮΝ›ĄwoҎ_ŊgŸĐíåší'Oî ö÷/S••ņv IÂeî§Mååë„n/×ŦZĩęܤI“6ŖÎŗ´´´ˇplĩŊ{÷V—ņ”TŠTāīīÔ–vĢl;|Ãjˇt(âããĶør4 ”––’yyy.ZąjÕĒsááá‹ųRT*• ŧŊŊ,_žüŸBˇÕrssŋ>|øøRH*• ¤RéĮ•BˇÕ–/_ūĪöööoų2,Xʘ˜øŧĢũ6lČ.--%ųJ[ļlY[|||šĐíäGčķ 6đgą L`ā°Ô;Ößâ#õĩžžŽ;píčŅ“„n'_l=~<á0I6đ‘úJéõ°ōȑÁūūn5;nJ^^ŪRÔyļŗ|ųōz{{áklÍÎΆđđđÅŽ>ļvĮÖ­[Š‹‹¯ņ”$Ižūúkjë֭δۃ– ‡:•nĩmHgâãã?=zô‹yyyœ]SĢÕÂsĪ=wëÔŠS¸KjĘÔŠS&''§(•JÎŽŠŅh`éŌĨOœ8áåãĸŖŖĢ_ũõQö–đ6%??ļlŲ"X‰i>;vė…­[ˇĩ´ß•5¨T*8tčPááÇŨÂč"B~aßž}}¸ÜF$;;žûîģŽ?žXč6r͸qã6EEE-âRŸ“$ iiinĨĪģ#!aÎË--w6.^ŧŪ[$ęĪÉ5uēfxįn‹DũcŋũöĢJĄÛČ'ŗF’6ŪžũõūĖĖ~b__NŽIéõ°`īŪV?/¯?mĢĒréĀĢ%÷É'ŸôárlČÎÎ†Ķ§OTRRō’Đm䛱cĮ^Xŋ~ũP{ˇč2ÅŨÆÖî˜5k–´ąąņëũû÷÷ãrŠĸ`ÖŦYˇÅbqŦ3Už7:“ šĻSÜx† ¤¤äĨīžûîƒÄÄÄ6."*• ^}õU2$$$͌Ã‡§:t¨011ąÍŪ<}Šĸ ''–/_ūSUUU„Đmã’īžû.æ_˙úבŒŒ ÖËwEQ‘‘{÷î=ōŨwßÅŨ6.ŠĒNJxõÕWI.ftI’„ÄÄÄ6wđhšĻBBBâ,Xp‹¨ŗVĢ…ÄÄÄ6wu&Ž?ž˜k}ž`Á‚ îĻĪģŖ¸x×?[[õ+”ʧnqQųĩ´t?(•OŨjkkîîÎ$Ā۟~Ēlŋwo֌íÛo¨OŸļûz’„ø­[oéÛÛW¸ģ3 p_įŊúęĢ$×:¯'8“†ąuųōå?åääØmƒ¸ëØÚ{÷î­loo#>>ūW3åâãão‰DĸÕÎäLĐ4­W{uk‡Ā`„DEE%-^ŧø†­.I’‘‘gΜ9RZZâl>|8uܸq/<˙üķˇmUöZ­æĖ™ĶÖÚښ_^^ænFMĶԗ_~™đØcŊ=gΜ6[•’Z­†ÄÄÄö   ?ųå— îØOĨĨĨ!­­­ųö˙*• žūųÛãÆ{ÁŧŊ{÷V+//˙6)) l æäääĀ+¯ŧr-***É]IŽõyqqņ0wÔįŨQQņ_UlėŒĮwíúGÃ'ŸŦ[R`uēfPОá›o>˙öüųŗ¸SUWK”_ŧx¤ėüųū;~üņÛ ĩ(ŊŪękPz=äh4𡲲†9ŖG?~āįŸ]˛›-ėŨ쎞´´4äĖ™3G222ėŌy‹/žŅtž)4MSåååa­­­ųsæĖąylÍĪĪwëąĩ;ĘËË7Ī™3įņ5kÖ\°Į)g&NÖŦYsaΜ9;q1E5AÛ“Ō­S^;“žžžëÚĩk)‹-ōc“ļH’$¨ÕjØž}ûÅÔÔÔyššš.Ŋ§[ĻOŸ^ėããŗ|ųro6Š)$IB~~>”––’S§NęŽyøĻŦ\š2øäɓ;$‰tîÜšžlúIĢÕÂ?ūņŠĸŽ3fnOč§YŗfIĪ;÷EjjęĐŦŦ,V›+k4Xģvm[īŪŊËžüōKgZ“Ā™™™igΜÉĪČČT(ŦúI­VÃ|p;  Đ]‹Q˜#==}ךsįĻŊņÆžÖčķ?ūøōøņã˙gûöínW@Æ&ON_}öė¯?÷ÜōŪRi>~ØâŋYõŸVĢ…7ŪxŖõŪŊ{ßÅ Ũ!Y˛dIžFŖynÆ >Ö¤kĩZxá…Zärų'ΞîŪ¸uˆ’Ļi‡ČŲŖJ‚ J“““ĪÕ××O>|¸llŦČÔ (ĒŖbX¯^ŊĒO:ušŠŠé MĶnŗŲ5ŧŧŧNEEEÕét‘!!!EGG?ā\’$ Z­***‚‚‚Ē;v“ĸ¨M´mĘl ‚ $^^^˙ķúõëã"##e2ŲŠŨ´Ÿüũũ—––úļļļ.pˇŌæúIŪŋ˙eąąąŪõõõa‘‘‘ršL×1hĩZ(//oŊråĘEšĻĢžûîģmmmáBËîā~Ę0`@jxxø`‘H4"""B,“É~ĶO_~ųåíæææĻ   ¯:LĶtœĐ˛ Õ_0.99š¯%}^UUEétēŗMMMûN:•@Ķt’Đō qÍ}4üĻ——W’ŋ˙ ĄÃ†û yâD üøcų{÷Ú/õęåĩĢēúûX˜ín™ÖB„ļÆ>ōČ)_OĪéwÚÛ%ņÆõ“w2ė5$ §¯^Õ5ÜēuŅģW¯ŌŌķį=āŗž4VvAEáááÅ Hģ{÷nX|||ŋŽt^YY™ŽŽŽîēQįŨ€ã=Í&ë ‚ ŠbbbÔūūūs™ąĩŗ Œ­ĩĩĩWD"QÅĪ?˙|ôęÕĢŽ\[įŦņqXXXDīŪŊNJJ ”ÉdĐŲ6Ą( 4 ĶŽ]ģvųäɓ§iš~AhųYļąČQã]r( ‚€ÜôE"B!•Jg›vĩ˛˛r#cđ‚žd€ 5`6AâaÆÍ0`Ā“Ė1mmm'O:õo“~rh$Ä "4Œa`,:𲗗׿˜Ë—/—744|Â_]=ƒîAcúŦ„‡‡?cÚOMMM_?~¯I?=đ ö‚¨€$“>IĨŌį`sLeeåSC´ķ3ؓ ĸ2L‚ äRŠtÉačsã1=îŲę ‚ ō@ÕŠoäÉãũGĶôÕ_~9õ‰q=NĮ1ĐÃtXWtõî!“=øÎÖ×w~g{ÜXŲÆ÷PLĶ´ĘäoŠTú'xPįíĻiZmrŒŠhšŽē Bb|ĻĪ›ąÕxÜcMOÄøæ1ļũŦYŗ¤MMM [[[ãnŪŧŲ‘nСo_ŊˇˇwikkëgĖ– mg† %PŽīzšCiĶCĐ͌6[•+ŊdöbĪ FDEO m5žzZ ĮV#Ŋ§§ö85hÚ˙ÜtåĖ÷$ė}†ēræ{ö85=Í&ëĻlļˇēræ{ö=,°áy2ę3ąQŋõ$”`“1d|ļ4=čė %Øđ^š 6^Ŗ§ĸÛõ?Īíąũg,bĢ`x÷{ėûk§=Ëdgô”ąõ7,‰­ ãyãuœ“Œ'Ūeí1%ØhØô8ŖÍ&àã%ë)J*ˎ5ô€…QÉėˆ"÷ˆ@ŽŊÆôŦ@ô"íœSAĪŅU`|Ūl6ĻĐ ƒ^ŗŲĄ4ĻKzˆMŅ °1 Đa$S=(⨛j&¨{pPÍŪ€€Ą˙]%(¤ØR=ÂĄä°čF†-@˜}cfqm=ŋÍž)ĀĨŨƒ96ģz\ ĀĐ_vSÆwėī`WØŨFÜ^×w…ąÍöŖ=t–Ŗ€€kôœa˛7]Z =ķųƒaš„]ī°qܕšÂ,%8hŖG8”`§a pßhs‘‡ĮV¸({‚’˛yׄž0ûfĪ,.ƒ[r8 vôãžŖ CK;´7ÕËãģíî:Ŧ+¸Đk.•6Į1öĻ @GĀ‘ėĮÎp1;išúßĶŪa{–+uÆ%R˙M¨ŧž+=ÅĄäd7v¸2lM””[Fūš2hŨ}öÍŪY\†Čą;Øe¤'r82Ļ0HŨRWuƒ8x/MĐö„@ƒą­\öŸK¤\aÜdžä°QšĨ4Žƒb‹ŠĀMmZ3(¸ĒxjŧŽåMȝŪĶ^ŨŪĄäʰčxxÜuđäʰpīY%ŽŌÜŋŸ¸Šēm ¸›ípë@§Š^ĻôƒĘ$ՋËōņ=-mĶŽĩ1ĻŨšsĀŦ3\Ĩ[€ûf쀓Ų]†ž–úĪC@ĀEÖĸ2ē†Ī{¸ŊC Üļîk´q5‹Ë(yĘŨ”<‡é‰ār9øŦá8-Ņm9LĨC/é–ũdgŗ“ öŅuw¸Lõ€žČ`0ļQËÃV)îūŪĀũwŒ‡­RzDęēI@ˆk‡¨GôŸŽũWKũį5íÕ­JŽ [#n§üy0lÜ3ËŲIwŦøĘEĩθŖŅĘG´Ūí9ŧ¤z™ŌS *ÎRŊ:ŅSf)9hô¨Šš|Œ L`VėnŲ.ātv’Á$õßUR7m‚ƒ­jĖá*Š˙ŧú/níP ĖM6N [ˇ}ã:]Ėífßx4üŨ­Ÿ$ĀMĨÃθc €'c  gláĀSĒôŒ-8ŦLÚ.‘6g+\lUcž˛kĢ ô„ §éęp‰ĨÆąŽˇĀÛ:”Q7˛)ēķ ĩ)î^ŧÃ­jēÄŨS˙ųšØVpZžfŖ=…n™-! L#˛‚ ”Aˆ™” Ixxø3^^^cĖČäđ~’H$ŲũúõpûömũŲŗg?ë$Ÿ‘˙|‚ *L¯O„L"‘dt’éīÆČ‰Pũ$“JĨĪĀ ãŸŽVVV~ŌIĻ,ČāI„|Č“įĩŗL7nÜ8O’dĐũ4bĈ?úųųųšČ”×)Ĩ\NĶtO"0FWމLō#F<ËČÔÖÖvōÔŠS˙æaŽ5ũ$—JĨ ˜˙ī,“I°‹¯ß’ 䘞w ŠT:›ų˙˗/—744ėǟ‚Ī;÷ĩÚÚÚÉ7oŪfú]@@€æ‘GųbûöíûŒâÛbtzŖĶ ‚§ĻĻfĩ´´Lš|ųō(ĶcÅbqåđáÃ?6‘OūK Íjš{wĘeîAų|}+ûxyí:T[ģÍxŦøKõbú$‚"ĸŖhRddĸ˛ŊŊmJSSÃō‰Dũ+}}ũvũđÃ×ۄę?€äääyˇnŨšCQ”ÔôīƒūÉĮĮįȁTÆūãkíŸi˙QAh‚P0ûãY’īāÁƒų|ūĻ–ČĖĖLĢĢĢ{Á’|\nUcf–2‡‘ī… 3¯]ģö€‘ߡoßķ!!!_īØąã=!û/::zЎˇ÷ŗ­­­q7oŪôeū>pāĀ‘HtôāÁƒo›Č—EĶt$Ī"1‡ŗYĘ÷w!ĮŲYŗfIÛÚÚūpųōåŠ---ũ˜ŋûøøÜ>>ŋŋ{÷nX|||?™Lö™NŸ>­khh¸číí]ZZZ:Ļétû)wđāÁƒBCCÃE"҈ˆˆqW21ũäååĩĢŦŦl=ŸŠž qllėR__ßéwîܑt×O%%%7z÷îM^ŊzĩŦĒĒĒMĶ/đ(ĶĮˇ|}}§zzz‰ī'—ß)Š­V UUU”N§;{īŪ=#úI*•ŽōööNōņņãוL%%%7zõęUũĶO?‘W¯^=OĶtގĩ$Si\\\ukkk\``āĐŅŖG‹:ˤŅh ˛˛ōšOMKK˧ååå›ų’Į(“$99yõõë×e>>>ÃĻOŸî'“Ũ’$ Z­***‚‚‚ĒŋũöÛæĻĻĻ<Ļ/AUÉÉÉUõõõ“CBBŠŽŽöîJĻŗgĪ^đđđ8ÕĢW¯Ī:Äģ?nܸ÷îŨ‹ y(%%Å[.—? Į īķūŨž}ûÜŠS§ÚZ[[ų6Ļ€ ÅĐĄC 0āŅáÇûĮÆÆŠ …YųÚÛÛ/ĀËÖŧļ7lØ;m÷îÍ ėĶghė°a"šD˛Áƒ8F{ų2¨O‚džęڝ; Ս—nļ´<͡ņL„ÄסĪ'ƒ :xpđ°° "Š4BC#8ŽĻĻ *+K ĸâđíļļ–ķÄßΜų×w”!::z!ü‘yOår9˜ž[BŖŅ@YY™ŽĻĻĻņ… u:Ũs|ŨAˆûöíģ3,,lH@@@`DD„XĄP˜•¯ĄĄáb@@ĀÁ,qP˙Mimm}ßĪĪ/˜ŖLõ/€Aˇ¨Õj(//o­­­ŊŌÔÔôËŋˇņŠīŧŊŊ+ÂÃÃŊ,ɧŅh °°°ĩļļöЇ‡GéņãĮį:ĸ˙f͚%Ĩ(ę[ZZBd2(&q1ō•——ˇTTT\Õét—H’FGWTT4Đ4]{üøņŗ|Úo&ō}1oōÅÆNŽ ŧ ŒĨé•+Yš”JZ1z4vlÅäÉŧüļ4Mß˙üOiX؄ڧŸ^HīØqšūâ‹_Y}vė8M?ũôBz먏ãsį.áMž+V§§§S*•Ŧt‡éīĢT*騨ØÚ˙üį?RžåKOOoąF>šĻiĨRÉĢ>1~Äļʗ››K'%%Õķ,¤§§KHHhek“™ĘÕøÎ;īđ2–2Ÿ§žzjWBBB+[;–Ą  Ā!ōŊöÚkyņņņˇl‘/<<\Į—}d*ߘ1côlĮ5†ĸĸ"z˘1úŦŦŦõ|ʡhŅĸeņņņˇŦ•¯ĸĸ‚ŽŋõÚk¯åņ-ߨQŖšŲÚ*åcãģđ&>ū–ĩN‘ŠLRŠ´ëmÚ´ivé'š6čō˜˜˜æ%K–<ËĨl4m0ôėŅé4}?`ȇ|¯Mœ˜—ÜZ‘•e•3iú)P(hiPPÛŗcÆpn”ϤĖ[/‘„ˇŊũöŋY;’?oŋũoz˘˜Ö3æsn”.Z´hYBBBĢĩ†ž)EEE´T*mãÃ(}öŲg_–JĨmÖzĻTTTĐršœ~ūųį?åZ>{õŠŠ||Ô]l||Œ[Ė'--MjĪXAĶüŒõĖĮŪņÕTžgžyæ8–sp*ߔ)SÎÛj“0ō)•J:55õ4×AĶ+V?ķĖ3Įŗ˛˛ė–k;œĻiņ3ĪäBī10c„Ŋãķ‰}‡Ģą‚Ļi://?~|=WōĨĻĻ*m™•ėÆ>âJž¨¨¨…ááá:.lš6ؔ111ÍŖFzŠ#ų8ŗ-ųÂÃÃuöÚáĻōEEE5r%3[ûNW÷ãDisũ‰ˆˆ¸ĀÕÄ`ôŽ÷Ø"ˆGÕĖÕKĮ••EÛęTŽXą"8<<\Į•"ĨiÀ9ū|›J&ĮĩLééé-ļ:•QQQSƌŖįjĀfdJHHhĩUi&%%ÍKHHhåZ&KŅ#sŸgŸ}öåųķįsÚOĩĩĩvɔššĒœ?žž3hƒB ×ŲjĐÄÅÅ}˜••ÅĨHtEEÚdĢL‰‰‰šrŠŠŠŠčˆˆˆ ļČÃ|""".p­3išļKG9B>ĨRIsáôF=üpuŅīΉ3iúɕË9q*##§/\¨˛Û‘ėüY¸PʼnS™˜˜x0//ķߡ  €ž6mZąŊō%''oã*ØbJQQUm¯|qqqr­WhÚ īėÕ-4Oã—ō=ûėŗ/§§§ˇp-_mm-=jÔ¨f{ō¨¨¨)\Û!4m°EÂÃÃuöδĨĨĨIãããoņ!Ÿ=v ķalp>ä›9sæM{rĮÄÄ4s-MĶtzzzKWK›œnÛŲŗg{ķÍ7‡v^Hm/šššāãã3}éŌĨĪZ{î3Ī<ķßÕĢW÷ëŧ8Ū^ōōō@,˙ONNÎdkĪ=|øđá7öéŧ˜ÖÄb1ŧûîģ>/^\ĩråĘ`kĪŋxņbáĻM›úq-Ķ–-[ŧųå—\[dōôôÜûųįŸû˜.ŠæBĻ]ģvyyzzîĢ.ŧråĘāK—.ũīŽ]ģŧ¸–éĶO?íW__ou%Ŋ•+WWVVŽ{÷Ũw9í'‰D›6męwņâÅB[dŌëõoo؞Ň3@&“ÁƍûÄÆÆVX{îûīŋŸ6hĐ ?äååq)Čd2Xģv­8==ũ€ĩį.]ēôYšĻ§ææær*“\.‡ĖĖĖĄĶ§O/ļåüŲŗgËĖĖĘĩÎx÷Ũw}._žüŽ-ú€!==}Wjj*/ōåææ‚——×sīŋ˙~š­×X2iR^Bpđ(9‡ē”A 4MO]cõXČđģßŊēzāĀÁSRRæq._JĘ<čÛ×?%!aÎËļ^céŌĨΤdeqŋ‡BĄ€1cÆLYž|ųj{äkkkËP*šß:Q.—CBB¨%K–ØŦ¨rrr&{yy=Įĩ^0čģÔÔÔĄéééģlŊÆĘ•+ƒ/_žüÎģīžËéøĀȗ™™9töėŲĮ둝˛˛rŨ–-[ŧš–O"‘ĀęÕĢû=ķĖ3˙ĩã2bOOĪŊ}ô§v€ÁŲ¸qcŸÃ‡ļį:õõõš÷Ū{ĪųlĩKLųæ›ožųä“OúđÔ"Ŗ}i3Ī<ķĖW­ZՏkųļlŲâ]WW÷iį1ØŠĘ5kÖŧėéé9ąsÕ+ŽøčŖŧž˙ū{Ģ*-_ž|õđáÃĨ|É´nŨ:¯’’ĢĘĪŋųæ›Ûâââ$\;Ũ†‡yÆ >ĮŽû֚ķæĪŸ_ĖTŨâCĻUĢVõ;wîÜAkΛ={öą´´4N\S™ūô§?õ›={ļUNĀɓ'wŦ\š’s%`hfĪž`í@xėØąo7nÜȋL2™ ’’’įΟo•cRXXXąnŨ:/΃Á'ąÖāúę̝ūõîģīrn ŒTOOΉVŠâīŋ˙>˙Ŗ>âĨŸ”J%´ļļNYŗfU†ũš5k^žyķæD>ŒeÃģˇnŨ:OkuCNNÎdN7›c™aŨēu^ÛˇoßjËš …´˛žūeoEĘáŖ´4¯ī/]˛ŠęffæŌāę抜įž{ƒ7ų.üœ?fSfæŌ`kĪ]šređ÷ߟĪuāĮĨR ø‹­A >ß[CPãäɓYR[Î/..ŪŗūeäĶétŗm ĻÆŦuëÖyō1f~ߛ7oN´V÷1œ={v_ã<€aŧ¸{÷ŽÔÖ ÆėŲŗđeØ>Æ2˟?ŋ˜Š‚ËļÚ% ožųæ6Ļ .H$HKKëgkPcɒ%yÇ—ōá#ܡĪž=ģĮôīNãP!ŪącĮ:>‰DͧO÷cû#!.))Yʗa`x°ãããûąÖ­\š2øčŅŖ|CršƏ˜™™ÉJY͚5KzîÜšXže/Å`0Ν;;kÖ,)›ãĶĶĶwņ9˜0ũT\\<Ÿ­A˜žžžkōäÉbž`CÆÂ_|ņgļ›sĪž=ûĀĸE‹üø”Š  öíÛ÷ļ5įėÛˇīm>õ8€õ:ʔâââ]|ę*ƒ^OHH°efSyųŽuÉÉ^b__kOeD,†é=æ7U"ą*Hpúôw{22{‰Dũy“O$ę ÅbĪsįĒwX{îɓ'w,Z´ˆķ™ S˜ FaaĄÕũ7uęÔƒĶ§OįõŊ0čŨM›6Yũü9B˙ôŨ?üđkĪsĘÅČg­î0ŒķwīŪåmÂT>kÆ GØF†1VŖŅ‰‰‰Û-kÔ_ãų6ZÄb1ŧūúëS§Nee8ĮÆÆî™={v߯2€ATUU-௠š6mÚ†Ųŗgķn,X§×ĸ‡]7l˜¤ķŪ’|¤žÎ5JĘöœ‘#Į/|ėąąRŠ4žwųââfŒä‘Įžb{NXXØSā€÷ā~jŠqoKV!ĄizĒ#tŽL&ƒ¸¸8Éã?Îz*ŲQúĀāŸ:uj`||ü‡VČį°1K"‘Āë¯ŋkqÜ2ÅQãŧ5ã…)§NÚ\PPāų6lØāSVVfU˙]ģvm_™@ak—˜ĸVĢ‹aī˛*ŲØrĻDEE}žaÃN—/uG^^ÔÔÔl`ūßiJŊ^ãˆAĀđ#‰D"‹ŠW¯^‘|GÁVŦXáŗŪÜ1AHüņGa ,Z´Č/..îod’Mœ8qˆ#d‹ÅđÚk¯õ‹]j°°§RSS‡:¨‹ÅđüķĪ÷ŗdTDEEe>˙üķŧäŗwF"‘@jjęPKA“ØØØĨ¯ŊöšCd’Éd0mÚ´ KÆurrōęE‹ųņ. ÂQŖF ąäčtē˙÷Ū{ī9D&…BzŊ>ÆŌq>>>šŽt čtē lŽÕëõ“aėŪŊŋ÷ą¤Lųõ×_Ž’ C‡ūŊSū˜ë 1`]r˛WãíÛ+Ųīååũû9sXûOv3ū[ŪAŲl2dHöōåËō^‚>>>ŋg{|LLĖú7ŪxÃ!Æ(€!¨qīŪŊ—Ø÷Gé_C˙Ũ¸q#…íņŽŗ:ôņ$ļĮ'''ĪsÔ8o"ŸÅņ‚!::zJjjęPGÉ'—ËÁĪĪ/”íņAHFŽų°#l7ƒ]2a„lƒ’AˆCCC:Ę—H$””4 Ö7ôôôä-Õĩ3b’gw0†IDATą222yÎáPFGGO‰īgé8F$I‚FŖąé{‰Dׯ_gAĻ…ąąą"{eČΎŧTE.—A掉mq:›~Ōh4 VĢ-ĘÔÚÚjv!OrrōkŅŅŅsŗĖɤŅh@ĢÕZ”‡‘ÉÃÃÃėÛ2dȐl6Á 6ŋJĨb%́Ÿ6wˁŸļW&•J†õķd)hâëë;Ũ^™(ŠĩZ (Š2{ččhßäädŗ)Š×¯_—Y’Éœ<$IBNNNĮĮR_ÅÆÆZ P466>niąô,iĩڎ÷Îiiiũ,ĨQßēu+ØŌ ËV°‘)!!!ĀR€‚ Iddd ­ōüVWZ:G.—ƒ¯¯īt‹ 0Č' yˆKųēû›Š|–t¨)>žžÃē•$¤(Аd÷ōiĩŋ9GC’ >}ēËãeƒƒŽĩ5’­|žž~#ēKu­Ŧ,úú:¨Ŧ,éöüÂÂmŋ9§ĻĻ JK÷wy|PĐpĐéšÍŽÕĻÜģw/ŧģw՞ąēģq@,ÃŨģwÃØĘGDDwƞ-ōąØí3ߙÖÖÖ8.åc3>„„„<ÄÖ ÷đđs%Ûą")))m–ÁŨģwŸå˛˙،ņņņũØdÜøûûŋhÎŲ°$cWúŲ’|ąąą"ļŗø …B™’’Ō­kɎėlײés66Cjjj֌3ēõ Ŧ•¯Ģ>íŒŅnz|ÉÉÉķŌŌŌēõĨlņ ,ų …† ’ ā$ĨH$RZō¨ĩZ-$ ‰¤ãßÖ|ߙČČHŗJbāOÛ+°rÜz襥æ̝¯īt{eĘĪĪąX ršÜĸ"‹Å8ÔÜũnŨē5ɒ`N&FaĘd2ČĪĪm'ƒ¨3‰D"ŅsĮ\ŋ~}œ%€ÍoĮ „–ÉdpûöísĮ466ŽąäX’‰QlĸOrš7wĖ;w$–"•–dRŠT P(:•%™Ž_ŋnö‡ņņņfî{6}Ä<ß2™ ,ĩĪ’CbÉ a#“VĢŠĸ@.—EQŸqš\^^^ ēû~ÖŦYŌ¤¤¤@s×°$“Z­­VÛĄØüvC† Ydî…BŅ­ˇEW˛9G"‘€ˇˇ÷p`AjjjVJJJ—ŗWļęrKúeH ™3th—ŗCÚ˗¤(ˆÅ˙ū|Į|­ÄŊ{ƒ\"ép,ģ"D,feЏ9~ĘŖŽîō¸šš*¨¯¯ƒ  á˙îLee ”•íāœ'J 44žújÔÔTuyßĮËĘ 'B2bĈGēė?;ÆjKã@|||?6뜂?ôĐC]> ļĘĮfL˜>}ēßŧyķX— ėröĘVų،)))ŪŠŠŠŦĘņļ´´„v5ŽÚ"ÛąB&“Ĩ@(CwúÍ¸Á&xĖP__?ŲVŨY>ļc›€;C]]ŨļČו]ËÖ'°&(ŲÚÚú WōuÕ§ŨÉwëÖ-Vŗä^^^ lũ}ģ’‘ŋ “É:&čœÂĄÔétÁ–ŒdĩZ Œ"aœkžīĒRSS˙ØŨ÷ÍÍÍÃ,9%lîiÍÔstt´wTTTfwß{xx ļä”X’I,w‚ äŖGîrœ˛E>ļãƒ\.N—ÎB>IBBBWōą+ØBō‰üņŪ\ÉĮôĄĨqC.—Ãå˗GY’ĀŧM`IÆÎōąCd2477› ›0¨ģvš“¯+ģ–íø!‘HĀÃÃÕĸuŠĸēĩÁ­•¯Ģ>í ąXĖĘÎøå—_‚mՁ]ÉČÖ_ ô&Bė%˜12ųBĄPĀŠS§ē5ÚÚÚø+a× 2™ŦÛô(‚ dĄĄĄ4íf'I’ nš\RŠ´ËŲ‚ ävõ“X,ąX ųųų —ËY9K2™  ĐŨ.‹ˆˆ°{I’$S äŨ /1~ΉL999ĶKƏ؝!+‘H2ėÍŗ'IH’š\999gš&NœØĢ;ã_*•.°wm3åįį[|ļ"""nv'SkkkœŊë9d2H$ˆŒŒŠĸX=ã}ûöíÛŨw?˙üķD{;‰DŌņ{1‹%z÷îŨÛÜ÷7oŪôuÔÚS,čƒ._ž<ĘQksL‘Ëå0bÄ‹{>ļÜŊ;‘Ëb<ŠŅŖA1z4ÕņīÎȆÖģw-Ļåęt×Į åp‚HÔD"(,ÜcĮÆw8¤‰‹› įĪ›fÜēu+„ëߗÍ8 —ËáâÅ‹ą–Ž5`Ā€'š^{ÅfLÉd@Q”ÔŌĩFŒņŦŊzĨŗllƉD–Ž7`Ā‹4k`;VˆÅb¸yķ&›’˲Áƒ[ ԞŚqÃÜxÁ@„|üøņû™-֌!lėic=Ž^ļČb­]ۙĐĐPšMPŌĶĶĶSų"""úŗ J˛yŦ‘‘­ÜF›[æ,ĨËÅЧOVŠRN‚¸_ŋ~A\],??”JĨUŗp]áééiwm{ąX YYY@’$Ģô`ąX Æ ÉU_tÆ4ōæL˜Îä˛MŖîŽ~ũúąš…é'š\ÎjmgīŪŊũ€w¯ƒIgbC@@@0Ÿ21)iEEEVĨĀw‡——×`{7æYb“ŌÍ`i0ĩg0ŗžõøųųņˇˆōĩZPÆÅuĖpÚ MĶv]  Dĸū’2ÎwģŽR$ęmm-ļŦ,[ø‚Ë1gÔÚņÁÆ ÉGŠÍX!„nãzÜā[Æ ˆöÍØj×mkŪí[åãÂÎļGFļrģŒCiúÂw­ąô}W´ˇˇˇņ)“3ö€aā‘Éd “É,Ļŧō-“JĨę(xĀ&×QũÄŦídŗæÍQũdī€ĖĩL‰¤#*Éf†‹oyH’´ę7koo×ķ)cH$(**ücū&—Ë;Rnąũˆ9y¸:Į‘ō9ą‰ŋBéõŦCõéĶ 2dƒw›ōĘ}úܟ€Đéš!0ĐrŧjįÎͰs§Ą"žHÔjkĢ,žc+ļūžŽl‘Ī‘c‚-ō9r|°õ÷ĩvŦp¤|Ž7Ŧ•ŅŲÆÎv­5íą×°E>GcĢo`ÜNáPˇ[:&++Ģ#ĸÕjAĄPEQŌí÷æĐjĩāááqÖš-ÉpŋĒ’Z­ļWŠ’ĩĩĩí•I­VwT5KJJ˛(EQpãÆķŨÉtįΝëöČĤu0ļiÄĩێ I¨ŦŦ,ļ§3-ÉÄŦĢp$ædbÖÉ0Š%l”ˇQIvŲˆË—/—ŗIQí,SRRRĮŗ¤P(:ԚH’6Û1œ?ū*twãĢ–úœÍ;ĮĻ]ĻTUU5›‘Éę>ę,“BĄč0üōķķY=ãæ¸wīŪ)Km´$S˃ ØĖÄčõú;æžŋyķæM[åø­ŽdĢĶųÖŨÉ×Ũß:cA‡vāĶĢ× Jßĩ]“%“uÕŅ^ēŠđp ôzŲ´éž|Æ*°ęͧŌëA}ú4äh4ŖŅ@Ō§ŸuįŽ%ĖâééUĶUą€””yp℡ēkmmÄÅ͝Ž^zéūކ*°†™HŽBC#˜*ŽPSSõĀzJSjjĒ OŸūvyÃļüžlĮŠĸĀËËĢÆŅōY3&øøøÜ°$Í7Îw×V[åŗv|0GeeeqwzĪÖ÷—íXaNˇąÁÖūc;nXŌÍF(sŽ“%;ËgËbŗ6Ž9ųē˛k­ņ ęęęÁĀųēęĶî¸qãF=ųú÷īß­/e‹o`­ŋ4M ū‰ŠŠĒnjjĸŲPTTd×÷ ššštjjǞ;™ÂÂÂ~au!+îi ĨRI€Ŧ;™Æ_/LēûߎŅ^™šššŦ’wūüųzw'SRR’Ãû)55ĩŠ;y@Ŗ­ĀŠ|l0g7uę?‹žōmnį˜Ą>|ø7lķ°--gģ¨ŧŧŧŧõāÁƒŨ拌9ōŽdbËŅŖGoĶ4ŨíœrPPP5ÛŠrŽd*))šAĶtˇ‘‘ž}û^`åëN&Ļ,1[*++¯Ķ4Ũm¨¤šš™b;ģČU?ũúë¯ˇēûŽĻiĒŠŠé ÛkqŅOÆö_5#yöėŲ öĘdé;SH’Ÿn#ų4MkĢĒĒXũp–îiMJہėîûęęęĪ=zÛ^™ŦųíÔj5 2ätwß츐Ī–t(CëŨģŸi/]2/Џ§¯^ÕŅ4mq°Ķj˙û÷ŗgˇ˜;Ļs%WKˆDũ-žsöėņÖŗgoˇt­ōōōÍeee:sĮđņûjĩZ8xđāß-GĶ´Ö’.ąV>6zEĢÕBkkëg–ŽEĶ4YRRbv&“÷W­VCŸ>}vąĒĢĢ3ûžÛ"›ũ|õzũįlŽED#—ōąų}5 >ü6×ëÛˇīKļ‘52˛C4 Œ9ō6×8pāI6[XY›÷#((¨šĨ|?˛Ųö‹KH’„ž}û˛˛Õüũũĩ–üŽåŖ( š››)'IyUĢÕĒÂÂÂû¯Äž+WŽ\4g„\ēté[„ĸ(hii1›ÕØØ¸Ķ‘2‘$ Ŋzõ2ûĸéõú/)“VĢ˙ãæŽaķRq‰FŖ   ¯ÍãããsÖŅ2yyy™¤=<}Ž9rœ(,,´¨›ôzũ—Ž.îŖŅhĖŽgĻ466îBžļļļ-lŽíÕĢ×gBȧ×ëŋdsėõë×?rôûĄŅhĀßß_ ā$%MĶdeeåuG)ZĩZ 4M›­P^^~d׎]ĶüjĩZ[[‹,Č´yßž}×Bp)SSSĶ>sĮ”••­ßŋŋÌHĩZ —.]Ę3wĖĄC‡V82@ącĮŽ–C‡ŊgîN§rä@ŗcĮŽÖ˛˛˛­dÚīHå¸mÛļۖæ{÷îiY|čĶO?Ŋeɘimm-r¤Lģví˛hŠDĸ G=OEÁ™3g~ĩd4Ķ4M544\t¤ÁĖF˜ríÚĩG,,éPSôííįHGĘG’Đz÷nÛã=ģĒŗ–O¯‡wŊVY_oqÜa8qĸtáŋ˙ũžC >ŽūķŸ-ÕÕĮj†ŖGÎûíˇō^Ŧ^ŊúļH$ú ÛãËĘĘÖ;J—ŧøâ‹m---Ŧ˜ōōōÍ[ļlšæ(ų,XĐÚŋ˙5l¯ŽŽūŧ´´”tÔ,ĒJĨ__ß\ļĮĶ4­Ũˇoß9GÍåä䀟ŸßVČGūøãŋ8ĘéUŠTāííŊ‡íņ4MS^^^ß8*h VĢ!00ĐĒ™ŖlpFžÆÆÆØ4üüüžp„ßđ€ũK8‘CyāĀUii)É÷ƒŽRŠ 11q›¨¤¤äĨâââk|+‡ėėlHJJz‡L‡Nu„Á #GŽdUŽōŋ˙ũoĻ#ņe˖ĩ[ÜĀ 666}õęÕˇų”‰ĸ(XļlY{JJJ*›ãSRRR—-[ÖÆˇLoŋũv˞={Ļą9>55ußʇ$Iøāƒn>|˜U?…„„ĖĪÎÎæ]ĻÇ7|õÕWķ,KĶ4•””ôß2iĩZ(..žVRRō™üņŋņ=°i4(--%ËËË7ŗ9žŧŧ|sUU•C‚9999žĐšsŒ‘ča°dggCHHČ|kŒō‹ø÷î­U••ņ._ŽFŅ?Ŧ˛F>šĻÉž}ҟ|˛–í)6ŗk×f5JļŲJųō^žŋ€€€ÂŊ{÷VZ#Ÿ#t €ÁÖņ÷÷ײb ™Ÿ‘‘Áģ|Lā“íėC\\Ü|GĖÖę>…B‘°lŲ2ۃBZ­ŠŠŠ،Ϥ¤¤¤:"ɌąÖĘį({’$Iظqã ļv ƒŖlpƖ;pāĀlkÎs”ßŌ•ũë4%Āâŋį,[ļŦ¯IŖŅ@]]]åĻM›Öą='33s>ŸĘA­VC{{ûˇÖČ÷*Ÿ’JĨ‚ž}û~ģ}ûvVŠZ4MSŋûŨī^ãS&ãĖ†íāŊjÕĒsņņņëų4,T*$$$l]ĩjÕ9ļ2IĨŌų”);;fΜų7ļFXnnî׏<ōČ>čėėlxōÉ'_g{üöíÛ÷õíÛ÷[>ûéõ×_o>}:ëũ;6mÚ´ŽW¯^•|:"˖-k[ēt)ëķÛˇo_Q^^~/į0/^<Įšķ&L˜0míÚĩŧNT*ˆDĸ#lu”)cƌ™ûĪū“WųōķķĄŊŊĩ5e÷éĶ“ö9sƒĪÔWõéĶ oo˙iSy9ëq‡áØąƒs~üąüFM ûFVV–@MÍɆƒ?]jíšÛˇo_ņÃ?4đÔĐjĩPRRrcĪž=VŊ]ŌģwÔ%$Iž}ûnėŪŊ{’ĩį:B˙Rk׎m3fĖ\kĪÍÍÍũZ$á[>[t€aœ‰‰)āÛé]ļlY›RŠLąEžI“&mæ;蒓“֌ą 4MS3gÎüßA—ėėl˜6mšM?RZZÚ[ސĪ[Ξũ€Ží_§r(322*ÃÂÂū:EQ°råĘ#FŒHˇæŧW_}uŸ——/ʁĸ(ظqŖÕJũúõŸ‰DĸŨ|ô“ęj­L˗/˙į­[ˇŽđ1H2ŠŽÛˇog5ëÆ°f͚gĪžũ–™4 ˙´aÃ̴Ɔ ˛ųš‰g‚kÖŦYaÍy[ˇnMā+õUĨRÁŊ{÷ —/_Î:í`÷îŨ“øŠæääMĶû_}õǓũ˙û߉ĢV­ēŇ#’““=öؕ֜÷ÄO<ņ§?ũ‰™T*„‡‡/ļVĻUĢV2dČĢ|T$IBaaᅭ[ˇ&ØrūĒUĢ΍7îžR_I’„O>ųÄ&cžaâĐĄ¯đ•úJéõ°ęȑ[ÁūūVEæMéßĀä­[ßæE>ŽļlYÕ9ŅÖkLš4i"Ÿŗ0˖-k8pād[ĪNåK—R]'NœøŠ­įīŪŊ{Ōž}ûnđ5 ŗ`Á‚ÖđđđÅlƒąŲēukBAAA_ŗ0*• âããß°V÷1ŧķÎ;ķJKKIžäŗuŧ`ČËË[ZWWWÉWĐ%''ÚÚÚv[;Æ2ŦYŗfE{{;/ļ@G*îkí†å˗˙ķŪŊ{…|9åļÚr ¯žúęžūũûķ–úĘĖŪ˙Æūeŗˇ‰Ŗ?QQQ_ŗŨû ĩĩĩt||ü­¤¤¤yļĘ4zôč .e2îŊÔ5ÅV™ĻNzÜŌž[ÖPTTD‡‡‡ëŌŌŌ¤ļĘqk™FÕ ,öqëę∈ˆ œÉTPP@?účŖW€ÅŪwŨÉôčŖ^áZψˆˆ ļʔ––& ×qšGQnnŽŲ=-}ĸĸĸĻÄÄÄ4[Ú;ĐZ™ØîŲÕÕgÔ¨QOq-“RФŖĸĸžļUϤ¤¤y3gÎŧiÍŪ›æhjjĸ•J%˜˜xĐV™hš†ÄÄă\ęš6čō˜˜˜f{t”Š|\ętFž1cÆčíŅëĖ'nذ•ąąœî9Ų¤TŌ ÁÁ­I‰Íc!ķyôŅđMIIĪčwė8mÕŪ“–öĨLJzF/“%(í•/55U9ū|=Û=ŽŲĐÔÔDΟ?_/•J7Ų+_RRŌŧ„„„V.åŖiƒ>‘JĨ;ė•/**jJxx¸Ž+Ŋb*ŸŊē…6ŽY111Í\˗››Ë‰|+VŦ×q9VĐ´aßS{Æ0æ⨨¨FŽå+((`Ŋī¤ĨODDÄŽ÷M,**˛Ë.é,—v#Ÿ=öĨé',,ėŽå̍¨ ÃÃÃu]Égw‡ōõ™;wîꄄ„V{öÜÜ\:))Šž $99y2)•JzüøņœČ”˜˜xP.—[ĩ‘{w2ÅÆÆÖŽXą"ØdbŒÚØØØZ{_,0lˆ|LĄPĐö ŪMMM´BĄ ĶĶĶq%“RŠäDĻiĶĻÛûģ€866ļÖ^™jkkiš\ÎŲ ĖČdÆÍŠ[žzęŠ]öʔ––&ŠŠj´WϊŠ :!!Ą599y›Ŋ21ΡŊ#ĶÜšsWÛ+mÔ ­\}Æ`@#Î퉋‹û NĶCĘŪ aįOjh¨2!8¸ĩ"+Ëng˛@Ą Ã ŌqáL2™,AĻûíÛíLžũöŋéQŖ"›§L™ũ2Wō=ûėŗ/ĮÄÄ4sa”ŅRŠ´-55Õng—ų$%%Í ×qaô1ī.Î.ķaôJ^^žŨōÕÖÖrĻīL勊Šjä"pÅČĮŸÅ|ŌŌŌ¤¸\1c=—ō™Žų\É7mÚ´b.œ!Fž¨¨¨j{mF>ĨRIO:õ8—ōĨ§§ËĘĘâL>.l^SųÚkû2(•J:""âBwž '%_Ÿ˙ū÷ŋSl5Üåđ /s)ĶÆĶl•‰Qø\Ë´dɒgmUnjL˗/įLÉĶ4 īŧķÎËņņņˇl(‹ŠŠč1cÆčŗ˛˛Ös)Ķoŧąz˘1z{dzã781´MeŠŋe‹ÁSPPĀ‹L¯ŊöZžT*mŗEĻÜÜ\:&&ĻųwŪáĖ(¤ ĪøA[ ÆŲ¸qc—2Ŋđ ÅļĘdœ•ä\Ļôôôcļ:pJĨ’NJJĒ˙Īū#åB“ßîŲøøø[ļ}L€"==ũ—r1Ÿ7ĻŲ㌛šøO;9ęá‡m­lR*iÅčŅôƎ­^1yr0×ō͝ģ$X*}ĸūé§ŌļĖVîØqš~úé…tlėô sį.á\ž+Vŋøâ‹ŋØj”2†^RRR=׎äŗ7āÉčĨR9™gĐ^ųrsséÔÔÔ&ŽõŠ|öąssséøøø[K–,y–ų^xá…bš\NÛ¸*((āUž—_~y—=ĩ‚‚Z*•ļqm‹0{lšž zíĩ×ōøoíÚĩZm•ąÃų’oŅĸEËlĩ}Må›;wîs÷á\p>>qqqŽ5ĒYŠTš}!›ššč‚‚zéŌĨw""".p)îJ&Æa#Ķüųķõaaaŋp1+ŲŨ'11ņ [™ōōōčųķįëŖĸĸĒų$MebŒIs2ÕÖÖŌyyyôūđ‡f>eb"6ŒLæ FĻ™3gŪä2ę֕LS§N=>sæĖ›yyyebŋÄÄă|É´bŊ⍍¨ę?üá͖dǍ¨čp$šŒžvū¤ĨĨIÃÂÂ~™?ž>//ĪŦqc*SLLĖ>eŠŠŠĒž?žž  €•LŖFjŽ‹‹û/™æÎ›qaéŌĨwØČ¤T*éQŖF5s5+ŲŨ‡™­d;č2ĪúøņãëįΝˋ!júyꊧv͜9ķ&ÛA—ŅŖFjæËĐ3ũ$‡„l‹>üVBA7)•ŦÉŧ3čQ6/ŠŠZÆˇ|O<ņÔęāā0ŊR™Įʹܹã4­TæŅC‡†6?ņÄSŧ>{Æ÷bõ¨QŖš-Ŋ ĖØ‹īwƒĻ FߨQŖš-é6Sų¸Čŧ`ŅiQQQ–ÆSŠŠŠč™3gŪt”|ãĮ¯ˇ4Žw–ëYÉî>JĨr23h|ééé-\dEYú˜ÎϞu,+**:äãĶŽ¤éû6‰ĩō-]ēôßv.#ßÔŠS/]ēôŽ5ōƒA‘/==ũXzzz [ųjkk;f%ŲĢšĻyY´ÉaaaO 2$ûúõëã ""ĸccŖo˙ūũ¯ûíˇwāĒŋŋŋöĐĄC+h–ŊÚKrrō]__&‘HÚ~øáĄzŊ™úöí{A¯×YVVļÕ2!ŽŠŠĘ8pā͎oß8p a*SMM qīŪŊËF™ÖĶ6Tĩ˛EĻÔÔÔ,‚ RĮ 6ėNWũäííũīƒæ;RĻÖÖÖgnŪŧųˆL&kģwī^¯¯/üúë¯Ο?ß{ā'iš.t¤LąąąK}}}§{xx  ĨMejll¤ũüüjwZ[^Ũ™$ąąąķ}}}§wÕO$IzU;Z&…BĄŦĢĢ{Mœ8ąWssŗ˙õUUUÍ׎]˙ãŊzõúĖÚ2ųöȔœœŧúúõ벎djkkkõđđ8uéŌĨ‘H4ãŪŊ{á#FŒxD&“ÄDĮ‹ĸĩÖÖÖ^8pā:NåHųLũ€ČČČĀŽäĶjĩPQQŅāīīÜŅōEGGO‰DĘëׯ>|¸8%%Åģ+ųΞ={ÁŅ㯊|w5›öŸĩv”K9”Nü˙öÎįĩôŒã_AB”5ĩ3šÄr‚ŨÎB6ë*¤%ˇYzÉĻĐvÂö° ēĮ‚D˙€b—ģ‡zX’Ō%ĸ…ēštw īj2ëÅ::Ų'röā7Î/ ä0=8¯"i­~į™×Î÷sY2Ōh?~æÕķŧŪWšœ `@%ŠĸtîÔÚßÉđ ?đĮhĀÍîéD§N%ŋđKŗčŅ)ļ“ƒWš €ŸÖ„ô:šÅbņį[[[oŒŒlGQôß Ž™p _ų9ÅbņŖ.ŋ›&䇖߸øG[ĪžŊ=rôčö÷Ž[_ŲÚúÔ ŋŌéĶöĮ/^<›ÉįßÚÎįGÖīŨ[3ƝX,~øôéĶ>ūüäčččÚúúúŸLõ˙ßĘĘƟM˜īH?Ûļ/įršęgÛļ}utttrcccvrrrueeåīQ%#Ú!ũfff>8zôč;Ōo{{û_˙0$?›îg:uęÃBĄđé×h4ūũčŅŖOMņ›œœüŲɓ'ÚåˇhBÛËīå˗_¯­­}Ļâw`Ęļ€|EŅ{Y{´ų8œ(Š’ŋķ.Ū§9ž)šNûr2*7™ęôĘ˸ëGŋ7ĘΏÜO?íŽFæ>úŅĪtøuJB!„B!6”„B!„B”`CI!„B!D 6”„B!„B”`CI!„B!D 6”„B!„B”`CI!„B!D 6”„B!„B”`CI!„B!D‰#Y @.—ŗ؊§ŸĖårŽâš~Eĸ‡S €ĨđšgL&§(Šŧ^ę¤{ĻœÍår*įŠ(Š|:Š;år9 @IŅ)Nn2Ņ)ˆĸ(Đė#‰sũ$ũbĻú7Ķī`øŠÖ!IÜē  w  _æ~@ŧÜ'Ųs~sK?ķũtÔ8~IÖāØ~š(Šb:ÄįėŲŗ=wîÜ–ĩ˙|ąĩĩ…ņņņ}Ÿįû><Ī{¯W›žžžwéŌĨīī÷uÃ0D†8qâÄĄpē~ũú‹ŒôzÜD§Ķ§O?ŋråĘ[‡ÁéņãĮČįķČįķûvZ\\üĻ^¯˙€NęNš\Îqį‹Ri˙šZ57™č$„ĀōōōgwīŪũ•NIœë7LĖTĮũv>;ûKeŋ0|‚#GŽáȑcĘ~ëëŸķíˇë{úŠÖĄ×~ęš_Ō¯Đ/[? ^>úĪĪâĖaégžŸŽ×/éŦÃΈJp]ގyžjĩ Īķú>gnn.Õ˜čtûöíæ į˜ætæĖ™æÜܜōäį°8-..ö}œNÃ9•JĨÔĮ¸iNžįayyŲŸŊčŗ,ÆÕaō-4įw™ú­¯Ū÷ņŦĮß @ŋƒí7h~–ö–~éúe]ãL¯Ážįņ;”„B!„BÔ`CI!„B!D 6”„B!„B”0æ;”Ũ,,,@JĨ˛īskĩ|߇eYJį͉NtĸĶArŠãcĸSR׍~úđũÃŧûîoö}nŊ~ Žâøņ1Ĩķ‡ÁôøŅ~ôŖßaō3ļĄB@õ‹jĩŽ]ģF':҉No„S’ēnôĶGî P˜U:wmíŸx˙ũ?$ęgzüčG?úŅī0ųģåÕ÷ũŽ_|ō<žßy *Īķ„Į„‚q~‚˜NtĸĶwöōĄSöNŨ>&:eísØüz9Ļå÷đá*lûBëßA°„Fcĩã9A°!î# w:Ž…á„¸|~ėũčGŋÃãˇ—ã›æglC)BāęÕĢ(•J´<ļ°°ĐņSŋļmŖT*ĩž_ĢÕPĢÕž3)ČÚŠû‘ĩ“įy˜ŸŸ7*NĻ9IæįįĩųÄuō}Õj5•8õōŲ+NžīkįqœdœĒÕ*2w˛mģ#×-ŽSZųrØë֞/ĶĖį*~ōĩŌ÷ũųéÎĢa¸ƒ[ˇ*˜˜˜Eŗųēq”Į|˙/‚ĨÖq˚DĄ0‹Baa¸ƒzũVĢ ÃÁžú꓎&ԔøIt֝~IÔŨņĶ]’ˆŸÎ:ĄûũĢģnčöĶŖuÎ#M¨!ÃøĨҍúõĢF6”žīÃļmģû€-Ëjíį•ĮJĨ,˂ĸŖ ‚ ã\Įqā8Nėä ËɲŦÖsLŠ“@q Ĩ.'Īķ`Û6lÛxoδœäkA`ĖĩBĀu]”Ë娟võsęįĶí$“uœ-˜ē„˜››Cš\Ž}¯+NĩZ ā8ŽÖņĮi~~Žã´ŠLÖ1:ķeZų\Õo¯'á'ŠÔ]šžŅX…eMŊrŊ|~ŦõHyŦPxųüÂp§c%Sˆ XÖ$ ŸC>?ÖjDƒ` –5 ˚ęhBM‰Ÿ|í¸5 )?]õ )?]ĩ!Éøé¨Iƈ_7’ōĶUC’šÛęĒ!qjn?ŋ$ę‡NŋAõÃȆŌķ<8ŽĶZŠ•@vĖA´’ėšÛĪm_%Ņut9™§JĨŌlq'Ûēœ\×íx=œÚ'Ļ\;éRĢÕbOrú9Õëõž>ŨNr5)nŖ¤ĶIŽ!ųAEÖNŽëĸZ­âōåËZĮˇü˙Ē8ĩë$ķå~Ž[;iåsU?$Uotåú X‚m_€÷Ņlî6Œļ}Ąĩę(Ä&&vŋ_Ųl>Fŗų¸ã\ųX7ĶĶ;^ß´øéĒIųéĒIųéĒ IųéĒIųéĒI?y^Ö~ŨųNW ‰Ss“š{§á7Č×Ȇx=0]ׅīû­e~Ką:–‡ÛZ܉cN&Æ Ø-@r°™ā$?%q]WËö"Ní{ËulIĐá$„hmQĐąE§—SąXėéĶí$Ī/—Ë­ŋĮ§Z­Ļ-'Äuō<7oŪDĨRŅzŨâ8U*•Ö„OĮ‡&ēŽ›$­|Žę§›¤ęŽ\/W#gf.ĸŅXEŖąŠzũ´ļŋĘUÆFãëÖyBÜīųc>B܇eMazú"žüōãâ§ŗ$á§ŗ$5ūtՆ$ß:rMRãOWŨHÂOg IbnĢŗ†¨ÖÜ$įŪiøõķ5ōW^ģĻVŠw¯^¯?Q’vû€q§c‹€ NrŧÜöįM§ËIî)—[LËåræNōSß÷cŋŅt9šŽ‹ ´lyÕå$ûžßqNNŨ>ũâ$ˇX$}í†u %FēœÚˇŸ$#'™|ߏ•tÅH“ųōüųķŠåsŋîú8ų=Šzãyž–\ß}ĢŲ Ę˙ʕÆ]ŸŨ•FųHšUVk4VŅlî`bböÕęåîą^̘YÆOW HƝũXœ\—dütԆ$įciÔŌ¸~ĻÆOŽėÆ­!IÍmuõ*5w?Ųø%Q?tøY–Õˇ~ŲPĒ īšŌ>HĮŅēMQ‡S’÷Qur]WËD[§Sš\†BËwu9ģoļ;wî'9Æã6:Ú? 4éÚÉí&Ä)ëÜ´—“ëēÚžûĒÃGÆĻ=_„|žU~Æ/Ë\īû7ZÛc%ų|į}(KĨ_# wP*M%ú °Ēņ˛ĢÃøeY†ņ˲6ėįũkj~ɲn ›_˛Ē!ÃÎmMŠß^~&ՏŊüúՏCĶPöēYMÖú9eÉArâĩÎÉÄ8Ņi°S–>&: ķž3Ũ/KL÷ë^ÕėEڍ¤Äôø ë—Õ{„~ôŖ_|ŋŦĐágėw( !„B!„˜ JB!„B!J°Ą$„B!„ĸ„1ߥŦÕjZî[7,ÃüäwĩZM5&:mnn?hN›››Įé4œ3ãû~ęcÜ4§A÷ė21Fí˜8Ž’ß“'ãž÷ûŦ5ú’uüÕúlŋAķŗ´į°ôK×/ëgz Bā˙ŋ¯‰ķq}ø%tEXtdate:create2019-11-29T18:01:14+00:00ûE %tEXtdate:modify2019-11-29T18:01:14+00:00ĻũtEXtpdf:VersionPDF-1.5 \ 9IENDŽB`‚trillian-1.6.1/docs/merkletree/treetex/images/large.png000066400000000000000000004522041466362047600231760ustar00rootroot00000000000000‰PNG  IHDRŸÅĄCu IDATxėŨ{\×yøĶ3“)`ˆˆč™@…„˜ā,!z¨ŊK”RĢdŠ*ÖVšÖĘŽŗvąb{TüĒrŲ  üą•xXōÚåĘÖR&U„Ĩ(ŠeG"BflpaArG0Āčn}P_h0œG?n?n÷īTēû>Îãwē!•žû“’B€dY6œeŲĄ,ËNe˗į˛,;’eŲX ēĸJ @€ @€ @€(Ŗ@5üōō1į7]÷‹ĀuĮ­Ī @€ @€ @€ P€@–e“Y–Esŗ%‚ĐĶtI @€ @€ @€ P&gYÖHļķRęCe2ĐW @€ @€ @€hB x^*ˆÜĖ9č&æÆ­ @€ @€ @€(…@uŠíĸ3žįĢ-Á]ŠoƒN @€ @€ @€ũ,PéįÁ;Í ÄĪ)ĨÉækZ˛†™”ŌxĨR‰W… @€ @€ @  VtaŸt‰’dYKbˇ:đÃ)Ĩ#%aŅM @€ @€ @€@_ Č|îËi7hÅdYv>Ĩ4VLm5ÕŲĪjēŌE @€ @€ @€mųÜVnč,Ëļ9đxŅĻB€ @€ @€ ЅĢē°OēD€@9Ļ—ęæÉ“'ĶĄCą*wJ.\Hņ9/ccc7ĪåĮΞ=›âx\;=ŊhÕ|>žßã• @€ @€ @ {d>wĪ\č ˛ ,š…äͧOßO”‡‡‡o•'''oŧŋy2ĨtîÜšAį>įīįžŸķ~2˲v.ķ=§io  @€ @€ @€ @ Pg˔ééé›WŧüōË7ߟ8qâæûü͑#G˛3gÎÜø¯ņy‰˛hZtĄƒT @€ @€ @€@]2Ÿëâr1Uē˛#ë9J,Ŋ}đāĸ ĶĩâNÖzĄë @€ @€ @€Ú' øÜ>k-č{|ųí&!žÉn˛ˇ @€ @€ @€+ ø\Ŧ§ÚXD ĪąŸķB%ΌŽs333)ö~V @€ @€ @€Ę% ø\ŽųŌ[Ĩ8{ölŠ`ķéͧo“ŖĶņyn‰ ķøøøC‡JqO”P/ŗ4÷ĖÜzŧ'@€ @€ @€ @€’ dY6œ5PΟ?ŋä]gΜYō|õätIŲt› @€ @€ ĐĶ•žÁ Đ2,ËΧ”:ą>öߨT*˛Ÿ[6ŗ*&@€ @€ @€4&°˛ąÛÜE€@Ÿ §”~,ĨtäŽ;î¸zß}÷ĩ5øü§ú§˙qũúõ?T z˙ElŨ§ķ`Ø @€ @€ @€ŽųÜuSĸCēN ĖSJ“ÕžÅæĖ§ŗ,‹å¯OĩšˇG+•ĘɔR´Ҏsҟęk›ģŖ9 @€ @€ @€\@đ9—đJ€Ā\4įį )ĨįšÄû6/ŊYÎã ,šĸŖĪˆÎƒŅķģë3 @€ @€ @€@ Ÿ[ˆĢj%ˆå´ķ nÍŲÄY–E€ē]ŲĪĮ+•ĘŅeLķ,íKĢķ šåš—sš @€ @€ ĐŦ€āsŗ‚î'P^ÔæįEž1™Î5—,ËÎTëŠųž.Œ>í^ ëyŠĒ" Áņc”†ÆWŊ×  @€ @€ @€Ā2‚ĪË9M Įbię<āŨ< Ûpfp–eä=ŸRŠ×V”čÛC•J%úÚLÉŅ5gv7͘{  @€ @€ @€ũ& øÜo3nŧũ(īŨœī‰KQĮ_a%˲¨;2 [€>\ŠTNÖŲīUũÍ]ō=­Ã¤á |ÁũS @€ @€ @ t‚ĪĨ›2&°Ŧ@žÜtX#Ë767›9ŧdÃ-@G 8Χ—l¸ų“ų>Ņá%÷ĒkųņęŊ^ @€ @€ @€}+ øÜˇSoā=&PcIíÚF5‚ļmÍä­.Á}jÎ>ˍ2Gā÷ą–ÚގũŽîëí¤ë  @€ @€ @€Ũ( øÜŗĸOjȗŽŽĀķ܀smwˇđĒ,ËĨ”ŽT3¯ëi)ÆqŧRНįĻ^›ī֑9žī‘ŨÂ&UM€ @€ @€(§€ās9įM¯ûW ö)Ž€hœķ@hK—Ķn†:˲ŧŋņēØ~ĐyāüFļvĨRikļvã‹tŒŽĖėÜŋ[û[ĮĐ\J€ @€ @€h^@đšyC5hĨ@ž t=ã}žœvéö#β,æņ7ˇĖt`iíší7úžgæĨQ÷ @€ @€ @€æ >Īņ™@įdØv~ęíAžáAéx0 öÛîڌôzįz @€ @€ @€@-‚Īĩ(š†@ëōåœí-ÜzëVˇsÁčx@tž­ŪęvÕO€ @€ @€言āsGų5ŪĮų˛Í ŒĨ¨#@˛e{ëKs›ĸcdų<—nŲôۚŖ!@€ @€ @€Z! øÜ UuXX@ ra—~9ꁃ~™iã$@€ @€ @€}* øÜ§oØm°sÛ¨K×PžߑČxĪŗĸK7&@€ @€ @€„€āŗīâ"¨{8GĻsO[NģxäĢ1Đųžßą$wūŊ™éąq @€ @€ ĐÂĪ=<š†Ö6|9åÆûČ`€ŗ}}Û6=ÕP<´ĸc`y Ú÷ЧĻŲ` @€ @€ @€@ī >÷ۜQ{ōũ›#HŲŠyĀYĻj{üûŠ™ôũ4ÛÆJ€ @€ @€J, ø\âÉĶõļ äŲ¨ųŊyFjÛ;ĸÁžČ÷‡æ>ôС N€ @€ @€t€ās÷Ė…žtŸ@,ĄœíßÜ}ķĶī=’}ßīßã'@€ @€ @€]& øÜeĸ;Čz‘a%–ĶŽ?ûíVAŧtĨ€}ĮģrZtŠ @€ @€ô—€āsÍw!ŖŨˇo߁ģîēë§ūâ/ūb[–eëįVzõęÕīŧõ­o}ūå—_ū7Ī>ûėoĖ=×ÅīķĨŒã5‚ĖyĀŲūÍ]ō‘V,K­S)Ĩ3)Ĩ#ÕĖĘwKķÚ*ŲÄųī ~ ˙dY6eŲųz˙m™sũsQG[5F€ @€ @€ PŋĀûŪ÷ž;===÷\Äw+/ŋürväȑl˙ūũįŸxâ‰{ęīEÃwD -‚kd‹ŋēEZ!@ā{ŋ…ČˆŽC⁌cudâ—eYũO´,ūĪPô_!@€ @€ @€čBá­[ˇū—U" úīxĮëągt ĮKöF*‚iT‹āšB€ĀōmũídYvǍ[æÔĶé–Wv @€ @€ @ ß~øá§N*>6YĐ€.092™;šŊŲoß ãí |ՀxˆŖđU˛,;1'`\ô[čžøŠ$ @€ @€ P ŸúПúčÁƒ‹ŨŦ/‚ڏ?ūøWšĀčē}k›‹[ tģ@ĄûĨgYVÜr 7˙UyĶ›Xb_!@€ @€ @€č¤@ėÉŧ}ûöĢ‘ĄÜĘÁír×1օ–ļs€.%P@Ã+ dY6ÖĘWæÕŠ( @€ @€ @€xôŅG˙¨ËmĪ e5.ŋAŽ|˙æxî”‹v XP Ž‡BZŧÜöüf,ŋŊā”9H€ @€ @€hƒĀž}û<účŖ=?‚ĶĒĪä~×ģŪõ䜡åËûFĐ(öš=’RŠā–B€@÷ äûDĮŅų>Ņ7ŗŗ,›lÕŋ%KÔ{ŗũîįĶC @€ @€ @€@ Üwß}Ÿ>sæĖqœâOMLL\Ē™#؜Ŧ,§ŨCß+CéKø ߲<÷ŋĩøA–­1VLP @€ @€ @€Ú-°˙ūķK…rNœ8qËéX:;˛—#`ŊĐŅqüüųķ7Îßrãœ˙ī˙ũ,ĨôávU{´U`úßøÆ×æüôßôļŪ_ĸ‚ų÷ŧŠŌ,{Ž­ŖÔ @€ @€čs}>~Ã˙žĀđ† îūūĮ[ߝ={6>}ú–ƒĮOLqî… ˇœ;wî܍cccc)ËÕ÷Ũw_zī{ß;žĐ9Įč ,Ë.lŲ˛eûbŖŠ÷ߗ¨gĄ{¨?–úļ’Â0 @€ @€ @€V>ˇBĩœuNīŨģwõb]ŸžžžåÔɓ'S–#täȑ49yëÖˍŽķQâ\\ŋP‰āõk¯ŊvßBį#@ g–Ü{šŪ_Beū=KHŨúĶ:E€ @€ @€4' øÜœ_ĪÜŊcĮŽ÷Ī /5¸ČtŽŋ=zôM™ĪKŨ;÷Üđđpē|ųōčÜcŪ ĐsKŸįļ¨_ĒõÖÕöüžøL€ @€ @€Ô. ø\ģ•+į äÁę@/–Ų<ī čOēĀūûRwÛũ9EFM€ @€ @€š|nŪ°/kˆ%ĩgffnŒ= Íy‰ķųÜų1¯ô•Ā÷ūą¨qČËũûRc5ųeuĩßä• @€ @€¨_@đš~ŗžŧã/˙ō/ŸĨn+ąˇsœŊœ#˜{5įKãÆkėûĮĮĮĮoTqčĐĄûAĮ‡sįÎŨ¸~ąē×­[wuąsŽ Đį–EŊ˙žD]ķīYĸū%Û^â>§ @€ @€ @€;tčPVo9sæĖ’ˇ,wūÔŠSŲūũû?Ņ`ŸŨF€@ ˛,›\ōŠEN.÷īĮ"ˇÍ?lŲí|Gt‘ @€ @€č1\œĩiõį#GŽdûöí;Đc”†C€Ā<,ËΡúߓę??¯> @€ @€ @€-°ėv qËVuĨRųOąDv;Ë˙ņ_zæ™gžĐÎ6ĩE€@GNw ÕN´Ųaj’ @€ @€t‡€āswĖCWôbvvö÷bOįv•t_Ŋzõ[íjO;tL`lįΝ›;ĐzûūAëĀā4I€ @€ @€čj‰‰‰o?÷Üs Ŧ^[üĄŠŠŠ˙ī}ī{ßÎŽŅ9š˜N)J)H)MgYļô&ņÅū3sĻ™Žģ— @€ @€ @ Iŋë]īēVl čÍĩÅ^Īī{ßûūm“Ũu;Ũ'0œR:”Rz.Ĩt,Ĩ4–w1˲@ˇĢDā[!@€ @€ @€č¤ĀG>ō‘nU‰ĖęûîģīJ'Į¨m ˆ s›#ã8‚Ī –,ËN´ęߖ9õF? @€ @€ @€nØŋ˙ųV-ŋËm?ņÄ÷tÃ8õĻnYZģ–Ú˛,kåÚū‘q­ @€ @€ @€t‘Āđ–-[ޜ9SÜ­įΟĪbIī‡~øHSW¨_`ŅĨĩkŠ*˲á #¨}S @€ @€ @€ēL`øņĮ˙J,ÁũōË/ĪYŅļūˇ§NēąÔö>đé˛1ęĩ äKkGvņĸKk×R]5}ĒūMžã÷˙÷ãIįZđ]C€ @€ @€č”Ā˙ø˙ōöíÛ¯6’Aëƒf>úč ujĩK iXZûDJéTJ)ŪV˛,;–eY3OˇÄŊąĮsü5/lP*"@€ @€ @€X\ öhŪģwī7>ôĄ],æų™Đķ?G :2Ļ'&&ž-ÛyqWgtš@sĪTģ‘õܒR͂>QgúFĐyŪC-Ņ×ɖtRĨ @€ @€ @€Ā˛•e¯p[Æîŋ˙ū]ŋ~ũGŗ,[?55ĩö7ŪøęWŋšöíۗūø˙øŌĨK—ŽŒŒ|ũ;ßųÎņgžyæ ˇŪî].Aæ:G†ķɔŌé”ŌLģúœeŲÁjÛŅšYÖ҇s)Ĩ )Ĩŗ•J%ú5ŋIJÛ€~¨}žß Ÿ  @€ @€ @€ˆ,ÏîŲŗįËķEÕæ.:%вĨĩÛ< G,Ž @€ @€ @€”M`Īž=ŸxûÛßū_ĘÖoũ%@ā†@[–Önŗuė˙|¤ÍmjŽ @€ @€ôŊŠžĐ´ĀęÕĢīβėo4]‘ h—@,iÚįĒ >–R:Z]Ōē]}he;1–X•aî˛Ũ­lOŨ @€ @€ @€EėØąã‡ūNJ)Z Ũ+Đ+Kk×"û?Gp=^ @€ @€ @  2ŸÛ€ÜëMT*•õcccãããŊ>Vã#PR|ií>O)EĻķŲ’ŽĨÖnΤ”Û˙šV.× @€ @€ @€æŸ›7ė÷ÆöîŨģvķæÍi͚5÷;†ņč"^_Zģęs)ĨĶÕ%ÆkšŪ5 @€ @€ @€@‚ĪMāš5ĨŠŠŠû"ëyjj*­\šr':.{Ÿ¨ūEđuwJédJ)2ûąÄØcéíƒũ8xc&@€ @€ @€íXÕÎÆ´Õ{333DÖs”ÕĢW¯îŊŌÄŌÚąŦv™cií Ĩéyë;z´ēüvãš´Ū[  @€ @€ Ч‚Ī}:ņE ;˛#ë9ĘwÜŲ… íˆßܑ”Rd;ĮÎąĮqŋf8/Ĩ&€ŽŒđ‡–ēĐ9 @€ @€ @ qËn7nįÎyŲÎÛļm‹@XÁZ+/­}*ĨŲŧPlgįÅŨķũŸ#­ @€ @€ @€-|nj?U97Ûybb"íŪŊûŅ~ŋąhŗ@,­įx`sOˇšen.ßû:ü @€ @€ @ `Ën ÚgÕMVŗo {ĶĻMņúŽ>30\­°´vąÂąüö™jÆxdC+ @€ @€ @€@A‚ĪAöc5‘åŲÎy‰÷WŽ\Ų™öJ€@Są´vdčŽĨ”"c7‚ĻJ1ą7v,ŋũ˜ĨʋU  @€ @€Ág߃fŪQÍvžYĮŊ÷Ūģę›ßüæÍĪŪčbČ(>xīŊ÷ūК5kūÃø_­.a}ĄÃ}Ž€ķt5(Kkwē?æhIķaļyē%¨” @€ @€ô›€āsŋÍxã,įš™ĪQõš5kÖĨ”"¨7S`SĒ"PˆĀÖ­[ßw×]w~å•WŪyĪ=÷ ŋį=īY=6‰Å)ÍĖĖ|āÜšsĮžzęŠŋēíļ۞ŋvíÚŋzöŲg?ŲĻī˛Ĩĩ ™áē*9[ ōŠĸëēŲÅ @€ @€ @€ <ōČ#ß~ņÅŗšūđ‡ŗj@§Ā–TE 9~ôŖėßŋ˙ü‘#G˛įž{.[ޜ?>;qâDļe˖+øĀūIs­/ywDž#û6ö ŽŒgĨũaKœ+ @€ @€ @€ū;įī\™xŽ÷ŸúÔ§˛={ö|ĸC}Ō,7 üƒđžzôŅG˙:Ęõ–—_~9‹€õC=ô—O<ņÄ=oĒŧņĢį< |6îXĝ‘uū\uņ"ęS @€ @€ @€@Ķ‘åúGī˙ûGÆÆZķü˙â_ŧåë_˙ú§ØÜĨĩc)įĮRJ‡SJįšŪáî8ZÍNo͗§ģÆĒ7 @€ @€ @ pÁįÂIûŖÂJĨ˛~ą‘Ž ėÚĩëáÅÎ;N UøĀ~äĩ×^ÛwäHŦ ÜšAíx`øƒüāįæ´/­™ŗ3)Ĩ‡RJȌ÷JybžbŪb @€ @€ @ NÁį:Á\~C`ŦšŨŧ ĮæÍ›Ķĩk×î_đ¤ƒZ(đgög'Oœh}Ü0‚ÛßųÎw~xÆ ˙(Ĩt&ĨŅî“ÕLįxUĘ+Yę§ Ë;zN€ @€ @€|îœ}i[ŽŦæČn^lSSSiåʕ;;ī8VėŨģ÷Ï?ūøhĢ–Ûžßį_úĨ_Z=88ø?ZZ{žLO|Ž" úPOŒÆ  @€ @€ @€@›ŸÛŨKÍDVsd7/UV¯^ŊzŠķÎ(Z`õęÕ˙ÃÁƒ‹ŽvŅú"ČŊ~ũúQKk/JTöąüE‡zá IDATv|Ąbīn… @€ @€¨A@đš$—Ü*Y͑ŨŧTšãŽ;†—:īĸūÛûo[—Ęz>yōÖÕ°?žÎž=›æĪûįâīôéXyá255ĩnßž}>ëhN)K)ų÷Ŧ&Ķ @€ @€ @ õ‚Ī­7îšjÉjŪļm[kĻ{nđÔ­c^Ŧs ‘/\¸p#¸<=ũæ¯i¤‡‡‡SœËƒĐ Õ=99KĖ˙Ũ…Î9ÖRJĮí˙Üsi @€ @€ ĐÁį6 ÷Zĩd5OLL¤ŨģwËíĩÉīŌņŒŽ@đbeĄs,Ņ eKGā9‚ĶQâũšsįŦ:îã7î_đ¤ƒŊ"p6Ĩ_†#Ŋ2 ã @€ @€ @€­|n•līÖ;]Íj^r„›6mŠķīXō"' $pįwnŠ q=%‚˰>zôhš™™šåÖ<0ãēøŧXyõÕW;įxĪÄūĪ‘"ŋø=3T!@€ @€ @€ >7nחwF6sd5/Wâš+WŽė\î:į tJāĐĄC7›^l_įX~ûȑ# fGßŧŲ›~xŦēüv}O9ô‹Žq @€ @€ @€”’āŗ¯AŊī¨f5/{߯×,{‘ t@āøņã)ËK•HGftü-ļėöR÷;×s‘8ĨtĒįFf@ @€ @€ @  Áį‚ ûĨšČfŽ%ķ9<6lØÁgY‚ũōåčā8_~ųåß_*@|öėŲËgG@9–ØÎ÷zŽ÷ųįxŋ1Џ.–ãŽŋ‡zčMËrįC%šīŧķ΋ųg¯=/›ĮĐĮz~¤H€ @€ @€XÕĀ=nécz˛™GGG×U÷H`B eßūöˇįŲgŸŊ–RZŊP#ĶĶĶéüųķ7OÅūĐņë'NÜ<ž_3wĪį›'xA푑‘Ī-pĘĄŪ8^]~;6?ŨģÃ42 @€ @€ Pŋ€ĖįúÍúųŽáj6sMSSSiĪž=×tą‹4'0ķŌK/ũy=UDđ9‚Ō͔Īūķמ|ōÉĨ×īnĻ÷vĢĀŅ”Rl>Ö­Ô/ @€ @€ Đ ÁįN¨—ˇÍÉ{īŊ7˛™k*ąųäģ>ûŲĪÎFp¸•åđáÃijjęjeę.@,ŋ}0Ĩ4Yšë( @€ @€h‘€ās‹`{ąÚF˛˜ĮÆÆvíÚõp/zSw ėŲŗįĐ?ü‡˙đzĢzW]nû™_ūå_ū?[ՆzK'p8Ĩt,Ĩ4\ēžë0 @€ @€(P@đš@˝jŦšÅ\×07oۜŽ]ģv]7š˜@Õ đ˙ŨŠåˇĪ;—>ųÉOūų§?ũéjĸ‹ní=HĩõŪí˙Ü{skD @€ @€ P‡€āsXũ|id/GsŊSSSiåʕ;ëŊĪõšxꊧū—˙ō_ž;zôhš™‰-y›/ĖūЇ>ôįcccģųÚÔЃgSJ„>ԃc3$ @€ @€ P“€āsML.ŠėåČbn¤ŒŒŒdÜįÍüɟüÉî^xáį|đÁ7΍¸`c%‚×=ôPzöŲgŸųÚמöļĪ|æ3_mŦ&wõ@ė˙˜iC$@€ @€ @€Ÿdqpž@d/Gs#%˲ģšĪ=šøÔ§>õs?ķ3?ķƒO<ņąF˛ OŸ>Ūķž÷üÕ<đ“–Únv6úæūĮė˙Ü7sm  @€ @€ 0O`ÕŧĪ>XP ™ėåmÛļ ?õÔS‘ Øxúé‚ŊrĀō=öXd*ŋņÆ~ōÉ'ŽoxĪ{Ūŗzzz:ŨRAd9G–tėíüīūŨŋģ444ô•/ųËųË_žå:,!ëŧGôŠ”ŌCK\į @€ @€č9Áįž›ŌÖ ¨™ė剉‰´{÷îĪ=÷œāskĻG­5<ûėŗŋ‘Rú¯}íkÃW¯^=tęÔŠü×˙ú_‡Ūō–ˇ ŽXąbåÚĩk/ŋņÆ×ĪŨ~ûíŋô•¯|ÅōÚ5¸ēdAsՇmŽUŅ ^ä  @€ @€č5Áį^›Ņ֌g:˛—­zûöíqë;Ŋß} ˜yōÉ'§”â/ĘôîŨģ?üôĶO?Zũė…@ņũŠėgĢ>ĄŠ @€ @€(…€=ŸK1Mídd-GörŖeķæÍéʕ+;Ŋß}Z)pāÉëׯoieęî[Ã)Ĩ#)Ĩ[×wī['@€ @€ @€^|îõ.f|ī¨f/7\ÛÆ×4|ŗ ´PāōåË;gggZ؄ĒûW ß˙ųD˙9 @€ @€ô“€ās?ÍvƒcŦåČ^nĻlذ!‚Ī /ŨŨLÛî%°”ĀĀĀĀĀúõëW.usšˆũŸO§”b˙g… @€ @€ô´€āsOOo1ƒ+"kųŪ{ī]—Rš,ĻGj!PœĀĀĀĀ=ŗŗŗˇWŖšŧIādõá›Co:ã @€ @€č!ÁįšĖ e¸šĩÜTõągôž={oĒ7hĀĨK—†ŪúÖˇ~§UĢ’Ā\Ø˙ų ‡pæ’xO€ @€ @€Ŋ& øÜk3Züx&ĢYËMÕÁįJĨ˛ŊŠJÜL Ûļm{åž{îš;Ĩ4ւęUI`Ž@ cųm[ĖUņž @€ @€zF@đšgϞ5‰lå7[ĶÕĢW›­ĮũŠxá…ŪV­Sđšh\õ͸RŠ%¸í˙<_Æg @€ @€č Áįž˜ÆÖ "˛•‹>G+•ĘúÖõTÍXˇnŨņŸššēĢąÜE .Ķ)Ĩ™”Ō‘ēîr1 @€ @€(€ās &Š“]ŒlåČZ.ĸėŨģw­Ĩ‹TGÃÛˇoīøĖĖĖ#ÖĢ*K M)MÛ˙y)"į @€ @€ @ Œ‚Īeœĩ6öšČlåąąą]ģv=ÜÆîkŠĀr“Ë]ā< <–R:a˙įéĒ– @€ @€:" øÜöŌ4:ļ˙ū‘ĸzģyķætíÚĩû‹ĒO=šŋcĶĻMąävĩėvŗ î¯G –Ū>œR:UĪMŽ%@€ @€ @€Ũ, øÜÍŗĶážE–ræŠ*ā[šråÎĸęSfFFF‰‡"ĸ\ŧxņžfës?:ÎĨ”ÎĻ”ŽÕyŸË  @€ @€ @€@W >wå´tG§"K9ĖÕŖ‘‘‘Ŧ¨ēÔC YŲŲŲ;ō:FGG_Éß{%ĐFã)ĨąęĐmlVS @€ @€ @ xÁįâM{ĻÆČRŽlå"K–e–6.T]M ÄRÛųw|Ŋ›ĒĖÍˆåˇTƒĐ×âN @€ @€ ĐaÁįO@77?::ēēčūmÛļmX†_ŅĒękT`îRÛ¯žúęĒFëq&b˙įŖ)ĨMÖãv @€ @€ ĐQÁįŽōwwãׯ_@qĄebb"íŪŊû@Ą•ĒŒ@ƒˇß~ûÕüÖÉÉɕų{¯: û?Ÿļ˙sä5I€ @€ @€… >FŲsMWŗ” ØöíÛĶĀĀĀdĄ•ĒŒ@ƒwß}÷Í=Ÿ†då7éļĸNĻ”âĄŸCEU¨ @€ @€ ĐNÁįvj—¨­ČNŽ,åĸËæÍ›ĶĨK—ļ]¯ú4"đâ‹/ŽÉīĖßz%ĐIØ˙ų`JÉC:œm @€ @€ @€@C‚Ī ąõūM‘YĘ­(7nŧđkEũę$PĢĀÄÄÄõüÚxØâÁ|oūŲ+ DúX5 ēƒŨĐ4 @€ @€¨O@đš>¯žš:˛“#KšeÆ |.|?éVôU=-0yįwŪ5w„/ŊôŌÆšŸŊ'Đ! )ĨX‚;Đ  @€ @€(€āsiĻĒŊmevōŊ÷Ūģn||üöŽHkŪ$pË›6mŠũČŪt•:#p:Ĩ4“R:Ō™æĩJ€ @€ @€ę|ŽßŦîŽf'ˇdŦąŧņČČČ#-Š\Ĩj8pāĀäÔÔÔÍĢ#Ķ˙úõëö#ŋ)âMM)MÛ˙š fB @€ @€ @ &ÁᚘúëĸČJŽėäV:‚Ī•JĨ5JˇĒĶęí9Ë—/īœ?¨ŲŲY™ĪķQ|î´Āc)Ĩļ*čô4hŸ @€ @€j|ŽEŠĪމŦäˇĒ ĻĢW¯ļĒ~õ¨E –ØŽĨļį–;v\ûŲ{] KoN)ę‚žč @€ @€XR@đyIžū<YÉ­ >‡jĨRYߟēFŨ-÷ÄRÛsËķĪ?ī{9Äûn8—R:›R:Ö-Ō @€ @€ °€āķB*}~,˛’#;š•e˙ūũ#)ĨąVļĄnK \ētihūųuëÖŊ1˙˜ĪēDāxõßĖØZ!@€ @€ @€]) øÜ•ĶŌŲNĩ#+9–;Ūĩk×ÝŠÖûY`Ûļm¯Ė˙öíÛG=1_Åį.ˆåˇøŽvҌč  @€ @€Ü" ø| ‡)ĨÉjVrK1bšãk׎ŨßŌFTN` ^xám‹œ–‘ŋŒÃˆũŸĻ”Ntŧ':@€ @€ @€|^ĨŸíÚĩk_d%ˇēLMMĨ•+Wîlu;ę'°˜ĀBKlĮ^įSSSw-vãē@ ö>m˙į.˜ ] @€ @€ @€7 >ŋ‰¤ŋD6rd%ˇŖŒŽŽŽnG;Ú °€Āpu‰í[NÅ^į333ÜrĐŨ'p2Ĩ4œR:Ô}]Ķ# @€ @€čgÁį~žũÆŲȑ•ÜŽrũúõž(:!0Ų‰FĩI @Ø˙ų`l•P`Ē"@€ @€ @€M >7Å×{7¯]ģvmģFĩmÛļ>OˇĢ=íČÆĮĮīXhyųxđbttÔ˛Û9”×nˆôąjtˇ÷U˙ @€ @€ @ Ÿû`’ëâmˇŨvG=×7smė¯ģ{÷îÍÔá^ŒŒŒ<˛Øōō/^ŧ§‘:ŨC RJąw  @€ @€ ĐqÁįŽOAWu`ēšÜ–Nmßž= X2ļ-Ú™+0;;ģčCŖŖŖ¯ĖŊÖ{].p:Ĩ4c˙į.Ÿ%Ũ#@€ @€ @€@Ÿ>÷ÉD×2ĖČBŽläv•Č<ŊtéŌ–vĩ§šĀĐĐĐĐb{›¯Xąbc~W%8j˙į’Ė”n @€ @€ @ ĮŸ{|‚ë^d!G6r;ËÆ×´ŗ=mĨzxõÕWWQ"PBĮRJ'ė˙\™Ķe @€ @€ô€āsMfŗC‰€Übûā6[÷b÷oذ!‚Ī˝wœ@+nŋũöĢ‹Õ;99šrąsŽčbXz;2 #­ @€ @€ @€Ž>w„Ŋ;íDōŊ÷Ūģn||üîŅĢ^¸ûîģŨķy```(Ĩ4ŨĢc7Žž8›R:—R:ÖĶŖ48 @€ @€čZÁįŽšļwl¸š…Üֆcé5kÖ<ŪÖF5Ö÷/žøâĸËŊöŊ€R O)y€ĸÔs¨ķ @€ @€(­€āsi§ŽØŽGöqd![ëōĩEđyíÚĩ?°ü•Ž PœĀÄÄÄõÅj‹īäƒ>øŪÅÎ;N ‡ĢŲĪļ4(Ádé" @€ @€zI@đš—fŗ‰ąDöqŨÚ]"ËôęÕĢRMÛ ßßíMŪyįw-EđŌK/m\ęŧsē\ öŽôŠ.ī§î @€ @€ @€@ >÷؄6:œČ>îDđ9ú[ŠTÖ7Úo÷h@`ÉlĐM›6Ĩęu nˆŊŸOÛ˙š›ĻD_ @€ @€ Đû‚ĪŊ?Į50˛;ĩ×íūũûGRJ“5uÔEš8pāĀäÔÔÔĸĩlŪŧ9]ŋ~}Ëĸ8A <'SJņ°ÅÁōtYO  @€ @€ @ Ė‚ĪežŊûŪÉėãČ4Ũĩkמ‡Ŗ*‹ \ž|yįĸ'Ģ'fgge>/‡ä|YŽĻ”y§,ĶĨŸ @€ @€(ˇ€āsšį¯¨ŪOVŗ‹Ē¯Žz"ĶôÚĩk÷×u“‹ 4(KjĮK•;v\]ęŧsJ$ī˙|Ŧš]ĸŽë* @€ @€”M@đšl3ւūFÖņrÁ¸4{ŗĘXyåʕËfŖŪŧÁM Ü<,Užūyû/ä\Ų.¤”b î@+ @€ @€ @ e‚Ī-Ŗ-Oőu<11Ņҝ]ģvmG; ņž¸téŌĐrƒ]ˇnŨË]ã<’ œN)Et,Á­ @€ @€ @€–>ˇ„ĩ\•FÖq§ƒĪˇŨvÛåRĶÛ˛ lÛļí•åúž}ûöҔԨr×9O dą˙ķAû?—lÖt— @€ @€%|.ŅdĩĒĢŨuŧmÛļá”ŌtĢÆ¨^šĀ /ŧđļüũ2¯‚ĪË9]JĮRJ'ė˙\ĘšĶi @€ @€tŊ€ās×OQë;Ø YĮ‘yŊ{÷î­­ú] –%ĩãû855uWŋ[O ÄŌۑh… @€ @€* ø\(g)+›Žfw´ķÛˇoO“í„ÆûA`xëÖ­ƒË tpp0ÍĖĖ<˛ÜuÎ(ŠĀŲ”Ōš”Ōą’ö_ˇ  @€ @€ @ KŸģtbÚÕ­Č6žššjWs‹ļŗyķætéŌĨ-‹^āb&W­Z5PLUj!PjãÕ}ÍmwPęiÔy @€ @€t—€āswÍGÛ{ŲÆ›6mj{ģ 5¸qãÆ5 wŒ@QãããwÔō}2FGG-ģ]ŧzēUāp5ûy¸[;¨_ @€ @€ P.ÁįrÍWáŊlãČ:î†ōöˇŋũļ”’ H7LFöaddä‘Zŋī/^ŧ§G ‹@.û?GúT~Ā+ @€ @€hF@đšŊ¸ˇ›˛GGG‡ÆĮĮčVCčRŲŲŲ;jíÚččč+ĩ^ë:%ˆŊŸOÛ˙šÄ3¨ë @€ @€č"Áį.šŒte¸šm܁ĻßÜäÄÄDZŗfÍão>ãb††††jŨã|Ŋ‹iU-ē^āduՉƒ]ßS$@€ @€ @€ŽXÕÕŊĶš– D–qdˇ´‘:*āķÚĩk Ž[\J .XfžÖ^}õU˙>֊åē^8Z]~ûBJ)˛Ą,Y–MĻ”ĻĢÁęxŸ—XÂ;îŋĶ•J%>+ @€ @€ Đg‚+}6ás‡YÆđí–288˜Ūō–ˇÄžĪ –Ü~ûíWk­xrrråWŋúÕZ/w˛ äû?ŸH)=–Rē<βl,Ĩt(Ĩ™Ņņ~šr"˲ŗ)Ĩø;)Ŋ—ķ @€ @€čËn÷Î\Ö=’Č2îĻās āõ×_[Ũqîžûîš÷|ˆU"ÃS!Đ/‘ĩKp‹gY6œeYŖĪ§”ŽÔxέâˇõœĪ˛,×  @€ @€ô€āsLōbCŒ,ãČ6îϞ˙ū‘”ŌÜĨ\ģŠ{úRr_|qM­CčļßF­ũv&NGÖķĮ?ūņ_Š› §”"úšę’ŨMvĪí @€ @€ ĐÍ‚ĪŨ<;-î[7foÚ´)íÚĩk_‹‡Žú>˜˜˜¸^ëĐcU€|đŊĩ^ī:Ŋ"eY:zôčGĒû:5Ŧx¨čL–eV(JT= @€ @€čBÁį.œ”6ui˛šeÜĻæjk&~׎]ģŋļĢ]E .É;īŧķŽzîxéĨ—6ÖsŊk ”] ēĖv,ąŨŠYĐ€n6›ē}S' @€ @€ ø\b̈ėâČ2îļÁį•+Wîėļ~éOODāĢæŋšop!’ dY{4ˇ#0|ĖÜ%˙˛č> @€ @€|^ĻׯXąb:ŊŨXÖŽ]ģļûĨOå8pāĀäÔÔT̓Øŧysē~ũú–šop! Tŗ‘[•ņ<_&Ī€Žëų•øL€ @€ @€Ũ' øÜ}sŌ–EP­[ƒĪˇŨvÛmAĐH_ \ž|šîŒúŲŲY™Ī}õ-éĪÁfYAāČzngéD›íŸļ @€ @€ З‚Ī}9í)usvņļmÛ"(1Ũ§ScØ-¨wŠų;v\mawTM ["ãšYȇ,ŋŨ-_ũ @€ @€ @€@1‚ĪÅ8–Ž–nÎ.ŽĨ‘wīŪ} t¨:ÜÕCCC[b)ízĘķĪ?ŋžžë]K lY–ĩiŸįÅhڝqŊX?'@€ @€ @€Ÿ @,aĶÕėâŽėzd§ LveįtĒ´—.]ǎķëÖ­{ŖŪ{\O d;”õœ3MWāųg¯ @€ @€ PbÁįO^Ŗ]đÁßŲÅŨZ";õŌĨK[ēĩúUNmÛļŊRoΎoß>šRŠĖP…@¯ tÃW @€ @€ @ Ÿ{`ëÂĩk×ļÖģ÷mŊm4{ũƍ×4[‡û Ėxá…Ū6÷sīŸëĀriy˛,‹}ž >Ÿ}úô-5/tė– RēqO\%Ī333izzúÆk|^¤Č|^Æa @€ @€”M@đšl3V@ːU<:::4>>ū@ÃUŪēuë`Ŋƒƒƒ4{¤Ūû\O $‹f=GĀx~YčØÜk.\¸ÆÆn]( 2Ĩ#ÃÑhŊpŠfa/|ŌQ @€ @€(€āsiĻǰŽ6„+Ŧõ+ŠŒĶ5kÖ<^ãå.#°œĀäĒUĢ–ģČyˆāķää÷“Šã}ŖwīŪ}#øÁįōÍYS=Žlâėv{‰åŽ×Ž]ģŽÛûŠåy¤Ņ}Î/^ŧxO9FŠ—:+č8ĮÛųû>GÆs —Øķšŗ×: @€ @€& ø\e9*Šlâ2ŸCķå—_ŪPUŊėvŲŲŲ;íãččč+Ūë>eˆ r,ŖŲĘLŽ2˙Xŋq.˛ž#М_<˜bĪį(ņzčĐĄīųī5˛ČI‡  @€ @€ @ ĢĘŅMŊ,J ˛‰#̏ e˙ūũ#_ũęWcĐseč¯>v¯ĀĐĐĐP,ĄŨHYąbÅÆFîs,úok’ĪŸ?Ë:6˙š8Į_^"ë9‚ÖËžSĨRY´/y]^  @€ @€ @ ûd>w˙ÚÃ2eĮŊģvíÚW(€ĘúRāŌĨK[øĢ¯žę!FņÜ×íZŨÁáááŅË´Ķō~,ĶžĶ @€ @€ P€āsA%Šf2˛‰KŌ×˃¯Xąbē,ũÕĪî¸ũöÛ¯6ÚģÉÉɕŪë>Ũ,PÍ6î†ĀīénvŌ7 @€ @€¨]@đšvĢŌ_YđM\–Áįëׯ7œąZ–qęgëîžûî†÷|J)yĸõͤ…ÎtCāˇúĐ}­ @€ @€ @ ĮŸ{lB—NdG@ˇLeíÚĩkËÔ_}íN_|qMŖ=+Ë鍎Ī}}+0–R:ļiĶώwRāĪūėĪ.V*•c)ĨC)ĨáNöEÛ @€ @€ Đŧ€āsķ†ĨŠáʕ+;Ë|žíļÛÎX-ÍÄčhË&&&Ž7ÚHüf|đÁ÷6zŋût™ĀÁ”ŌДԑ”ŌŲoûÛ?¯ęãßú[ëSJUۏ~E :ã  @€ @€”P@𹄓Öh—ׯ__ēŊkˇmÛ™p–ŋDvlčȀŽ€šlčųJ> @€ @€ @ KVuiŋtĢxéjqņ5ˇ°Æü]ģvmk ›Pu 8p`2bh´lŪŧ9]ŋ~}KŖ÷ģ@›âa‹ČŽŋ<¨|K°yĄūT*•ŖY–Å*°nG9ZŠT–Zî;‚åŋK ãØé9Ûíč§6 @€ @€ @ ™Īu`•ųŌ={ö<ŪLŽScĀßĨK—ū:5=Đîå˗w6;ŒŲŲY™ĪÍ"ēŋÕ‘%ÚØ79Jdŋ)Ëšzną—ČŒŽoĢËÉJĨROĻuœcT*•|iîȘnU‰ĀķBËm×Ō^dpG@}wõâXR<ūڕ­]K]C€ @€ @€ž|î“éėáČ".cŲēuë`uīĪ2v_Ÿ;,044Ôôw˙ųįŸ_ßáahžĀ\<Ë9‚Îņ>˛‚# ģÔ2Ösī_ô}u˙į`GļqŅ%–Ún4đ<ŋ/`qĮkdAGÆwŧ* @€ @€ ĐAÁįâˇŗé2gŒŒ ŒO´ĶK[Ŋ#péŌĨĄfGŗnŨē7š­Ãũ ˆ%§#Ķ7–׎ėäĮRԅ.•ЕJ%hQ÷Œė:—ÚŽ•+" ą w,É>”W @€ @€ @ Í‚ĪmīPscÕėá5ß\ŗi͚57W‹ģûU`Ûļm¯4;öíÛˇ f5Ģčū" ŊTās›#0܊Ėä[ēXŠT"Ģxŧ‰w†ĢT*U*•Ļŗ˛oéܛ?DšeYô)ö˜Ž×…‚ēŽ€sô÷tĨRéÔī%ڏŋøÍ†ažvC{aßÄđ† @€ @€–|^–¨üDÖppËZĶwŋû]{î–u;Üī"–ĖŽßĪÔÔÔ]_üâ;<Í÷@K#°Ü–F0ˇ+Ju?čŽéO (üŽĀy”pLč8ÖUŽÕūy!@€ @€ @€@OXvģ'ĻqéADÖp™ƒĪ1ēëׯ.=Jg ,(0\ĒķņÄĖĖĖ# ļ⠁æ"C7ö)ŽĨĩ#Ķ9ßøLŪæZ[Cœķ=˛#}ϐnmĢj'@€ @€ @€@Ÿ >÷Á„GÖpĪĘ\öīß?R]ęĩĖÃĐ÷ö LŽZĩĒ´KџK‹mˆ įČÆĀsž´vė[YĪJk"ķ9‚ûąov”B‡<  @€ @€ @€@“‚ĪM–áö^ČŪ´iSÚĩkמ2xëc÷ŒßߝfËÔÔTŊĢŲzÜO šŲ{GĐ3‚Īą,tB#øŦ´O _Ö<˛ĄÃ>Ÿ“ØĶZ!@€ @€ @€Ÿ„+Ņm“ÕŦáuųÍ]eÃWŦX…@Í###lŪŧšæë—ēđâŋ÷,uŪ9ËLĻ”NT3#đAĪįŲĐ1Oy6t,Ž @€ @€ @€@Ģę¸ÖĨ%ˆlá"2?;=ô>_šreg§ûĄũr ĖÎÎŪQTGGG_ųæ7ŋYTuę遨_8˛i#ČKj 6wīÜĮCņ@@”˜ŗxX Ž–™^UņB€ @€ @€ed>/TöĶ‘-Û^(ëׯ_Ų ã0†ö ŒŲE”+Vl,ĸuô…@ėûGm”XV;2kžĢ %x‰€sĖ[<0Ģn<—RŠ dC—`ōt‘ @€ @€Î Č|îœ}[ZŽlá^ >ßvÛm…eąļ_#¸téŌ–ĸ:ņę̝ú÷˛(ĖŪ­'˛e#@Aæ^æY´Ŋ;âŪYĖe>1ˇ§dą÷ū¤! @€ @€ Č|nÜŽwöRļđļmÛ"ãĖžĪĨøæuG'oŋũöĢEõdrrRæ}Q˜ŊUOüģ”g9Į~Áy–sė#Ŧô–ĀÉę^Ũņz¤ˆŽ€´B€ @€ @€UÁį˙*lذa W†Ë'īŲŗįņ^q´^āîģī.,[~```ČíŸŗĩÂĞĀņwŽ”Œ ŲØ#Xém˜īXF=ūâáƒX’;@ˆåÖ @€ @€ôĩ€ed{{ú§ßúÖˇŽī•!nÚ´)­^Ŋúî^q´^āÅ_\ST+ƒƒƒEUĨžō D 1_Z;2›c?`û8—w>›íyS]šģ›ŋ×Ļ @€ @€… Č|.œ´{*Œ,áČîĨ˛qãÆ5ßüæ7{iHÆŌ:É;īŧķŽ"ĢéĨ—6Y_?ÕõĐCũŊ7ŪxãīžōĘ+īŧëŽģVOLLÜ”ģxņâ˙{ūüų###˙ņå—_ū7Ī>ûėotOėįÄČplÖXnY!°”@<č(ņũ‰‡ēæû“eYėKŲûņˇüĢÛ1†ŗ•Jåtu^ @€ @€ Pŗ€āsÍTåģđĩ×^ģ'˛…{ŠlŨēuđ˙đ# QÆa/MlkÆ2?¨ŌT+ņ ĮĀĀ@ĪėĄŪF7ŋûŨīū­×_ũoīÚĩkŨÁƒĶädÄģ,7ļ8wî܁ŗgĪXšrå˙~Ûmˇ}銧žzxÁĢ[w0ū}ɃÎ|‹}}íãÜ:ī^Ž9XˆŋøNŠžÆw*Žĩ­dY˙FûņŊ^îßÅxØ"ĘĄ,Ëâ{ũ=^ŠTüwnÆ  @€ @€K Xv{iŸRŸ\î˙d.ŨøFFFÆĮĮ'J×qnģ&‹\v>ä¸~ũú–襤 ūÖoũÖß{衇ūōÁüģO?ũôēcĮŽ-xž9ĘN9r$Å=?ņ?ņžûîģīĘĪ˙üĪ˙ÄÍ Z÷&‚nąŦvėÛûõÆŌÚą—¯ĀsëĖûĨæÜÆC ą,w”įĒßŗJˇŦDĐ9˲ø>Ÿop đøß°>ŸeŲ‰jģeũU1 @€ @€Ŋ! øÜķ¸Đ(Æ"KxĄe>ûîŽYŗæņ2AßÛ#pųōåEˇ4;;+ķšÔ~đƒŸûßųõĪ˙ų?@rŖ%2Ĩī÷~oŨüÁüæŖ>úGÖŗÄ}yFhėĪÁįX.9„|V-/ŋ6Äw,~ņĀC,ƒ]hŠŠ‹Üw:B/ētAĄP @€ @€Ĩ|.íÔ-ŨņČŽ,áĨ¯*ßŲČd]ģv픯įzÜ ĸ÷<ßącĮÕNŒŖDmŽMLL|ûmo{ÛçNJccÍ'v§3gΤŊ{÷îÛŗgĪå}ûö(Ā#hąožīCÕĀŗĨ… ĀUEM|Žlčø‹īcŠ#KšéKĒû:GļsҁâčÛsY–E Z!@€ @€ @€Ā‚ö|^Ĩü#;8˛„{ą|÷ģßŊą7l/ŽÍ˜ŠÚRôžįĪ?˙ŧīŪSôÎwžķKŸøÄ'F—Ø×y‰ģ—>Ôų‘ų‘ßK)Ŋ­Áå°#hYĻdŽ%ĩ›—fwļõ‘ ÷Qâģ™ĐņŊŒŊ–ëΝž#ŨtģÚ§…^b îTŠTÚēwõBqŒ @€ @€îųÜ}sRH";¸Čũn éTA•\ŋ~Ŋį–/ˆF5s.]ē44įc!o×­[÷F!õ`%ą,öĮ>öą–žsŽČ¤ūĩ_ûĩĩû÷ī=sk-‘~Ĩų=ąŦvd› <×*čēv DĀ9˛đãÁˆX>‚ČņĀDMäęRÛŧŽéú&čĸ3Ģ›ė’Û  @€ @€čÁįn˜…ôĄ—ŗƒ÷īß?Ō‚åD[0 Ēė¤ĀļmÛ^)ēũíÛˇĻ”š_KēčŽu¸žŸų™Ÿų'ĢV­Ú{4ˇēLOO§ûīŋė˙ãü[Ë´•g‘Æžē‘AûėFĻfdš*ēY ŒˆlčDG‰€r,ŋÜŋ=Ŧ^îšj•…ŧœŠŧ ŠL% @€ @€ô†€āsoĖã›FŅËŲÁąœøŽ]ģöŊiĐ˜#đ /ÄŌĖ­(í î´ĸ˙E×9üģŋûģ˙ë‰kO‰%¸?ûŲĪ>ūÄOÜ3¯ÅČøŒ,įÂEVfž§nŨËĪĢ×Gˆ&"¯ņ Ež }KĒû0ˇ;9˙ŊŨŌ @€ @€čoÁįۜ˙ÉjvpOŽnĶĻMiŊą$ŠB`QV,‘>LMMŨĩhŖ}xâŅGũÜĪūėĪŪ6<܎•~ŋmũĶúOW=ųä“OVÉã߃ˆ~G†čšj°.2Ge9÷áw˛G‡ßëx˜"–[ĄãA‹üa˜Lwĸʲ,īC'Ú×& @€ @€]& øÜeRDw"+8‚dŊZblWŽ\ŲŲĢã3ŽB†ˇnŨZøŪ⃃ƒiffæ‘BzØ•DĻåŽv,ˇ=Ÿ+–ß>pāĀ–ĩk×>SŨ7öɍ ŅØ7W!ĐĢņ@Eū]Œūc?üđSs‚Нw§ߝĢ6  @€ @€XF@đy 2žŽŦāČîå˛~ũú•Ŋ<>ckZ`rÕĒUM×ĸ‚%îģīž˙í'ō'W/yQ O:t(–ā"Ë9öÉUô“@ŸûÜį>wg‡ŨúÍŪ;<@Í @€ @€ Pģ€āsíVĨš2˛‚{9ķ9&bÆ ‹ĨųFļŋŖãããw´âŒŠŠŠ4::jŲíꔾūúëģ#šSell,Ŋūúëķ÷}îTw´K íY–ÅÜíŪëyū8‡ŗ,€ž¯â3 @€ @€>XÕ§ãîéa÷CVđ[ßúÖõÕĨv#ķK!p‹ĀČČČ#›7ožåXQ.^ŧ(ØYÅß°”ëɓ'Sd'įåėŲŗ)Æ.\HķƒÖq./ųš™™™ĮcįÉÉɯų5ųëūāŽnŪŧyįg>홝æĮŧč#%ƒžõüÃlūī0~ĢQG^â÷;÷7¯ū÷ą%īį€xK€ @€ @ _d>÷āĖ÷CVpd îŲŗįņœ>C*@`vvöŽĒY°ŠŅŅŅW<Ņg÷îŨûáŊ{÷.ēävąNŸū~,ęÜšs7‚ÎŧĘßįdq]‹ sܯ(ĮOąŸôÜcų=ųkÜsíÚĩË?{%Đgc‹ˇžß`ÔąĐī0~‹ņđGüÎ{¤ÚūĸũXŦŽ @€ @€ Л‚ĪŊ7¯ĶÕŦāŪ؜ŒĘĢW¯ž{Î!o ÜŠZQVŦXąąõ–­ÎĢW¯ŪÁ¨ÅJžŊœŸĀVžŖÄ}sŗ)#đœ×Ž¸6ÎĮõ@;räČÍķy}ųk´ķ­o}ëŋË?{%Đg‹ūëų †ŲBŋÃø]Æī/ꊕâaEĘĸũXäz‡  @€ @€čQÁᛨČnUĐ­›¨bIå×^{ÍōĮŨ4)]ԗK—.miUw^}õUÛŒAæ<Û9\ņŸã/‚^GŊy~~ĶŦβldūqŸ ¨O`ĄßaüžĸÄà Kžã’ī]X_“Ž&@€ @€ @ Ÿ{lR#8˛‚ûĄ ú?ģûaĸãíˇß~ĩÛjēerrreMē¨f|ŲČrž[ōlč@Ī͔ž{÷#°Øī0jĪ—ß.Ļ%ĩ @€ @€ ĐË‚Ī=6ģ‘ YÁũPļnŨ:˜R˛Īd?Lvcŧûîģ[ļįķĀĀĀPJiēÎ.õũåye@Dfsžwū9Ėņ%2,ã|\—ŸŋņÆ аĀRŋÁ¨4~oķ‡q<Īą$ˇB€ @€ @€ZŸkQ*Ņ5ũ” <2220>>>QĸéŅÕ6 ŧøâ‹kZÕÔā`<ķ ,'YĖ´Šũ›#¨Y•yfs˛"ĀĮĮĮĮo\įōWd<Įų¸?˙‹}g*qū-oyËķ sŒ@|ī ZĪo0nßŌüßa~|ęįē0˙€Ī @€ @€ô§@Ĩ?‡ŨŗŖ;|øđúšŸûšžᜁ}ņ‹_Lŋđ ŋđНũë˙ũœÃŪH?öc?ö˙øĮījE|īNž<ųĢ˙ūß˙ûԊúËRįž}û<đĀO;vŦŽ.Gp+Īpž{cžĸĖ͈ŽĪ‹]ŸßËq?õÔS?˙ŠO}ęįōc^ ô‹@–eņ\øÉŒE–úM-ö;Œãķ›ķĒ?[ŠTšwĖG @€ @€úP`UŽšg‡YĀ‘ Üŗœ7°ŠŠŠ´víÚ˜wØG“wŪygKĪ9íK/Ŋ´1߯¯Ī<ķĖÖ¯_˙zJŠŽ%Î <‡áb­ÅŽĪŨ?˙ųĪ_ûô§?ũĢųg¯úL îõ°—úM-ö;\ėøëēû1į^o  @€ @€č!ÁįšĖ5kÖ<>1Ņ_ĢP÷ģß]ßCSh(Å SÍÂĩlÚ´) ôÍC +|īčK/Ŋôį333[æî%ģÔõ­8÷­o}+–^téáV´ŠN] ¤OW*•É,Ëē ;ét7tB @€ @€čŧ€=Ÿ;?…õ`hhhKd÷Sš~ũē xûiÂkë&[ų;Øŧysē~ũú–ēŌķ—ŦZĩę˙‰%|;UbčĄĄĄ¯tĒ}íhŖ@ķÕöļŦ5-ˆ‡ŠŽUŗ›¤”.TƒÍĨ”NV?ßԁJĨÁįN-A0ķĶ?ũ͐RzŽÚī–n{pËĀ} @€ @€ @ +ŸģrZęÔd5 ¸Ą›ËzS_ũõ}eíŋ~/ËĪĮŌØ­,Ī?˙ŧŊÆĢŸųĖg~ôЧžúĢXģ]å… 麟ũėė“O>ųŽvĩŠ-ˆ`íÁ9ËiĮû&Gvķá:–ÔŽŒčN”“ßūöˇ˙”Ōîj`<˛´#x.Ũ‰ŲĐ& @€ @€.|î‚I(ĸ ‘ũYĀũVbĖWŽ\ių2ËũæZæņ^ēti¨Õũ_ˇnŨ­nŖLõđƒüĐG?úŅëíęķáÇĶģßũî˙Ĩ]íi‡@ÁųrÚ§ĒAįÔ.´œvÍÍV*•Č|ŽĖčv–s•Jeî˛Ņ~ĄãI”X&\ēŗĄ- @€ @€]" øÜ%Ņl7"ûˇÕK 7ÛĮVŨŋ~ũú•­Ē[ŊåØļmÛ+­îõöíÛGSJ@RRJ?ũĶ?ũ ūn;–ߎåļßō–ˇ|ágög˙|%Č—ĶŽ€sžœv,ĨŊčrÚõŽ­RŠDĻtģ– ˜Šfg/ÔÍČ܎qE@<°û÷r!)Į @€ @€ô €āsLjd˙öcæsLßßü›s°GĻŅ0 xá…ŪV@5ĩT!˜2G)–ßūžđ§'Oļ.ųōôéĶ铟üäŸ[n{ŧˇŨ*°ØrÚ”­g9ízĮËuˇ:}#đ\ŠTâuŠÁįčOŖ#:ūüģš”˜s @€ @€z@`UŒÁRJũœũ;::ē›ûÔŨ IDAT.Ĩ4]Ͳō}čsv,‰zLMMŨõÅ/~ąĪĩoūŗĪ>ģõöÛoōėŲŗī9qâD.fÛי™™Ī˙ų?˙įgžöĩ¯Ŋ÷ÖV}"Đ5X=›#Ë9J_c9í ÕĪ-‰€p–eđŊ—ķ~ŲnŒåąJĨRO€;â/|"ë;J<ĨROÕÛŧ @€ @€ Đí2Ÿģ}†jëßp?g˙NMMĨ={ö<^•Ģz\`xëÖ­-Ī„L333ô¸eCÃ{ꊧž˜˜øÉũŅŊ™Ę͖ŗgĪύkhhčW?ũéO˙PJišlËf›t?zZžœv=‰k#]ŠTbīåĸ—!ˆ`ņî:ĪsģëČúŽ€üĄj€<S @€ @€č!ÁįۘĖÉjöooŒĻÎQÄ^×ĢW¯žģÎÛ\Ū›“ĢV­čÍĄ•gTąķ>~G<ŒĐęŋˇŅŅŅ˙ŸŊ{Žúžī~˙[$à ´:2,D ɸ ŗšÁ˛1GtâØ§ÅĄØuž^žtzđIĻÍ8äIŽOŨ'ŊĀ“'͙äqs`ŌĻ$cŽkNڄLŦ16ļ[Û1ŽÂĄx(+Ԓą8ÄÂÂ^#äzĪ|đ˙ąģÚË˙ū˙gÖģûŋü.¯ßJÂûũŋEĮŽsģĒЖ˙đÃ˙ÔķK[ļlų7~~Ų˛e×oܸqŪāā ÉdŠĪ|ęÔ)ŖléįŸūÍŗgĪūģ1æŧøâ‹ģˆëUŅĘúõ"āæUjЧĩĩՙÅekМk#ĐŪŪ~—nFđb冇  Ÿ|ōÉocžųę̝šwŪygį3Ī<ķĄŸūô§ŨķįΟ?55•hjjšõÎ;īüĮŋßîííũ÷‹/8xđ ‚OÕ§KWĐNA BŨ!a¯ßŦ Š åį2‘HhüËķā[hõOAõé;XWtöǝĒg—ÕĄ_ļĐĄ+ü°r € € € €A ø¤Ņ¨ą-Ęúõ*āVc]ŋLëü>|X_¤ëKsļ˜ äršy^u=NŸ?yō¤WÕEĸžƒ*[TËÛĀĀĀohíėcĮŽŨ¯GĩŽđ„€į Âjęg=ôˇDAXkÔVˆXĮĻĄŸ´Æ€ tĀŠæ € € € € PN€ās9K$ BŌTךŲŪŪžėíí]=22BđŲ5åāœJĨR^MA?kÖŦÅÁ v '''ov i]ÄB;vÄĮÅBĢ›šŠ[Ah{Ēs¯˛ą#NL÷@@@@pO`–{ES˛G=ũũũÍÕØjV¯^mšššî li˜'ccc+<ŠČ311ÁÍ;ub+S]7 ÔY —#P€Ļ›Ūm45ÅŗnXzĀsoXÖqŽĻŗ8WYĪ[ŦqRZc7}Ēđt“. € € € € ‚Ī!Keû*ë7äŨ¨ģųĘvMĨRžën0¸"0wî܋Ž\¤ĐL&ĶPd7ģĒвU^ÂéT# @ĨÖnŪkœõZĶi+ Š ŗ^“M[¨?į*ŊŪ Bk, Bû3Ԋ € € € €ĀŒdîÍHė”íĢŦ_6c&''[pˆˇĀ’%K<[ķ9™L*cWëÃÅ[ŊöŪ§ĶéEŖŖŖąŋyĻvAŽ,! é´íõ›uŠ=e3Ë2” ŅnĄõĐø>fŖĩŽGupŨī—=ļ2`™NÛ}ī Ö ´ĻPW6´ÖđV Z™īl € € € € ā“™Ī>Á;U-ŲžīK*¸~ũú{^~ųeMąĘ?ĖüųķyŲíŗgĪ.ö˛ž¨ÕĩlŲ˛×Ž?žŸĄųt € € € € P‡ĀŦ:ŽåR2V–¯ŋ­XíZ{öėŲKÖ,šã@__ßyĒšĒŠUĢVĨ1 °ąÕ `¯YâĉÛj¸<”—|îsŸûæNūņ˙ņŠŋüËŋœ1đ\ØÉžžŗcĮķƒü }rrōŋũÖoũÖĢ dž€×JŨŪme”j}]mcļxļ4x š€˛ž×[7E}zŠ+Ī\(Áį™Ž9cëÖ­ˇéį5F[۝wŪyüwŪųOO=õTãLŲÎå\ÚÚÚĖŪŊ{ÍŨwßŊBė~ôŖ/wžĮ”šŊ× 8ëĩ2œlVЙéŒ=ĒpD@ŸU}nõųÕįY~ŋ;BK! € € € €q øŌŅVv¯×ëۆ…*‘H°oXËÁvú1ļ~9؍Ø•Íf—Ģŗ2L&“É(w\k;đƒüéüÁŦÜŊ{ˇQđ؉MĶq+ũöÛo˙ŸwÜqĮA'ĘŦ° ä4Uą˛DĩfŽŪkēbslĻP¯Đ‹ĶÂ% ĪīŊƘũĄÃ5p´@@@@ Ÿƒ1UˇBŲŊ­­­U_‡ úûû›ÉVŠÃH_ÕĮļ•+WzūĄŸÁņņņģŽj oǐa2™Œōlm.\ø‡ø‡hŠ'ÛšĒŲ÷w7{Æ úõ_˙õ˙Zę<ö3ļˆ*;ƒ_AhM!O&t¨†Æ"€ € € €ø%@đŲ/ų:ë%ģˇ4`{{{˛ˇˇwué38ALccc¤ŗgŖ6fŠT*e/066–ŠZ˙ėūÜwß}OũáūaŗSŲÎvšĶŸ•Q=66ļëąĮ[;ũXī™Nģ<.Œ€‚ĐšB^Ųũ B+ã02ŊŖ# € € € €8,@đŲaPŠëą˛{=Ē.\ÕhßĻĻĻûÂÕjZ[@ooīŧÎÎÎzЍéZOĶé4Ķn× ‡ĩŲ?ûŲĪū×wß}w­ĻĮöbûžpŨ×ŋūõ§ę¨Kķ3v€\iSVZSËë‡ZS΄ŽôĶ9@@@@ZkšˆküPVoOOYž%†AÁ††'ŗ˙JÔÄî ´ˇˇßÕÕÕåKsFGGŖŊ4Ė})Öö{îšįû=ôС§Ûž^ˇĻß>uęÔGfX÷^Y›ģ­ĖMŊVĀy‹tVĀķtXŪ#p­€‚Đʂ֔Üēyãekæ€kĪd € € € €1 øÂÁVV¯ŊVj›īI“§ĻĻZ=ЈJ! õƒ5ŨēÛŦYŗûQoØëėėėŒė:ĪVāwWĶmO˙,|éK_š{Įw'“IM]<ŌĮÁ¨°ĪĢW¯žšvjdf*hhhø@ąĩž÷īßoŽ9b˜V ųÔ)-{õĻũ:ĪŪĘ]ŖcÅŌēöƒü ųđ‡?Ŧėg6đO@AčõÖLĄũjF@@@đP€āŗ‡ØNTĨl^ŋÖļuĸũ^•!Ŗ7Ūxc­WõQŋgΜiōĢ~M÷íWǎgūüų‹*+hÅdZŠ5JgM‰­MÁé ŗ}ūôŒéR×(pŨĶŖåe‹oZozrrōŽâGŲ‹ ØAhM_°×ʄ.ũėqã¨@@@@'>;ŠéMY7YYŊŪÔâZn¸á†wCÜ|š^…@wwˇoc­ļnŨz[ÍåTcŽ ē ˜HŖ{zzî@ļ\Áb;Ûy||Üč1ĶVę•c˛‹•ĄāöĪ~ö3eåŗ!€@p|ÖēëvZčĢ~§Š´@@@@Ú>׿æÛUĘæ%ķš2ū–––ëYcą2̐Ÿ•éččXāg˛Ųėr?ëcŨĶ3Æ'''oc?ώyūüų—įüŸž_īwėØqywŠŠ˛+ŊĻ0ēØ5öžųķįΎ_ķŒPđy‹1Fsėk=h‚Ёƒ € € € PÁįzô|¸–lŪĘŅĶé´ĻžĖ:˛•÷UOĩ lÚ´IkėzTÛĩÕtuu™ŠŠŠ×aO)'N\5Mųô,čRׅa˙Ûoŋ}ąT;5UļÆv°XĶf+xÜÛÛ{å×yZZĮŠ]ŖëĀÖņrۅ .”;Î1ŒĀ)cĖÆĄuWʓÆ‚Ё‚ € € € P@c5'sŽī+›×÷†„ĄĘŦœ={ö’0´•6Ö.pîÜšĩĩ_íĖ•š\ŽĖį:(õŗētéŌîcĮŽÕQJ0.ũ—ų—o ũg;ģš°U k-fmģwkĻ]sųũČČČå×úŽ+|_ėûdeN—ĘžV`ēĨĨĨd Ü.ƒg”€„Ö:К§_}Öҁj(A@@@@ ”™ĪĨd¸_Yŧ~fx¤l“”Myá…î˛'q0~gÎŽYŗ† _Ÿ$MS>}Úíl6썊"‚|ęĐņãĮß,ÕĀžžŖG5[-×(ƒēĩĩ•ų¸Ģæ\‚#  ô.+ZĐʄ~oŅøā´‘– € € € € PT€āsQ–`îTīô€M0[œV%‰Ái -qC •J­đûį"›Íō9Ģbp“Édˇß7 TŅÜĒO}ũõ×ĪV}‘Ãhj‰¯9\,Å!€€ˇš[_Ač{1ēkåe‚ĐŪĩ!€ € € €T/@đšz3ߎPo”6nĀö÷÷7[_ØēQ3å_Ų"÷ėŲcRŠÔĻÜ.ËÄAĸ$`ĄuˇË^+ÍÍPQaú‚ € € €„L€āsHLŲģĘâeĢ^`öėŲŗĢŋŠ+B"Đ„´ûäääí!1 d3WŽ\ŲȆը¨‡~ø§ƒƒƒßÛĩËģdMˇũŊī}/÷ÄOüRÍæ2¯€šŋ×3dĄˆ&Ūņ¤å € € € €@h>‡dč”ŊĢ,^ļęššššĢŋŠ+B"ill Čš\n^HĖ|ofGGĮÛĶaãāôũa~˙šĪ}îžW^yelhHą ÷ˇxĀlذa‡û5QX@ŋp´&´‚Ņ @û„Îįķmų|~{>Ÿßm=žĖįķzØīuŒõĒüaĸi € € € €@-ĩ\Ä5Ū Ŋ[ģųĒUĢŌ‡ÎcüY€ĩöĻså ŊŊŊķ:;;g8ËũÃē1$N/:vė˜û•…ŋ†ļîîî%áīFe=ظqã->øāągŸ}ļš­ÍŊ‹ĻÛū˙ø|ņ‹_üVe-ã,ˆ¸€‚Đz(ûy§ÕWMŅíęŋ…Ŧ`˛n‚Ņŋģļ—0žęFŖ|>¯@šÚĩ/‘HŒ—¸†Ũ € € € €! ķ9$5oŪ<÷ĸ!1¨ĩ™Zwũúõ÷Ôz=×W ŊŊũŽ LG?::Ú\Š@ĩL‰k6ũœęf‚k„|‡Ļßîėėüüo˙öoŋŠiąŨØ4ĩ÷Ŗ>zôĐĄCwēQ>e"€@¨ô‹įcĖcŒ‚ÂOcŽ ū:Õģ|>¯ōGŦu§Kž‹U§swëŽbį°@@@@ $ŸÃ1P™žž>‚Ī5Ž••{S—sY€‚4Õu:>`ĒĀ7MëfwuuŨø†ÖĐƒîihhøŊßûŊqe(;ĩ9rÄlŪŧyęĉ7<<ŧÆŠr)")`ĄˆV°÷1§‚Đų|>“ĪįtÖßõü{U×îUY*3’Ŗ@§@@@@|Á +kWYlĩ Čî7ŪX[ÛÕ\dT*• ĘĪFkkëõAļ JÛ•ZŋūėŲŗ‹ƒŌN§ÛqāáƒūO'Nœxz˖-ĻŪ,he;ęSŸĘũę¯ūę¯8p`›ĶíĨ<ˆŦ@aZĐ B×ŧVŧÖtļ˛Š5ŊˇS›ĘŌÚĐÕdO;U7å € € € €Ô)@đšN@.ŋ)ëÚzÔWWĒšá†ŪuĨ` õU`lll…2fƒ°9sĻ)íz&''ozŨlß×ŋūõ;Žŋūú_ũĩ_ûĩ1eAWˇŧОåW~åMe;ké|đģnļ—˛@ ˛úåŗËš’[ÁŪ—Ģ B[Sd+x]Oļs)`•ųĶp—âa? € € € \Æā6–ŲĘÚ Jv§Ũϰ=ˇ´´(+U_dVé [GcÖŪšsį^ J—3™LÃđđpPšØv”š*]ŲĐĘdlÃlØŖ>Ē€ņw›››˙ø‰'žØÖĐĐđ–ÁÁAŖGáĻ éũû÷›_|ņŌČČČŲyķæŊŧpáÂ?R&uáyŧFj°ƒĐú7’Ŋ&ô~cĖžrååķyũ˛Ō4Ûnoš†ûT"‘rģ"ĘG@@@pF€āŗ3ŽŽ–rã72Nu §ĶécŒÖäËË:-ƒtų’%KæĨ=ÉdRS}Īgl†A)5“Ã… ēg¸4R‡Ÿ{îš˙ŨŖ‡éėėü•‡~øųÄ'>qûŦYŗæ\ũõs_ũõ7ÛÚچ;;;˙ß'žxâKÜ<Šá§3M@Ah-J¯‡„>bŊŋęÆ=k=fe<{ĩ)zK"‘P{Ø@@@@.@P3ā¤lŨĻĻ&NŲęPVå~ôŖû^zé%ƒu8íŌ Mu”éŋƒ6FĶÛŗtéŌŽéģ/ŋĪårÉĸb°ĶΆļē:¸`Á‚/ŧúęĢ7ëũ?ūã?Æ@€."€@€”õŦ‡„Öŋ”ļƒĐģ]šj쁞˛Uį–R'°@@@@ 8Ŧųœą(ՒŒ•ĩ[ę8û+Đ´åķįĪ_VÁМ"îîîĀŦå­ĪØÖ­[o Ÿ/MÍfŗ‹JUœN§Ī—:ˇũcccą˜‚@@@@*>W æõé6l¸OYģlõ (+õÜšs%ƒ^õ•ÎÕ> d:::øTwŅjŗŲėōĸØY‘@gg'WcĖĀĀŋĢ*úÄpx$  ôŊZVâ­ˇŪúŧGuĢÆ‹5Ļ‹ÕË>@@@@*˜vģ ,?NUļŽ2*ŲęH$ TÖßŖØ— i8ŗiãd2ÛiŖ+ˆe˖ŊfŒ)ēļķņãĮįWZN”Īŋkbbba”ûHß@ |ų|^w—øØōe?' –Pņq¨@l¨2¯ IDAT@@˜I€Ėᙄ|>Žl]֒ufúûû›1=ΔF)~ lÚ´)¤Y´ŽņÔÔÔ ŋ]‚^˙ŦYŗü \įJûÆÆÆæ^yà @ ™4c0m   € € € €e>—Á Â!˛u…žžžäĀĀĀ+‘’ü8wîÜZ?ë/Vw.—#ķšLÁž3gÎ4ŧŊęe&“i¸jGLߤRŠÔÚĩkĶîĶmŽĀöRM;räˆēü˜~Žöëøūũû¯˛ĪÕŗŊéõŠS§Š–aŸcŒ)Ų†‚sx‰ € € € āŖÁgņ+¨ēĮĘÖ­āTN™I@™ŠšÎvĻķ8 Í °f͚‹áŅķ§Ĩ---ī”Ē9™LjÍį d֕jĸ'û/\¸ĐmũîÔÔōžtžJ@ ų|^3Į=Fåņņq388xųYīíÍJg2ŗoßž+Ahí×ųvĀYīxîééš|Ž^—Ø4õvŅv”8ŸŨ € € € € |öŧšęÖ­[w§˛uĢš†sK hŠæ†††Āe˖n1GĘ ¤RŠA[=›Í˛ŽxšA3ÆŦ\š˛u†SbpU}ccŖ~÷Į>?Ãg…à ā@Ų€¯Ë @ëŅÖöū¯q“wėØqĨ•:Ļ@ŗ‚ŅÚô^Ņzč\mv úō›â˙)ۖ◰@@@@Ā+‚Ī^I×PĪĨK—nUļ.›sŗgĪží\i”ä§ĀØØ˜˛dĩ•Ëę TCũkĖ T-ڂ e˛m¤;ĶéôyĒĄ @ ’7Ã(XŦĀņúõë/Ÿí ˛ WpY§•éŦczؙÍvĀēš†”ĘĀŽ˛ NG@@@pI€āŗK°NĢ,]eë˛9'0oŪŧ÷Ķqœ+–’|čëë \€nÕĒUiž¯ũàLömÛļ-¯Ŋ„h\ŲÚÚzŊ~÷ ,ŠFčD@ äŋŸ4uļËO>ųäåi´ ×vVŋ|VöŗÎ:fgBŽ÷\Ĩ™ĪU‚q: € € € āĨÁg/ĩĢŦ‹,Ũ*Á*8Ŋ¯¯O_ž–ĖŪŠ N ˆĀéͧ—¤)Ķ›Á—âĶEŦ÷ŊŊŊķfš*ũĉˇ•¸<6ģΜ9Ķ¤Î˛F}l†œŽ"j;øŦŒf 5­ļŊíŲŗĮčĄMAhŗ×‡VĀZÛöíÛ/ģüæŊß}WĻāļ÷ņŒ € € € ‚Ī+˛tžÖ¯_ķ%S"ÆčķEļjéOBWW×mL­]ÚĮ>ŌŅŅņļũšg@ īG”§5HÁcMĢ­MĪĘlV€šˇˇ÷ōúÍ JëŊĪv´ÖvZĶvkŋ ­ũ*ŗĖ6Tæ‡@@@@|hôš~Ē/-0heé–>ƒ#U tvvęš›Ēž ‚&ĐÖßßß´F)°:99yģ1æ›Ak[ÚsöėŲÅåÚĄā}2™L–;'ĮÚēģģ—Č"•Jn]ķøĶE(.0^|ˇš`Vđ؞R[įŽŒŒ\žÄ<++ÚŪ”­m÷îŨ—Ÿõ^¯U†ŊĪ>—g@@@@p |čx­_ŋ~“‚lÎ Čô7ŪXëlŠ”æƒ@Ļąą1AĘ\.7ĪHTŠā}2™ėŽDgjīÄåedqá…¸[Ԯȕ P¯€æÄÖī#-%Ņ–H$L>Ÿ/YĻ‚Įö4ÚĶO*vLŲĐÅļRež›H$Č|.á5 € € € 0‚ΐ‚æÜdeéėâĨ‹/n:yō¤EQ†OZ;8ˆ?&N/:vė˜O2ÁŽV™ŧ2*ˇÅ>Ûמš<—Ëō‹rãĮ1Ĩ€‚Ė 6_4[=8eŒQWķiÛYĪi‰f?{xôčŅī+^Đ&?›CŨ € € € €E>A Â.eį’ųėÎH,\¸°‰/.ŨąõĒÔöööģēēēŧĒŽĒzFGGÉV-!666ļĸÄĄ+ģįΝ{ņʛžØ´iSÆūŨŸN§ĪsŖL ?twhļ3ší`ŗÖsÖŖ0Đ\Ŧ:Į×āķ÷ŋ˙ũƘŊVãßO ēØPą@@@đO`–USs9eį–;ÎąÚŌét‹õÅkí…pĨ¯AžÚZC_qB^ųōåËCۃúšîÜš+Ë´ļļ^__i\1Py‡–V6Æ(kY‹.+xŦLfšī5Æl1ÆėĒ0ˆĢ@¯¯ÛŽ]ģū‡ÕîŦ†(­~í´2ˇ}m•#€ € € €Cæs0?mVvn0[ōViÚßũčG÷ŊôŌKŦŌąÔôÍvvhĐē@°ôˆT˜Īfŗ J—¯#gΜá&¤x 9ŊE ™2š5v]["‘8•Īį´V@ۏmŸÚ`UlĐÕm ĒÛhũûNejŗ!€ € € €x,@đŲcđ ĢËXŲšžÎiÕ(h™H$VUs įK@Ķ7ÛëâĢeÆ0,="ą_Īš´Î{G ×Åîččx{xxxĻK8ŽņĐtŲv°YĪ Ä*(̇‚ąv€Ö ™=V Wë.{šŠĘĐ.ĩ)+ÛÎĖV Ze|+­ũĸKÉą@@@pX€āŗÃ NˇaÆû”Ë掀‚–/^lu§tJõB Čëg2™†Å?ĮŸ_üČû{ãp-\ģģģ{ ëĶŋ˙Ųā1(hVPĩ\@Öq˛‚ėge{š)ëYčJļÂ@´‚텁h;]I9œƒ € € € PƒÁįĐÜžDYšARØíž{U~"‘`j_¯°]¨gɒ%ķ\(֑"“ɤ˛{­)?)3N…XW[ÜĖÜ Š',ĻŖ­Ô.¨@sŠn$‰]ų|^mUP׋mŋęŦą"›íŦgũ>Õßg­­ŋ1úŨjgK×X<—!€ € € €L ø<]$ī••Ô)…ĀãHúûû›‡‡‡ p9ĸé}!AžÚšŸŨԟe…—>zՑØūl.[ļė5cLˇ4ø,]õ™ā QĐ´Õv0Tŋķ´ŲĶg{žŅlÕ_ÍĶÆĩ[}psSāXu9ąŲhMŽļ+xŽ@´Üí@tĨŲÕN´‡2@@@@H ˊd¯BŪ)˛rŨĀžžžäēuëîtŋ&jpC ģģû]7ĘuĸLÍZ°uëÖۜ(+bedŦŦđˆuËŲîŧõÖ[7Ø%ęŗ´iĶ&ˇ;vu<#€€{ 4+ãVk?f=ôZ?ß zŪk=dU`ÔÎÔu¯Eu–lMŊÅåļĘaKĶmWĶ+e>ËZ}°ƒÛ{­ąŲa-yPMyœ‹ € € € ` ųŧB˛rƒ×Ŧhĩ¨ĢĢË\ētéVcĖ#ŅęY,z“éččô´éŲlvy,FĸēN*ø2ãĻõî=ēčŲgŸņÜ(ž011qÕßåsįݍb?é(—ŅŦ@ŗ‘ØÎįķ Ū*Ž€­“Û>­gíRāyz;•íŦúôĐø)#ZhŊļ3ĸY bēī@@@@W}É]âv{( l\eåzXe,ĢR€ĢĄĄ N8Gŋĸ Ļ_]ëėė4Éd’Ÿá"Pé4Ō“““ˇcžY¤ˆČī rVäņé ĩ (ŖY˚ÆŲūû¤ŒŨHšKŅXÁáōųŧúŦ ´mP꒙ö_ÎHN$ ûąĸUŋŅ;­ņĩĮ4đ™é~ĀQ' € € € ` |ļ%ōŦl\eå˛š/0{öėŲî×B N hbŨ<ÔM?ŋSSS+‚Ú>ŋÚĨŠČ5t%[.—›WÉy<§§0Ģ_ŸķT*•Š`?éa(hV°TAIKcģ^°‚Åų|^kU+P[Ë´ÕvĐwGŲΕ~Õ'=´)­‡‚ė @k?h ‡'@@@@Ā ølKäYŲ¸AŦ„É‘fĖ›7¯ŪėGÚA!Õ „aâ\.Gæķ´a­t*reŽĮxSæäUÛØØ72\%Â< Đ\%ĩ4ŪĨé˛ķųŧ‚´Ę×ķ5ŋÛŦĸ¸ŊœMœH$ôô­0m÷ÍDÛYŅAīíC@@@\ øė:qu´ˇˇįĢ삺kčëëk;t萞\Þĩv3’×U:}ŗ__ŗfÍœ'OúU}¨ëUæøŌĨKģ;ę~ÔÚø ļkí×!píŠŗõŦÍŒÆ:ŖŲ˛¨ú)‘H؁ZŖM>ŸWÚBŸJ$a_CYŸ;ëYŸũ{RYßv&ŧ-]ĩ € € € €a ø°Ėįķ‹Ö¤Č6GS¯_ŋ~ĶË/ŋLđ9DŖœJĨVT:}ŗ_ŨĘfŗ üĒ;¨õjėJĮ-›ÍÆō÷`ąŠÉ—-[ö72õSMģB*PhV0Tŗ ØDšÃ ä°XÁæ¨ÚڟŸ=V€]ŲŪOZŸ%S :ļS˛ōIŖ@@@@ĀU‚ĪŽōV]ø ˛qĢžŠ j°Ļ÷ŊŠĻ‹šČ7ąąąĀ¯ÛŌŌōŽo@­8™Lv“Õ[~pŠMMūÖ[oŨPū*Ž"€@Íep8䊀ė BëĄĶ+Ŋ×ĒI7;ˆv…B@@@@ HŸ4Ę­430@ÍmSdũÆoŦ mbÚđžžžķAīúĒUĢŌ‡VF]TŗŧĒ‚jnĐĮ5Ûwz€~bb‚ŋĶUÚ¸ Ļúk›õŦÍÎH%ŖŲáÉSe;ëŗ§Įô@´Íŋ<*C@@@/øRÛ åĘë¸iÕĒU•ŸÍ™u ,^ŧ¸)ŽAŽēņ|*āôéĶK}ĒēÚj >ˆÍ;÷bÁÛ˛/ĮĮĮ—=!ĸĶéôĸé7 uwwŋ;<<ŅĶ-j˜hVOA<=”YzyášKįBœ( DĢteDkh}–íŒhŅÎģS" € € €ø @đŲôRU* ˇĢĢĢÔaöģ °páÂ&+…ĩø\đk‘  ,zöŲgãJpMŋ—/_~ÍžR;âší;::Ú=Ũ¤ŖŖCë‡s#ÃtŪĮI€@sœF;>}ÕMzh+ DÛŅzfC@@@B)@đ9@ÃĻ,Ü5'MI§Ķ-Ö4Ę:a ž@[sĐ›ŠŠ“'''o7Æ|3čmõĒ}ŲlVAԊ6ÖĖž†‰āķ5$눍€Ļ&Ö”ŲƒÖMz¯ ÍpēuY 0­Īŋ‚Ņģ­Īžū}ĘŋQų  € € € €@¨>g¸ÚŦ,Üā´(-0?úŅî{éĨ—øb/ãillL†ĄŠš\n^ÚÄ6Ž\š˛õđáÃAlšĢm*ļžųô5 ]m…#ā­Āô@ŗjˇ§Ī&āæíXP[ptŗ…õlĸ5=ˇnĀĐĪ…-œĶ@@@@`šÁįi >žÍÜxãĘÂeķP@Ķ#' ÚöĐŧžĒz{{įuvvÖS„'×ęĻ­ß{ėØ1Oę C%ĢW¯žĒ´Ö Ę|ŒÕM!ÅÖ3×[ˇŪvāXYTúYáŧĐhÍPŅĐ ĸ5†2ĸŸ´Ņ:Ļ@4ËÆhĀh  € € €ŧ'@đ9 Ÿ„ 6ܧ ›ˇĘ*ŧxņb̎ĩR[­íííw…e]ôbë÷ÖÚī\×3ūüEč‡/]Čfŗ•/˜íK Ёkt툞60ŗ§ˇ37š‘â.v 0Ŗ€~~öXũLégl¯u•M zFFN@@@@/>{Ą\AĘž%ø\” §$‰Š×ĸuĄzŠŦB LSY§Ķéķ'OžŦĸw‘>U_”WŧéwĄ˛ÜGFF*ž& 'ËgÚí(ŒläûPhV†ŗ6;ĐŧĖLK„'œĐĪ—~ļôĐΜ2ĸˆÖk;­sØ@@@@_>ûÂ~mĨĘž%Čp­‹{úûû›‡‡‡íĖ,/ǤŽRŠT*,7i´ļļ^_c7c™~vuuŨ622r F™bŲáúŧ3…{Œ>Áī*æā-Œ—€˛í@´zŽ@´ÖˆÖŋk DĮëŗ@o@@@ŒÁဠŲˇū DOOOrŨēuwžōĘ+ø× jŽD`lllEXnŌ8sæLS%}ŠÃ9‹´v5ÛŲŗgWs~Îĩ3F¯é S¸_CÂo4mļũ°?Ÿd4{cO-Ô* u õĐVˆļ׈Ö3 € € € āĒÁgWy+.ŧGŲˇŸÍ‰Ž h áK—.ŨjŒ!øė¨Ŧķ…͝;÷ĸķĨēSb&“iv§đ•:99y{5MË ÕôŠ’s;;;+9spCĀ2+[R›¤ŌCëĖ2…¯ꔉ€ģ…hũŒ+ŊÛúšVV´l € € € €€ãŸ'­ž@eŨ*ûļú+šÂ ed644Ŧuĸ,ĘpW`ɒ%ķÜ­ÁšŌ“ÉdĘŖ)jc˙ånĩkuĮqĒé͛7X7ÂÛúúúâē~xĪöíÛwŽŽŽŪööÛoĪK&“Wf¸pá…|āį,Xđ÷>úč—XW¸Ø'§äžrfMßK š$­€}3‰:`ĸ5=ˇ=›-ÚŌp@@@ŽÁįŒ…˛nKĐŧX4ĄŊŊ=‹Ž†ŧ“ašĘ:ŽŲģĨ>bÕfõÆmĒér͌Ÿ>}zi)×îoģãŽ;ūöüųķ7˙â/ūbzppĐčŅÖfĪú|ĨĮí§N2CCCk'''wŽŒŒœM$Īũä'?š˙Ęŧ€˛˜ ƒÍ…ÍšųŒ OÂ@´~G(#úɂ@´nšĶZŌl € € € €@MŸkbsö"eŨVģĒŗ- ´|>ŋ…ā twwŋüVž×BeīnŨēõļÄ>ķyéŌĨŨÜ`3ķ'7î7,üūī˙ūgžzęŠ?üøĮ?Ūŧ}ģb!åˇžžŗcĮ=fc–ėŲŗį×įĖ™ŗåŖũčī<øāƒß-u$špVđHYz(ĢQ'6@ P@ŋ4ĩžúĸ[öZ'čß/úŨA ēPŒ× € € € 0ŖĀŦĪā×ČēuxÆ úúú” Ļ/ÜØ‚+éččXÜæ]Û˛l6ģüÚŊņۓÍfĢžš#NŸ“”ôēaĄØļzõęŠbûŖ˛īá‡îū­ßú­W_{íĩ˙öėŗĪVx.Ö÷;wšoûÛíĪ?˙ü˙ķą}ėPąs"´ĪÎVÔú­ĘX|ĖŗÃZĢYÁĸ-Ƙ{1ģŒ1Ęp&đĄÁ§+¸$ @´~_čwĮV Dë÷‹ĻčÖī6@@@@`F‚Ī3šYˇîĪTƒ‚>ëׯß4Ķy÷Uāšyw}mÍ •kšéd2ÉZî38•:ÜŲŲŠ5ŗcŗ• ĐΟ?_Á{e˛FnÛ˛eËoėßŋ˙ØŨwßŊâąĮ+6ŊvU}V6´Ęų…_ø…M7ß|ķk[ˇn][UÁ<Ųž9Š@s0Į‡V!Ee;ہhŖ˜VZ7ģˆŽâˆĶ'@@@`Úm1k,jĐĘē­ņr.sB`ÕĒU*æ&'Ęĸ w6mڔ Ķôôšfzjjj…;á*ĩ¯¯¯ę,æãĮĪW/]om¨nž¨DãÎ;īܙL&˙ëŗĪ>;ģȚΕQōeAĻ?ųÉO>ŗuëÖÛ80\ōä`Đ8ëFÍÄagÚĶgk \e2ŗ!€^ hF=´i] õûŠiũ-ž@@@@ā}‚Īī[øōJŲļĨĻZõĨA1­TÂ7Ūx# r‘Ásį΅n|rš™ĪZ,r||qd?˜ul͚5KĨ,ú¨mšjûņĮ˙ŖgžyÆņĀŗm•ÉdĖ7žņ–ûīŋ_AÛëíũzž)Đûõâ4V4Ū( DëfŖ53ƒĻ÷×1ĻųߊW € € €ÄR€āŗĪÞL&3VÖ­Ī-Ąúŋ7SÆLLLTũˇ&“É4 ‡%YõÚą¯vO6›-šžšnŽŲŧyķ‡ŸzęŠČ#ŸūųūüĪ˙|ŽĶĪĶŨ5 ÷G>ō‘öŽŽŽzâ‰'~iúqß+›šTFsdÆÖcSĒChļƒÍ…heDë÷š-ío+Š@@@<¨: āiëbPŲØØØ ØüX¸pa“5… Ļ8e ˜@*•ZļYĘÆëjsZZZŪŠļ‚d2Š5ŸõEļũĨvĩEDęüŗgĪF&{üSŸúÔŪŲŗg§‡uĶÜ[ļlšåķŸ˙üĮzčĄ¯ē_ãåÔ9}~5uļ=eēŒ!ĐėŅ P x*PˆÖī>eDkčÂß}üÛĶ!Ą2@@@ü øėû•Z•m{å /|¸ņÆ[Ŧ`_Gĸxåccc F†jĢ%čĒVØØ•+W֚˛ní*Ŧ)ŧ§)ĶģTëÖņ_ĒÚ¯éļŸy晏=ūøãåNsüØŪŊ{Íũ÷ß˙ycŒÁįræ}šyŪņQ  lœ÷Xĸõ{r¯Õd;#šßÁCZ‡ € € €@ÍŸkĻsäÂ6+Û֑Â(¤>eÕnذភ^z‰ās}”Ž\Ũ××wŪ•‚],tÕĒUéÇëKW} ×m°ąąąęĩ¯Ŗ¸Îq™@ÆĘô.zŠ~7-]ē´ûØącE‡iįÁƒ>ōČ#ך=Ũöt{úíÉÉÉo˙ä'?šúņ*Ū+›YA”bÍšĢ€äTˆ€ū ¤ßzčĻ2eDہheKkjî8˙;)6:Š € € ‚Ī>ŽuooīíVļ­­ j[@žD"ąĘ~Īs°NŸ>Ŋ4X-Ǐ5q>W Uxĸ–#ØēuëmˆÃÍ 3fxgŗŲE…>!}­Ž–d2Šßzŋiúíī|į;wTQŗjOm›íie 4WÉŠ €€% lįÂ@´næŲiŨĐcgDˆæã‚ € € €@Č>û8€íííw…m [š\¯ZSÛ^ŧxąÖé]o„O@?ßŊŊŊķFFFÂ×x‡ZŦū×ú{.›Í.w¨.ĻŖ@wlZãîŧķÎwß}÷ŧiģ=}{ķÍ7§~üãģ!dĻ@3ÁOGŠĘ@  D+ëYmʈļŅvF´žŲ@@@@ dŸ}0eŲÖ”ņąŲ‘Ž:‘H,ˆtÃÛšļūūūæ°5_74č&“‘‘‘akģSííęęē-Jk;åRXŽ>#3­Yŗæâɓ' / ŨëK—.ũúā ’ÜŽŪN:eöíS"Ü{›ĻČŪąc‡ũ֔:~äČ3>ūŪ’ĄvšCCCF×ë{ߕ‚Œ1úĐ‡æŒŽŽūņĐĐĐĶVĻ]ąŒfͅhŧFŧ( Dë† ŖwcD{ãO- € € €8&@đŲ1Ęę R–íL‡ęKåŠzā.–WOą\[ŋ@Ļ–uƒë¯ļūršœ¯™žõ÷ žΞ=쏖tcN2™Ŧz­čZęōûšJ>#Ųl6ô7Æ\¸paŠÃĶ7Šĩ´ĻãļƒÉ…į;nž`ŪŋŋŅ{mvĐYûT×ôútūC=´ŅŖāŗ"Ūš ąyC@ŋÔíŦįÂ@´~gÛĶsŖĨ´@@@Ž˜uÍvx&@–­gÔWÔĶĶ“\ˇnŨ_‰žhZâÎÎNOęr˛’“N§Ŗ°Vo],ĩÜdŖkĻĻĻVÔUqˆ.ãįģJŪž+V\Wėĩŗà >oߎdˇ÷ˇRĮ•-­ķõPđÚ8ëJ]S˜Mm—ĻķæÎ;‡Āŗ-Â3 xĄwcļcöXŗV@ĐŒ[Ë)؁hĻ€ÖôÜĸC3Œ4@@@ *Ÿ}Ie×*˖-xSSS‘ đOģ˛­¨eŨāĘJw÷Ŧ3gÎ4š[C°K¯'ķ;›Í.vīœiŨM7Ũ4ãôâēųBkŸ;SŖ?ĨŧķÎ;%3Ų\.Ü<îííŊ˛kúqMĪm¯éŦį;v\~((­MÁčbSx_) €QP ZĶa(ZĶsĸwG}‘¨*ũB@@—ëú4^d×ú_Aĩ}}}m‡4ÆŧŨāNqW ŠĖPwRCéZĢwxx¸†+Ŗq ™ß3ãņãĮįĪt–nžĐÚį###f:7 Į‡Ž=ú†1Ļh ]Shf*+‹yddäJWώ×|V°Yg{ÛŊ{÷åi¸õ\jK$˙_ŠcėGˆœ€ŅöÚ Zžbģ1F$ŽXûõˆ € € € ųė f5E‘][–ˇį*Ãpũúõ›ŧ­•ÚĘ ,Y˛$´ŸÖZŊē™!–[=™ßĢW¯žŠ%Z‰N‡yísu)—ËŊĨŒæR[ađšØ9Ķ+@­ ôô­Ø>ûĢįΝûSû=Ī €ąP y—•­€´Ņc´V´^ŗ!€ € € €€Ÿ@ŦĄˆAe×Öp—x °jÕ*“L&•Áz˜~w!ŦĶ…ûíĻúįΟŋČĶ„ļ¸Ų†ūūūæ™Ęīėėœé”Āonnū'{ZlŋĢúĶéôWũǟz@#`ĸĩNôëßZ#š@t`†ˆ† € € €„U€āŗ#§ŦZeײS ĢĢËhá`ļ.ž­ęîî~7Ŧ=×ĪúĻM›b{3ƒĻ¯sėĸ|lll,:uĄ›~/ĨRŠP˙^úáøh-f?ˇgŸ}vâŅGũޟm n@Ā h]h ĩF´ˆVF´Zہ›†7d4@@@ ČŸ}eÕ*ģ–-¸‹/n nëbײLGGĮ‚0÷úÜšskÃÜū:ڞąĻ¯Ŗ.ĩÆÆÆRöë>Ÿúá8Vnęm7ûĨĀw.—cĘm7‘)ŋ€ˆVFôVw” mĸŖ~S\øG € € €ø.@đŲ‡!PV­˛ØØ‚+°páBŸÉrÆ…z4]r2™œ1ŗ5ԎˇĸŽą0Ûļm[îxĢTāĀĀĀĸJg˜;wîÅ5ŊĻĻ\ũõŸ~āûģüšŠ¨ųĸĪ|æ3SŨŨŨŋ]s\ˆ 7qcĖ>cŒˆÖûÆMĪ­įXĸķų|&ŸĪųÍYŗf īßŋßĶĄŲĩk—immũû {Z1•!€DE@gũņŌTšž[Ōv zˇ1&2Y+Čŧ;ŸĪäķųŧ1æe+āŽ ģ/k>Ÿ=ŸĪīÍįķÛŖ2Čô@@@Ā‚ĪÎ8VSJ›•U[Í5œëą€2ÛÛÛīō¸ZĒ+"…)Ģsš\,3ŸˇnŨzŗ<ųPėĘårķ Ū–}™ÍfC=ũŧŨšmÛļ}ėO˙ôOßņjúmMˇ}ôčŅņlŗÛĀ3 €u ĸõZXfCˆÎįķmų|^į×ĢČėÖ 7Zû1+­@t,˛Áëüėp9 € € y‚Īą˛i•UëqĩTWĨ€‚ΉD‚…šĢtsëôJ3CŨĒŋŪrûúúÎ×[F¯ĪfŗuĨę֛53MÉ^é´ÛačO%mŧ÷Ū{‡>ëÕôۚnûcûØæJÚÆ9 €Ô pÄŗËʈ. Dk­čĀg+člŒą2šëY2EheK+ˆ]O95 — € € €I€āŗĮŖŅÔÔt_Ü ;R‚/^lu¤0 ŠK •J­ûĪĖéͧ—Ö…Ķ‹•5ŊtéŌî(w?™LvWzsEsT,žō•¯|áÜšs?ØˇOKiēˇiēíd2ųŪîÕBÉ €\( DīąÖ…VFtāŅÖZΚR[͇;,Vy B^Qá € € €@Ŧ>{<ÜÍÍÍ7„=æ1™oÕ%‰HLqë CĨ*ʡbZZZŪņ­r+NĨRŠz×ëÎfŗ‹|ė‚ëUWķųnllÔôí‘ų"÷ĐĄC}ôŅGpīŊ÷§§ā>uę”ųČG>réšįžûÚÁƒõå? €^ h]hũ ŌŅ…čĮŦéĒ øVÕ7+0Ŧ ¸[kUĢoOæķyeCŗ!€ € €ÄL€āŗĮŽlÚJŗÜ(ĐÚÚz}šã…Į4kÆĀĀ@ä2Á5÷Į?ūņ _ûÚ׆5Mv­YĐĘvŪŧyķԋ/žøÂķĪ?ßũÅ/~ņ[…~ŧF@ ãÆ­;q¯1ÆDkĒj„õėÚŋ5Õļ5¸—;Ȁö’›ē@@@˙>{;+›ÖÛZŠ­&­7{éŌĨ[kē˜‹(Pаˇˇw^ÁŽXŧœ;wîÅz;:>>ž¸Ū2‚|ũ™3gš*mŸf͘œœŧŊŌķÃtžÖdūÎwžsķŋũÛŋ}bÆ ã BW’ ­@õūũûͧ?ũé‹÷ÜsĪŋ§Ķé_{â‰'~)‘Hč‹}6@‚.`ĸ„ÖôÜʐļŅģœ…)ŸĪ+¨­ˇ›2 ŨšâۏūP' € € €@Æ2Į8䰀˛h•MË­UÛĐа6­l+Ûúûû›ÃŪ; ÛÛÛī9öžTĶūå˗WszŅs'&&"ũwĒÚõĀsš\¤obøÛŋũÛ¯cžúĖ3ĪlzéĨ—vūɟüÉMMMMķV¯^}ÍÔ GŸ˜˜onnū§ūđ‡_2Æ9zôhŅĪ;@‰Ā~cŒÚ1ۍ1 BąöëšÖMS|_ķ÷´ÖÂj¸Nk@÷rƒX r\‚ € €„L Ō_ęm,”EĢlZļđ477‡>đíĸ-Í466Ö=usŅ’=Ūõ a1Îl6ģ Øūjöuttŧ=<<\Í%a:ˇmåʕ­•6XôÉd2?3õų…^xÚŖĮåíāÁƒú^7qüÅĪūķOXģY$Ú*Œ'@& ŋsöß:e ہheGkŋ¤žąŲÖ´×~g+đ­Ŧî]36˜@@@B-@đŲÃáS­˛iŲÂ#0gΜHg}$4Uuf ĐĪ}:^tėØą “Ž}ŨŨŨKŦ,(NŖ\ÕÍĘ O&“Ũ$otųË÷9sæĖ/ø"Ū›šŠ@˙”ņlg=kúlĸ5…vĨh}ƒ°íĖįķû‰„Ú͆ € € QÖ|öp`ÉĸõÛĄĒúúút‡ūåŒ;‡Š¤˜*4UuTf ]ĐpõęÕSU wšSũÎÔ)×6OĨ<­0X• ūėg?[Ŧ&Ņ@<PāvĩF´žíĩœ5­öŽéSkįķyžuNPļ ƒâA;@@@Č |öpHÉĸõÛĄĒ4Ííúõë79TÅT)ĨŠĒĶéôų*ģöĶ{æĪŸO °Ė( ,Ēv6Œ~ŽĘr@Ø ĸ°4öc ŅʒŌ´öÉ†ļ € € €‘ øėŨ0ZY´ŪÕHMu ŦZĩJĶܒuYˇdm¤RŠ”nˆÂÖÚÚz}úQEɰҸkúõ*ę ÍŠãããwUÛØ~ŽŽmÚ´)cëų|E… €\Đ%ûŒ1÷c.ĸ.\øˆ1&h7Ûōų<č+ÃÆ @@@ zŸ=SeĪV›áæQ͍ό€Ļ|æš( {­s…íĖ™3MQčG5}pbėTFWW×mÕÔåsãø9˛ĮķÜšsk­×Z @J \D=]ú_°Ŧ‘¯üTŽ € €¸+@đŲ]ß+Ĩ+{ļŗŗķĘ{^„G`ņâÅą etæÎ{1(mЎ™LĻĄŪ2Âtũļmۖ;•ĩ~öėŲÅaę{Ĩm­%ŗŋĨĨåJˏÚyÉd2y÷Ũw}ļĸÖ7úƒ €€K%ƒŧãããf˙ūũfhhČčuąmß>%RŋˇíŲŗįōš…ûŠ•ĄōôPŲeļ’í*s ‡@@@B"@đŲŖR§˛hŲÂ'°páBŸÉ´ķač–,Y™é–“ÉdĘ›/ÚNœ8áHļ˛ŲĶ>|t+Ēō… ŨÕöoåʕš –ŋĻĻĻ.˙uęŗUŅ q €á(ųo“ˇoß~9P|ꔖŽžz›@Ö9Ú78øū?g§—ĄĀt[[Ûåsė ôÕĨ^yįČō,WJã € € €@ >{4dĪzíB57ŪxcKooīí.M‘3DiŠájƒŒ3ĐÄæ°˛§Ķéôĸ(v8—Ë%ĢíWccŖŽ ÚڍÕvŖĻķåÅĪQMt\„ _÷#Å ÷ôô\&īÜšĶd2×ūĶĸ0ČŦK¨ÖC×i+V†Īv [¯9RPëÕ/ķų<čĢIx‡ € €DF€āŗ7CŲļ|ųō9ŪTE-N (øÕÔÔtŸĶåRŪĖŨŨŨīÎ|V8ÎĐįhĶĻM×~ŗŽæ×ÔJ§–펊ŋ(NŸxÕŧžžžķú9JĨR+Õ0ƒ €@Č ÖCæ]ģv] —ë†ÎWZįkēíbeØj•Ŗãz_f#ø\‡C € € €@˜>{0zƚM§Ķšr—-„ v477ßÂχŊəŽŽŽaīDaûĪ;ˇļđ}”_/]ē´›ĨʏpkkëõåΏöčĀĀ€ˆd&øĩŊŊzĪéͧ—jĪØØO¯Ļá €U ØŲÎ @Žã\Ē ;v\9d¯į\Ē •§Œj;Kúʅŧ@@@ˆ…Ág†YYŗ `˛…S@Ķŧ^ŧxQëŦ˛y+Pr:o›áLmĘN&“UOŗėLíŪ—’Íf .[ļė5ī{ā~ĩN+?>>~—û­ ^ ---ī¨Uʀ^ëh €áPPXŲËÚėįr­×ÚÎĶÔĨĘP`ZAi=ĘMģ]Ž>Ž!€ € €„[€āŗã§ŦY‚Ī@ģXE"‘ˆTŽ‹TŽ­)Ē•å•MYĀSSSL\ÀΚ5kI —ū’ŽŽŽˇßČā4°gÕĒUi5Į΀NĶh  €Đ͛ZįYķ]īÎfŗok™ĻÃļ§ÍÖŗ˛”„îííŊrúĐĐĐåsLūå_ūåËYĖ:G{zíéeč\MË­Į–-[Ęļ[[[u3ÚŠöFęĻĶ+ˆŧ@@@b*ĐĶ~{ÚíëŽģnޞgŲÂ+°qãÆöááa­×{$ŧŊWËŖ8Eu.—‹Mæŗ“ŲŠĩfüßÖŨŨ]uPŨZķ8ŽĶNŗ.dĀ?Đ4@Āiõ˙$öŗ*ÕûSÆũ­´˙_EĪC­­­ũƘMĶ[ÖÖÖfvīŪm`ÖŗŊŒŒØ//¯]ø^Age2īŨģ÷Ę9Ķ˰ƒŌWN(ķbbbâ{ÖaŸõ°˙_Ë~ÖaõC)Úös™9„ € € ‚ĪŒÄ›ožyyJĒĸ —4eōēuënyå•Wė/t\ljb ĸv͆˛'Ož,ėbd_/Žlįœé˜žX­zĶĪą ēĢž0ähk{‘ūūūæááa}éūŪ|Ą!īÍG@ @Ā*ÛĪ:T2¸ úb623hMķZoŽH§ĶąÉ /ö ÔßÔŪŪŪy…S;} €ø `•íg5ÁÍāōL],¸é:ˇīw ģoösa‘ļŋž 3§ N*ņ@@pI€āŗK°vą—.]ēĩփ]ĪÁhnnnFKâŅ '× ŠØĒUĢŌ‡Öz|úâ+Ę[MëĪĸ/#ŗÕŗĻykkëõ‘¨°#étzŅĀĀĀåŗģēēL{{û]###*ŧœĶ@pR 0ãVåú\.Û¯D"1žĪįčÕôÛAŲN% ˇo(Tļŗ”.č&8”Oí@@@H |vyX•-KđŲedŠŸ3gÎ<ĒĸcL„§¨ŽCđY_Â:ļ1{ÄՔgΜiēzOôߍŽŽ^ĩÎu.—ã÷qô‡"€~  .+XZ8 ‹Ŋfąžč ōļ/`ÁįbÁ`¯ũN{-N} € € +‚Ī.7Ų˛.{X|___ÛĄC‡ôe”}Ŋ‡ĩSUt# ĶW?’ĘtŨēuëmˆĖĪ^*•JŲ™ŧՊhJķ(ށ]ÎASÛĮå&?û=Ī €T)ĩārŲî'‰Ą|>¯CŲũ.{žË5ûĪ—ëpĸx‚ĶN(R € €ÄV€āŗËCOļŦ˝€Į‹/ž¸éå—_ŽLĖCžjĢjëīīÜ4į­­­ą˜.Xv§g|ČfŗËĢũųüąąąĩļ¯ģģ{‰1FĶE=ĶĒÖ.^sŨŦYŗîŦĮ¯°^#€DRžÚĪvÆ˛ũŦN‡)sŲ‰ARĀ×öpĸŧZËØŖŠĀkŊ8@לĐ`Đ@@žÁgwĮdPŲ˛îVAé^ hęßd2éčtÂ^ĩ=„õd“!l÷ŒMŽÃtÁ]]]ˇ)ĐÎæĒ€~ÅæF˜‰‰‰ĢūŊ2wî܋ŽęR8 €@ė Ēũl•ígĩ=nÁå˛ãee?kúíeOt÷ā‘D"Ą6Äa#8‡QĻ € € PRāĒ/sKžÅšÖ¯_ŋŠÖiUkǐ‹\ĐÔŋdÛšJ|ĨpeÎFq_ũ>H§Ķ‹Ž;vĨ¯Q|qöėŲ̞Tëíc§Y^ļlŲkƘĢÖ1ŽÔ)ށũL&ĶPčŗdɒy?ųÉO wņ@ :vPŲ~VPYĶ5÷XĪ ėéŊn ÚËA™]ÖēÕōôz͘mņēŌ×Gp:ƒCĶ@@@ ~‚Īõ–,AY˛Q  •ėp ,^ŧ¸éäɓ1čŠŋ]looŋKÁū(nŖŖŖ5Ãfát€ô… ‘r{ë­ˇn¨uL5ĨųĻM›2O?ũt\2Ÿ“ÉäUk<Ÿ9sĻŠV?ŽCđ]Ā*ÛĪå‚Ë 2ëÁV§€ĻģÎįķ XËwÔYbŗ_H$”ĄÎæœÁiį,) @@\ øėĒ]¤˛dí×(mqĢndwGmßîîîwŨōZš›7oūđôßV&´‚|™ĩ§? ;¨l?\èYmT 8ŸĪ÷c€ÖØšĩ)cũ^Īnņ–-—ātY"€ € €€ÛŸŨn[šreĢ{ÅS˛ €|÷ģßŊĪCđŲÅĐŦĶ3]ŦÎĶĸ™.¸6îå˗›­ņÛĶŅŅą 6 cĸ¸v9‹2kˆk 6@įė ˛ũLpŲyc_K´ĻĀ^ŸĪįwcvēĐ- ĸĀŗ‚ lÁ 8ŧ1ĄE € €DJ€āŗKÊėØöööHMëU¨ŠUđššššæĩZCÕYĩ,×BĘ8Lŧl؞׌1ŽŽŅœÍfkÖúäuOŊíĐ õ––ë“Édrzæŗđ˙üĪ˙§u¯Ã2\´Â!`•íg‚Ëá7Į[™H$våķyŠ€ļ?õÔŖlgMŗ­2ŲÂ+@p:ŧcGË@@„Ág—†ĄŠŠéžé_–ģTÅz( lÜëŽģnއUÆ˛Ē%K–Ė‹jĮ­é‚õå^dŋ”›5k֒¨ŽŸSũŠjfŋS>…åLMM !në^šđ˜AĀ"ÚĪ—g‹ķa+P<”ĪįõyŲaĄĢ]dŋūm›H$öÅŲ2F}'8ŖÁĻĢ € €Ô"@đšĩ ŽQv,Áį  Bxʛožš4„ÍU“Ŗ<5u‚ŽnŒßęÕ̧†‡‡Cõ9.ÕØ­[ˇŪVīße—ŸEû( ĖÕo|ãˇcņąQŽēÚ5æBeƒé‚­u­—zƒĩŽ6ĻÎÂŌéôĸz§ŨÖėQ™†|&ÎR3!ôõõËÔã3q'`•íg‚ˁ"„! 8âÁŖé € €ņ øėÂ8++V&ļh (hÔĐа6šŊķŋWš’z``Ā˙†¸ÔĻ Ž V?wÛļm[ūøãÕVBpŽ펡5Öė‘É/ãQr&„ͧO/-s‡@7ė ˛ũLpŲMmĘFĒ 8]g#€ € ā¸ÁgĮI™5kÖ`ŊYm.4‹"hnnnv°8Š*ˆú”ÔęjÔ§ vëæ›'NÜfŒųjÁĮ%î/ã|.9BKKË;q˙ĐpMĀ*ÛĪ—]ŖĻ`@Ās§‚ĶöMąöŗįĄB@@;?¸ƒĘŠužXJ ’€Ö´ŊtéŌĘ ĩ)*mqbJâ [DyēāT*•ŌÍnlQš1aõęÕSõú” ČÖ[nĐŽ/7‚ ÚÛÛī9´v͍[Ā*ÛĪ—ë&Ĩ@€œČ@Đ @@÷>;lģyķæģxq¸ŠW‡€Ö´G\ZB‰)‰K´Ũ‘œ.øÂ… d'—˙¤eæĪŸŋ¨ü)3UÖo:^tėØą™Oņ3ŨpËåæ…¸{48 ØAeû™ārœ? ô@ P Ōā´ūRíÖķ)ëYkOkŗgiŗŸ­Ũ|X_¨ęū؜hëīīovύā–åé‚ĶéôyˇäĮĮĮģUļWåjbnPĒ\û­ˇŪēĄÜŲqĀ—ëÁąLKKËoÜpà —ŗę˙õ_˙õĩ‰‰‰īdŧœĘKPϞū d?ÛÁeûßEvpYŲW—!§@*˜éīŽ”ļŸ NWÄĘInäķyûߓ*ūT"‘ā†D7 )@>;ŒŽlX‡‹¤¸€ ´ˇˇ'{{{ogŊQG(ĶØØčZæŦŖ-­ŗ°¨NÜŲؙǓĻäåĄ˙›ÕÕÕu›S7(E$ŧäxëĀLcŽ›" _Ö ÄÁžíÛˇīüŲĪ~6855ÕzĮw´ډĻī]144ôāŅŖGĮ'&&ƛ››˙ibbâk/ŧđÂĶ%Ęc7Ķė ˛ũLpyēī@§ÁépŽ[$ZĪįuŗƒū}ŠĮÕ˙cŒÉįķę§n^´û‰ ‘}: 7Đ‘°ëQ6lĀÚDs\ĐÔÉßũîwī3ÆpЊØ—ŦĐ(O|üøņųn}p;::ŪvĢxOĘ={öŦcŲÛ1X}ƙf͚嘧'€:+Ų˛eËoœ;wîŋß|ķÍŠ}čCspnkS˛Āĩ›ŒÖÁļũû÷÷9rä?MNNŽ-^ŧøoŋ˙ũī˙ÁĩW°'fvPŲ~&¸ŗŨE@ „Áé0ėŽM ŸĪëߙ;­õɋ˙ĪËÕEÛÁiíŨ›Īį÷cö% Ö/ŋډw €Z€āŗƒÃĶÛÛģZŲ°IQPđššššė”°n~ ›ÖŪŪ~—SYĄė`AŖ˜.¸ŖÂ—ŨŨŨKŦi^C}įŗĻ]gĢH`ƙfƌލ–œôØc­}üņĮŋŪŨŨŊvįΝ%ÎĨē˛}ûvŖĮîŨģĶģvíúT.—ûĩO|âŋrīŊ÷†ûnŽRfŋė ˛ũLp™Ī €8!@pÚ Å”aMŠŊÛŗŖÎî*[z{>ŸWđųĻæŽS“Ë@đH€āŗƒĐMMM÷) ɐš››[âŅ[ozÕŠ¨‹éEuēāÕĢWO믃ûH íĪK—.ívęī„ŦÞ ^îsQÉL™LĻ!Ęōųŗ?ûŗ?˙ęWŋú{Ÿũėg¯›>ĩv9ŋRĮvīŪmŽ9˛t׎]?ū“?ų“ŸûÜįî)u.û-`•íg‚ˁ.‡ €@lNĮf¨Kw4ŸĪ+āŦĀs%™ÎĨ ēúˆĻéÉįķ{‰ÄŽĢņ@‚&@đŲÁQŦSA›EQ. ŧūúë ],>vE§RŠT\~†Z[[¯ā÷˟?QûåX—˛ŲŦc>–ĩOZ+r[%ëc'“I­1Ž/!B{CB™kģįž{ž˙Ę+¯ÜōøãWí\Ļ\“ÉdĖc=föėŲŗuãÆ#[ļlųŸ~øáŸ–ģ†cž ØAeûYÁåÂM_ėjũüÛĪ…Įy €Q€ātGÅÁ6åķy5Íļ[ÛNk*oeA‡zV4ˇ€(@ |vpŪ}÷ŨL§ę hŠÚ¸qcûđđpdƒ?^ÁØØØŠ¸ü 9sĻÉk_ę›q´J­•}ôčŅEĪ>ûŦŖå†ŧ0'ī$E%ëcGø÷EΊ+~|˙ũ÷ˇhēl76­­,襥Ąž|đØ-ˇÜr÷ /ŧđ´uQfQ;¨l?O˙ũIpš(;@@ â§C<Āų|~¯ĶlW" ˙IęÉįķ[@WÂÅ9 €x/@đŲAķŠŠ)ōtĐ3 EuvvšuëÖŨōĘ+¯D2ķĐë1˜;wîE¯ëôĢž¨Nėv0prrōvcĖ7ũˇzë]ŗfcŸqũū‰ōVÉLš)aķæÍ~ꊧ"•ųŧzõęgūę¯ūĒEĘnošĘûŲgŸmūЇ>ôcĸ8#ƒÛ„ĨʡƒĘö3ÁåRRėG@* 8]š•§gzxļûĨg?iŒYoīā@‚#@đŲšąČ( Öšâ()  |ėßŋ_Sž>†öŊK–,™ô6:Õž(NŧmÛļånO›öuÁŗŲė§>C]]]‘ ŧÚ>•΄PI†´]fž?úŅ~éŌĨKŧ<Ûʂūô§?Ũūøã˙ø;ßųÎÍö~žË ØAeû™ārY."€ €žœö„ųęJŦ5žĩÎŗ×[FAīD"ņ€×S €” ø\Ū§âŖĘ~zZÅ1:QļŠŠŠ1ę˛Ģ]čTÔEÍÜÎ.ZŠË;Oœ8q››UDŅŦ^¯¨^ =*™ Aw“Éd˛đē0ŋūæ7ŋųũ×}įˇžõ-ĪģĄéŊ÷īßŋöË_ūōÎO~ō“{"U—ŗwī^sûíˇ˙ŅÃ?ü‡~ø§UŽ ė ˛ũLp9\ãGk@@7NW¯ēŗúKŋbg>ŸßĮúĪŽģR  €5 |Ž™îę ßxãĩŸ¯6‰Ëģ9sæÄfĒh—Į4ĶŅŅáØ”Ä.ˇÕ‘âĪ;ˇÖ‘‚Rˆ2PŨū=8::ڐîÖԌŒ5Ũz-×^sŦ—.]Úæ`ü5*ØQéLš\.™Ī[ˇn}ü~áŅ~pA –~ ^sŒvëîîYŨ~sæĖyÛ­Bc,›dĩe¸ÕümLß°'„ÎÎN×{Løđ‡?üå‡zh‚|ŗfõíoģüĄ‡Zį°ø,EešĻ¸œĩÕx’H€H€H€HĀ<#N'“Iŧ§§žŖ;ŨĢ…œûŲéV`ū$@$@$ „ ølÎmĐëWs’b*n# Öōōōn+ˇjåõš j#|Ŋâ.ØH]͊SRRâÚ˙ĩp8|—Ų–áąúMw{~ĻÂBēŖŖ#]Ž9Ö××‡Åą*Ąžž^\ŧxņ‹Ë#EešNí¸BĮ\¯âˆnmq‘˜<     8JĀMâ´:0ī7Åį÷Yp‹H€H€%@ņŲüK—.]ã'‹My* X°vwwĪ÷TĨnjŲœCÕ0œmCCÃEˇZņĻĢ$ę“ĮŒÎlfžfĨ•H$LŸŪ Vŋéøæã A›+ÂĨkŨĢ•——ĪLĮÁÉcøĀ"ŗfÍZ´k׎B•})*Ë5Åe'”y“    x€Jⴊâs<™LÖ be   pÅgāÃęÖ¯ ū%0}úô /‰ˆN´d(šī7—ÄgΜŠs‚ĩUyÚQŸĒĒĒ÷Ŧ*ŋéōYaŒrž\+>?đĀŸnjjJër{ķæÍ"‹Ķ§O‹ĩk׎ƒˇgĪQSS3r^o9ŊuëÖ1ņĄˇˇWčãáōXŋ~ũ¸´‘īĪūķ˙ „Č$>KQYŽ).ŖČ$@$@$@$@$`+bÄixŸÂõŌû žvzšd2‰w˙Ô÷˙‘ŠZų “+m4Dņ­ļRgf$@$@$@㔌;ÂyˆÂú•Áŋ,XPéÅÛŋTōĢšķįW"ëcģ]HĩžĐø´˙ĩņ'\p,Ė~VÄãņRTŊ "ę1hĨĨĨeZA™(pŅ[oŊõáļļļ´%č á8Ũų#GŽŒœƒH ąûˆŋsįÎŅôp5ŌĀ9)D#âé÷G/bD¤žpá ! LoBėHYĐŠá#Ã{S– Ú|kč°ēV8}Ü&    °›ÄeŧŸCœÅûú:!ÄJm÷yĮģ;„e|¤lŅžäßëŋúÕ¯â;!m°ō&[Úē¤ÅuįšI$@$@$`Z>›šēēÚđܔ&dĮ$$‡ƒąXŦŠĢĢ /ō °ÃesŞô’ÆÆÆČūũûņaä‰ûĻļļö˛ĨĀ„eeeAí#8ílĢķ/&}+XƒÁN,ĻxJ]›PwũŊŊŊw !ļ)U ƒ…éíí]9]€•2„cĖÁœpLo x™a ƒ´ŒF؆H8č¸I—Žūēß˙ū÷UœsYášH€H€H€H€ˇ´´Lŗëˇ°§§gēs-Ļ^Î^š‡ôdĢĒĒŪĶīįڎD"sÅqÛy¸Į†•2Äa,ØĮ:‹TB˛<!YZ@P––Đˆáné°Ŧ\šr$ \ WŨ8Ÿ- d;Īs$@$@$@$@$@ž$öCÂĘo˜tig Ÿļlâō0   €EĘ,J×7ÉŌÚÕ7Mŗĸ˖- wttĀõ0įÆĖIkl¸žvŗ;åąĩ1žą6‰L;~ü¸ņ‹988x›ĸESĻXVX÷{ô˙ĻĻąą1’OՔ”¸v@BMMMĮ‘#Gĸ„õ²´hŪ˛eËčŠŽŽŽ‘m)CxŪŊ{÷čy—qp4XŌ…lį Lßxãß|ķÍt—ō     €ŋ´ !ęą,Z´hVGGĮ¸Ú[ũ “îûh\!„ĀŧÔ $@$@$@ ås‘ @k×"zčrˆ@‹/žÕCU˛­*p= wĘ~ gĪžzĄŪvYßB°…B!2kŗÂē˙7œīxŦ k ąûûû];˜.>kåt+“3…\į3]gä8,ϝŋūúŒÄe     O¨BādŊb“Ŗ\w!0Į!ėîėččøBĻÚæúFÉu>Sē8nđZŠĪŲ ō   ØD€âsq ã°v-. ^í˜û줤$ŗJā•ŠZPģ„K Š^t’^rŒų‡ížėÍbåĩųŽ ņ„ Y•ŖŖÄuaûöí?ûėŗ Õ ‹ęōōō˙ĒZšX     ĸ Ā‚ũW˜!,C`†Đ ŅYzôÛ*„X)„¸WąNąAQŗG^UEŪEĶa$@$@$@Ep­ĨPŅ57!XšBpd ĀŊĐ×סˆ4ō'KVŋū/UWWOϘzWĀ}¸]m˜H$‚ęČ^"ˉmŸ &\ɞģģÎâ Aŗ*G'IzbÅ ŋ­š°SϤ¯ŧōJ÷ë¯ŋ>Ū—ž2%dAH€H€H€H€H€rĀ7„fšÆ|ČØĮtqØ“ĩíI; ‘bĩ2áo._ž‚:ę‡ōqūgeZ‡! đŠĪE´8Ŧ\=:ßfTü}é”)SJO:åoÔžģģ{ž]VŗĪŌKΝ;Wai6%n§ûpX‹ģí˙ sb[uĪ;W=zÔĻ–ļ>›B6‡Ķ§OÝûëcrH€H€H€H€H@Q°b™Yša• ĪPŌB"ŗŲu•Ÿ[ZZū‹&:ƒ,ēPo Ņ ŽH€H€HĀ.Ÿ‹ +WĢ,؊(/uĀõ×_?ÉÁė]›ĩ×,7ķiˆx<^ÚŅAãÂ|˜Íœ9Ķus>"¨eŌŲŲ9Åh\7ă=æöÎ'`îë+VÜšwī^ŗ;Tō)FÁqŸūų/]ž|ųžÕĢWO(8/üëŋūëĢoŋũļėŦ11e&E$@$@$@$@$P ˆÉ°`Æ"ˇĨëkXøJk_¸Įļ<#ÉdųCôV%@dF™ā2 8­NĸņÍ(šŠRn–ƒH€H€f[(=Édũ_ʝ ™7į(€ĸe4XDSˆ–4¸&  “ P|.h[mm­§\Ž‚WJ˜w´ŧŧœ"œbpF‡ Fõ\4 Xxã7âûöísõ€;įŽvŖĢōîînË„Ņ˛˛2 „rŊõŧüįž1cFŪĶ@ØĮÜņ2 ˇŽ?ķ™Īüå† ūÛîŨÎŦ[ˇNüÅ_üÅ˙ûŲĪ~ö-ˇ2dšI€H€H€H€H@1øV“ÖĖXC•"3Ŧ—ŅāæūˆžN‹Ī°z×|ŦžGÜ>iŽš×j¤ûrŦH€H€H€ $@ņš@pK—.]“īœ”fÅË\DķŽ ØâzØEXr5î÷įΟ_” ’ęįĢĒĒۺ̌šĢrŒwÍĮ`$šhˇįSč@†B,ĻUcõāƒn{āذaÃvģ߆ģ풒’ŽĪ~öŗš,TÃÆō    €ĶäĖԊkË8ŽĩgMÍú8Ô[@ąß0z!mĢhˇäŲvs¨­˜-  €P|.°ąaŨ +WH%P]]-įãI=Åũô|Í ŋ#˜8=÷]°`AĩÍĨuÕ}S]]=Ų*>p9ŨŌŌ2íVeakē°l/$CĖôčŅB.UęšíÛˇßŲÔÔôŋW¯^=Ã.÷ÛpˇũíoûÃ‡ßĸ †H€H€H€H€Ô" -˜ĨĨ/„JXÜĘĖ›ąø&­Éd,`=lg8 |› 8ˁîz!mŠö•ÖŌfæÉ´H€H€HĀs(>ؤ°n…•+ ¤€ˇ˙~|øęc#•ƒŅũÖÖÖ¸ŸŊāwdhhČíî‚Û4×ĪF›Ŋ¨xV͝\TĄr\\¨5oŽdGNƒĮāāāmBˆmFâ+§MŗlĪģ˜^š;ūØącˇ=øāƒ¯ųË_ŽZŊƒî­ {öėŸûÜį.UVV~Âē\˜2     ¸ŠDG x†ČŒĩ\ JBh†)ˇ]U1Ģ 8™L"yģhđ_iU}´t‘ôķáã s$QˆÖ pE$@$@™”d:ÁãŲ Đē5;?Ÿ ‡ÃÁX,ÖägųÔŨ .§ķŠo珉DÂõ–ĪéęeÕ1XúŽZĩjŽUé[‘ŽÕnɉDŪķ$[QO3Ō,tpĮæŽ?ũo˙öoŅ'Ÿ|ōāŊ÷Ū+`™lv@š6l7n<Ŋzõę?:xđā>ŗķ`z$@$@$@$@$ 8ˆËŌÅō!ˆ‹8ą"3,kīՄΠB¸xÆqķ_Ō‡•Ģxšrą.°seƒķ°>^YĀ<ĪFŌ΂3ęÁkÜōžāî*īl™*Éã$@$@$`ŠĪ…‘ŦwĀÅla%åUļ€0VQQąÆöŒ]œaĄb“‹Ģ<Ļč Žž8‹MÂ}og8yōär;ķ+2¯+Ÿ`ī×í` O…ŪKÚÜņ°PđJč}úé§˙¯Å‹wÕĒUC°P6+9rD@ÔŽĒĒÚõŌK/Å6nÜø–Yi3    PŒ€´`†@ˆy|!.KŅPēΆ˜ Y/2oÕDfÅĒŖvqzöV‰ķ›ĀŊ6 ĪŠĐĨûEēũƜ׸¯(D§Ōâ>  €/ ĐívÍĢVXˇp)/ņ¸ž8qâlTՔ*†BĄų…ŠMĻ@DΜ9S§@1 .ÂŦYŗ–û}Axq+Ũ’ƒ}0Œæ(ƒ+N›ā ÁsŖÍ˙öo˙ö˙ŲącĮ}â‰'~´gĪžEëׯ55…WÖ·úߟųĖgîž÷Ū{;\qc°$@$@$@$@$›,QąH—ŲXC• FsbáišYC›’+Đ? IDAT‚>Ä~ŗÜpŖíÖÕÚ÷*`Á‡Ŧæ!DceÕĘ\Tûōb  0B€âŗJ)q`Õęwą, wS OI9ÄŨ ēģģCNųæ°Õ.™­ŲĶĶ3Ũę<ôé{ÉŌW_¯bļŊō î™3g„Ūxãøž}ûđīŠ ‰ÄˇŦ\šōÁÖÖÖo,Y˛dĘG>ō‘ëÛÚÚÆ ҧOŸõõčsģā^VͰv~ūųįģoēéĻ­ûöíûŌž}ô˛-qM$@$@$@$ā*RdÎ43æč…ØGÁĪÁfÕ,“14\T¯×DŲBFŅBŧŨÜđ§ĸAB4ęŽ{–B´ƒ÷#ŗ& °ŸÅį˜ÃRÜ $‰ĀĐĐPuĻs<>–€Û]N­Ma{‘ũû÷ãc„ĮēÍŌˇĨĨešÕΌH$rņÔŠSčŠehhhūŦYŗ .¤ –ĶįmĮ…ģwīŪ&„ØvėØąúõß˙ū÷ÛđŧšũöÛÃĸ_{íĩ‘b,^ŧxDp>vėXoīĉ˙ĩŋŋ˙‰×_}ßë¯ŋnGQ™     K–ËøN–k¤ņRŠËķ° ÁAQšĨ2\SCˆ†‹öÄ"]ž§–m*—ģ×N-[žûÎą č…hÔĮąf   O ø\@ŗVp/ņe˖…;::đ2ÍÉíîv—Ķ9Ē—Īi×ŠĪĄP(dĩ¸š ŌM–žŊŊŊwĨ–ßėũęęęÉf§éDz‰Dĸā)-|æúũôΝ;åÜbâđáÃņĮüÁ 4_ž|ųƯ}íkO÷÷÷?Ë9朏‹™'    @ž Bâ{X ÍRLÆālôŠ@dvƒÕkžÕö_ô@  cE2™Ä`ô!œVĐĨļV4SVúēŖÎŖá–œB´)x™  €j(>Đ"´j-šĪ.[āŋßúÚk¯Q|öYÛR]¸ŽÅb“ēēē šÜņklŸox„ W¯¸B8wî\…BÅ)¸(7ß|sÁíŠß]x&)8sw_x¤ŋŋ˙ČĨK—ž …B}ũũũ¸ģ:,=    €ĮH‘ĸ›Ü†Č,iÅŧÁcõfu˛ĐŦšũ8°}…˛ŋ0Uˆ–btrž'   °ž€´`ÖĪĮŒ\åSą-÷­/s ĩ ¤ ŅøßŲ­ũā˙EēíVģ,   ¤ øœ$×nkkk›Õ"BŽ2đŧúpôõõ-Rŋ¤Ž—0^VVV°›]ĮKob‰Ä$“ŗ5)Ė7lk†BˆbEJ;Ë ËpĢ]B/X° z˙ūũ° .ęėŦĸiy+ĸcy/Ė}](PĪŗgĪö3ƒBŲņ:    ŧĀZSēÉÆß!XCHÃļ´dvõ÷I^D™Š' …čÍÚ˙\së…hü_ņĒxÎLH€HĀŸķ„üîģīŪ ĢVČE`ʔ)Ĩ˛S‚Ģiū? ų’#‘Č´ãĮgĻčŲ™3g†-šŞÃ2\ā |Œē5Ô766FŠ)ŧßᐇđÜĶĶS] C^K$@$@$@$@:Šķ1Ãbƒ^Ĩõ2ž?Üü ĸĢ*7I@)øƒ-…hü/nŅJˆ˙9XDSˆVĒÉX  =ŠĪzļaÍJËg EL:•Ŋ9îƒYŗf-‡Ëi!Ξ=kûŧÉfq?qâDĨYiM§ļļörGG‡ŅčŽÆsÂ2ÜŅ ž9,%Š ÅZN•šCxÆÔ `IW† ´ ‹@$@$@$@. į`ÖĪĮ,ß%!nÉ9h9ŗ “Eô$ü?nÕüŋÂ"Z/DCŒ–˙ŗžĀJ‘  ¸Åį<Û ÖŦy^Âč>%P[[‹šK12‘Ŗ€3Ü===Ķ3œōŨá &\ņ]Ĩ‹¨p4Ą¸W~¤ouuõä"ĒjčRXĪ;vlځ ÅW1RKKË´bwyÅũxíSáYsņNņš@ˆŧŒH€H€H€f W$āRˆ–ՁE4…hIƒk  G P|Î[$ŠĘīÆö+Ėe\^^ëL† ēģģį+6eHÚu‡í(-ƒœķ9 X¸Īrš§L$€ß]Ė%ßÕÕebĒîH*ßáŋŠÃÃÖ[Ûģƒ KI$@$@$@ž& Ÿâ2„()2Ãōá9Ū͎+Gc`h,z!ZēÎĮšH€H€l!@ņ9˰$‚5+ !€šŒ\;¯‘:‡ŽĻß'ëaˇĖaü~ŠGļäHú”ÃÖîēČÍtæ"ÜR áIĀŌL,N<‰L+ö‹ß]ˆ°]]]ģ,.ŽrÉCp†øCCCķ•+ D$@$@$@$Ptķ1CdF€ĀLQIƒÁ Āz!Ū FoâoÆFÜ! °Åį<āŠģ $`”@uuĩ#œŅō9oá…A§Ë Jūšõ°+įwĘz}ppđ6¸™ÆGžåmāöÁ.gĪž5e°N"‘˜d9p3čëë[á!',"‹D$@$@$@$™,–ąāûA Ιå fˆÍœË53Cž!HOƒT¤Õŗ^ˆ–¤ĩtúĢy”H€H€ @ņ9h託ģy\ƨ>&°`Á‚ęũû÷ã’ˆiîƒ'NTĻ9ĖC."°jÕĒšNÍÛí‘Ņ.q>‘H¸z0G$šXė­ËiXP?~ŧؤ\w}(–…žpáÂTšÍ5    €R¤¸ŒĮ™åaBŗ‰¤uŗR…gaH€\O@ūÆ "蝄Eôn­ßƒ\(Dģž‰Y PƒÅį<ځVŦyĀbÔáp8‹Åšēēē(>§š'ĸŅč¨X’æ´¯A4{ã7âûöís՜\'Ož\îDCšÅ EkkkÜ.qâíŠS§œhSō,))™nFBfYP›Q;ĶĒ–ųinüŅ‘ÉNK …k   °—€—!6CāAĀû„imčĒo?­\‘ x‡~‹6k‹^ˆÆw¤ĸųMéöfMH€HĀVŸãއĢņčŒIB@tzæ™gÖ!|7˙¨ö×ÖÖN1Ī7QΟ?ŋČ7•-˛ĸđBQWWUŨÂÕÎ6­ŽŽž\$VG/īīī7åÄ§sÉ×477O” ¨šņGG';4%ŽI€H€H€HĀ|“Ĩ¸ áÛR¨¨ĄV„X3 €ĘôB4~Û`ŊE+0…h•[Že# E ˜ŌŅĢhŨL-ŦWaÅjjĸLĖķ`Í …æ{žĸ…U/ŗ XōƒA×ũÆ ĖvYöĻŪ,ĶRųy˙ÜšsnŽ?ŦuÍ(˙Œ3&=zԌ¤Ü”FŧŦŦlô÷˙“K–,i}õÕW)>ģŠYV  W¸õÖ[[§M›ö÷ģß5TUUÕôõõég™0aBĸ´´ôn¸áđΝ;7čÄIWÖ×§…–"3Äeš ‘Y.xį‚ĀŒöe  ˇĀoÛVmŅ ŅØ–B4Äj  ČH`ĖGQÆXgŖŖ[˛dÉ=fuŠȎQ~ÍĪë`0č§ųŽÛ4ĄyL“ƒCKKË´1šC$@$@$`˜\mßqĮ– Īú­_Ã0ņ?účŖ˙^ÜŖÛŠķ1oŅÍĮ ÷Ų$0ŗœOnŗ!^P¨đč Áj‘ xžgüŽcŽh)FËßūĩÚÔ ž‡Ā ’ €_Øj‘åV¨°ZĨåŗ[[OrßxãÕj”DRØmĒNÍ3—bQ,›ÔÕՕ9’ZgęŖŅč ĩФTię͉‚V•îĻážÚĒô­L÷æ›ožbVúŗfÍCCCķÍJOõt/^|SēwpčííŊKąMõ:°|$@$@$ mÛļ=ˆ9ž­rĩ­žO<ņÄuŸüä'ˇ&“Éį€Û­Áä˰b–Û°5,âā~•ķ1gģ)xŽH€ŧG@ ҍž °ˆ†€AFX‡H€HĀĨ(>h8Z­€Ä(Y D"‘*ē?ƒ¨Ļššyâ˜#Ü‹Âáđ]]]]ģ\‚ÃQ+mnŠ‹õļķûj—Ü;cŠiöÜá‰DÂ7–ĪW¯^ũP:—åø˙ˆD"ĶŽ?>†5wH€H€H€rØ˛e˖ī~÷ģš#ZŖžž^Üu×]îģīžŊBˆ[,ČŠ$ņŪ‹Ez%’īÁĪĨĢlˆŦ Ī4I€HĀŨđœØĒ-Rˆ†+įnôZ­æ†ÄŲ Ā"ņ•W^Ysøđaŧ41×\&“E D"1)åw3€ā6kÖŦå*‹õéDÁ ÕņõaŗŨ“Ã’úÔŠSž` 9å3îŊŗgĪF}•$   ĀŨöM7Ũ4ÁęyžŗîˇWŽ\ščžûîģûЧžz&[\›ĪÉy˜Ĩ%3˛‡8 į`Æ÷ŽÜļšhĖŽH€H¤-Ģ‹h Ņ’×$@$ā"Ÿs7V\ŗZÍ“1H ¸S.//§{bŦUÁ„a, Rp“Ĩ"æ“E™ ===ĘÎŪŪŪž<(h%¯9sæŧíBŅ5nļ{rŗ-Š­lŗbĶ†ĀœiÃėŲŗ‡]x?‹„ד  @ÁÚÛÛŊķÎ;qÂŨvjĄˇlŲ">ņ‰OĀĖ ņĖ•å‚âÁ Â2ŦĪ 2s`ujŖqŸH€HĀl°zƂ ĸå´ X3  (H€âsŽFYēté§Å•Eäi€;偁Z imkU0aOĀM–Šƒƒƒˇ¯Hsåļ]ëK—.Íļ+/ķAGĻŠĄĒĒę=ST8ąyķæe|—ëééŠV¸č,   (G ¯¯ī?ūņ'¨P0¸ßūđ‡?)//˙ËC‡ũƒeÂ;˜´d†ČŒmXœ!@`fĮžƒ+  %č…h<ŗ FoâķJ‰ļa!H€H`Œ–ãbúôŦUiĄéĶÆ7šÚÕÕÕĻ ,&ŅļäTļVĩ B†Œ&L˜p%Ã)å;í"ƒBĄPH90ēe˛HÕE1uŗŋŋßuĪu+,č#û÷īG'Ēįį…¸.ĶM´lŲ˛pGG‡/8dbĀã$@$@$d2šĸ¯*aíÚĩâØącŸB#>ŖBX¤Ģl)2ChÆ"­˜=˙Ū¤Jģ˛$@$@EĀ)iõ,…h¸į–Ī5i-]tFL€H€H 0Žë¤.Ŧš…_kUZhΏWžO`Á‚Õ~CŪ¯uú-†vģ#N_õŽÎ˜1cŌŅŖGÕ+X†9=8§ģģ{~†ĸ9~8Ķ\ŧV s'wttX™…éi÷ööŪezĸ×ôƒč‡Đž‰?ÄbąĻŽŽ.v&g‚Äã$@$@$đ>úyķæÕŊŋëü„đˇß~{Á’Hq.ŗ1đY.čœGgŧė¨Į6   x…€|žĄ>č€EônŨ4ĸŊŌŌŦ €Ģ”¸Ē´6Lq [féAáp8ƒUËģJ íļÍģ]pîÜš ‡˛Î;Ûēē:ÎÉBÍ ęÚÜÉøØrU0û÷ƒ[`Qí*6‹ÕecápØ*qŋ€ķ  P—ĀęÕĢ×ĮãĐo3‡Í›7§=ŲÛÛ+vîÜ)öėŲ#°€m,8.ŽĮą­[1•ķĩ€ũͧO—Įôë|āĖE­;qYēŨ!„Ā‚NvÃ{ ,™× !îBŦBlB ā8NáY’›$@$@ž#€×xæáų‡5ž‹xFâYšV˜åšJŗB$@$ "ŠĪŲ[Ĩžššybö(‡BĄųf{B€ kĄEĩ2ˇž)ŲØÁ3Áđđđde Ė‚  €Â.\¸°tõjčˇéƒ—Ķ…¨ŒkĨ qšĻĻF´ĩĩÃq)2ã8‘#GFŽÁÂYnœĐũ ~đāA¨ÕRhžvņ51ŗ^dFŧk™éŌā&   ø€^ˆÆ ,„-ĸ5\‘ €Å(>g +UXĢf‰ÂS$`˜æ§---ՏX7|­×".\¸˙W5 bcŲĄ”!K3gÎTvÎᆆۅņlV°’™jëîîneÛP5VŠåÁŲ\ßcڐžž>>wRÁqŸH€H€Ōøũī?‚qēŅ8Ķ\ĐšqķúõëÄb¤ƒk° a5™mšëôŅ#h×L:ĩO™!4ÒæÔ™%$ŽI€H€H ;xūĀ-gĄ+Ut`3€Y†††ĒÍJËÍéDŖQŠ!ŋ9­­­Ų'›Ëp­Ũ‡{{{§Û'ķËM ŗŗsnîXęİę>rbÎmģŠÖÕÕ]Ε'Ÿ;šņ<   d'ĢåLÂŗŧRž‡­ˇ^Æ6ŦĄĨuķÚĩ˜nōZĐĪ-qM$@$@$`;x54æ‰Ö Ņ›„ŽčŸŗ3$ (>gD+Õ,pxĒ ˖- ķÅEÄkkk§Đ'?Ūnrûûû˜n•įČnjj˛›Ũn[uųa>đ .LÍu577O„ĮĪ\ņxžH€H€H 3ĐpĢ keéB[Ɔ°,­˜åį .C”Æ‚k0/´^˜FŊ›o\+Ej™6×$@$@$@ļĐ Ņ˜ŪŽK`M!ÚÖf`f$@^ @ņ9K+VTT Ã–L#—¸K–,šĮ´Ũ™E,í†ų[ƒÁ +æÄŽĒĒz/KUl9Ĩ͑­âHÔxeeĨínŸņã2wĶ5š8júũRRRâuËüz#ėĘĘĘđ{ĸâ˙ˆémÎI€H€HĀ aŅŦ–ą‹ÅF˛ƒKmézkX:Cx†km,+WŽšVÎõŒką`–Đĩ PK÷ÜVԃi’   äEč šE4DiŊŨ–WJŒL$@>$ā¸ÕšÂĖ㍍…ËĮĸš„E!ÄBŨ´"¯Zĩj.ŨŲgƉų[‡††ægŽĄÎ™ ¨âF^Å Ž•ÉeîĻãš8júm•Eĩé-<Áz#ėđ{{čĐĄÖW_}õZĪváųņJ  đ4!   €mŽ`œ˜–wC|ÆüĐpĶomˆĶ $@$@:´|ÖÁĐoÂ:d $`&ÜS}}}ŽpŠlfŊõiˇžÆ×ÎÜ#ڀs5!ĩX,6IÕ˛™P=Ķ’°’“Ę.Ų͸bŊ; ä+ö+WŽ\3Í2#cĻA$@$@%0ūüˇ!ô`+@|N/Ũ1™,Ŗ.\¸_îsM$@$@$ā(ŧ,lÖ,ĸąŽ×\scdŦŖ3Fp” 3' ŸS€čvR8ĐŅāĻiĻL™RjZb.MȍķŌÚ‰ēĄĄáĸųš9/XЎˇˇ+7 aÖŦYËQ6'‚›îŸp8|—Uœ4—ėš{h$ōėéé™näˇƒžJKK}=čÉÜL‚H€HĀģđŽsåŨ/ŋürŋt­RuõĢ_ mßžũv!:ĩaeÅé4Tj –…H€HĀĪ`ųœ*DãyŊCą–B´Ÿo ֝H€nˇ3ܰNĨås8<\oŧފ1 ގ¨„\zq(šĪ˙­ėwæĖ™ēė1œ? ‹UUÚąŗŗsŽķDƖÂāØ#öíšáū‘4‰Ä$šmöڈ0kvžvĻAY•˙A;ëÍŧH€H€H øƒØ ņVJŌ]æ†îînņėŗĪ^Zŋ~ũíœĢwŪy§[q¯ö ‰ōÃĒ "4‚tĘé54 \‘  €C¤ėņ΁įĩœgCēæöe_°CíÁlI€&@ņ9CĖž={8Ã)&ĸD"‘*­Ã×ŨŨŨĄĸúāâĒĒĒ÷T¯&,{Ŋ.î˙ IDATÛ䓛`( Y% "]¸ĻŪģw¯'k+++¯ä&|-Fuu5Ũ~…Åx$@$@^"Bŗ^l†X‹yĶú×>ū|"Ē XbO›6íÅcĮŽĄHč°FŲõķJBŒÆĸŖņîãÉ÷Uڅå   đĖŪĒ-Rˆ†§ŧ›āg9Äj đ,ēŨNß´5UUU“ĶŸâQ(Žæč\ētéšâRqīÕnr ėåÆÆÆˆÎÊbdÍ×IË^}Á 0ƒAåæČŽĢĢ‹Z%ĒęëŸnģŠŠi(ŨquwwΎR¤Wå>ĩ‚ũģīžkØC 7ÜĀ÷=+i’  ¨Fēpq +ŖŨš ‹_tūÂr \cĻžQ™ōōō˙ąy3ĸ¨{ėąĢĪ?˙üwŗ”Ø´y'WjŲÛáî ĐŅÍų'ŗä)  °˜€ĸ×iĪkˆÎ4&ßU¤7‹‹ÁäI€HĀ^´|NĪ;ŽY§Ļ?ËŖ$PĖ%^^^>Ŗˆ$\}ioo¯cîˆ]/ŸJ‚´R44ÚV(C0TÆ:E–ģŗŗsšÜļ{]YY‰ŧŅ阹cÕî2eĘo„ †­w3Ĩ‘é8ÄXVg:īöãsæĖšÎhjkk§hVQ´‚2 ņH€H€Ü@ī;Ō6Dfŧû`[[ËŖG~öúë¯`õęÕáúzgû‚wî1p~-Īw:É@ļa Fā‚8x'Pú[CV€k  đŊ9•^:đ|Æ9ŦH€HĀõ(>§iBXĨÂ:•Ŧ 0kÖ,100 œXfE]ĶĨŲßßĪߝt`tĮ0@s*wuuéŽĒĩ‰DĻ9eŲ›JBEWîVŠĒŠõΰÎEåÌ3,ķŲÃŋĩmų ’Ã˙ęâŋozíĩ×(>+˙_Á’  d! ]LCL…ˆ ņĪ6X6$6§ËĢĨĨåáuëÖmÛŊI΄ŪŪ^ņ•¯|eđ7ŋų͝E– UŒFį68ÂBÛzŽėė.6/'  < č…hŧß@ŒÆ€1 Ņy‚dt õĐ cš6UĒ*ĸJšâņXĄų1Ô477OôcÅķŠ3(„ÃáģōšÆî¸gĪžUf…Bī8üsįÎwĖŽŧā–pîÜš +˚H$”sÉnF}[[[ãųŧ§ĀCĀÕĢW?dFŪLƒH€H€l"€t˛.¤ą@4…Ø ÷ŌpĄ gė›&<ŖnßųÎwū{8îpŌũöēuëÄ#<ōmŗëĻ öëÁN2„ˆ֒3\‚5   €} 8ËŠ4 Jã،‘p˜NÛ $@$ā*ŸĶ4,ĨTp'›Ļh<äšëŦ7gXÆËĘĘ<)™3‘HXfjvYNĪIĄ7SŨ;;;`‚Á +VŦ(ÖJ&SÕL=nõüÔ7ß|ŗenŊM‘gbįΟ_”Ī{ „jx+Č3F'  ; āÛH/6ŖŖĮĐų*įk–bŗååzꊧV<ķĖ3ũ§OÛī™îļKJJ:>õŠOũ'Ë+zM¸c)Fƒ5:ŋ!>ŖÃ ,°(FÛĐĖ‚H€H€4z!zŗöND!šˇ €ĢĐũmšæōąUj  €™ `3\;ÃrB&:JĨ{ÉuĒΞŌpŋ}õęÕ˙Ø…­°˛žíļÛ‡‡‡?fu^yĻAčđ†U4\žŖ“īq°ˆ–VXré<“ft   <H!Īcŧ;!Ā[ Ū¯ä 1í0W$@$ā,vDĻđ‡5*ŦRSs—L%‹ĩŌŌŌEĻ&ę‚Ä\bŠIį1փ9qâDĨ~ßÉm¸nîččp˛ŠyĮƒÁ câ<\,×ÕÕEUļœ0Ė[œõn*d#ûĩĩĩpŽÎntšz&"$ ØfĩåĐŦ  @1`)+-›ĄÜĸŗÖ9°lļ^É-Ļäēk_zéĨŋ¨ĒĒę\ąbÅWŸx≲x¯æĖ/ 7ß'NüØÁƒ÷™ŸƒŠ)âŊJ˙n%­ØĨ­oo}  €ũ ,ë-›Ņ‰ 1ÖŽžąĸ UīŲ¸qcôOūäO^¸ûîģŖmmm‹ŅKį={öˆ'Ÿ|ōęŋ÷8pāŽŊÜÍņ0A?ß'į§Ô[Fƒ3   €9 DcAĀŗW ÄŗĮąf  Ķ P|NA:iŌ$ŽŧMaÂ]k@X˛dÉ=¯žúĒoō .ä|ęo'mÎ`ŧę;i ^my4Ĩ~'á­âØącĶTé¸K$“,otvvbŽcĨCwwˇåķb;=Š(tz֘úŠ21M  ĪÂ Öxī“ ,Y}/  Z1ģēēēõ?účŖKbąØÔææærŅŠķBClÆōōË/'Ž^Ŋz6˙ōšįžûĪ~îĶü‡Č{JžÂŊ†Ņė 8ā[Č7$ ŽI€H€HĀ"úÁ`xîę…hũ9‹˛g˛$@~"@ņylkäĘrlÜ#c4Ëĩ…Æb{#։'*ŊQÖĸËK+Š ŪĻ’uĢj|Ŧd_hÚ  ŊÖčuä …æī†x šŋ`eŽŠÜPG–‘H€HĀvđˆ—!öĄ–Íž›3ĩÆÁƒ÷ !°` ¤øíoû—O>ų䇉DŧŦŦė:ŋ|ųōåęęęŖ[´ø™’ķûņT1ZZÛÃ2ÛúûŌ7ƒˇũ~S°ū$@$@–Đ?{Ĩ ˇÜō=PZK[Z&N$ā]Ÿum +TtT3€p¯õõõ-˛#/UōˆFŖÃĒ”Eõrš÷7ۈīÛˇJ…öööåVģKΡÂ*XË2CėtšânÛGP9sƖų‡í°°–moĮrĄ÷W]]ŨeÕŨąÛÁy €Ī Ā’Y Íõd'#,› $pčĐĄB`a(žîI ~AŪˇ°ĐBĮ8‚ė4Wî{I+W$@$@$āō™ŠōâũĪ[Ė-ßņŦå€Dˇ´&ËIŠ ø<ļ!bM46 qŗgĪö‘¯­­UŪ°ņÖŗ>æųķį•œĐŲŲ9×úÚĪAĩßmÄNÍm;FŽ*kRUUõžņV.<æœ9sŪöŌī,ž…Ō¸páÂÔB¯åu$@$@Ž%€Dŧ`‘b3Ū8ĮŸk›Ô—G‡7îYŊ–´Ø×‹ŅčĮũÍr_Ū&Ŧ4  €  8oÖ)DoŅŌÅsĪb>gMÍ$HĀë(>ëZV¨´|ÖáĻåĒĒĒ&kķ¨ųᥭÔ<Á–7n‘@P ƒœ#ÛGXÖÕÕE?n ļõQ&L˜pÅú\ å ō˙\}cccÄP-ŠŒtéŌĨŲE&ĄÔå===Յ¨ššybGG>ņ1É@$@$āMø×[6ã7bŦHųûīÍ6÷k­ĐŽE °ĀŊWŨxÆ}qüđŊ-9pM$@$@fB4ŌÃŗŅĸÍĸËtHĀã(>ëxŪŧyäĄãÁMë D"‘*Í A˙Ņl}Æä°jÕĒšp%Í`ŒÕĄĄ!%įg…(ŽÚ@ÎÎÎiÆČZkᅎ@ûÄbąI]]]ÖW¸°¤åUaWįqUŋ—žíõķ¨ū˜¨eee¸7)>ĄÂ p=)¸a­ÜāB›‚›ë›—ȃ€›å%øŸĀ˛I÷ū#ÅhД¸&  cđ^‰ÁŒXôB4ļҝ ‹h>_ąd,đ/uČÛ`5HĀ6c_yå•5‡öŧø|ōäÉåļõHF‰DÂq3Ę`0­Ž.Øø2]’ž:vâĉJ§+„ö ‡Ãwuuuírē,éō‡0n—ģtÍÚC^耝‡Ã˙.ā™ķæ›oŪšwī^Ī?sŌŨwy2.  €Ĩ 0ë-›a‚Ž9Xgb›HĀ^šõVĪč4Į˙(\u ˙ŸX‡b´ŊmÃÜH€H€ÜG@>7QrŊ÷\k-âëSÕn@–Gˆúúú`,kęęęōôĮĒJn‘]vßÉN —ÛŪâ.X° z˙ūũöfš&ˇ––Û,zĶd?搊îŅe#‘ˆmƒQ`) …B2o7¯ÍŽįĖ™s]GG‡›1°ė$@$ā&Ō…/Ä*ˆËŌĘ–Í›ŨԒ,+ \# ī8Į|ĢIËhlë˙Ī—H€H€H`<ŧo֖T!ĪOŅ|WĪGHĀU(>_kŽzæčt՝ÚF.a+**Ö!v™–¨‚ )äYA:鋤ę\ĩMMMCéKėÜQÍÕ5:~ônōl/Đāāāmļgš!C¸G?zôh†ŗÎŽŽŽžlW āÕ¤ģģ[Yäyp¨‰ĮãĨyÄO5‰Ti¤Žū¯¤-’ €ģ Ā’ī"šŅ‰† ÅfX63 xūĮ1˜Dų;k.ũŧŅčHįģ—¤Ä5   ŧO@/Dã9Šgčí´tÍM!ú}^Ü"× ø,„hiių ŦO]Ķj,¨§`ŪĶŌŌŌEžĒÔøĘÔp€Įx(šŽ(:Wm}eeå´\e÷ëy•Ü;Ģč]ŪįÎĢÛvŦUļĪŖūņ`0X´7,Á[[[ãûöíchđ•\@ æžûî{¤¯¯¯å­ˇŪŠVVVVęË<0000yōäîÚÚÚįŸzęŠīŌ’@O§āmĖŌ¯›ĨĨ- ÆĘ IĀÕĐ9k-ŊëPéA/Fã= ŋėL/˛šÛÛÛ]Ŋzõ?ŧķÎ;wô÷÷õĪŋĄĄĄ÷Ž\šryÚ´i˙6}úôgļoßūx‘Ųņr  k āšˆA]XR…hųž ąÚ!™Lęŋ5ĀC~s ūō{ë=@€īž¸+ÜUIŠĪxÛīíŊ " 8E ŧŧŧÜŠŧmĘ7ŽYĻڔw˛QIĖÔ¨ę_t” A-‹Męęęr´LÁ`0ˆ˛0d'`†oöƞ1cÆ$U­ĀĮ–4ķc3î-X‚Ÿ?Ūëž2ƒäđ[oŊĩĩĸĸbũÕĢWįM:uÆĸE‹ĘW¯^)]ŌÕ2|úôéčž={š×ŋõÖ[ŊĄPčõ˗/o>xđāžtđØ8̎lFĮ:{Đ9æ›N°qTx€H ÍúAčHÆoÉڔšß‡Įšh !š››˙˛ŧŧüOįL:5ÜÖÖ&°ÔÔ _~|8räHtįΝųčG?ú͞žž˙SVVöë—^zéüíΊGH€H@!ŠB4žȅwr<31ĐËsīāšā ëo,i?ė´6ŅL&Ĩ8ŋ3xŽËhEšá*Ÿ…ą:…õ) 8E`Ō¤Iéŋ’œ*ÉųB„ i†üāw)‰L;~üx~ú06ĩYŗf-īęęrÔ}}0Œĸ,*xPtnß63,xķalˇĨu>e3‚ą÷~W&Nœ8ÛhžŒG$ 7F=údMMÍ-÷ß˙õčp7 J¯]ģ =FŽ9ō‘Į{ėöd2ųŋî¸ãŽ;6nÜø–‘t|G DXã]:XāB›‘nV•L& Kd˛ŌĒi“ÖÉ,ļ cĮ’’âžûîģûäɓ[īšįžH–ÁVē+ŽmÆãqE1Ўˇwūž={旔”<‡˙y׎]ĢÆ]Ā$@$@ĒĀģˇŪŗˆœŪB ŅxfâųęڐL&Q'ų.PH=Fß'’É$xŦŖ]F^c&3skZ>°:ukĶøĻÜ čĐųōbĨ! Ōģ@a-{öėŲhaWZsUKKË4UëôôôLˇĻÖÆSíîî.Ú-˛ņܲĮÔ̓=ĸgÍQķ)v4Î'žŠqCĄĐ|ŗū÷.\¸QąŽ, @nßøÆ7ūn׎]§îŋ˙ū[đƒžĶĨŒŽx¤ņÕ¯~u>Ōüž°-]<ƒŠŸŨBˆšĩ:ąÖ !Vjĸ3:Ŋ(<ûčĻ`UIĀøų[ŗYË–Ņø-Âŧ—°ôōlA.ÆÉd˛f͚5¯Ÿ?ūį˙ôO˙Yŋ~}&/š’ąŽ†pŊ˙ū 7ŨtĶĮ—.]z~ÛļmæŧH€H€T"€wrųÜÄsÂ-Ūáņ.īĒįe2™lK&“ōû#›Ĩs>üņ]Ķ•L&ˇ$“IŗŌĖ'Æ%Ÿ1ôŅãV§ŧ×Õ'WĒK–,šGũ’VBDÁÂJîüUĒÍU‹i œ§2žv‹™ãKpíH$š˜é_#ĐŪŪžÜ ÷ŅųđŦ­­âļÔú Sē?}út[įÜ.´œŧŽHā}°vūô§?ũģ×^{í‘Ŋ{÷–ĄãÜŦËi¤ųŪ{īũûûīŋŋy™•ļÂé`ā§´.€¸ƒ4°€Đ|¯&6cŸbŗÂ Éĸ‘€ ĀÚb4ŧ,Čß"Ão–üŊ‚mĖí…Ë}å+_ųôŠ+ēošå–Eģwī.XtN‡aĶĻMâąĮ ˙øĮ?ūoögöBē8õŠ)/žøâ˙DžÅ¤§āĩņ¯›aIˆc蠂¸#t^1 €J¤ĢQ)Fã÷ V^訆Ĩ”´ôžiī‹*¸ũöÛųâ‹/>öÄO\kg+ŧ€@Ԟ:uj+Ŧ ÛÛÛY‘Ķ$ °…žz!Z>+ņîo%/Y˛¤•ĸ˜ĢīaO÷`__Ÿg?rēģģįĢb™ęļFÅšjUŋ[å*ĩūįoS힃Aģī!¸Ģnmmu•ëĨÔv3S0öúTŠė¸On&đÁ~đé˗/īÅ_ŧŪLkįLL`ũ͟ūôēēēēĮĐéŸ)ž ŽCXÆHtę@˜Ag :^`M(Åf¸ļE  ¸€ŪĘKZzá]WūæÁ’ ėnŖk–-[ÖõĮüĮ1ÛÚ9SCK+螞ž›››˙2S<' p Ŋ÷~|āģĀQ!ÂŗV|ĢØ6i‚ˇy2/ŸđŊø,„Xhw'¸Īī9V?yķæ•e8åúÃĒšŽvĐx<^ĒRyëęęĸœŋ;s‹TUUŊ—ųŦũg‰„r–ķCCCķ¸‡ÎŸ?īæ>ĻziÁ{O,Ģŗ˙ŽdŽ$@ų€õq$šķg?ûY9,“í Ō zéŌĨšãŽ;Ü2Bĸ Ę —´čTBgŦĨå愃đ ×ĩ $@$ā5č`GĮ:×H1éÎíø]DĮģōáŽ;îøŲÃ?\o•ĩs&°‚ūÅ/~QõîģīnöÉ™Pđ8  xžđœÄ3R Ņr* <íûØēö­â”qÄZÍÕˇ×ڗõQ”€īÅgX›ŌōYŅģĶgÅǍ¨¨˛ųagá… š6WŠm…V$Ŗ`0Ri>¯ÎÎNåÜ8ËĻR`žåš TËō8ŊÆŗ VÆN—#5˙D"a{™Ü>Č îĶÍŦÄ˙ŠŠŠ5ŠmÃ} u Ķû7ŋųÍwđƒ8æ&ūŋûŨīžēcĮīĀ…žUĐq ?ˆ/™ŅŠŅ.ĩ9_ŗ:ˇ5KB$`übĀüMDG;:Øeg;ė8Õų­güęķŸ˙ü—'MšÔj‡ˇņš‹‘Š-ž÷ŊīMܡoßūtįyŒH€HĀõ¤[xN"`ž– ҚåąĶĪŪõÉdõd Ë xÖŌŌ(šéͧWËx$`%H$ņ ĪÍ5wâĉJ+Ų1mæ[Ž—••Ų.Ŧfj}¸ŨƒŅLį:~ķÍ7_ą;oˆ­ĄPhžŨųš•_ooī]fZ‹CČ…BØÂ@$ (îîîũâŋxŊĪŠ(÷ˇžõ­˛ŋûģŋۛL&c@Ā)!–›ņž,­öĐi$ŨÎĻû$@$@c ā7b´ ōww¤‡ ųģęXÄ /ŧĐúČ#üíŪŊ{e9Yc Š={öÔ­\šōé/|á ÷8RfJ$@$`|ßāųˆĪF<!DcĪC fÅķąč ÍšŦŠčģ%™L ŦÆ@–đģåsÍÔŠS)>[v{1á|`NŌĨK—zŌ-įÂqß' Ú\ĩ ß/Z[*͡Ŧ ™îînåÆÎÎÎ)NđQ‘…QŠÍôŌ!ģģģÛĩbŧQnŒGn%đũī_:LŲ IDAT˙˙đ‡?LsĘęKĪ đMMM5_˙úר?nņ6ftüžčÂ1tūČųšaŲė˜@bqũ™<  XMíøMÅoŠü]Åo*ú`ę,ø Æ>:ām Ûļmû=99đJVŪ?~öŗŸĩ+ęũC“k  ķH!ZNaŅ´đLÄZ‚Í;Įd2‰kņ\U)ā;‹,%āwËį¸fmj)d&NF@X¨ŦŦœc$ŽËâÄkkk›\Æ)cqUšĢwzƂúü\#c°€JŽČO:ĨR‘„Sķbcā„j,Œ6 „bX˛›fĪž=ėVfr`Z$ ¸ÛŪĩkןūô§?UĻhč€_ąbÅŋCŲ6nÜø–CgŒŪ˛=ŌeŦ)–”™I’ €×@|Öꁡ ü6K¤ø]Ƃ8Ļ{€ģí?üáQ zR!@ß´i“øæ7ŋųĪBe§žRË@$@%€AZX¤§|ˇāYˆãX ŌËˆŅøvÄ̇ûí@  ÷ŒbGžĖÃG|->ÃĘT5ĄĀG÷̚BÂÂųķįŊøQcÛH餞؅{\•æííīīWöšĮK;::kw¸Fv,ķ +āŠ<ĩdõ‘ÔƒvėŸ9sĻΎ|ŦČBąŲéöôô˜Ģf›]@ĻG>%p⁧?˙ųĪ_§‚Õ—lé~{ãÆ˙"„ˆÉãEŦĨ 5ŪĨ  <Ķ"ĘÉKI€HĀĪäoŗd€ßl,°ÜBįģ(1稁B5xôį?˙šĖK‰ĩæ~;‡ŋüÔSO}I‰Bą$@$@NĐ ŅxJ/M9…čd2‰øĒ¸ÛNeQœâs*î›F@YÁ´fI(4šéÆ2KV{ŅĘÔ’ģ„‰ÚF žž>ØŌŌōAÛ2´)#ō(4æĒ-.ĶŽÆˆ>e\”;ĸĒŨë ,€keĮųÔöĀ`‡šQl­ˇ‚`ú‘Ô6â> €s.^ŧxK<ŽîŖļžž^ ÎI!„ßUŊ،ŽÈ˙{SÄæ”KšK$@$ā!P„ß~)F、G€„Žx,RŒũ>ioo_ôáؑiyŒ˛‡0^VVöįFã3  €/ `PčQ!zŊÛ]@"މä.(*‹č6~Ÿë5+SˇĩËëapąė5KáP(4ß)KG¯Ü*nžĢÖÎ6Ā˙O{{ûr;ķÔįĨ…úhąĘĘĘ`A§”’a…:Zá,œ‹Å&e‰ĸä)ÄVü†ĸ†‡‡'+YiŠüI æø€Ōīh–Ûoŋ=K‹0tÆCœÆˇ *ˆŅgĪžũēƝĐB(ßÛoŋŊĀķ­Å ’  ˜E`DˆŪģwīYŗ´8Z?[ دÉûV|^ŧxņ°2õkÃŗŪjhiiĨĨĨ‹Ô,]aĨęîîÆ<ŧ EPÅu2„;+D°"ĐŒģ´ŗŗs6˜3gÎÛ6eåÚlęęęĸNŨCœ‡īr<ÄVXõŖTšOŪmíÂō’€ŲîŧķÎ/fę|ßŗg8r䈨š3uú°kĨØēēīûņąčãoŪŧyä˜>.âœ>}zä¸ŧ:Ũ1yk”qîÜš҉ pŗŠ ƒ…H€H€˛ĀĀ$ PÂ`%)F/LįrĪŖlĪ>dĸĻa×`)äų—.=}E´bŖÛúsÜ&   Ԋēv?ãŌ=SĘ /% $`:ߊĪW¯^ũ:ĸH@5ååååĒ•Š˜ōP+†ŪĩkU™ĢvÖŦY˝˛Z-žĸõ)”””(76´`žeëko,‡ÎÎNG˒H$\gųÜ×סČĒ÷•p8œ4ÖrŒE$`5ÁÁÁÛĶužŖã}ŅɎ}}HídGœšš´d'âK‘Yætp Ž´åļ\ëéķÂ6ŽīŊ÷nBpžæT8Ü' Č—Ā‘˛˛˛‰xnéžGŲž}ˆkæķ/]zúō`Īa K=Î}  HG ™Lb*ĸą8¸‡˛ų—éû0ĨĖnœĸ.Ĩ ÜU‘€oÅgX—ĸSžT#0iŌ¤q&ÕʘOy.]ē4;ŸøŒ›‘€ã/===Ķ3–NøM…BŽYڟ;wŽB ㊠’+˙H$âØüå¸?"‘ˆŖâ÷¸Æ1p ŽŽî˛hEšpáÂԂ.äE$@Ļøío‚č›plíÚĩŖ‡S;čĨ˜,#ā=/õ8÷I€H€H ņwBŒ|ƒeûžCZf>ã2}Ļ”YŠéúRĘÆ]đ­øė5ëR߃,z ††tčyÆŨEYJš›'•æĒUŨōy`` š'^Ķĸ×ÖÖZ&šVH‡*))qtÃŲŗgģ? EoĨ@ĮKĶÄ-´ŦŧŽH p••••éŽFgŒXG'„ŒĶÅÅ1)2c"4öå6Dæ 6ˆŪŪ⌖¯ģî:žÛPå  " Ԕ••›/ßgĘ`õķĪß@ °°Čúōr  ˙H+>ÛũŒËô|LmÍR;õ0÷I (žŸŊf]ZÔ]Ā‹•"€y8—,YŌĒTĄ /LMss폑˅'įĪ+U™ĢVŖNÍ×k´å‰Ä¸Î Ŗ×¯&*įvíå¤5x*S§Ŗ8iyĘÂāžĨŋĄÁ`ž8ÂÕ`c0 8E1Y?eļō@Ŧ^ŋ~ũ¨X­]o4léķ   XI gĘÃįŸ•­Â´I€H€ō V|ÆõN<ãRŸiꑱŧiâō "āWņšMŗ.5‰‘HĀN°rBxeDm<ŨHf;yz%/æĒuƒÕč„ Ž8ÔæJ x°TwŌ<Ĩ-,RSōJģ[]]=9í uZúęąÁNęļ"KFEØŧyŗĀ‚€N éF;[’—aåŒņq=:ôiÉKhXtĨ;&ãpM$@$@v(äŲ‡˛úüŗĢ^ˇH€H€œxÆĨ>Ų $`_ŠĪ°*UŨzĪŽ€ų¨G÷f__ß"õJ–‰bąØ$MLĪ˙b^1JĀ­sՎVĀÆšsįÚ˜ÛØŦTuIî 5øX@BX*¤Ļf–n_ÕyšĶ•Įđž‚˙ĢîŲ+WŽÄŦJŸé’ O2„aÄ’aÁŒíXėÚŋîž={F-ĸq p­eåʕ#qájMρ8ØG:¸éf:V| ˜   äG ×ŗŠ™ųüK—^~%fl  G í\Gv?ãŌ}Ž+éĩi˛!.“€!~¯kaccŖ!@ŒDN˜>}zÅŠS§œČÚԔÁėį_jzŠõÄŗxhhčwŠĮšO$@$@IwÜîg\ĻīÃÔ˛´åMĮ}ȇ€/-ŸaUJA,ŸÛ„qí&0uęÔ ôڝ¯ŲųõôôL7;MŋĻį ;éQä%%%ĘÍiũ>ˇI€H€H @{^xá…wŌ]›éš•.nļc™ŌÉõŦKM–ÖķæÍ;˜zœû$@$@$Āé ĮGĻ;Ę÷9”.-ŗžqBˆŒeM—/‘€QžŸaUj㑀"‘H•BÉ9dķá …BN‹Mų”Wå¸3f˘ätųÜā˛¸ŠŠiČiNĒå¯JģŠ0ÅiËkÕî ”§ēēÚõTäĘ2‘@žfΜš_ēÁÎ÷Z;ãŖŒˇÜr˛væÉŧH€H€ŧK`hhč=7ÔĪŋįž{î?ģĄŦ,#  €ķ€›]7•ÕųÆe đŖø\ŖY•†Äˆ$`7Xē-]ētŨųš_ww÷|Š=æPUE@4§6ÖĨRYY9MQo]éSÆ@ Ģ-TĶįœûhmmmZWŽš¯47† ƒQ0ĻŊŊ}šš5ŗ.5;„áŌŌŌrëjĀ”I€ŒzÜ-âķđđđ˙g´^ŒG$@$@Ų˚5ë%7<˙Ž?ŪO˰l-És$@$@iėLsLÅC{T,Ëä~~ŸãķæÍƒU) (KI pũÄä*¸ŠVļ‘ķ,æĒÍķĶŖĢPƒ•˛]|Æ@ ƒeŗ=Z4…ģtĮ­[UŒŌŲŲ9×öF(0Ãn¸Áō÷´††ÜÅųķ-°~ŧŒHā}ģvíęxå•Wēß?ĸæÖúŸzęŠgÔ,KE$@$ā6oŋũößĢ.>Ÿ>}Ū‚^s[–—H€HĀqnuŨ"’;Ū ,@~,īÔ˝8ÖĮ†5)Ũ[Ī™9GÖÂWŽ\Š..į¯^¸p!į5Š˜Ģ6Ž•Á¤Y“ -í3ru܍ŋ ƒQfΜ)‚Á [~—Újkk§dlQ“N€IKK <0 8L`Ú´ioĸƒ[Հ9ŠKKKér[ÕbšH€HĀ…<¸›T.úΝ;Ņ ūIå2˛l$@$@Jpƒø|Äe.•lh*=߉ΰ&ĨøœūfāQĩËEĢk|âĉJĢķ`úļpÜrÖHMņûžjÕ*Û-[įĖ™ķļ‘ō9GA~î\ۛeîYŗf‰ĄĄ!e­Ôõ^ŧxņMvŧ¯€Iooī]úŧšM$ā tloŨēÕ™Ė äŠ˛%“I7t ¨ Ŗ  ¨B›Ž9ĸJqƕãŲgŸM:thû¸<@$@$@YhĸŽęßOĒ—/ ažR€īÄgX“ĒŌ¯úÍÁō9K ššyĸsךYëh4:lfz~N ķ ˇļļ:jŊ I7„“'OÚ>§īĨK—fĢĘĻĶ÷Øtvv*1 &‘H¸ÂōųęÕ̞ã}÷æãVõūešHĀO:ôËíÛˇŋŖb<,˛Ÿxâ‰Ū_˙ú×?ôS›°Ž$@$@Öxã7ž˙ĢŋúĢ!ësĘ?‡Í›7‹sįΊë–$˙*ņ   { lļ7ģŧrëB¨\žŧ*ÃČęđøėkRõn#–Č õõõÁŋßaEÚ6ĨˇÃeŦMuQ"›ķįĪ/rĒ íííËa!ɐž@Yú3juōŪ‘TŒrķÍ7_‘eRy‰DĻŲaų [å9ËUn#–L$P/„Ø"„XßÔÔôWķ7Ŗ\ü† ÄôéĶ×ŖŒZY9Wŧ‰7“" ŸX+„xõ… å555˙ŒgJ¯ūå_ūĨûôéĶhĪžMBWxS‰#ËB$@>&Đ6?~ü%<účŖ‡ĀˇŋŠČ–eēFĀoâsŊfMĘö'å @čƒå›ōÍ\@~”ef“÷§įĒíėėtŪg˛jNYgĢ"Ŧ@äTeŖ¨bĢ!Ξ=ĩÃōå˜={6ŊTäjž'kŒŠÎڈķuĪ>ûėSŅhô%•:āaõ5yōäCøG!Ä:­ŦĢ…ģ…Ą­š7˜*  x™ĀˆčŦuv¯Bløå/y÷Ë/ŋœPÉûžÅú§ú×B¸$ŊW[c°Eh/ߝŦ  OßHuą^ųGôGŸ¤é)œūÎwžķ€ö}'ãû”L#ā+ņV¤°&5" ĀÍriiŠc–ŽÅV­ĨĨeęĀ`7ÍUkN Kœęęęĸ…]]đUõ*[ųã˙PˇĘĘ FŠĒĒz¯ā–ļņB;ឞžjĢÆŦH€„': !F]zūđ‡?üøúU耇ÕץC‡Ž>öØczo<(+Dh,ĄyG“  %0NtBĀåįHXˇnŨ:U_ɁW>øā6Y>ŠĐ:Ü$ HGĶ%b€.ž‘đ­—Ŋ@āˆ‚î­7<ƒåˇŨNÍËY)͇—2š‡€¯ÄgX‘ŌmŦ{nN–TˆōōōrˇrčííŊË­eWĩÜNÎU ņŌ-ƒ :;;§Ų܆ʏ tÚ­ōĒUĢæĒr˙466FÜāRčʕ+íē—-[vģx0°:#FÜkKKgŊč,ķE'ĀמöĩŠā~ûĄ‡ēíļÛVk˛ˆr-;*(BK"\“  ¤#Ut–@č;wîΜ 1øë™gžéOx%‹‰5-Ąõ4¸M$@$ ãy'鎎ž@ !ĪÂæ@ ąYP6x#‘"4Ŋ|čépģ žŸaEĒJįwA­Å‹|G`Ō¤IŽid—ËXŋÜ ĒëĀĀ€ŨÖÄNUĩ |r÷]Pa¸čäɓËČ6[–Ēˆk"yļ:˜vŋÕąXŦÉ´™ @*¸[ÃxtFlÖ:$F-S#c˙öÛoß7gΜŋw˛yWWW˙Ī}îsΤ+ŖîEh n’  Œ0$:ÆB<ūøãĢ~ũë_ŸvŌûM:õļ ¯ôÅM'BĢūĄ/?ˇI€H€Š# Eg 06ō‡)˛~WCWoՄđL‘åŗ ÖÚø†Ĩ‰į$ā+ņ9'saPˆ@CCÄgWÎĨ …æ75QË0ķv:sæL™é哖“V×ų”qíéÛÛۗĢîUcΜ9oįËŅėøĒ FÁīĻ0ģ~fĻ‹Åęėä&áp˜Ū*ĖlDĻE×HŅYē]Ã(xÝ ˙ø˙øč /ŧ°ß îFõĢ_Ûĩk×Ē<“"t°•H€|Ûë¯ŋūßWŦX1d‡æxFĮûÁƒÖŅŅqŗž,ylS„ÎŖ’ €‡%:ë8ô>|xîÆOcDaĢÃΝ;1@õREEÅį~ũë_˙§탸ŦwYJē@ŧŒH€% Egˆąō7kÃA›˙ĸ.„`;,ž—đę‘ZĻ­Úŗ Į_ĨŠ‡ûŲøI|nĶŦHŗņā9PŠ@cc#ĘŗPŠB,Œ ––‹ęšhNÎU;wŽōúęh;ö÷÷—îØ´a§•j!Ur‚‰žœ* FQŊ­Ā­ŧŧ|†ŽÜašß×סHßfÜ&(ˆ€iĸŗ>÷įŸūÁ}ėcŸxä‘GVZAÃÚYÎņœ§Åŗž¸úmŠĐzÜ& ī€č ל_aUUŦbÜûŌK/ÅŽ^ŊēuåʕÂ*+hÛ÷Ū{/ŧ}ŧĩzõę?Úž}ûãZ=ŠYIABΛIēšŧ–H€Ô °^{ÖáĪēŧDg}€LÂŽaC €Žb‚ĄáŲĪ|Wzj-¯ÍŸ€oÄgXÚiE”Sđ OĀ͂€Ķ––ãizæˆ#îģ:;;§¸…`mmíe;ˉDĻŠū|‰FŖÃv2IÍËnWčŠųë÷ŅVh3ũ1ÕļģģģįÛíĘŊŽŽÎÖ˙՘ŗ<$P$KDg}™0īōūũûgôôôėC'ŧ™VаvFš¯ŋūzĮŪŊ{#æxÖÍČļ^„+X °ŗÂ9Æ! ĩ ¤ŠÎč˜.VtSãī~÷ģë{ėą{žųÍov›m kį+Vŧ÷Ą}č‰üāŗ7nÜøÖ˜Ė‹ßĄ]ÃzTŗ"õx“˛z^#0}úô 7ÖÉiKK72ËUfXBÆbąIšâųũ|4!„€+[ÂŲŗgŖļdTD&ĩĩĩ<āČĀÛÉųĘĶaSŊ͜‚/\¸05+#ČJĀrŅYŸ;:(~ôŖŨ~Ë-ˇ´>üđçaŠ\Œ+R\‹4îŋ˙ūw&OžüąŸüä'ˇā†M_Ä\ÛĄŅYvVäĸÅķ$@$ .ŲÂú˔ŽøLÕmnnŪĩ{÷îigΜųĘmˇŨ6ҏ˜ §˜xōÉ'Ī}øá‡˙ĸ˜ô \KÚ$F! ÅŦÖÜLŖ/ bĢ%Īē@ €t!jC 6k×Čāß@ °2lĄĨ=¤o;p‚%t?ö‰„Öt*’ 7ЕtęŗĪ>›b;V__?ŗ°°0ˇŧŧŧWŸ>}üûõë×CÔETTT|÷Ÿ˙ü§ģÉdúiđāÁ_TWW?‡%&ėōĶc—Hh=P§2 B€čņ[¯ČF¨ĒN:ۋc#Ąa”ûjKK u!ô4 wÅ}ÜŲ 'Č_üáŦ'o ’А•ŒĢė֋÷Ŋ…|ŗyzqSSÕyE듎5júž}ûôœD\†!ĸ))‹@FFFëZĩ‡R6ãÎsÃK7 cáɏ;ωĖŦ…”vTi`’››{åÆõxŽ0gŒÂø˛ąĸÂĖQ[Ēu Ī—ÂÂÂĖ/žøB>ĸVĩ(_B@)đŅ/Zŋ#䚿ŠˆÎ*˛f͚M‚ ā'9r$lÛļmö.`PN0'ˇ­^ !ÛČēŗ†Ļķ„!@h‹K¤ŗŖšWmŲ˛Vø!áģÕūÛĩõŨļ°°Đv SȆŪ3HYĪT͐0„!āE`ŪŊvÕ\T‡âü IDATŲ%HmD4<˜ĩōbvI>™‹ņÍ oqĖk‹%>Xũ•ŠV ¯ ŸãââÆÚŧG•ō!4C¤‘ÉdJŌŦ@ ĘÎΞDaîŌAz­UĢŗįŦ$äAVŊ“ËKĨ˙™’’’zHC֌Ql!Ļõ€ŖĶ2ņüŦGǝ¯ĶŖ\*“`)é E/͐“GC"Ą $!@x ĸ!æ@x_i^Û¤áy…oI$ĄĨ…-å­I^B€ā ņ™‹y†°<ÎŦb.Îkx—ĀzĐØĮŧÆË÷4̏r'Wî$vC` đ(<""­žž>„'ŲËËËYiË”íd ŦowPåS§NĀÛ3TËđä<Ŧ‡­§ņæ`֌QlžÅx f.áųŠG{đöņņÆ $! x>ŦĩYlƒt†2‚>”ĩk ‘„öh‹/lžįÚI@%„!ā}@ų/%(‰Ą€i#ŸŊ ũj Ü1÷AA/|ņ~ˆ*”ŧ¯ Ÿá5ĘqâE}Ēę&“Ч—1sIhhh(9uš#**Jķ5Ÿ=zĨ:ĩQ'W‰ģTÖÃÆx´X,ēŦÛ­Ĩ!€ŗ=Jã>âŦX­×Ļį§KŅń€’éŦ$šžį%’ĐđēƒōHhĪ1ĨB€°G@TÂélŒ~ûDBë‡=•LÆE@œī@:ƒÅ7ļ”ÔG‘T@B# mÂ6^A>Ãk”eEŗ7t4ĒŖg D,O¯eeeƒiĖyÖærwŸ:u*@î˙‰Vŧŧr‘ô ŲcŊÂHË5 úÂ[˝×ķx÷îŨ5n Ö7$$„›ū,ĘL[B@!ˆtVH•˛!Z%`)[B€đjD%<‘Îėv"ĄŲm’Œ øB@ŲžÎD:ëĶ~ ĄŋhXŒīpJFĀ+ČgŪŧF Üߨjn"k1bDŽ›ˇk~›ĄĄ5¯¤NĻĻĻúh]´Ųl6ķæ‰ŠŅĘâzÆZ÷Îʃ1Jg×čqžÕåΝ;­(3<<Ü+Ū õ—Ęei^›Év‘ŠHh9dč8!@Î#@¤ŗķXąr%‘ĐŦ´ÉAŧ!€0ĪddÅVĢIŋé¤FālIIŌ(‚€7(cĶŌŌ"A‹2!tBëĨ666ĻëTŧËÅ0Āå{čį0›ÍĄļõģAĢĖfs y˛;˛_ŋ~W˛ļžącI!11ņŦÜ95ŗhŒc xdĢYowķŽ÷s÷^Oī‹ŒŒÄdyę)t?@ ΊļĩiMgZíWĨ ŅjžÖûúG„€#ˆtv„ _Į‘Đx—ĄD„!Đņ{ß ¯ [Jl! ~ĶŨ-ŒÂa$@ú(ļÚČci O>Ã[´oßžEz"€ĩXTO\)줤„Ģ5Ē]Š›7^[VVÆ$I×Q[ÄĮĮŸîčŧRįXõžuTŋãĮëâQĢĮ:åŽę/=c F=˛ĮY,–`ŠŦZū)?bĈAZ–Ie#`¯„ ŌYãP¸8QaAkB+ ,eG†B€HgC5gke¤$4ŪmHaoŧ6Ļ„€{ˆž´"éŒī=Jl#€o:Đ" 5šŅ~” €€áÉgx‹ōâ•f€ūDUPˆˆˆŗW4똘˜‹ŠfH™]B†™™™dŨ| Įēté希ōGÉ+ŧcLiōŽņ‘žņĢgˆ{ôežĸlHąŖ˙„@'éÜ @œŸ&šķ$ņ B@ˆtVVĻ2IhQaO$4SÍC„€†€tŸx&’‘ą†ā+T”8§-4‘Đ Ģg6†'Ÿá- ˛†!Ā;---Ŋ9ŠCĒ-t+'âō'fEE…Ļ^đz…kö¤e´"<ŖŖŖcô$ ]Á(99šÉ•ë•ēVuʝ‘=%%ĨŪ™ë´ŧįžÆ čË‹…—šFËĻĄ˛øE€Hg~ÛÎɉ„v5ē‡ Œ†€H:Ciģ]„‘nÔhMÜŽ>ĸžHčvĐĐB€08đ]k ÛL¤ŗ1s"[­ŗЋAĀģ %0<ųląXü9l™h‡@bb"´<Ŧ}@BģÖSî–0›Ífårė<§ĒĒĒ>_åW”””pCÔuīŪ˛jí5Ÿj[§œšÂâōAAAũõ4˜ņ}âĉæ‹"\G€Hg×13ŌRõ‚ú%B€ ŒŒ€=é|ƒ|6rŠnm ē-´GÆE¤3ŒŦæÚŧœA<ãHÉ8Ā€$4ļhk"Ą9l[_evIäĻĻ&"Â\BŒ.fx¤92ķ‹/žĀC—Ų”‘‘Ņ[Oō„Y` Ë455 V(;§˛ŠŠŠánްZ­ApĒ~ž\Ȝ÷l'õŅzNÔēŧNĒ˙ëi, EųĩÄÎ˙UVVZ:ŋJŨ+ú÷īņ›ožQˇʝub-ËuŊ{÷ļJ­­­=QZZ Ģō"éqÆūƒ`ÄOüP IÉ{@ûŋjûĄ_€„†=ŽąšbÃÃÃ=H*āéͧ ËĘĘ6‘RMŠ ũ'G 566ö†îŨģ÷•æ\\\œo{v°:§ā}Ęw(âņ|cZ_ Ŗū̆€HBŖO oā‡0Ļ,÷qŧ)  Ą}qqņģ6šYĒ5"eLhˆĀ´N’Žŋ .Ôũõ×˙dđš9$$žoŦ?×4lBC…š ?é÷ڞšÔŌŌbī4Xj2™ŧÚ(‚;BÁÅ^5Îæ-ęâmt9!ĀIIIj{’ĩ•¨ĒĒjrÛ#´§4uuum>ȔÎß>ŋČČČ öĮXß÷õõF˜ôUũĀ0`ëP\’^ķZ'ŦOÎĒ1Šmyx‚3C¤ 8P÷÷˛úúú ­û •§;a999ø›ØÜܜĐŋKjjĒ‹īų_SUU•PTTôĮƒV566īÚĩkūæÍ›Ÿe äĮĪ|XGŗ ͝āŅ?`•„ny.\ˆÍČČÆø kkģUUU•ˇ}ûöe8ųM}}ũÆ-[ļ NÔ×Yč]$¯ÄN›6mneeå(qüg¯3„ŌŌŌŧĸĸĸW0ū"""ūĶÜÜŧbÛļm̍4‘Î 4ã"0KB=:388øÎķįĪ_Ū+11ą›ŖņWTT”WXXØXUUUf2™ž˛X,[ŗf Œą(„€›`üĖ={öė𘘘0ĢÕę÷OûTTTtë‡~XwņâÅĶ={ö}úhH:ēUĮĐĐĐÁdôátNßtüøņh§/öüÂö揞įŠzzxųĒ^) (//×tũnŦOÎj[Ā#ûāÁƒŊwīŪ­˛Šd‘ĘB´ÅÅÅE;vŒpEĐĨLÚ pÛmˇå÷ŨwiĪ<ķŒ_g„s›m; ÉfΜ‰_×ĒĒǍģīžû­éͧ?” •^$a5 Ō™!ā*ĸŌ}IŗpÜ---a×_ũf__ßŅīŧķN‡ wš aü͝ ‡Đ­´´tđwŪšuʔ)Eīžûî$“ÉDžĐrĀŅq¯G`íÚĩÖ/_ž>999öŸ˙ü§[ãs&~sįÎ .**6{öė¯fĪžŊjŲ˛eXkRí$*.ąÅ3Ėđ KĩõÂüu#ĄŸūųëV¯^ũFzzzÄâÅā \O˛,xŨēu+**~ĖĖ˜˙â‹/>ãznt!ā=<ņÄŗļlŲ˛xÖŦYÁ6cF—+ûđ[ŧxąeɒ%y]ģvíĩ×Ο?ūß\ÎĖų¤ß|#ŋŽô`ˆ '`y0UߍZZZĐņ†÷0Ody=­ĨĨúŒ‡šģ‹'Hą~oYYŲ`xzQ"Œ‚@¯^ŊäÂ8°RĮ˛˛˛PVd1ĒÁÁÁÍF­›RõÂŗ?77÷JĨō“Ë'55ÕGîkĮõˆH€õÉYž‡YZ&„/ D=Œ‡"""hųÖ°BōŦZĩ濌ŒŒķŊzõĘÜąc‡[ÄŗŊ(đVYģv­0uęÔa7nüfųōå¯Ų_ŖāžH"KÎøĀ¤Dx‚úhĀ}L•ôÆo<™]vã7ŽÆ˜éČĶËY@Dc,[­ÖŅČ{ãÆ÷:{/]Gx‹-Ú°dɒ/,X âK‰ņz׎]ūūū3¯ŊöÚÚüüüL•0…’ķÛ„ķ D<Ģ„´÷d+’ĐčKđ&„Ōž}Ėyđ€ŅŒ?øāƒ÷^~ųeˇ‰g{Q@‚}úé§]OŸ>ũ—;î¸ã[—Ø_Cû„€ˇ#€ņ‡ņqđāÁ—?øāˇ‰g{aųöÛoG|úé§/˙á(F9ö×x¸÷qx´"_úæķLƒßÃ[Đ0Ä<†ī9Eįŗ–––Ô––ôGŧ‹yJ<Û7Hčc---îYeŲįÆāžĄÉgx‰2ˆ9‰D¸ĀƒãââÆē7ÆĮĮŸÖ ¯.aqU˜đbš‘‘Ņ›Õ5{ ,9XRRĸö‚ĖŠfŗ™c ŠŅŅŅ1ˆT˙ĢõúäŽTH2ž#ųĻŗ@ԃ÷÷÷§Pû5§įf˘ąųÍ7ß|kŲ˛eîzœtTu(wėØáģgĪž;§OŸūĨÂÆrD:w>S) åŠ)Ō0&V¯^ũ§õë×ûaŦ( |ũõ×ũūųĪūJ~Ĩķ§ü^X°`AĖu×]wŦĻĻ&wÛļm†u§ž˜Sīŋ˙ūnŗgĪŪ6{öl(&•JRŌYŒôAŪÎJĄKų{ĨŊĸ$ôœ9sÆĻĨĨ}ŖGŒ?wĸítÔLĸäÕW_ķÔSO}>cƌY]OįoBāĄ‡ē †‰J=Jņƒ$ÆuŸ>}’'Mšt Ņ ¤įŨü/’‡˜˙@:#Ė6%B¤ķ>ļ0ŸyLˇ´´ /ÚjG ‚[Cg°Sõ#“Īa6/QU¤Ė -āÁ#íüųķũĩÄċËōxu솆ĻœŠƒŠ×(Ļ”VQÆ6Y—””ôns@åŦOŽrngį)– p;…o eÁķ8"Į(\=ĘN_b“““OFGGį¨Ąø“VMT zȐ!'rĨįŨøO¤ŗ Ņ-!aũāEƒG$ôčŅŖ3GU1|øđaJx[ĘÕJ@( ”™––v ëēË]KĮ o@kĢoŨēĩøžûîSĖÛY7„†ŧ ­VëWˆt–šŽĢ…€TiōĮc:%%å…/žøbÛ /ŧŦ†ŅŖŅ˛ŠŠéåŦŦŦ-ŌsôŸđF0öīßŋ†‰j=J1…ä_˙ú×°÷Ū{ī_XĶ]z΅˙Ōį"2Ā“Ī%J„€Ģˆ‘=ЇđįmmgˇZé~Apƒ€V›čvSŽ7,ų īPx‰z„ŨL0†Č“ɔĘXmÄŠŠŠ1ôZōm*ĢĶǏ¸¸nZ_WW§I9J×ŪÚ ķ”ÎWšÚc’’<¯O./ˆĖ–– `iАččč 2Ņa>|øgoŧņF”ÚŠ?)4PrėŪŊ;(,,ėn`D:KĨ˙z ā1 Ë.]ēŧ‡°„ļ5š5ŠĘZž|yė˙ûßO5T–hR7*„p~tëÖmŒ‚Ö"Á¸sí“O>9899ų eéėht‹ĸ(BBįääĖĩZ­÷)ĩċ35 ķōō&í btQ@˙ŋúęĢ'büÁ0Q‹$.3dȐ;]$ ! Œ=á] ˛Hg-Ė;Ę CDŽí’Aą- 6îŅ:ĩŽ #І%Ÿž’H­Į•§6[__ĸv9äfĩZƒ<¸Ÿnux&jš&+kቝ€¨õ’ÚÚZUCLŖ xÃ&%%ĨŪYü”¸ŽõõÉYōĖîßŋ˙E%0W"ĘĘĘ^JäCyčĀ”)SūũČ#X”sčLÍ „ˇËųķį?ræzIF¤ŗ“`Ņeš!ā6 ŊmÛļO0´RüIÁ˜_¸pađôéĶwHĶBĀ@ČB~ŦXąÂ_ÍhrX‚ėÎÉɉĘÍÍ]/wŨq"íĄ]Ũp›„†áU}}ũ¯ŧĸdzįņ˜9sĻĐŖG‰÷Ũwßįīĸ+ c €PÛĄĄĄŲZ=J‘ø÷÷÷˙Ŋ!¸ÅyoŽm­^Ŧ× ˛! 4XŽ!ÜąuŠ„niiN}S¯OëWŒ‚Û°äsPPP"Ÿõ#Tޚ˜LĻžjæīaŪŠžžžfķ Û@@+d‹ÅŌ›×gŠÚë kÕNt§/)))Ņōųk[ŸÜių´ž%Īėōōrf ‹lFDø ¤Ä1O?ũôR__ßŅj‡Zë"`W]u•eÖŦY{;¸W°*†Õ;>AøQ"XBĀ%ë.§§§Įęaø!‚†ą3 Īņm o@ //oĮ<Ŧņ,â h??ŋëW­ZuŗxĖÁVTžCJk:;ˆ鎀#Šåu˙Ė3Īø9ŧ@Ŗƒ Ā:´(???SŖ"ŠB@w`øņÉ'Ÿŧ Ã+=…Áø˙øãß”!ΤķŪ­‰tÖŗąŧĢlĪđ„.’,­Ô›ĮąĪö˛ ô6B~sŸ K>Ã;”74î{U@ŌŌŌ"A`–`aÍRMBĮBR¤°"œ8qBUīa5ë`ąXT_o˜ú{‡-ČėsĒCŠõ9ËRÔ›ĩŸ>}A‘R׎];ėwŪšO/¯i%`y_QQaŨ¸qãŊŌã6Og‘t†Ĩ;ÖŲ%ŌŲ$ÚeNIhM'OžĖÔ2ÔŊJŸ|ōÉl<äŽĄã„€‘Xž|ųk/^Ļ§á•ˆ'žëׯ_á@/Užé,F[–’Đ­kR ‚ΰ6éöÛo7###XOÃ+Óyķæų­]ģöí6Ō!``Nž<™?oŪ<_= ¯/Æ˙ØącÃîšįéúëĸą1žũÄy$ %B@kĐ˙ w€ūÆī—æ2Ûûš–köˆtAĸ-oȒĐ/ŋüōK,Ī HĐk׎}Ÿ7€I^BĀUāõĩråĘ[Y0ŧ‚ėšoĩZũAČŲęB¤ŗĢJ×ŗ†€HB#J úķĸâ~Μ9cY1ŧhÜ{öėŲúÄ˙f­åHC#0oŪŧ…5551,^hŅøxöėŲI"\‰ž§ØR"ôFßr˜Ë ›į2„ÚfÍ cĻ#FŊąsŠ|Ŗ’ĪŠ6īP—Ā ‹ ŲÕØØ˜ÎĸŦååå}X”ˈ2j˛vo||üi^ņ;|øpw5e/++S•ÜVCvŗŲ ™a­ŽzâaMláĢ >jÔ¨é |YIˆCĪsVZÃu9f˘1‹¯/Qz(ā'Ož˜””tČfaL¤ŗmyG  œœŧ—¯/)¨PĀĮÅÅEååå-”§˙„€Ņ(..~›%Ã+ā <šˆˆˆŲÖ\"Y{ĐhM@õņڑЛ7oū+†Wb3`ümßžũ÷6rA*16&ŌY‡ļ,!Đú^~Yee%ÂĀŗ–DÃ}ÖärZC’Ī#FŒMĄPît!g€ đņņa2t^hhh(¯ësÖ „¨¨¨nZČÜĨK—(-Ęáą ­ TĀFO_ÖÄÆģ<´UĀØĨ,/^ŧ؃ĨĨBđgužq Xīŧ8l˙ūũ‹YS> )  8zôč˙ĨđÚŪŲ9 ^kĐ7%ąĻ|Į߁ū@ xƒ÷B/ŽŪøņãąļōVŧž¤M9ŲbąŒĻĩ-Ĩ¨Đƒ ĐJB5ęĢkŽšĻ‡Ūáļí1EôĨK—vĩZ­ŸŲŸŖ}BĀ(dee­ž?~WŊÃmÛã ãã[nš%4%%%ËūíŒ!PuæĖsxx8ĢFđ~fÍ#Ûé&4$ų ¯PŪBĄ:Ũbt!! øxõgˆ˛˛˛Á,(,b¤”L§N P*¯ŽōŅǜŽdp÷\rrr“ģ÷:sߐ!CĖÎ\ĮŌ5 ĩ"[ą.9KŪŧŽÚī đĐvtNËcMMMƒY3ÜéŪŊģ&Ņ´ÄŲʲZ­3fĪž˚ōAÄ~Ų˛e~gE‰0cƌynᅁ,V Ī„… §§§#ü!%BĀpüôĶOsVŦXÁä72đ×^{mdBBBŽá€§ ‚ øųųĨŗhx…ÆAô^ŊzÁ ^lę„€Ö444ŒaŅđ 8Āø8,,,MkL¨’‹đ“É”ĖōøƒlMMMS=Š#ŨK°Š@``` ̆WĀ Ä@īŪŊY įČj“’\ü ;hĐ H–ÅŊæškēåääp̏g[’M_õãēëŽ ÖWŠŽKˇZ­ŅŖGÎėø*:Kč‡@KKË4 ” #—ɐäsPPP—­ABN"˜˜ō™9íō€œŦ]æ)ļĩ{Uī !žĘĒķũ܆&Q 7­ČV‹ÅrV­:(•/ŧą\€Rųš›‹Do||ŧŸģõĄûôCāįŸN€‡Ëiā0l`[H–$ŲXE ĖæYÅĒ|đ<üũũû2+ F¸‰@^^ŪuXkŨÍÛ5šŒ¯4™ ҁiĶĻÍĩZ­]u(Úé"a|ÕØØ˜įô t!!Ā žžžw°lø1˙ßÉ ¤$Ļw" ēn]Xc[ZZRČGķ, I>wíÚU÷õ5oI*ĐĢa2räHæ,ĮJJJzzUCŋ˛ã|}}š --6‹Ú!ā­V+w†NXãXĢÔĨK—>Z•ån9č#X.ĀŨûē/LĢHŽČkąX Äåá%ܕjúÚÜÜ\DūaZųŽ€ŠJC7UÎëČ˲mĩZ™ ų+mŒ1cÆ‚¨“Ŗ˙„īTWWĪf]ųŒÃÂÂ,xÖđŪH~¨ŦŦÅjČ_ f^¸p E@hkΜ9“„o+–žįΟŋ‚eI6¯G€Ŋ/rļéPF$ŸĮŲŧBÛT”v#!””$˜ÍfæŪ0bbb. g–ë‚Ĩ233™ë,a#ŠS§ĒåŽĪ%15ŽCCC5![kjj|Yęr˛0°\@Ē-’œˆēĮø1bÄ ] §BŨB⧟~ēWNų°}ûvŋuëÖ9ĖÛŅų%K–´Ūķę̝^ēו––ļĮAû}šc—2°……ĸRzŒūŧ#ĐĐĐ0^Nųîh|‰õutΙą‡û?—ŽYąq ‚ŽŦŦl–¸O[BĀœ>}:Áųėh|Iëëčŧ'ãĪQ~Ōō` Cé1úOđŽ@mmm´Ŗ÷Gį=EEE˛īē6M" yīp$ŋ°aÆEHˆ˙/ņļŽÎ{2ūÄŧ‘‡ŖŪËŅq:Fč@KK æ‡sƒŖq"•×ŅyOƑŖ{Ĩåņęb8ōŪ P˜R"ŒŒ$ŧõė!NŒŒ$Īg{TTܯ¨¨Ļbö­Yké)ĢF]Ž=zĨųōš‰í* IDATœgYY™&aĻYôæuÔnQQQēFK ‹ī-đ ollLw„cææætGĘwQP âœø$­Üy‘dķ„BĮā=‚˙›6mjŗsö×ā˜}‚,PTÚ§}B€gŽ;å(äŊÜøB]åÎu6ö:kãrF&(ãųôé͚Ąņܞ$;_ôčŅŖĀrãKŧPîŧģãO.?ą}ú0VG.‚Œač´8 …ÍfŗĒ!ąŗŗŗ'ÁЁR{222zÃûœĮ¤‘§ī8Ŋyĩ׊S§t}–ˆDíņŽęŨŲ1ôī   ū]GįŲA ĻĻÆėČķĮđ!ƒ„˙ö 9šķP’ã'jP(ˆ˙Ąčxúé§ÛėãĖūs”’’’jÛŅ9:Fđˆ€Ųlv8—ȍ/ÔQî\gc¯Ŗą&‹t„ĄœŦŨCᆗœœb/ŸÜø¯“;īîø“ËO,[ĖĄUUUĒKˤ˙„€š„‡‡_ëˆüęl<ȝwwüal͜9ķRU‘ŋ}Âüøũ÷ßĢÍž8Ú'TGĀĮĮ'Iü6“&7žÄkäÎģ;ū/ž5É"–‰ņ7pāĀ›Ä}Ú !āP'!7NDšåÎ{2Žėī˒lĘ*9Īä_ÑĪđ%˛„ÉžFB)Œ@¯^Ŋ äj˙V­p9ÎfĮ3įlYēĪšĻĻ&U=WĘË˙_ŗˇŖ67ŠZ}CCÃØŽĘfų܀Ú|sŗH¨:jŊ=´UC†ĘĘJŦMH‰ēwīŪŨ‘¨âG ÎA1€}i’;kĄP|øá‡Ą,—Ūâņ˙=zİôãq…(B@šņ…ËåΊ9öPޟŸËbČ@J‡ vøúúļ3ȕ_âÍrįŨrų‰åŅ–0"ŅŅ҃ €ˇOšķîŽ?ȀŒŗ@r9"Áp>00,ëí‹ö ‡€Üø+*wŪŨņ‡|Å{Å2mÚÍÕŽŽŖc„€Æ´ŸÄ:øNeSsŠĄåÖck8ō™AoP=ڕĘôĮ VUU5Ų `gNJuuuôōÖA‹€ü4›Í 8Ouuuē†jö¤B%%%LJĮ‡×<‹Ą¤áfķĐÖ͂°ļļ–Ųq!Ā#öãÄžöįŨGČד{íåbißhäs؀ē˛0ÉB¨…ˆ€€€éjåīNžŧx:ēS7īILL<ĢĻ\ĄĄĄĄŦzd:[oĩÖ7†G5/äĒŗX)y‡^ķ­•ÄD.¯Ũʖ“I<^WW$ū§-ß@ Ĩ~öaˇQ3ûķK–,i%ŖĨĩ†ROLđ„žė˛ËÄŨVĪhx™Ø_ãČķäŌMô‡đėĮ—´Úöįœ{4Ö¤ŌB@ûņeĨũyOĮŸ}~öåŅ>!āMt6ėĪ{2ūp/~HxuôŽëMØS] ûņeˆũyOÆōƘÃzĪøF¤ņg6íķŠ€ũ8ą¯‡ũyOƑŖ{íËãußPä3ŧ@-K(¯ArŽ â‹Ĩõ8:–Č8WZĐķk?íy.ō9ÔÖÖĒâ5,_"?gāQÍĢą…ÕjUP9uÉyH0°ČĖĖÔÍķŲĮĮĮŸUœÁ6ę† Ģ¸°*—Édúɑlø(Bč&üƏßĒ€b ..ŽõrGįF ŽÃû°Ä…R J…gŸ}ļÍžŖkpĖQǍ¨øNecy;*ˆŽ:#āh|‰ãĪŅ9gÆ^Gc cTôúB9ŽRSSSŗŖãtŒāæææz{Ų/qėáZGį=Žōŗ—‰ö Ŗ!pōäÉŖŽęäh<¨9ū`\)žˇâUę=&ʇōëęꎋû´%ŒŠ€ãáîåŪ;Eœ/\¸ĐnŽĪŅ–Đ‡LZ#Gī :bĸhŅ&EsĶ9ŗĄC‡žũčŖæņ3ŒTüđÃĒę :*ŸÎr´´´@ŲąMîŧĢĮ=Gr÷Jd(5™LŋxH˛ū×PžĪđ%ĪKÖģɧ$&“Iõĩ[•7>>ū´ŗ×ŌuĘ ””%˯qP•ÉöR.‹EÕ°Ū— RņOUUU5˛76jā"æÉĶzäđІ§ļ(ģ–ÛŒŒŒ–=ÄûõëĮÜōZļoeųúúî"@Š[J2‹ųÚŗßĮuŽŽ‰÷ãŖJÎK[ŧ†ļ„oôėŲŗĒÉŲą‡˛:kŽdÁ3"00‘(FA ĒŠŠŠQŠĘ¨=ū˙­”Ŧ”!ĀEųųųg”’Óņ'w¯( * ū.îĶ–āC‡­AŋV*ɍ!GīšŽŽu$Į‡~X§ĻķCGeĶ9B z>wrėiOƑÜŊ’”ųؔd¨Å_C‘Ī~~~]y ƒĒEcSÆC ---‚•¨įΟīo<„š¨‘j!qûöíËũ2555žj´bHHˇ{0ŌĘČČPÍcx§¤¤pR kSS“.^ÚUUU“Q>Ģ Ä8Ö~gU>’Ģ-ŅŅŅī+Š€h›ģr{ ŋü…r9RN„€ūÄÆÆîPŌøC­AÆ>}úlR+ʗЁ€€€cŧŒŋ5kÖ,Ķ#*“P ??ŋj˛ž<!•cęX¯0Éį ”žÜ]­ŧĩĘ722ō‚e:u*@|ĩČFZ cÕ,̤¤„™¨ ÎÔS/Omģ,Gl1^VVĻ 1īLģŅ5mXŗfÍ&›b­í Æö@TWW?Į˜X$!ā¯Ŋöړ}ô‘bŪ— ĶÁ͇>ˇzõęŋup "¸C ššyäsIIIG Nîú Ŧ—]vŲvÖĮ"“466ŌzĪút*U]ŠY7>Æķ!44ôsua Ü XįŅŨÚŨĖ‹œm1ųœjķmSAÚ!ŒŒˆÆÆÆtęfĩZƒÃĢD@ûGDDLöĒJģXؘ˜Ŧŋ¨xhōāāāfEaęōēē:ՌPҘ˜˜‹LU¸au 1b—õˆ-ũû÷įĒ-;ijß>wîÜ×J…ūU ŦĪ?˙ŧlãÆÔƟō%tB ęØącå:•íTąđL+//˙ŪŠ‹é"B€#ļmÛļŠuã+ĄĄĄ_r+‰J8…Āēuë–|ôŅG N]ŦĶE ŋ>ÔŠx*–P ĘĘĘąnüųNŸ>ũĸj PƄ€į(ˇv™į˛Čå€õžšŒŪaō۟,¯›(×sč8!ā _2Ė“<ē7Õ××W—5S’ŸÛlÔ$“““›¸Ļ­āJ‡&KHHi[?{đ´UyãØČČHŽ<Ÿ?ŽK䔞={ú°ŪsĘËËšíëŦc̆|---L{Ÿ€'åģ-Oy˛€@DDÄ!–ŊO ü̝¯ßÆV$! 4•••e,‡ūĩ)ß_QēŪ”!ĀĨ_~ųĨ.†ŧÎÖŊ   fĪž=uĮYĀč:n(,,|Š  āËoŨēĩlīŪŊ;Y–‘dķnL&Å<Čč°#†|†÷'ËĄ+ĸO  bÂ㘌?hLŗ€ņÅbQkíŪÔîŨģĢ•ˇ‹5eîrŽ-āik6›cTD5VÅŧUÉZ/Oöēēē@U*¤`Ļļ¨ÜĩŠ‚p•k+VŦ8ÃĒū˙÷/\¸°„+PIXBĀIBBBųķŸ˙ˤĪ„_|ąŽ¸¸øq'ĢC—\!ŧhÉ6§^­^Ŋúû#GŽlä T–pĀĀĀ÷Y0ü8zôčōŪÉÆ¤Ë¸C ššų ôsž dxĖbːLxÕÁ1–ųŦwkĀû“ČgŊ[ĘׁŽ]ģĒ>י:eggOBhJÚ#pâÄ ĩHDÅCUkŽ € ĪČČ Ũü˛˛˛PģCŠíoŪæã¤¤$‹ ZŦą<,¨ZcŖXôŒĒŦVë€6™ö“åģöJ/Š2ÂÉûøøleuüEFFÂëĢĘKšƒĒée ôö×_Ŋ—EüŨwß-$&&ŪëeMBÕõ" îúä“OĘX[ú†WsæĖiÎÉÉÉņĸæ Ēz[ļlš~á…uŦãyđîģīžÉĪΧņįe}’Ķę>ĖđwŌĢŧ†ÜF_0Œį3+۟œ0›cAŽĶŗ ååå}ô,ߛË ŦWĢūŦ¯EëlŊÆ:{­3ׁ\ŠÍs˛X,Ē…FĢĒǚĖißҚ`å'Œ5j:ĪũŨÛdņÅŸųņĮ˙˒ʇ?ü°nÆ Wx[{P}Ŋ 7NEøC–đxėßŋŋl͚5yWkPmŊ ”””ŸzęŠ&–đ0F‰ˆˆ8°f͚MŪÖT_īB`ōäÉ3~ē{vÆ_VVÖ ,øŽŠHB@qĒ&Mšô kƏ0ŧēöÚkį+^[ʐP“É]VŊŸŲ íãd;†|fÁûĶIĖé2B@Q@ Œ92SŅL]Ė,444”7OGĢČėåQQQĒxžO:u€QÚTéuąAŽ2Û!œ,$$¤‡“—ēu¤Ē´bčëqqqnj%i9Ō˙ tychˋ/ĒÚ_¤¸Đe¸æškn„<”ˇŨvÛ2ejGšl#pĮw<Œ>ĪBÂ3Ī‚'Ÿ|ō˙ą É@¨‰ĻŧŧŧõŦŒ?Ą|üņĮMkÖŦÉVŗŪ”7!Ā<đĀĻ=z˛B@ÃđęđáÃį–-[ÆÆ„ĖB#‘ †Eā‘Gy°ĻĻæôēulDæŪ¯_ŋīæĪŸ˙7ÂN3" yY‹ĩÄd2•ō ļQČįq6īOžÛ‚d'ÜB )) ëˇĻēuŗB7••• ælR¨ęēgsęÔŠ5„8zôč•jäĢužÔ/#ŽVŋAiĄĄĄƒy UĨČ ŸDDDhjTBˇoßžR1˜üļljjˤp$”,7ÜpÁßūöˇËXPĀ‹^_ˇß~ûŖ˛Ķ BĀ@äææžŊ“<Æßĉ˙iĩZi­Yõ1NJ<wß}÷ÁÁÁßą €Įx­Ž‚Pᄀ†deeå<õÔSÍz‹¯^ũõVŸŠ"Ãë“ī!ˇ[‰n$:A OŸ>ĒxŋvRėĨĶ ¸ôŸūh‹€ŲlAqĪwŗŲlæÍ{Uų'NÄȝsįxmmm īÕ !‚ `ŊxœšëI+.Ŧ-C°)m¤Đ™ŦŅŅŅ:솕ķįΝ‹fE’Ã5ŪyįėĮ{ŦFõgĄô‡ōáâŋŋqMjēš0—]vŲ¸ûīŋŋF cū8_PP0ŌhR-į@øí”””9wÜqGŖãF'ĢW¯ū~˖-YÎKMWÆ@áˇ˙ĄWô]~üņĮ_{íĩ‡Œ(Ղp_|ņ™ŠŠŠ^}UŸĨk1î>|îšįūéŧÔt%!Ā&“ HŸAô ũ}ƒQ 8 A>Ãë“ī!ļ†IczõęōY"ÉŒJJJz:s]ŖЎŊŲlæž`U íēē:ŗZyk•¯¯¯/ę ¸ŅäīŌĨK­ęĄd9J)t&[eee¯ÎŽaåŧ-‚âĪVęgt9Ž;ö͔)SĘáĸU‚Įå5×\Sŗoßžũ{÷î­ÕĒ\*‡` 7úū”‘‘q^Ëë7Ūx㙯ŋūúŋ,áA˛Z"°zõę};wîüō7ŋųM– xŋéĻ›~pāĀ~fĀ AČĪĪ?ŊdɒC7Üpƒ •Æ9ÆûÛoŋ}ôŗĪ>Ûĸq•Š8B@qL&ŧŸĩS üZŧ?ގy`˙z”ㆠŸõöúä¸ũItƒ 0pāĀ⏏¸ązU'&&æĸ^e{{šđØĖĖĖTœDä5t˛ŖūÚŅqw)Ÿģr°z_MM/̞u$—ÆÛa<…ĖW+ÂBGíAįAJīĩMMMw>ūÁmÛļ­2™Lb1ö[‘€F4„ņ4ūėáĄ}ƒ K<‹õ{æ™g>Ą‡ŸŸßk ˜:uęÔ0Œ=üœM œ1˙Ô=zô‡>}úܸråĘ+WŽ”Ës1HĖÍmHšč8!Ā!ĸ—Ŗė3ūüŋ ‚𡠠 -›6m“‘‘ŒwĪÔTÜę\Â{§müĢĢĢ+™:uę[¤š 0æ0ö0žËŨHĮ ŽpH$–BŠq+÷ä3ŧ=‰|VŖkPžŧ!pîÜšh=dVÉ{RĒp[Ļ‘ÖgVŖBvíÚĨTÖa111ІņVJ0WōÁŧĒxԐėėėI0ˆā1ŲÂ`Cû¨úˈ\یvjkk°Āc?á@f| Ąwúŋf͚M‚ l*((ˆ=}úô˙Ŧ]ģ6Ģąąqā˜1cĄˆpDDCá÷õ×_7–——ôī­[ˇ.CXŅ/ŋüŌhD J" AŒŽá N‰gie@ ‚°ęË/ŋŒ0aÂÂå˗_áīī=jÔ(?9" ãīŗĪ>Ģķ÷÷˙:<<|ßēuëž­ôÛoŋ•f-÷ãĪ" åĸã<#ö s‹,ņ%­\AAÁ]‚ Üõ駟f=zôž‡zhTDD„åę̝ös¤ˆ‡ÁH¯Ũģw×”644|¸gĪžįDe¤4o™˙{D@ˀC‡šG@4üpjüåįįį ÆAAA7īÛˇīĻŗgĪˇ`ė90øĀø;yō$”˙ŕ••˙*,,\ņ÷É'Ÿ8ĐΠD×đŠ@§Äŗ¤bU[ļlÉÂ~eeåŊŸ~úéoĢĢĢG :4c/6ļ=&\•”””õîŨûHss튝žújÕW_}%ÉļÃŋx÷DęôûÔvmĀØBˇ6úA›!?é‡[ZZ0˙„†ą3K*ĖaČk{DDÄe•••DmōfĄŌjČĀ=ų oOx}R"ŧ´´´ˆāaå’Ļ‰Æ Ļpˇ+,11ņė7ß|Ķî¸'ŒäŨk ­ŠØŲ …'°kv/Ælmm­âķååå}4Ģ„Âiųã‘Č qÎ%VávĄė\BÄŗH0šrcéæÍ›a ߚvīŪš’’ō{AzŠĮÄmqqņ ?’ˆ€Á¤­‘p‰xļĢxéÖ­[oåįį§~ôŅG˙+îKļ?˙ÃÃ÷|Qé‡g @(F@ÚrҍÉĨõøöîŨ‹0Ŋ—BõæįįKIIšÃ”ÚÚÚĨĨĨ7.åo—Đv€ĐŽ!Ž1ÂZģlT(a…/ŋü2līŪŊŋ ŋÚ•Ÿūų?‡Zãáø#ÚXÚ7ŽĪmę[XXø’ øÁ°1lčĐĄŗ|||’Ú\ô Iũņɓ'ßsÁØĘ> n0LÁ<ī@ÍõÕöŅ>!ā˜Û@*ŖīĘ&Ŧ-íĶ---x'moÅ!Ev!ĩq ŧ§×Éfn ܓĪzy{¨PU ‚BŨŽ1bôūũû5ĖáÁGŅôíDĮWÚëŨŪŊjĩŠ–$ĨZu@žuuuŠ{˛Â›š×įä†įöŽ;Tˇ>ä‘ČõņņņWŗ?RŪ#ā.ņė¨āĸââb5ß%7Ŧ…E˛ 5Œ•#Ač!ĩĒō IDATĀžĪŽĒ‡ņwŖŖ “ĐPÆS"xFãO4ĻPbŪÚ^\\Ŧæ{ ƈ:ČLãįžG˛ˇ‰gđUŒAJ„Ģ â[ÉŲ“:ĀØŪՆOÜ)?Ĩ-/Ox{R"A!æãã3Lk,âããOk]&•ׁ¤¤$‹ÍbĒí ÚkEdhŋ~ũŽTŽŠŠ ÍĮ˜r;Ę#$$¤‡Ŗãž Ŧ÷ä~=īÅû„ŲlVÜÜAÆEFFļ gėā:ĻŸŒŒŒŪL E‰gąåˆ€‘ -¯€ø‚r?ŪĐŧĩÉ+E€WâYŦ0ԁ0Úō„¯Äŗˆ1Đ"´åƒcîpjuÆ*H4c Bâ´Cd0ŪĪ´‡2ŧšƒkōšK—.ãŒâÖŽģĶBĀ ‚‚‚‚Ü¸ÍŖ[Ο?ßßŖ čfĨPĖzSFF†Rr1‘V­LTÖI!N:āäĨN_6dČ-Č[§åqåÂ~ũú MMMƒ]šĮkGŒ1ˆGÃ9āSUU5Ų:Ķ=Ē!âš<_"(  ņáīQ|čQ"xAã}—įņG4/Ŋä”"Ā;ņ,օ0 Úō„t˜˙\^ã™ąJÂcTôeL4‡EãëÄōH<‹•"ZD‚ļŦ! jQc?GkDŗ†GōpM>CILäŗGíO7 Ž]ģ*ZØXjjj¸_;Ū•ú˛x-ˆ,ĨÂJŖ~ cYŦ' 2aMcŖķ‘‘‘”ÆôđáÃŨ•ÎSËüÔXÛ^~,"—ˇ„÷-ôŪä6°ŧPü!-ąmyŪ€Ã^¤”ĀøƒáĪÄŗˆ3Đ"´åŖĪ"ÖD@‹HЖ@|á] ũ sžäÚ+<Īxn,’ŊŒ?„æŨđ•ÁøC=(7un–—rŅr~ķŠĐÛ\“Īzxy˛4*HBĀÖä ŗZ­š{[Û×ÛÛ÷AdEDD(æ‘XWW§šƒšm˛X)Ō kĢ)Ģ–yĮÄÄD)íisQË:(]VJJŠęaÃ-Ko įžŪHũ_éžŖq~b˜N(˙Œ’@€Á‹T$ՍR/LJņ0ņ,ļÆĸ(ĐøĄ-‹x1&ZD‚ļ,#€ņ'_0ž2B hx’‚ØŖD°Š€‘ˆgc‘€Æģ'?Úę…æ7ÁZĪo^z›kōY/OŊF•K8ƒHļ‘#Gf:s­BפúúúrfW! ˜ČFi˜Į°Ā5DmmmLGįŊøœ’/úŠ<Že,mû’’Õ×b>qâD ˆ\SĪž=}x”Û`2ƒxƸ5ņ,6y`ŠHЖUŒH<‹X‹Qˆ€Ą-K€øÂō â<Á’lJČB´(Rj!`TÃā%`䁊VīĄ|=Eˆĺˆ Ææ?xœ*Š—ķ§-!ā č{ø‰ßBÎÜŖä5 ŧ Ũ˙y&ŸĮŲŧ<•lpʋā†fŗY͇–ŅHJ;ŒāMŠ”ėŅŅŅ1<†î¨ūJ…SŽ?ŨQ9<S…b‚ë¤F(r{@ú÷īĪ­wx]]] }}h_SŒL<‹@ŠÄ`""´e#Ī"ÆĸQ‹]A,äĐĪ™™™ŠĢ /Ūįe[(ōX5Û§žžžÛĨ ŌŌŌ"AP5ąįŦäQQQ1L)Y”ĖK)™ÜĖGMr55))ÉâĻ\ēßfķ–WŨëȨŪD<‹M@´ˆmõFJ $o žlUm Ĩ'`""´Õ o2ü1o{†ö‚+K[fđ&âYl" E$hĢ7xCÄŊI1-q ZK´Š, €īœRÛZĪz#‚õĻ ûŪĮ­į3ŧ;ÉãRīąAåŗˆĀ€ē ‚ ‰ĩXhhh(ŧ')é@TTT7Ĩ¤0Rhi“Çw˙{˛‰‰á6d˛'õvö^By;[´"×áy–‘‘Ąšį\\\4ĪŸQŖFMWlĘÄYŧ‘xą!ZD‚ļz!0×V°ŽZ/9ô(—< õ@Ę”"āÄ3ęL$ ‹P$–ū āÄŗ Đ"´Õ Œ?Ė B֛ĐŪÔÚúÖīW {aāÁBB„Ÿq,ĸ† ܒĪZzwĒ<åI¨…€Åb ‹‹ĢVūŌ|aÂ3‘"­ ī˙O:Ĩ˜Į{—.]ĸxĮC%ųc###{Ē”ˇæŲ"D6 H”*844t0īÆ(xžUUUMV û|"""&ķŒ"Î\ŧxą‡}Ŋh_5@<ãŖČ‰/TŅÛTô>Ķ–PŒ?DzđæņG´ÚŊŒō—CĀ[‰g"ĀD$hĢ5 žļ ‚°Ä ‰/kŒ?10€ˆ¨ĐV DÃŧyņ,âK´ˆmÕDkŠŗôlŒü —x%ŸÃB ×T!B@@lhâ™FĄžh0…˛HMMõQ(+AI"[)™<ÍG!| nXÉ5âËĘĘ#˛=mowīWۘÄ-ĪK† âLuuĩbĄÚŨm'/šÄ~°z÷ö$~Š^¨ŪŽÕ_}0ö pûžú%˛[0Ā„! ŪN<‹-"A[­‰gū!¨7'`xĮķȐd€77.ŖuG?Cm{+ņ,6 Đ"´UĪđ4áËR2lčm.׎„WgDD„™ĨB˛Ŧ ō9((¨ŋō 0@‹b¨ '0›Í ū ôøE588¸Ų‰"šēD)|x&Õn°ÄÄÄŗj—Ąvūx~ƒ[­r@ÜōždHttô…ožųF-ˆxČ7,''gæ?ü0ŦŽŽ.Õ×××OēŠŠéĮîŨŧyķ_=ü˜ÁŗĪģ å3Ĩ_H@(A€‰ŪĐîāŌÚ~ß~ûm†Éd"Í ĨĨåĢØØØ¯h?iļô_YÂ&Mš4ŋ´´t`í÷„‡ã†ˆįöm Q$OÆ_ė¤I“ūGŽũú÷īŋ{˖-ȟž{6°Û´ļß×_=ÚĪ͏([sssS```Ņe—]v@ö#âYö—­H@{LHäææû駟î-++ŌAûÁەƒŒ=:ĶĮĮįĻĒĒĒá---ĸˆâøëÕĢׇÛļm[%ws …<žÁž<įŨ,šÉÛ Wį?ŅnĪMbû:u*- āRÄē .\ )V¨ũ˜ŅBååå]wäȑĢĢĢSĩ_BBÂÛkÖŦŲäA]‰xnÆæ$Dbđč›xüøņ7———Oļ˙vĮø‹ŒŒ,øųįŸ˙šwīŪíE #, ×~ĐŊX,–¯\l?čX0ŪXœį@ˆã=˜EŲ<ę \’Īđęä9lĨG-F7 Ī=???ŦûŦz*))1LbÕÁŌĻEŦr YÂ#|rss¯ä8´ī†Xß[)"ņøņãŅöų폝Ļ7ˆ[1‘Ę\YYŲKēī ˙r{÷î}wKKːž={F§ĻĻ ĶĻMbcÛC¨ĒĒŠŲž}ûQQQŗ:T˛ŋ˛˛ō_………Ģ]PXx†ōĪ#%—AÛ @— h|°677ßtöėŲáņņņ–qãÆ ø9jŋuëÖMFû}ûíˇežžžGÜh?ƒB¯_ĩŦVëŊáááŋ=}útÂå—_nÁøsÔ~ĨĨĨ­í÷ųįŸ—õîŨûHssķ •ņD<Ë75Æ€HÎ*EÂŦVë ´Ÿ )ŅŅŅaâķ3,Ŧí+™Ø~Ũēu[\RRRúåéͧ_9räČF[™´ŅVcA&VWW:th°øütĐ~Ö­[—g6›VUU•™LϝÜh?<ßAî8Ûŋ´GDŸŨ% ÃŌĶĶęÚĩëä .Ä4(xƌ­ĪOûö+**°}ûöÖö;vėXyllėžĶ§OŋHĘx}ÜVjlzzúíhŋĀĀĀøÄÄÄnâķĶ^*ąũš››ßhll<ôī­[ˇ.sŅ( ?ėũe_ęéĘģyĢąNCCCVccãĀ1cÆŠĪOûbŠŠŠlĪĪŋUVV–ģŲ~öŲŌžgÄN˜0aauuuĒŋŋß~ũúŪqĮ­ĪOûlˇoߞ€įgFFF]cc㉞}ûîZˇnHSgŊ*ņB乁‘Ŋ\Ų‡'(Ū đū9Ō…:ĨN˜0aöųķį¯čÕĢWԁũņíŽg¨}˛ĩߝ~~~uūūū_744|¸gĪž7\h?û,ißC`Ŧ|'ÚĪßß?úĒĢŽōÃķĶžũDŨKQQŅaaaUĮmí÷œŒîc ‘Ô`ĖĀbÂû~P29ûü`ąíd2ĩ;ÂÁĢÕēwåʕVĩÃcr‰H8D`ōäÉg8 úēœšššå/ŊôĐ[Aۃģwīūū÷ŋ?ŧsįNO-ÖĮŨ{īŊÛæÍ›§mT.íĨ—^žzę)ŧd¸BlčĐĄo?účŖø°PYZí˛ŋņÆËvíÚÕ[‰¯ŧōĘĶoŋũļE‰ŧôĖcúôéöėŲ3\ † Vņá‡^ōVPŖ ĩķ\¸paũ+¯ŧr™Ė ŊÚÅkš˙Œ3f9räņ &\",] ´´TXˇnđá‡ÖųúúîĘĪĪĪéä~"ž;ČvÚ)i^^ŪÂüáÖ[om%Lė?X;+Ǎ¨HØž}ģđ駟VũüķĪ›ˇnŨzsg÷ĐyåHKK{­ŠŠiĘõ×_áHáĐYIbûŊųæ›5k îęä"ž;Čæ)│t„ Ģ|||&;6ĖŨöÃķķŊ÷ŪĢ6lØō5kÖ<Öšxt…Rdeeminnžōæ›on%Lėu:+ĪNüļnŨZ–đįÕĢW˙­“{`tĨ Œ(9FĀY‚",++kuˇnŨŌ¯šæšnîļßÚĩkŠĐE#ĮŌĶQgģęĒĢvDFFÆĻĨĨĩžŋ¸Ú~xvbܲeË÷‰‰‰÷:á‘éÔ{•ŗ0čuNŊŖ/X° f˖-[† Ōâĉ]1ūė=:§ĒĒĒõŲYXXXŋwīŪzöė9uãÆ:ē‡Î)‡"D=ztäI“z[­Vŗģí÷ŅG5üûß˙ūqĐ AS:i?gŸëĘU’ĪœœzGGû•––ž?~üøîļžŸ……… _}õÕˇ9999 ,øŽOČø“>üRNNNÆ~Ž$č^đîYPPPS[[{xÆ “ėtV0bxØEÃ,WDPâZxfƒ|öT¯¯„,ŠåÁåšĪ/^ėIÄŗb}€22 iii 7ڛu)[×ÔČČH"ž•ÅÔŖÜ***h-V-cęÔŠĮ‰7ÚÜSSSŖT”0ŖxĖwéŌåR(I™îäîax€š{3+÷ųúúbŲĩį]ĢģvíÚaͧO˙2::úåO>ųIJxņb—?~P( įΝ+ėÚĩ+đę̝ž8f˘ę'žxb–LåœRjÉÜëm‡AN/("Ú%´_ffæ‰~ũúÍ˙ėŗĪ‚ŅŽĪČ÷āŪ͛7‡eggß4jÔ¨ŠUĢVŨqe<˙üķ×ëôôô; #ûŦyųōåąÕÕÕûnģíļüÚĪ9ééĒžĀxŋõÖ[1ĀߓöCû#äˇhŅĸ 2…Ŗņ@†Éˆ¯éax?c âyÕ.‰íg2™ ņÜķ¤ũđÜEûac755…p€-‰Hč†ÖĨ1bÄh•pøŌ­r™”Ŋ hsŗŲ RČŖ×ͨË=zôJOĀąX,Ŋ†MLLĖEO0‘Ü‹ĩo=î’ütûĢ !o_C`Ī˙‘#GĘ8öuænųōå¯-Y˛äË;īŧs>|\UÉUĖ[oŊü駟žüģßũ‰ˆg9āä;$ ĄāAûũéOŠVēũ^~ųåˆ7ß|ķ­Yŗfí•‹Îx‚Āƒ>¸åƒ>xXŖũ”JČë駟ļ,Z´(߁Ä~D<;¸CĪ4Œ Œ‘ŋ˙ũī<ķ”HxŖũ0Ļ1ļ´ŸÅx}h?ĖM˜Ŗ0W)Ų~¯ŧō @‡Ąũ$J@sŒ=ˁ4ūDD:Ū" #B˙‚€žô-ŽöģãŽ;žŨŋ˙ėO?ũ´•ôę8įÎbü­]ģļĩũ˛ŗŗËŪxã'ģ“Žrf˚5ëĮoŋũvö{īŊ×mĀ•û厅äļmÛĢÕ:í÷ /Ø?˜‰x–Īņq‡4 Ûrrr*ũũũgîØąÃ×Uo=ĮEũbšcĮŋ^Ŋzeĸũ`œ'w-w´ß•W^ų=pŪîŦ:*ų ŋšššÜk¯ŊļÖÎR$žERÕQtŦ-"VmČ`=b| 4(ķŨwßõw5RDÛ"~ŨÃ8ÆxÆ¸Æøļkŋ_/¤!€y í‡y ķ•RíãŧáŊķk@@–äśsC#CĀÖņfÉįT›W§Ž°QŅ„Û€ ëŌĨ‹ĒÖ2Ŋ~˜íí\:ŦEl6›c:ŋ˛ã+úõëwĨŅŧ{;ŽąķgOœ8á1žÎ—Ļ͕ļčmŽuŖh-ĀÂÉæ|IĄ§T˛ŗŗ'å™Y__§. åĪž={ö܉ĨGŌú‰JĀÄÄÄÁiiiß!´”íÃ,›+ëČIŗõæ˙ +đŽ3aĶŌŌŽAÁŖVûA‰„ŧƒƒƒ­đ Dh7o_ÉēŖũā-ëįį7+Ĩø“Ę(ļúĘB™ļņō Ër€TĨämhŒ<Ķ06Đ~J)ޤĸā™ŒŧŅ~ëļö“^B˙ŨDāĄ‡ē 퇹I­öƒyC ˆšÖFœņė^›A)y‰€Fû8šúęĢc@+e4' íˇ~ũzŋ-[ļü ŪIŌsôß3°ÄËSO=õyVVVoj¨Ņ~0&yũõ×ũ6mÚ´8'''ß&1Īî5H@Ãc5ėŽģîznöėŲÛ|đÁ0%Ĩĸ!ßgžyÆÆyšššëĨįčŋgĀĢuá…[—-[œÕHČ÷ņĮī†r$^´ SņŖä<mhŒ=b|(e4'E4€ÄøÆ8ŋīžûæHĪĶĪĀûæ%ĖOjĩŪ‹0ŋ4čˎžIŦŲŨđ~FømÃ$îČgxsEÁm˜^DaĪMMMƒÕŦĒĒj˛šųSŪŽ#PVVęú]mī(//W+äpۂ4ŪØPÂ3\cąĩ*Îcō9""b2 ŒÔ +ąeÃč>>>†"ŨF9dȐ“đĖRKq+ø¸‚gYCCÃ琐|Dņ,Čĩ˙wÜŊnŨēC ,ˆUKņ' eŧđ ÁĨ—žžN^`RpÜøŸ““3wëÖ­ÅđLVKņ' eüņ´ Ī„……ŊDÄŗ—ūˇĐx†a,`L¨Ũ~ĸc}Īn—$Ļ‹Û!’’ōöūũûW*éíÜŽÛ´ŸčE;`Ā€īƒĮŗZo%Āzõęĩˇ´´ô x˂ V3‰íī¤áǟĻņį9ÚX[ŊŠŠéex׊Ũ~ĸäØąc3áG"žÜnCŒŋU‘‘‘ÃÃÃTŌÛYN"Ņ€nĐ A×'''Ÿ”F>ģ‡Žwˆ–Ã:bķVWĖÛYŽD´ xP^llė÷‚ |FãO­NãÛųHtttÆ ÛÔ0Z•J!zAūĪmé9úī:xĀsLiog9I0ŋâ9ų–“öÃCžĪr ĒÅqxs-ėŠ¸QŪ‡@PPęk‹H1RĪHLL<í§†ˇēœ$Pr ”^Īž=įØ<Øå.Ĩã åC}}ũ|đA°ÚŠ#Šč+č3éééCЇ¤įčŋķāŲ•=TÉ0•ΔŽöCŸéŌĨË{äí bޝáGjję´ŸŪęŽKZ×3-,, >|ø5D ČĄÔųņ´´´˙wÛmˇ Äģˆa­ čūõ¯YÖhUĻˁkĢkaô(Åí÷˙÷}ŌŌŌn‘§˙Ž!••uõc=Öē6Š–ãF^oŧņF”ÕjyIÉM€ßC=4XmŖ9Šxč'(oņâő999ŋ‘žŖ˙Ž!œœüІ ]ĸ§3 ÄöËËË›Č ŲY•t;_WW÷žcjx;ËU í‡ųķî„ VÉ]ĮĐqĐęZjXYî<ŸĢĢĢIyĢaĄĸøE k×ŽĒ’AĄĄĄƒ‰HaĢ?~ëXx”BCCCØO=ÕĀ×^&Ŋ÷1†sss=Z uĀzØFé7Ā!˛õn–Ë ŅNËŠ2ÛļmûäųįŸŌRq$­ŌŗĪ>Ûõäɓ΁"EÅų˙Į˙—zĩߊ+üOœ8ņĩŸķm&Ŋä!B­éŅ~(ķųįŸīÖĐаC*ũwôųâââg0œģCŲĢĐ~û;wîÜĨlÎŪ‘ÚīĮ|s5FûũųĪÎÉÉ)ĐŖ|Ū˜3gÎX˙ßkIœH1ƒąÂ#Ąũ233KB8k/Į%"T:đĶŌčX ĘMNNΜ7oŪBéqúīĩ““ĨĨŅĒT2Œ{ŒÛs\zŠū;Ūf˘Ą[ûaŪ šöüķĪcų3–<üU]JUËĘsG>÷ėŲĶGK€¨,B€WA¨ö°ō”ČãW–ånöTž˛˛2UÃĩ{*Ÿ'÷Ö{rŋQī-))āiŨŒļļáįM&SOOqfåūđđpîŪa÷§?ũiUzzzŦ–ŗör@˙Øcîˇ?Gû#pÛmˇåO˜0Áĸ—ōŌĄũxāāŌŌR ÁÖqsĩ;‹u_¯ģîē`-=.í…@ŲcĮŽm]īŨūíwŒúüã?Ž›á¤ÃØˇZ­Ņxt,-ĩGsÎ3Ī<ã‹g˜^ sorrræbŊdāąÜ–––°‚‚‚MXãROųA øúúŽ~ú駗ę)oeŖũöíÛ÷* ¯ô” øōōōœ^xaŽžrđVöÚĩk‡íŪŊû)ŦĪ­g‚áÉūķŸ™(zÂÔŽėüüüĖĪ?˙ü~Ŋ ?DPūæÍ›<â1ÚvŽúûšsįŽ×ËđJ”ãK–āy Ŗmį`žŠ­­­÷ø{öŲgũׯ_˙6æãÎĨÖíŠRA°<"Ë2: wĘCĩŊ9FŽ.$G^ˆjzīÅĮĮŸf¯/))Éb”ÉIÆ0Ā3Ž599šI šôĖSŠĶF"öáųŒJˇK||ŧŽJ&%ë "]5ã&%e•Ë û}ôŅMzü@>›>lŅĸEääĨãmXĩjÕÍ'OžĖÔ[ųŠ €ŋpáÂ`RŽmŖŽöŪxã'Μ93Œ…ņ )ā;jąļį–/_ūúŧ^^CRiđ ĀŗĪéqú/æšäää0= ¯Dé0ū0“WD¤ķí=÷Üŗ%##CĶĨ 䤂ūwŪšÚOĄöĮ˙z| IDATûß6yōä@= ¯DŠĐ~6lx‚qŧ(.ۏ?ūxÃŧyķt5܁ĀüEܧmį,]ēô}Ŋ ¯ % ŋ Į_ūō—w;—šŽxNĄŋŗđí‡ö›5k–žÔ:Î!€öÃ|Ŗˇá¤Åü‹y8//õčWëŒz›7ōyœÍ›ĶšŪMW^Œ@ßž}…ÆÆÆĩ 8ū|ĩōĻ|=B Փģ-‹a׍.))ņÄķ4ĩ{÷îŊ=Á–Å{A´"d吏EEEŠæßSų\š„ŧ ÆY,–`Wä`ųZĖ/#FŒIJŒÉöÔSO­ÁĮĢž^_RĄ€_ˇnŨ5žYŠŠü˙^xáy”ĸ„ĸžÚOD¤ãíʕ+gŗ |Ĩ„,ëÖ­{Lܧ­<čã+WŽŧ•ĨöÃŗĪyŠéŒˆÚs † s0Úīĩ×^ûH”‘ļō Üö7ß|ceeūCûˆ[ŋ~ũJyŠéŒˆĀO<1ë‡~ĖĘøƒūę̝öģūúë)üļØHlnģĻĻ&†Ã+ˆ‰čcƌ ¤č4šä”'&&vcÁđ b‰ÆĮ>]ŌHüŊũöÛßE×3â•T<<đ<ĀsAzœū;Fķ æ ¯ !æac^v,1GA>síđ!ĸČųˆí|qq1y tŌ~999ųŦx}‰ĸŠđYYY4ūDPdļčãŦx}‰"âY€g„ (|ŗŠĖvÛļmŸ°āõ%s1ædĖÍŌãôŋ=Ûˇo‡âY” ø‹/?~‚¯/Š„XúđáÃÔ~RPėūgee­ž?~WV"&IÅÃúīGĨđŋRPėūīŪŊ{ÃŌĨK™k?ô'ČuÕUWąū×QmwŅŋŅĪĩ-ĩķŌĐ~?ūxž_íŊW`~a)b’ؘ1/§¤¤°lĶĐĐ0†EÃ+´ R.ŋüōp•ô~†høĀĀĀx ¯.äēpá÷ž*v”Ø+ޏĸ‹†W¨3ž gĪžŽbũšÎzôčŅ™999Q,^XĖËŨēuÅ0ȆŊÍųŦĸ'ÃũŒD#ÜG ""Â7Öũäī$/Pylô:rËĶõ{æÁę -Üzą///īã /C:~üx´!*ĸ`%bbb.*˜PYYiQ2?ōęßŋŋĸiU'“É4ŽUå0€>000F+|8͆ēVĢÕͲ×ē(î˜1cz衛Ä}Úū‚@uuõl֕ÆW˜ĢŠŨÚ"`6›sYWžCâøøxŧ*öūÜ~÷~øáĻŖîY¯øZ%ɏ?žÆúķÆ;ūūū´FĨã>Âúû ä;sæL’cņŊûhccã@Ö mĄˇ/÷î–r\{æŖ,ĸũ*++YŊ]ěŪÍž7pC>Ã{͍ŠûFĄ}B@)āŠ¤xˆ ĐĐĐPJĩ’˛ųDEEy¤˜7z8õ††ˇZ%%%n‘ÖĘļŽ:šuīŪus›D4›ÍfŖõ*ĀŖ[!ÄSmD­BŲą‘ 旸¸8æ?&¤h}öŲg7w¤|xõU•:NK–,ļoß.H¯Á~iiiëqņ.OŽũöžŧŠ"kûBö•@.!!ŲI L"„aČEGGp@PEqCFåG?ÔaĀ QD\ĸĀ‚„a ƒ,ša_’˛¯„˙yã-l:Ũˇ—ÛKõŊUĪ“ôíęęĒķžS[ŸsNJäץC‡˜‚0ÄqŊpáÂXgĘwŽl¸¯j-;ä-VžA~GíĪĨũļĄ­ uĻŧãŠÖōCÅßēu8BĢy@Ģ­­e[7ķXƒ:M‹üöîŨ+*?иoßžI<ō=ūļŦŦŦ“˜üÄÚ˜Ļuû#‚@žBc4ÆjĄgžwîÜš.bģļ)?ĄúĀ• ęsŪárä×ßõõõĄbÆ#åWRRŌÔwb Äo~¸éĻ›|ØÖŋ|ŽØl;vŨíÉ(ųá{ãÉ'Ÿŧö'TŽÃÁVÕcÍQģMLlTT”¨CŒ rĄūΕī<Ė]Č””ÁŊ&$$D0į.Gš~'аYŦ#ÂHųŲá*Ž\šųąĀáÆ“ß˙ū÷ĸ‹0ô’Hāį-Ôv ŠŸŊŧŧh?fhËŽ~ļŒņĢ7Ũm[ORŅŲ•q@O\ētIķ- ēą-ˆõ”šúŧĪ;į¯ö파Œ[Ü};õĒĒ*UÆyW”̉‘ī‰~”IQ__ßÍëV+ēa u7ã<ę""",ĩŊ›ŗķ‚ņA"fŒ^bd&Ę{(e0ųMŽjâøíĖnˇ‹~¨ņĶzĘ}~~~71įgōĶRvāĩŗ˛đ°uuuĒûTw•įųķįÃČ'ÎxĒĨü ˆ€|ЎQ&ūø4=zÔmÎøxåŪ_Ŋzĩ­˜ņÄ(ųÅ-úČ÷üŲVUUÅđã=ü>< @ĐxâLvā™–íČc­PÛÃsȝ˛˛’æÕ'†‘×ظ¸¸FĄ–ŋ>đi‚ü>Ėļnžž1‰iii‚F ŖåcŦP.dÉxVXXhŠš=ƒ÷ãÅļŧ7R~™ŋ` ˙ˆ<°l–vģ}Ŧšoæ÷wŽ|įá]8| ÄUhū:ívû}–eļ„§¤¤ĄŽ,Čáį§Ã=VI3S‡Â´ÎŌ2ÆįÆÆFœŋČãã€B¤¤¤ācGĢ|MĨ{€!N!—éIž˜˜ØR-5yyy]Ôžk…÷\1véâžŦq…/{UU•Û‰ĄĨs vnqGã<ꍏÎFĩL(++ķRր؇…ūˆņ Ąä7>Šaq%Ž”CŽŨģwĮŲ€–ũĀ 8´ŧúųų‰:W9“Ÿ–˛geŧũúõĢfō#Üøõ|}ĖowÎxĒĨüĐū‰Âŋ…” Ę­ŋQíQŋ†÷îŨģ\ ąQōCŸ;uęÔkdˆõįÕÕÕĒ ¯eė~?JĩfȜɉĩlČ폌Íˆq8īčá8-T–…âb;uę$¨ 3Z~üúĀį!d[__#?ŪÃīĮ‹ÉI,žđ‹ĪoW晘§B>PĘĪ;×&dĐA\iii<)Ÿ]mļ›nēi”¯Ā#å 7” ã%ę?āŲ… Røņž|ߎ]ģdą1ĮHųAhƒū„æ/ ôz˛ŧøØųå—~Bu錔žH?ŲĄ/æ<īĐĄÃüxOž¯ĒĒJ$|ãķA/ųĄ~ŪBc'Ÿžäädč4›wŦü„æŨÃc ;[XŌÁŨ2ÆglUcžŒYɌÖå ũúõģYKîlˆĶ’OfäåįįĻĩŗ8ô(@œŨŦ&īŧŧ‡WK~#/üĄīD}ĀoĄāīī/ęč'”ŪŨãüũũUõā‹–ō#ÆJŒbs¤‰ˆˆhíî21 Ÿ–ōÃ7$æ,ũû÷oúŽ›ŋ…Í*å¸RŸĩ”äEúLâ@ ÄÃ6mÚ°īwcŧŊŊUĪį´”‡$ҟč?]¯E3Ööŧh6‹ĸĩŠņ9ŅązS{Ā8Ā8 ĖQŧŧŧ4]=åΆ8a.Z.V•7Îîu÷ŗŧŨųėfWjiaaĄę3NN:eŠsåōŠ  @“hŨqå3ø…՝¸Ģí„ŧ=‚ ÉdgQÁ9ČÖC~PŪbSūé_7´–”CČJ)Ö[K~ܕCúSÎJ´nZįĮ¤äœZķ›Ŧ@ƒã úr@kųZŒ}Ä­/õ,w-å‡Đî6oŪÜlK`Æi}8 ĨüH^# Æp€đĨąųū5žÃøŦŠmįZÎ:˙°„ņĢ6ŨŨ ĸŗœYöĖ´ŌŌŌ-Y#xe°ŧÔq 55Õ6dČUÛŦûųųÅhšŨ°:tžåĘvæt"ú*WeŌđ[nîķKĢŨ;wvÛūŌj[cÖ××ĢĒĢ8#¯¤ã*~ā #–+qîĶrčBĸĩėäĸĢŽŽvģ!äb×2ōƒ xü‰mģ­%wÉĢĄĄĄF)­å‡üđ‡€ū–ÉOŠD”Ĩ×Z~(2ƒōãϘüjjj<ÆąM™D”ĨÖZ~Bų)ŖČ3SWWW+î;Á)!~ģ2ĪÄ<•ėÜCŽž)cPk-?P ÃŗXŋIPUWWŗū“0ÅĢÖō#Æg´C ĨäčénõjCCCŊ@ZË}&œđ‡ ļû€ZŲ;Í9āŠüšįöë7‰‡,Ŝ‹ŠŠh˙~'ÛīaûmKKŸąj͎Ŧ´TMaÄZšmÛļU}°đÄ:¸íÄx-UTT¤ĘŲ@Ģ•ž43LĨA1Ņą9ÍĐTĶĩۑãܑŪŊ{ÛUNņ‹Z­č.,,tÛcC’““­tîŒ3…6”d%>Jđ×TCņI”vˆĮ=ŧq‰÷3ŽÆņ›BYYYžÍf#üĮy_^^.zæŦ˜ü´–Ī/KHįΟ¯€žPč‹kÎ>OIûĶZ~0ntüÔŠSEbxŒ’Ųļõũ.wU—6g}7ũ.qŒ)Í ‹É ĩnˇŸŗĀڟ3î\˙ĖHų ՇëŠųõN­ą@(/wˆûųįŸÄüR(-?ĐAū°û? mÖ××_āĮ{ōũ™3gމá7R~ AŦúđÜĪĪO¸˛‘DėzFĘũ'qdÆUlūĸÖYå(7ûҞe˟ÅęŊ‘ō (8 `>Ãč?õüôžpīl<ŅK~ā+?o!Ŋ Ÿ˙ņšsįöķã)ŧˇäÖÛ-(dd3’ētérbÛļmėė´fœaŒō80iŌ¤’­[ˇjuūËđ3flž7ožŧÂY*C9pęÔ)ÛŦYŗ6ėŲŗgœŌ‚ããã|ķÍ7–:ÃU)Æ{îš§ā‡~h§đ=ˇ¯ķC† 9ųË/ŋtVČ$w[Ū¨Ŧ+|ÆN›6í§ ¨>/ŒŸ!M÷‹-˛-_ž|æø4Ņ%FKRRŌö?ūø1oWą÷OŒÄ㙤ÅĮ–q$ßäääâŨģwˇ!÷ėjŗ%''ÉĖĖėÆ]ų#‡/zČNĒÜAƒĪÎÎūÕ{A*ą‡<‡üvíÚĨxŽa†üzôčqōȑ#jÆDˇ•fŸ>}ÎäæævT Pkų‰åGčÂķôôôŖ999ŨIģÚlIIIE999Jy!ÆoWÆ>g4@ų7eʔoˇnŨšî,§=1bDūĻM›;{j-?ąüˆ

:Ô<ųFfäŖuōÄĮVppđ5tēķ;ĄĄĄ{ÕČOŲ9ã3ŧâ### ĨņÄg­[ˇÎˇ‚ü@cLLĖIO”‘3Ėqqqg‰âÍY:ū3­ÛŸX~¤\Č/"""“Üŗë¯hŅĸÅEŒ-Jƒŋ]ûœŅã—Ũn˙ŗ4žøŦ¨Htã§ėĐZ~bų"Đūzöėųšg×&ŋpá‚Ē­ÅøíJûz—Č ķ—ėėėŋ‘{vmâĀŪŦŦŦ25ŧĐC~b†/Їö—““ŗF ­nüΖīžûNÕÖÍZËO,?ÂûŨģwƒNuĒ$7ģ:tčm˛ÚX)41~ õrâĐöœĩŋ}ûö0ÃķõRēråĘ?͒ßõ”üz'$g’îčŅŖhĘ'Ę$c¯đ„hžüŪX•f ãsdd¤Û*nI‹%fPÉ’’’îVųúu¯•””Œž.‚ŨPĮĩÛgįååšũvęjÎ'ŽˆˆíęšČÔUAņņņĒÎ#CāŽŧq]šÔÁ0ëÎÎ:}aaa{^uĸövíÚĩKĄ˜Ą=@ųŪēuëŨ´Ķi4}KÔ~ĀI+ęXMMÍ#Ë´BY ĢŦĐū@chhčR+đÔH}}}7Ŗoĸ=@~>>>ŌN§ŅôuęÔé+´ŋœœœ:ŒÕFķ‡öōÂÂÂö[A~‡ŽXŗfÍÛ´ķĶhú‚‚‚NXaūâ0˛˛m›yÄßß˙¸į^6ēßæååÁøeã‰îü ƒ/ԇēē:æxÜ\J%(nMW œ3Ų‘<ąėÚĩkÛÁƒŠī“0>GDDü—G>͡+p:Íōiŗ‚ņy8;_–/6vĪ8 Œ83ŨĮĮGņVybĨ¸ĢąI ¯ÕâUžkl5˜ĒčuŦfUônUUUĸ,˜Ø”Šũ&VŊ+}›žŦŦŦŗ;÷—XõŪ˛eKUgĖsųdāī‡bÆĀ"•… uëÖ-VūĻ{ŋąaÆjWŸÉO233ņQȇ›7oū† N•?QĮÖŽ]ģ‘JâL$ AÔmIU4””č+d%ö Dõõõo[ÁxyōäI()ŠWT]uėv;õōƒqް°đŦŅŧąBypHŖŊũÁx#Ģøi4pĨŨų ßíÚĩ;b4oŦP^llėnÚÛęW``āVā§Ņ4Â(Hģķę—¯¯īVŖyc…ōęęęNŠŲ9ÉHl_xxø'F–ébYØÚŨĨE2.–¯øuęĪX­éÎ̆KŒŊĀ8 ‚؊š  @ņ9{BE………us×mv…đZ1îÔŠSQjčîͧĒ-‰Ô”eÖ;ŽÕŦЎ ‡!’…æ°Ûínģ ?úMŦzoŽZ~L}}=ë/åŗË”X=DûėĪ?˙ Īwύ-[ļ}ZÕųâjĘ2ãWWŊĢÚÎÜ ŦjËlŅĸ…ĨļëĪĪĪ˙;ÍĢ |hŅĸÅOjåáîīÕÕÕ­ĻųuëęÕĢŨ]jņÕÔÔlĻ]~­ZĩúL->wu›vųAIéîrP‹†]šwĐbŒV‹ĪŨßĢĒĒ:IŗķΡß~[ģiĶĻî.•øšœwh–ßW_}UÁvm•îņcĮŽ}JÁƒÕĢWWb‡ HĄŽlũģiĶ&Ē¯@褎y”““ŗ|ãÆĒÎ]7‚|ôë{öėšČvmæöŽ;^ËÎÎĻZ~yyyEÂÔSkŠ­ˇŠ7>———Į¸ģԟęęˈs8ļŽuЍ7ۘījė}}9āu¸ÂR\Ž Ë3%9Ví+ŨJŲ.üüüÂÔlŨbˇÛ/›"H ÅŽ+XŲíJQĒv!pĨLŖßŊņÆ[]Ļ+å9rdÎ;ŽĶēúyöėŲUĄĄĄOš‚Ņß…bÕĒUÅ4P |xņÅkwîÜų€;ËĀlšššĪ.Y˛¤˜Fá_˙úWúw–+Ø0ļL™2…Ę]Ž0&gffžÅí Fw~×ĪĪīáɓ'Sšõ=œR~úé§l×ņōŌ“O>)žĀÄ'pühhhØÃŒ'âBˆŠŠú'­ō[ŧxą-222Kœzö$ āKđ‰Æ€zúh¤š|}}wŽXAį‰F¨W:tø?ZxE!%ååå‡iu^6mš­cĮŽĪPČ7)’°KžetøÔŸCCC•P¤Ğ3x$"""üâââú¸ ž˛˛˛ŗĢy°÷õå€ckiKĄ/G~ËįīFGGßō[Œô¯€€ˇ_Ŋęā‚âņÖËËĢŊ4­›ÂŕŨájW”[‰cvģ=ÄJ[ū€ˇ|āņĮ§NåCDDġėŧRį- 66ö|(Ō |čŲŗį+LyëT2%ÉÉɋi”h1bÄB&?įōëÖ­Û;4*p!ŋŽ]ģNuJŊ‡?ÄØú%ŒÉąąąˇy¸ˆœÂwŦŠÛG[ûƒ3Ęã?ېžžžî€‡?ÄĒԋ/îĸm÷ČŽs™™™ˇ{¸ˆœÂ_ģví8¯Ōf@ãÕúõ닷nŨĘڟ fgg˙|ĸÍųôlŨēĩô9!ßãĄ~đÁe´9Ŗ?@ŋ™™I§g%5įķĪ?õčŖÖŌæŧępŧÚeá]#ÖŲl6K|ûĐn|ŽuŦāŖ¤É02ŦQs IDATËŦøô÷÷ŋÛUeeeŪŽæÁŪח8Ē@éęŪÔÔTˇ=ģ—ĪíÂÂBEĶž={úņķpˇ{5+ÂÁwī\t}ƒé ĸœŒ1khRĀc,Ƙ˝¤+ĪúõëG}÷Ũwõ4)āŅūFũ¯įž{î¤4ĪNĪĸE‹hRĀÃqgâĉo1Į+éēųđÃ߁ūŠ6ųŨvÛmV\ĩ'ÍpS˚5ëšžũôLœ8‘í˜$CÖéééOĸŋĸ% €üĐ/ĐBÅt”`œĄM~1.SĖ7)Ō`|ļÄšĪTŸąJĢ5Ĩ¸Íž30Hs‚ĀĀ@WW-‡'''J—ÆR˜ÉU1Z ĩĩĩƒ•¤ˇjZŦ|V>ŦôĢĨ_”Ö`t÷•Ŋ;v R+Ë!C†$ĸßu÷€ēSWW7Čj8ß}÷ŨÛwīŪ]O‹¯Ķhú"Ŗ\ k׎Ŋƒ,\¸đAĘŲF yŋ˙ũīĮŅĸ€‡ü@˘1cŦŦ|0Tļ¨ëčŗhPĀÇž`íÚĩC e‚… {ėąĮž€ühƒ1cLρÚihŅĸEɸqãžĄeē'”ƒ–ŧôŌK“hį ôÁ@˙×ŋūõSZäĮ+8ƒ=õÔSŌĀÚi¸ëŽģÜ~ûíhq~ŅŅŅ'Ÿyæ™ˇiį ôMš4éŖ¨¨¨m´Œ ô<ōČ#ĖqNFA=G}§ŠũĄ?@ŋ ƒ|O‚qĻM›6ų´8cÆxlqĮšGÅRŧƒĨŅ’jã3Viz‚âÖhĄŗō<“0466ļu}ĸcKgŗa¯ë́ĒĒ*EF3ĨéõĻ_¯ü1Ϗzޝ^´™¯Š:0ÜŨWöž;wÎ_­\ŠŠŠÔ8;¨-ĪŦ÷p6ļŽM†“îūđ‡;iØ~›( 1œ-°E‹Į‡ú0 \(Anšå–ÉÉÉėŦR™õ ŠšģīžûMä@ SÉžÍfC]G§AųĄ/@Ÿ.g§ĖČČXN‹c0Æb&?ųurÖŦY‹iØũÎ'Ønû‰'ž`ŽōÅGÃ{ŧŧŧ˜­€'ŽWQQQi Č÷ø¤O?ũô8v˙€ãÕÆËVŽ\ŲĪã…ĸ€ptĘĘĘ*3ÛųåƒæxĨ@x6› õõŪėŨ?ȎWč”!đėÔvģ} ÎĮ1c€ļęKy-@7ûüK´ũ+WŽ|ËV})—ƜM›6˜Š€‡ãÆ`ļęKšüūõ¯õÅųĨf*āŅūēuëöˇ´´´mĘxöŨģw÷üķĪWšš{Ääɓëzôč1ËâĢžLŠH)))7›}~é”)Sę 09î(ĢāWddä`ŗQ>č`ōS.?Ô{Ôeoj—ũö#†ü0ūbvŽ[bëmĒĪŦŌt“ēÄ`0hÁ”””ė”Ģ6ˇĄC‡Žbģ¨åžąī)5°FEEÅ`ģnšq`¸'Ŧö‡ėÃÂÂē5Cī$ÂúĮĘnUį¨ÔÔÔ¸ėėã„ũT=ēéĻ›ŧŠ"H18˙rãÆĻœ? Ĩ˙ŦYŗĘbccoS@2KĘá@fffښ5kΚqū,ä7sæĖ˛ėėėū’ØOøüķĪG͝;÷ĸōC™ .< Ė’r8€ēœL8 íoŨē5CûŠ€ŅŅҎb 2À ųaėÅŦ€d–”Ã//¯;ĮŽ[i†ü`xŪēuëk׎]Ā!‰ũ”É(āo¸á†ī¸ãŽz3 Đpü¸|ųō÷kÖŦaÛ5˔7ä׹cĮgįĖ™Sk–üęęę>xíĩ×ūÉĨ‹ũ–Į 6¸téŌ;“'O6\~¨/(ˇžžūĐ!b–ŠËÔûÚÚÚ¯ŅōĢ‹œ9î¨ã>ƝĸĸĸoÍp‡ü0îq3ųˇŲląę$bĖ[TŸĩXĨi Y)ŒÖā ĮũúõSíĄUXXØŪH•5J¸——×NIz+§ĩÛ헭Lŋ^´„)ɛõΚJũŲ+ÎČZ^^ŪJ~júR9r¤ß{īŊˇíŽģî2ė S|pũå/)€×;S>¸V'<Øį­ˇŪÚ%„QJ@”õĀœ=zôhŒÍfcÛũĒaɉ'ē>÷ÜsĮ’ęƚ?ūQÔ&?õÂīēté2m<5"@~X1ņņĮīrČΈbŨ˛ Œ=ƒ&Ož|Æ(% 䇱öíˇßūöȑ#Ũ’ąÚĩk×6(PīŊ÷Ū2ŖäC÷ĐĄCëŋûîģowīŪ=Ø ¨nYĖæÍ›?ōöö~ 55ĩÎF"ŋũû÷˙“9î¸ÆņĖĖĖÅ555ŗĮŒSf”ŊĐū˛ŗŗ_ĘÎÎū‹k<ûíÜÜÜY.\xvčĐĄ FÉå <¸åîØącžgKĀ5ô;wîˇcĮŽ•hF9@B~č¯ũũũaŽ;ŽÉãĪ÷ß˙ĪÛoŋŊÎ(:Œŗ_uuõH˟\C@ŨÛ+l6ÛTę¨âDŗņ9ŅąJ“C.ûÉ8Ā8ā °ĩŽ———ĒU|(7,,,Œ­|vEÆŊÛącGEg>G™ų%uęÔIļ‘555ĩ§˙ ÔaÁĪĪĪĪŨûČ~Ȑ!Ēv‹hŲ˛ĨųĩŨ z÷îmˇUņÉ %K)Á ÚvíÚ=%„žJ@|`Aņ~ėØąmû÷īoĮ Ī’˛‘“ äķĪ?ļTo%#FŒ°9s&ķāÁƒQĖp)G<’iJ˛ŗŗãęęęV€ˇz*‘ 8B(+''§;“Ÿ¤l$ C[@›0B~XąĐšsįĨhķL~’â‘LųmÛļ­Æ$ČOO% ÆVôŅk™áKR4˛@ēsįÎĐũû÷€üôtĀ‚[öë×īĪL~˛Ä#™čņãĮ÷úėŗĪčí ų=ōČ#8c}ĶĻM“$‰c $9Ôȑ#ã˙ū÷ŋ79`éŲūāā…*ú÷ī?‚.%E#+222ēŧūúë%z:ĐĄ^ ”sĮwtCš˛d‰œrhhFČoõęÕGŅ_3ÃŗSąČ~ˆqhđāÁãīŋ˙ū*=čˆĶ#ÆYČĪ Īāų^ÚuqÔŸą:ĶŨ•Ú˛[%KČ8 ĐĻJKKÔfWPPĐÍŨĪxUËÚŪ;wîœŋšzôčá1́>,—7ĩĩĩãÕßĨKšliJW__īũAQQ‘š>3ąGŗōŽMqqq0ÄY:ŧų曯.]ētÄwß}wR% >ŦđõĮ?ūņ!vÆŗöUįöB~¯ŧōJ”Z+‘įėŲŗĢz衇ÖŦY3J{ž#Î^˛dIÚķĪ?_Ą‡ yBņ‡2ØŨÚ×5´ ´ ´­åGˇPđ/X°`$;Ŗ[{ųaL;vė“z(‰âc+úhŒĩÚ#đėqôĉ_‚s†Öt ۇ>úũ÷ßÛŲVŋÚÖ5lũ ų >|­p(üN:•ŗqãÆNėŒuíå÷駟F…„„lĐÃ’8=–••ådggĮŧúęĢÛĩEāŲšĄũeffļ†üĐN´v€$ōCū(ĮÍļú5Ŋō = ] }č!?8­b\]ûá‡vgōĶVä~øá‡ŽÄNkHâôˆņãŦ›ËhÕ ĩ•lķܨ5>cu&”™,00hˁļmÛļÔ6G–čͧOŊēJJJؖę ĢĒĒō˜äyyymX UUUå'úĐM`‚ŪJá¤ĻĻv÷¤9 Î ÷÷÷ŋ[)ŸhLŸ––ļmÕĒUą2kĀ€ĩZ(qņ!5`Ā€úœœœ]YYYËiÄî4A~›7on‡•­”Ž(Ž ŋcĮŽ­gōs•›Î߇üžüōË`Č/))ŠZ % ō8p`ō„âe8§‚=UËômh#X6Ŗ…üĐ†Ņ–/]ēô:üL~jĨ#ũŪŦYŗC ˆą ōĶB ˆ1499š c*ÆV&?i9¨MņŽqøũ÷ß˙ĪmˇŨV§…ä7lذKƒžÅ{‹-Ø1j$ņŪ´iĶîÁ*LČ/##C:8=Ž7Ž$>>~ŌÛoŋ}3“Ÿ„\xüôĶO›7oŪīæĪŸō‘GŅD~päēīžûŠúöí;„ÉĪáČxō›:ujߊS§žÖʁų˘1ãäÜšsû"d°$*8€~ ííäĐd;ÆO´c´įÁƒ߄ūYiė€ü`Æ<ã•Ģ !?ŒŖŽņ´‹‡Č[o—ÁrS’´0ĨT…véŌåÄļmÛpŽ ŒŒr`ƌ7lØŠ&Ë;īŧķȲeËēŠy—Ŋc,-Zd[ž|yĮ’…'$$}ũõ×’ Ũ Á‚ .¯ZĩJÖĒÔūũûūá‡fxŠ˙ŅŖG8p \{J0jÔ¨ŖšššØžUvčÕĢ×ĮķįΟā)[ļŸ:uĘöÄO|ģcĮŽtŲLĸ?!úˆwĶŌŌü._žÜwäȑöÄÄDÛđáÃmááŌŨ &ø;xđ`Ʌ ÎäååíǍ¨€G*> XПą6›íĩ¤¤¤Ö“““ŖÔČ/++ĢŦĒĒęä/ŋü˛ˇ˛˛rÍfsŨš­?vw(!Ößß˙ÅūũûĮÕÖÖۘ––ļ‡?9>ˇnŨZėë뛗••uÄfŗŊhŗŲŽËyŸĨq™Ã'véŌ%1 &555DŽü 0Bß ųåä䜩ĒĒ:ą{÷îK6›í1&?—å"7ƒŠAAA‰7ŪxãÍíÛˇęͧOøøņãmčCĨW~›6m* ÛŋuëV8ņŨÎļI—âžfĪįļk×îĻččč-[ļŧaėØąMíOŽüĐæääÔîßŋ˙˛ŋŋ˙;wî„#îͨcIqā娨ØĢžžžnŧņÆ6)))Ač?åČ#č?!ŋcĮŽĢ¨¨Ø››‹­­ `Áü=66ļ¤mÛļˆŒŒėųĄ˙ŒÅ´ÔyāĘoĪž=ũüüžßŊ{÷ Ŧũ9ᛯO?0`@yeeåČččč°1cÆ4ĩ?šōC˙™]qęÔŠËkvíڅãŪĻiL#ËNœŸĀyĩĻĻfh˙ūũÛ&''ûĸ˙”#?Ė;ŅB~………g/^ŧøÅņãĮņŅĪä'Îo­Ÿė‰˙%(((Šk׎ ?ôŸrt/\ųååå•””|ŸŸŸŅfŗ=Š5‘᎙Öņ›VĻąÕ™´J†ŅeutčĐĢĄŊSŦ@Uē2ŌęŧrúĨ-$!!! n€W???|@ƒCĶāįį㠆g0!999đNųÁ}č)ũšŪaaaažttV>ãXnũpƒßsm6ۚ­[ˇŽ–ččč„ũû÷ĪYļlŲ€úúúPb ãâÄGĪöíÛKĒĢĢ+üũũ˙×Đа įų9Ō ?Æ3>s™ĻßoČoųîŨ웿:555CŽ;6sŲ˛e ũúõŗķ ™D~ųųųu0˜ØíöˇwîÜšŅA"´†W3wŌĸ[į<ˇēēú™ŦŦŦ&cą——W“üžx≔V­Zyß~ûí|E<äˇ~ũú €Ē°°°˙äįįŋš{÷n˛ĘōC` $cĒÍÜĘĘĘģ8Đ´J˛S§Nc-Zô×'žxĸoģví|Η~ûöí+¨ĒĒĒlÛļíȏs.ž[˜üŒ‘J™ZQQ1ōÃŧ°ĄĄaŌÁƒ'WWWĮųûû‰ÉέZĩ* ü1 ` ļQt<Õ!?OR'­æ% ĪĪĪ‘ŸŸßô$00pŌöíÛ'÷ŽˆˆđĘČČį+âŋũöۚŦŦŦʈˆˆÂ   ŦuëÖáLRâŦųáÍ_šķZëĖ?c˙SGŽAû›ģ}ûö[!ŋnŨēĩJII áĘ9995,mllŧĐēuëŨ<ųŊĖä§ĩ˜DķÃ\#ō;~ü8úģđ†††Š999Ûˇoī/&?´ŋāāā3mÛļũríÚĩK9Î:˜{ĒŌŨ‰RɈqō+ųņĮ›æŠl’ß'Ÿ|2Ąŧŧ< ío˘1!\CÚßW_}UvôčŅú:üRSSƒíĩŅW’]" ?äKúSą˛Yŧë@;9ž{÷îĻš䗟Ÿ˙Ė'Ÿ|’ųĨĻĻ&''ûq减ėėė˛ .TžŨ™ü\—œ0ĪØ’››Û$ŋėėėØōōōšŸ|ōI’——Wû>}ú„ŠÉí/""âŋáááŸ|ųå—os ۃ~˜Ķ9Üō'ôVTÎר]ų|įw–.[ļ,Ä-ĢÅ8`"˛˛˛pæŨĘŨģw˙E)…Ë—/W´5¯Ō2Xzm89ŋķÎ;OnÛļ ĘŠ>mÚ´ķ ,PŧŊ°TÆ4>wŦ ĮGŊ¤†'íÂĄ„/Ģ§ôͧO?đÅ_ôUR—=ŠŪžüųĪ>žiĶĻ8roņ+1;[)–ņņņņŅĀZTTtųÜšsûm6&ũDáĀg€čw$ûū‹ė^ $€˛ĮŲJ­ņ!!!;wîÜ9Ÿ8q"ŋŦŦl§C~b…!OȗÉOŒCÚÄC~R†FĩōãT´Ą–åÂᔨö͙Ą_ü>qä)Öŋōé`÷ę8ĨÚ ˜Ąããø:ômĶĻ œ9mššš§}Ŗ3§N(Ņ'3ųŠ“‹Üˇ ?1CąZųQ욯×].˜'ÂHĨĨü sĖ_îr&QŒCjžˆžuüM7ŨÔÍßß߯ēēēæįŸ>ęč?Ō“ræ´ŗÄR¤A~Îæ‰jå'5§ĩ“(&ãæžÎڒŌö‡9-ūÄæDŗÃr¤IÍÕč^¤æ´–c’ÁԎ÷´Ž|nˇÛ™áYĸVąĮŒj8€sH}||:Ēx7ŅąjZÅĢė38PTT” ŗÜDooo0<ƒJÎâĩÛí—eōĐĶ’yLpęÔŠ(ĨÂíÜšsŖŌwŦžž°°0Ôę8ôCI Ļø#É dߛ››Kîå\ĄĐ€bƒ/åpK}|hJ9^­+++[Į䧞É:ž‰ö§‡üĐĻ™PGÁ9˛–Ãc5íČ)õ•!”ŦÎ`<^qîÜ9ū&?Ėr!)?œ9^Š•æ<[ĨæF.îņ¯BiŒöįĖņQüđŒ1țÍ?õĢfūœņrXüķĪ?+Ąī0ų)á˜ē´0,#€×bA­ü7ūœå-V&‹—Įôoā¯3Ģ‘Ú3Æ>´môĨ,čÃĖ]ĀkgYN~4§Á6ŅŠŠŠMĢeĐé1ũš-éŨĖ+Ŗ:üēmģãÃZVzŠĄnãV+WH1 ,Mä3åŸZ€DŠ!}đŠÚØ{dčLy¤–KPZ Ú<Ø{Î9 Gųį<ņ§hͤ~ˆ§bO\á‘Q^š’˙]ŒŠČŸũ8šc(†CōgA?Čq|T[:“ŸZÎɏÉO>¯hLЎü? úqü•r\U[:æ/L~jš'ī=Ÿõ”ō÷”@ļŪĻ /•ÆgŦĘd†Ēę #ÆÍ8ĒØpŖ”'aju‘ÃĀ&ë Ö¸¸¸ O“m^^^92>wîœŋœtîg[×ÖÖ–ƒÅ“úƒŪŊ{ÛUcSRR"äđŅŌ8vO€aĀęAī•=L¨o ŅSyʙüŦ/?Ļ@ŌO†č?õRj(T˜üŦ+?(p™S_ųéá8ŠáÃ6“Ÿ~ōĶËņ‘ČN]ĖDųééøŠ!;´AwøÎŌGŽåJᤨG ÎsL~zp×fƒS0qÖŖČe(ÖĄëAŒ抧ã#؅y‘§ŸŠëŠ4>cU& ',00čÁîŨģc{TE“Ÿ’’’Ņ0Ną`4Å&Eqttô-LļÂ\RŗęU8'kÄVUUÉĄÔû%ũeŦ'ļ'ėXƒkäÔĘĶčŠüt(đGŨGår‘CžŪĘ?Đ@Vä*éäĐÎŌüļ*Y¯•{ā1SęWĶĐ&С‘6ĸGI0>Ŗīd @íš ž‡öš˙šŖ§)õâŖPž0 ŖCÔ+0į+Ŋ8ûĢQ_/ĮB5ōgÎ;„Ú^õv|ĩhL~Úʍäžęé8ĮäG8­ĪUoĮGP͜õ‘r5J~žä<‡ųJ‘yĸAE)hJßŖGYį柳nŊiÁ*o???9g\‡;5ô&‰ŠüđĨ‰^OéĀۑ7`=m'TÔ‡ÆÆFĢoS|\ę˜ICy¤§á’PÍäG8ĄíÕå(†üųÁŅ„í8`”ã(F˙Éä§ė“Ž;„b&? íŽhz;^j™ü'´ģ)?Ė“¨2ČjĮÆf9álOM ÎøŒÕ˜X•I ‡!ŒnČlØY 4š[8+ɓĨ՗rˇŒļÛíwž7ĢĪÂuO._äĻ.ÅŊcKKK<Å0Ī—dppp ?ÎB÷ø1ÂđEX¸„Ú\TūbĻ€ĐFn$#•L~„ëÚ]Íhžĸ@ŌNJâ9Ũūc5ϧ*mĩkÃ,'Æi466ļ•Nõ[ ģŨ.k įßŪ`ŋĖæ€c ɕ=§OŸ–ŗ Øl8š– i$ÃVËpÔ𤠡ßxãųžÂÔ8hČÅU-7­ģĨ̍¨ŊÛ…؍Tū>>”ĄÄ%βÄR$áãĘ(å-CHL~ŽWŖ•\ųIΑ\‡įö9  āJŖSjĮiĄˆAJģ\įĜ¯œķGÉSĸŧ5bÕ3Ą‹ÉpÂõ+æžF:>‚b&?×åFr0ÚņŠČå˛ā:ː;;Øuš‘ ?Ŗv ebūéIL‚[+äg”ãčĮ<ɓœQWŠŲjœ:ã3ļöõ4eŋ­˜åÉ8 ŁúúúPŠ4Üį^^^u.0ģU;ļŒfžÍėŌĨ‹@ėõQJļZžūMëŪ…††Ę:ŗˇ˛˛RŅÎ Öåȝ”+qиtéR¤ÕņĒĨ?11ąĨEˇ˛%Ę?#•ˇ`3>¸˜Im…ûí=ČĪHÃ3)™ÉpÂĩĢĘ?P kŽÉo­<"ŗŗƒ '\ģĸ˙4Rųj=M蚄œŋm–ü CjVÔ8gÕO͐Y%Ȝį\¯Đŗ=˙dΏŽË 9˜áøˆr1wAßɜ]“#q|4Úy‡*ƒžk,4õmČĪhĮGÆ|ãŽ'đ—šqž:ãsmmmˆ'Ô†‘qĀl¤¤¤D(9 ŦŦĖÛlšYųĘ8ĐŠS'[\\\Ô[ž´Š•đ"//OŅĘōžģ_Ī;į/Ŗ§õrW„ãCŌa€•ÃFˇKãįįĻd\Ąˆf(˙Ÿ)ĩŠFÕͧ–(™‰Īų÷ā”pF+@!HL(_VB)ÍRūųQãŅ/Ä ÄĨ‹Œ$ @&?×8Žū F`Ŗį@5sžrMvxsOŒ}L~ŽķԌ ?ŖW]œžd@!˜ĩžÂqΌš'p`ūɜ]“¨YŽ š9?ē&;ŧm–ü0ŪĸŨ{ĘüXŠ0ļSgLRēĶõ:Īr`đL`{û~ũúŨŧoß>9Ûä…'''z&§Ŧ‹:::Ú1ú˙ûßg(ŧŧŧ::{îŠĪÂÂÂÂ<í™į„{\ `į‡D‡Ö#› Úː!CˇmÛfÖĮ<ąÜ€ņMJЇĐ+•Ž›¯–ŋ‰wšŒLÕā“‘-5I$b:E IDATÔā3SųÆų=)ƒ‹jđÉȖš$jđAų`ôĒ!.ÈPJ~0’ķw’1̟ãŌ¯ÕoĩøĐūĐĖ D(U‡Ôâ3 —ŌrÕâ3Kų|sÉytRmI->Ĩ|4+ŊZ|Ÿœšƒ¸ČŠ%ôûRō#N*„"{roõĢZ|˜ŪexČõ´K9Ÿ¨Åg4ÅÅĒÅųP\š6/ Íaü… Ĩž_Ôâ͆RũsQƒ|CßÕ_ōKœeLųaî zI }/šˇúU >ŌwI=zņ†ČOjî‰ōÕāĶ‹n=ōUƒmAjėq$Ķü‚ī–ŋËtRƒOs‚]ČuXÅęĒaøh3>':VcēĀ[ö*ãã€`UŦ——&]Ëe¤Otlá,#)KBĒĒĒ$W>Ë]íJ.WiéͧOũœfS^^Ēhwz§ųYá!Î ˙ᇤ>d=Ž?ģŌģ˙ūC<ųč´—ĸĸĸ#ęú„ ÆüĩĒĒ*&$$$ŧ}ûöūíÛˇoļ{ÎÖ­[‹#"" +**ʂ‚‚>ĪĖĖÄä›Ģ¨ōČ,å-X…/ĸ8šöÆĮÖļm[_>o¯UĢVĨ^^^øĖ4ņÉŊ1bĤ†††?zyyĩ+//ęÕĢW>žüøøĖTūŸ āķķķ‹...ŽÂ×ĐĐP“••U)O”‡f>HNNž0F žēēēÕ999Ü9§ŲĘ?°ũA3`zzú܊ŠŠqAAA!_jjj |ņâÅÚC‡U@~555§ĒĒĒžâá3SDNËV‚/88øLcccžˇˇ÷?7oŪü'cŗ• ÆįÍ|Ĩ _ZZv{ē.ųĩhŅâb@@ĀIģŨūöÚĩk7^—ˆÎ›đôôôŠÜú)†īĖ™3—ËĘĘJDđ™­üw‰Ģ@ž†/<<ŧm~~~¨ž .”]¸pĄÚ >:Ĩgŗ…4čą-Z W‚¯ēēzņŽ]ģļq@áŪl#„ üFõLqqqēŨn÷“ßáÇ+jjj*!ŋ–-[mll\ÆÃĮJÕĪpĨøBCC÷–••­äáƒüä8Hę ã߁á:|EEEmRSS›Í­‰üΞ=[•°dÆ Î?ŦõD"?īØQŖF=Dę§3|žžž—úé§|f;>1‘×y.6%%劆††AQQQAįΟ’ßÁƒK+ā“ĪQcSĒ¡iĶĻ>ŗå=æO‡¯Í?ĪËËëá+WŽts†ē—víڕrę'Mr#:¯đ›ož9^)žV­Z}ļcĮŽ×´¨ŸTŸ‡ 2ÜĶV™ŅT+-žÅ´ĩŌŌRŲFO3šCm€ ĢwŨ‹Ö‚ƒƒÛIy{WUUųi].íųqÎ ŋ6ã͌­ÜáŧâI[iK9+€555qžÄ>Vô9ēŪŊ{÷Œ6mÚ<tStttøœ9sl‰‰pØt`tˆ8~ü¸m˖-É~~~/œ?ū¸c2 E.>>ČÄÜiF:>„wîˆ#˛jjjēråJ÷ččč…øb×­[—ėíí=ŋ°°đŦß|iVœ5 ˛ĀgŗŲz÷ë×/`üøņŠä| ęęęNWVVŽ>tčP‘Cy̘_hRŽ1âÁ7lذ€áÇKáÃŋĻú)€o‘Ætē”]¯^ŊæŲíöņuuu7=ēI~ąąÄ~%˜u3|>>>¯øøøü\PP°îĐĄCPh_SÚæ`L$hßĢW¯6ßāÁƒåāƒ3ū"öîŨÛ}˖-ˇ|+i3DC~÷FFFvLII Bû“úĪ[kjjā=˙_??ŋ˙Ûŧys/ 䇾ģI8hĐ NõõõwøøøtR!ŋnëÖ­ģuāe-[ļ<âĀĮ5´S”2hĐ …Āwà 7Ä&''ûȔŸŨfŗŲ÷îŨ{žĸĸĸ—9’.sՇĒ\~tM8hĐ !ß­ˇŪęƒūSĸ~ĸ˙Ÿ>§ģ>šLš˛ Âãã㟠ö|_xx3›7×føŧŧŧĘ|}}wæįį˙ũȑ#3Lvœ­Äø’’z†*Ā…<ūė[ļléļe˖ ‚OĀĐÎå‹ŋÃSRR^qß‹˙úԆ `t2Ķņ|Dß #@Âȑ#įäįįŽ‹‹‹”Y?‰ülëÖ­‹Ųģwīûöí[ļ߁&CtøČ‘#ßž›ožš#žd´ŋëđmŲ˛åž­[ˇĸŽC~f­z&m iėŗŲl1#GŽ\HđĄīğD˙‚ΧŠ‚üđĄmĶbøZuuõ€ßũîwv`ƒü$ÂuøbbbūøŸ˙ü§Āßß˙Į;w>oōĒgB:æžpž[—––ļüōåË}###åâkJJJ07ëÖŗgĪ)Ā×ļmÛ-†vRžY×XāģzõjĪČČČ(™ōģ_ĮŽgīÚĩëlģvíļoÚ´ ßļøøÕUßî›322v—––ūM­üÖ­[×­]ģvĶ÷ėŲsҁo&z‰k,„Aø*++7xđāŌ^K üãšü€Ī××wÆ˙ū÷ŋÂāāāoŗŗŗá„ ų™ŨĮ49ĪŨ|ķÍ ūūūs >Ō ÃēÛ¤[BûĀ÷MōƒîŦe˖1>>>§ŒžSŽîˁīéļmÛ>ŨREEÅךššĪĒÅ×âû(øŅŋ˙Ī_|ņÅ f€Ļ@ŒāĀøņãĪėÜšSԊ4tčĐ×§N:ۓWôYĩB 2ää/ŋüâÔ4yōä’^xÁŖŒÔ‹-˛-_žĸ×ôôôWŽ\yŗUe¯†n9|ņÄū@_ĀīAƒí˙×ŋū%ÛŠGŒhgÔ¨QGsssģkI'Vįææ.5jTģéͧûI(¤%‹vLĻm¯ŋū:<Į?r(’$ßĶ+Œ˛?ũôĶ÷Ū{oøÔŠSĨî’d|K—.-‹]õÍ7ß<*ų’Ž €¯ĒĒęųAƒÅj…oŊļ7ŪxŖĒ˙ūË7n܈=ĶđåååŊ~×]wĩÕ ßâŋmŸ|ōÉÅ<ģf͚ˇMíęˆ“ ū–žžŪQK|ĢW¯.éŲŗį,ŪjZÃĄßž}ûۚ2eJđÜšsĨļ’ôÁŅõsĮŽĮž5߄ ^ČÉÉypÚ´iMõSB!-ßúõë bbb昍JÍcĮŽŨ˙ėŗĪ69 h…/33ķŦŨnĘl|ŖFzŊ¨¨čO“'Oސa0‘-?´ŋ[nšå~ŗW{0`ÕĨK—î|ôŅGC´Â÷ŌK/Õūøãƌs˙̝žē]’):&€üŽ?>yöėؚâÛ˛eKIŋ~ũŌÍ^mÚˇoßĩEEEcæĪŸˆņÁÕ°wī^Û[oŊU{ęÔŠ#ÁÁÁ6_ZZZæ… žđ MōĶ ęg׎]Į™Œ/ŧoßž_566ö{úé§ũdô$áC~/ŊôR]YYŲa äž––ļ&((hĐŊ÷ŪÛäp% @"đ͞=ģž¨¨čÜŨwߝöÜsĪ”xEĪĮáŊzõú.22˛Įƒ>¨…üļlŲb{á…ĒŧŊŊ}ē¯VøæÍ›Wŧg˙ūũ)’×1đũôĶOSSS#ĩ‡o?č^:uęôåĻM›&éHždÖĀwᅏ{öėŲYk|juKTŸģtérbÛļm1’œd 4áĀÃ?\ö駟Jî+ÜĢW¯įΟ?Ÿ5aģĄ™Č0%Θ1cĪŧyķ ĨËėÂäG}fŊuvVVŒō÷fee‰ŽøņÄū`ųōåļE‹9uV@Ž?ōÍ7ßt3ģ~›Yū=÷ÜSđÃ?`g-Bė°aÃ>ÇĪĶO?íëĒŅ™OŒ´0ōmÚ´Š M›6fQ Á—““sĻ  `֑#G ] F>|âããģÁ¨§—ü`äģtéŌcfāƒâ!555æå—_vŲh)V?7nÜåælŖņaûąŌŌŌ÷ƎŖ…QÖžĐĐĐ?ķļ$å'×üŪā6›mŠžøž|ōI[VVÖI3Œ Ā×ēuë×āôĄ‡ü`dG˙™››{´}ûö÷mD1 ß˙ū÷ŋ˙ū÷ŋī0z•œ"ŠŠŠ–Œ9ŌŽ—ü`¤…‘Ī,|'NœxãŽ;îG˙Šu@ũœ3gN]QQŅūŦŦ,(á ]eƒŖ JKKŨ˙ũ!ŸÖøĻL™RßĐа/++ ĢØ ŨÁ;)444Ė…Q] Ŗ3Ÿ?0ō=ūøãõ~~~Ų™™™ˇ; ÔÕÕ=§=đÁļhŅĸz›Íöo3Œ Fā{ë­ˇę žŲšsįFËN§Nš:kÖ,Mœ"øõō{ã7*Š‹‹ˇ˜/%%å—/_žįŲgŸÕÄč,„F貲˛šššĶ–_||üĮã^ũu_-Œ˛Bø}ôŅZ//¯Īsssīá?×ų;aŧ2vÁ‚Mģ$i]Œ´/žøbmHHČęėėėŋhŋD~á|7""bøŦYŗ‚ôŸÃš2::z… ֆāûāƒĘŧŧŧŪÜąc‡Ņģ˜59íØlļaķæÍkĨ—üV­ZUėããķŽ™ø^}õÕVZÕų큥ŊŊŊ:tČč]ĖÂSSS3ŊŊŊû­\š˛•Öē`Ŏßûīŋ_:OÉ._TŸxzŨēuQ|áą{ÆÆ}8 Į‡’ ”šbŊ[ŲÖÛúČAĪ\§Nzöë¯ŋvÖ¯Ÿ1cÆfO3>Ë1˛&$$}ũõ×ÍÎ ÔS^fį ž,\¸píĄC‡D?Ä<ą?_VŦXąôûīŋwē‚ô–[nÉ˙øãąÍĨĮ†I“&•lŨēĩĩĢ €âŊ˛˛rųķĪ?ĸĮ‡—>ĄįĖ™S{âĉ­[ˇn…W÷€3IKKKįŊņÆ!z|øp|GÍÜšsį8î3Ŋ~CąŲØØ8sõęÕ!z|øpé&F‡’Ķ0|ĨĨĨs6lØ šS~|………ĨD‚üZ´h1ûũ÷ß0B~0ĸøûû˙ŸQJ$(Ļą ܒ%K|ŒĀw˙ũ÷W]ŊzuŠQJ(Ļ###˙äŽøøšŨnĩjÕ*WW:ķÛ˙F°YŗfAɲ(33Ûô逯[ˇnéK–,ņÕŒ(Ī>ûŦĄø’’’ļ÷ęÕ+ŲH|3ŒZÅ|ŠŠŠIXMj„üyä‘ĘŪŊ{O4j{jjꎁ&ëáÁo\¨Ÿ3gÎ, ûƒAJMŠ[wƗœœŧsȐ!ŨôpúāËF°ŋũíožžžŒ’_÷îŨ÷Ũwß}MNs|z´ž‡‘áwŪ1_§NOŸ>ŊN-|ūßōåËĪÆÆÆŪfƒY¸‘ōƒe͚5goŋũöT#Vyc5đúõëŗf˘Ҵ“ŸßZßß|pōȑ#ũŒp €ĶņņãĮŋtW|pĘ­­­]ûÔSOŲĩØi@JŪp`ŨļmÛҜœœFČøŠŠŠÖŋũöÛáFč^P?Æwúôé¯?øā]œ>¸ō$ vîܙc”ƒ äwųōå/Ū|ķMCtgŸŸŦBŊ¸ 2ųwø 7Ü šĶdYņŒnÅŦdNJJē[ TAAA7Ģža,:xđ í̝žēqø#q§N‚×ņĩŋ>úuągiiiS¤ÅoäEŪŋ–Ą~œ;wÎ_ŠL=Īī%<%4€‡ā;Ž$žšx\ąâT¯PRR2ÚYŪ:t¨vö\‹gĀü|>ņīņH :”äaT ‡FļËÂÂÂöR|jŨē5Mķ*)ruyŪŖGœŗ%yŗŗÂĮ˙÷   U_}õ•î“gĐåđĒUĢ|'L˜pkJJĘ˙Č9hÎhtåYFFÆgíÛˇøô6ôe§ˇaĖFž }ļō G |“ŒĀ#đųųųŊ€ąI˛‚š– ŧOŸ>g&Ož|‹Ņø0§ĀÜÂ5ō%ßīÜšs!đ-Y˛DwÃ3¨ü˛˛˛ >…S$….$€áō›4iR˛ģ}‘|;wî ņ÷÷ß§(Ą4ZÅÁpŌˇoß#3gÎ4œĩÂ"”đ%%%ũ˛páBC Ī šO?ũÔŪĒU̝õÆÅ;đŊņƆžĢâ˙ņ4á›8qâ_…øŽUđõėŲķĖúõë 1<|īžûnĮÂÂÂí=öØĩÂ"”đuėØņÂęÕĢ “ øĀˇnŨēC8âIˆ.­â?ĘAyzėĻ D'đŸ¨7ā¯P­âP˙QOĖĀ7pāĀ“FāC?†ūĖÃ3ä‚qũ5ú5ŊņĄžŨģwënx6ĖoīąĮk‡ņIĢē(”æ˜G:tHwÃ3æK˜W`ū$D—VqĀWSSŗķ%Ŋøø0/”ƒ&%iĸŨnĮÁä,PÂ!œĄD(†žáO(?J z,0:úøø¸ŨļÂÄ …ķãaĀÂ=Ž0 ÃāN y§OŸž‡´aaŋ{Œ:=f˘Ļt0”â}Ô_ŧ‹+î­úôéƒ-ŊDCFFÆ-ŅŅŅĸĪ]y>s Ęāø‰ōČo!š2ņ.ō0)„ĮÄÄčŪ6P‘ģĨ=ŸoÎx¤5oíĀéųß5Z—ËĪOŒl—ā‹ŸŸŸŸNŪũđ:´åÅé~ ^Üļ†8>u'ÄQƕÔÔTÕg>Ãđ;Õ(×/P<÷Üsą}úô9¨×GđuíÚõvŖī\|PBā#ø`ŗå>Ķčw8đũū÷ŋŋÕ |øˆ6mZ‚œ ´M†!8)üũīzÛ0š“|wŨuđŅÉĀ~Í0d>|¤ë‰†!b8iÎa}cĀSԝîŨģŸĐS~¨#¨+FāCÛOHHČÕßA´q#V|qųG”’’’nE§žĻąŠF3đaĖÅØäĀĮ…¯ÉoŒŠPĀИ&„C~gŒŠz⃂ŨēuMgĢ“˛¸x:vė8[/ ČoåʕG͒đÁŠMO|ŋüōˍ2,ēų}öŲg­āā‚í†Iŧ–WȆĄˇŪz+ÂÅ4—v8y}ņÅpp‰ƒûLĢß8†ÁËË닏?ūØp|pPžęęę˙§—ƒ CĀG#œVšr!Xûöí{O/|Ä0§N3đ=ņ=zô3ŗä‡rõtß.\¸°Ė(§j~ũÄ.[h8î‚ûLĢßčˇĐžŅ΍pZåŌūũ6đ9Ž â>Öä7vKÂø|Œ oã“^ēė&„ųæFã#Jp<Ņ æEppŪŋŋˇø0/”ƒã3V_r•īFVxV–0ø8¤2”đã ‡Ō›køÃģBų —ĖbâŒ€ååå’^8ņņņē›´Ä \üãoΙ0:ðŒēŠ€{ÔSÔŅ3f4ū._žÜdp†ĄÆÔaâĸĩ+’ ø6Ã0Äŧ68€@‘Ėwõ7ÂföíP_rrō­ ´Ÿ†!.>8€$&&NÕÚĀ|mÚ´ų'đmâチC|555/ŸŅ†.>âĸ5>Ŧ˜mÛļíãf†øø@‡Ö+ô‘ę…†!.>â‚c­HŧWÔŗ {„~´{´ĒĒĒ×´6đú-3œâ >âūøzöė9Å §q>>ŒÃZãƒa;Ŗ)Īč+ä‡ųĄø0߃ã¯NㄏæÁÎV°Sc|ÆęKŖ•ļ„Yėڜb8!C ?NČđ'–_ķ’YŒŅ •TÎæååžšĪ>ĀPƒęŒX¨“¤ž"_6qOŒČHwÛmˇ5 5ūđļäFZJ‘Gzzz“ŋ-$el!š€Ȁūõ  2ŒŠŠręxAę‡å“â8@>-ˇāž>úČ4ÃˇOCũLHH¸EÃ-¸Ãq†į|h–aˆ‹ ]ētšUCQøųķįˇŧņÆĻž¸øā@€ķĐĩÄ…0VÎŅ ?(ccc§ie#Žf.šōƒ UĢVOhĩE5ÁgƊ=..ōø***æjĩ…3qüĀĘxęį’%Kšä§>šNž<ųōĒUĢ|Íŧ‚ŽŠŠŠZĀ73qĄlÔāĢŦŦ\Ž>Ô´g3 {„¯Āˇ}ûv_)I/ክæi‡~üėŲŗßĘŲâX>âøaĻá’Đ ųa†ü´rĮ8Úĩk×{1o0;æQ˜oh…ķ Î;§™éôOø |˜Ÿ:uę[1|ÔŸąŌĘhĨ-aģ6တŠ„ %ü8!ßX~ÍKf1Fs {÷î8kŨéö›111FĶåjy¨‡“&Mj2brˇŖ…Ą+ššũ ę'×ĐHŒž0@cu.VAã÷ĮÜô››ŸĢtęũ>č2dˆ¨6F"¤1;påBVĨëMS^^V… đŒÔÁEĸŽ’đå—_’Ÿ‚W.hiˇÛ/;ËĘĖū€ËŖÛĨÔvģŊu†+ĄņVČPĪ}Gīß  į‚+)^Ø ÷Ķđq@č†āÉ'ŸlĢ… ?­[ˇM>Ā`ŒĶ€BĶĮ‘ŒÄˇŨv[ŦVø Ė§Á1‚ā-ũû÷O9räG$Níį„ŌâA0߁“áąNâÔ^‘xE›ü`ĶâŒVŦ1bD ŽDFZĀІ˙ô§?Śš"˜ā"WôåPøha ĸÉņƒ‹¯E‹c´0a ĨÅņƒāƒĸĩ¨¨čĪZPhrüāâƒL vüxéĨ—"hpŒ ø`ČiŅĸÅl-đa („ÍZ1K0q¯ZĀhrŒ aH…ÁŅŲ )’VâūÅ_ėĸÅņƒĐúũ÷ß{˙÷ŋ˙]#Ļ€'éd\ÃĄČ§ÅņôÂĀųi…¯ąąņSZ?ž÷ß?;uh!?äC‹ãÁ‡úT[[ģV |¨Ā'Ŗ.’DŽL.!čŸ~üņĮˇhÃˇaÃ_ôërqˆĨÃøyæĖ™%´8~€NŒÃčĪĩØ øāHBƒã‘æQĪ?˙|æU$NíøhqŒ €īЧž˛‹áŖÆøÜĸE K­Ŧ$ vį+Qĸ° J„â„ Bųš3˙Ŧ‚-""Â/..î×ŊĨ…‰N4ãSaRäÅb‹wü! .˜‰Ŗ1ę!ūHœ'Čo¤Á ØZ\‰‘ ëĩk×^—ž)!å˙ŠŠŠÄH”ÚbYė=5ņ XåKvģ’ dž›uÆļ3ž Ž^QGaL•„x$į=5iBCCÛ8yĪ´ū€ĪŖÛĨÔ§OŸŽáÖq'<Ôô‘Đx‹¸†zM ”‘YÛļm[ĘHF’„à ›ĻB1€šh`ˆ---GĶĮ (ޜ‘…ķÆÆÆ™4xŊ\ä ˜ĢøđqWZZ:‡¯p‚‹\aëØąãŽlĄ‡ķÃĒĢĢĸÉ1‚‹/22ōOޜq†w‘øP§Ā{Ô1‚YémĢiđzįĶ|0šb`>´ašNôéčÛĨxIzĄ+ƖÔÔÔ$ņaL†Č|Ø>N$4âƒBųôé@n8* IDATĶĢ]QĀã|^8ļĐäøAę `ŽP€oܸqŨhrü>°ŌÕÕãQ⏃](h2Ŧ|0€9 DD¤Š¯ĀG›ãÁØącĮ\Ú>=--m Ũ4ĘO  ޝxå•W¨Å'f`[Qa`ÂY¯49~€vЃ:\5€á}äƒūŠĻ€ö‚zåęņ(ĀG›ãøL `ééé8>Ku@˙D“ã|č×]=~ã Æë'Æeņ…c~@ˎDv¸b>…Ô\ÅG[¸yĶđķaĖ1äĶC‹ņ96999Oģ7B8!C‰Xßđ'”ŸyčXÉ\Āëīī77Ž÷›Ž8Ą[`‚Ą˜.ą-- WØÚ&L¸ļÅ1V=sļßFŒŌ¸b•4V#!ŽÜmnšīŌø›xÅh“ÚJXė=9ņ0‚‡ā=dGÄ! ^ ÉōƒĶ ŪŅ3ôčŅÃé _=ËFŪ¨W¤ž+ŲúĪ7!éI›Äļچôrx`tģ”ZņŨšsgÃwˆĀXËoQ7PgЎđĮuŽŅŗŪpķŽĒĒ āŪ;ûššš /PÚ>~Í0Z8qUuذaŸŅŒFãĢW¯>Ođ*Ŋ^¸pácŦZPúžQéĸEjË+--}^æjß×û=ā;qâÄjË ųÛʕ+ŠYĩĀĮXëÖ­_ãĮËŊoÕĒ՛4:ļúÁ{Ô1r¯ôZ\\LÅvŽbtCąåЁĄ´´tŽ;Ā‹1 ŠIôņbø%âˋ‹_˛d‰ĶŖN$ōĐí1đaėr_CCÃ4:~€iæ *™8Šf|ožųf t*ņÅvęÔi Ž-D~?ūxøĀßUƒN1-[ļIŖcđĀöāƒڅ¸rđŸŨnE+>PîģīžĩģĀi ŽI4:~@>Ā7vėXÕøāTد_ŋŽ´9~ē|C† éĻvû{āČO­s'įhƇz…úĨäN3>ô íÛˇ Öyũú'Zë'úuôīj;ą#ÆÚ?H_|žžžŖÕâÃŧķZuKĀ1\->Ėë^xájug˜7būČםQa|ÆĒKŦž$•]Íį€˜Žo(2žū„ō3%Ŗ€‘/,,Lt‹ÔÔÔTÃˇ’uU2Ā„?]°RõuuįΝM÷ˆÃs‚ņâÅ׊Ä*Âyķæ5MqÅ=ę4ę0 b0 "?ĢĐęLž5zaÁsđ|$ŧ oÅäšđ,33ŗ‰˙zĐXRRŌ^,_#ļ#‡lˆquüAāķ͏Äčw%žC‡Õbīc;rŌvÄŌh/‡FˇKĮĸ_Ʌ……ŋ-íׂ 2ōovV"3%%%BÎj)LēŊŊŊûŅĒ\!ØfĪž­J‹-=ŖŖŖģĶŽoōäÉj阮Ō">>ž­¯>ā{öŲgŠ^\|˜ƒ¨QŽ7îį•ōÅČôŽšĮ05øāt0kÖŦ #éUZQāōœrō)//oéŌĨ˛%åäŠuākll§—,YBÅ9ēb|‚úĖ™3ĶÕāĢĒĒZũôĶOSëĖĀwųōåÉbøÅWUU=o|EEE3á{VZZú˙hul!4;ęį¯[)’H™W8ÎŅŽŽ}¨g2!]—ŦŽŽîAÚņĄũĀõ:ÂåŨ„ŖŨŌŽũûņãĮŸŲ'Œ7ŧŦŦė^Œ/4‡W_}ĩÆi4†ĶėXFđ`~ĨæsaaaCiuŧââÃą4äW*ŒĪXu * ôp€‰°j‘‰„ %Bq0ākøʏ´Œ’ÚÚZŅį’’’ŅÄ(f%NfšF2Ôc~āŋĢ$?~^fß„‰ŅĐĨKąGēÅķyĢ[A—••y‹%Qzf­X>Rņ4Ö̘˜˜ŽbŠ#ļ#—â÷š ü[ũšč0¸rÉĶũ7Úū¸ã­ŅÎ B !9Æg(Įh^uI°á-((hXģ éø×Ç/§]yš¯¨¨(M)>(-h˙8'øūûß˙NPН  āoV‡•wEEEâ×?Šû8NŨwß}!JW¯cÕ,œîŦ€bJ‘>---‚fĮ2"ŋ?üáĒđŨ˙ũT;Î|¨gŊzõÂî^˛VÍÂqŽvųĄũ ŸPęˆūíV6CLJ|7ß|ŗ*|Ī<ķ Վ;`)ÆgŒĶJW¯C~ , Úą ø0ŋÂpm/-uŽ­ûpĄ9g+|›§ö¸ŅžR[šģ+§ā$‡ „đÅÅÅE™ÕO ÁB4^%%%9;ÎLCzöėŲ™vå á›R'>–FŒŅÆ*ø”*8ņ1oå‘dņņņo‘{Š+”1´¯šåb€" !nœŗßĀgå0@Áų)ŲŪi­ #øîēëŽļJ€)))¯Đžj–Ô?ČOŠ‚mÕ Ę1`TŖā´ŠrŒā0`@{% ¤¤¤÷h_5Kę'ä§’ŗ×Ο?ŋ ĢrH4_Ąā>9Gqqņ;´¯š%´Ÿ¯¯o%øŽ^Ŋúā¨ĸ_YYŲĶVpŧ>5ΝVq|$øjkkG)q~,,,|ÖJōSęxöėŲņVÁįš .L$ũœ+Ō[čŦŦŦ|D.’Ϗ¸ø.+8΁^āËËË{Đ.ã^YYų;ĢāSáÜnĮ@ČĘ1N/•!7’$ã%ÆM+Ėŗ°C­ą7Zœ;1Ÿ$øDW}‘F\}||¨ˇÜÁVã€YŠŊĀØ´—OļlÆy´Ø*™kr⧟~ŠŖŧĸĸĸ•§Ę6<<œŦđ-áķĮnˇ_öTž8;kũvÄŸ§œ l6ÛG|ėŖŲ.ŋq m~‹iūËßßîôéĶŠ÷ŧ%”cĸ˙Ę+¯t%÷R×6mÚ<9}útË)|O<ņD_)\äyMMÍCVų8Í uųōåXŊ.+Ūkå9đŨ* œÍf>ĢÉīÃ?ŧĮfŗÍ—ƒąŦŦė>+áC]ÛļmÛŊ6›MÖųäåååˇZ ߞ={Æ:tH>///jĪōǐÅũ÷ß˙Đü@(ũå˗ûZEyú1VŸ8qbڑ#Gd­0õööN°žĮ|ŽÍfÛ&$/~đYaÕ,Ąûž{îņŊtéŌĢ6l¸ƒÄ9š†‡……õ´Šãp˜93Āßß˙…M›6Mr‚‹< ŒŒ¤~Õ,!–ā ~č›ožy”/ō;ÖJŽĀ0f˘ ’’’ĮvėØ!g|íÚĩkÚW]reGŋĪ?˙|bNNŽäÎp‚ôˇBĪhŒÃ<äÆolsäČė<ØL_˧yäȑ/L™2…úUĪ„nāÃ|Äáœ$ ßŊ÷Ūk)ųa>IđRa|ÎĪΝ2dČIBģ20ĐÁÂÂÂu .čáqqq‘ā‰Ud!ļ0kŨēu[Ģ`ĀWWWw“š/22Ō2Ę[`B_ˆ>_ž &ŒŽŽļ>ŒÕĩĩĩ⺌r˜€rđĨ§§ĪŸ9sχPåaqŒŒæsāįŸ–ĩJÁ|Jj8püøņ?ĢyĪŨßŲŗgä°ģķ@넸bŗ9øÂúJaö4‹uxö[Æŗ˜Ā„_ŽøRSS-ĨœF(LŽ;6SjõļÜ6l˜e<‹‰ü€oåʕsl6›ĶÕQ0.XMųŒÉÉÉžåååsĨ>Đáų~ë­ˇZn×)Đ\[[ûŒÔę/ŦˆĪņ>č¤Î ]ĢĒĒæLœ8ŅRÆā=zt@PPP“B‰>Ģ)oA;úüN:I:'üuÎtCÖ ĀרØ(éœTZZ:ÛjĘ1Hâwŋû=::ZŌ9ŠĸĸbœUņíßŋ_Ō9ŠŽŽn‚ņÉuŪ)..Nˇ>%ÎIįΟnĩū“ƒO˛Sŧxņb‚Õäį¤ęęjŅ]踠O:•b5ų)qž*..îm5ĮcĐ ēšrûmU|r¯P­æxŒūbá…Ib2ãÆÃIũ‘•đÉtž oŨēu¤ņŊûîģ8zB*4íj"•ˆļįčī?ųäėŦ(Ĩ{ŽÅ<•6úĨčAũÜŧyķ›Í3ŸĨfĪ܃!!!SŦĻ|įAs~~žäę6l)n5åđáčøņã’č ´šr ø “ŌŌRŅķėIë˛ĒqøNŸ>-ųĪ~Ģʆ"'ąĢ ¤~ĸî‰á"ņ¨ÃVė_@ŗ|ųųųƒ­Š†e"'ąkqqq/+ސ‰——×Ãb¸H<ÆH+âCŸĮ‚CėZ[[{ŖÕ”īĀ|)))O‰á"ņåååQVć#8Vb×Ö­[Û­Ļ|™ø,i\>8_aK_1š9â›đI¤ĄōņØącCēwīž!A\lßž}Ã$ŌPųÎIpŧ• .6!!ÁrŽĮĀ”––ēĨžé$Ø@ßcGŊsę aUĮjp;CIģŽūN’ôIGš"•eā›hEĮjŒ×ˇĨ¸`UĮjˎä8'5ę!+݁ķJȏŸĨj1{Î8Ā8Ā8Ā8Ā8Ā8Ā8Ā8 Μ9“bEãĐģwoŠŧĒĒĒ+~ _‡ĨíįįmU|W¯^•ÜŽ8<<ŧ­•īø@—ƒĪËËĢŊ;ãŦh\€LZļlyƒTû“#cŠ<Ėx|’íONdũReĸODß/•.!!Árģb𕔔$Hā :th¨D*cN‚š‰qąV5.ĀøÜĐĐ0HßpĢâƒüZ´h1\ _Ÿ>}ŦĩŦÍōkÕĒ•SãW÷îŨ[ҏˆĀײeK§ō1bÄhĢm™Nę#úOŸģÉŊĐ5==}ŧÕļ'8 ŋęęj§ÛâÂųéŦ ŋ”””ŅoÉ][€ õnĐ A8ÃųZU~Ø*99yĸ3|čŦŠũ>úgøÆXU÷ⷝvÁÁÁ–Õ-9vËsŠĪĒŽÕ¨“Äy‡ŸĩPĪy†ŠO'§žÃLÚĮ;Ō“ <ΉĀûS9éøqxŽI3Ū%A(/ōŒ]=›Buƒ_§Ā!Ą8WëŪ'D ¨ģü8Ąr'֞@ĢÁ‰+ Fá*[,ŽßŸtÜ>Fˆn‚Ié•ä+ bqzĐ&V‰'4‰aæÖ?ŧ#VOI>r¯¤|\IŠŖ ī¸B›PYrã„úgt|rŽriJ§]Ü14Ŗ,žÜĩŽ“äM—.]…mŲ˛+om¸Š…+V\÷Hč­ã¸ļo߆ÔŅų5 BtņéåņËāŌ į¯¸¸Xđ<]đü­[ˇŽ ëÚoĮڀhČĪĪ4žĐ‚oīŪŊĸøRSSá< X˙`oooÁ-ˇiĀGh\ŧXxįénŨēĩ"iÄŽbihĀ\ CŦ ÖÕÕ Ö=VQã øJJJšę&hÁo~¨¯¯——(f2ÆĻ'Ÿ|ōڟ Åú~†;ÆNÔ¯?ÍÆ*С€ü …ž={JŲ–čííí'ô. ø61|bs‚§{÷î}Ä[ŒÆšøuĐ ŽÕCW Ąëˆ#ډ9&9˛ä%‡Ą|„â„đ‰Å!øŠŠŠœŽŽ5jÔ(1ã‚ ›PŲBīȍËī Í_ —üüün\zøŋ[ˇn=F2Ą D?–ōĘËŲÜt_šrÅ)žÂÂÂŅzÖO! Ło||„—bsÔģ6mÚ8Åwūüųĩũ Ÿ!ēåÆ á“šģ@~7ÜpC;ÂĄëÕĢWÛ¨•ŸŪø¤æ.ĀįããĶQ‰kŨēuøäĖ]Pī{,BWÔ_ĩũ§Ūō“šģ@.555N¯Đ˙¨í?õƇļKū„dēŅ˙ =#q?h¨Ÿwœę›Ũ¸ĸŪaüæÆņŸŽxî•J¸ãeĒS…œƒM0@ĢGֈ&Ęlî×*7å ũ^‡š4.Ąŧņa†Tō!t‘5 üxÎŊ qųāBVÜķƒÃđƒv-{4ā#Ŗ§PpîČX&”DиG >´`Sā9ŧߝÉcIŗ@ >(¨ą˛•ÎĸÎPN)WhĀ<¤Aĸ3**Ę閩P.á]~ h€uWÜķCeeeŗą›FˏG>ô+ ƒŒBõShnÂÅ'fÜ3hB“šqiĮo1Į1’NˏG>!Ė„nr•rŽ8wî\{’–{ĨŸœų‹>Į„ú%Ŗņ ÕMÄ9›ģ@vģ]Đ1ŽČ ÆMŊúĪ76ûãĄĪŲÜi***sČûbÆ=1:Č{|z„ŌˍCžüü'5wAŊƒņ‡Đ$t3î ŅÆ}ŸOPzšqȗŸâ¤æ.ø6’rū3î Ņf$>9sŒ‹ūūūNåwūüyÁųŲøPžÔÜũFė˙oī[€´:Žô.â%ŒÄ ,´ŽFHB62¨@ØĸÄh5ØR‰”qaŲr*NEŽŗIĨ’ l6‰wÄ(ĨėVŏ]É؊SČą­¨$†0”Y=AŲáÁ @ĖH3Ãh@`RßĪFMĶ÷ŪžīĢfîŊ}ûvŸī;§ˇOßū[[#ņ…-Ė­ŸÍØö9eʔëUģŌĪÃו‰ĪÔĪŕh_Ū}÷ŨkuLęĩŠ´É[oLĪØÆ™đAFŊ UnœéņęĩiqĢI.õœëe›žą3á3Åé2Ā>1ž¤ķYgæâģÆÄ´Ėˆc8Š8x?ø,AŪLáŒÆŸä…<Ô8Ä̟+ÁI–WTųŧwq0fĒM z\V[ÃŦØ5ėTœ7 føCŧМ°ú$Ž>‘Ųåc•øMe›âLÜC'2)‹s<‡ ë­ø`’ÃW”lϞLq&Ėēũ…ŲibRę<ëõÅV.”—U6SYļqaíL6c+ƒ)]^réŪ“ŪķŽŗą!Ŗs/Wœ"Č•ž™î°1=“wœ.^ēģ쪜ŗķnŧņÆÉús&šô4EãCyzē ĐÁŪŊ{ŖœëÆ/Û\ĀŨˆCįx™Ōl+fu¸ô#į=ę>čæĄ‡Đ<œ āģãŽ;ĸ~w¯Uę™úŦ ø ôg’Od­ßģx=ÁŦYŗŽzpŗø‹ÂXgęj×aÎ=đa¸0ŅązõęZ;nņáøė‚ÛCCCŸvU ¸Đ†bĸĶ´}áW\1^Yčwž°/Û\Đ„… ĻöĨūUú…ŦŽ4Ėšį>ôb[Ā™ôP_Xeėvppp˛Šî–˛č}š ŗgĪ~?Ē}™4iŌĨ‘ʍMŪ6ō˜ō1řđ…ÅŠrsæĖwõ8\›dSĶéō˜ŌÛÆ!_=?›ą ž ŗ?‘ÕäÜÃ=“lō Žē<ĻôļqĻü7vAûēoßžČ/ķ'Nœh\\g’­L|6c´ąq‹?ÂÚ×2ņ™úš¸ōÁ5l8ng S˙h“wžöiÂfŗĒ vNPã´ķæŠS§~ ÅÅÖ=¤/_fUV7ĶųŦ˛ÂsđB†?Lj¯Qœ=ˆÃ,âdRHCŪpaÖ ĪÂA–—LĶØ „Ų†jS&;Cė,‹­‰m‚aä‡?Ø6âwÖ¯qn*'L+"wØ}—âĢÄo*Ûgâ^!€Kđk“ŪŌrm’ÃW”lϞLq&ĖEڟISœI.ŅîĨ ϞlãĀ ūÔū,LΤōŲĘ`JW¤\Iq‘ø9HßÔȝÄ&“c&įîÁā$€Ąđ‚‡?L˛āeR^æÔĮp``ā:5N=˙ØĮ>ļĖôœšĻĘsč /áa˛Ī;÷ī‡Ũŋė˛Ëū¸ļo†ƒ/isî%ͧˆô†?Ø&đáSœ.$Ī:#ßĩēėĢŅ▩ãžŦZ“#œâ„õ¸?Ž˙ļ&îŖL8LyÉķ<^Ü ˜lCˇ)0‡x„4ļģÆĖ„ĖN`vWR hØ/ĘĐË ĢOH<7Î]Uâ7•mŠ3qNáЄ3Â9ô¨ę--÷&9LqEÉf*˧Ûę H^Âė4 7&Lqē\RDOie3•egjgÂäLʍ­ ĻtEÉeŌ{Ūq6<ŠeޤW'ķņU’ãĐôLŪq#f<1É—Ĩé™,qqåešo’+.?Ķ3YâPz˜DÂ&ķ &šâō6=“%ŋI‡?ä†īĒĢŽz;LļW_}u+œgz0ÉĨ§Ņ¯MĪd‰CūĀ„‰$´&|ˆīééŲĄË"×gΜŲ#įęŅ$—zßtnz&Kt‡—ķ,aōäɃĻįMr™ŌŠqĻg˛ÄĄÍ†~䨖—åÜ$W\~Ļg˛ÄĄ<Ô“]ÆÉwß$WšgLųØÆÉ.ôtž8M2”6 į:ūđĩFH¸°Ŧ'<{öė^ÚO“Ü6÷÷÷÷DÍõč’LL IDAT455L›ˇūœIÛ8=¯ŧŽÃļÍ5ÉWĻéÛ¸°ŧeüĸãŗģčĪŲ^›x{VfæĖ™# ­@;¨§‰z/‰ģŒdžâ$‰ČŪ”Ū6Î$žíØĨž­ŋ)‹ZܨQŖŽ™nšd3Ĩ“8SzÛ8ÉC=ڎ]Âä—ŧĻOŸ~TÎÕŖI6õž~nJo§į%×yŒ]ĻNzZōS&ŲÔûúš)ŊmœžŽ‹ģ “l&9$ΔŪ6NōP Æ.ęcÖį&Ųĸ6ĨˇCžz?gz6Ēü$÷ŌämzÆ6΄ĪVŪË/ŋüHDÚīŧķÎú}“\zũÚôŒmōŌõ§– ;vėīč|VYš8Ī1 rî-ėœSŽ6L†ī¯ĄÜU?ޝ̓û˛%(Ž˜CœL–ãY\ëqk•4p6 \S^§ˆZgĀdēMáÚ‡ŲfąĮ([ ŗsØ5ę„8Įʑ8Â~Må˜ęŌÁŠō|eāOÂŊI÷˜EÂļ˙øCģ%m‘ގ´:0ÉaŠË"[/ŲT–)NˇÕoėĪd§EķĸË%uH¯&Ųōā%Œ+Ä˟ôgy،Š<Û8p%2á&W/Đ'Ú1<ē´&nķŽŗąŖ]¯ŧōĘl|•„HLâ`bƒÕëŽûpaÜĮ 4ž¸gz&ī8 (įęÛącĮNęĪ™ä*dŌ9Ôå„LmmmŋÕã•k´qđÁ.°% ūÚÛۍ0ØĪ•W^ųė>Œ0.Žpę2I†:™ôũnܸqŖ¯\09O\ÁĮl0,÷‘#G^ ģ˙Úk¯Ŋ,툚Æ|hĶDĀáü % ˇˇ÷Yp WđÁļäģ„„Pįۜ9sžwÕ>Å$›ŠņôéĶøMČ TO„ļÕdߎčOGā™ôđöÛo÷×Į”ú­ÚuØâđAwh¤ūȗn*­[ˇ¯ŋCĒŅ#įãĮ0Ų@Ųø Ū—›dŧ~˛k×.|šZ˙NžÛąËŪŊ{ÎIÁļmŽIļ˛ņĄŪČ_ØØ%L~Á÷Æosžj|ĪÔ.ˆÜr ŗ?šßßßo›Vö7v†°ßä|aíOÕø ;ÔqéģLcÔ´˙‚Åt ë?ĘÄgęįLåëōƒƒ–––}zŧzmę˙MyĢm žĪŗũ4á3•ĄĘ-÷ûûû_×ã•ë{öėiRŽk§.ā ÃŦĘ Û5jÔ;jĪ/nÄm˂žÛqqĻ4(OÎVĻk|tÛ0ؐm\[ĮĘpå¨ųģt^%~SŲĻ8“>LÚĻ3=ĢĮ™ä0ÅŲ–i›Nä0•eŠŗÍW¯_RNŌŖISœ­\(?‰lϞlãLe%‘3Š+[L銔ËÄmŪqQŧŸûÜį ]]]!wÂŖMĪä'Ĩ¯^Ŋúl[[ÛũQįΟ˙Ž¤×&šô4úĩé™,qzūę5đÅÕŋĀ'‹ËŒjėčččU9QĪMŧĢ÷MįĻgŌÆõööž5=+åÖõ‡6%4|ųË_~OŌëĮ¨ŧõ´rmz&Kœäk:ŽZĩęũ˜Å|ÍHczq&šÂŌJŧ陴qqúģ÷Ū{BwîF+tLr…Ĩ•xĶ3yĮIY .Œt. íqßūũûÎG´1ús_œ}ãËŲЀžq͚5đ"&ģ’{aGĶ3iã ģ(ũal [_ĩˇéąĮ ĩôöÅ$ˆ‰Iwß}÷áã‚ĐĐŅŅązũúõ’ü‚cTŪ$ŽG˜žą Ë3,~ŅĸE‘ø–-[ö—Ϟ%ŋ¨{’F?šžąĶķŠģnkk‹šœæÎû¤ŠlÉ7ęž¤ŅĻglãÔŧâÚ¤u_l*Ψķ t‡Vž .\ø“;w†fa’-4qČxĮ”‡)NĪ×FwŨu×/ĸđ-Y˛¤3Ēũĩ‘C•Ë”Ū6NÍGÎMĪĘ=āīč訅÷‘.,DåozƔŪ6Δ_÷°;č' ôkĘWâL˛É=ĶŅ”Ū6NĪ/Î>‘/ęW>´?zžęĩI6õž~nJo§į7vAžh˙}Åŧ&n„Ü[´hŅ÷ĸđĄ}•ôú1*o=­\›žą“{âĉ‘Û:ĸoŦo]]•˜ŠËž¸mą#ī /ŧ§ãÔ2ų ļ–œ5kÖļ˜2víŪŊÛËÎ[?Ž3æÅ8|ŋúÕ¯cŌ8yúkjjz)J¸ŽŽŽ—vėØáeû ||đAčOwggį†M›6y‹īĖ™3į~[(D‰7nüxđ1 ũŧôŌK#ëߙ3gŧÅŊL˜0áą(Ũôööū?_ßmoÛļm…úõÚ ´QøĐūøZ˙ĐîŖũÂ700° ũ¤aÛļmƒŨŨŨĪEÉ><<ŧĶW|õqId˙7uęÔ]žâĢ+ˇĐųeÁŧGČ d€ 2+3fĖøkĐp. ĮMžãwmÖûˆ¯>ųš- aōäÉ?ņßôéĶC/XŒ|úôé>âƒĖ---‘/įĀ|>N°^žEOaG8X|Õt†KâaÞęīäɓQŋˇ^ƒ|>NpÂæĐ6ŠžÂŽ---Ũ>âƒÍ =†K⛚šų¸8 ú{˙ũ÷aĮ &đqņô×ßß˙?ÃpÕãûz{{øŠoÛļmߌÃ÷ûß˙ūŨ˜4NŪ†ķdãÆßŽîĀΝ;Å¤qōöĻM›NŊøâ‹˙=F¸ŋøÅ/ŧÔœ';vėøa ž]ž.Ūyúé§ûwėØņ(|Ûˇo˙ģW^yåHTWīaŅ䏒īĐĄCßQi\ŊWˇģČņ5ôûėŗĪšŠ!JŽzģšpíĪ3Ī<ãåâ$[|hgŖxrõ^Ŋߎ\‡ūßĮw#ŒˇŽ=úf‘ø6oŪü}|w>Œ+a[t>ģZÃ( d€ 2@uëÖ=ũŌK/y÷kë\đõøNŸ>ũˇq&×ŲŲųøŽ;ŧ{>,|ˆÃˇqãÆoøø‚Ž—Ō‰'>jīÛ>~=„I|Hããž\€íÅé6ė#>KįB­ŽĸŽú0)ÛŲŲëŧ ‚`“úž8įt6nܸu>ę_fÄ9€ (|Ägã<žŲŗg?ī#žŸ˙üį˜ÜŒt._kkëË>âŗq.øŒo˙ūũGã&߁ĪĮÅ;õÉw+|ͧOß㛃ø°(ú‰ XĀëÛâ$Čk‹é|Ã{ƒŨÅé÷˙đ‡?ô@ß>āCģa!3_õ ƓĶĻM‹üŊõ:öž7ŪxÃ/åA€ūũļ…ūÔĮIŨIũaÜe!ŅÍ›7{ˇxú|t>[h™IČ d€ 2@re`o/čXŅné\Āüo|{ÅätWW×Zî;|ø°w_ae?>ØāĢO„Z$u' ^JŸzęŠ_ZHäå×QÛˇoĶŌø8Q˙b-vb6ėÛÚB´6Îāķmë_ā;uęTėīYŖnĸņíë(ôÕčĶ,ږ>ßžŽžÁÁA+|>~…É÷ņãĮGnÉ)ēŨ°aÃÞ-ž>ËÉ÷āŠ§žú7ž-žÂäíW\aģķ7‡OølvmĻžžž?GzŸä:uę˙ĩ‘ @}ÄgŗĢ đŸ:uj­øãv¨Š×ĮÅWp~õööūû5jԓ>ęí† ž™3gnņmq äĩŲĩøŅøĻŋzģ0ø0đMŽaÛ…Ģ5|žõīØMãJč‡Îg›VˆiČ d€ 2@rc?ūxnų&§‡††bˇÜ9Ž9˛á‘GŦŪ%ä‘Jx™9}ú´ã˛&įØącäĶ ,^F'Mšôs[’GõŸėøl'ßÁASSĶ&ŸđÁÖ ŗ­ū&L˜đ3Ÿ&  ‹$ø|›`ū†‡‡ģlõwüøņ=žéīĉkmņ=ēÛ§ÅW°OÛÉwp000đ;ßđ]zéĨq[‹zû°EĸOøž|ōÉaÛÉiø÷|[\öŨī~÷d|ĩÅW>-ÄäôæÍ›˙J 0ęˆÅWëׯ?æžĩk×bKŅ(\rģøļøę‰'ž˛ŲÕąôG?úQė"4áÅ#œ [Ļ×DÅîß˙ū÷ŊښúĮ?ūņq›]?;C=÷Üs^éú°Å=Þ]°;[Đ^Øėj‚üāüÖˇžå >´ķoÚâC;ëĶâ+āÃÂ\›…ĮĐßÖ­[˙ėÉ'Ÿ›-Åmķc:2@Č d€ 2˜E‹íīíí=ëCXž|ųÉûīŋ˙Ö$ ¯ŊöÚŖžā[˛dÉŠ¤øn¸á†÷|ÁwīŊ÷`ĮÍúkž?ū;>Ø&dŦãkN‚úķßÍ7ß<A"|‹/>á >č")>čÜ|h#P—Ø&’ļúĸ?āĢëĪ"ÚZ´šžčī–[n9d .‚Ûoŋũί|å+ÞāÃX$ žļļļû}Áˇ˙ūŗIõ×ŪŪūĨÕĢWû žŗĀ×ÖÖözũų„oįΝgīēë.›-UG(¸éĻ›ūÔũuuu%ÆˇlŲ˛ŋô ߒ%K:G”cq˛hŅĸī=öØc^Ô?č/)žšsį>é >șŌ¯_ŋŪ ũôaa–#I€z÷!ŦYŗæ,ڋá-NæĪŸ˙œ/øĐ&ŇūÄ'| ,ø ĩ$Y°`A7Æ>čã‘á-N0ŪņßC=”ŸLBČ d€ 2@ėđe/iI'˙ĀÂ<đ5&Ȁoųōå?ŗ×Üš”žL_ŌÉ# ô_šÉ1āģûîģŸđa0->čÜ|i&Į ?_&8ĶLŽ_GGĮV&Ȁoåʕ™“_&ĶLށ_đĨķe‚3->8Ŧ}˜āLŗ0ö‰>āKŗ0øÚÛÛ{|X˜Vž,~Lą0°Öø‚/ÅÂ@āk^¸pĄ‹ĶâÃsŽ;ŋR. Ŧé·ŁiÜ×ŋūõkĐ.5Ēū|Yüũ%]ũų‚㏤ oåʕ÷ų°ø1->`d d€ 2@Č š2āà NL%ũ*XHōa‚3íä0Ūyį]Ÿāœ3gÎ`¯JE}Áõ×_ßë:ž_͎ās}‚ܧøjVđ9˙õ:đÕõ'2'96Ãļ]ž žüã=I@)i›]Ÿāž4“cˆ N×ŋ^ĮäQŌ¯JEč3]×_V|ŽŊžæĢYҟ_¯§]ŒXüčú.đaŽč$ÉŅ‡Åi‚wbņÛ… ’Do’öŽ;îøĪŽ/^ž¤_Í >wĻ]Œ>|Ŋžva đÁŽ]_Ü |ĢÅæ’}Xܙva xđaq'úį¤_‹Ž/^ŧŨõÅ̇]tDæ$GwĻ]X–„Ļ%d€ 2@Č V `‚ĶåÆY&@^,\Ƈɕ´“G‚Īå ÜŦø\Ÿ Æä&)­*›!ļ3[ĩjÕûŽ:0ŗâs}u'é–rĒ1Á w5d™<N´M.ãC۞vōHđš<›ŸëÔpާ]XũŸËÛĢâëģ,ø\žĀÅÂ|=™đį4Ôæ3Ā×ëpĐģ|I~nâ<|X8â2ž, įÔuũ5:>,Î<Īā^¸ŧø “˛âsyņ*څ´ ËęjŽ-îD;åb>´ MRMŪėōâ܋AYíãWíŽqŒ¯TƒKxŪęōîgė&'d€ 2@Č 1 ¸ē/wŗgĪ~;FüØÛpÁ‰æZČáåĩ†đ.:ˆđr—qōĄ†ĪÕí›ķzšÃī›ščƒLyŧŧ"WņûØ$&VĀģčC›€ļ!FüØÛ¨Ã.~Á€6=|Ž:ˆ€}WŦ‚b`[Aô5Ž…Ŧ [갛]Ňíļŗ,L|ØšĀEũaaKø°ŗMŽ,üȲ0IôįĒģ¤ũĒMirjģ¸č`>,ŽUdM|Š…#Ž:P°p'+><ī">ØS^økö yĀ{úsq÷āĢīx՚¸Ō)€Wņ]}õÕÃiwôˆh]\ũe]¸ŒøęŨÅÅņoÔw„JŊ° ø0ūqƋyā;å‘ 2@Č d€ 䯀k"åå5Ķˁ‘K0ŧü`ō!ëËĢ⃃Č%^~ęÛ5įĸ?l­ë’,ī—;×(Ā—æˇ°ÄõcƒãĢ}Î\ y-ü¨ëŅ9|hëĐĻëv–ōēæ@qɆž*/|Ø^ĨčS] y-lžŨŽáÃĸŠ”öxŪc.:°°%/|â`pIXq÷Ũw?qž"R^¸ŠoŊĨ„tŪcøũË[oŊõWÚȁ…XT{ž )/°Ŋ¸k°œļÔ>×`pč¤ŨŽYWŗ‹0đ ŪuYĶ\ÃÎa.Ô´ iđčĪ4:>ô3čo\ 臡ŋŦ #D'¸†/…-*>—˙C˜[ʲãŽ`㑠2@Č d€ ÂĀĩ×^{ÔŠ žķzųŠ“åĖR žœ_~œŌœpzäXk[Ėšāƒ}ÖŋZČeá8rÉ–×ĒwU÷°uW`ʁÜô‡ļؕ/¤."|lje:wT™0ŠŸ+°œ~Ô`ēäËkĮUpšâÈpW5>pܨøP÷ë_ŦgÚîP­wrÎPˇĢԟ‚/7ĮēāCŸ|U.AßT×_îø0ĄXõŒ-ŠÂQÕ[ŦņåĩŖ‰Ø&ŽĀ‡‰E,Ē*_ũw sˇOđÁsã7îTyĪëø0ņ]Ĩū€/oĮēđ3wîÜ˙ĮL•øā¸, ~âøĒĒ{(ˇH|pĐVŽŲĸô‡|ĢūüæņS!RįÔcÕøPīí€*W^įŽāC;ž&5ô;U. €ūĐ?„¯Ú*į–€ã§ĸđa\T5>,Ā,ŸjĒ<'d€ 2@Č š0Ќí>ĢxI‡SŖ(ĮžÂLí%¨Š—<8NŠrĖĒø°Eu/A‡­ļsūâYV;­lCp~įüÅŗ3ø`3E9†$¸CUØ'Ę,Ę1$ø`û¨U,p)Cp@ęzŲm6Úļ"{ĸŋ*ėĀWßj;wĮžŠ¯Ē Säĩ•¸āŅĐ_•øŠrœNleZž"C‚_xcA \€/ī/f—ąøĘ^₉÷˛đUą@B_÷ß˙„ë"Ž˜Ø¯ Cym•ÆM•ø t ĀÅÎUé¯ |°˙*¸ėØҟ,@ye´×Đ_Ū_ĢŸÚé˛đĄ_Ęk+x—\W…ã ĖŊŦZĩę‹"KĮĒH”4wVeĖ“ 2@Č dābgŋC„UĖeŊäaâŊ¨/jL甝_Ų/éĐV6éRõˆ—ŧUĢVŊ_ÖKzŽ!~ƒŊĖ ( eĒ2xŪÜčøādƒÍ”ІĄ­F/PgjÖĨ.āŠúĸ˛õWÔQĒâęįÍe~Ĩ!úËë7t x΋*{ đaÂ?¯ß˜=Œá \°€§Ŧ.‡‰÷2ņa‘WY \dâŊŦ/†°@ĸŅņaw‘2õĮBŅŽ!ЊĐ_™;Ԉã¤hĮā+{‹āËë7‚GØąŊŊũK°”[FĮPYød‡“˛¸ žĩ›I‡ŽũP™ ” ?ôGh×L|į'ø0Ž(#Čĸņ‚ŏĐTöāÃx°āEņ#øxBČ d€ 2@rg/é×_}o‘N0ŧøÜ{īŊsįÎ}2w1.X°āOđŌ\¤“øđ"Y%ž"ŋ2>|‰U>LBā+Ė"ņáÅøđ›x1æ”ûmŧ¤ÜĄ”5ņŽ’„2Ám‘úCŪā°Ŧ‰iløŠt >´ejŲeœŖÎß|ķÍÃhŠ bŸUáCÛ]$>ô=čcĢĀg7úŪ"ņaė|K”a“JÍEãÃä)đa⯠Ÿ Qā\pZçpQú|Øm Ŧ‰ie3Ķ_QN"Á‡EsUāÃ,ā+ĘI$ø°Đ¤*|XÕ¨ø°ĀŦh|ø ú+ËqĸÔŋÖĸņÁ.Ā_}Ņjî?ĸ`1ļĸ\đ[”} žún…ífb‡úģ>´EÁW_´Z >ôŋEâÃĸ˛ĒđÁ>‹Æ‡ū§ž[KéúøĸH|7_}QnŠø0,Æ}x?Š/Z-ŸŠÍa d€ 2@ČČĖVræíË+^ Ēx1× ÁVvpĀåéË^ –/_ūŗ˛žÖqÉ5ž8ƒ“(O'˜ŧøTņb'¸äōv‚3āĢMÂįís _ŪN0‡ėŗĻŋ"đÁæKüZVĒ›~lîč訚ˇ}Ęĸ˛ž–ÕA)×ÍhÃŅWåé§zY_“*xÎ;Å×胁/ĪIxYQ5>8m€“ȍŒ/o'˜+ö)úË}`ĖWÅĸ$ĩœ yãä>}”õĩŦŠI=>,ÎČĶ &Nuä[Áĸ^€¯Y!GžNÁįEÕøP~ŖãËÛI$úsÁ>Ąŋ"ņU°¨åŧú‡ö­ûD{Œ|Ņ~W`ɇ~ íK^A  ßŠßʕ+˙øōü@đaÜW5>Œķž[ƊoTÉ6ĖâČ d€ 2@Č@Rš-ZôßîšũöÛ¯^ēti°bŊDyėÚĩ+ذaCđ /ôŸ>}ú—3gÎüO=õÔ/eR\âæšsį~wōäÉķįĖ™Ķšž—_~ųAp 8‘å\ÃwÉ%—,ųä'?9# ž­[ˇ‚`¯kø–,YōÃwß}÷ãwß}÷ŒyķæĨļOā›4iŌΎnŨНIŅŸŠúÃ_’°e˖›7o>2mÚ´_lŨēõ ú’äQ`Z8iGô‡ļ:LĐļ q_ë’%KžsâĉO.Y˛dzV|&LøŲK/Ŋô‡ô×:ūü˙5uęÔŋ÷‰O|bFZ|°Īßüæ7‡úúúļŊúęĢ˙Ô5|cƌšuņâÅS˛āÛž}û›ŖFzŅ%|˜¤;~üøßœ9sĻ-|MMM›ļmÛöo]ҟŠīžûļ3IûŌ××Wk;7mÚtrįΝĮęøūq’öŠČ´pŒ;v ô|°ĪÖVû íįŽ;jøŽžúę 7nüWEƜ$o|Itųå—?Á͟ųĖg&fÅ7eʔ˙ũâ‹/ūYŠL |ãĮ˙ķYŗfĩ.Z´hrZ|›6m:uôčŅ7ß{īŊŋ}íĩ×ūk‘2'ÉøĻL™ōÍqãÆÍÎĸ?āÛˇoßáŅŖGĪ%|pōõ÷÷˙û+¯ŧōę,ú{æ™gĄŋ'NŦußg?ûŲÉh?“´/¨ĩŸ‚/‚oīØąã;Il¨Č´õGūô|¨ÍÍöÚĪmÛļÕô7eʔ˙ŌÕÕõD‘2'ÉûĻ›núĶI“&=8sæĖÖ{îšgņĪžŽŋūūū×ŨŨũÔųŠĒģ‚ū.šä’¯]wŨuWfŅßŗĪ>;400đģ“'Oū'—đÁIûæ›oޏíļÛŽX°`Áø´öųôĶO÷=ēûwŪYã*ž{îšg<ÚĪ$í Ūkņn$øĻOŸūOŌΝŅų\]=fÉd€ 2@Č  ĀDîĐĐĐŋ>tčĐĸĻĻĻ&8S0Ö'sņB‡ŋˇß~ģĪž=´tO:õ¯Ö­[÷tÂ"KMžßÁƒíÛˇīpč*¤Y|sIDAT͘1ã¯}Á×ĶĶķéĢŽējŌ-ˇÜŌĨŋŨģw÷õ÷÷÷7î7>āÃD|KKË??pāĀ|[|oŊõ։éͧŋæ>|m3<<üߑ#Gžvüøņ›D˜čÔ';ņB§ ėúkmm}š§§į[Ûˇo˙ģRŒ„…AiņAđ´“ ÅL<)>,öF˙đÜĉõēÜ|ķÍ>ō‘L‰˛Oā5jÔąŠS§îō)cƌųG===mļøf͚ĩÍ|øš¨¯¯īķIđų¤ŋú×ŧ÷¨ø06Ķ';1™‰ ö9{öėį7lØđˆC ތmđ .ŧFė3>8°]Yp•ßéͧ‡_xá…°Īéͧwnܸņ>ā;uęÔJ4‹/ž4f˘KÃô|ģwī~ãOOđa'‡’āƒūššš?~Ģúk^šråŋô.‡Dø07ąoßžƏ˙ģɓ'˙¤ŗŗĶy|+VŦXsđāÁOA˜{2Âô||īĖ™3‡=Á×ēbŊÕIđÉÜR›\׿–ÆŽûĩ*ņŅųėrķEŲČ d€ 2@âhmoo_844ô鞞žY'Nœ¸lƌûįĖ™ķüøÃßApn–3.wī7:žyííím!ú{9‚]îĒÆJ˛‹ßŅŖG?388Ø,õīĘ+¯|ļĢĢĢģQô|GŽšnŌ¤IŊ---‡&Nœø\Ŗā{ⁿīŨģ÷SÄgUŸ]K´ôøŖũŊäē3΂ˆĮ×ŅŅ1īđáÃˇĒõ¯ˇˇ÷ŲîîîŨ ?ÁwčĐĄ[f͚ĩûĒĢŽzûõ×_žQđ­\šōSŨŨŨD|ĩŲ­$øŧt^„ūžsܙĮæEoŲ˛eËŪz뭏¨õoãÆëcO§{Ä(°Ļ?_[[Ûo×­[÷|#ākkkûôĩ×^û)UÄcîÜnmkkģ%BŪĪŊ\ ø.ģė2ŧģO–ösæĖ™ŋėėėÄŧ‹īús§ĻP2@Č d€ †c{¯i8T"žšđņŒúķQkƌļ%Ų~ã>ëÃņų ĨpŠŋpn|¸ĶčúëōA d$ž ä9đ(õį€2ˆ@ũe ΁GŠ?”Aę/yģ Ę@Č d€ 2@Č d€ 2@Č d€ 2@Čđœ:Ÿ=W Å'd€ 2@Č d€ 2@Č d€ 2@Č d€ ¸ĀĪ.h22@Č d€ 2@Č d€ 2@Č d€ 2@ū@ø3…ĸۗËJ›…áøũC‘ø˛ŗKũeį0,Úg3öņ{ĸ×CŖ´/aøZƒ Ā_‘Ą û$žâ4HũeįļĄísTv~˜ d€ 2@Čˆg ­­íõûîģ”éR ø›6mZē ,žZģvíĐáÇ'™’Ÿ‰û8ęĪžĢ°”nŸO?ũôŨŨŨךđ]˙Ū}÷ŨāŌK/­ũ™ĘĪ#. ßĖ™3O<øāƒķ(Į”ņ™XIGũŅ>“YˇЋŽ}}}ÁŽ;ÖŊúęĢ_ø°ÔΊn_zzz‚––– Ėų,ßŌĨK—v͛Wœ˙™ø˛)”úŖ}fą ĸëߎ]ģ‚-[ļ´‡,>öž}‰Â7wîÜ',X°˛šš¸ĩãUęø˛ÔŧsĪRŲ8lôú‡š‰l ņi2@Č d€ 2`ɜCg=ķįĪ' .ņš¯\ęĪ}EIˆ:ÖČõ/ l7ŠîŸZ —‘ú įÆõ;]]]g1ÉÖ~úŪžÄā[ēzõj×U)ņQ‘RņÍFˇO´;7xßžDáCŋũúˆúsŲ~Ũ>1žäo>‡žOČ d€ 2@Č d€ 2@Č d€ 2@Č Ö ĐųlM’2@Č d€ 2@Č d€ 2@Č d€ 2@Č@t>‡1Ãx2@Č d€ 2@Č d€ 2@Č d€ 2@Ȱf`ŒuJ&$d€ 2@Č 93đøã}}}ÁęÕĢįŧaÆ`׎]AsssĒį˜ââ 'ú įĻŦ;´ĪpĻiŸáܔu‡öÎ4í3œ›˛îĐ>ÙĻ}†sSƝ,ļ ų\×ņ…[‘ëēƒäYôG|áē/ëõÎ4í3œ›˛îĐ>ÙŽĘ>é|× ī2@Č d€ ĖĪķæÍKU Џ=öXĒgËzˆøÂ™Ļūš)ëí3œiÚg87eŨĄ}†3Mû įĻŦ;´ĪpĻiŸáܔq'‹mB>×õG|áVäēî yũ_¸îËēCũ…3Mû įĻŦ;´ĪpĻ̞OnģŽŪ!d€ 2@Č(˜|šŧtéŌ‘RļlŲRûšy$"Ä8p 6Y!ņˆÃËâņåŗĢø¨?Úguĩ“õõõĪŨúgęÛ!mŖôīÄį÷øĖgũé}ŸÔ+ÄK >wí3­ū|m;míĶW|[­{‚—īļŌšuÔëõwN?ŦnØŠöIįŗļC)Č d€ 2pQ3GōWŋúÕÚWĐ8— qØB /=Z[[kiņÕ4Ōc%§ŧØË5âpžõZĘĖr„ ‚į$Ž |—Ȓåč>ØĘ#Č"úÃyÁ„/ŦīCyyõī¯ŋūzÍÃÆy`Ceá Ã#}9äVÁë:ž0<"ŋāŅņÉuÕø`§UäyuųãŽÕįķģdąOâ;ŋ>…éWô'ö˜įØ%Šūōj;Uđ >ķ쏄˛ŖZ§E§iāPú:œK¸2Ūũtũ5ÚØEđ_ҟĘĩpžæč’ū lSú“4xôgĒÆ§öw >ąW]iŽ]͟ĒOˇŨÖá5 d€ 2@Č@) `މŧ€ã 5Äɗjˆƒs×`Ģ_Hcĩ¸<‹û’Fōz衇FōÅI–ë4ŋG2]Å<*_5ĸRüsŸĒoLŽYŗ&:wõ'“‹¨xŅ[ąbECáƒ}ÂļPĮŗWímt=ĸĻ Žâ\˛0¨ŅđڔFĩOØ$0ĸŋ-Ē}‰ęÛa/yõī_üâƒüā5D™h3Ņžāˆë´};2ŒĒEáĶņč}9ĘUĮ:ŽãĶņ€W]~uŦ‚sõēĻØ”˙ōП.ÎŋŽGŋŽŌ_^c” š +0ãhkŸyãC{"ũB^m‹Kø 8ÎŗoHŠ/¯ļ6ĸÛ'ÚÍŧĮ..áƒS6/6 ŲŌ„¨ļųEÕŋĸõ×HcŨ>ĄŋĸĮ.Uę}0gŋ¸bŸāRú{`CŨ“ki|Ž:>Ô=`„ū˛ôgŋĐųœEë|– 2@Č d 5˜|Â7^ļ1Įš8˜qxqŦá¸#DM\a`.é$Ÿ,×iēŠ/-ũ9Wņ‰ŗv ö¤Ënsí*>Ô Ô Č'Xmđči\Ň:‹ uV—ÛöÚU|‚ e°OYDc‹KŌšŠõ€Į´ÁU|ŌĻ@>™(KƒŅU|ĀôųĪžiũúõi Õž‰ÂˇvíÚāÁŦĨĶûvDâYéûu’ôīø˛Vę›:~@žŌÎčųÛ^WOĮŖËĒsŖßOr]>ú5U_IäMšļ|:˙:ũZ—Žä#íŒžÆæZņūļŽü$Ī´õ/+ž"Æ..á“6ElØFWĻ4Yô‡gķh;‹*ŸāĘsė’TEâƒ}7ŌØEÕÎĨūƒÃFģčø`÷E]P/ЎKÃ5ū¤niŸRp›ę§ÎGØ5d„-ōQû\—…OŪ_ĨŋƒÃY06"ž0}$wUŌžˆ>u\Üv[g„×d€ 2@Č Ĩ1 /x‘À˜Ø@Ā$^@0ĐFĀ= x9 slčΜŦ×RfšŖ‹øŌā{ÆU|29f#axôxņÁöa͍3X]œ%¸ˆõ]&@Ô:Ÿ§‹ø€ úƒmb˛%KpđĀ61š)myZŒŽâ.Ŋ_IƒŅE|¨p:ÃV”}Ūzë­Ą};xĖĢŸ6mÚyj&´)¨ƒÂũy ^HúøĨ(|:]Üãđč÷u|EŒ]\ÂWÄØ%)žŧÚN]w¸.bėâ>‘ĨQÆ.&6ŌØEĮWÆØe–57ĄãËsüÖ÷•Oíīâú¨kņEɛôžĢøT}昸åŗÎ¯É d€ 2@Ja@žōÂÄQ(GL˜I•˜ü@P_R'Ęxi1Fį˛^×2KøĪU|:_ō“Ū[–ŠŪäX•ū küÁFđ—v…ŋĢúà 8†Í ×Iu‡ôŽâCÅ*ū˛Wņ“ÔAĩŽ$Åę*>´'ōC#Ú'ô„ē—EwČÃUũ >čąHũ‰ŊKëŧû÷å˗Ÿ7~úKÛ/ˆÜqú“tyâĶņH;‚#t…˛ôąŽČ‘ôX>dTå×ņáž:ÖK;vA>yāĶå3ņ¯â‰Ã‡ž]l4īą‹Ôe9B„(û,Ę/bė"¸äX‡W*>ô E]—u|ĐBžīFēū‘ŋÄeé˙LuOpÉą |ĀÔHcŅŽāíe#]Âđ=vQm_ÚOČRtũC˙€úuübǐ]ę^YøĐ׊ũŠ˙”v ÉŅU|ā5ņ‹ĢøPīT}ęcl:Ÿ“X1Ķ’2@Č d€ TĘ&đ’'/|^u0Ž{ō"ˆ{Y¯Ë]>•¯˛°Ąœ˛đŠ/ĪˆöŒ;ąī˛0–Ĩ?L°ėÜšŗ,X#唅Oô7RpI'eᓠđFĩO´Ÿecƒ‰”Ĩ?Ŋŋ,É< Á§cÁĩL˛–…KĘÉC:ĩ/7}¤ė2Žiđéxôk0č×eā’2t|hTyLüëxôkõyŒ[ĒģcŅø0fū*Æ.eāC6vQíSė[w*Hũ(ō¨Û&ĘĘģūĄ_h´ą‹Ē?p†ëF썸Ä>Å6Š´G=ī2ė}ÚĪ*Æ/EáĶû;ŊÔy.ęē,|ĒŊ…Å”oYøt}ǞĐųŦ˛Ás2@Č d€ §°¸ë/×Y¯Ë"Ĩ,|eáŅË!žsŒdĩGũyįĸŽŠ?ę čöw]”=ęų–eŸ:^]Žĸމīŗ:˙Q×Q÷ŠŌSXžyčOĮŖ—w_OŸįu|ēŧúužōeÍËŸ.ÜuV™ō|ž |:yĘ—Wøâd(ęž 6”­ķw­Ȩ̈×īuM|į˜ÕųģÖõĄ§×īuMũQ` ÎūâîûnŸEÉ—oYõ/Jūæs;ŧGČ d€ 2@Č d€ 2@Č d€ 2@Č V ĐųlE‘2@Č d€ 2@Č d€ 2@Č d€ 2@Č@t>GąÃ{d€ 2@Č d€ 2@Č d€ 2@Č d€ 2`Åķ؊&&"d€ 2@Čȃ‡~8l*ËãÍ7ߜU8ņEąSũ=ęĪīúgAž×ŋ(|°]â‹bČí{ԟÛú‰“ÎwũõõõEBlt|ģví |n?ãôG|‘æ]ųMęĪīú‡ú|¯qø6lØlŲ˛%Чīõខ6ē}b|ų˙ŠeǰĻ8ÕŋIENDŽB`‚trillian-1.6.1/docs/merkletree/treetex/images/leafdata.png000066400000000000000000000212261466362047600236410ustar00rootroot00000000000000‰PNG  IHDRęŧvC2¤sBIT|dˆ IDATxœí}pᙨŠ"i*úZ_,3ŲâŦ‹>&ĢŖÂhēR¸­l—Ĩå$͸58ŠOSĻ<ÎrŌŪprN.ĀĖ]ƙËÕC¸™ĻÉ9Ią—ņų’{ƒÛQÜHŦąv’ąÆōŗ˛Bņ +ŠŋĨˇ€/BvÁũđūf<ÖËwŸ÷Å>ûŧ΁`#!!4ŽĘ ō$BBˆļ[fĄn@ąëĘW)„P!DŲŨĄfAQëT/„cw„šc]I“(i>aģûE Ô &))QVÁHBq“”CĻÁ‚PvķĮlRˆl0›e§ŧjG/FąģŋfąÅn5VYēp­ZUĸ¨ŗá-ŧeņũ,ƒ(*Á4PvƒGՓH’$eDQ,ų}:I’´Ü–­LĘę€(*ÁLT­›,Ë I0 ąX dYžįût: 4Mįū­÷žÕQT‚™¨ZSšĻ!ž{ JQ—˜ĸ(Mg›ÁĘŦĒAā­v @¨iT7v°bÆb1`Y6§”:Ą@ÕôVÄĸl‡ĸ(‡ÃN§KŽSëĸ¨[‰FŖF ̰…kÔüаĸ(FYÜǃ(*ÁV†šĻAQeÂá0(Š€p8œÛí•exž&÷Šû@]3™ J$ʇĻĒ}ŸŨũ&Ē ”ÍŪ`5IģûmdęK0††;v†jr7Š(*ÁlŦVœšTTÁTB´…ĶŪqģûK T-ȘIZ¨ĪŗÁ51Š‚Ũũ$ĒČ§?ũé˙aĸ˛’)/  ␠ĮĮ5F+kÜŪ.Õ (ˆE1(›ßČj6õ `,d•´hf@”]ŗęIx–BÕd€8`d§ģĒ!n({tŖĨî BĄ>øāˇßüæ7ŋe~œGƒŨj " ĀhĨŒ˛i[øõvđ1‹Ų¸Ō4ˆ Ų—Āqä%ę ˛S]+2֏ƒ†Ėa# $ĄÄzÔXČZn ‘0d-ŠÕštßĪví€PĩŒ@+ü;X˛øŪ­ĩā˛Ú…BŊBAvĒkg5ŧ&&E°z=ZŽqp†‚Ŗ°k=Z ˛ĘZŧž-D Ģ N:ŋLCV& ˛į­5 Éđ@(vĒOĀ Í˛C{×Ę‚í0PÄŠŪÔd23A ßö6ÚŦ;v,566ÖŲ•z|`qu:ÁÂPāúöĨ/}éÍ@ °RŠuM$čĐĄCKCCCųíQāœĘáVC*• á%ŃÁîŖG~pōäÉųH$‚J)m2™D‚ Žã2~ŋ˙_â#‹z{h¨B˙ßģ l¯ĄŽ¨]čõz_ (ę銊Šöüđ¸ųųųųöööŠÛˇoWCE7<…J[ĸ8d aÕÃŅÁ`ČNG­Ū­GËJ6•›W÷ļëȤ••Õ*Ân%͗#õãŊS•Ę Æā%Å8M3!AåM8U)œ*—ÔJÉI‚IАf:uMX/ĘJ*•JR-7õĸŦUūF0‡jQR öYIHP9!jSRLjÛ?–ø˙rTĢ’bJē5Ö¤R9!į_ígvĩŦŦŽ*'žžĨĄžrūüyßęęjĀÚÚÚjkkĢÜÖÖös ~´˜8d“k•¯ÖT„!;+Ôr1ĮqÂå˗ģWWWsE¨šššÎíÛˇī×NĢ “ >ÖvAu#ԉ'ūōʕ+Ŋ{úúúšY–ŨPB–eEΟ?ķęÕĢ—~đūûī˙Y‰öÆ!ëė^ JŠ)ĢŦ>ŸīEQ؎ŽŽOúũūí,ËB~)IY–A’$xûíˇo^ŧxq–ĸ(éėŲŗĪ[${9°īo-ũVĩĮéͧG<8Į+Š÷šŨîãņxaE´q¨]Įīq(˜* <éņxf#‘HEã‰DĮã™~ÖĻž`ČĻ’ÃĄúûûßãy^ĩNK)’É$ +yÁŲĩŦ¤˜\O:õ÷<ĪŖT*ĩŠņKĨRˆeY …Î: OÕēáWģxŊŪ€ËåēYŠ-… 衇ēCv÷Í"~xøđá˙WŠ-E$Anˇûc¯×°Š?UT^͌ĩģ\Ž››ĩĸĨˆĮãččŅŖØŨ?+pģŨŌ4™LĸîîîģåH˙ßFģ°‹ĩĩ59‰<črš m×årA*•ú¤ĮãŲ{îÜšŸÚ¸ƒ…Bg9ŽûÃü čF°wī^˜››ÛžcĮŽž .|×ĐÆĩA€ ę+ã…3דÕOL&ƒĀJ‘ ϚāÅ_ü2Ë˛ĻBņ<^|ņÅ/ÛÔETî¨C‡-=å-$™LÖlĨ4Į3ģŲ#­d2äņxfmę" *ˇŋßΨbÁj‚€ŧ^ī v÷ŲHÜn÷ŒÚ¯ckPy]YTP.Ē9)E4…ĻĻϟZr3‹hjjúĐ*Ģ*I,,,8åEl*Õ ^¯7VĖļŠTĘÎč3ą$ú(“É C‡-ƒümÁÆõfQarrō-ŠĸähÔÜl•ƒƒƒĐĶĶķŸLŊ‰=(>Ÿī¯Ėŋh4 ,ËūOȞc:âQ „(ŸĪ7gVLe$qBĸ.S9vėXĘŦpÁD"á¤ĩi!¤¨”•xŊŪ@(Z6ú!KĨRvįüą„ąąąvŸĪ7gô8“É —ËusllŦŨî>–€•[ßī˙z(Z6ʲÆãqtđāÁ9ŗčY Îâh”eM&“ČårŨ|ä‘Gž˛ģoe Aåv ģ9ŽËčÉZÉdĪķ¨ŋŋ˙=pÖæ‡éŒĩ;v,%ÂĻķ"#”͆ŅÛÛ{ÉÁ–4Ÿz¨ë<BÔŅŖGĪûũū•J­k<GX…Bß´ģv2444~čĐĄĨJ͇&“Iä÷ûoå%/¯x°8¨œÔžÉB@Âëõū1Bčå={öė+V7#IH’oŧņÆĩO|âīŧõÖ[ãÆâI5Lûƒ>ø>ÜúØcQ,ËõāÂõ{~ûÛß*˛,/uttüÛ_ũęWŋ´A^=$ā¸U7#Šš%Ų áœĄ×ë}ĸ¨§§ĻĻÚqëęęęÚŌŌŌâÁƒߝ™™ųËÉÉɡōÚ_˙{k}ėœEŽĐŌņãĮ?ŋēēúėÂÂBûĘĘ ÕÔÔ´_{{ûÔíÛˇŋģ^/ Ųuߨ}boŠ{ž‚šĐ`Lq ŖÚŠf6{tQG–ūŪuįđP˛‘üzI¯˙W¯nfģŧÕ7nipHPy=Ā‚ą[íÔīŽ ž~WkÉCĮW*¯â`ü1 õ÷ãQmĒĶ‘€”ŋ03ĘQɛ-`ôŋđĒՑĀö ōZÆė)ĒŅSj'ƒĮŌĖ˜á˜%ĶözŨL ģ­nVd>ĸqLg1j3 :c>“аְjã‚†ę¤¨_īŪŊûÜÄÄD 6naĪk2›Ũ%w×]9y‚Á`÷ĘĘʧĻĻzBÍ¨ßÚÚ*ģ\ŽũøĮ?ū;‹å€lŒé–-[ž]XX8ēŧŧŧËõĀüâΝ;¯æų7‡Á<˙Ü(d÷ÄR2ĩ´´ÜØļmÛ˙-ÉndČžĀ$ŖeŽ:§|ŸĪ÷ʖ-[>CQԞb.8ûäääĘŋ¯nßžũŸ={öyČN§Žƒ=9xXāƒÁāËĶĶĶß^]]=xôčŅqtI~™ ™sæĖ™ˇoߞŪĩk×7֝×MÃëõššš"{üņ‡†)+Bčß}÷ŨĖĘĘĘg͒éžûîûģîîîO544<ŌĶĶŗ“a˜{Ēˆĸ˛,ƒ,Ë˙´´´4ĩēē:j§Ōîßŋ˙™†††otttlcæÁbãˆe>sæĖÆÆÆė–Ųpžôx<ŗ•xĮãqävģ3vg[衇.øũū[•Äkf2$ …BgÍ ¨îëë{3T‡›ÉdP(Z…BgB†ŽQBT(:V* DOĨR(ŦؕĢĘiãh §Nú{–eŅfSĻā vüˆÁ`°ÛįķÍ ‚°)ŲĘžlēēēV,122ŌĢ7ŗE<G~ŋ˙ÖđđđŗFČ4<<üŦĪį›Ķ#S$A>ŸoÎ(™ÔéÕ+ŗŅãhÔącĮRzĄđGėî*§‘ßī˙zĨV´øe&ŒË¨\Q™LŸĪ÷š™|>ßk•Z¤R`ëęvģ¤wŦTd~ŨqÄ2ëG[pģŨņČÁƒįĀä3¯'ÍČr‰Dt)ëŠS§žüÜsĪ--— ›.æÄqœ gÆQŠįž{n‰ã8SRĻp'8mmĄ¯¯īMŖ,i!ņxÜÔi0ZĪlV6ų@ °˛™iŌØØXģ™YîĀĘK/Ŋôd%2Åãņnŗ*d2ÔÕÕĩjôúۊqé5RfS~–eYSÃķ<:}úôˆō÷÷÷ŋgfyÂT*…ü~˙­JåęííŊdVŌl„˛‰Ę*-ãÁ˛ėŒŅŗĻ|‰bYÖĐ<ËNG[đx<ŗfeąĮāDĪ`đøøņãŸįyŪTŲĘNûúúŪÔ*×ĀĀŸš1Ŋ,D ĸE&ŸĪ÷ŠU2 üéæU{ÆņĉĻËéÂįķŊb֔ˇxíííWÍ. „ + ŅĶęāÁƒĻMÅ YˇöjrQ~ŋ˙–ōd2Ãö%ėĮFŊ‚ EQ˙íÕW_ŨiÅŊ\.|ûÛßŪ133ķįF´įõz'OžüO<ņ„ÍŠ˛oߞƋ/îŋ|ųō˙R‘ë…gžy&¤ĩø˛^ÖÖ֚Ž_ŋ~ķŌĨKo”ēÆī÷˙×/|á ˙˛X:VŖimm…[ˇnĩĖĪĪüģßũîÍļcį8:-z†ęč訪v‘$IN§ĄTNĩīķųėg?û`0ėVŊPģvíúZ<-ōÅbęA),ËÂ͛7¨]GQÔĶZŽrraĪ$-Ûyž‡æææ­rk„L8G°™(ŠzZõÂ21Ž-ÕđōĮŅQŠĘq\¸¯¯¯šÜ5˛,C:šĻs˙ŽäûB†ĢW¯ž _z€[ˇnų ŨÜ Ņ"ŸV…Đōb›™™yTíRNŽX,E˲9…-EQ°¸¸XvęģŧŧÜAĶågĮåd’e$I†a ‹,—O€HĶ4\ģvÍ]ö"čGŒ(ŠšŒEQ0??˙€Ãuvvö_ŠŊąDQü㊒ī aYnßží×!vŽ•••]j×h‘¯’Š•×ëm†ō‘&Tgg§ęī\N.ŠĸrEQĒJĐĶĶŗJg¸ {zz>ĄG&šĻ!žU–īO[ŠÎÎÎ&Ø|ôũđÃëū}ąkå3ŸųĖã(EŸŸ¸’NEQpãÆû hŠõų|–¯ų†ˇÛũoĘ]˛˙ūô܃įųœC|:žĮ9žë/›RŌ[ˇnÕ5æEEQ‹Å€eYM˙ŪŊ{w‚E]˙{]¤ĶéĸeRT Ĩ¨ÕNkkënģe0“X,‚ TdĖ„ĸ(‡Ãšˆ)§#Ëōf”6õÕBūGQ”{ĩīÍdmmmIí;å+‡š\ĸ(Ã0Ā0ŒĻНŲ2EŖŅ܆ŒÖ鸨#^[+ŠR‘ĖŽRÔ;v\R”ōáĸáp8ˇ—exžEQā%ŋˇų7ŋų͜ÚEjōÜŨ5EÔÆ#NÃĩk×ūĄÜ%ĶĶĶ7ôČ%Š"ŒŽŽÂčč(?~\U&Ü(]âBSLp9™†šĻs|ūzĩë/ŌÍÆ#§¯ë•™eYMãįh8ŽûÖøø¸ĻÃ`5.­.^‰DÂ0ŪŪŪKšnZ|j„BĄeP9Č÷x<ŗVËÅq\Æl™2™LEōęu%´seQ'&&ž399šŦåZĩQ­;§’$ÁÜÜÜ×tą įĩlģTļŗ[Ž>úį•-IKKËEĩc*Œr)Š׎]û§r×444ü^Ģe)%>2ŌB:†;wî\Ņtq ėGG)*¤SŠÔU+oxæĖ™FĨ˜žž×ǍF Ë2ėŪŊû=ĩëVWW`Ĩ\’$ÁîŨģË.š››bĩL+++?×ĶÆŽ;^ŗZæÆÆÆˇ-ģa% 3#ōY/KÎHų8ąĘtxxxQcŧe~ĩåĻãjģd´•2­ûÍęõõĨNž<9o•Ėųãč4‹ oŊõÖŠĄĄĄ[V,¸‡††Ū~ûmÎČ6=Īw´¸‡éE’$øå/9Ĩq6 lÛļíĩŅQķ‹z‹ĸ‹‹‹ŋõlé­[ˇžąbŦĸŅ(lŨēõ čOl§ŦŦŦŧnÅ8Æb1På °>kĻvNŸ>=bv¨˜ hhhȔdÜfĮ+âô'•D{<žY3c?3™LÅq˛~ŋ˙–™!ŠTĘđxTŗĮqŗņÆļ`fđu"‘@ŊŊŊ—Ė’}llŦŊĢĢkÕŦ)° čĢ_ũjÅņ•/ŊôŌ“feS@!–eQĨ™'†‡‡Ÿ5SĻP(´\i։jG;Ą\.×MŖv0nVúM ĮqÂđđđĸĄÂŖėKFO™'NüĩĪzr9 3b#‘ˆiÁ×NGÛđzŊŸĪ7gÔ42™L"—Ëuķ‘GyĘ ų}>ßkĄPhŲ¨— ¨ŗŗķ#Đš)&A@FĘÕÕÕõ=2=účŖIŖdÂų=Ī/ôȤ†‘ãˆe~ôŅGĢąîŦ§ Õ; ‚  ŪŪŪKf[ŌBN:õåC‡-陯'“IIJŦæ'ZåęęęZÕķL&“(Ŧ<˙üķa„LĪ?˙ü_č•)‘H ŽŽŽUŖR¯¨Áqœā´q´•ĄĄĄņC‡-U: ëG0ˇĖÚ8ŌÂØØX{(:Ëķ|Å/›H$‚X–1zÍ¸Ų—  čØącŠxģqâ8:ĶjSZ€™ĩFõāDš"•ĢšĐ=ŽŽķL"÷B•@¨ˆĸUQTĄ ŠJ TDQ „*€(*PE%Ēĸ¨B@•@¨ļÚ-Ąfa:;;˙ÆčFoŪŧy_ssķŗÍÍÍkFˇ}á…§ĄtŌp[qŠĸáģyŋAí”ĘIÃæ ŠņĪ`?˜ëÃZĒ_ ”D×"WŠvŠ`0؉8ÍU¸8ŖŖŖpá…Rã ÷ˇ×ũû:BQ?õŠOŊūÅ/~q›ž6fff ­­M—cū÷ŋ˙ũ…+WŽ-čvģŋáķų´”÷Ģ”ĨĨ%XZZ‚ŨģwŪ8”īWggį߃ÁöÍČUŽŨZBīo¯÷÷UÅŠēoßžÅH$ĸKQā7ŪXŧrĨt2užį Ëpo%jũÚŦÕSkˇ–°ķˇ—$‰l&ŦEQ°"—o­A•`)EU53 ŠJ°\ģEExīŊ÷ ‚$I0:: ’$Áāā Ȳ 8=ļĀø܆–ëĸŅ(Ȳ ų…pIËjŖ.5‹ŪĻ,Ë088¸á32ÍģI’@„ÜØāúĄ8˙EQĒr^š¯Ã÷Ā›@’$˲ ˇĐū­cąXNécąˆĸąX,÷ÂčīīĪ}VŦ F:Î՝ÍoˇXãĒVÔÍ(œ$I†•‘Īŋ?Ã0ĻŊŠÍxąØŽXŽ( |îsŸƒt:ŗzét:§T¸@1×|˨åēX, ÃĪķŽÅÕžõö!NMĶšäeE$I@Q‚+XĖķ<„Ãá / MĶšÂĮŠĸ€ĸ(%¯uÄŽo1°đ Ãä:AĶ4¤ĶéÜ[QE i:7pųĨØņßāŋĮĮ˛Ŧ!VŽđūøaĀūĄđ ã‡3ŋü–;ŋJząû䷅û[ė3#ú`\-WČõ-ŋÍü ‚ããŲėŽxüō¯ĄiZĶuųāņФĻĒVŌé40 ’$Áž={Š^ĶŌŌƒƒƒ*Ĩ‹ĸņxxž‡ŅŅQ(•UĀĄUEH§ĶĀķd•[Qīy›Ū_Ξ,ˆĸ˜KZø™^ō*ŧ~Ŧf°ĩbYļ¨ÅŌļâ ÃĀšsÅĢx>ųä“9+˞,đ<Ÿ›Ē ‚˛,CšsZGZöXMAIDATTQįųÜT û͟Â[üCä[Z+ÎĀÔd,ö=ū?~Ą¨YÃX,ļÁŠˆĸŠĸÜķ™žūâi#EQ9 nÄøáuœ]äΤđ”T/ø%‰§ŦũũũP,_s!˛,ĪķšX$ŅŅQH$E֑ŠJQÔ=JWl‘ ‡ÃšM3)u-ā5›(Š9e+wŠĸrooØ`Í ?Û,…Sķüļõ‡u+^áįÃnđt—Dĸŧs\ūŒŋ %I‚p8œ›As@q¤ĸF"\ČuÃē.ŗާ‹øķ|ĨÁoĩü‡oĪcĢŖįĄ.v˙|ß|ķÍÜgx\ØČMŅeYAî‘)˙>‚ äŦ(ž¯wō?Ķ˲99 ÷æw‚bĐ4 ’$Ų* ^īâņÁЇ ū?gx ¯aYdYI’ā+_ų |ī{߀ė¸9r(Š‚p8Ŧû…k8gļĸ‚ĀÅq+)ÎäņxfKÉčvģdFqâü+¸~ŠŅ”ëWggįGf´ ŦŅ}Éd2(‰ žį‘ŅŏA@PÂiŪ¨ßžeY”ßN&“AZÚM$ȑÕđ&”Ķ}sąĩÄkT§Ëk'ŅhAČÍļljB+ŒgAZũŦkVQÕÖ{NÁˆ z!˙üŌ ĶđJ`’ɍĨO+Ų;ŠYE%ÔøÁŽ6%5GžŖ„E%ĒGL}/_žÜZĖiŲ9Ę}o”׏ըõkŗc¯Ön~t‹Ķ)柏ŋŊĸ(đ˙WŽĮâœ>IENDŽB`‚trillian-1.6.1/docs/merkletree/treetex/main.go000066400000000000000000000306471466362047600214070ustar00rootroot00000000000000// 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. // A binary to produce LaTeX documents representing Merkle trees. // The generated document should be fed into xelatex, and the Forest package // must be available. // // Usage: go run main.go | xelatex // This should generate a PDF file called treetek.pdf containing a drawing of // the tree. package main import ( "flag" "fmt" "log" "math/bits" "strings" "github.com/transparency-dev/merkle/compact" "github.com/transparency-dev/merkle/proof" ) const ( preamble = ` % Hash-tree % Author: treetex \documentclass[convert]{standalone} \usepackage[dvipsnames]{xcolor} \usepackage{forest} \begin{document} % Change colours here: \definecolor{proof}{rgb}{1,0.5,0.5} \definecolor{proof_ephemeral}{rgb}{1,0.7,0.7} \definecolor{perfect}{rgb}{1,0.9,0.5} \definecolor{target}{rgb}{0.5,0.5,0.9} \definecolor{target_path}{rgb}{0.7,0.7,0.9} \definecolor{mega}{rgb}{0.9,0.9,0.9} \definecolor{target0}{rgb}{0.1,0.9,0.1} \definecolor{target1}{rgb}{0.1,0.1,0.9} \definecolor{target2}{rgb}{0.9,0.1,0.9} \definecolor{range0}{rgb}{0.3,0.9,0.3} \definecolor{range1}{rgb}{0.3,0.3,0.9} \definecolor{range2}{rgb}{0.9,0.3,0.9} \forestset{ % This defines a new "edge" style for drawing the perfect subtrees. % Rather than simply drawing a line representing an edge, this draws a % triangle between the labelled anchors on the given nodes. % See "Anchors" section in the Forest manual for more details: % http://mirrors.ibiblio.org/CTAN/graphics/pgf/contrib/forest/forest-doc.pdf perfect/.style={edge path={% \noexpand\path[fill=mega, \forestoption{edge}] (.parent first)--(!u.children)--(.parent last)--cycle \forestoption{edge label}; } }, } \begin{forest} ` postfix = `\end{forest} \end{document} ` // Maximum number of ranges to allow. maxRanges = 3 ) var ( treeSize = flag.Uint64("tree_size", 23, "Size of tree to produce") leafData = flag.String("leaf_data", "", "Comma separated list of leaf data text (setting this overrides --tree_size") nodeFormat = flag.String("node_format", "address", "Format for internal node text, one of: address, hash") inclusion = flag.Int64("inclusion", -1, "Leaf index to show inclusion proof") megaMode = flag.Uint("megamode_threshold", 4, "Treat perfect trees larger than this many layers as a single entity") ranges = flag.String("ranges", "", "Comma-separated Open-Closed ranges of the form L:R") attrPerfectRoot = flag.String("attr_perfect_root", "", "Latex treatment for perfect root nodes (e.g. 'line width=3pt')") attrEphemeralNode = flag.String("attr_ephemeral_node", "draw, dotted", "Latex treatment for ephemeral nodes") // nInfo holds nodeInfo data for the tree. nInfo = make(map[compact.NodeID]nodeInfo) ) // nodeInfo represents the style to be applied to a tree node. // TODO(al): separate out leafdata bits from here. type nodeInfo struct { proof bool incPath bool target bool perfectRoot bool ephemeral bool leaf bool dataRangeIndices []int rangeIndices []int } type nodeTextFunc func(id compact.NodeID) string // String returns a string containing Forest attributes suitable for // rendering the node, given its type. func (n nodeInfo) String() string { attr := make([]string, 0, 4) // Figure out which colour to fill with: fill := "white" if n.perfectRoot { attr = append(attr, *attrPerfectRoot) } if n.proof { fill = "proof" if n.ephemeral { fill = "proof_ephemeral" } } if n.leaf { if l := len(n.dataRangeIndices); l == 1 { fill = fmt.Sprintf("target%d!50", n.dataRangeIndices[0]) } else if l > 1 { // Otherwise, we need to be a bit cleverer, and use the shading feature. for i, ri := range n.dataRangeIndices { pos := []string{"left", "right", "middle"}[i] attr = append(attr, fmt.Sprintf("%s color=target%d!50", pos, ri)) } } } else { if l := len(n.rangeIndices); l == 1 { fill = fmt.Sprintf("range%d!50", n.rangeIndices[0]) } else if l > 1 { for i, pi := range n.rangeIndices { pos := []string{"left", "right", "middle"}[i] attr = append(attr, fmt.Sprintf("%s color=range%d!50", pos, pi)) } } } if n.target { fill = "target" } if n.incPath { fill = "target_path" } attr = append(attr, "fill="+fill) if !n.ephemeral { attr = append(attr, "draw") } else { attr = append(attr, *attrEphemeralNode) } if !n.leaf { attr = append(attr, "circle, minimum size=3em, align=center") } else { attr = append(attr, "minimum size=1.5em, align=center, base=bottom") } return strings.Join(attr, ", ") } // modifyNodeInfo applies f to the nodeInfo associated with node id. func modifyNodeInfo(id compact.NodeID, f func(*nodeInfo)) { n := nInfo[id] // Note: Returns an empty nodeInfo if id is not found. f(&n) nInfo[id] = n } // perfectMega renders a large perfect subtree as a single entity. func perfectMega(prefix string, id compact.NodeID) { begin, end := id.Coverage() size := end - begin stWidth := float32(size) / float32(*treeSize) fmt.Printf("%s [%d\\dots%d, edge label={node[midway, above]{%d}}, perfect, tier=leaf, minimum width=%f\\linewidth ]\n", prefix, begin, end, size, stWidth) // Create some hidden nodes to preseve the tier spacings: fmt.Printf("%s", prefix) for i := int(id.Level) - 2; i > 0; i-- { fmt.Printf(" [, no edge, tier=%d ", i) defer fmt.Printf(" ] ") } } // perfect renders a perfect subtree. func perfect(prefix string, id compact.NodeID, nodeText, dataText nodeTextFunc) { perfectInner(prefix, id, true, nodeText, dataText) } // drawLeaf emits TeX code to render a leaf. func drawLeaf(prefix string, index uint64, leafText, dataText nodeTextFunc) { id := compact.NewNodeID(0, index) a := nInfo[id] // First render the leaf node of the Merkle tree. if len(a.dataRangeIndices) > 0 { a.incPath = false } fmt.Printf("%s [%s, %s, align=center, tier=leaf\n", prefix, leafText(id), a.String()) // and then a child-node representing the leaf data itself: a = nInfo[id] a.leaf = true a.proof = false // proofs don't include leafdata (just the leaf hash above) a.incPath, a.target = false, a.incPath // draw the target leaf darker if necessary. fmt.Printf(" %s [%s, %s, align=center, tier=leafdata]\n]\n", prefix, dataText(id), a.String()) } // openInnerNode renders TeX code to open an internal node. // The caller may emit any number of child nodes before calling the returned // func to close the node. // Returns a func to be called to close the node. func openInnerNode(prefix string, id compact.NodeID, nodeText nodeTextFunc) func() { attr := nInfo[id].String() fmt.Printf("%s [%s, %s, tier=%d\n", prefix, nodeText(id), attr, id.Level) return func() { fmt.Printf("%s ]\n", prefix) } } // perfectInner renders the nodes of a perfect internal subtree. func perfectInner(prefix string, id compact.NodeID, top bool, nodeText nodeTextFunc, dataText nodeTextFunc) { modifyNodeInfo(id, func(n *nodeInfo) { n.perfectRoot = top }) if id.Level == 0 { drawLeaf(prefix, id.Index, nodeText, dataText) return } defer openInnerNode(prefix, id, nodeText)() if id.Level > *megaMode { perfectMega(prefix, id) } else { left := compact.NewNodeID(id.Level-1, id.Index*2) perfectInner(prefix+" ", left, false, nodeText, dataText) perfectInner(prefix+" ", left.Sibling(), false, nodeText, dataText) } } // renderTree renders a tree node and recurses if necessary. func renderTree(prefix string, size uint64, nodeText, dataText nodeTextFunc) { // Get root IDs of all perfect subtrees. ids := compact.RangeNodes(0, size, nil) for i, id := range ids { if i+1 < len(ids) { ephem := id.Parent() modifyNodeInfo(ephem, func(n *nodeInfo) { n.ephemeral = true }) defer openInnerNode(prefix, ephem, nodeText)() } prefix += " " perfect(prefix, id, nodeText, dataText) } } // parseRanges parses and validates a string of comma-separates open-closed // ranges of the form L:R. // Returns the parsed ranges, or an error if there's a problem. func parseRanges(ranges string, treeSize uint64) ([][2]uint64, error) { rangePairs := strings.Split(ranges, ",") numRanges := len(rangePairs) if num, max := numRanges, maxRanges; num > max { return nil, fmt.Errorf("too many ranges %d, must be %d or fewer", num, max) } ret := make([][2]uint64, 0, numRanges) for _, rng := range rangePairs { lr := strings.Split(rng, ":") if len(lr) != 2 { return nil, fmt.Errorf("specified range %q is invalid", rng) } var l, r uint64 if _, err := fmt.Sscanf(rng, "%d:%d", &l, &r); err != nil { return nil, fmt.Errorf("range %q is malformed: %s", rng, err) } switch { case r > treeSize: return nil, fmt.Errorf("range %q extends past end of tree (%d)", lr, treeSize) case l > r: return nil, fmt.Errorf("range elements in %q are out of order", rng) } ret = append(ret, [2]uint64{l, r}) } return ret, nil } // modifyRangeNodeInfo sets style info for nodes affected by ranges. // This includes leaves and perfect subtree roots. // TODO(al): Figure out what, if anything, to do to make this show ranges // which are inside the perfect meganodes. func modifyRangeNodeInfo() error { rng, err := parseRanges(*ranges, *treeSize) if err != nil { return err } for ri, lr := range rng { l, r := lr[0], lr[1] // Set leaves: for i := l; i < r; i++ { id := compact.NewNodeID(0, i) modifyNodeInfo(id, func(n *nodeInfo) { n.dataRangeIndices = append(n.dataRangeIndices, ri) }) } for _, id := range compact.RangeNodes(l, r, nil) { modifyNodeInfo(id, func(n *nodeInfo) { n.rangeIndices = append(n.rangeIndices, ri) }) } } return nil } var dataFormat = func(id compact.NodeID) string { return fmt.Sprintf("{$leaf_{%d}$}", id.Index) } var nodeFormats = map[string]nodeTextFunc{ "address": func(id compact.NodeID) string { return fmt.Sprintf("%d.%d", id.Level, id.Index) }, "hash": func(id compact.NodeID) string { // For "hash" format node text, levels >=1 need a different format // [H=H(childL|childR)]from the base level (H=H(leafN)]. if id.Level >= 1 { childLevel := id.Level - 1 leftChild := id.Index * 2 return fmt.Sprintf("{$H_{%d.%d} =$ \\\\ $H(H_{%d.%d} || H_{%d.%d})$}", id.Level, id.Index, childLevel, leftChild, childLevel, leftChild+1) } return fmt.Sprintf("{$H_{%d.%d} =$ \\\\ $H(leaf_{%[2]d})$}", id.Level, id.Index) }, } // Whee - here we go! func main() { // TODO(al): check flag validity. flag.Parse() height := uint(bits.Len64(*treeSize-1)) + 1 innerNodeText := nodeFormats[*nodeFormat] if innerNodeText == nil { log.Fatalf("unknown --node_format %s", *nodeFormat) } nodeText := innerNodeText if len(*leafData) > 0 { leaves := strings.Split(*leafData, ",") *treeSize = uint64(len(leaves)) log.Printf("Overriding treeSize to %d since --leaf_data was set", *treeSize) dataFormat = func(id compact.NodeID) string { return leaves[id.Index] } } if *inclusion > 0 { leafID := compact.NewNodeID(0, uint64(*inclusion)) modifyNodeInfo(leafID, func(n *nodeInfo) { n.incPath = true }) nodes, err := proof.Inclusion(uint64(*inclusion), *treeSize) if err != nil { log.Fatalf("Failed to calculate inclusion proof addresses: %s", err) } _, begin, end := nodes.Ephem() for i, id := range nodes.IDs { // Skip children of the ephemeral node. if i >= begin && i < end && begin+1 < end { continue } modifyNodeInfo(id, func(n *nodeInfo) { n.proof = true }) } // If the ephemeral node exists in the proof, make it a parent of the biggest subtree. if begin+1 < end { modifyNodeInfo(nodes.IDs[end-1].Parent(), func(n *nodeInfo) { n.proof = true }) } for id := leafID; id.Level < height; id = id.Parent() { modifyNodeInfo(id, func(n *nodeInfo) { n.incPath = true }) } } if len(*ranges) > 0 { if err := modifyRangeNodeInfo(); err != nil { log.Fatalf("Failed to modify range node styles: %s", err) } } // TODO(al): structify this into a util, and add ability to output to an // arbitrary stream. fmt.Print(preamble) renderTree("", *treeSize, nodeText, dataFormat) fmt.Print(postfix) } trillian-1.6.1/docs/papers/000077500000000000000000000000001466362047600155755ustar00rootroot00000000000000trillian-1.6.1/docs/papers/RevocationTransparency.pdf000066400000000000000000002642741466362047600230120ustar00rootroot00000000000000%PDF-1.4 %âãĪĶ 6 0 obj <>/Subtype/Link/Rect[116.83 701.13 137.62 713.42]>> endobj 7 0 obj <>/Subtype/Link/Rect[137.62 701.13 148.79 713.42]>> endobj 8 0 obj <>/Subtype/Link/Rect[148.79 701.13 181.82 713.42]>> endobj 9 0 obj <>/Subtype/Link/Rect[181.82 701.13 184.88 713.42]>> endobj 10 0 obj <>/Subtype/Link/Rect[184.88 701.13 205.66 713.42]>> endobj 11 0 obj <>/Subtype/Link/Rect[80.14 625.14 129.65 637.43]>> endobj 12 0 obj <>/Subtype/Link/Rect[129.65 625.14 132.71 637.43]>> endobj 13 0 obj <>/Subtype/Link/Rect[132.71 625.14 199.96 637.43]>> endobj 14 0 obj <>stream xœZKšn@7Ía<Øu°‰ą‹5Úũ~ä$í‘b=fFԌĮČeáĮĀæ’ŋŸb=Hv“Íąí%ą›,V}ŦúĒČîßĻŋæÕ,˒Ųí¯ĶÅít?ũmšĖš"ÛŲüJfŸĻe[Æšõ‹î~šū{zš~žĻŗ˙MŗŲ5ôũĪ4Mf˙˜ž˙Ė~1éLũũ÷_–<¯GĢßIÜ&I’A+éũ63|D%T}ŗLg îíGŽŽĻŗēŽËzVguÜdŗÛ§éûŸĸ2ęĸûč-ŖytŠ6đkMĸ:ēƒö>:F7đ}}öĐãņņįˇ×Ęp>Y%q‘ĸLĶåÍ2›ĨŠ­@YĻu’ĮEÍķ˙ eOĸ70ĪfÚ@{]x&ĸą)ŒÍėy’8ŠĒĒDrõžešVq“Ã\´ŸúÍOĶ,)ãĒr›)6ugjöpũĻéŌŧŽĢĖm~ķt,z´ęAŨz@”ŪŒ=Ÿ‹&Ž[ˇ™úėQØSĘ]KÁ}Ēq?W5{ō'G—&UŪė4SŸĻCÕäŽĨÚ5„Ā>ˇQįS’G¸JÚj<šZq͏ÍÔ§ķPIšk)yåSûyTŗ&ęōL(ô5uTãģ–jK@oįUúēlQ–tÖZX‚?z¤JGGę8Ē61¤÷øS´ĻŲÂ˙9°M ŸŠé:āāø~g]ųŅv‰čŠ ôkÃô–ÄuíPĄBĀĄÂǍãZ´z 1{‚ Ņ ¨p)t<6OUqâRnîŖÜ (_ųäžS4;*"ąČŋ€d›$N ^ĄcŲ-đœļÛĘm–Ø”žåS^pŽŦĢŌm~ë\ 2ØÂīē…ZˇđŸ§+x¨fOÛ'Gų<‹ëÔm–>͆ņ(w-m}Ģ,ũ\Õėɟ]‚K:ĐÔÉ|×Rí+Ē îzχ=´bÖ\—ŲĒļŲ‚|øų*Øī<åĪ\o°+Į ŦxJøžĀūV öéCÉXĸō'háSÉ:ã}™į­G"N”u,sĸ2- ƒ-}>ˆ:aF=V‡ú* ˇĐŪ€ŪjŊAũTŋ9ęŊD{Ŋ¤Į-ËÛÁõ5|? ˆ ´pƒe›ZžŽ^# īŧ˜Ü Ģ@W…ČJ aœUÄuDrV* ŧEŠžD/,Ôģč=hŗƒųˇˆ† rDDî™đĄŸĄĮŠõ>qz}7hŖĩžO¸ÆņʅŲŊ3‘ õīqøN°ū8R06č eĶÆåķr`ŖÍr ,4H´|b2ĩ7h˛?t&(U…€¯B!-Áߋ➖5đKˆžØZŦdCr Af‚ĮŅJrZ į%ę|d…}z‹˛ÄuŨ`ˆŸĪqé:Žoņ‹ÆŲ„Ŋę¨9õâq>Š@Ũ!Cœ4ÜČĸĨ’e!_ĩŋ.â4Œc™ÆeÃöĢ°ßąŊ'¤ƒÅ_Ų`Ɂ“^"âŽpAų†ē4gØ0ųíŊŠO ÷ ĖH>7Ađ85r§iŠ4Ú Hwė‡$Õâj•‚HT`gØō&nsFBiuŌžl/Ōpq7Œ–"Ėę¤LõšĮ{×zä ÉqqM}…ˆú–(ņh9ÂꖩdÃŽĐąû ŨŋG€ÔDgTâČėö:lOš6$īŦŅ$!Ū-ŠQĄ9‡xŨÂÜPjŽĒ*Č?¤ō˛-Rč†}Í›cBQ8uŒĐqč8ŅĖš÷ ū6–„+4š2JyĐ"ęeÆA8`ãP‰g܀Ä#†îB'ŒC 4yq]zßk˛äĨ¤x!æĨß{h4#Š!Ú׉ļ—:§¸œBŽØĪDㄛåqö Øą´Íŗ„›B¯ ˜EĶ@­‹ģ5›ēÂ8# =¨+] J•ĢOx*8OĪŲQ”Ovčļ“-¤N#‰‰*Ŗ3†¸qŊ5‡=­˜Œ îŠļ‰ŗ šE [ŌF“ ŅĀŋɎĢæRɔ°ĄēqĪTaœ^|Œ*7Š9}”jjMYũ€ūv…ģXã> :Ú ŸČ ûw¸6 šQ,°4ėU'â#atŲ ãÔ‰äėöâP"öÕ7G;]ąNˇ+]*k戞x;ĄE’Ô;ä›N =aŠŗ¸ ģH ÎĄ‹ zYĀ_f]đZ+ŋōõ–6YúI~kå|q•# ēðŲ-LËÔĩGhī8æ:NkÄSĐe”xŠ Ŧ ģlzęâ9â)Š:ŽÂxž3i-âétÎĄī%ž-ßõŲûŪ}@[†´īF×Qo ;ÄhŽŽ°âk“ŖBŽ’ īXb“wÚŲ;ybUYĘ(9˜6…šÃÅí4Øé”v}ŨNŦ›pb§GKd¨č-ŗĩ°Īf'xƒŋîUņ<ĸRīN{Î0°…Õ>ŠE–† ČaQˇÁ]Đ1āŗ Œ.ŋėđ.‡ĪJfú‚Å›3W|1ŦäqÎĖž3ËRE+9Tģ­ėŽž÷‰r?ŋTīņ`u’vŸš—}ü¨ā™đd9ÚZ_3‡´Ā=6scÍŋDĻ0ķVÆėvĀĘáŒĶŗäQ¸‹ę‚‚I{RõgĖĮŸŊį —{ŊFŽXS•ŲgVŅ/°E}Ŋ„Ē˙‘}€`@VX|Iųfá9–¤GēĘÕÕ(yî:{ŋGšĒ3L‚ĸ?g˜ŊĩúKœ–Å ŌÃJī“„DŽ0|vŠ—”`¤XRWû‡X—‘}ėDĨ› Ķą‰´u9G˛]G`FĐž¨÷:dPũ­•¸ŅЇ”fNwöXÚ-¸|e<ąÁ…Č ÔjĘį2eÖǺא˜:åD$E,vLœôíø:é ^‚`‚S<\j÷…^üa ^ōŨ%>Ü=ZTŅOĪ/ŖŋiĪ'Gš×Uđ/ŧe$ąōį(M§U(LJSgĘ1]a IoŖ3āâôŽ886÷8ĄŅyĪųn‡,(Ų­“kīŌžĩĩK|Ī€j܈š/ŠVyXõx-ÉãĨGĒô“ÛOE™ĢĮžā„ßíŲ÷ąôÍ˒|ķö-†bĐ Ę-;ËĢ_´Ŧ\¸Y>åjHĢ˙„?E°@q—Ŗ+fOķģ¯žÆ3Õ¸C—u܄ ÄL€Dz&ĐæHj9DũĀ#3įmļ%Æ˙É3iŖr`ŽpŖÁԊļÅs1¤Đ+WÚeXā™3ō|´€1öĘķ‚5Ÿ‚ö×ÔŪĸJy›LbS ėœb­™:ôŽÍäúH…8ŠFRĮYUõ˛BUZŪcæ:ã9íŖčdšâĮWš˜jLĸļ¤;))ܤr ÄyքãŌ9aõ¨}ž-5>>>/MediaBox[0 0 595.3 841.9]/Annots[6 0 R 7 0 R 8 0 R 9 0 R 10 0 R 11 0 R 12 0 R 13 0 R]>> endobj 17 0 obj <>stream xœ•ZKo7^@7 ˆØÚC‚ A‹õršÜGEŦ%ŲV#Kļ´Ž,߂´(’éĨŋŦũ}9Kîƒ\Bĸ}œ™ofžRū:~{?æé4DXLī?Œ/īĮëņ×q4ÍO>ÃU4ũ4…šuĨŪ~˙9ŽÆ_ÆlúĪ8ž^ÃØŋÆ,šū>~|ŠĻ`6•Ÿŋ˙°Öƒųõly…EE1ÜEk#á#ęÉe@Ų‹Y2elz˙—OŲT¤00ËD˜'ĶûĪãĮ×ÁH–Áüŋ Á:˜7ATAlƒÃëā6¸ƒ›Ã!xOfđn>ÂāÁū_?žyēŋ–h€ČĻ \ ×딇œË°(Œšo4Ī ō׀ËsĖDôwÁ%Fō zZũŋÁīČGųŊWQwĻĨEf^“Ō, ™haJ.ÅŠtœ2r`3æ• ŊIJŽãķĖcä̟2RÁōn*IÕQ’ Iyá•IĒíJęÉÃyøâ” ųWøÃ%N á!b0Vž;åAž%ū˜€<ËÛ1á´íÜg[ĖÃȐRŧ?6Žķ$›đƆ€d+\ąŅ´Æ]TÜ´%Š<Œŧ!# ŒvČė`õJŠ6eé|×vS$ūŦ™õNm So0‰4!—Ø€+-.áJŽ?ŊŪb•š‚̉f!Ŗs›“<đd|€ÕäŽhG[Û/Tiģ$I@uɲ—*I§ĒüŪŧŧ°čāsŒĻËŠxŅéN5“<ŒŊŨ‹ž¨Ë‹ŗL āĖې–īú˔)Rž"$€¸ˇÁ@íYAķ°īm1=päŪKō‚s˜ģöžékÕqĄŠęą[§8ĄíĄ‹īkēŲtš*ŦÎü6B‰­Ëø/ØęH %‰Ū⎴íėŊE;*,™°mĘîîXŠMNCn_‹IãúWmCÂŌ4Ė[ĢŽ“Œ%2īđN%ˇySĄÍ dĐ%„ė?GāŅīƒp•5ΐ>ÁģGĀäSN÷É%6Š}ZĮA“ÄP`[.YõŦJ㎃&áąŦ3ŊД ōējƒŌ'W¯Ô'ˇ I ǜKDGÂļ@™Ũŋmđ)9¨Ô]Hŧk„ß{x˛čȀŌ- fVõ5o¯Cu†Š)gŦdÁÍŽąÜ §lõPëR!)˙3Ž„ûsŧ_Ŗ($Ę\QHøá”­—ēzĶvH WHZJęâĢD [‚Ļ0ŽÄw2NK¤ˇuđÜŠO’Ō~Ü5"NŦļך÷ÛÅ “]/K•-¯‚ mRŽYcvÍŦ"QĐEë€/6øx…ƍ°jo`ēt™4v§ŖT’ĐLMš46‰# ‚Eyt‰OÜYŽ„Ņķ5g%ƒË‰DĖ6ūŧ°šÅ—Ā˜%"qĸå¨t›a„ÉnIņé-õZa(/ŅŧĘÕKē=ĩbdĄ7Ϟ9|–8—ŦŪjļ[a­ĸį/˛Ü¤~×OQæŪ¸āyÆƕ$÷mŊŠŖĨ륎Ž3´k‹Ųr‡ÍqöĩŽąĨöĩíYšÆFw{ô‚ÂLi$ŗJÚ ā.,;@eH(p´ŧHē*|™RA*ˆv¨ÎeÚ@ØŦwVģôŧæmEks‹˛ė`Ū[Ļ/įŧ–s֑sâą?įĄđTōĸ¨íWT[+ܝlÆ3˜îŨ¨qhly2Äf\Ā(ī2Đ×Ö§ ÍždÚ5R!dBy™ēę0ÍQ}p‰a×G˙v€Î1ŒŠü´ŌRĶĨ•§đ,ōC'÷ŲÃĐq?p,2[ņŸąK|h0ŌL7k}‰wđøÍ.:7œ•vĮ(ˆą›Ū`æ3Ձ­qČicû}hSĢ˂vĢX!ō¨’“ŒöbË34Âv-ô%Ę´{ Šõ>ŨÔDöĩt{5Ę:Û͋4›yg“‹ÄPOĒ7E*”.Txē„Ä2ëŽÜŠÄĐ'L#¨6šnņîŲJŠUÄŽz=÷v/Zę~zĪŪiågúÜJž÷¨āŠôJć´T-IĨKĘųfŽ+Č~ōeŖđ`8AI’!ŸCŖQ—M< WĐ įŧ{ϐ›"fzķ; z1´â›BâĪ‘ŪšmąŽí:åS­GØēzhペ‰œxpÖ‰=1!Ģsaã r53ãKŊõx‡™oc!íëCĸƒŪ#Ąh{éĮ剓YâÖķîėTûÁSVđS–1ŗ}åjđģŽ(utoäĶēôđFˇ.Ĩöáē>Ú6~Ĩôo“͉ŠP27ÕVlŲę̌—'ĐĘ10kĶģNxr6°fĐĒ:{4P >>>/MediaBox[0 0 595.3 841.9]>> endobj 19 0 obj <>stream xœ•WˎŖF-Šwdáî‰UĢŖD5ŧÁÛcÆ&cCÛĐĶŨ¨E3‘&›ųŗ|_NĒl7Œ"$(ęqš÷ÜsOߜßN˜¸™ËĨ{øėŦNí|s<7‹|ô|EËsŋ8ņ2–á¨ĨGŋ8:ķˇãģߝĀŨ`î_Žīšŋ;O/žûf|W]˙ü1˛‡õũjÕöäŌķŧoŪI{øÂ+}đ”8ûž]ßw¯0Žz}7N01M#™%îáĢķt'|҈N”â ÷­Čqŋ?‰Z<ˆŽįWŧî0œ‹O¸ˇx+ĐŽņ\qŅ o…ø(>ˆŠ JąFoΙ5Ūņ,ŅęÄ3´…í’Ī5.ĩâæį—ÃFá §O]Íāj@W§f$ |ĖÕ¤4~:k'ZĘØ‚’ŠŠžîá_A`jöėҧGîEÃøW ¨gTËÁ€(H ]ÕΘŅ-@ # ŒÖā¯`){øJBģ%äz冪{äĻĨÅũ !îpofÜ<Œ!î ˙ž<ØÂūâď1НV⅞ėĐŗÁĶ7¸˙B_JåȕŊܚxa:ríˆ7åīŅÄQpFiælIļ ã˜qCû—­€Į3 ļ6DMĶq=ƒC”ČlžA$ƒÔā0øĨSžeļ֌mEbäĖEŲĮĒ2x@_ÅVkŧŌYîЛ3‹ˆÎlÃ˛ŠˆjƒHĻK!„gį9|_@4˛‹ O2ßŌũNÜŌnËotdcĄéúH§•ˇp!ĮÛŪÔīíˆp*ˆ_y×i¸ĪwRŲŲˆí”ˇÉ2•Qráí›z”$ŪĀŊvīq5W]1%ąqî‘#ë÷Á͆h^Šč”{š$z°cE×PĒ1]Ž4Ü WĮ]œIøÂXPĻU tėŠ §k#<Õˇģi&Š'ŗlŽ„I” $<23íČīš÷Iĩ?ÉŖcđ5ą+1IĄ¨jh´ĒW—Vķ@#¤‹ŧ2ün$Sō‚1į$‘ĨļÕx]ô–ÄV*Œ…I$bÄÎ"Æ2ąÔrˆØú8dîßŋĖMNš°ķë3Qmûļû™EåԒ˛$'TFtAĢÂʍDUf—1ĄFą ĩ;“8MzĩŌl`Ã†Ō—ĖîPÜe. ģ\õ‡ƒŽģ }16øhN Ķh„ˆužž'CˊCļŧ}‹•"į<[Ģ[úŅö[si˛­{Nøk6ĢŅˆ†ŲŦxH*GtîI5úēš=x0)ōIāÉ`– 1„5ItŪI<˚Å0Îbé…į qhŠŦ*ŦKl# Q)ž ŲĄR:^’×c–Y=Ņ[įšČqāŽŧĶ'”#u~8Yl-ëĻˆ—‘\΃Šãa˜õ2Ŗ#ŠáÍøT6œģV#Yܘ*#Ë:Šk&úãÉšnņđ4BöĨ„ õ5/PĶdˆpž8#ĻᨨŅ÷†āös^÷đuÅX‹Qb­¯įˆYyŠ{ī/l˛2âd)“ålQ*ŊėG•Į˜ÍÚÁA+ēŦ ũˇR‘āM¯üĶ˙#Â[­ĩLQŽ)eoN/9žĄŽņšŗŽšîĪ;"ĢÎėŒõé/Ö$Q$Ãy0_Ļ˜‚­O÷3â‡Xsí˙˙=î_k\˙ŨĻd endstream endobj 18 0 obj <>>>/MediaBox[0 0 595.3 841.9]>> endobj 20 0 obj [1 0 R/Fit] endobj 21 0 obj [16 0 R/Fit] endobj 22 0 obj [18 0 R/Fit] endobj 23 0 obj [1 0 R/Fit] endobj 24 0 obj [1 0 R/Fit] endobj 25 0 obj <>stream xœíŊ xTEÖ7~ęŽŊ§“tē;ŨIgĨ ĄY‘Ü@ˆ`Âj‚F‚‚ÎHp›peUÁWPGeĀĨIPÃâ—ˇqdFqÔŅ‘QÜed\ĐQŌũ˙Uõmˆq{į}ßī˙|Ī÷$áwOm§–S§NĒ{bDdĨ$“ûôs—ŋu×ëHš•HŸ9¯íŒ_Z>ØU‚đ×Dę_ĪøÅķ>ūgáqDÎŊDÅŋž?wöœũ§žr6ҐÁ3t>Ō–yķŋ ņ‚ųŋ\rū蝏ųßĖųÅÂĶgKʄ)Dg9Ÿ˙ËŲįˇeŒVÎ%ÚŧåƒmįĖmû× īzŋŽ(õ/ę.ʸ—r”"Ę!ŠH"ļ ~€įq*}DÄr0Úé>ú++aAę`ߐžf™l'…žÂH¤nēž<4n`iT@^šN㙂2aZĮn‰Ÿ˙ŽŖkčÎø#ė’øVä_M ¯Ņƒŋ+Œ†ŅD”ŸNséCų]jŽßLZIvIS˜—fĶ+øũ}¸–ŽŖßą_ĮŋFĢēõUS-ÕÆĄū´NY¯žj}ˆ6ĐnĻÅO/ ~”Ok¤pü•ø[TDÍtŨ‡>…Y—2Žōč,ēœ6˛Lų]OŋĄsH-ōõ1´4žfĐŲt­Ą­ôKcęĢęĄø¯âī“FéT‚>- Ų6Aē[qÄGÅ_§“i'=ƒņōß.ådå^õäXMüļø”A0ÛÃW+ÕĢē/Žß€čĪ Hd"Ú9.ĨĮéYú}&-/§q4-˙žå˛ +‚Ä_‘2ĨeŌ2ų%ˆŅļ ˇKiE1#ģh7= ŲüöĶģĖÃ˛Ų ė4ļ}&9¤9Ō‹ō-ōųe…)ŋ…ŧCT-Ąģéaú#Ŋ@/2õW°Fv&[ČndˇąũRTúDúJą(—*ß*ŨjQlėÛøÄø—ä§,:‘.¤åí]ÔA;čO´>ŖĪé0sŗál>ģƒEŲ~ö‰d•ōĨIR›tƒtˇtŋÂ(IüæK#Ĩ:´ēP:rēDēVē­ŋˆßŊŌëŌ›ŌĮŌ—˛*įËCåEōrTî”÷Ęī)nĨH¨ R&)3•8fĻR=^ĒnQˇŠO¨‡´jmŽÖĻ} _ĸ_fųcw˙îŋĮ(6?u@w-Ф !‰ÛéNčũĖÁsčŸĐãũôf!‹åąbôģŠÕŗ6ÄNasŲ%l%ģ†mdˇ°;ŲÆ éč{XĒ•ĻJŗĨšŌeŌJéJi~wIĪJ¯H¯JŅsŸ’Ãō yŧY>cX"/“/ƒd7Č[åå—ä÷å䃘5ŸŌOYĒ\¨Ü¤ÜĢėPūŦž¨ūŋwĒŠ]ęŸÕ#ęMŌ˛´­\;SÛĸŊ­kúPŊQ_­ŋŦnic9Ŧ?z¤?R&Ö`?iĢäQ–ŗƒHČe Ĩ`äaĖÃTŦŠĪŠFŽa^\<}ː2•tΊJüKØnÂ~OË5I†UTöS;{CÚ¯<)GûX+ËTî•ĪVŸ“ōhŦŅzi´›ĻRĩ4CēU&ö.ÛBīBßΧëØYl1mcŲvƖĶ˒WžĘ.Ŗęø’ÂŦl<;Dč]ŦĖĄSé'XŊAÆnWœĘ¯aŸ:éĖč}ôû-}ÃÔø'°n2ŦŅlX™uĐ÷ˉ[ŊŦŗåX™° ŋĐ^¤LƒĻR.¤CôoúPŨ Kú~lrģōN|Xŧ + ̌ļ`ŨͧãąbŪ…–<Š8‚•nƒ-ŠÄĒn¤™4‡.‚ÕۏÆo_ŋ žžī7lû†mƊčG5=ƒßĢé5ļëđøŸįũÄæP}ÄüŦUb=TĪU×Ģ[ÕęīÔ´Aöet 4úmhŗ #8ūLŅWĖ‚šÉ¤A‡ŖīMô ŠY~”ư,jÚ-mŽd1jšŌģëųQŦC°§ĐīčU&1Ft:Úˇ žČyJ߃ŧ”u eŦvúãvąáŌ´g Ļ`ĩēЧ7č=H;.ú5vĄŽÍ@]_ŅI4- ĨFļ3đ0UÁ˛ÖÉ„ŧ ˜›Fŗ|öđĩb…ē(—ĒÔw˜DbãÃĨōŖØcâHߌŨ+›Žc‹Đ‹ŒŖ›2Ø$›‚>ŧÄd%Ęū"zq“47žR>/ö zž~‹91”sõ:"ŖvšQ3ę¸ę‘#dž\9¨ĸ|`Ų€p˙Ō’âĸ‚P~^0Đ/7';+ĶīķfxŌĶRŨ).§ÃnŗZtMUd‰Ņ€ąĄúÖ`´¨5Ē…ƍ+ãņĐl$Ėî‘Đ "ŠūģeĸÁVQ,øŨ’JÎëUŌH”4Ž–dî`5U— Ž Ŗ/ԅ‚læä&„¯Ŧ 5ŖEx‚¯a'Âyy`ŽõΝ FYkpl´ūÜųkÆļÖĄēívÛ˜Đ˜šļ˛´ŨfGЎPÔjÛÎ|Ŗ˜Hžą#ļKdqĸSŅŦPŨØhf¨Ž÷ *Ž='Ú8šil]v^^sŲ€(szč´(…FGSÂĸÍDĩ1Q]4\ĀGCkƒÛt­Y×éĻĶZÎ9Ą9ŗOiŠĘŗ›yŠa´[õ]xĀ,ŠĘĶÆ4­ė™›-¯ë_äŅ5kVŖ]“›zææņgs3ę¯TXßēĻM¯ƒĻҚtysS”]Ž&ƒ|$|T‰ņÍ å)­gŖÖĐčĐü5gļbj˛ÖDiĘyíYYÆÎø~Ę\3­)”­É5ĪŽËŲîĄ5S.čČ4‚™ßÍ)°ŨšėvWŠp8{æÍ!Qœ‡Ļ•,ã= ‡BDƒ§Ņ“ĻÆ4œ?æ§5§G1ü43pEį`FD­cZ׸GđtÎU ŨĄāš/ :øÉwSf›)ZĄûKâAŽ'GU ųÉp4ŽöīĪUDƒ9EG‰ø˛įvJCCmî ÄGíėæå^Ÿāĩ†HtÅäĻDō;ņīôÎąFF•"ŠaÚĖ5klßÉ̇ZŗĻ>Ŧ_Ķēfvg|ÅiĄ ;´f§t¯tīšļą­É íŒīZ›­_׌AĖg# ŦŪbĢ&o7ØĒŠ3›vēq’Y5­Š]bԘÖŅÍÛ ×´ފ!RĨŖŠ<ä1j`PôvÉ"˛˛wD+DŽ"DüôNF"͒Lctz§”Hs‹4ü”Áá“¯â^NŖwH,ĻéR‘NĒ“ÉĻ+1F™MIōVDV8Ã~ō‡Ũ‡ĢģĢ'ēŋ¨žĐ]M5ģā1¨"/5/ĩãHPî:b¨ô-•.¸&đ)I.Rģp‚œaXĪ’~%­•dIédĨŗTĻvJ§>bąĒŒVœršāū2ŠÅpǤ” U%Ķļ‹Ũ ÁFË-ÕxĐxMõ-ĢUPK^^ĒĻZ0l°\{˙æ?ŸÍ¤ŠJhũØxÁŗWđĖŽŋ¯žĒžŸ˙câÖ՞ÕŪM´Q{Úú˛ü˛ũKŲZh-q”8K=ĨŪĨęRëĒEO×}žtŸ¯Tę/Ēz‰z“zŖõYų÷vĩ†M†?ÅMl?ļ_‰:ã]Šūˆ 6'(›iøüeŠÅe¸Ō"ކY)lR K12ü‘”NVb䧕Ųä”O]3čSUeU䰜ŒâÍ:KŅz….c"Öud/›šîĸ 'ē[ˇL8øÅAĒéū"ܲč@˜SháŖg---LՔPRŨ”ôy}jQQ(_Ku{WUjX`tė…OboÄVą Y„9ˇĖЌũ-ëîsīzū™Íįn•˛O>ô!ģᑺŲõ›NÖŸsŲGąob}r—Üõđqá m§õÆqUŅ-…ZZ@eęƒĒ¤ĒVY)„ÛŦ…vÂžß KãldgöŦ ŗÂi8e§b ÂãĢā‚’Öv8MMN"W# ĢzÂÕ_$T‰#ĩĒŧe–úNRã]íšU*Y{– ÛĶĢÂáp3 ÉĒģ:785/#ĪÄõJ͑ĨũŨAy°ēëëØî¯b‹žBī×a;Đ{™Š;*#•ĪT¨PPŖÆã‹j¨ę uŋĒÔVĩM=¤*+Tˌ$“E’_ÃZ‰Â˒ģø|ķąėELĄŗ•A›Ė):Į\5čCũDįR׹u×7õčĮmâŨęXpĮY:¯[^‘EQŗtIî)"mĐΞ"Šņz't›UķZķ2nc%Ō~õoĮÅghÎ&™ŖCōvģ\d)˛ËŠĖdˆĖ°æŒˆØ‚#FFŦņũ&5~“3ŠxhV‹íë'6EąÚléRŽâļl!i€´–Ûΐæ+s­gÚΓÎW~cŨj{ČēËvØúÍģIYoŨdûƒõYÛ_ĨW•WŦ¯ŲŪ—>PŪĩ~dsžg=ßvŠ´NšÔēÎļ^Ō›ėsĨ3•3ŦķmįJ(zÔ ÔYl'YN˛6Ųtŋ­Ü‘F(ëH[K—%‡ĸY­ļ )KņYõíš4fZ“€ lVÕĄë•šËQ Kâ–%KŖÅąķ‡ĨËîŒX WqÄÎHēÕpķ€Ũ‚#šÂ$Ũ†nM5d˜šæãJ„ŸV~ĐũōAžŨi”Ą• bąZ+eÅ#ˊdˇŲ*e A ÕČE’6›ÕĒ[.æędÎîá ĩ:š%ĄNžŠĶ"jĨnčË-ĖōčrĖÂŖö Ũ!uJÍ4葁‚d UĖÁĢqZ ŗúÅĸƒá°ģúŸîęŦLw÷ĸîEÕY~ww8Œ÷Eč<(úŪŽT†W^ôÔʁ~NÂÍP‹†húTŦK|˙v{pøđfnđ#ô0LáE\mã&ĮøÔ l7Î:Û;{3öNėīęŽ#~ųƒoę•Kž]ƝڈŨ!˛˛?.ĢŦY2eŸEIÃj€tŠ#Í^#w&†ÍŠŅ#’+u‹G×-˛E’tŲ yAV˛ÂGŦđ+•Ú‹ÂÚ¯52 {ŖŊÕ.ˇŲWØĨÍö.ģ´WØ!gĢY)§†kęԈĩRŦŒ.,`ž6l\VæÚ‡Ģ'¸ų ›1ąūXjZU+ōÁCB =ÚI2ß ­°:Ōõˆ•k ÄKBŽcDŠۇXV؇ˆ—50b™Š‡*{åJؐ•zųrËzËfKģå€Ŧ=%ŋhyŨ"årKDi™dšFŪdŲ,?h‰ĘYė:¯Á:xHD2đĐųĘs–WF¤ čž!HšŅ°æ ŒHĶđĨëûÃÃ"éē_’}úŠX) Ö'J†~Š4Cˇz¤l}‚4VŋYßĻ?/Ŋ&} Ŋ¯˙[˛K%ú úųú*ũ>Iã6čœpō‡’ĒĐLB¸ aŠYPjbéąŋvo‡”É/}S/ī9‚CŸ„“)Š{0û”G_—TĨŒO9I?Ķ~ĻcĢõ^׿ĐÃŽW­6ÍĸŲ|¯m¨ĢŪUŸĸ[ÜÖTË“âqu M9>eŠë÷K6ûųÖķ3ĪÍ]e]•yEŽfõzŦŽ×T×R×eŽë\wšTWĐéđ8ŽG†Ķį-Lw{XĢgŗGōx(˜Į÷§Ë•A,ˇ=F19ŨNÉųrvņf-Ēui{5E[ŲbÁPEH åeô´ĄųƒN?fCŨ-‹ˇ„ĢĀM€[ė2i|—ÚMiq]ä~ŠĨVâbG]ÔÂ%UéÅĄVĶŊ^_zž

!ūžō<’l¯qÜÎÔÎ܇Kū0@Û‘ˇ#ÞĢÎ-Yĸī\Rōšã•ŖŲ6Ũ5=ŋ94ß1/íŒŧ%g 8/÷ŠÜōi!nŲû"œs3ŗ"“ķ'‡Ī<¤,Ę_ē8˙âĐ?ō˙ŌÂļū΂ü‚P•3j°58ëōĮ„ÎtÎ ]āŧ0ĩsMū=ļ{[ōĶ­6ĢSË×B™ļL§7_Ī؜ ķÍđ™ÁČB?[čßä—üģ¤š” mudU˛Yv™GĻqŒĢīøŦ`¤‚ŦĮĸõl3|Æ.fa˙TŒŦ*ˇÂ”˛ūV˙§qķ鞈¯A/.Ę(ŪėŽē%wû451™e1„†ŠMÛÉŪ<ĪŪD÷aĐđ9Ü Zūĸ%| AĪ €UL¨¸pō!ėÜQĮ^“žĶž^•ņ€ öl{í5RŌǜÁ´*›@ OûĀp9æŦ˛ų9ŌĢÂ=šÍ-)c„m„sHūČqŧsL~}čÛoķmÔŌĖUgkI/ôrŋK¨ ˙:tpPI8eē–áņyĄYÜc;ŗ6­ŧzÃq'FvūŗuåōOË<˧Į^Mŋčĸ‹Į—Îĸ/.]§ĮbÅ^aoælXuÁäȸ봁#g\đ@ۓķ>{Κčô!ųU‘Âōyŋ|tí˛7ÎbŒë×x;ą†u:Į•[+” ĩŅÚf]a]oÕ5ĻJ…Š,édąú|YĘrn—Y™aĶô Ģ å|!š*ģĨ6i…´gŒLK÷}æŦLnÚ.aVāšĩ` á1vnŨa}ģĢĢ…›3„ûeė­ØåĘØD副ŋūvĒ˙@¨ŒĸU˛EÆ|=˒ŖæzŗNČ—3žđoîˇR­C3ë3O*š—yFŅE×d^›uOÖÎė§ŗžÉvhš3ÃĢez‹ĩԌæĖķ¤+¤{´‡´?hŽĮ"¯šĨ܂ĘAŠœFx`¤ĀČ/Á#37˛°āHTPŸËuŗÂ•9.—QŽ;7šûī\%7wLRS(€ŽMĪ3rRkōŒl7ūŦH^§´ä!Ew8møŽ„Ų—9xAmԃ„n/:ØÂ]âp"v€{”p Â,ļ7ĄņÂn…jØ^žË5LnoØĐ˛s#Ķ æH-áæp˜É.wbJĩpÕ,†"rŖ&{ŧž<Ž›šĘú9lč0č+Î L͏ŠB‘4t›˙åÅ= rvaė#ģ[—ĮũĻå7Î¸åšßŸØ¸°a;učGÚęN;Øm—ŪxķuÍ̉uŽģüĜa™–úúöU3¯lČ) æL;2ö—´JqõČ•Eà æB*ĶĄ 5ІLú‡1š)Ĩ9­Ų;?eAÚīEū 2o”ntüÁũ˙_Ũ¯ø?Ô>´|˜ūaÆ×Zúđôá'¤ā­÷7;8ôiÃŧÃüōyęy)+Õ+RVgnIģ×ģ3ía¯Õ%f);ÂéCižˆk°“§dö‹š’qîb Ų0‡iŠv2P” ”ŖÁë1W찊d}:ãŠ,Ę<āĖ›˙/+[Īķdf5%ĻŸĐø-üÅÁ0?ĸĩ'Nh  ›°4q$’:Lå‚į§5L‡2(öąëôI .Z~Vãŧ æ ņÂ‡ą™÷āīJŸTNļaëŖˇžŧ°üwO°"ĻĀo+ŧ—īŅĶ ģŲXß)”ƒ“YYZŗÖlkN›ááoÎŲ¨ßdũÚjmëˇĸŸ4BŽ8FdD2Oë'dÔeŪdĩz ˇíĒ=‹Ģ¯ËŽģR06_ŠËYÄp&7RR(ëę~ŦŸ;Ī’™ÛT}t„‹WO8Ø]ũžXé5kš;å˜&Ú@[`[6Ī;Īŋ GkiÎËb0mpĨŪL^Â"BĘ먎ĩÛg>û6öDû%,ŗ;­ŧîÂŲĢ.;cÎĘ[OnfÅØ1\,ķ:É}¤më‰gßũ›GîØ„ņÖbŧÅĐå°ģv’;ūĩQo¯ēÉzŗķ÷õ^Ûnëngg–Åâaã¤ãĩzÛ¤~[œkg=m{ÆņŠíUĮ×úWNgNJN†U’a¸R#)eŧ˜!gmčW#¨Ë*]iĀMIktĩē$—?īlgfGØā4qČĪ &ûųĨ .KPŽ F LĘf~ģæFˇgĨĨAĖŠ=ÍĪÅ]`×)•g$”¨ŧßŦ~ ûmę§ôKÉŗΔnZ„đwNũąąŋQâŠņũRđ€ōs{%öĨšnąņĨĄ(‘Æ;ƒBiĻšâ´=YĻFėe‚‘VÅ;Ũîã$ÚaĩŅÚŧš0w‹›p+Ō"šw’‹7ęâÍģ +á:—WÃ@aû…?5X€Š%ˏŠąųq'9Ol‰é‰Đ'}ÃüC?|0öņå ˜įĨƒ,Më6äKfžY,Ÿ?ã”ęjÆĻ”ß|ĮCŪ„.„cOĮŊhí8ö‹ —ŗXč~l˛Ō*v‘r6Ņ8íŧܕšRšÃŲ6č įŠAJÁ”+Ø`i°l°1Ōųä”fOsáŒŌčęY)_§~ž6Ō9Ø;˛dđ8BŪ†’ē‡Ũ>ÛU°Ûv‡ĶŪßá,vy}eNļj׀‡„ˆ‰vĨ !uØ ZŌ?ĄĄÂI(‚5#[˙Y*_p”bN\ļ2Žö ŨŸŠõ/ĩeųųĸŗfffe]=ˆ Âė4l4¸ /-ŗâčęûÂ\îƒînąÕbųÁ˜>~r Ņ9ŅxģÕĶĮø —ûĩUđĶąĖQË"ąnSxžQ:/ŧ ë–Z|Ēח´ũC°„Í ô Ƀ{/…‚Ø,Ō=ĮÖōŦ֒[2ãėa…éÎe]¯\tcũ~ĶGĩíž:öŲÛG.m=ãĒUķį^Z_<<Ŗ_žwPčÔ[î{čę}ĖβîŋūČņ{vYŊķ*—téooģãöģ7ßa]ŋĨvÍKíF8…XŸH÷h6:õīėßĖĒĢ^ĩ@jJŸĒ2&Ĩ{RĶŌeÄR¸PseŨjŗy2l^"ģ­Čb5‚‘­,neVˆSâÍ/ˆŦ÷oöKmūC~éS?ķ“§Č›!–-ĘnÎ`‡2XFϝ&!xø”æEB‡Í˜°ü q2õ‰-Ö"ŧXÃTė¯ũ¤ œ°"ÂÜk<Čļ­ztö­“rcī'WöāNŨīn×ļęęî Ō {gŠ[}E÷'4tûZ8o÷‰+ÎÛIV~G•jĢ1ŦVi…5jí˛îĩ~jUÖVërëf$¨˛Ļ“ĒȰ↸™’ŠEb¤ŠšŽØ${†ĐÅŧ‚ˆ’i1ĮulđŲpjnĶQ8'œÎ; \Ë2cīŗLåaĻĎ|{‚Rôíë˜ĄÕ˜ĄYâFđs~^~ŗÃ™*nŒ‹2Ë"ēė–Ķĩbë<íAÛcļgŦĪÛ^ˇŲĻĘ­˛äÔũÖzí$Ëššú°õ-å rDųRS'ę-ķ´‹”uĘ-Ę­ęÍÚÍúÍ[@IĶÂJXí¯õ×û[ʝ Jƒjƒ_‚ÉÅĻÚŦ˛ĻØUEã—ØvģEˇÉ6›]é”~idŠå–Ē€ÎôšNÉ^ÄV  Ã™Žš_™nwĻûđ"?VŋQI 7+œ-ÕĮ. žiˇæEH\ąđë:§%q¯Įøm ĶSWŗL6žÍŒ]Ī.ũ9öåĨ8GfįÆ~Ũ}*{suė>4}l6§ŠûGŖ”ĪĨÚ¨J+Ô¨ÚĨîU?M\:.W7#AŐd¸$rŖäŦQĻōŊY3įipbŽĖ;ÆeDÚFXÅb6r'•‚ģmÁ ;24¯#"G,$T'ĩŒõׅAšŧtĒĩĩtEéĻŌßh÷ę÷8ŌrDK÷–î/uQiyi#2+}ĢT+5˛r"5ˆ¯™Ēž§čYš^nĖl:÷’~ŠîNM-ÎÎÉ)*ļAõRÜEiŠÆĖ!­Šl!ŠSĒ7R˛˛‹rsļ0‡ĩæ°¤í(,**æG;Q਄­5œCŅīb-6jj  8RlŒ8.R^übņ[ÅrJq xEąLÅÁâŠâxąRœYōNuŌ‘6k [Y}ûŽ%‡ĩ„Ģ-]q™ĘĪ˙‰3?;'Ė]dNĪËā>˛OxĘ>¯XĘÅG—ōąUŊŒÉkģæŨPQį)Kī,ÁÚÎ-žPĨ´L5Ķ*5ģg¤Īđ6ûo”6j-7::­û¤ŋŠoX÷9ŪWß×>pēīĩûúëĪ>û)ĶÖĞūô_ą'/‹oš}˖ͷnŲÂĮģ6ö åFŒ× ˙üfcāđôqéRZDŽrVĨG˛ëäņÎņéuŲ˙ÎļÎĐfõÛë˙Îļ`ũd ]į~ēáĩÛŨ)._ž%Ģ >yjŠË•Räv GŨŪF+ø}InMbœ‹&ŦÆDē$‡›p’{ßīįiķzúéÔÂ՝Gœ‹šĢ~lÔk™6ø3w2)vdgĶՓ0ÅŪĢævɧŸą SÛ8'ö÷XwėpėĩúéŨĘ;;ļŨÖqī› +‰äabė[Œ’UfuąŠęéøãFN)WŠn<ĢnȗkˇÆū…1V@ŸŨcé ŖKKÕB–b_Ē/´1mŖįÆâëû[uOŊGJÛíÜéz:īŨĐ×ÎÃųZŠsēsŽķzûi÷æītčĩ!Ŗ ŽčŒü9E+ĶVzŽČŋ´Ā:ŦhŦVo?Á9)Ĩ>otžž_P\4Ė1$ß5 )Đ5›šjÍķ;‹ųųų!Ŋ ß°Øqžį‚ŒsK—ö_•qY˙›3Žīŋ#GČš‚]í[įŋŠ˙oûGhž<¯‘Šxœ@$āeoy™w°%¯ąđęBŠĐđįF ŗˆW°ēXÅV>€ č—WáfîÁ8öš–9ņžĶV“Ø—ø­yføüN.ō#°ļâö´ áE<;|Ė ŗ!cķ˛ĸüĄyõyĶXŗo[ā;ĖlĖ')YyųRIēĶ!•dÍR˜R_boĖbYõézMw ūp÷5‰–EŲüzīyîqįu&hž¸ū,āņũ‚D<3KčlÎr˛Ąųõų×å?•˙rž–—īp*J™>= æŪ}‡¯Ŧ†™ Ī/ŒˆÍ\ė}ÄwšJ+[Á1™˜[Üp*ĸdē%3&Âf)‡‰Ák jī`Ÿz}*õC†E|üvĘg–âzS|q¤øĻg°Ū)YŦ1+ž%™ƒ—œâ‡ŋīmYÄßüž“ˆ&„aŪJ&nwá§ĨEë âĪV{ZMJ Ã';ĢGļ;ø=įGÛíUâ诸KEæ%\~luÅâÆ’ī~=/,ų÷]ü˛¨‚eĨ}ú/‡z2ÆĮî;yŲëīžūrIėĢÔYM +‚9EėņæĻ/>}­›•‡§L/É)fxRF͸i͞ĢÖ5:ā õËșwBÃ×ü%ŠUˆ mPoÞđ‚Q$ŨlĨ)#\'¸šSôĖ ōËŪ ōĨĨ{˜/Mō0ŋlÕmēÃĪŝBžÍž¨OnéōÉžNĻ´g0n2;(ƒ˰Äp9ėÖr[9á”8 V%Œŋ\äK›žQãŲäyĐ#ˇzVxÖ{özyTō¸=AO…Gņdfŋ9éL4D‡ÁNŒ„ØIžxŋô<’¸ķt‘ÉMËAņ Š€‘:8?ÜÆ°ŒPĒGČÔĮ…Æ/SCC)L•.ė˛įŸā?í×'^Xeˇ^|1ËRŠöĮĻ]ÎÉ~Ŋ˙āÉc]Ī^Ü˙ŌobĢ!Ÿ+aeĻ*Eđn5|'Ĩž‘zƒ*[ĩL­ZĒNmRߗtqōIUė^˛ex<6Ģ–î)ĘČ n ]^á%xYkū'ŧĢå¨{`a‡,Ėōã ÄĶË;hI\÷ay‰aʃōď.8kë‰,30ĨfÜ9ũYæĻ駝ēõisĖŋîČIK°.)0N;ü ™§ejIVyDį?,üÆĢ â0ĖšYašlˇXl;NlRšœeͲåS™ũiģkûáÍ Fl¤Ú=”i/¤ūö°¯$kÂ$í°1§CÔeˇú" #+ĶČF5ü qUXŧĘ6ŌėdSė6ĢU’˜†°ĩŠßūœ’ˆŨß7(NŸ/ËmĢąMÂ!¤SĒ0ėŠTeWj”IŠŦė’*ā ­0RCˆaBd–éx ē•É•+ėŸp°;UKæÄąsëŪqáŸrį4­ŠĄ bi‡[ømŠXęy,/ŨĮ¯jĶqy$6?3§šÜĪąŧ¤×ũöCcŊeeRŋ„L­8 ‡LėMc$k#M˛éĒ5›ŧR?%UÍŌ=Ö~ļT‡#-,‡ĩŊJŽŌÆÉ㴍ōFMܞį 8"´+ŠĒXí6őMYŠWõX3mGˆJ”bĩĖZb+v ĸaę(k=/¯ŽĶĮ[ĪŖķ•ķÔķ­įÛÎsŦ¤UĘJu•u•mĨã5zMŲ§îŗžfÛḈ>R¨ŦŲ8ūM˙VĢ_뇭˙ļv”™sdapyš‰Å)L03°Áâí>ŲĘ]5.ɅYĩÔZ™‹‚Ú/É$3׎ Ët>ĩ“e%œŦĖ ŨYūîîŦĖnbžŠw›ŋĸCüöE<)UôRœ(…‰ÛëvÃY…a~ŨîŦÂ(ŋ†ąļžrÆZNŋŊ˛ķØū¤éļŸ{3\+Ōų–'ËŦ9eŠO?ÂRļ?Ī2bÛbŸ=˛š1Nęäøöui[÷tč†+6Y™ŨHg‘i%*KįģĨߑąx)?4ūPŊH“¸ėXs8L*NģKsK”Ž)钂öāŽĻˇÂUčdbÁ¤8Ë]%ˍČh͐ųUŠØK‹"â†%-§_$ƒŋoĒ’ f„ŅɊ Ģ$b“x,U‘‘34bž ôŨ,īcßČõ-Iš\¸ŽņõČׄI{Œu€šF€ŋŸĐučb’&eq”žH7ŖÎÚčéĮ4Qy&ʤ‰ę ×`|;‘†ņ(û`ÃÂ4ÉŌEĨ˜ËIāŊŠŨČĄīcgĸ­Ģ•mÅ>ēMČuŸ”¯ėcĒē-ūĄJėYu›´L„ŋG{ƒu%ō8åč™÷ŸĻ˙w ŊĸnŖy¤î‹Į1žkøšĐ?f@0I‘ŪŦú[ÂlŖå,ÖŠO'ˇFô°P1h„jĐ0Ĩ‹j” 2 §B¤O׎vw=ęš}LWbžŽĐ3($ۈļ¤W°?ŧ~Đ =ôč;:×[—’4НŊ)×nwAUĐLŦģ]Ānāu˙Ū†>ŽŽį{ˇĪb€ŽLčküāQũ|–nŊ*ПŊô´/ũÔ{ëeoĘ÷nßÅŪ‚uŠ~\™?ˇÜÆqÉíßû’å{Ķü×ÃvüUØáhĻšŽK  uė1íČnøw_`~ Ŋß­×ÄwËĪÅwk7ÅīŅΊ?ŖíˆßŠq—ŨSģ§ä^ĘåÄ÷Åä>ĒŅ<ĶžŨ,Ęĸ}ąÎv€´ °ūΤĶPīųžĘץ|+Öä‰ú.QļĐ/”ˇi=úž"?˜HWĻŌDn•sF:l:ΎËëEūås:W)Ex č-”ĒétŽöį‰ŋ ŌŪIäņ4u&ŨŊ+WVĶoÔíÔÄįŠCŽĪ=Ö|–eŨĻtømēYųcîŸôĄOœˇ#ū Ÿ>’|njņņ2įQoŖ )„,ē„ŒŽ: Yđ:ĩ—…ŋAęĢ(ŋ‰.˛ØčfK1ėĶ—”ĨֈļļĶICČ]ûõŋ°>>†ŽM§UĒ'ūoĄ˙÷Åãō7XCc}q0äePĻú1Ũ‚ĩ´JČ'A×ōõ#L\G0žiŸø:~7ŖmŖuZônö‚}˜ˇ1–ŗh8”mņoQv,ę Ū6Ō' ˙„īSF|/_/zųuíŖ īƒđ˙ĐŽü.ú{-­‚-Šĩ|LwiÜË&ūÁõ% âˁeĀēDš;Aqθ–.âéŌ\zŊˆâŒ¯åˇX{ˇP­|/NÎķā?|D—Hå´RžŊ;ˆ=CĻ‹x\@%ōAjŋûĪJÕFÃD9/öņ¨QiÍQÚiŽGØ\}ŸÚI3ÕĶágŠzLHCÁcĨFm-Âåņûx9ŅÆ×q/‡rU ž}M‚÷ųÎ}žŖēúĀûË˙Dūōží§ŲĮęŸ'¯|ĸĖߨrz(LĐØdéJÚl–^‡ŪEËØ ņ]k}/ŒëW–ąU@# (ËčvĐ2Џ€}Ā­ĀāŸĘēu?ÚÁĪŌī`ģ@‘7đ(đ÷d^Ođv~(Ŋ'”÷âģzÆÕJĒâĀĻønž(;E”ķa‡+âģ8äsÉÆĄš¨TˇPŠô6Ōg€¯W\-Ą•…(;…äŸëĶO?=ähôcr>@Ŋ˙ŧ҃9Åú*ãûķ˙¤˙`~—gųoρB‡>€OŽĮŸd{čTļ?ū ėšÆ‘ˆS–į픚œ'¤¯éŊæē2”Ëŧw:ÂÕÉxīyũš8ę]ĐI=HB¯$ƒCų;ĘŊãØ ë؀īĮļûc˜FČŠ^™†žŧũũ¸æĻrŠ ņČŠ9ŽÆ§Q)/ËŲ†8 ë]Ō۔Į!OAŪQ~Gš6qšĘ]œWđ‹ųIęyīų/)OÁ€Ī<˛zĶžkļ÷ē흖´%?TĻ×Ú¨øą:˙_ÖÎsĀĶĀūļ=g]ÜŸî%øQøĒwáœõ<]IÔŊŠčÛĮ‰ŽĖ‚Â|ä~¤MG¸ô_€i @ą}ģá6äŊ ŧlV˛é|͝ĖD|l‚ˇûŗžÂ?įûŪΡCüߎnAøO´ėÛ'A¯ũåŖāk…päĐâôáȟ`ß?2ø@?Ā9Rūہsš?ōįĐ˙]ú#į˙*MÜP‹đ9ŅßŪgˆ˙2MÎįĪĐŪgäü˙Mž%žGM9Āį{ŽŖĮŲį'Ī8IŠųüˇ‰/€O•Õņnø”ēđŖáË Ÿ›û&ūö>áO2ķNQPî;s˙•ûÎÜŊUœķ^D͉üœ/ú•ÜGzØViÍŧ&`÷h Ęüũ9ۓ‚ũõKø–ë9Ė‹ÎSˆ?Ŋ+6÷1ļ'ū%č ˆįb/ŗ&÷´¤mũžũūžö4ūŸî‘˙=u’‰ŊLŸgĸw~š‰|ŽŪ{ņŠŸÛģ˙Û{ųėŅ=÷é˙i<šĪ'aE•ē~ß÷K{û?˙9?÷?÷ö;zġsüDžˆ÷öK’ņŪø^ū÷u/áĪdaŊ%ŅkŨũ§Ā:­,Œŋ–\¯É>ô^ĮGכזS06IŲ*)֙įŽÂØãōũÍr„*-÷S%â‰cG؜xsbc;āK%ū}ĄËוDŲ&Í?§ĪŊõ–ûįÂ?„ĖDß×c.ž r`$l~ytŽËųˇTągdėŧüœ+ˆ‰ēžü1_đĮ(Îyįđķâ)ˆ§ĀgkÛŠŋ×įī.œ_߁]|šģã‹wkĸĖ)ânųuš;?gâ…Ę;ņûÄ;?Žį$ßŖˆģĩ›Ėw)ŲünH˙”ŋû‰ßoŪĪÍĐũØ?§éZ-õī!wņsPöt~7%ũƒ˙Û\âŲoŌ2~?Å÷+­Tė1Ž÷Č#€fe(õĒĖ÷T'Ëß î;īZņNæ[Š*÷Ķ ū.Ėļ•Žŗ>M×Y0ë ēMĪĄÛ”sčZÛÚháwČ9´īWÉ}˛ũĀŨŋË,čq§)ÆÜÛ'ũ ģZŋŗgģI>ËČfJâŨyĪū“ž ę”_īũđ}güķŪs…šĮŸqtĪī}O; TJE{‰;YėŲĒõ8D߅Œ{÷%ŲäŌũcžPŌ71{ļKĖwpĀcĻM~A-ˆųšÄīĖԕäWÖĐiküŠŖeā3‰;Æ? ]ÃûŠ$ŪÛegÉŖ¨NâŸ[žFq'ų˛x‡wš‰eĐĶ{Ä;ŗÅ]ØmĀīã4˛zī°ļŽ"ūž˛ mqˆ÷~ņ§LÜĸLú™ięfļō55([„Τ™īS” —]tt¨‰ÅüŨ›xŋ'¨ÕĮûš$ÆČīæļAo!ųvq?xr˛ŦĨ†ĻéĢĄ¯Bw.AģõĐ6__˙p Æ=ŧ—Đ*é_TÉÁފŋ-)Ŗ#2QĨĪģ_ūNØ|¯ļ¸ãáīļ>įåDŲÄģ\ŽūŌV–gž'L†Ķa‘öœ‰ĪMÜĶ(˙đ­tßĸ~ ũúgĸO˛ēÚ ā9̈́ äōw€ĘIÕw1Ļ7ĀËiyo ĶÂŪ0ĶŗzéœŽî ¤ū~üXšëĮĨõŌ‹ūúņcõ†z顟č_Co Ŋá?čĮÉš 7^đũ˜ØHŸØģ°OáŒúß?ąWŸĘ˙Ŋã@ Œso|žÆ,7˙øO<`ĸāgiėĮņO€[ÉĮĀۊg˜˙ĄŲNü,„?=>ŅįíN´-`ļģĮėë# ŋëį}GÛąˇí‰ļŅØŽ„ŋev">Ōl÷ŪDŋc>Đ%f{”Ŗāģ÷âX~qØņn>ļŠĮĀûÃŲ?~GÂgŠuš˛ŧ3Ņn7ΉņL ĖĖ_uĖ.Đ38'ž{hã{ĩE"â”ÛZas§Į^užųÎúFnī4ôFŠĻ\ >ę°qŋÛpqž„ŨįÉŋŠ÷ęs °ŧŒø?PĮfčĄ vķ2*ámˆ÷2‹ų÷,ņ;šĪ!ŋL“9„¯Ņ%öęZž؎ŖfmúôeĄūlũO´N;•_"ö<ģ>ņ3āwđwd:kšÖé¯"_ĻzėWc’éÉŗ­vy<Ž {’ZSŗū<Ō/ŖšE!Ūž>„øÎ°dÛGßŨßĮĪšbŪšî\™Ā‘0pĸč3ú š šün@ČäYœÉe Ū9Ŋ|FN͇uu„Jt+ü‹‡i•Uĸ›ôYâė>RŲD‘ß ”ņ÷OÚ_h€z)e$ĪîÚëë/1‡&åw#Éûõ6ēYyuŨFAņ^Ëŧ8J“uđ÷mĶ-ü[‰Ū~MŌ:ęߘwGÛ0ĮÃ)ß;{Œ_ĐūFâNa;Äߍņ÷xÂīčE“}âīņøģ4ÔsĒđŋFŅŠú…Ø[ī§zí)ŖŽŸ~ądQPŋ›2šĻ΃nrßáŠLŊ…°Æãc0OĪ‚žDÍõ=Ã\sî3mĮ”DēX›HëžŲL?ø5° ‘ĪķâËáîOõ‹ŧ_'Ęws[ÅßÁIüŽÆDwâŪFŦŲ¯y¸‡œo>ũ÷iōŨũuĻßúĶôŋx‡Æ×0˙ĻęŪņ÷ĻüũđČdëķ/ p_.ū§¤Ũ›&Ū÷ –ŋ÷˙ؤošôyŽkÜ×ëM{ŋōcßŗü¸kŽŗ$ũîw/ŊiķŅīr~†*=ž“ų!ú_Ŋģīëa§’ôûߘwrGŠé—÷ūįŊ~ÚßM?֕¸W¤+Ŏ9?Ŗßp}˙Lûŧ'…ø7=?­Ęõ#ŌˆĻ?pŒŠo ~ÚÕāģûK ū™%Г’‡}^Ÿ@üaā)āīĀ'@Đ%ŗøgâ_nŪ˙LŲГbīØ Î'?ô}Ũí6´{ڙŽö`ÅõgŅ_XņMÃO>;騆-§‰1~Î÷ŸğĐŧËhį đ|ƒvžôsޤܓrLĘc{_ĖW˛ĪÉöÍz˙§ķˆ:×ũ~|^âŸsüoû§úŽ>˙3đ&c-ŊažKŪH|ģ‡:žÛįĢDŋßÃũ¤ġ4[㟙ør}øˇQ&ž0ŋYú€ë– ā@;&ĐNo=øÜ„O|īÖŪ‹ŋǟ“¯ūí÷Ĩ~H>úÜøŸĄƒoę7€ū 3ķ’eF+Ŗ¤zÛFøTéĀęø.ĩ6ž‹}Hũ”iüûĄ¸ĀLÆøyčĶÂjOnį÷¯ßû. ųžÜ¤ę3TĨ^C•š—.Q3ŠgZ-ûp•Āūđ'ÎD1~ļ›Īŋ'æßËû÷Ūæũ¸k{i ū9ōÉüæXPi˙+ŧītąwŠoé÷Ūļ%<2ņũ4ÖZŌĪÕ[éJũø’wĐÉĻ-šmŪuĨō}á*q'TJŖßPÅAc÷ƒōõ0ļáčŨ+§ü›6Ž[Ļ/ČËß'{ų7Wņaü]…|˙^KđŽÆËLž€zoīņūéaŽŪßlü˙ũ~Ģ÷ûŠ{_ôsßfüܡߋ˙‡īTzģņsßrü\ü{ī`~æ}™ōNüqîCs;Ēįōp|đ{ačry¯C—ōPîRéÖėØžĨ€y'štė—GųZčßĘD}đĪ Å+ŋ›oëņ÷ÖÂwĮũRyWü)nįÄwˆ$î,Į÷¸Ģ­=zO;Š& [ ›jŪÕ^ÉĪiÂŲ4ng¸ b‡Čˇ3â^r ây ģÄÃŌšX W!܈üņ ;Åm< <ŗöMÂf ›Ém_‡°W˛œŠøG&`ƒ¤ €üģD?¤øģšØ5 đ='ļ‰īMÂvJ‰zÅ=$ÂæßGá{˙n}6ĘÕūœŋdú—IķŠŪņŸķ QæŲžčôÎ[ü[ø O“7ņw^Ės—ųm´6XœW„ŨÁ\f÷¸˜wÎ1_Ģ)6%û{į™ōšMžé!§?˜ß–$éŦÄ>Íåøž°•ŒZD°qĻŪi¯áį;~vؚxažũ’g9¯Š[%Û ØWņīüÍũ~ßbâ7â’géVņ-ķŗü˙›ŠíGš ŗ_û€.`/đęwŅũķwōŅķĐNū¯mÄnҞ‘ūi–“‘ūwŌ„NäĐt~/ÎÁ˙^_Ô˛iĮÃæ7ŋø_Æ?˙w •˜xīģĪîįāMa˙°ä|ÖDvČ×qî×e ¤`>ÜQĸ´= x6eŪ ø°'ûūB” ?Ģ(ģ•(į”r ĸ Ōķ\Ez›¨0–@ņ¯ûЇ>ôĄ}čCúЇ>ôĄ}čCúЇ>ôĄ}čCúЇ>ôĄ}čCúЇ>ôĄ}čCúЇ>ôĄ}čCúЇ>ü_Æ˙G+úŒĒéjŌH"7•ķŋŲĢWKOJŌöi+jō}ô €L<ƒĀf@&Cž¯CwV iAÛŊáʝņ.F ée×UŽØ#oŖY4ÉÛÚ§ķämF]Ĩ ƒG&hų AÛ-‰lŨS¨Í[9 QŠš\ l4thŊÄYŪ"ßŲ^@ wŖĸ”Z|7†gāų"dôūnŒånúÔLQĐĢģ:ŦŪü]‚+[ž \)xēĀƒĀ‹€J ņÜÄĄ;‘w'Iōōíî€ģÖ&ßNËIž™R˙7ģän!››:RŌ+Zˇ|=5Eå ÔH¨vØ6„â íeƒ„:lŽJ7ƝE§×ĸ#kŅäf<™ˆ/ŋļ#ŨËĢŋ´=%UđũĒŊ"’t¸ũ•ÂųÄäšōŲĸ€ŧ ´čé š §ÉsČ)úit¤¸+W Ŋ¯‘3¨Ųĩ˛—*Aëä,ĘŖļģí,m/é_‰‘ũĸHŠė¤¨EÖÛ+ÁŨ˛!„ŋĒÃjįũ[ÕîΨ|Tž\ÖɃR+PĘHyTļafmb$Ķ:ŦÎĘõĩy†9 b   R>[Ttv;*ĒM•ĮĘ9äEŪYr.e€ÖËũŊWžƒęAoë(Ę t햯\×đJŅü¨„jępē*ģj­ō(äFåĢ0W‰Æ×w ¯¤Ú"š„* 2^ŽĐrĄôkZƒY[ƒ™Zƒ™ZƒN­ö‘ŧ9ĢQĻ\žÚäķh=° aŽVíčN((ŠÜ)gĘ~ÆŊĸdHÍę°ēxĪüíiéĸ˜ŋÃáĒŦyT^ =_Œ: yI‡Ī_špˇÜ_ e@‡?›3´ĩC]•}‰ŠŖ—OÉŖrÁ“+÷kĪDkˆsE“ž“ör!I/IûøtK/"Îéķ&}Á¤JĐx—´7ą(¤ŋpēŋ6Gz•Í’Ū¤MIŌnéIĒÃëR'ī…ôš´“j@_E|čNĐÁ ģÚķž tJ čû-íN/Ŧôd{¸Ü  ̀/Û ¤y+k Ĩ'¤Į)Uü´ôqŠ‹ōAõƒvIKčЇ¤!4t‡IŸ’öp—‘Ļá í.Ū…hģÎɃí'´S"ÖXØ#= mŖ,ŊŋŊ( Š[:Š )ģQ“î––´įŌjmŌŦ‰}B›éUN)Mēŗ}¯d}ûž``§´^Zoø‡…F™q\QXQVq, –‡ī ÖēĨĢ`@6IXŋŌZ<‡QP‚ö°^ZŨŽ ‹ÖvcL|\­ĀsŗĩâŲ&B„§ûhî!Ē‘.§I€„:–ˁĀŤāy!đ+ā×ĀE"e °8Ö¤ māhG›āhG8ÚĀŅ&8ÚDëKÎŅ ŽVp´‚ŖUp´‚Ŗ­āhŧŋ­āhāhG#8G#8ÁŅŽFÁŅŽFp4 8 p‚džā0Āa€Ãā¨G8*G8*ĀQŽ ÁQŽ pTŽ 8‚ā‚#(8‚ā‚#Ž ā‚#Ž āpƒÃ 78܂à 78Üāp ˇ˜ŸĨįØŽũāØŽũ‚c?8öƒc?8ö ŽũāØŽũŌyÛåŊĩŋË^°ėË^Á˛,{Á˛,{Ë^°ėË^sčK„0$¨Í2`9°āŧ]āíoxģo—P¯Ĩį‚# Ž(8ĸ‚# Ž(8ĸ∠Ž(8ĸ∠ŽÍ⨠ŽÍāØ,86ƒc386ƒcŗāØ,w)Ā9ūsĨü§Fē˜5Y°×J+XŠ ËéA—ŅĢ‚^DÛũ5Ũ#č¯čA/¤a‚žGE‚ĸ>A—PĀÂÚÃRjŊ0“€YĀB`đ đ ‹Đ‹Ā[@\bä+)ú$}“ū ū˜Ž>¨ī×Ĩm’ļI{P{LSÔökR°6[r ; ĶBW‹įrØ8€ėûäžöĀn-íHK{ņ@“Û‹_Ô:Ųt (œušI§bܜNiĖ@ąÉíRp{q/Ũ "ˇ”5ŅĐB“Ģ ŅR¨=0$ŋ=PÅK[¨˜O<͍LtO8•;ĐĄOw˛&…öĀÁĀĩOĀū1 õx-ØŠ€ŧXØÉfļĀž˛ÛQ¸6Đ^kãåą?l7i”͇÷Ž܂ēXáÁ›W•uZ|%úŊZ4ҏ$Ø)m3Ō+%e‹'fĻZ ‘Ū8%°‡w“šY“´íá@#*QļŽ/ė]Ŧ\0ŁĒā._ž¨wXŲ.ĒL´>ōí_ØÉu|ú°N–jô×éëõ“õŅúH=¤įëũô\ŨcIŗ¸-.‹ÃbŗX,šEąH˛xø˙`æ˙'ŽG˙5ŽĻđ§"Ân‰?Åŋ胭ŠY$:ĸérƒÔ0u4kˆvN §Ŗ‡§†:™mōˍÍĸi Ô0mttx¸ĄSO‰ 7DõƓ›ļ3vU3RŖŌĒNF͚:Yœ']žMƒLēüĘėÄXæåW67“ß{nŋ&mTjU}ŨŽũ Fô˙V˙?6 endstream endobj 26 0 obj <> endobj 27 0 obj <>/W [3[277]20[556]38[722]40[666]44[277]47[610 833 722]53[722 666 610]68[556]70[556 610 556]74[610 610 277]78[556 277 889 610 610 610]85[389 556 333 610 556 777]92[556]]/Type/Font/Subtype/CIDFontType2/FontDescriptor 26 0 R/DW 1000/CIDToGIDMap/Identity>> endobj 28 0 obj <>stream xœ]“Íkã0Åīū+tė˛骁0—–Bģ-›´ôęČr0ldŖ8‡ü÷+ĪËNĄ˙@O{Ūķ¨|Ú=ī⸨ō-M~5ŒąOá2]“ęNc,ęFõŖ_î+Ļ?wsQæâũí˛„ķ.SąŨĒōOŪŧ,éĻ‡ĪŸÕĸ|M}Hc> endobj 29 0 obj <>stream xœíŊw`”Uö?|î}ÚˤMę$¤Ė$“„ HHD2"z I :ˆHGŠ"`Q–ŞРMġɀôĢ*ĘÚײēbÁļĢĢ™į÷šĪ3BÖ˛ûžûg2|žs{9÷ÜsÎŊĪ$#"+-#‰ėĖs]öqN1Rî#R'Κ2cÄŅE˙ ŧ<åĒE“Ī<ú¯'ˆÂ^"JŪ9uŌ¸‰īėmFÔ~ęMEBäåߍxæÔķN[×~!⧁÷¯š9a ŋ÷Dƒ}ˆŸ1náŦˆLéeĸkūˆōŽYs&Íę;§Õ´…Öą4—Ķ6„gOōYT.8hũuf’~švĶu,‚TŠĄ“tŠjh~‹Ĩ0JĸršC‡ĨŌ_ô¯Xģš8%S ŖĮ¤¯č “ųeJĸ2WīH 8û<â0îhЧbęGƒh Æô ÆúŊÍr”rũo”N^ŽžŅí´•^`wđI|>ß&PFęŌŅ Z˛P6UŅ4”šK×П0ķĖÆbŲĶė#)Qž/đMāG}fŪž Š7UŌ|ĖæYz‘Ūĸč6’Mæ>Bš%+ō=A?€1§RWęĪ@It-]Žm¤Ŋ|Ģt[āŲĀ?‰A&$ęˆQSĖŋŧ:EeŅ,‰eąöŦ/ÎĻąÍė'Žņžœoã˙”)Ÿ"iĢtPzWú›ôĩÜW^(Ŧ†é9zĩ>U_¨oŌę€§NĘĄhs ]Aã0Ģkh9Ũ@̰Z÷áŗ‘6ŅvzŒüt˜ŽĐkô7ú€žĄ˛H֕õdĨl2ģŠ-d{ØAvˆŊÂ^å |ßÂOInŠ}o“Iސ‡ČsåWč¸-°7đ˛ŠīĶë_ęMāĻ<ĪG;R-MBĪ7Ņ:ē=îĸGɇĪz›ŪĄĪĀ9+>vĮ,“u`Y>+bCØPVĪϰyl[ÁngkŲ=ė>æcû1š§Øsė¯ėSv}΀Í<ŒGq'Īāyŧ#īÄņ)|%_Ëwķƒü |Nķ×ųū6˙ˆÍ”ĸĨ8|2¤lНÔ_#͔J‹¤ĨŌ.đķEé}YÆúEÉ9rž|Ŗŧ]~T~EūBūQ SnWÖ+w+)ФÚÕËÔ!ęTuƒęWßŌ$m¨6Y[Ē]¯­ĐŗÅmŲMû°;öbĻ-~øz€^cOŅ{l‡Įwą!üAv'‹”iēt/ûŗRMˇđRîcy‚ô-[ĀPŧôûŽžŖĮ¸ĖĪ0ü ÛLO`'ŨƧķ…r%?$7ąyō̞ÄĪŌū•čG“Do ˆØ Ö Ą)4ƒîįqô"߆U˜MĪĐũĒ•¯ÅēßAŲŧ/ucũÄÚđķôvG4+Ŗ+ąOšØVe€-–>åáTÚøßXOeMVí´œíფŲYėŧ' /Õl*/a㊉>f[ØĮ|$ ä7ĐVyŠō:{—yØ e*ää÷Ĩ~Ōd˧Ö?Ōė„S4@:AcذûOqõã3iŖô$ûŒ°kå)ŌTŒr!—Ų Ø ģiŋÔWŖ>t@:@OąŌ›ĖCĘ ŲÕlŊ^ŲÔ@ßĢ;ä=Ō^ĨHNŅ_ŧÃļŗĶúū5ë/H#SØ}röåĩØŊsĀĄ0څú÷Acė BY؏ˇC^ãĄÛŦØåUĐ\č ö vĖ āRËĄA<ƒĻķۚK#ŌÚĶÃēØÉWSöWy'ôÃoīŪ˛^—•öėQŌŊ¸[aA×.ķ;uĖķävČiŸ•éÎHw9ĶRS’Û%%:âãbcĸíQ‘áa6ĢESŦ*ŖŧJwUŖË—Ũ蓺Ũ}ûvq÷8$Œk‘Đčs!ŠęŌ2>WŖQĖuiI/JNnUŌk–ô6—dvW)•vĖsUē]žSn—ŸÕ­ExM…ģÎå;g„a9ۈD ’žŽŽĘÄŠ.ktUúĒL]]ŲXöö†ŲĘŨå“lķh¯- Á0„|÷ŦŊĖŅ‹î¨ėą—“%ŖōĩsWTú’Üb>)ĢrÜDߐĄĩ•Éééuķ|Ŧ|‚{ŧÜ}|QŖ•ŨøÔrŸftãš&ĻCˇēöæ[}›ßNã=áŨĮŠõIãęDŅô[ás,>›x1ŠÆcĘkWļĖM–VW&Ns‰čęÕ+]žÍCk[æĻ‹g]Ú@]žUÕ¸ē ]ß&¸˜˜ˆá‹Š˜“šäŽ)Wē|Vw÷ÔÕW6bAÚ­öҰEéûÚĩķÖß§v•ŽÕ#jŨ鞲dwŨ¸Š”Ŋq´zØĸũI^WŌĨ9ķöÚŖMn Â#Z&5į!Ŗ¸Ukf'#r÷ƒø\\I­é.“ēĶę ŨQ ?u ĩ|ą Ķ|ÖōÆÕö"]Ô÷)Yvˇkõ÷„ewŸûōŌ”qÁ5Ëū=‰ ŽfC~(ėķx|ššB.´r,$ÆØËˆw똡Ā΋ܺė.°†ÔĸZ]|đ<=]Ŧę­~/Gġlh­wŅøä}äÍ÷ÔųxŖČ9ʉ)r–…ršĢ7ē!žH¸kņ>Kvķŋ({BlåÔ>–đŲ“ĖüęáîęĄõĩŽĘՍAŪV¸$fæwoÎ †|ąåĩR2†x˛däBĮ4‘ÚpŸœ…Ē!Éũšĸh¤0W•ĪŪØ×|ÖŲŌĶ˙ÃJ~ũ‚¨e‹Õ‚Ãôõđ\īyIü’á…¯–0`9›W¨_ŊÚvI^ôÎęÕUnWÕęÆÕãüú˛ņn—ŨŊú0ßΡ¯žUŲZQŋ~äÖd_Õmu˜ÄTÖŌĘŠĪ^7[5t¯—­^_{Ø'vՈÚ}œņōÆ>u{3‘W{ØEä5RysLjšDŒĒ$}ˇYɇŊDˌ\ŲH0âüŒŒ4K(Ņ?7ĶėF~: #.–_ÁÖJŖū{9{œu‚Ģņâ}¤Č~Öé€D6M2J˛¨ŠČį$ąōũÖŅO%zė˙(m*d˙Žt`S)•!l˙.ĶŖĶŖŗđ`$ĶĪ.éØĪ^…~"—| fœ •)¯Q$yŖ6F>É5;ńâ–4?û~?üüėģƒ<)jÆâDšøŨ9;ūŖ˛˛.YãŅ…1ÅEÅ*ņx{,ëŋņú5OŽ™øiÁÜĀPVËĻÃxiՙë=ũß <¸Ÿ˜ž>0”ŊVyŖ§ŲØúČÍ´9RŠ´[b.í×>H8`ų\âíė~6|o_1ˆ4 <'†ŅD‹‰ŽãšĒš‹(Ļ›]bwŪũŠQOŦ™ĮÔųs•×;w~h<ĩęĖRö<;øëĪÆažDIąJ˛ņJ†.÷Ë=>3ŋÃĪué\P\ -đœĘ;u +D3Cĩ7”7črEī{ËätydLz\VÜČŧT5w`ÉĀūĨ蘨X{äÁDQûčîÔ-Šļî ļ qž„=žA߁5 į EMg›ÎR™X¯˛˛˛sebéVFvō(×؟ĨčG ‹Ž)4Ļde¤ũŲgKg—ÎÔžT՝‘Ũ­0ύ k‚Ŗ¸@Rãã ēÅt+äđ0d#¸2ąøĒėÎČĖlŌÅ1”ŪU†Īa×DũöŲLTęÅģfģ3"š|Kī†Öíœļõ›9Ŗî/ÉØŋ6­Cjˇš97í ËnSŽØĸGޝ[äīÜ~ã´Eī-’úŽZŅĢžÃOë­öK+§LĮ~ŽîWÃčYīeŪAęÁŊÚxŽØ$ÎËUļD„ËVkĒ’nąhIrzt˛ÍšžFíR™ęįc)Šl•žáaoĒUFEë#›n2pzkLhoæâb‘|Ö`æw gCL4ž—ōPė°oĨąCX¨Sư9Ãũķ–FНûg˙•ŌŊnåȃîøĄeGcæ_3˙Á;**Ûɜ°ÄÉ*FmąŲI’ņ ‚ŗ—āĄËiŠ-NQlšĒĻY-q(Ē*Â}—”ˆp›U’™į0–cŅČĸøYą7"bcĖnr ę0+†F3n`ƒėcfĨX ´ÔÜúŽËʁĻ6XŲ)ŅcUDTŗ”ZJ5ģĨt¯ĘËGÔzÃÔÄđčBĢ ɯŸöF†Elj19…Vņ@Ōûû@ÉcüÔ ūĐ8䜘ÄBæŠ+`KgĖ͘4šu}vŊ”ÂâšFąoßÔNMTŽ4õâĪ4íúų^ūIāû@ĻāÚ0œëē‚k Í÷&rI ˛FĖY–ŌsLú˜ÃŪdsđãŌåWÍå?Ė:ĐīHtåĀæu÷„VēŸ‰%†ÁpŧĮŠūëË ÔëŸĘEr/jOŨčiīĐŅY–-+Ėž•×–E͡”XFĨOI— ķrÃäüœė)ОŌÜ9)6ÂÖĩ]ŽĮ“g‹ˆŗŲ"2æël§eÛē:Ĩ0GmTKđŗ˙ķĻåģÔėĸ(WÕÚŨŗÜÜ­§yŖc )͞63MJ{‚/Äq0Oa?nđ üGtŧ0ĪMÁtAÛ75œē>2¸ũKšõŊŖÄXŖ† šīVXTT\”Y ĐzZÕÚA닓Ŗ&ԞÝĢĒZ$7,@Q‘dŋbĪ„õ†Ū<î26˛|§˛EsÖĨęūíáįæÖ&õLI8uYö¨É÷¯č3m\ũŽÆ‡V?˛˛î–á1ᑩũģ”evÔ`ŋįUŗFÎ ü°tp×+ ŲĮQvk¤įŠ’ãĮ>,x\÷ÅÚĮ’›~öNnኌ.L9}pÖüøÕŅEŽV;Dįg•e]_?9^]œÎ¤˜¸øŒXa–¤”LIuÆrîfí Ą!)ĶéTĩØ˛%:ŖÂŦޘ˛F)ų)e)ƒSΧ())~vÔNVÎc …ëgEŪ$kgbųTFƒi,<(ĒÉ4ĢfR+К‘ú@HTP+áÅfŪlė„ũŗRXą§ĄŽĢ#6bĶŲ‹ŌÖŦŸWF>+~Ll¨éI,V"RöT‹ÕŒ5ˆ1 ĒĒŠŦëÁ5ˇ Øp|ȂĨ7^6msĮÜlŸą›&/;~[ql­ī÷~īõ5Ÿo›?sÎIv cÕí7ąv×ÜüĮģ6ÎĮ>› ^'@ž“é6¯­Dš7%ų.UNĖöŽĪvk4“<Í~­u‘ũ‹ĸÆ%Äu°–ŗZ^kQŖ2#‡‡ąĖÎÔHka8IŽq†iIN9Œj]Ŧ3ãėBd‚KËN‰ĒĨH{$ŦNí^ōā„´Â!1ÅõtmÃYø+†?×āÁôAɌÉĶײÅtƒ(ĨŦøiķ#YÍØö]'öąšWĖØŖxāĐë˜zÍ ÷^žîĄ9/Ã#š§H–īĩ=(=/}"}/ÉVŋ~Ė; ŋ{á`ë2ëiĢä´æ[7YĩĩęVL™IĒa–r¸Ļše'R&ŊŠ*Ē–#Û`c5íjŲj‡‹ÁŦ1˛,LDƒËäĶ2—ŊaQ…ō| ”Ŗė¨éÕB9zf{ĀXhƒ˛w`§2Ŗšĩ,ģLööĘ2bûĢŗÍÔČŪéHËÁ#ÆmfĨv6iJžIÁĸÖ8Q4ĩŊۗ”^æiųS'ĖzCKo¸=eÂŧĶš Ę‹Š)ĨprL—xļg6ÃiÅÂQŠôđ*Ū;øž|îÔŠŸbåėŸū*<’*(¸Ģ…Į&&›~ÁÛ!Ė^čRŧĘhųJųVųnųOŠf•Y”ä–°}dûŪĻLŗŪ(ŨĨž’ä •cĩÂ÷f‘e4(̞Y"H‹“Āā0nĢ…uĄˆpąëížŌ&Oа †Õ.(QO˛wĖqŗķÆeU‘d üGw ˙ąĀô­ĢÍâÖÔ8MSÅí¯,Šá**amY5üG›ÕĪü^Mž¯0åÁc#¤1ÃĨŧhkÂ1šÛāX‚¯PY†{9°Éˆ4ĩ~ŅĶ4ØŨlØ:]ô/[:šÁ%šénZJëÄ֚ŊeØ>ø`Ė-iR+8đˇŸôd1ĪŋūNåČĪsŲyM“yúSbuŠ ųІäŧ#6XYϜV$ s*ųĘ&åQå¨ĸ+Iwŗ#IŧÃyŧšŦ1Ļ`ĶQz…ø2:Íä ƒĪ5U6-ūؐP ™žcŠ4ycĘ($Ō$DڈEĻ#Q&!Ę"iząI!Âa"l¤B„)¸=Ô[dēc.•ëfÁn%×-œVČņá¸Á+ŧ Ÿĩ œ)'R—A—į˛~ŪËĒĸYž×j+ܔ÷¸ûhŪ+Ž“îO¸zã÷ž„=æ=îP+#k,##GÅLŽŧ>nt†%#˛›Ĩ ˛Ęĸ扁ްJr9ĪÍœd.{É<øKÄRĶŌÜNWœK$¸˜Ķ銊‰qĮÆÅʼn„8—åT“œááP49LÍuĻ g,ĪĪ^õFÄEYcjãėkåđģĻ{#œŠö´Z(y§Ũɝ"ÅIܞ[{‰,z°0.ģ3Î+TŽyž3aJĻ)˜f°ečYf?+؆Ÿ f°Ã}†HBŸG…ķŌ 8EŠÂĶ ü“†‚ØÍpĖA÷L“ÜąAÛĢ^.ī`Ö¤M nŠēÂM79î8ø‡Ø>ĨCw6¸¯:x§Ŋwá‡ŽĖ˜&g?:ģfÚ',ĶevĶūTMVaéøMۛšøŠ~ÎBīøGˇlXË č ŦĨƒÎ{‡djŨ4îæ™–"^eŠáŖÂ'ķE–…ŅGĩ<ũ’ådt¤”ā€r¸ÃaŦ•×^2ËX+kx¸;ÂgGÂ;‹ˆ°ĮÂ7–ü,āæœŠ9ᎈXS˛GØĄí ¯ĩƒx#Ę"˜ŨP3#äˆĮųuđ§8;˛ĪQËüėˆ7ļÅÚ$’#"h Äb <Ûtd6Œ˛Ũā9ÔDY)%ŲĪÂŪB‚ƒG(ČqĄ„ClUš9~ ¯5ŠâŊmí§ˇ|}ģ•×Ä÷Ģŧõ­‚)röáo›ßķúĻëøãķģõ9ņm z"ŧĀaā^$šháaІ3^L˛8Z´ˇ˛ÆŒY\U’ããŌ¤ē¸úøš´įĖøF§ZްyöqKÚ-N; ))NYƒŖå"oĮüBĘNOr‘f×fi’67#{R ~áŅ™J Žfk/6§Á 'ŽØŧ +. zŨõũą/ū8×ĩ/N?¸vf9ã+ãëŽyÛėnl=+~iį…—žÛyå3ëîŧ7ŋqÉåF¯Ũ4ôžW ūô/Ķ䞘_4ĨĶŋŧ•ÎųЍúøéQJønÎJy`Tŋx%Kîå‰/–KŖģVl(&Ÿ"8P—¸-Jŧ…ŨE?¤ĢI‰ŲáŨY_6Å>5Qĩ¤ŗ˜h.Ĩ:xttP}Ú푩Ns';œ‘aŅ9iuĩŖvcÛņv~žîÍ')ktt-YWšÕ.Ūp °RFŽP§”a{ąW ™XÃ%.ä ]h=gnÕāá)ē$ŋÅŲ R!1ü1œi`6BĸÂÅ)ĸŊtÁBX<+xĐŲ°~ØŨ'ŽŪŧ­æč´…{Ŗ“æTßwlycå‚I}Ķ”'˙8ŽúŨ—wÎīôLĶQŠß5zac­\ßoŨĢæ.”FƒĪQôw‰UēŲēŪ˛Î*Ģ ;,ĮåĪäIj6Ī‘ģŗ"Ū—-bˇ0-2ŠKa<**Č=+OaAöE™F( ‡$o¤ŊĐ¸ÆˆbŖÎâHOš‘fÁ]ĀŽ2÷—D5vÃ&Ŋx˜•6_oØŋi0›âŪå0ŒI\zĐÔD:`M"LĶŅaĐ}iAëRg^ˆØ=Y2+†č†Øâ2„8´šqÔĀÜÜqØÆaEƒûįw{ĸ¤^Î~kɂö;3^œ ÔÛ<ûNŋō蛃ašQ8wųõ7÷G—Iī{“Øq_ú}ŌiqŌaÂå0!.qÜMĨ*¸Iē5q[؎šJZļ*LĘ ĪLĪpw—]áaR*,¨Ė™ Ãb)“ą휱šâė–ęōڙ}ˇ1+Ģu‰3=à $Îkī(na.X\”eĪâYĮĸ3;R‚=ŋŸĀžîTķ´šĨg{~×ĐtļÁ9į Îf7Ÿ]ÅŅ5Z\Ē™W+d Œ#ŧ';\¸˙ân%ŗŊ8vÃ<ŅÆC0ÅĮ¸‚ÍhŸ=ō`įå5 ff>Č)¯8qāğåŊō˛ųWLí˜vŨéĸšqĮWú—/gĶÃ]]ÕØ;?7wIR‡™}—8|WxãŦšŽ]ŗÛÕŋfđŨŖGÆ—ī älhé€w~’%Éē!ė1í1Û'ņ%jđ ­7†ßœ¸AÛ`Û%=¤ZÚۊh lķÂį'Ēy,ß^Ũ/ZŽOJ„ņHHŠK€­¸ėHHÆCąÄY:ÃxX˜ĸXȒ”`ĩā åäaI‰6Ĩ]NB’Eą;j„YˆJŦ-Kbö¤ÁIc“f&ÉI~~ŨūdėaQRÂ]vZy_š HųJ™Â•$‡âPÚŲē?Tƒ„P›o|fƒö*Cx˜į`AJKM ˆy—Ųā1|ËH{č"Ĩ!Ë<™Į†^cˆû.sKIGO,š+cŲÁÛcú]>`Ũ´ô„Ôƃī=xė˚Éå[ų¤Ļē‘ųĨåũ—Ö¯f/â`ŧĻ‹[ōöznlTé÷–$‹ņ5“­sž9*č‹CzúéŨĻ)ļŋi?ÁŪXQžđÔzQšíáŸŪũ×0›øæ˜Öō‹*ÖÔ`Q^Ԍšü/,^žKciZ*Wjčļ’Éüaz„?Ŧ¯—RéKy7š„ē m,č^ĸߍōëäš,t!0 hnv?÷ĢQ~ž¨+ÚhÆ\&[œ4SŠŅ˙‚ūę”ãtđųCjPK0ŽãT#ęĘDHļ†ŠS=Ō'"˙q¤Õ‚>‰x#ÂkQOGøy„Úĸáb‰āS=æxNžH×ZHãÜŖ@=ě1—Ēåû(\úŽē#oąz'}tâ]€ЃüKēCÍĸĮ!_ŖĐū=Ān´šĀ…‰4õ;í|Dí~}g‡ø$xƒ]°M[CËÁ÷ŸÅŽ@ũ3Ā›ė8ŗ„úËĐ˙BÁsąîŦĻéS´3eÆéHŸi`.…W‡ąŽ_CžĪ ­UA9s‘Ō˜ Ü6CŒ!C΂0x˙0Ž'ˇÁŗÛž|€q úN„eō ™˛iČdCČŋX+CfÍ9Ô2fėĻ žíÜ <¤îĻ%Ā.ā!”ųTė!ŗbœĄļ…l ™ QCž§ĶA´S$æ)dĒ™ŠŊG4ĢyBļBTė;!û‚r/uT* "!ŗBŪBTđÅ?öŖØÍôâ\uŒ/Ų oЌ Ŧ/ Ņ/šéZeđ{@x˛<‡ÆK7RĨüMäō)Ũą–ĶõĨbnü ēÆrŒ„ĻŒø=­čŨÚėJå}eđķ Ú:[~ƒgČoĀžėŌ?SˆTvņĨFøßhk°cfž -ķūÛô˙/āo*ģh2Ÿ+oč:æķą'´/XgĀĸHß,r-vˇe:ķk#ÉŽ}§ŠŊāĨŠ—ŠeX:9z€( é#•÷hž´†zĘ_Đ$ļ ļā ¯ÅÃÜIIĸ/ū&Ũ ڝÕBŽ.‘šÖ˛ĸ!ymM…ÎʔA…< üīTû÷Na„~6ėt´C^õĢ›åķ$í’ĪKåT?ŪB>ŋDû‰­å˛55l ô{hŸŠŊšŋЏBĮ )ô/a9Ąō­éÅúŦûäCŸĸúāŪū#°˜€ŧlŒķ}ėÛ%B—Ą¯×ÕÁ4A}ŽĻJ)4^­G_RŖZ@ɘ÷WÍ6õ ũË =í˛Ĩ‚OČ˙2dG•Îd1ôŲK4ĘĐ7/QGÎblÂ~ĒÛŠIM -X÷ŧ؇ÆœM•bäÉt§üũ3Ėã~é øty­0ōˆJĨ ú)y<Cy•îS{ŌÄæöDP‘&Ư>EŸČ˜Ŗōaķ׆ôąX{ËJũsíĖ˙iúH>„2Šô‰ō‚˜ xĐ͘SQw‹~ŊhKĢŅɟŅå0ŌŖÎĩúA~Ô´ä…!ÂhScØėŖĘŸ‘7ŪÖh”6ũÎĻO4ŌD_k°ū@įé/özė[Gš(}ŲēʐÅ+•åús’Ÿœ!;,ĮžģA?Ŗ\ :s7(ô>öáoˆīE? ˙Løëaã3é.u-R_ĄEō´HųåģQ™tûHF¸J˙4¨ˇ+%é˙¤qBžM_Æôg´žúu“Ņ_Ĩ1á§ĖĨë¤ 4Šĸ2蒡–‡!+cHüŽFÎzë-&Ä÷šôoƒh2ÁށŪÚ ųÃ%;}p^@+pŽ}y˙‡5›!/§TšF˙Xęšˆ†˙3Õ°i°Eûå Ģũt+âOČąôä#M:@>#ũrŗõøĢúųnØąRũ5yŨ'Ĩi/=*ŊŽ ™‘D=åvø_™úāû`ā ö!äŗ†îWoĻÁh˙^Q˜…ör_ũŖ^ c ĄÕ˜y5u”úS‰/ƒ./ÆÚ<ÎUđ“Äa|bŪFģ¨'ĘČ÷R1øôeŌĀĐ4á?Ā;-¨KPŦé6aÔĨĐyoB÷ÕÁg‰Ąehķ;ĸĻŪĀ!”Ģũi=ÆúéŨļ" ëØ LF:ĘčĪ"­BNÆ^1õÔ¤]‰|?ŌąÎM'īzœčįs@¤‰Ļ8ĐuĀĩĀ€*€LúĶģæxô! K‘†ö~Ū€:˙Dŧá{€ķĀ&āVÔyųy@5⠁ŠBļ˙ͯųŸĶ_ļg˙)maĮēc~ÚÚ&ũĮ4´žŋC[ÛŽĐú˙máƒļĸ&BķhaKĶf†(ščÜĐÍŊ ŖJ…^ēQčcCŠá˜zņsaC@WB~'tąĐ‡ĐÅĪC.ôAŸ]öØÆy÷ hĄoč6Ã8Cˇ }m„Cô8mnáģŒV̌2= ŸyŽ1˙ ؍Éō‡zŽđU¤éÍjœ‹Ā‹ņ!˙Cą̓m†/­wųÅę7°+ŖĀ§3b ú†}ŠĄ¨3JØ\ØõđiÎÂŪl6Α°;°ģG3ƓôgC?7ûĮē°KGƒ˙’Åų;žúIËɆ´éĸ}qžAîĻ9ĸ=tšķ˛`>qŽîūv*BÚDívČKãܸ ´žØ‹?ū‚O#|4ĩŲW Κĩlã#}°3-û ÕŗTaMŋ&šŲ?û=´÷EAzŨ/ûq-έeīŌķĮ8yŒūĻ´T?Øėkփb¯ō¸õXB}aēũڞ í„WÉæ]B…lŪ-”ö`šĀle -~”á œbpĻ)ŅÅŲĒ›) §ōyãl}m¨-ȗčÅß~Ŗū pqM^Aá+Ėĸ2`7갉Ŗ _ô× į¯ž+§ŅC>ĮXā%Ä_ŽŀÛāU"úˆ_6Åôux­îũIŒ9NC9á;Ū M§Ģ´^ĀqēJšüōaoî‚OÄŠAÉĀyô(öÚßŅFwŠå×Pú—ž…=M=€4 =ßH=ä8zŸ„ŨwZaņĄûĶīf8ŗŗGĀĪ÷€7ZÜQ="€ŧˇĖû– ¤ƒ_§+@/fÁŋ†øOAėh;-8›žIOáü|ųŊ čūŅ­˛ãMčĮƒ÷o—žŗĘ[uÍo ¤ šÕÁôv­tAû´ŌûüÂ8~­Ü¯ã×Ōŗ[éŲ˙ƒqüZģîÖ@ēû7ÆWŨH¯ū/Æņk|Îl ¤gūÆ8ĩŌĩôöBĶ _}ĐwŋWüŪ(|yũ4ÂđÃHø˜qfŖüEŠ*ŋĄžđ;ū\@™ë€Įø\á+~|xd¤§ƒÂ— <| ĪĀ÷HŸ*ō„o7 Č&‘dÖoBũ@Ÿ/đâwƒÂ Œö'ęŋ ô@>&@x‰Wæī|ĪD8Âô}?ĸĻ5nŽEä‡mc~MNÄģ`8€&tœD>Î.:|V=ˆEüs :ÆĒŋ @ü´ŠôΰOË6ėÁÉTĒĄķZ$]+¨Ąw…Î]ĄŋÖÂV0tá‡Ô Š87ë8§•Â—Ž ōFú^}N?Ģ>ŒŗČžû>Pœ¤Ÿž‚á/ŧķßs´Jų'ÚģgËaTĀßÙ}Č[ĐüawEŌJ䯤hã>ķ°áįˆ3á2ە¨û Sãā›ô¤ ­„Ęq&ŠPjõíĐÃOj*õQæPšúĨ*K¨ÜŌᡠ˙§ŸäכÔíŦÎėĪ5Ûŋ8 ā´%D-īŅãZŌ7Ņø6iļ>ˆsa;/„únq'Y~֛g‚ŒPWøōX“ŸĮŧĖđŅpž Ũ‡*9Ø…Æx&ö3ä+~MŸČĢi‘–N5ĘŊ8Ãî‡ĪrČđ!oE_Cƒ}^eøV°‘Z'úDé@>qž7ÎÔ§ČĢđ3D…ŋōK}æPޏ+0ÎëĮŸ‚xĐ?mnCÜ#ā,/î€[û5!?Ē…OaøĒÍžoh> Â~6Ī?H˙ÍßXCõâĖ/î' ßŧ5 ŽÉ¸ŸøĐā_?áhПē ôzfQS`ë>côë×Îč_¨Īęg,í ÛŠĖđ×`Ŗ•wHˇ|.žáø?ŦĶ"43™øh{-pØ\3ũug;é}HķÁC)BüjįBį5ņÍÁĀGAũq{đīĻŽû8€ķb{‹°įHėĩÅĻ> œQŲîg5ņ7!ÄߕčÜÂWĢ3ũÉV´…_/æßŠŽm¯ųOĪrb‹wEŋvwy‘ęMÛo/Ūię•ėxāû?ōŖ[SŲŧĮü´Í0i@ČpŊãÖ´õŊü¯ŨĶ˙†[:Ŗô˙Z˙Ļ-i~ßđ;4čĪ Ōî 'Á‹ âŊÜŦV~ûuŋz–ŦžôßīUƒgÃfôË[ŋ_QķžyŽųÎÎđßë÷âÃo ųŨÔ ō¯ļ  ūũ/@MŖĀ.mmކ¨qwúPī@Ŋ;h—ÅIہŖ-či‹÷{ŋ ‰Ņ `—ŧŽļG[ĐĶ~ųŊáDu#úŨˆ~;ĸŋŽīIŒ÷$ę ˙˙7ˆ9ĩÄsüPØÂßÄbš.`ąĄęė@?; úŽ@ˆī!>†øš_ķ˜CũÛũ˙ģŽōlčüßĀī­Ë˙jŪŋ5ö–€_2Eø$AzĖxw{ɘß0îôgāZõ[Đoáŗˆw“?ˆMŽB÷īŌ$ú3p-Ęžć˙&ôŗ‚qķŊGŋŠŖoėƒāûŲqâŒõKüŅĐäīZ­¨ā•xažÉÖbŊ…/q‡ĐņAŨWnĩQŖļ†ō =ú!m…Ũũ‹8§ĘOĶä ŋ76+xžÖ-˛G“Qé*^¯; t }2’æ `|iA ĸ3°ú/ŧ ,OaL7›īlpæč¤?<üP-=ūčūwāĶW5Đ;˜žÄôE[BēWŌõW•'ôû÷æĖx?7ëWtžŖ á/`üoJC(Ūx߲2Œw žāģq÷0—:cū˙/RÄûsi§‘vĩ‘ßWœ÷a[Äš?•^ žq)p&Ŋø]Ę}ŖäˇõÄ{ƒ§ânmĀ|]øEŌW8›?I}ĨhąĶĀÆ ž -Ō+´˜õŖÅ|7â÷œË3AO/?ĸL­––#ü GīIkhĢâĸĪáį^ ÜÉ_‡{’6ņĨÔųģ¤$|dq5JtÜų,~3ōuęÁჿ ˙/´ôA`??Oql-Îæ¯ĶŌĐ}Ũh!‡^’VŅÕRgĘáč¯R8Æ˛œî‘ÂáįčúßŲJŨ‡ōá(7TęŦB™+$ĢūĘÄ Ė\åøË—ŅJėü;¤ÁW÷)˙ ÁJd}ˆūŧ›ĻĀįš ĀYĻéCņ=Čȗü ŦĀb~WÄ |—øE x># {d|W‡ũ ģĖoōßĪ0e~|ŌĪĩ´L‹†ONĶ‚ß+ī‹nFŨËäõ§Ņn#-o§įÅ÷ԃ~Xš 2‡=zGčTPņÎLŦąé“éÂīëÃ_ĨLq ŪE]›}gØ Ũ¤@Æ7 ˜ß…Ņ™ļU?/…aį‘ĸ&‚‡æũ•Ķxŋ5˛ŗœŌ[´wƒ˛‚ē™÷¨úč‹ßĩŅŋ@ģâģƒ¤DŊģql~ˇFØîú÷jWšwjú~øī Ėwú‹|¸.üŽeōũŦdĨeü¯ô 0Uę@;„ŧ°ítÛŽ.ä†ŋI ;ÕĀø ĒŲXzA^æ3pĻB¨įķõ™­đedj/°“ŖbČɇ­žČë.CG˧šĀlČÍå| 0px‚<ƒx˙‚ŗŦ~;`nĀZĪ„œGAĻģ ÍΐÃ0„ŨÆwĸĖwģÆģIČNĖīŲļßķ ~Ά˙^yi3-ÁxœĪBņ^aŦä.ø^^ĩ>å4ä‡cŧČßüNTŠĄË@ _Ģ^ŋįáq­}7qį.Ö2ū†ŧK?ū4›œãuœô\ =äōŦy. Õn%7tMXP÷e ũ.ĶWAžå y…Ŧ7ûį!;äŠ;ôĶT#Ũ€3Swšižß4îK^v>q/bŧo:‰ķ‚x~’AÚąā=Č~ā Ū‰a}OoĪO[Å÷Ö_š}ÖņTƒē7ü:M›-ŠGh#daŖ´Œ^b+áß­4üŨ{x Ëō{cÆū,Ú{$øEŋy˙>‡>˜õ Øōŋ´Ú´a3”ŋū24œĩ˙{XÉl ĄęŽ0~ģ‰Č*Q[LD{‰bę‰â˛‰â'%T9v›H|‡¨ŨÉ;ÛІ6´Ą mhCÚІ6´Ą mhCÚІ6´Ą mhCÚІ6´Ą mhCÚІ6´Ą mø¯ÁÄ_ōŖo¨”ęI!Nvʧ‘D|9H2É{Gt~œ?ŒR^žs_I×ĪwîˇĮwtŸ&ĸíéz}īhžƒŽį™:ã9 H¨žcßĸüŽ}c ˛ĐĐŽË0°Ģ÷ö5Š-¤Ö&í\ ĘmÛ_šPġíīÚÌįv1ã™YčŪΡaŒįgžų@p= ŖķmûãSÍjÖ8QmëūvÉ]ŖŽō­(ąõļCÜęĩ!;f°:Xãį{ŗ/ĐÚ&ãyŊņk<ˌgžņŒ æ~.z7žGįŖÆ3ßx–ĪÁÆsĻņ4Ęŗsø|‰Īø|Î>÷ÆP#'ŗį1ģ“yķ˜×É3+ ÛWč\įgaŪâBg'Wšŗ+Pāēܙę–äöuvŌs+œÅŒÄ˙Â8YČá ĸ˜h‹×Īv ŦŒhZAV?+ۗ;ĀŲÛĘzĐYtWü ÷åÎq>…Ú.#Jäâģö9ęčg5ûœ˙rú-lŸķG§Ÿ3oŦķįYį?;ŋwöwžĖŨå<ŒRÚįô;ũ2JmÎõķ]Ū(į­ÎaÜYįBįUÎĢ]FÖUé Ū0įTĒĪ­wÖēüĸ—A.Ŗ—˝hæ1g%2+rũŒ=æô:oqt4ĒvUsvqÎqvrŨå™Ũu0Į–#ČcÎöč,ÃčĨŌ92Âa-^ûŽļv§ļv‡ļvŠļļˇļļ§ļļH[ÛM[ÛY[›¯­õhkŗ´ĩŠZœ%ÆbˇDZÂ-6‹ÅĸZd ˇ%Nü!Wøk˜qĒ]UOŲÛšxrķerfáԟ|ąR5¯Ū‡UûŽM ęņ.ß?†ģũĖ6´Ū§¸û0_L5Uč“čëîŠökú0_ą§Ú§ ]ģ—ąÛëęãĢüŒFÔúY’Hē)Yüœ‡ąĒI7­ITŋiM]%,(K,‹é]RUņ ÆāŗÅ_bOŧôŗWYtĢ\ģ_s^Ļ!:Ņĩ"ēVDS}wV¯õ=œZįë*zj]ĩoũpטÚÃlÛ]Yq˜="H]ía)íŠ&ŌĨŧŠēēj,QbŋG”Û#ĘYŪ¤2QŽĘ,oådf–så vfššrî×%åŌØ#ĸ\Ž (įxŸŌŒriŽ÷[”Û{Ä]Yą×íĩuÄ(sÄlËWjq:Q$ŨiÁVqEœŒEĒ.é,ŌŠšH'Ŗ'‰],ã4ËD¸Be"DOž˙čgR§rš•!ĩ{-Ô§Ž|ŒIėŗzë‘Ôk{ōzU|ŨSįŗšûøÂÜ}¨Ŧ,Ņøo{ÔpŸŠ$ Ĩ{Ļ'.M>"Ûi”GrD0ĢcīŽŊE¤WdEŠ˙.6˜•¸´gzōļ3˜eGr4úh1Îyķæã‡+§U4˙›ü™¤ķ¨Ú—;ŧÚW6´žv¯ĻUúŧuHëJ ĢôëĮĖÄNH,‰’Ô\°9Íj 7œĮ;Y1†PᙋĄ Ŗ–œ7×xbƒū?ØCsŖ endstream endobj 30 0 obj <> endobj 31 0 obj <>/W [3[277]11[333 333]17[277]35[1015]37[666]40[666]46[666 556]68[556 556 500]72[556]74[556]76[222]78[500 222 833 556 556 556]85[333 500]88[556]]/Type/Font/Subtype/CIDFontType2/FontDescriptor 30 0 R/DW 1000/CIDToGIDMap/Identity>> endobj 32 0 obj <>stream xœ]’MkÃ0 @īų>nėÄĩ‚.ƒöÁڍ]G)Å NzčŋŸ+m*ː~ŽlÉVū°{܅aUų[œüWÕĄ‹¸L§čQĩxBVjÕ ~ũũØĖYž‚÷įeÅqú)Ģk•ŋ§Åegus8|ŨˇYū;ŒC8&côĮg2ûĶ<ãˆaUE :ėĶVĪÍüԌ¨r ŧĘÃyFĨi^r~ęp™ą GĖę" ¨ŸŌ€ C÷oYŽjûëīęHĩ Ô[V„úžTY‚P#)Íģ īĨ-fĩĄąŦ„ĻeՃĐxRÆ€Đ•Ŧ,ooŨ†ŸEt|ĸi@č*V„Žk4œŅq^†3":ÎË t+ēĻãËą„ŽgE7ÅŦøž,•ÂŦ¸ KĨ0+.ČR)ĖĘŌK˙=éåŅ/ũ(=äO1ĻöĸĻĨ&ē´ĪPúzžæK”J_ö͊Čk endstream endobj 3 0 obj <> endobj 33 0 obj <>stream xœ”} `Õųø{oîŲ™™Ŋ¯l6Ųė&dƒ’ÁhFšTä’C‚¤åRAQ *(^hëYĢxTņ*Ě*ÕzP¨ZũiĢŌ­(?KОû˙Ū›l˙˙īŋ›}ķ͛ëÍ{ßũ}īa„‚V!™3—.I=šøāoPķBâÄK^zŲÛË[ø0ü.ŋtÁōK–Ėü!Ŋ ĄKߙ3{ÆŦũ×žFčĒz¸fā¨đՅĘaėWĖšlɲé '&Ãūz„f-¸bæ aė pū3G`Îe3–-ôučĢÚ? ÎO-\<{aæũŅ/Ãū „<ŋvĸ(übÂS(ĘgQĄÂđ;Hˇųš…ƒô8Ũ’¯āęÎ⡍čy<=^F¯āÃpÕ&´u ßĸ0īĩũ­E"š 5ˇ  ā+@ũOq´ĐjŅŖĐĸ=pî…čz´…p¤đ%Z‰nâŪ…ĢnB:*GgĄqč t;>ŋpš†>åo@ƒĐųčr´¯*L)ÜQ¸ģđúÚÁũļЍ<(†fÂwOááŋ B}áŠ{ĐčS|ˇ˛ Ųđ”UpæĪŅbô ×ĘãÂĨ…cЂ2t5´GŖŅÜErp÷Ųč Á+¸Ąp—Į í…ŨpVĩĸ9čA´7⑤L˜V]؃BđŒep×Đ´žč%ôքÅ' ‡QÕ sá}:Đīp—ī^o† —ú F8rúzíÃiükr…  [¸Ļđ  ūh´ö)¸ōsüOr=|Wr¯ņ# g#/ôË]´ˇŅoПq ×âąx2éCŽ s‹‘ OėßYh.ô÷ũp÷Opo'ŲË=Î?ËKōû ^‘,úú9ú5ÖáMSøJücü>ū+JĻ“Ÿ‘ŋp?åŸæß‘fĀ[˙]†nGĪĸbŒĮã‹đŧ¯Åwáđŧ$g‘‰d>ų–›Ã-â^âΆīūJūapĢx0?%ŋ;˙ûü?  kĐx‡ÕĐú{ĐÃđf;Đ^ô!|?EÁö`/|S¸ OÂ×Â÷z|;~ oÄOãxĘ>üü%ū˙'ž"‰“2Rß4YLŽ&?%‘ŊđŨGž&˙âÂ\9—ã¸&Ž…ģZĩ–[ßmܟųŋ—/@?î6 Ī ¯‡EMúąŒäˇO<Ū]ŨũIåoÎߛߒī(üa cĐ Ĩ¨ Z?žķ`ŧīŒÛ„ŪÅô] Wã3ņųĐ3Ķņ<ŧ/ƒžŧ?ˆÁÚūKŧ zéü-´Y' ÖæĶH9›Œ…īČl˛ˆŦ'w“ō>9ÆIœ‡3¸ W͍äZšŲÜn9w/×ÎŊÍ}Ėũ…;ʝ€oWųRžœĪō9~$?ŋŠ˜˙‚˙B˜&ŧ%|&ĒâeâąSüoi tĻ4N/ĩJwJÛĨ÷ä6ĀÎWŅ6ôęõÁûšÕÜpnēƒÔņQō;ō;Āįéh7šĻ’øfrî Â2ņtr:ƒķYčë×Č#ä(9Gá héīÜM đĪĀωâwÁģũîŧLÔđõä[QC[0"đĖßpũø÷úˆûKüŖčŧŠÃøyŠXđĻ0•qĄ_r‹đuhސz\ž đx ~øÂD<ĪGÆ âūŠn@ķÉĄC@Į7Ŗûđ,ūRtĒÃ+ĐčI Š>ÂåbĩÄošü:âĮˆđOÃÛ5â Ė t#nåŋ%ĸĢĐ^^EŸpĪAë÷’_rŖųÃÂxPĀuh ZTX– SøwđĨˆÃ“Q†ßÜm7€/ƒíJā*Ķ€§męŪ |ā,n4ÔDsÎŧ˜âAøŪ|‚ š 4~!pąßĄq"éD— ^ \!ū­ühjáIô@áRtyánÔøÁÚ ¸ãFôēmÄ7å¯E Q(į|ž0‚ėFú’uäC2Ü{ęøBogp}ß_ÂΙ‹h˙š€š ˇūØ]öt1:€ˇüžpׅęōcČæÂn!ŧī§h|áŠB)VҜÂ4íBŋ4CĘÁˇãwā}¯EŗÉ…%Üėü\č‡;Ąlč­Ģ€˙Üb4ņ,ģųĖ3šNŌ8xPC}Ũ€ūũjOë[“ĢîSU™ÍT¤ËËRĨɒD<„CÁ€ßg™†W×<Ē"KĸĀsŖšáémŠöl[;ŸMŸsN_ēŸž3zU´ĩ§ jÄŠį´§ÚØiŠSĪ´áĖK~pĻíœi÷œ‰ÍTję[“žNĩī–NuâŠã§|û°tKĒũƒG3x=ƒu€ËĘā‚ÔđȜaŠvܖŪ>béœuÃۆÁí6{ÔĄéĄŗÕž5hŗęĐP{8Ŋp3Ÿ‰@ÂÇl&HÖĄQíąô°áíŅô0ڂv.3|ÆŦöqã§ /+ké[ĶŽ‡ÎL_܎Ōgˇ9v ĘĶ.m—ØcRséÛ [S›kēÖŨÖiĸ‹ÛrÚŦôŦĶĻ´s3Zč3ŦĩfFąF˘˙@¤xԃjp܅ÛsšöęjŠ"ŌPShã™lŋĄoÍŌN’N/4S°îCã og´ Š…î/+Ŗ|k§.†öUã§8û)tq| ˛ks-í¤ér'Ņ#ĢÜ#=—ˇĨ“;UWƒírļįĪ0Cūás†´ãĐ˙åđlįø¨ éQã§NI _×VėÛQOŲsŽî9V„ÚũC§pqR„HœcG)§õœLwĻhí|ūD†Ôŗ:%°’ÕāԈvŗí§lQËĘū—uĶĢØæäeÅfļɝēú)û§4O[ĮAƒATŽš8uŨ:õ”c€jÎĪ-nãŅÄ)eŠĄíhPfū: ]ƒé¯%ŪnC— Ĩ'ū9UÅŨSNŒáøPėė[3Ũēu#ŌŠëÚÖÍč,Ŧē82ĶëvWČ+ëos§ŗ°ķÖxûˆÛZ ¯æā!@Ŋ9oŋŲÆ7O˜:e‡ ļÂͧl!˜ m;ģes›˛#…Íj ­Ĩ•t'EwĐ( /š…ČėüøĄUė(Ī*ØūĖNŒXėÖa4ŗ“8uĻ[G ŽwęlVG?”Į 8Ĩ7ö0’lé ØH0S°ģ„P™Ufe Ā tO¤¸Žļ€ŽŖß‚­Č'mÂģČDgØjĨ‘é“dĶėÄu[Ņ#^ļļ%=âũâL.ÅqÜsÖĪo‹äĖŖ­ŨG™GĄæĻæĻūũp+ÎĢ~ĐĀAuĸß ‰ņ§÷ünôÔ]Ģ—Wž‘Îá\~ü.ü=ö~ķQ÷ņ}-ëî}ņĨ|i>uĘķgÛZŠ2‰ĸšųÚõÃļ=ÂũČ Ųašdßwtč:žļ U%“ oŠ—xŸķۘƒĪÚéO#Ģž2 ßēČZ“t¯Æš\ų•×ŦŪ5uôŪüxŧ˙y׎{×M}įx÷GßäŋËËĐĘgōŸāĀ>RҘm*tęŗb'gg1×DVqR ;H,  ēã  m€Øāyô~hɑÖ#ĖCMfjĻĨyČė>„-_c˙~u uÁ€(U8hûžqhČíŲŗčÖėč茋āšgáN2\ãXcG’…GÃ#ĶˆÄ„…pB”_x{$7Æ<Đj~ŽjGęß-‚—l( žEúāÎmÛčī„b-´žC;Bhc›œ&nBü8žg­<ÚÚ ũtČiÔÎ={ö&ĄI…/xKč‚ņ)!†­šĻ=ÉĐ<žŗ'‰…ƒļE÷…Ũ—LZÆi­‡BÎY°ÄéúŲ“"- –’IË̈́"ą­Æ’ŧHęzX‹ŲØRĀŽŌÁU,¤ŅŌ4(5Z‡ja`÷@ąÚK[ß,ūûŽĀDz§ĪKđõxDzK“Ö SĶhIëznyōžb*j&퀤=ŋ!‚Ÿ~Faŋ=†ג›=7oxEōDČp˙ųÁķĸCãũĶ‚ĶĸÄįKķ=3ũ ‚ķŖmņåäjqŠįc­xŋt¯ųFä#ōžøžįFŦ§š´ĩ´'øŽƒvßÛÚIá+ģ,]ßOÁH1Â^ŒžÁē(AĪP֗Zl\,/=`É´Ōē3´Spi Q.ÂÎ@2=ą3ĐúäëˇŌq‡—nÍ‚’‚­‹XėÜēLūÁôƒá×Ō’s?(—ķ›žuB!‘˜.¯ĖúÍPŨ€–™M—Kâ¤ųīnXēeÉŲķŪ}ôŊåwíxzŊ§Ÿž~Åy­ä]Ėã3ž›ž5_ø(ŸĪŋúüũ/āŸįīûö0ØÔķž™ģ†bė§€zĮëTô‘=LuZÎJ•Ŋ!bĨęŧ+9[ˇęįķ+ɝ䙎Į Â)Ö~Se]ĄŌ^D˜rÎÂ~ÆKøĘļz%zyzÁ˜ÛQŠ<.†0l‰i@Ŋ@īåĨ÷pJ°"D=;qž 9„¸ČéGöĻŅŨ@öÍáFl5ŌūD­Ā Ų§,m‰ĸÔ¤_GŽwœõîÄûūRģ„ŋöĖĨŋųætheXBôB’Dv (ƒö€LŲ#7J+`5Œ¨5ļ2ēr¨AąL=â÷‹“tŠ3–Å€olÅ4J„$Å 0=!™¤G“ /Ijôm“äE[#j8œ*5-BRĨjßÛCË=¨–ĸHŽ™–ģPō#=Ô|>Âh+†EÜįėˇ=>?™” Đ:zī-pkįÅõ¯mÖķ˙éi”"éķčĶØÃ끧 §‹/ /‹/J¯Ëo$¤sĩmĸwž6Ë{ī˙-ž]žĪbŸÅĮ´—=/øIÜL˜%fŌU8 Ŋ¸ɰU`„cIՔEņÍD,HÄäDŒÃDŽ%8=iv’'ļŽĩ°Õ‰#Ûč Ú䭘hĒK¨ĒK¨*%TÆÍÔ+ÃīōRbÅ/’Õ(…L<ØÖŦmÍd:š‚ŦŅŋ“T R|įfFv­G@2åšĖ#Ŋ5u75ęn=`ų(Ž@ąÖ{ZÎ{šÛ‘ČĨÁÁ¨ˇ..Rb&X–4p`C=P“$@’ S@ ‹/D™Įüvã×ūø!ŧÃ˙ũīß=zÎS¯<6-ųüķg5Íėē~÷g—Ė˙ÉCëü{?üęų)Īėzâæũ)N.|·÷r¸c;aLžŗˆØÚ‹}{ĸ›^$0Ĩœ;¸OZia$UĩO0™ā“}B=­k‘(H÷”IŠ-%eŲ=áôl-ež{jéų››AF‚Á?ôšųš¯Ņܝ@tđĢ=¤××čüpëBkiœģ ´Āœ˜ēJ_XŖ¯ Ü˙…Ž )Ž!GĶŊŧ„ᚘŽ)Õ°^ÄÔķ¨ãx™ ŲIž@Q2ĮV •4S÷šCės‡Ø×Ë}WNO]‘"Š%‘Ô*É=UrO•zN•ŽĖ2ļÅ(kf ŧé‘čUŲõ}#xđ–čģx'Œt˜§‡G¯¯éÄwą#wˆáG‘Éĩö°åî”.@‰ Čâā pĀ`.xQ e/ūA!ƊRHƒz@?(‚H´DéōėäŽŌ{æ¯ÜôØuuį|ž+;×Ė›{[ ŖėĢ_.{sū%ŗ~ŧ>đũ_đ ‘Öļ˙xÅŖ‡É˛ëfūøÆSÛ^ŋtËŦ靖|鎮ü?>§XCĩ…2áI”Ä'lŋŸql+= …˜ČōPč$JŲa ų™Žāc%qÅëRį$ ų™ęācĨ'BK†“LžŠ“;†~Õ)Vâ„WMƒ ehƒį“ Ũ‹‘ÖĪT 0üĨŦ†âícčāîŨ€såúøK4X9*ļŧd]ÉŊū§ü¯jīkŒËŠ?⭎qJ?ĄŸg'°đÎôĢAŸß˙Ļ×xũ¯ĄōŲ~ÚÛģ4T¯aqąQ/<~—"&0ÛĸÍŗĻ›W˜+Í;MŪü X„ĄXŖˆ!Å"ëSž]¸øĀķÁ[ŧÛūĒ•žŠj§ [+ÕTŊX?´Zđz<°V>-'CBŒ!1^„’}R)(~zc œŋ,XÆÚĄ`@=!;éĨā ~ÜņümŪVõôäÃîÆŪxW–—Ü~äˇŨx•šîÖŨ=¸elsˆü÷sųĨĶōG˙ú][öSmt4āW¸R Ē&ÜPÆŗžą:‹@Ęí-Ų*]4J÷ Q™Ŗ­˛Ōː‰M‘Ę-ÕQPĩ“ŽÔĀĨx:æpŧ*iëX×AĻŅōd@W“eLzSVÍdؤ8fü-Ė”ÕpQŗÜķŪķ7.nĩ2wˇRÜę;?ЇIvpXtXjĒobj>7Kš%ĪķÍJ-‘¯JÜ$¯Iŧ/ŋ˛¤ØJǎP ͘…ĘØ‰¨LĨSeô€E[9N'ĐÎ8~—j”ÁšmƔũ mˇ‹2.fez0+sĨÉ0 ,1ô$xÁÃ/PMČ\_ŖÂÕļ?ɨ:Éô°$ĶĀ’¸Ņ.oO_^æÃĖŗ^ ‡čĩámi¸“TlÍõčWëz‡ĮXtSĪ詔Ŋĩā“X(–Ā’ŖĘĻ(QžæŖr/]Ž,sår8Đ šã[#5įΟ|Ö¤‹ÉYģ.íčžzߍÎøų-Ÿ˙¸{ĐØ;Æ,~âąk¯y†Ÿā×otŋ3ŋųĶĖļü?ßYwčz< ¯ĀO˙zã+'>n}ĻĨķáû7mLÜėn Ÿeö`;Å H”"6ņ\y°°€ BuÍGåĸ ¸^­Ä•įđ×ŋŸ -~;ĀØâZöė9ņ]-DáOį_ĨC[ģ“[ŠŦ r2^$SĄ’#Sˇ[܉ĮĨ?Îĸgņŗ)žÄdž‰)ˇWINedŨthŒyEkcŖÁ'3‡SiĀFÄAŒƒ šˇNä9BVoÄnÍīÎ˙z+ĐYĄ!ĄEØ īæ%%”™~ī*=':ŠŒü{Wpžâcå˛LK‘gLK.žtĖ1 e‘–<ĨļOÃĮt˙,fRnj4CLÉgĮ<"{°ĘîÃJÅËîĪ`‰ÁØk€.O(ī•zߊ{ŠFK•ĩf?ķRyŽŌfŪĖ­7ß^ģĖÃĻGZđd2Μãi7˙Žũ]˙ģWá5^įŊœGUžEB%IX5 #Dߨ`VCJŌpˆp­ Ō:.Åk¸JI ‚œ9ą“,´$k_Ú˛{ÆÛ§ĨĐl‰ģ`ŋ—˙”įÖķ˜īÄØöŒĶē¤O5nŊ†5ēoŌ^‰Ŧ”VIDú‰ņū>EáÖXÔūíļ™d^ Á›ŠíuОNĄ ĪZXR˛ ŨX˛=(<ËũBßÁuč¯ëûЁ’ŋ—X^_‰URÂU‹UVu"U:RŸ¸089:G˜_r­īV߃ÜŪņdŖõ¯PĖ ˜1đâ“-ULY^Õhķq`i\<É+fÖ8eSãXi˜0›œqš°ĘXY6%c@j*44ÚJ9šœ9~G€—Ļ h€"ŗ˜j‡¨ üj1‹|ēŧ‚4Ôû*ęđa)K™ |”Uņ¯œ‘õŗCų~ļ }åO¸æô—ë^ųÉĶvŲįk˙ !ũŋ=ūk|ų;ŸáI›÷ŋÕwÃŨåŋŊëÅü—ëvŅ^ŨöâM@ĩ zĖ>C’˛|§„% q|pÉŌC ēz‰yx…8¤Ä4-öfJŠ1TŅ,>Ęt#MâcTs#Œ,Ų0å;˜mLme•ŲĮ­ęénO8Æ/˜ž°ibrxX8”§7™ŨMũûÕY °ßFî㟑öîqÂÎįķCžīž„žËĘá]ŪA8|Üfũ@ˆPßĮÖÁg0ČÖēzgÛˇŸŗ­ęãlĶg[’tļ‘˜ã3ŠÖÍú”°^Ø$p\ øįhjG|-¨ŖãЧč0|)¨\8v:,PlîūÚíîoÜî>j›ķeŨũ˙~K¤ĮņŌ:tڔ-Ģ€Ãļļ,ZÜÔŨÃÁ¨'†ŅŖûĄũųō+”EAĪŨ ôS =' Û&€m’S”%“§ė2‰8ŨČ1äāX§p˙Oĸé`m¤5=ČP¤‘RKOƒ‹ƒ˙yĢã ôA)äŪWČ;Đēŋ?Oiâ~„DÚgra[“Ģ=ĐÂJ×葡SĘ^Ũb‚z ē}Ē(¤ųčaÁĐ8a"+/’ĸzDú“ļÚ­ŪNĪō˜ˆúN‹īöŊûn':NqÁRisW—šo_U-r9§‘ë’-•؉ŦäXÉŗR`%Ĩ[;M! 'Ō~"Ū“R[eĨä uęí˛K™%-`-Ĩúę V‡°×ƒd•ž8ŊØM^$“‘™d˛­9˜čģ-Âô]ŽÔaޘæĻ&įeZ{a beÜ^‰ˆ!H\æ—jk´ßBWjįjį\>Ŗ×x§pņKõeŪĩēė!‚ܨôŽ%Ŗ8ĐņåŅúŲ^õ~ōw¯t¯ŧ‘{J}Äđzû $ DkĄŸ (k`ÔYV@#Ōu¯×¤ãÔæ[å#žd#˜uũˇ)š÷ˇCšĸ2ĩÉQ’Ô”­­ô`ĪNxa/öĀY¤6fHX$ųŖŽŽRÆB›dō )ĄMX%呍[-ƚĸ4jŅÚéĻhÉô ؋õÚ=Đ út”Ųë *&Ķ;Ö^ĮÔ؀ĒyRŊx i…ã€Ĩų>Ķ.FĩkpŦ ŽQ.˙ũf¯Jkzéî{ÛËŊ5ez'€ƒŊ1p[_¨íÛč J č'hQ+z ĸä\†C၃ppHœÆÖũ¸_Ô/mķMx1?yS~аķøww3îg܉c#øˇŽ7đû§(u=§”JOŧĢčņT;¸ų0<.û—#Z”;Ǥ”PH/%É ũÉDâ8Yá Q$™įĨÜ•K´ä­J‰ĸār4ĄG. i‚ܰŗŒ>ZSœōŒķ´yzVyÜ#Ž5&Ž™LŌĄQ˙;šÍ˙ģXę‘ÛŊXgŽ5×ÄtËĘ!P 1Õ y6Âī 4Ģ^NAcĐŌŋ(:9ÃŲŅīŨĩ}DŖlpĀRy´‘Ē`ÛŖp@Z›f íI7JŪüüt˙Čv?€%X`‚ßoĮßuĻ3âtP S‰­‡^įČÎ×OäaĀWķ+a°W_EŖŠ {~,ŧ‡ŧ(ŽCö¨˜f ĮãÆ\ätmą[‹:¯ŖŊĩĸV`PÂIP÷IY™0uNTfĶe“>w^pwË7ų7ō7ãkw=Üz~˙ķˇ;ŊžŲÛ/{1ßŨũ‡o[9토Nũ`¯zú0‚ĘÉģĖįņbßĀÄÔŌKäËJÁ„ar’•++€đvéŽĪôđ¸tė_ļúbõ°=ŧĩŧ˛Ūĸû%•õfqkˇpüŋļ–dãpžYÜŌãöšdŧį%ÎKMđLK\–XŦ,ķ.7nRo6î͟6:ƒŪ/ d{Ę2–eX†Ļøâ¤,RE E …cŅd˜ļ¸í˛ƒtÂaTVÎĐ"1 ¯œtq#éâF˛GõOfŊ‰n,Yt‡•éüõLûiˇˆ­ŠŠ…Ģ*¸ŠōÐÐÐČ˙CÄ˙‘¯ĻOßøŸėĄ"ņEDЁ*9‹ˆ’ËuÃNc-‹9! ęqĨČsŠŲŠJŽ­ĘļŅh˜C,ßĘņ"&;ŊĀGcŅF 8­~^;Ņh–āW ŋÖŲBąą ‡Âū4wdL3Ädnŗ˛GÉēŨo_ķæģŖĢ&_8ōʤË/ė[6ęĪøŅ›îsßãų~ÂÎąŋ]ūĐû%™Š1Wåáū7Ū6Ø#u_ÅÕ Z>r‹äN+|Á˙ ,ũ~ä7;Pe1P™u#–ĖãĻPĤeeŒ•ēëšÕ\Āã `Égœ VbVÎäfōWrKx>SŲĀ5&†rįJį— /V1ĸr×"M+š°ęŋ7M‘‡Žq… d\ ë•.fÃīœėČē@%EÂĒŌŗ¤‚ĢĖ 4ęĶÃ2Ãk§Ļ&§'exæéķŊ—fG–{ŽŅ¯1Ž3¯Ē¸2ŗ†[įšE_gÜnŪTqCænũ^ãŪ`˛¨B÷-Ëúâؘ’íƒŗõ‰ųøũŗh6đŊīōø-qĪ„ôžÉĘ Î!ō?ĮŨ•ėĢ$“!Ž1÷œåkl…_qĶĘü€ĩ‡œoÜđęĄ,Q’ŒË’ČsDęŠr¨…dŧoĖύ~'đŌC!ԗ9N˜ļbâ‡ÛđBŧ‹¸ˇÛŪžô‘ôŅĐâķ×ĮŦ¸4Ēé ,ęƒûPqįõ’I}hŗKé=ûÄ”1īrŖÆ2æ]†ĀYUščÉ>— }=>8ßDJ­Ņū3/b$×:ú@Žú•‹WÎŊ2&X„hq„öĐua[¨gtŅI2ÃŊwŅų%IC•ŲŠĘlļĄŪ‰¨=9Á@8ćQ‰ ;ŗĶ^Ч˙öē+ž™0nÚéųãį^zũw?}ü_k„ÆķOˇ?Ú88eÕ5kŽ˙üõüßĀ˜—ß~áŲW~i:<#7čņŲWüzÖܡW{oŊcõEcëęæWžméU{¯\ō%ĨŦ~ Gw2í'vTdüKbĨČ,|é˛ķEfáK˙ÁΡ($$Œ>b‰ĮJ'šrkĘņIž Ļ0Šå0đ6\ô‰´=Œ?ĘEæøkîūÅå’'\ޘwĖ,zGyûŊ-_-P2´~n˛ ŠæĸĢŖįSf•5P?ņįKøuų¸ ?˙üąŋSĪ:hrÔGš­f)üų ™uŊEõüéōū_0ŽD@ŋoÚ* HŠn5ŸEˇöÔ Ŧ¤@XhŒQDåd0#>MQ’A€>K3Œ”iLĶō)š †eŠB“.b†ĸČ26E|>ËBr,Ž™g)xÆ˛ûŧÂA>ÁŸ‰ĒĐ ˛ŅŽQtĨ:ĒĮĒûčÕՍúĀā øęsĢ[õÖęyúÜęļ~ëô5} ũ,ö´Ŧrƒ%•,K’BOFŸŠÚ}ąjwtoÕ;ÁĢäa!œ¤¤hQnâķ 5P66–BĨáŌHŽĻēž‘oŦ9—?§f˛Ü’ģDž›[Ē­ÕŪĐūĨ˙+g Ē÷bŪŦ­¨( DĻ÷šĸ铨õ6{īô>â-x…Gŧ›ŧßz9oO€ĪĢ3sŋrsuØe4ßĖ˒'ŧ"ÍĒķf)Ĩ{™ÅëMpáNōŒ­Gj˜ō}O ‘POĶŅđJu@‚ķô™aÎčíÁrĸŽTĀĐĖ1jō2“)Ģ üģ¨Đ}íđī žrØ š.LĶÍ*3œ™Eĸ ÖŽ W\Ut’‹loĨM3wRŲ~ŲMYĄ‘$T÷Eī}8Â,Ņl˙Fæ‰IĻëû5v5’ ¸‘&tÚķé­Ã,ÎDĘk™&SË_-cˆĩ/‹{ER*6‹D 0áĖÂīĸBõ2ë…%ˆfļh´ũ"ŗIE/3aX‚ĸØđÉ8Í:tt\Îũ…Ĩwę‘ĖNÍ}ö•r͇ēsœ,ģžk9jbcO¨˜iB4 ˆe¨VÃtžAėÛP_éäxœI˜ Ąp:ˉ’Ŧ\*zā$Ži֎y›vŧōœ†ų]Šë†ßŧryI{äō}ˇÜüĖ8S —īJ„/Ū}Å´—͝ķXļä†I#žŊiĖę1¯ĢȨ—÷=ŖeQdŅ­Ŗėįļėđņ›ÎŒ?ŽJ˜UŖkĪiģhėWSjZÔDŊw&*ÁoØ×bA3*„a¸ 4—ļ—’ŌŌōD]âėÄÂŌõĨâS¨)v~čüXĢÜĒO1ZC?ŠÍ“čsŒËC—ĮēJ?Ô> ũ‹˙ëđ×Ņŋ–ė/-”FSB­Qč'4ļpž1N¸Dø¨äü1S3ƒ^^$(žÎ¤^OÄĩm"Ž$‹PE"ÐģbŸ›ÛĶæYåáKY֜‡Q MgbÆøQ×*?ĖPÔãĻ1{h†kZcO cīY‚-ÂR}ēXuČĮˆƒgôĀ;N?ĻtÕ92—sRe3„taPØ7āv|ķĨ¸ÅĻJĨLiĒ„b8f¨‡™öƒ}õ0C=L_‘â6;5D›‡#´m8@‡ŖÉ‘ƒNŅX(Z-nM=‰ŦtsæUė…­áĪjt’[Aķ^Œ•ĨAƒõ:I‚&J—Wr ]÷dtāžOu,Ū|ņĻEvūģ—vÍ'õ“îZúÜ/ŽZúœ°ŗûwŽŊķÍ+ķßæß˙9ž÷åIˇîykßk{(÷W8ČŽÃ߁…Ãv9sšŗTXi°Ōtú”•Å, zīJLÃU ‡x_Â#Eŧ{ƒ’L;Lb&iĖ‘gŌ“aíyī5ĮîØŨ:€ūhâĪHEÃĨ‰ĄūĄá ū á6[øgäg܃úæ1MÖŖę<2—›'\Ĩ-ÔWéOj۔íę6M ik´ŋÎ[>Ũ¸ÂXipĻŧ3ۏÅĐÚ YëŅ´F 2 :ŲÆ4ŨEHƒ"$ÃŖÂ+3ž\gúü—Ģ~c¯f¸Táɕ‚†í́Ŋ`S Â6ËēČ ›a„ÍĐá†1†į&‚ Ũ‚ õ‚Œß+öJ¸Tj–ˆäe>N•^&1é$9ķØŲR˙xũîÁA^ŌÅÅi/,Ëop ]|„&-vŗĢŦÆZŗõü1sPÉMÂa'?¨ž%Ž÷˜l§¸ĻÍ%ßūōŖü?yËķ*Ũ]9õægž¸qŪøĻđ {q VŸÃdõĻGãķŧúîû¯üxÎĀĨOįXĀs^ļW¨„×3zŊ>L ‰ ÉDõ‚Ā„ÄĨd–0[™hKt•ž'üÁ˙qô3˙goË~ÆxK¨´4Ŗ iTŒr'é4RĄŸBôQd¸>"pnâBu˛~Šū™øEč>â5qķzLxŽG˛0îĻķũ ŒéÔŅņüî6ŠËpO8 *dLsŸ…MËļÚŦUđ%Ššw˛|”%XLS>e‰Ī-Æ­,f Ņq´ŧt-7ØfšA5 ØmŒ–ød'y“ys6ø*$æčqæ‚ŧ,í•>• OņcŦÄIIF?LîII‡ŽÎ0EBŠ1œ‰&ëĮõâ4ÔZgæPsa•MĖ §é@ŅRĸŋ“Ŧ†FĘ¨`Éæ`°č•<6xöjŪ{7´Ũ[ģĩ;õÜUKąņÚeŽyøļã?‚šuãĪ"Ūc#ˆīí7ũÚGoīĻiH¤$pš `ĮÃv¸%‚d×*´*“<ŗšųÂĘltæ0ąŽ:`_@Ą’Kõķ}( ņũ}CĸũgųFĮÎJŒ÷M‹^˜áģ,6#ąL\qPŨrĶĖÛē?"ãĩÁ“oYņ4ž~ŧ—‚ ÕpUū“üŋĖÔĻsđ=k†Îy’Ę? Ã*°8¸ (؈ÖFûEíčÂčĪ´‡ô§u9ĻWéíŅŽ(ĨŨjĮJëKdĶŒ„Šƒ$đ휈ÔG8Pđŗ>ôÛ|1)‰ufXsTLqänĖâ‚[ûŽgņÁ\ĸ´~=ÂQ›RoÔցzQ€ųlǘĪϜŌ3Ē)zkž+ú´EŸöWLĖŗ46ô|–ŠDwᝨ Å*3‚zõq+“QŨĄÜĄVęÔibķé$˜ĄËí€i‰Š$Ę ÷šŠ/Ž,҈c0ĢW¯Æ9 ĮÅuVēĄŽĄ~uŽŸĻl:XL[[yÄģaéųĶâƒ\0lī^îÁÛͯqĄīįęˆļ‹o;q PŪŲųņÜW@yITcˇyŗōüĘõ5j¤eû4׌đŒ(ŪgbŲÄ>sĨ™e3û´ÕŦĒų¨ō`Ų7éo+­pH v’ÍU ŋÄDą™Bũ˜ ^…ēĐ>– šÎ6…DÂP‡—'45ŦËÔõž<ō›YųŊ]Éf‘d"‘}al†íp[xU˜¯!!“j73nîáÆaƍib-ĢũĘáÆô,šh[äÆa'eƒĮ\Z?fĪa˜ŗÄĀT^ƐД!S)CĻԊ—ŊƧFÁāKfc,hÎĖ?ÆĢ FŖFŒâŠQNŸn$蓝 yƒņf#šĢYRFŲsnĖI2]TtÚŊ94cҌ|ŌŧķÅÔķŽGkˆđp8vl‰J TâpépCÅŧŦY/V}É&Ī€ĄKŽģ9âÅKÛ˙xøōßßžëš'g˙qïžzāÉëVl|ūše§ÄÆg˚:¨ũVÜôņũßv˙ĒķžßģėYŽú÷]/ŋũęk¯RĒ]‹wyī؁B@RÁp=›T ¯ ßĀ įvę<Ģ †ŖõaŲŌŦ'`d$)āQ5Wæjîp͍›]Íŧ[ÅŽX_Pp—‚CLā†l:¸J+t`jÁZl$Ķæ•=Oaélc€4퍺\pE-Nx<ēĨΌaŽĐ>õëÛC‡CdahC¨=Tņ!`C`C`ƒČ8ų&´ę0ĶžėŨxú,ÆĀŽŲaÆ-x7Í­WVÁ1Į@„ąÂĖŽ1Á‘ã"ŊˇE97ĮmQîČŠPœtØäØÔ;Åø„WôJ¯¨Åą.‡@ÔO´åčē:Į4…‚VÚbC/­ĩ×w-ũå¨ŽĢæģŊ ė€īîn}âĄîéäŅĩ×N¸ãēî;Ü ƒÛDķ㐄Ÿ˛ŖD=éĻ,ĻĢEĪų ×Ųpˆâ<ŗ˜_Ķ™ĖÂJ‘•+áânW•îvĨģŨDénģ„š@™ĄÆąRdĨÄJėėŲn˛UņÉ`OÂĸ'i÷UÖ+”vĨKųT9ŦHH)U*Ģ”GŠUû•‚ĸ–* ŋK<á‘&`Ø}ŲS¯ĮHD^ĨŒ€øGø |;ßÅīįÅ.ū0OŸâ÷ÁĪ;ĻĄO.?ΆŸWéķy&(xWP𮛟ĩSĨ¨Ā‘ˆ‹›ØŒHęv=”Đ÷Žĩœúa‰ō0Ū7wttđÛģ÷xĪ˙ˆR)Œ&÷=Í%¯mw2ķ”^™ ßšãõŨŸ -Ãx‘•Bf …8YœĒp†ūwá¨Č)}cŅŒĢ. ¸Ë˙a‘õIÜÕ*ņ‰)YŊLįžĘz…ĒT°õ ŦĸŒUØ7BČķ/RFōBFėĢNQ¯æŽR?âū*JOŠ8-fĨŒÜ(Všõąz ß"N‘Z”ëøåÂĘkâ;üûâņKéŸâŋä OUŽã‰(JŠ"Î"ËI H’Čņ|FP‚ Ē0ÜŧŒa(ęžöxĘwbÃVž9Ëeē7<ÅŦ1ĶI/YĘ\1÷•13w+ƒ˙=a•Žįžd0Ŋĸ’‚aˇû3ÖĀ2‘“˜É0„ú$€01[E5ũĪe#/é-@Œ6‹ę Ø-:Jv`ÔõxŽA_7ŌÔ2žĻ´ §EØÔHɔ›ä&Ž•Eŋŋ>JÁĨʍQ":ÍG㐋…Ę•š’FE.)iixI#lŪےb›ÍeNPŧ…%“-Bš‹Ŗ‹…Ž-e,oiKˆn>Ųb6ŠÎ†íilŗŲã&ŖQ3“>Ę÷1å@ž4ą‚&!l‰Đ‹ŋŪwNĮ­-Žoíd˜ŅÉ_˛pÆi,ēãgžĖĪÃ/’tĨ°ķÄ.ܞ_Ú=‹”^“§ĢÜ0ˆeúŽļõŪ|ėŪUĖîíÅŠNáNÎ ūŪŧčūC% Đ ã6,›wĐ`'̎žÁŲöëīl˝Ŧ_;bŌJ…G„O~,‡ŽTX(Ŧ rE%œ#jč˜Č ‚ž÷Â]č0 R/šķũIšSŌKî8håh¨rQ=uƒ‹…‚n,˛4†?•ũPūC ÅL`ļ÷Â:XR°ŖˆYĐ&Ķä/;ŋČRĖ^ųH`š@‰Û} ˆģ@ĖJ܉4 ˆģ@Ė47JŠģ€× đģúŖé>°\ĀīĒ!Ļ ø\ĀrŨM6“]˜ÖŲŖ=z}†?ĀPūū,%üA8š"a9•V"ņ”ÂqédB RÅOÂb:5Õ}ŧ>ŗ!C2ápĖ›Yoa‹gîƒs0ŽãėņžÄŲâԍeŅę–gŪĻ8eVL]ŠkôQq×é§O¨B¤.ÍnŸfĖ.͘]:ƒ÷!L]k¤Q–Į1–Wōo,ųįQ¨¨p­§#v€)GJz.X‘éÄËļ–å|(ą ˜C'uėh)ĒŦJ*'šYKôÆąĄúŠSŊš`€” 1Š!ž^XąöąĢ>n{tœŠvTĪ?įʧøė}›†/=āēî+ɚË/;ëîˇģwQ_ذÂAžF^GQüōö ›>īwæn‹”›´gS(Ęø$5ĒĪ‘'‹-ōĨâ\YŽ7‡ø†„"ÃÍQžQĄá‘iÂ4åŗÕ×ē r™p™2ËŧĖwYhVäjTDAŋˆ›(LT/Ōpŗ…ŲęM 'xÉFÕ;ß㈛ōŊígÆPEœŲØq†:RĪ ķuŊųnX†ÅäÜÃĖ5RLāe@—í­ČÔ÷“0’L)%qŌÉYÅũ?nEΏŒēÉö2tņ:Ų iŧHķR׌1L@ †ĖVäŒ["6ŗŲđ8ʘb.´âR/lū$ęŖŽ˛â"/Ŋ1Á\”k5ĢõTüp3ŠO”j2Ęa‚rąpąÂSu…žåg“kQqĒmoĶ{Øˇüæ8tíßnũ4hĮ–ĩkļlŊiíâĮ•w,Í˙š{Īß~Œ“Xû­ˇ˙›ˇŪ„ÆŽÍĪåË+|(‰īĩ—hf_ķ s”É7§ÚS¤4ÕGK— (9ģdaj}J?/|^ŧEžH›žŸ'Ī׿š—…įĮģRī>Ž|{7y p š?UH…Ō|ÎĖø!æūÔūųkčËčįņÄĀŧˆ%<’W $xė°ˇšãŨ;ÖUtŧ!1°iØF›ąĘā“Ėņ–d#n0Į›Ņãx3˜ãÍ`Ž7ŖĶɑĸût4 gŅ9Ŋ•q¸%Å5™z¯ŌTņoŪ° Æo˜‡Mb6)äØhŽ˙ģ$ųCßZŅĩÖ˯æzՎ4ũûĐĸEØ*†Â]i§Ä;jĒī›ôRūÛ+ŪŊū7‹ë.{nŲ•OnZzÕãųšD>} > Kō7L61rŪĩ5ĒgQ1ĶkÕc|Ú´’%¤$”x'GˆųŊ>w}[ŸÛ~ŠF|â4Oi Į*ĸļ?R?€^ŪĀĸ+§¤ÔeĢÕESôĘhŠ^e>–(‹_ŅŖ0ö;ÉEH*|ˇyĻ*ĒáFۍûĒq5}&ŊžÚÂPíō”j'O’GļĶģTĮX Ę*ĢëÛt ÍV h€¯EĶ„á{Ęé|âŦ‘BÛŰĨ”ļ-Ű0Ua0éb°ļŠ"s;fgËcS2‹~ļ(ƒQūŠëŠö/Æã€ š ‰-fÂvņ7 )—[DŖrŊ ™C4fŸŖ‹{-bIHÔ§™Ųtã¤!ŗ@Oĩ+û&ĶB &k™>ĶorbšžŠ#ĨJŠcĄ/Éė–yĶqTžÖ5šĮU•Š*æø8*5K¨F›ŖÎ$§`&PunõęÕ¨Į¤^Č֓§,ĨT™­<4Ôôo ßđĨszX,ĸy‹qËĩ+–5d~ōÚcĪ\}ׄë^šjĩkWÎ]1/Ēßøō}“įžvŨŪņ‰ų‹g;#É 8wõ˜‘ËĢJsį\{iä‚i J'JüjEŨY+ĻM}äÂį(o­(|GĒ…P—î@ZŅü÷ôšũžwW rŅT6%;KŊ]öVE1šŽb…L%g¨  qÃ,GåX?E=QõDÃIŽ o“J̤õ@ŠŨ ĩK]Ō>IdS؊sŲŽ0de+ątĮR+ÅŲmĮîQu™ĒPÔõZԚc@ÚIæĄ¸ų’xmØ šNôā•h‡hž8•hV]ųF¯ 2™°ɧDkEƒ†,š˜ąķ›.^Psã[ˇmķįĒ’>bž9û12ķ6,-Čß~[÷OF×ĨW xõ~úŋđ ;PŒ†šƒáz’ō‡č§ÃvÔ¨Īųq…ėiØō€ŗ ˙P]ČĩKCŽ’ęąKC™H˜1f†™]öąā]O–b˜ ¯pEÃxÅčN˜š+ÂÔ"Õi—¸+ŒÃcbt`+Š1;# cbíąBŒ§ID4öĆR͊A§AJW“L)û”ũ ¯¸‚Té¤Åx“ĘĸLô‰L~*ĖUXpG=ÅąVŒāüģŲéU–ŪÕäSFĖ1Ūôę†NSĢEYÁôäĩ8Ōe+ލáY]Ŋô¸˛˜—Q™ešėaFŠ)Ė5¯øÃkz:<ÖåãĮßqzĮCį\6ļáJrw÷ÖÛû?áΛIãņ`Dc4’#Ē’tŽüwÅĩgŧEEĐI<—Yü”9¸CEįgĪRR‡mF Äė• d¤Ę"U$(˛€‰PÁæķÖæ>Ūc~ŧ°’ęômã/4•[*iēÕ¨„|‰z™ēUŠ×MI–ÕŖ*(˜ņ ”gęQ ØûČžžę´z”‚ÂĐú *%Ģ6ĸõ4RŒ'“yŠr ž„Ė•į*ËĐÕøj˛\^Ļ\­ŽÅkÉîéfyōstŋr—úzL} Ŋ mVß@ŋQ?BPŋFUŖ#j ŧŽA!ĩ eÕAęXd̊`ûBõtNŊģ°$ŧ}uDíÛ`Ķ“ ´/hŗ@h¯°Z"š‡f™~œƒžßžÜžĒĨŠö´ėAĒ$ËE (ŠŠ8B2Næŧ Ē ˛4xQRaĄVÃZšlÛ.…ŠãÛla•@€l%El\îųꊖ‡bŅîÖîÖXäЁÖâzK=ąĢņÔ9ętÖO1Ãõä§÷ZSn.ēŋã_æüę@Ļ4’ûzGūr>Û}ãĨWL\JnvĸQ"B €i>a:Í-/bšc×ŗāo‰æDP¨;–ų/'üĮō8š^Ž÷¯œ™­&“´ĸXœCuĖõs&_8‚Øį{HbŅ…üŗæ™ƒė&›’!ōÅ0Ų‰“Ūŗ^ öúÜYĘrĪI+&+qŖGRąĘ‹ēļƒN¸ÁJ9‡‹č7ôųÉ֓$D×o;âøä$'{[,ZĶīą5!ygĩ@ši–Ōœ]^'Ųe×RČ˛Ųžjqi ˆcŅ”Ņ5‘Íqĩ0áUŪR‹ŪfGYt•ŋ=æû{Ė÷ØŌÅ  \‘~ĀÕ|•œg]dŨaq[fMu×_ã]€Žųš€”ãžoĘHÕrÎ[¤!ōéŪaÜHŅ–FËŖŨį`Ņ?;`ŊI.īž˙­=ä’ã‘ÛNė^m€VđßĀĢM‚éb#E^möäØN@•íe~ĐĀ‘'ŠHDÛ`ŸQ›c¸Í–úŠŋ`ø°QeQb{\´qĒq/¯ü€÷AŖKč줎 ŰC1ίõ˜Ų€‡xVã;ˈč ˛EØŊ2DDt¤ĒDdkuPĖĘåŠĶØ.EN’‹ĸ"đ¨û¨_:6 Ũô€&Ht§™ĒhC5_C¯)ÄĖ %€Âũ5ë h§* Į,JMCęXöĢ_¯•ĢÆ QšŪVA ŋ`‹ãÄUlŊģĄļ7Å]OĘĮBgŸk­Ø]\ķ˜ fËægæ‘ClŊŖ“dAårké[‹+6ÆZ™!ģS†R@“ÜTÄ­o¤¤ŅÖ)iÔĘÍüčū–˛F“ÍĀ 6âō˛FÅN¸3ßs-,ĒÁbđtäz]˜JøA4ōÎUbߘāĪŸ–¨Élũ žõ㏆äŋ$U8˙¯‘ũÎŽ;ž×ē‡ĪkɡRŠ_–Ī}˜#ķÁžŽ8ŠUNî;ŗXiđÅĐëģÖqųŗĀ++5į 'ĀJŨQØ57¤ë ŖÖKԀÁy¸DÔđ‰ŅoûŒ”ĮÖRE¤ŒÖæbĮ"{bQ“n˜?‹‰šøV#A§|b_–hŦ L66М­Û0ōŠĒ~õ&-$Mņ…ôˆ¯ŌSŠUęĩzƒ÷ËSåĢōŸjņĩø[‚s}sũsƒËÅĨúrëšĀ5Á›ôuÖmžÛüˇîW7zv™/Z;_Š_ūĄw›˙ IuC~O"ÎÌ Έö4ßņˇųzæ]2 ÍŪę`4ā÷g|jv ˜wÆŖ<ÕO'ĩyDz”0¤6ņr‚$:Iķ6úÂt’‰ļ§ŲgûČtßË>âëÄgo7p9Wé!Ö[vJ맍ոqZA#œąĩ–N“ ÍņÔ `äĐyŨt…EĀVēâQĨŅK— 6ZÅEZ¨)…@1-âi/õ4į¯t2"ą9’Eõ”Žú._8ŊĻ霰•<ųË^ų8W^šûkG~ÁYũVLŽĪ_ú´YUŸo”đUŨ\ĩzÅR2˙øo7Ũ2ârpÅ÷—Ŋø×ÛqĪ*HN’ˆ¯“ŧ!ā Ķl§ßŲ øĖ$Ë}zÅ>€>¤JŠ5qŖz.AFČį*cÍix"™(OUƙ đL2Sž§\‹—Č×*ˇâ›ä[”á#$•ŗ¸œSå_Č`‰Rí f°ž€´Pčôß´¯“!ŠJdUÍ`Ԝ`ē'™!äāÕ:r–œgĘIÎĢ’Nlt€lÄÉE!‰ú€YäŽ\ßāÅČk{ÛŧĢŧ‡ŊS+č!ī¤^ņ&„Įĸ+Pq(ÂâmQÃü?í |TÕŊĮ˙w™{'`ČH2 a K@"™Ä– C A4(QTŦA´ÖŖ(‹‚ZŠU §žv/ÉoC“kJ(ėį{+é^^{´sĪËy ͝_ā\äŦų[ŧ¯UËn,†3AŠŋžĐmx”ˇSˇKX×Ŧé<Üū>…NÃՎ k§–Ž QŒTūėŧb”×KŨTUÖ4AĢl|qîoPŽ=ĸyGnoŧú'QsÉīiš¨”{~‡×ÎÚIš˛ ØU]§mĶTm>ÎxƒøŠ^čã}¤Ģr|ŸS.˙ęHĖČ÷<Ų4Ņŧį›+¨ũC™ˆ°ÚŌ‚ĀПx—{Õh¯Ōh*šĮMõŋøģL7iĒSÍMÕkļ%í$t°ļ”Ŗx‡ÍU*ņöĩ­‰°ĖaŪž”ŖiÉĒĸvščɡD˙åņ‹*W)GČ×(~WEiĪ+õâÃˇøŖ[¤ÄĨō!ōėΆa*y/&÷š˛oö`MéˇO-Î)î]ØųĘ)ˆáFíSåΤv^ ũwæ'ĻēŨÜgĒ'ŧĘJī¯ZåŊĮĢ–zgbęUŧm5ōn19uIJs˛IÕrė„Š\:-™ŧ͆d6†˙đ !HūR>ÜØŅ´æŽũWö:DĶŋAGLė=ēSE‰ø ;ūIŸ.kJˇ÷|˛"&į+o7¯øeģŸôėÃōĩâ;žŨÖxŧ‰_WU„áĪŲt9]ęŖoˇ}{§¤}ķ_tš!­øw %!õ]ēZ¯ĸ80ÖL¤Û=“ŠLYBSÕjē‹Ņ) oĨ[áļæ<Č]ėîKÁG L]ĨŨeāPÂf¸ŨÉ~Æ-ސU4Õë§šžÉV#â[åŲOŗĀZÜoÔ?ĄÍÆpē æMđW¯eŗøYeTĶjØ?į3`ˇ˛ æ ¸Ÿä}”š‚ē°ė{#œäûöÔ^¤Ąz•õ'ŧK9Â#ŽbČBP7 ķÁe?-Uö[ņ’!ū%l ¤ƒpîÃķ\øKƒyîģ"d HŊÔ­„fK{ 3ņūSė÷ûi6ŋsķ;!ũ2Mgb§ą(Äų_ Un…Œ K››E.Æjƒ(9tÕ×é&}<)ȝĮ—č•t9Ė ŌYâyŽÖ°\&¨˛õ'hŊö% Ãŗ;UxJä÷@đ5eĒ˙MũŒ´õĢáß Ö"ĖŋŠúPI“ČAúQQ‡ƒåˆë¸“Oœ70ߍrŊq}Į-ūKĀh”KÜČéAü™œį\îĘäĻáp{nĻ1°ī,Āģsd?ėaõõpc‹¤pŗųzRqœQĪ$xö Âé úƒŖ`#˜.E â&Ä̉úŠ:ÃuSÔÔ Ī~ä!Ō&ęŦũkEyÚmfƒ ‹ãI1ļŌI ‡Éí…ë,Ō˛Ũ ›Û×GŠú=GÔûĪų=šN5K´=ũæ4ˆ6ˆēåHnwH3ˇ‡Uj)-…\ƒzŧˆë,§Ī‘œ/\×Dž MH™öŽDÔˆRe]_äH'/šålڄ0§×ĸOYOcôy4Fû)]ĢAZoęī;ŧ܆Ôct…w/ BYN€ų1—\͘ Ę žŊxĪ-ČĪzyú#ŊAíŽ7(ĪëS)<[Ôâū éFŲk?cɄ?ģPû‚zČŗ}æë3Oƒeá}á6aS€dGžAo†˛Ú;GŠ3KÉg} æę稠l}/Ę'ũ<ÚėK=ĸzm-ĶŦ÷” ÕZlÆŅ5ę*ôiˆK=D‹ō–°ztZs×%G:õÕ-šĪ—uĘi ũŊ!9"ų|…zT„:Ų…ĮîŸÅø€>,ļëĢõmsũ<@OA>āÔOW=ãǟšëĨ[ŠąũģĶN‘ŽeÎûs˙Č}÷‘ÜĪq?ã¸wË0˙÷ĢÕ¨ĮÜŋNSeģî.‡4~,Û>úa”÷Ë2 ­gŒįŦÍZŦĩŲČÂũīĮzī}Gķ˜Zf5Éņ´ˇ3–ÚöÔÖG=ƒč&؟mũÍ ú™G'‹ôEÛhĄįĘ} HīzŲ‘ŸH÷}:ō| -Į{tŅ– =ÂLã<eAĪã‰ÚŖČg‹VĐ"í}č ėwĩãE.MAÚ;ŒŠ,ŲÎ3…6Į(K/E_ģ—*šŦø=8=\öŪÛ(Ú‡~ĸęŋ€›8jwëEčQ/Øī¨TČ s™¨ŗ—à ‡ˇAø PŦĖM"/„č"\‡9/ĻGW}â­ķ”Ō´Ą f6`Ę@h›ÆSđWĘiŋŽbŧ~”ŽDûZŠži)úõĒuJۂ÷šũ:Đ‚ČŖ-ī "įˆw/Đí>v ˇ­šŌšŽĸf}âQē_Ī QÆZģô“ˆ÷ØŨ‹ö;mwüûeŋMˆ{ėŲo.ë2Ŧ#p{1ÔÁ =€DXOAüÚ§´AGKQķŧ"îŖ~¨ŌŦ4&6Âŧ@˛ÜFØųlФh>úļWŅۈĄ-‘ÅcčNũnē^ŸLYÚ@´ŨöÔO˙-ÚęIz\‹Ą ũ =Ž×Ņr6먗Âû?Ũ’íߤbļW߆y5MÕsā)ŨŦWP•ļuījŖĪBYßįAԓ4ø?p%Ę'4U›Œļĩ÷'­­ėNÄņœ5…ŅĮP?á/ ‘VWšÕ"ŧÕ8”)ŌË÷§ĨimN§“Æŗ¤Oŧ'‡ ėFœÎúô°eĶDumëÕ?ĐĨÚeôceŗĩ ųZčbL¸YĸÜúëCčp7îûBū lŗÍĐŨ†Đûā>„ũ"d­!6´0ˇÉ§Ą,aˇŦ¯9ĪÂáxÎfާ›ĩë4ķķk€ōĨĩ‹qģG>E|CõKŦ] ęâ8ÆXHÍųÔQë û$øs™=ŨОž§4Ŧļ–ĻķŋaųG§< ;}>“É,åØđƒĶöCAų.W‰üũœâė:Dí”C֐“•CäĶnC0÷ƒšƒ“ŸN9Á~Ĩ°w•ę qžģíŨfwšļfVkŠ"§4ׇGh$ŖįÂ=p›Ŋh$cŧŒg/Ÿi֟i…ŠÔG[ÃiBėyĻؘ@=5 iíĘ~Đæ@ŗųMô€Ũ ˙Ņ4šáļ˨ĪažšŸĄQLXžå|ÕÖØĪōqĘÅ]>H_@ƒÆBĻC‡,įČđ6ënˇn;§/9›WÛpŽ0˙?ļsė¯üoĮĨę*đãč!šĐ# Ÿ\ɟūkD_ō]&xũĐ$Čwa‡ŅģŠ7ˆÆ}{Ø]ų$ŅŠ¯p+ėl,UīFëĨ^Ųv;¤_¯ ¯ÄöęUĸoŋÛl˙§ĒÁ ¸˙;Āx~ꏐ/BކûĪāī^Č_ÛĪ+`žöĀ| æAƒė :€Xø_Ű>rÆ<ôß.Ī>˙øž:Ë ¤ĶĪk^wšįß[:å؊tĪ5œōoM†­¸¤˜3} Ŋ/>÷9ßĮ‘(ĪĻpôRĢ:åEŦGŗ.ËúŗĐĨķ7ĄĮ"^ĸŽŽdŨ™õW֝Y…Ü Ö <"=Ĩ<Īé’ãFxßĒ|Ikt“rܜT{Zo ī‰Aũū sŖM ĖíĀdëMŒ]1ëęŅī~ų:ˉ_9cšĶˇžŅĮļ2ĻũģÍ:Fū€15KRáâ\öÃ$c÷X|Ą´6v˙āąüctø8ũ¯šqŪ!j$e1fĀÚŸõŌ3ô€VĖ­éšjvëlvé%ŽŲÍĪŨuĪŅgēR×f\íîBṅū|‹îī¤ÁŨŽ›Û›4#F…ƒ~ —C7ĸŋ€ūo%ŒQÖ#°[āũޞŧĪRĖĪŒ›MƒŦägë”ŧžÍŋĄØtĖ>ũuáļLRŲZ}v×[ÖĪ…~ˆ<ũāÜ~Ę#@,Ønrƚᐈû=Ŗ.ĪsõŠÖWúĀĨļ*‡ĐĀŗ0ĮĀƒž¸ŖŅũv€žáõxČ6mĐŋOlYãŗ;…›qbmyA?ŗŪĀk_ÖKbM¯‰øÛ¸yeÆPŋŗNs¯ ™Éŧ^bÕÉõšéÆ ŒƒS0Fņ؁x'‹=Ą9:¯ãž ŸimŠ@Ž!wt֒y}ŠĮ+Ŗ?ųÄ:Fø:ō'4PŸF Wˇ÷ŠJyũE;*öj–đēģv9í‘û[Ą6Õ´6j?­õVRĄwĄØoZĨ=A‹`÷„ų =adˆũ•Rg\å1ņ,kŧ–ŲĩyMSžŗ['é›Fãy=&<^ĮŸˇcé ąe¯cļĸÛ`ŒŋTÚûÖ×g_ī´~#×=gË1~~ķ˜ī^§ŸFĩ˜÷9k˛OCĸĢõÅ@æą;-N\ȗÆséBŽn‚û)b­ĪŪīá5¨aûp…"Ÿ?å5–ËĖ6ÃåoíÔíũš|ũ¸WŠ‹~ØkbŽ×†Áõ=¸_‹6z3Ú ę žRėáŨ+[ëiáīF{ßĖ(šH×,øĢæŊ#ē¯ëˆ^J÷ ÄēšĩQíhí„ŧU}Mė1ÆČŊĀ.úrš$Ö4[öãõ^bŨē—>  üÁaNī.ĨČĢüÅ`^ĮīČksũ‰đĖĢk¤Ō­ųšÔ×ļTčŠĨ4m.ô—ŊčëPvãPŽ1´Hû˜’ôa4CkO•ŒRhŊĄƒ„Ļ؟Áū=ȟÂĖ{ŋīŌÕΚŊ>M§Ą+š—ËĖdÔj%Eî–ËûDûvÃi‡Ā ŖšžîŦÁ)õgˆ;Ÿ*Õ:ÄąiA<šíĪü\+é%ã­OA;KŨĀ/ËL7°gŲÍ´īęö,ķŨĀ>˙,é8—ģsĨã\öén`ŸūoHĮšÂMuûÔķ¤¯Č ė‹. įĘį47°O;O:.wûËŨé@˙„ylĶ+˜›n…üŊī?…‰Ú×ôî1ŋ°fIķīĨģ˙˜˙ZĖ•­| ú<‹įĀK ˙`^mMlĄéd‚}ÉĮZ ú€Év\ėˇiˇˇ@ÆŲTkûo|ōU—šøŗŸˆ›ûŪ]Š`|ŋĨ2ېöĻ•-î›ėwūB-X¸ūũ%-4=ocíƒü%āuŅũ2]|Ÿ$ķƒßųĢĨ_ oõ5č3ĻaŦîhVÛR˙ }UˇˆūđÚ,ú; }_eŅĐCž¤|Ö¸÷ĖîđTbl"č'“Å~Ūũ0yô—Š‹į(Uč7SļzņhôˇˆCėË lîˇYįЖŅe@ėUŠ=!Ū;šƒ–´yNč/>¸é¨˙é}Œę1g[ę)#ū ŗ?Ėc\ß@wx~Bwzoĸzã ¤ĩfaŧō4ÜsqæļÆMåšz”ŪÕ4Ãė ûjJÖ˙L QK ×ŊEÅČŗl'îæŊ{“:Âūi{}EÔ?đ]/ԌôBĶ1ˇîčœđ\…<Šéš\ė9ũ‚tĖŅÉsc÷XęeFA÷ʤĨQņ´Ūøīa@OÍûōŗdŪāũ'ķ:čYBéÎÜŨ8‚|žDmÉûqÎztˇ úlĄ/Ɗ}-šĐ,0xŋ-HËųŦ„[¯qô¨fBŽ4¯98īÉãgķûKĻoØk {ĄŸÆQīã‰5ˇ”iûx{Q—¤>kÖĶ8Sƒ|šf‹ŠÄsōĨ•˜û(ÖMņŦŸ™ĻĐënâ1Úsēh ĨŖl.•íũvĀmi´lãķ`˙.ØjˇGn_l/Ú&ė×HûĀ]āzû9?ŗÚ÷ĮíđÅŗģl÷h‡īÁŠak5؈yHr¸ž*ĪR->CļėŨsũ)lU~Ī54nÃ|Ļę,{ünšrļc†ž÷Úč#đ› GvKŨ>Ÿ˛Ā–B7dų””?įēÆēž[ē͜ë<ËyôXģ9ōôs/ŽŧZĘôæs9­Čđs2-Ō˛¤šŨ÷]ģ“kn]y–ķöš\‹4Θ?…KQ&¤I=–õ÷qbŸŸĪ朇æ3\÷ œÎd†Īœ # cŪx:RĪ?'ÆCđŧ~7Ö?¤ųnëqÉ1ÉFFS0—úOŨX˙œũ|]ņ$âŪ~6æĄ˙Ÿä™hÁŪX!  Ī ´ Æ<.yĀÁ˛'ߝ|tōīögŧ÷ėæ4;ņËp˙ÕrüWËåßõŪįK{8ōŒž#ųėžqÖtŖ|˙°giĒŠƒÄ@žî[ĀAÉJmĨ+ŸUŌfĸ>Íį›ũœQV`nĘHŗ<cĐėĖxģđŲ*?[ū˜3íúgö´ķIœÛąu¯ŖxhyÆv–ėûŌĸŠiƒ<'ëįžã.ˇķú‹4ëtĪ*ąįĶÖFŒ“¸oī™G…ękÖĪ=wĸOøÂzÕŗē@\÷JHÖÛēŸĩMžƒ4ÄyājúE8˜Û&1ėņU§¤žÍzė­6Mąí[ŌåôŊÚ7xSÔEœ/ ˆųuą~=æô×SížC_āũ&íĘã1C ŨŠĪÜÜ!ĪËōÚǐ6ŅȗbmsXûæķ5|Žˆ39\N¯` `÷¯˙Îüž—X_šƒ~ü}ō‹ŗ?x&Îô >ëÄz‘†…gęÅD¸hũV[ 9Fō ¸éL×Ģ÷R?mæÃoA߉ƒũĀ\ÜĮCƀrđ˜O…ũ)ԓoáh:Ėŋô`nīŨIÉr~.æÛ;¨:q%ÂŗŨ5?6U*ŋqUjųîTĖ”4hZœŧ7đü>øĢˇįīŧŽĀîÅ3ĮMT‹Īߨ°Í,*4:€eÖ.OžĩKų”rôŠÔe † ŦߐķÖŖŪČ-k-ĖU÷šgŸ\JĪŗtŊįęįi„~đęÁaĘņ|M{rŠ—QŒql+q]xn7‹Ī‹ŗÄ ÖÎÚˇƒQFqQ/Ķh”!ņų GĒ[øË­đžĨb<géÖŪ神8?mˇ5Ąįš´í¸Œ‘įžgŲûcĐAŅötûœj/ũ)J´õ8žC5!ˇ,n%čš×^Yō™6Ž[R„WkĢú6Īk­lŪĢP‹ųŧ–đ{Ĩ=/ĩxŊúg€×,ŸÛZÅü_īoŠŽ}¨síĩv6Ŗĩŗg˜/pOÅ}vŖĩŗ­š]{.­í—ĄŽ˛Ž\ˆqĨŪ¨ļ`~üũë&F'ËëŖļžļLk‹ļ=sĐą”&×Dy4 ũW’ž\Ŧé/ļÃŖč›ōíĩyë;ų9ąžĘksŦ—jņâs]åį8üqrũV|nĸyv0•r_Ë}Ē3øl7æičo*šoQĐ õ;ģRÄ}‘X—ĖGķ…÷jŲ§äS”:ī˛ŌF‹ąˆ>ŠŨgi„đę¸?Ãøk÷W‰ZWģ˙Rßąû õC¸qø|Æ{5<Ÿsj>ņ 16}k÷“ĸ/äuH܋ΪØķ§nƒü9˜Öô%Š[nqÉŨŽlM/”~ļH?gē—{7K:ˆ1y?õæŗŊÍķ.ĸAâlôŸÅ|e žŗŌĸį;ëíĸœPFöŪžâžđ~—­3§ˇ×ÍšŪ “6bœæ|ü ô˛6wĮ‹8ĐĮ‰ũž*ëK™NžŸtA=} yîįĖ圚Ņ}-mŌŽƒ.4€Ī$‰ņ~OØüv#ΐ §ÄYfHØŊwcėqCŒ!/ƒˇĀoÁįāŊNÕøvˆķĨy>´ŽĪ4íô|€üz…ĸŧ㊋ąËÖW´ ŨĘëâ Ž€Ÿr¨Fģâ~ŧŠ×oÄ_Ÿ  ĩŧ8ŒWQRíĀ8}/FäĩcB›;l.‚ns­‹_ŲøgģŪĻÃ×-tüĩMÜwD‡†<ę6˜(1(é "˙a›î÷õ8B”­´WŽMFÂyØÔB˙DģĸJbĖr/QöƒDÃPŽ# ˆ.Û‘ŸŲä!ėKĮb,ķ0ҏ§‰ÆcÜŋ|>†H˜KŪ&*EŧS?åȇŠ4ĸkn&ĒÄX7k°$áƒëúƒˇ#Dˆ!B„"Dˆ!B„"Dˆ!B„"Dˆ!B„"Dˆ!B„"Dˆ!B„"Dø^(ü‹Vt‚rčI2I%eōˇžę[ÛūŠ<¤î¤IZ¯Úôx˙[{´Ūt¨ZD˙N­§–X3¨ĶRkcã˛bōúiÉ-S\“q ļz S…–{Ž Alõā-`áĘO“Á\°æ'Zĸ–P“ė÷åõÔēĀo¤1FëLĮ4ō㚠&€ đX áŽm悅 |!ž´Î5 BÚ;×< Dí 7f ã5ļqÚUÂX;Ĩܖ—M´eÁXÛŲÅﺁƒmëūųļėŲזą=˛‚,ÛDgíÍë¤uÂKvBÂoÁUQ_ĸE!?­×â(T͐6-ļ6-=k]ŊĻ“ĸКB•äˇöjJMtûŦŧ6ĒĨ§Xō̟ĢŗŸ¨Ģm×>k]Ū8õcÚęĻ~ŒR˙D ÕÃœį¸æ‚u ŧ ŽC=Œá߇懪ū‘2A.¨ë@=8Lõ¸úÔ¸ļˆ+ßįUũWŸú>^ë}\cÔ?āîꐴßÕdĪÚ)n22åŋ‡ŧéÜMŪÄvĘĒSߎ9Ų5*%ĩ[ëN#iÖŊĻĮ@_“sŊŋNũ¤69Ãŋ>o€ú…€Š”ŧƒ˜ßĄdP σ[€ģC¸;DAđ0XBĩ WHV‚߀C4@1đĒoÕ š:õ͚ô|^'õ u?uFŽŋŽž*äoÔW„|M}YȐIÕWj’ü”×Ī ~|>ČL<÷¨/ÖĻÅú­ŧöj=ōΏk&Č@xjŊÚŊĻŌ‹@vĶA/Áe }*äĶ´ŅKüôKQ“ų’~ņ%¸Ãe]ōēt5žę1ų’ūā#¸ãKúŊËqĮ—ô;īÆ_Ōoœ;ž¤WŪ€;ž¤O­Ā_Ō'LÂ.uęÚŌzúŗ'ĖQ’ķbÔۑKˇ#—nG.ŨNēz;˙Ŗ“:§íņš>}ckŊûøƒģ”ā%x…ܨg*ÁJđn%˜Ŗ¯V‚J0A &)Á€Ü­ CV•Ās§‡â•āA%øŦŦR‚éJ°‡LS‚ÉJv NMŠ;HˆQBÔæqŖƒŧd$zŸ59š‚:Ÿ‚>Ą×7%L8Jîn;î’IJ{mŸ\ÛÜ˙âŦšycÔ}đ¸ۏ>: hĒŅ>˛Ä⚠*Ā^pXĀ€ëîHøCâƒk&Č`!8 ‘œã@Ĩš2‰ÛDÂ2eĸ'°IŨ‡Ũņ/EM $ú|ž1ÚC JL’2!ÉJRŗŠS'"Šmīm_§Dīøgô7˙ŒĻ¨ŧ(õAõ!JDA<,åC5'ũuĘęšôŨūŧ8å?)IG­S†SēŌrU ķJđ˛L ęČŦš„ÉđS“Ū×ŋKiĮžvøO&ņšP§âö¯ ģũī&×éJŋ6[vøßIXæ?Yį…Ížô:bW˛pē3a˜˙؃ÂéŨx°ĻÆŋ€Å˙$ŒöĪIfÚŽŽ‚)ãŋ"}Ē Â+H¸Ö¨B˜;üš WûslWCØĪ˙$!ÞíƒÄöN‘Ļ&‰Kŗë”؁žæ*ŗĖœ`5ŗĖžfŠé7ÍnfGoŦ×įmįŊČÛÆëõ^ŨĢzÉÛąÎ:Čā_xėhˆzät+¤‹{ŸĘWņũüKč^•ÆQ¨ƒV¤•ä+EĄŊ3¨čÚäĐ×%ŠuJ›‰SCžÔ|%[DE“ōCÃ2ŠęLëŠPvFQČ,ž˛lģĸ7vdûá…gšL—×°ˇ?í>1´Ē¨¤,TXĘâ+ąŧ(´˛$yZŲNå„ōŨ‚ĘßY”—íÔF*'F]ÁöÚȂōōĸ:e˛pGÉĘßá5æī3ģŖdo’íníŽüÃ] ¸‹ŠĸÂ]¨(áNWØŨöĒ´QÛĶŌ„›ÎÉT%ÜTuNws°Üôč!Üt ŌAáæ`§ ģ Nā$)A8QēR‚p’ tN&ˇ8ɔN–5;Y&bŌ”7 ļ›čΛčÃpsž_P?ũof~F†R;ĸ|Æ´Q3SGMO5L=0v|(xmrōöåü 9¤ĨOŋvÆl–×Ė •§Î,ÍH-HŪ>bÚYOãĮ#R ļĶ´Q“ĘļO Ė,¨1*õš‚ōÚŅŃŗO‹kYs\ƒ‹ĪX16˜ã}–ĮŲüx4Į•Íqes\ŖŖE\$ęxqŲv/å—_:͖ĩjÛ6¨¯ĶģĨ”įwōŨ2RTŪ)ņ ēí‚Úf”‡.JÍE~Ô/¯_?B›âGí`#Å/‘Ōm—˛Y>ōÁē}j>eĖģ­ę6Šu}ũŋ °šwg¸}ͨ:מ Ž)¨šGTęSRʝ8ĩlģiÂv:ŋRčbĮŽmÛQuÖ^Û˛?,/fKMkvČv9l%žYūˇI)~t8¨îŽUIĘ<Ē*×BIE“Tt“Ļâ]§M-Û]Ї‡Ērŧ`•’ĄT9aČdķSۂßŲaŪmōNæÅ<)mŸđRådIķgVFsŽÍC€ô? š× endstream endobj 34 0 obj <> endobj 35 0 obj <>/W [3[277]11[333 333]15[277 333 277]19[556 556 556 556]24[556 556]29[277]31[583]36[666]38[722]41[610]43[722 277]47[556 833 722 777 666]53[722 666 610 722]58[943]65[469]68[556 556 500 556 556 277 556 556 222]78[500 222 833 556 556 556 556 333 500 277 556 500 722 500 500 500]95[259]181[222 222]]/Type/Font/Subtype/CIDFontType2/FontDescriptor 34 0 R/DW 1000/CIDToGIDMap/Identity>> endobj 36 0 obj <>stream xœ]”ÍĒÛ0F÷~ -[ē°-i¤ÂlZ Yô‡æļtëČr04ŽqœEŪžō|ˇsĄ†đįČ3>BS<~:NãjęīË-ōj†qę—|ŋ=–”Í9_ÆŠj­éĮ´žŪ Ķĩ›Ģē,>=īkž§áVĻūQŪ×åiŪŊŧüūĐŧ¯ęoKŸ—qē”Ä۟ŋJrzĖķŸ|ÍĶjšŠŲôy(¯úŌÍ_ģk6ĩ,| _žs6Vî[tn}žĪ]ĘK7]ruhĘŇĪåâ*Oũi‡UįáíīŽ•ļa‰ÎŦ´;D‰•vh`ĨMĩ +m¨eĨ͈¤čPąõŦt-"bĨŗˆ+C´cĨ#D{V瀍gĨëIß C÷VʃMXŠzT´{VzŧŪ”Đ×MŦôđeaJčQŅ5ŦôđåZVzø*_¯ô"ĮJ‚BGŦ$ør•„î]d%yD'$(,Ž”%ō-+ }y˜øōR hÂV4áĨ<Є߱2  ŋge€h/áĩ/X¸÷‰•î‹Ie8#X°Ô°2`;, øl˛Ŧ ØrŦŒØōŦŒCÄĘ9„ŊFČĄČĘ9„ŊFČ!ŅFČ!l—0B‰0B‰0BÉš#N‰0BÎYúŪhˇķ&‘ôŊŅnįm›7˙Ë6zļŠ¨“,=–Ĩ 92Ęļ!6NY§ë|›ˇUĻüĒŋF‚Pˇ endstream endobj 4 0 obj <> endobj 37 0 obj <>stream xœíŧy|TUŌ0\uîíŊ;Ŋī^“ÎŌŲH'鐁€, ›‚Â@ÜÔÁPW„Qf\fÅ@Ãâ€3¸/āˆâÂ3 ƒ:ŠĸƒŽ#¤ûĢsģFį7ī÷{˙{éĶuö{nĒ:UuÎŊŨ€ ‚å €aÖKü=6ã[ĒŲ ¸éâE—,ØuäĮÅJ?€lø%ķ¯žøKīßŌtsŠÖ͝3cöū…^@õēfĐ\ĒĐ\Ëĸ5eTΘģ`ÉUīĀ3­TnP›ŋpÖ øúöW†?`Ų¸`ÆU‹T—h—´ §ūūE—ĪYtj×a•i|õņsq¸)õ 3Á 8š‚Oâ7PĩĮ{ v˜ŽŸ‚äg<…{Ĩx<ŽJĻ0Á¸ î§ēR| ž€čŠūØQ¸Ž„waBâ[Ē Ā#đ äÁ`˜›ˆƒ–A¯‡GŖĢ*á˜kYTˆˆ_B. ›ņ&ȧQÆÃ}`‡4bnBMåm,h¨ūuaē2/Q”ø÷‰¯&fÂī0ĘŪˇĀpƒ"ÄoNŦJlHl„48%¤÷ü9QœX@WM€vX ×Ëá!x[Y5ۛ¸pj!–Ásđ:FDÛÁã¨÷¯áØ „đ>|ŠˆzĖÆåø’AĪūøūĉ™‰…ĐŖĄ–Sk:fb›,Lž÷ü-~,áĨąĮÃp\ k`-l†Ãđ|„Sŗņl‚đ4¸Ą&ÃLĸæŨ„Ķđ*E%–áŒá-øģBzö“L‰`% —¨l š>ĪĀ~8o͘ßMtb'āTŧWāx>†Oáü’ÉØû‚ Ü(ž$~/ĄN<˜x‚îëø!‡8S ?߄/h~š˜‡ĩøayŠÚžxŧ41,ą,ņbâ0„ ‹úVCÍyL"Ŧ¯†›a7ŧDמ oÁgđOĸ’€j4-üÂqx.%,žÆo°‡Ųˆ•l>ëb‡„ˆđĻ8IÜŌŗ=nwÅŋ‰'›‰?'ېø;ˆîSOhƒE°XâØēĪ‹pūž§{ČŅG¸Į‘4ßhüŖx†ÄIÉn`Oą„P-Ŧ^âņŅņņâÛe‰Q$[ČĀ e†4M€Vû&ĸæ#đ$qfIĪ{đ5:ЋExNÄlĮš¸a^‹×UŸĀí¸ßÏđk&29ŗ"lģ‰ŨÍļŗũė=v\á"ĄEčŽîļ …ŋ‹1O,G‰íâÕâ52 r›ō3ö3 zfö<ØķįxAŧ!~Y|Uü…ø{ņOšÄŪħ ‡"Âą.!¯§ųßwÂÃ$OŽÃįđ%ņü;ĸ…€*tÆ>‰oõ„÷(Â|ļâÅæâĨD˙å¸ģpîÃđU|˙‚Gđ†„}…*ZØÅ4‡ŲfÖÉ> đ=û—ō„ĄT¨Úi6ˇ ˇŅ|ŠL´ŠÅâEâ2ņe™ ›-ģOļAļ_öŠė šA>%Ĩ#Îjúo°Äa>l‚f&_°ŋ°(^ĪNãīY:ž@wKš…fVĪĒ€án’ō`QläfƒĸÁÖŗ|a’´°„Ö°Éė֏ã8͆“¤]!ŧÉ6ąéÂqXƒ‡aŨ˜€:¨ÃâŨ;ĐAĘžßâ#ʔÂŲĻKÜ*~.cÂ_HV#^ÃÉx›™¨UÅî„• x‚Ō h~@’ŋ'AĨxLXÍF°¨n>܍/ĐwÃ|ļG|Фõx96ãFĄnĀĸÆ`¸”ŨAļˆIž'Ā?đ&´ŌĘ=MŧÉ`ƒ(čØ,8ÄZ‰ëŅÄ đ’Ͱ WBöā>xƒŨƒpŽđĮ3Ξl†gNāVa8lÅĶâĢâĢL¤‘^ j‘öˆ‘„ņeâŊÄzĐxîëäCáKy=@,Ģ­ŠŽV \YQ^VZR\TXŸÉÍÉÎ gf„‚ŋĪ›îqģœģÍj1›Œ}šNĢQĢ” šLB^c¨ŠŨßnīÃĄáÃķy94ƒ*fôĢhīôSUĶĀ>ūvŠ›`ĪõŧøœžądĪX_O4øŖÍĪķ7†üo6„üŨ8yl åīhĩú;OHųQR~­”×Q>  üŽš ūNl÷7v6]1wec{ ˇUŖŽÕĪQįįÁVĩ†˛ĘuÚC‹ļĸŊĨ ŗ7ŲĘ@Š#¤:]Ą†ÆNg¨cĐ)d6ΘŨŲ<ļĨąÁ´æįubũŦĐĖN íÔG¤.P/ŨĻS^ߊnãŸĮgĢü[ķö­\Ũm€™ííėĐėS[:…­üÆŨˇĄĶ~ÍqĮŲ" nĒošĩĢ[XŲč˜įįŕ+oõwnÛŌŋ5ĀãÖVƒŽe™Mí+›čÖ̉ˆ#/ōĶŨ؊֖N\Aˇôķ™đY%į7'ÔČkÚ/õwĒBCCsW^ÚNŦq­ė„qWē\ŽØÎÄ1p5úWŽo :kŨĄÖ ž­X9îęmΘß9°%?oĢÁ˜$ėÖ4}*ŖÕõĪĖék“rRwž9Ž˛Č1 ]@ŅéŸå'LZB4§JÍŠ„•ŗ*Š}Z‘ŽęœM™×ŠĒo_iÂëųõ˛LCČŋō{ øj`͌T<Ķđ=đ,—“>QŖöŪ|g$Ō™›ËEDQO<%k¤ry~ŪŨl^h‘ÁO ‘š‰ļ3Z‡ųÎāUŨ1˜I…Îåc[’e?ĖtwAŦ0ŌÚÉÚyËžŪëŪ˛ŧˇĨīōöIōvāžŊĩSîûę 6sãÜ!hûÍs’í#/ ;šÅ߸˛=Eۑã”’í•}mŠ\§šžEpŗTŽšŠ•„rj_g^hŅvŠ™ô•KB=ģ[Ą$Š”jĐßÔihžŒ[ՁĀyQwâ$ŋJJÎ^–BŗsHd`šj@yzڕ!,†ŲČņ“WŽThk" ´reSČß´˛}åŒîÄō™!ŋ!´r'ųᕋÛ{9ڝØĩĘŨŲ´ē•&1‡ä“‡Ām7˛g hÚ*WtŖv;ÕĘDž@-—QæYA`.•‚×=‹āTŽšÖm8Õmø!:ĘĐ…ÚhO”CqQĀ0fR„ähŸņ ûÎÄdpüâ>ďŲö%Ų?%äÄ ˛gŠj [[Ö(JĻÜÃFŅļ/ŽŖÁ1üĐvŧį8Ԟ(.€ —‡‚á,üÕMō€Ã闋Ÿ÷ü#7ČĨX[ü ų!Ÿ“ŪËĘŅæ˜ĖžfV›lršĖ`ˇ™­5fŲ(•Ęŧ)-Ā@NąĶķÚ.”+ø<ÚFõœŠNčn4Ŗiđ`äŨ¸ ËLϊAĨ%dFäĸZLÜ .YafmŅ'ŗ´i&§âWĶĻũJá4Ĩi3˙Ãī#Ãq!Ã¨Öžī~ôąx÷ĢZĩŅŠ âˆ8Q ?~†-Ka›Ŗb*—“9]"ĮXe’Ûm™œ°UĢ iÂWOĻ‘+ũŅ]´cLáûĮ÷8!,Ą;[ c NŠō˛ŠAĻō2–E9ÂŪn3ŲØ˛ŸÅöÛÅņDüé ÖIØžŠÃ} ‡ŋFØ:4Áøŗ„-íŗęÅjqĄ1%VėœâķŅžÛō{Ãņ÷Jũ•Jé9S”ö)FŖcŠÁ Ä) …ōH‘ĩNŋ˛ųFG Q"°D_žW š{ŽSJ9BŨh‘Đ6‚Æc™Dtc Dĸ7ģËa69âĢŊ.——đ<.áyæ‹O•ęĩ›ÍvüĪĮ§ņ<—ĩHü{ H¤JcŽ?Á_āœ$™|Vİā/z…OÁ{đPÃL?+s…'$b0EJröņÃî°3$`AĪû%!§ZKãÃ.ĻÍl­WL ûˆO2ægmæ‹ã¸á3(ŞĘEķ™ßŗeW]E8Ŋ™ø„6ö߂<15v)5âûgڂčiM:!‰|firâƒØ„ŒĘæą<úvLåŅH,>Ĩąū.ãû°B|z›‰ŠCģ߁8Օ¯ĖŠSQ>;q ˛˙5ņĪg=iĒ4eەø ‰īēŌĶōųš‰ībĄ™'͗4-Pz=&(Ā,™.J T›ōĒe&™LįdžnöÆŗÅÕiÎĸßîB9‰cۊ$ʆëZbé DFãā¤DÖ_›Ė a‡Ķî´9­N‹S&÷¸ĶŨ^ˇĪ-ĘŗÂŲáœpnX”k´j­JĢÔ*´2š3bā7ģb‘gÆ _,ŒaHˆĄÛIQX›ƒFQ„>ČŖH.}"7Beꃕũ?¤ cVŖ×ėŦĩxöZ#l^¯Š6؝8‹Q&Ëâ1Rä6PäÔSdOĢ ņ(ËbĶQŽ"ÁBũ¯IS›¯ĻČÆség€ōUĖNŊÅîãWųj™Ú`Ŧąķ(‰ŨĀGģ­if…é[^nTŒŨF_EÕd…CAfĩZ¨lˇ•–˜Ę…ŋß8įÁ7¤7ęí”ySˇÁ`_ŸëĖ<ėŽMõGöāáĢ7ąÆŋ}čēĒōĀēꉋĸįƒëĸ—]ųfuČŠÛˇķĘˇĒƒÎ pm ĮIņ˙]ü‘tŅÖ.“ŌŨø1Ļ7ĘAŠrĮÜÍĻfˇ¨ŌībO€7ÄT­VoøŖJÉxŒjL(“1üŖ2åe+LnË.v˜vD—<2•Rëd–ŨėF0‚ŊSÃ%F#^4<΁~‹o%%ˆŠ…ž†S’B;q‚Ô™}0zĒMƒ høūÔū…â"h“¸ÜĢút†ŦWõU°ĩčįē gž¤üņ¯-*ŊS­tŠ?žžj'õá0™íbŅDšĶ¨×)UD‰ÍD‰Ã´–"đĶNZ.åš3ʆë¯Iģ%ë–ė[rĪ~FLyˆ)CXmĶÁ3?Ǡ͍ąąaø™m§9Ūo6S†lƒ9¸Rœ'h%ZVt)2—cnk†^ãtĨ‹cLH_ŊŠĐTkLNOʑkk눒GĢįÉßJ­(‰”€’X|FZSÂ=<î—gĪqyĖô;ÜKFaYâ¨xĄø4TÂŅØUų,„Z‚ĖfĩM´ĪąĖļÍ+XdYl[äØnWWx°4Å>ĨüRûÜōžõ…ęŌbŊßD”i6{E‰?äÕë@0iBÛ#ĻĖ Í*Ņ›ŠDQĨ…•ípØ5ÄÖûŠ ‹k‹Åbįā[¯&äĨŊä>ęéáæJ˛Wŋõ čĢâ˙_ŸËá_×ņ叞ŪÔr÷žø—ĶĻ[mF{áÔļ{øæģ&mž…UD˙ąP\åhŠšĻûʗÉŖ&-b2Ĩk‚_y(”îTōîÄžmzo-Ocyzg­|"cé*‹Ë1›Ķ]eœ Ŧ8R^ž^•š†\– ‡Ķķ}Ũ8?u1 kBaW9„3ŊĶ(ƒaŊŋņ$<ĖS'„A…ÍĒMǃĒcĒ“*™Ē<.€|C>Ë˜-3“–šW5Î\húÆt’‹æ  “ĸuĸ'ō\OÔĐÖqⱏ´]÷{¤/)¤ûÚEû2ŧî{žP1émčĢįf„LHy¯Ŗ™ōRc¨×š$=ŌōĀŲ>ŠœĀná<;3ƒŗĸŖCZ‹yMĪã˜īHZ+û~”üŅíÜØ_đņŖŧæÍøČéRË×<žN\猏´¸TŠiMŋĩm)ÜfÛ[(Ē ŨėҘF1ętcēÚåįå4C:ĻGééū@ē+¯DĒ"°0§´°°¤4=/:”WôĩäŠÔFękk‡Ö§GU‚4”<ÂärXë4Ke[ŽÉf3›Ō‘Li}6fG2˛ŗ33Ō#UåŧĒȟŠ”UV–—ĨW…‚^˛_*gI8//âģ2шËĻ-L´ĒJ­V)KŊeیz˛>e×?SĪÖÔ­gõŨlwĖŨhōFo‹ąĩLÃ2ĻgĶŲB&°=l74Ā|ûā\Iûbt$*éyÎgÚęH;ˆä>‚k!—‡>ŋ m€—ĐvŽĪđK…˙tÕšcH{r&wB!-•ŪRk‹QTHKæš43(ŠHÎ]ĀX:Ā3éĒ^ÛDäskΚB¸šįI°âG$)ķš\žIōÆōy]NßŋxMŲôŪ>Nß"6(îíõm’â&‰Ü…¸Ŋ7ÆÖÛN2ˇ“4ŗ…4s|Ë­.ŊĐ=Ļ´­ôJÛ-ļ[]ˇģW^?T}ŋŠŽŨåûī‰ē? }×ū™ũ{ģ‚|ÄēĖŽAŨä"ĩFb9ÕU.‡^fŦH+) e\;5Îp4ZfĖŦ'ũ\°*Ģ,3PO*:K”tEætīB/ķēš,™ąâp(Ģ[˜ŗ,gMÎÃ9ĪäČrœŋŲ…žží…d0 '’ûŪ¤æîUŨ=ÆÁ…)õmOî‚“ĒœÖ6wÛ Ŗí\­ĖĢÅ˸g2ŦÖÔē.K9á¤Ē˓^xjû,ܝ$ ÉޞG~ŊęŅ‚ Û/Ū\7Šõŗ?t3'h˛e÷C=×ÔXôĀÛS§žķt§XãáŦyĪËõ-kf”Œ+õ=éY+§­}ũö"ŪôwŽÃ§Ū˙ĐüĄ—x­ŽĐđá+~ũGî“Cŧ[ÄĪÉiŠ…IUPĸaJŲwˆ%JÚ 4f¨ ę"uŗZP;MsžčU”üĻļWŋAŸž3÷ W¯Į<ū IlŽ%dqŨO{ųe_pŨ5ƒš˜˙\ “Jĩ’w“ŧ÷™Ū{Ÿ{gŲYmšl^OyäĮxúŒŦŽëƟ$š¤Gõšâ!ō^tÛSP#&wã}ON?"!Ūæs:{ôy ֒LO–C6 ™ąąO*õ=Y „™ž*q‰ųJ×îå–Žu–{]››,ēļîPėIÛjŲîÚé}=íTąUNĖEáAã=.vmÁʂ OĻm.xąøŨâO‹•ŲÁnļ%æĘ, dfÁlSēŲž3(ƒrP(ÕĒōuãąØdŧ-ÔĨAŖ @ž!oQž—SĨÕf[6é Ū ŋ?ĶŲjõ, ÔÆĻ<Ø8P\•ö5E9o_(XžW~T.ʝšģĪē1Õķ_I’žŪBKĢ-PžÚđ…#múNŸ’ÎN¯ä aŠv´ÜZž;x˙“ĮšmĖōå3ˇúUģ:mÖÆæ‡ģqĨöbÕ¯/xî’ŅW^ž`÷ŦĢ\ŋđšgõ†Û/Ŧv˜ŒjŊ+÷7ŗzqyÁß cĒÆ]8wŌtîųæī'‘Ôz 3ļę˜WCĄÅ`0[‚:—ÍÎBĢĶiŗ=^…€XÛĻéÆY;•?@nũŦXŽāĄ BĨIč‰ōLîĘ ­ßj‰éU´É_h9j,ΜiwöggÂqICIú)ę4w‚rw7&DūĶvvd§6ÅŒØøKUX¤)Ę–=1{vöÁĮ2žÃš=ŪgŗöË^W(ËžPmb1–ČĒ5õ8Fsw"Nĩ)Ú4ŗņbŲ|ÍRv­úZīÕžÛŊģ|ĪwdÚ°;q˛KcČîN|ąÕkKnöÚ°ŖÄ# ߔëĀĐ9Ļ Ë’š’3 s8܍ōø?wšûÅ~>ĪCŽ[÷!ņķžw^Š˙§ũņ“/=&m­k¤Íá+˙Ī˙F}Ca“Qī´¸Ĩ}ƒĶcOZ%¸ÍČQhļ‰rĪī끐&­JŲēɇk}û|ĖįĘŗœɛ¨ûÂkÃBØŲßĪ´Ú¤ D[Į¨SÉÍ7…ãŠíÃŲ­CŌuĐΉδÕî5Aōū&ČÖG By턚:Di7 æžŨzīə¯ŗŌŽzÎ#¯ŧ?š×_tžJūčsŲŨÖq˙#‡†7[ėÆČĐËPxųu”v7ĩ7ķ7”ˆŪ?{AîÜ\Ļd|¤ e…ŌĄDPéuđ*ƒģĐîv;ėA¯ÚĖVĩŠilËŊi9øƒ‹´‹‚öĮh÷ŠüËųģ;ˆŽŧĖĀrēqõļHîō$‘ ?t¤čC.ģ!*m˛Č§ī)ž~Ų +.ŲiK-‚miJ“’Ģ˜ŗëb'äŌĖoÉâ'—áÄįÛBĘ gŸŽÂ^5’ôQę¤ÂŪ+Ęæ~ĸ,˛¤ŠY÷ņåo_}õۋÜ'•Ŋī}īŋßŊī‹ŸŸ^ĀuËī_šúؕWŊæü0)ɛŽŲÄ%™Árĸm!I˛üp06Om[oe%l(Įfą—ØKæ×œš>tq˙ÍņŠī'›ÎéÉõ”ąJī÷…žŠîÉž…îųžÜĢŨë=ëŊĪÉôKmģ<û…ũĻW=¯zåʍ.ŋŸ1í­:sšÁa(úūÆ÷ãGQ˙Ę[¨žä|÷îģšđ7ŧ\SĒw†’Ič~õ9Ō˙¸qՖ§îāŪÍaÚéL&É,ƒ×c™1mŗlšėfíÅ›´]Úí‘?EEÔvĨ^Ĩ}Å`ĒĘ  ‹ģ™ø,@°€nŒÅ\H’›‘„Ėļœ@:€Éī,ČwČUJud1ĻyčwDķۘŽĐŗ.˛´ŠVgųŌøFōŠm”´Œ>“žĄDųŗĢžã’ĩ„ūS[wĻūęXZnÄM ÍķAĝãCˆ`äÆąíÜŗČŗęųKīS#.ˆÖŪ‡…(éŅž…<~ũY?ûԝWŪZjuX”æûįūęJŧ]R´ēžaŊŽ;ÛÉåqŲĨmJ›Édėķ—InŅvŅvŅļ'Åō×ģNû™ˆVœ-_*_‹÷°Mø(ëÄmLũ˜üqÅvŲÅKŠ÷G] —Ōh—ô€Ūâŗ0ËT‡Åbw9…’͛Z”—WXĖ1¨“úC‡ēŠ*N­ ’ū&sjĘĒ,áåPyaqyyIq°ũ9ž€˜“MĸP ĸ VĒüÎŖ$ŊķHL3ūâŊEŠXQ7~šmđ°ŊZDÚęKJŠé1‘ņČģ÷kK\n™Bžé–9}čRx’|ä§Ą#;M}Gž8ĩïõY’&ŗ5š›0Jg g›>~'Å/íüp\ķŨSfŪ>uyŦžø7\įLģyéÔēÂųũO$q czzŌ°Æ5czūŲĮtaĘ5ųū+{žę;ēŽIōœÁķÄs›Ėų=ËbšAg‰3æįœå\âüĩSaÖZ,äũČĩĒ™,¨ĩyœ÷ZÉû^dŨxĪŗšNĢ܍ĶézFÎkš(ĘüÖ1´8ĶĮ.;ģ¯0ôHŧˆÖūpâœíÅŲÕAĢĀ*7˙ÛŠ|ŠlíuËpŸxCÚŒøŪëvųdÆ>ˆ=ķ]?ų& ČĨy7ÍĖBŌė€ļXŲLëbëÍV4C × ¤ Z¸09Ŧ÷AĐōô †1†ŊÁātöĮ^zŦđËX˙"Æw Ä÷;Žo¯čĪ+áē—lB“Įĸú }eÚ`ũ}T_­éëõ*SX;HģŨŨ•'fá d<33=KK<˛AŠOŖĸŅ3A!+RVTKkėčŌT3dHuM°ÂĒįU^ŋ ›MMĮL'M"˜ ϘI05Ĩ™Lú´ 5Ķ'-_‚,Øä }Ū`æ ĸdeŠĄ”•6––5Åx圪õXßT[_Ģ æĘŊá‚üėtšą*h’įW@ĨƒefZÕē4ŋŨķ•Ų–Û˜íL8ŨëĪ ķrxy˜…ĪÔ@Ąŋļ†oW fóĄÆ9,÷iG?Θ2‘h_ŌwœÃˇđĩŅŊ{wĶ`ø˙q.ĶÖÖņŗKܟãpĒĩĸL“™#fųP&wĒí>Ė–åúĐĄuņ%OkŪå˞?iŖĩīN­ũ:5¨_ƒH H|HŠāCĀÄ;)3؁Ō!"ąo›ÍU#œRJNöž.J“÷ÚĖViO$ų!gĩEȘ| 2°ÜOmœë…˙ũ˛ųu3•‹‡L4l— ŖK .Žk’˛cŠķķĒëĨęOx”ė!˜°¸ąŠŠąęÂÉ=;¸¤˛ûcãįôŧ#åīǟ”ž3;Y8kNH‚į“O" ŽÄųąŠwåī*Ų~ų~%{DŲ%īR Šå 6K1[9Û-lp?&g×úļáv&x|—ú Č˜—ÖbԊX}VfmrZ­gĐtŽ1j’V$ ͚ÔiiuИ´"Č4d˛sL‰Žŧ)iJJĒ*å¸ Ÿ|Rsz@TU1ŅvPĨöģŽ:ŅÉ ŠA2(k‹6‘AqrkrV}ĨlIRđzN‘gô~øŗbfq{dJ…RŽdrŒ¤Ę­LOZ“\ɚôJT—ĪBō×­nKR†:øŦŖ­T栔đo"0PTūÍ Ljšŗĩ}Lå‰és=ÕtĶ‚‹ŽéčoORąŦĩ!ĮģꂞoÎړÖkëWô|{Ž0¸‹|Û(Iė8æ€Īė‹ŸDã^ūŌÉÉúëŅßlõŠTf}Pü™üSĩŌ)ä ×*V 녧šÍ#-KgaēĶéIڒVFcze€IŠ ĻŦ ęr õUVO jZa@ŖÎ āŨĸ|U™ōp@¯DĨĢ4Ōü^}ú˜ôéé ĶÅtgI˙#ž ŖŊ$'ĸĩĮĨcĒä͎˙έ;ᤤ+[[´‹¸žO\hu~ä\/NüĪÖŦPĪ%Žķ ¸5åĩIGŋ¸ĸœxŒ|jéuYīyūãÕÉÃŌ…ũ>~ķÎëZ˙!aæú)S—¸|G<ņ\\ž< &ŋ Jz :īŽ×Ūuđ@ōíq˛p%ģ fš. ķTcԗšŽ6ŨnēOūŗÂ“t¤}¯„|ž`(čq[wą-ā ŨŽĘâpX-A7<ŗ%6&{´ôl&ҤY¤÷™e šÁ’fPgdVADŽŽ5r´VšƒU[­WœT0…+,ū }¨9´<´6´)t2$9ķzî<Ģ G>k#õ(=V—^*č‰Ļžˆ&­ķā˙đ>ÆŋķȘâŅŗ%ÍfōôšÍúŸÄ+~ißÄØŖ4ŽŧŅiV§™CeΊ {q‰ä‹-ā:^ßĀcaæĄ{&Ėq™ÉÍ šZ6ĮË$ú›Œvļ§×/;8*ÄiÍ4āˇąÛ,ĩž:fēZa^ÃSū§*~[ų†ųÕĄ5ŋg{¯æŖĄ_š—ũ}čķŠ˛‡š4fšMVŖę3[mÖ÷ĐUÁ{Ëvë5“Ė“+įU^ZuMå UˇWŪ^õ˜ĨËĸžŗj‡UFrBáâXu´ĖåЧ)ŦÚÁPVR éĶ´‚ŖŗĒē:` ÔĢģą|ģā/Ā‚nŧ/æ   J1ap`Œ—?ŧŽĻâņĄĒk ÆõĻ4dŦuaæ8ë‚<ŦhĻĨÎ[¤g!Šė0ÂßŦë{JÂ9ŲvöÉā~I’įUƒMÉ´*+†šüžLsĻŊÆęƒ*÷`Vø)2 Ĩĸ­ÖáģŖĻzHz”Œ›Ģ*ZéäKQr ¸9MFŠ7$Ŗ×ËūíU–2ĩgOâs°'ž‚†ÄW]5– RŦÛ‚ļ¨§ōė*m렝•äSU’ÖU‘#YeĄ¨’ë`‡ÁJ%ЏŌm°šm°hôĩ>Q†wzŽ› ú)]Ō÷?÷8ŪjąÛú=úąČû=úámɇ?YáŒpęŅĪu|;âÎã*ĮŨzĮčĒĻĸ[ži˜1ũ­—_^ϴ椇?N{hũÂG7ųļ ŨŊEˆ¤“¨ŽõēlÎhVåāHy4ÛŖ7;B× ŋė÷s‚–4—÷i’_k¯¨öš†Ņ……ū˛šŅų˸ŧŽ#û[%ރ¯W¯TUôrK@Đøũv÷ĩäĀīÂ7á3ø”Z6ßá=؏&x>G3ž‰•0æĀ=ät#L‚eđ´Āð.Ŗ+6“sąPsaA ė„5pĐîšaŧËĒáoåīŨâX ųtÅ tÅa¸&Āķ°ö6VڍßEmËŠõŦƒ)P•t×{áŪËĸxõ1Jŋūn‘îtt6lĻë’aW*đŅzÔT8ƒc ‹ë` .”°–Č‚ģą–îc"\ĐH3á‚ÉĐI~ë ø=|ŒŲ†jšÍ"ø ŋ¤yŪ[ —‹hfËč:ŽĶ\í#ŋĨų„=˜Iãl Ėgåpi`†ĶDÉŖąøīÅ9´õ’h  1y؅Qēg‡0Ā­dĒđQo"Ũs'Qæ]8Áĸ‰¸‘Fŋ—î—OÜKÃ+pÎJqœķåz“÷^FķäpCâSļŸîšV‚‡¨ÜCw_.Árš ˆnæÕZč:|œ5Ä9,Ŗō_Ÿī@7<áÚħhĸ|0ŧžx O­ÖÃZ–.‰z:Kįqz?x=ĩōŪŌį—ōŋüa—ôf¤HÂâw˜6ßüŦˇēi–Œæ÷0ę om™˙Á `7ĩ1œ‡ķ` ɧQ/åzŠ”¤Ôõ}pÉîePCt~~l&yn%‰^×GĪåüœ‰č )š&éyM-{!“äķô°tI\3,ĸUÉë{ÚIžĸpa¯Ĩ~p3%ÉĮnTB,q†¤Ŧ.ņä&ÁwŌJCw|WZĨ­D žFī&ŪÎ&šŲO8Ėĸ;¤C”ZgÃLâÚ*Ü “P„&œĢ`Ķ“¤ÔÁx„ûk„÷$âa#,ÅlĘŨE°T’äevJrŧB4O#\IŪÜl Ž-F@Kâ4\ŲޤÂ(‰Å2Â"OÂŖr@¤Āy7‰¤ÛFøŽ%Ú]Kr5™R •†P¸ JÁG×ßEĀ5Éã„˙•4ĪQĐ #iôĮá&Ȁ›éĒ;éjŽOž'°J_ĮŽĸ+.Ŗ;?@+ŧæ˛Lā,ŸŖđ>@š‘,ƒ "Š~€E…UĀĄ„‡Đ Ā&ŧ/ îÎÅÅÄĢí°´Æ ZCųīā'ø+ü^„§ā ØD\^A­{áŸÄßŋS˙{%ųÜGm;%8(…Ū‘įĻ=;î iL>bßxx%qd;Õ<Åęq5ļcžŒ/Ãiū“6<‚÷ÁG^ÏđœMší.ÃņXä7cÜGŊ?c#đmüę0 ÄŲŗëī5&0dūÅ͸ĮQŨFœ‰í${™R ČĨžƒÖåųÚâ5ūy’4åI¸Ÿā$õzˆÖ„ëédũũx3žK˜˙_ŖūéćH_ڛ˙ŋđ!Ü7JĀBĢ\ ¯…î'É߇{đG OIYP>5?|Ũ7×ŪēÔ\˙-}ĮrhĀAž¤M_zîG›ĸO*Eņˇ_ÚK[’Ū÷¤t;­wŪŽ„)íÂ.Š>NRÍË˙ \ų‡æ#ÍåI¸B*_Bkô&ø-l$MBœÄm’ ˜E>"ŲБ*pIátN—äp…ø|ˆ?ŋø˙/pEz“pXOĢv?žŽûHžH’ģ#´28 „ëáéú‡ņy| ˙,­ņˆ˛Ĩč û‰ũËgÃPęMĐg?˙[čo;~>%­ÄmŸÉ˙ œk9úÃ,ÉīHĮßãŽÁBōųŋ' ]HúŲBzô* .Ŗ0“ŽįĐL’Cēõ2˛bC įš’%K~ũ—°Ÿč9­4 ސ[% 6aOdq9ŊĘ_˙gPŠSpNû΃æXtˇœũO˙=×ũg0YRpg ŪJ‚…´™ü8;­_g €‹æį~# éûŧž_@æ›AûĀĖ'˛.NBö_iKcæŅ ĄhŨy8įá<œ‡ķpÎÃy8įá<œ‡ķpÎÃy8˙ĪČ„ ārB€đz  ¸3ąOØ×5Ą4ÖMÉ)Ų––Q˛œ§”vŠJkë …}°ˆā‚"L§xYĒFÅĩŧvÔžIØ ûđš]Tŗ‹jvQÍ.ĒŠē…į„gģ2|tëíۜ%ßÔš„m `Â]Â*ĐØĶRéôTē†Ō\J×ĻŌ;„U]U>}JúÓo(N0šÛÆŽacJvJ™Š¨”ŲĐ[ŗaÕøęœÂFÂj#aĩ‘°ÚHX}C1Ō¨¨~Õo ú Rũ@i¨@Nj¨Tfc—Ū–ĒĄLZh&B Ņ’J' ģJ|{ëڅ 4ô3RŧIOņ)ž.Åc¤x™ÔēLĘ/”ō Ĩ|­”¯Måy\Ø/öIąžĮÂ8á"ČĄšąÂ)m!“Ō1Tæéhá)% “Ō ŠŪAéHęgĸt„Đ$•/ rĨÊĖĶaBSWƒ¯¨n•§SŖûņúÂĄpj "ņš5›ŽJ5Ķ)^Fp€@zĸĐ@ĄžBPGWÄhŒĩÄ@bj)Ô5ÔRM}Ģ)Ž QiŽQęĨ;E‰VQ9Jė‰{ĸ ĸû…r("ˆ4´Čhœ<ē.đĘŖ;ä ųAcØj°PęOĨ>ļ ŧ”zŲĒ.¯/V§bÛĄ™ `ÁrļŊKfŌ×Y¨ī[H0†`:Á2‚‡ ž!PBm˛%ĻaĩŦVÃÆ"IwÎļh´DJK%SOz2ÕēJôu— 9DĻx˜@ ”såšjoÉGĀHt˛`/Á‚ŖœāYDŒ,"FM0‹ŽĪ’zÉĨ~ß$ĸ,`™tĩ °ß(ŧ6›j˛Š”M×dSßlĒ=J1JWđöf‚5{SmAI˜ƒ’piŦ a[Hq­”ĶSė‚]LĨī&úâ}]Ņ} 5˛;ˆšwŨîāÂø".¤–ÚT5ĪȄr(dQČϤ ā§@ŧÄŊĩÖP¸“ÂVSXEܰ<ŲaĶË–/+_Sūpų3å{ËģŲ íŦ=Ļ› LFĨĢÎĀD˜ :üIŠŸ–âËĨ8&Åö˜kĒîøTŨ+Suë§ęîĒk™Ē=U×4UW8U׍3cöˆîŖˆnmD71ĸŅ•GtĨ]NDWgÄVœ:øŖ•â)Jq:NęԁjN€’$ŗļnô}čąËws [IÉMÉŌ”dRÅ+Ÿõ.ņå%kÂÉ$#đŧH#Ā| ‰å)^ULWăŠ|Eļ"KRøĨIiPĻ)ĩJĩRŠ”+E%S‚ŌŌ8‹đ7Ē,rOä"E)o`> endobj 39 0 obj <>/W [3[600]10[600 600 600]14[600 600 600 600]19[600 600 600]29[600]31[600 600]38[600]40[600]43[600]54[600]62[600]64[600]66[600]68[600 600 600 600 600 600]75[600 600]79[600 600 600 600 600]85[600 600 600 600]92[600]]/Type/Font/Subtype/CIDFontType2/FontDescriptor 38 0 R/DW 1000/CIDToGIDMap/Identity>> endobj 40 0 obj <>stream xœ]“K‹Û@„īúsLČAŌŧdƒéK–€y;a¯Ōhdë‘Ë˙ûŒģ’^ˆĀ¨¤ötuĢęĪĮ—cš7U˙ČK8ÅMMssŧ-÷ĸâeNUĢÕ8‡íī3\ûĩĒKņéqÛâõ˜ĻĨ:Tũŗ<ŧmųĄ>œĪ¯ŸšUũ=1ĪéRĢũ.Ę鞎oņĶϚŠHq*õĩ_ŋõרj.|Ī5*Í÷-:ËokbîĶ%V‡Ļ\tøR.Ēb˙{lö¨Ļ÷× uC,õ$Ô¤„z)Pī!EęŌDBXjęRKB!qG A_­%Ąi!9 i$Ąé!ņņ Aē!ĄAړĐH;Zi ĄÅ$ —€…†g:LÂōY Ã‰V“ĐMØčáŅ:zx´h’éqĸíHč-¤ =ēˇ{z V˜†,/ôX­åá#t°Âô0äxƒ ĮÜ1=<:CÂĢuėėāŅažĖģ;xtėėāŅqß`ˇįüû۟‰x†Uî9—ėqĸ9aĪlÍ)Jč×e}VŠōĢūf+z endstream endobj 5 0 obj <> endobj 15 0 obj <> endobj 41 0 obj <> endobj 42 0 obj <> endobj 43 0 obj <>stream application/pdf iText 2.1.6 by 1T3XT 2012-09-11T07:50:26-07:002012-09-11T07:50:26-07:00Documill Publishor 6.3.12 by Documill (http://www.documill.com/) endstream endobj 44 0 obj <> endobj 45 0 obj <> endobj xref 0 46 0000000000 65535 f 0000004262 00000 n 0000028556 00000 n 0000043415 00000 n 0000072092 00000 n 0000087760 00000 n 0000000015 00000 n 0000000145 00000 n 0000000275 00000 n 0000000405 00000 n 0000000535 00000 n 0000000666 00000 n 0000000841 00000 n 0000001017 00000 n 0000001193 00000 n 0000087896 00000 n 0000006846 00000 n 0000004512 00000 n 0000008337 00000 n 0000007028 00000 n 0000008510 00000 n 0000008538 00000 n 0000008567 00000 n 0000008596 00000 n 0000008624 00000 n 0000008652 00000 n 0000027538 00000 n 0000027729 00000 n 0000028094 00000 n 0000028690 00000 n 0000042462 00000 n 0000042652 00000 n 0000042995 00000 n 0000043551 00000 n 0000070820 00000 n 0000071002 00000 n 0000071489 00000 n 0000072221 00000 n 0000086658 00000 n 0000086845 00000 n 0000087259 00000 n 0000087974 00000 n 0000088118 00000 n 0000088152 00000 n 0000091002 00000 n 0000091078 00000 n trailer <<1e60be3dfa88b87766acc38ba0b468ad>]/Info 45 0 R/Size 46>> startxref 91277 %%EOF trillian-1.6.1/docs/papers/VerifiableDataStructures.pdf000066400000000000000000015605451466362047600232560ustar00rootroot00000000000000%PDF-1.4 %ŋ÷ĸū 30 0 obj << /Linearized 1 /L 450917 /H [ 855 223 ] /O 33 /E 65118 /N 7 /T 450198 >> endobj xref 30 21 0000000015 00000 n 0000000804 00000 n 0000000855 00000 n 0000001078 00000 n 0000001930 00000 n 0000013075 00000 n 0000013171 00000 n 0000013308 00000 n 0000013452 00000 n 0000013589 00000 n 0000014291 00000 n 0000014700 00000 n 0000015108 00000 n 0000015484 00000 n 0000015697 00000 n 0000015992 00000 n 0000016221 00000 n 0000042529 00000 n 0000042767 00000 n 0000058598 00000 n 0000058823 00000 n trailer << /Root 31 0 R /Size 51 /Prev 450189 /ID [<3625d229fd65c2e4b180a4f7628da041><3625d229fd65c2e4b180a4f7628da041>] >> startxref 0 %%EOF 31 0 obj << /Pages 29 0 R /Type /Catalog >> endobj 32 0 obj << /Filter /FlateDecode /S 113 /Length 143 >> stream xœc```b``g`e``ÜÅ Ä`6+H”Ĩņˆ‚ƒôôÃ[Ĩŧæl`ŋȚu¨ëA˛ÎSÉ+sr”',ŗZēq]’âŒ7mÉIú”ęöļ¸ËöĸëCBPːĀĀĪúbÍ ŠØ¸õ-™ X˜WđėœÉān[ÎȔp¨úk*N endstream endobj 33 0 obj << /Annots [ << /A << /S /URI /Type /Action /URI (mailto:eijdenberg@google.com) >> /Border [ 0 0 0 ] /Rect [ 162.75 667.5 282.75 680.25 ] /Subtype /Link /Type /Annot >> << /A << /S /URI /Type /Action /URI (mailto:benl@google.com) >> /Border [ 0 0 0 ] /Rect [ 131.25 652.5 221.25 665.25 ] /Subtype /Link /Type /Annot >> << /A << /S /URI /Type /Action /URI (mailto:al@google.com) >> /Border [ 0 0 0 ] /Rect [ 120.75 637.5 198.75 650.25 ] /Subtype /Link /Type /Annot >> << /A << /S /URI /Type /Action /URI (https://tools.ietf.org/html/rfc6962) >> /Border [ 0 0 0 ] /Rect [ 384 156 432 168.75 ] /Subtype /Link /Type /Annot >> ] /Contents 34 0 R /MediaBox [ 0 0 612 792 ] /Parent 29 0 R /Resources << /ExtGState << /G0 35 0 R >> /Font << /F0 36 0 R /F1 37 0 R /F2 38 0 R >> /ProcSets [ /PDF /Text /ImageB /ImageC /ImageI ] >> /Type /Page >> endobj 34 0 obj << /Filter /FlateDecode /Length 11071 >> stream xœ­]éĘe;nũ_OQ/ō!'\Č„tC tŪĸå3lË:^ßW!}éîK•Îļ,˚%ĮŸA˙ųģ¨˙ĶFúųo˙ųãŋāOjLķūöī?ūõįá¯V&ėû_6ūÄ?˙ōįŸųÛ_üũŸÃĪŋūĪüDõg Ĩây˙Iˏ¯ūå#ˍķF 7LĀŅ~÷ۏŋ˙Sø™åĒĩļŸŋũåG|mAsøųÛūø‡ōøĮŸŋũĮũ—1Ž+Æ" ˆô $÷+wķ•R6×ČÁ|å÷HiWMcŦ ;.ĩ]ŠôžŌäĨ¯˛A šRNiŲ°M1_%Ôŧ‚üiIņJĄ B–$á’“7€ŽúJˇÔ6Vޘ-&Û~’îGĸ!mŲžĸ„ŋô`ÉGrĘWŽ9lsNא¸n(× ¤ÔKO •ZŽÚ Ũv^ÉMO9). iķ¨z@Á톋čöÔÁEtĶRZ! ‰¤ĢåØēR”ŸZ*„¤)?I5äî{\Ėøņbʈū@^? zŠ>ßee—'7íŦĘÕZYA¤í×=_c˙Ųŋ’ŌZë H ģDPšôē.´ŗĶĨtwš"]™_Ԋ‹ņĘ#ää;ˆ’Ija›ŽzĘŖē돴 ÜßW甏ƒŒ+ŒąŌe,ąC€…Á咰îyg¸‚7™#ú§¤_’s'ôO1é*ëWÂī ƒĨ –bSa_eũ]|ũ.\đáĘúũŋ>UûÃ[}s'ZSšJn…pFJ]Ī4WÂĒ.Õ1pd¸Ō›X0r"Œ‘ ¤UŽ„1RUBËęû:-]=aœĄ’ŗš+åŖ—Ģįļĸ’v9¯ėÕĸŲĐū•ŦėĨˇ{Ŋu%m zˆ!šr zˆ‘ŖĢ ŖjiV˙ĶĸÄm…Pššš/1î`ķĒ-&C8ˇŸ EĐ '„oܨÜęe%Ë}Ą–ģ ˙-jŠéGa1R#īŧJ5"á÷ßP(šG/EžÖAũÍ^ģ|ë ~oÔOWęÖAũ$ābŌ›ˇûÆe㨊"Û2”Žå ņ‘Ž×ôę’×#ųéVđĒQ}HgIO´ ÂS7Ž•sœáÔ+æ¸v ˜"´g\ŨÉQeŽŌĶJę]$eŽ–ŗ0eŽŪĨ°…ÔĀ Ų^—}G0@Gd”Kjā¨)ģRnIšĢč}Yŋ˛É¤fŦž"ÛP8Dsž#áä`đģ9pa%f„ļUWø˙"?-ķ˜ĖŊüŽ TNûĘŋAöCAÉŦ‡zƒ¸ÛŽb2†Üօę.ŗëGœôĘQ ‚WŨ:Îå1ruī{^AÜå}ķËWœ˜Ėj3ŨsQŗZlų–ˆĢʘĩÉúģ˙gįWpēDÅŗōoaĮŽW,71gē“ wõ!ŌJ‚]hDu˙BëũĻQ1X„:-ÔHÖIIé˙ŧĄ”D÷œ…pĖpĩW\œpR#O =Cš]8‰˛ēúylG*KģrĐ ō-餆Ūáō‘y–sWéŌ5}Ē˙‹pú°Ęˇ„SūÂ|-œŌáņø yũ"īÆX€2ÔŋžAvŌĮe¨Î˲÷`ŌÕ\hTíeÅÅE`¯Ĩŧ.ä, 5ØJēŦąTĖBûWĒō[¨iqÂĒĒ;‡ā‰ûW ÛŌXIŨ}Wè‡uGŅ…4Õ0ĘqŨQėß8Uĩ–<ú7먍ķ‰Ôb~‰ĻŨˆUÛJ áėvUß)ĢÅŊ€ėēDŨšĢŲŠĻĻU‰Ų,´3œ •*koõQ҆[žâĸ>]i¯fĮō•]Å*ísŦ…íh åĻdpŲŖjTQšŪ„­ÔņēüĒŲáwå ×įmßo˜:˙)¨Qqƒ8˛Å #m, ŽlOËdøŨÁ2šA| ņi™,¨8>PŠZSZÚĪË2! UQЅËWvÍ ¯ĒT]@>ųx0b .3 ČĮH¤^v‚ŽJ•#+ˆģBm¨3)‘-¤†† ĪĖ6=ĸ:ĐQØĻ VÉBj’ĢĨŅۊîŽŨŖÚcšv˛iU ČĻt IUL˛|éHU1ĶÁ¯Sg~ũÆ4Ė]ˇ,ƒ֊­;_"X+ĨWJe¨ƒ™āĸ 5:'œĒ)]Ŋ‘CLĘs9 %œŠJ¤8Č:=]%dĮ1W4Lé˛]qÅDh›•úhƒė'Ģłœæ™UtˇĘŲ%’Ę ĄĄšČ夒ŋ‹Ē ÄŲKÍdĪ*›Žš0\JD6åĖ aŨšY™%÷Ø]”JNŅ_ĨĀä'ôWfŠuPt•[šsî+]Å˙h†q]ĸP…‚XĄ°' Cŧ¤Õ•]v÷CT;éƒŖ¨ŋ^U-. ģu„Œd–tûB)"ÔAJĐyeŌI‚7n/ĀvŌ’ĄķĄœHŊŌLŠ2˛L$G¤ęãĒą'ļgu ”üL JE(f†nCø0 áKQĻë-G†KođÁ[h”Kõ+EWíŊ¤Ō•0LŒĩŗÔrdgTĻÅÄeQ÷ģ„Ä —’ÚՊ=ĸÍä+īMöČŖÂ€ėV–5Õļ‹X`AYƒog§ĸ ¯Jü Ē6v„T4*<î;VÖŦĄ° ¡Ŧ˜ûQļđHé“ôßŽĒŽĒŊ‚mUͧgŖYwVŠQą†ávâĒe cŽiĸnjŖU3qĢÚäąÛģēã’ĢܖtûŽ”YRĸŧPaüˆŊížK-,¯ō–Ųģ›ō mÜ >[õ mÜ ÎØŽĒâēŽwzŸîP9ą>ŌYC šnŨyĐĶ ąFö•<‹UĖ~ö-Kŧjë~|RL͚Ũ|ŪPAųM.Œpˆœ5YÉâ#(ĘLĶĒŧOČUAI+]œ;ŠĢÚrfčž}ŨŨj3ŽjÎųCP5K+„’Ú )uvFŧŠå9gÕĢā/Ķô$_Q[:&!ôjÎČyęa§Ų%UšfÆŨˇˇs&ŨÛÛ9/¤úĄN?åŧéˇCtŪ‘˛TB*Œ€ \ĢEÂQČs5Ëtޏđe,Ķš åˍévBSE‡bVŽčĪLw;3Į#ĘČĢĩÆø2ĢqZŗŲŅ.PŌŅ‚Ũ‘ûŠQÂ6­*$u#—ŊK¤N¸„Æv¤GQŠÉHW/ˌ]”ŗÕތ+ģėļOށëNN:7h‹Yhŗ8r‡ėē顂av˛Z§*™"ŲŅUëĨ“glESāk+Ã%Á ą?í 3Ã$”¨)0Ū†ŗšU4ģÉŽūŗŪDÆ ’› 1CZį*?UûGí)œĄT5,ƒUz˛ƒ(õ‹Åe§KSĢžs\ē@ēi姒Œęt‘Š(ŊCFŅ8o§Šĸ限ލŌëŊ2cŽĖ(]ȎŠōKČÔę( Ƨa÷å—Tėüq÷e’*šČÄB‘ eŠŨÜų0.w>1áRŠ€ũŠÃ9¤˜"9ÆŌ ,ēcQe”ėŪR×w4#čÜB*zIôî´0fPÛR%Ē嗴ƒŒ+JcVGU~Q‡iŊĒ*-ĒÁH6]Õ¨V˃™Z5‡+§Ė.‰ĘūKš0 Ē”‹ÕĀģ+ (›ÎmZ- ‚„"AWYĒNˇôhČĒtõ™OSÕ¨%ą [UŠ1N7­"hsŋCčĄef؜čĒ•cb–yS $ÕIßrÛ§ûûĩŗØŪ‹ėۊâ üÚvb•¨ļ}0ß .=<_ÃŨ {ÚEŊEEžŦ ŸÜÅé ß ģŖ_päļ€|,Ō¯eŲåiĩĘŦŸvâĻé Ɗí.Ná Æš;ŖRU´KYA|ōL=‘¸"벁­)§tsBŽQOhFŒĪ¨ôĒw0[ūĐdTĻ4=e¨=!†pŪYTG$Ę97#ĒQŪč9à Áœŗ/–%ƎĖũNŒ?’aōĩȰTāô•ũ] b3ÜļËŧxyĸÎzĖ„¸pcĖô+UõŗĒŗŨM°Ė´W†ËŪŌ^Ŋ1æž5÷Ņ‘OŠ~îÍWs¯ôˇåŗZ*’a¨Ũ›Qn 3npÜsVnIĶ(Č´¯Ũq’_ˇ2Nõž>"?ãÄŗwZkœØ Ŝ}ÖĨŒŧķZgTŪy­ĕ{‚ŠÁÖu€*ÅVT6ÍŦī ˛ģŗ†~ÕyCzOķô¯Ī”{•ũŪ ž@OeS ‰ĄûÎkŅ}įĩÎ ŊķZgtUōį™b'‘pû!÷Wvŗũí‡ųrf¤ĐÖwF7)ģôŅĄËLZĩ°‚8ŗ=ŖW3vļ#™yĐą.´éxtíĘ žŅUf(%WÂģIEr ŌČ H a0ęvtŽ˜;˛×ÅĄ+¤æHXéĻ8Î3*ļ˚“#åōlaü'ŖO÷āH¸_˛"ĘĨ’_˛r Äęä5#:\æK}ŌP¤3\rSnŠđœ:ęX1ļģM¨7NšŌ.+9ŧ‚!1nķ3rŖĒŅĄÎŽ¨ĩ+Nđ^HAĨ=!uZSŒnĘqŲȧŨĢEĒiDŖˆœÅŅŌ™–™™$ąČ~Č$=‚ÁoW嗆˛SŠ„´3Ų$˛‚¸*ŋ 5SÉe–iė~ˆrSɃŅöQYg¯Đ^ä7+ëŒ qdQ^‘ǐYY×­ ųTYW cūYY;į1Įl÷¤ĸzŦ‚†•3ļĒT/Aô™įfĒŠ‡J¸ŠĻ” ēŪIŠə [*#úĒ6wX”[J4æœ/­‹ę%R=Tƒ…÷BÎIČcUåŠëPҘ „—’ã`*¤Ôü˜Į°|e›ģ1ëī‚°ëŠ„•’<‘k'$…ĘL›ģīL:Da5g^ëėԀ4ŠŲŗĢâC‰ĩg‰^îĖ(|”č&:%zFŠí|YŅÁ;ēU%Bč_‹*+ ¸û+wgxôŋtw$-ō€HWXA\âÕLŊ€ė-ŗąlcqžJ@}把kjj¨=+ˆë”~v5‘ ŠųÚpÉ*(ČÅ%#{FŊmj [x;0ånß÷¤z(ņ ážģ!—uyz;dĄ—ˇŗlhOŠŧŧÄš2Īv$Ō1eŽŦč:oGéoʙŌđvjĖŨwũŨ™tĶÛAwČųįˆĸ\Vt]‡õĢDoYh7ëõ6WËQΕ‘zUĩÄØĻ‘ n‰ĸ[Đé˜ 9€Gˇ‘Ew¯TfH!Q\}î–vęÂ#ʆēžŲØjÎČĨ]ĐLV\|Æ$"ÕɎT‰ÃH—I•*ƒ}Ee‡XĘíØÎNĄ°’eۏN!sGœw ŽŠ4#tŊá†ËĢæPŽc’YeBWÕÔU$GÂQ3ÕŅbb Ąæ -īįÍĸˇ!™Ü´™ )Ą[¯¯’¤—ąîvˆŦ;‚vƒÄŊÕ÷“žsķž­Zš^^ŪEųCėq¨iŧ€¸:ęר9–…ŧû‡o¯jE9¨<”ņú‰S÷đ‚ˆ+tv“¯ 5”Ģif˰B;^Õ d?Īę†e2|V7TÔ­¯č;"”˜ķŲVl Į”Ĩ­DęÜ՛+5’SžC áA- NĪÖĢ ˛įŧ# iQ ŲŅcΟaJ_ÄoN*á…2D- ų Fæļ‚|š(Ũ0Žŗ?0 0[îßc“­^3ãéP|…i”„tb%9F´Ûb˛ÄyĶАE¯ËWœúCūŅRw&ĄDåĒd!”¨^É ČË%Ŗ‘cĖ˅úįŨũ+š_ …ä+‚:Į<ēuŽĨÆTíx!įJPAGî(ėˆ2†"7C9׌ŽÜdøŌ)QT”BÉ‚ĒƒÔëĸŨv´NŅUUÜË(„/% Į;TB9QwZ‰]FQ›Kb`ŧ 4Qņ;š¯()h1ž›U˜čiF‘ži+%]–)}0vÛ`Â]eŋjˆ° w_O v[­LĄü\Ļûé(tŒ˛j|טâ”v2¤ŨAPZ>šš .Ɔi$É …="ĨöštaÜ_ÉŒ‰í¨Ė‚JvWQp rp0TfÉeZiëB`$9ž,`4Ŧ=áÂ}Ęq9s>ÄRįŦ>ē#´ßYnņē#Ėy%”CEe5wÕ-„ŠJ Lú—í0)„^肮­h  O.ōšg}UæĖãÆ6„›0ŋˇįĮ/‚-ĒГuf]užá|Q\0ãŖYĨˇ'ō1ãĩį+]‘´¨KäŖĀ~eWW5ØQėžÂËč(0! Áː „]fiķˆėžV QŠd!8"š0ķĻ žĢĖŧŠ*RmĖŧQ]ĖŧŠŽ`c—Q×@]ŋšŒŽ}5 õ' ]•@Ŋ:—/œĸO†í×NŅ]‹ąw>bœĻĘČąĸ&" ‚ĒL9VąÜi—sĩös’ŅáÆ8?3÷2Žxá9ĻčŲKņfÅw7ûņ­ŠĘL5P…į´‚¸×H0r>@øœŅEöfæ1Ī )KĒÁ°‚øŲī ƒ mW„‡w(ÜĻŲų`4ÆcĘąng&o*4Õũ•OĪGEũëķ–įĸWCŠfōæ\’ ĪJ„Đ ^“Ūvē!UwMęē!īaŽˆYČõˇĖü:ãĸę.ĪHûyC˜.T ũ}Į(˜ÅĐߗ{ƒY ũ] îÕ1zfܤļO¸ûķ)ÎņBŨ˛ŋ{;°@(˜…\zGMōi'ßĶ%jäŒTŠ^#†NH7Ģš+a(æ3Õw”•Yy!5ƒ­wš:f§ BÕtWˇÜ˛#‹yūÁˆ9_†­ …ÉĶ ávGŠģgßv 3ØKŸēÁŖ7FX:ĘuôŽō|uąĮNvô¨į&˜Ā¯RgsũĖ‚ī’õ‘ÛAĨäųŽÎÔNΑܲŲ-Š9k•ŒAw†úģ•0s;hĨ:s‚Ėg1{a !ģƒņ7gž ĨņKRfŠa4rĖsr:ĪÜ/Ę, Îg%jîŽNgōĩļĻût1'5åęŖŅPÚ*Sž3Ŋ#ÆâØĪ¨D(cظ>Hī$ēNf|VŌ`åūž)5’2ŗŅ ^ŗlÂ.û’*•)˜Y=,Gîé!T`§bØmw"QŨ ¸$˛63äs&ëČĨÄDR2s”Ō™¸3%“Ķ ›ž †!w—ˇĻ‚1I•œ0&‰ '8K}$Š­ā6鄞ž‘_OGˆą\…9’ŗ€k-shĮų‚UĖ:Ŧ‰qeŰà >Ounˇ†Üū•9¸ŨhĖ] 7D_Ģą¯žUQŪâKõKoĒ €ß ´ Čnņŋæø”cĩæ{ŽO9–"Tõčīæ ų Ž ž¤ŧĒŧî+ˆĢšC’iŒ˛‚|ŌŖwÆĐeųEj0K˯•%"/åvøå33˜Āųø…’\…JÃnŪ n7oĘžA|™}CgZŋ˛{S˜[ōúįî”jĒ;OžRŒ]įĸœļš…Ü\"LŒf!÷˜23礀fĮž.ôéŲ4sƒėīfÂ)ËÅlÚ?mˆ bĐuOb.ŅX`{ûđ MKũ/¯ģę;jVocÍęâ8î™/žA|EjAęhņŅ“7âÂ'¨–ą€|bí‚yģ ˆ›ōŦĄšų†ų~\ÛŅ+|Bz…OČB¯đ ŲQUS1- î]œ†öœ˛’nĮŠi•Ļ ÄX˛áˇW %āŒ.RĶjéĖ/ķET蜩‹›†Ŧ {Õ¯ĩuƘi5ƒ1&Ē_Gˆ†ëÜgĩoKlĄ23€„fŋ|V­y>"´‡ôĒū !.Bˇš#č ˜˛€|¨˜B6¤ō4‘åĐ0ú@‚ËĀË ŅiįQĖ}õĩ’u[ëB.3ūånXwO¯b,į…Ųd‚N]˛ŌΖvû(ĢWÜ,/¸ōØ@‰„ūĪJbB“Ii™p.šLrč+åö×ŪÍîËB{Œ ­ė.ķ!™ayÁu¨sŠ.eŒčGŖŧ€v÷9•,„âW̰\jĢGv =‹_ÃēŽë™G€¤1~šåąŒĀ@PŦW×U™\˜2y–Ü3ĘR ÅeFY ē.œS; “sYF6Œ^Æ,§ŗP˜1|ëØi@†u&7æ+2Š22(ÃpŋO]ãu#ĢōötņãvŸÁ¨_#ÁģD‘qÁž0÷Ī ĘUÎ s• 8ŸŠ;į*÷ĖTۀ陎é†BešhĻŽ1—…œâ@H.&ļŲkNäˆ*ēV›p!AĪB$'„‘Éę&FBÛŲ‚.V(x%&ÅÎD{F &ˇĢ˛Bąö„Ÿ‡ŒŅÉÜĸ=šŽT1§č+[žBdâ´ĒĻž^'œ[gĨ[grŽÎ´N`â§*/4Ģežķpũ¤Ōŋv‰ŌŅ™yE@n˙’ë3˛€¸ų`õÎ\@Ü|°—×”N7(ĸ&Æ âŸV1Ŧe7ëxĀf_QņŲâg¯ß˛Ë?{ũ.¯^?˛!ŧŒú(B\tĖ7a÷Wö=)uļ#tĖKÜO/K+.ŽĖ“ …čˆĀŗ#ššā1Ųķlä+Á×=Ŧ™ā§[H0ánÕų+‚ ˇšĘMWfaGæNĨá­˛Į@}Å43˛PE§áīËĀĢá‚v@ģ{A]­ā96āĸVpÄAÆŖMōÉģĄЧ!œ´Zãz_‹áK7C Æ;h¤‹#:fĪØĸ‚6ŲKä<ˎíŒ-ņŌLA!:YéâŠĀĪØ"(IØB1Ú\Č9ãĨ™rŌŗ@Í æv5˛ĐĩÔĮĮØ4˛ŖŽväjväZęŅ,Đ2Ą."•í]—/ƛŒ•ŨE8DÃ~Åí FFųų_zé§î=ƒ â g„Ņ]Ĩ0^ĀäŽjÅĪN­™réíŠaeæ„ũȉŨŲĨ×-CšFžŽū&įƒ\CeÖ*hGi†ü.;3Ÿ)‡ą\ąÛrĩ¯ČxŲry\ČļLFá3[ R|AīãĩËQ Sx¨žĄ˛K†Ŋ،đqĩÉ%Ã˛2œËĶV4!ƒî>đŦ!aО/„ZĮ`.™¯°EŖR0Ŧíœŧ*[#.bjĄĶxūËęD—3V*t&ÚQ„ÛĘčä6ŖÂvÄ0.ŗÕÖä+sNØEDB8ĄÅkųČØAđĖĩ,ŅŖ×Ŧeé l•]ǰCDņėfWî߀šQ?s_¸ ũƒ*ūÚ‘ŖgđëuƒĮz-_ŲS–đ/0¤lqyZ¤\ĸÁÅ=ŦųœČEpA>Ę, žÄœ“œN]|aäÁ°} !”CÉ=ĘjČBM'ÔÜ/Øē\‰*īTĖž÷Ŧ˜Zƒ/g/ ÎŋhˇŋbëĘQ Ļí¯r‰Bež˜C"䟘 ;z&˙|ĀdjĖ7ˆ3’3Ú/DåRîiâdļčõ$;˙"]bPqBǤŅsžîE1įė|LŽ‘1Ôô*%ü‚(‘­ƒY[-dÂOiāąCgãũëeu}sAoH°{vŨw¨oļRaˇ€Q:ČΧ.ĢĨ›5ĸâ8›ģęLäŒųn…œōœ×•kd¨H…åŲŽ1{¸J&§FÔcžE|¨V,‡îR=9B*„‰á E”yŸUizXŊ2Ķ ‰knŒũσ“Ô3cdĚs~¸ĪÅųj°ģđÅY8֌÷ ãzä.y…—ãŠĨœ›sīܝš¯b¨ÁL7åĖ,™í‰RAۙ9?ōpmí‘9?UĩŨėŠÁ–\vK5\FVí M5ܨFV}ķqĖÂākŽ…wJŠžˆv§”ęétf˙!z¨ÉBČUw0S=Ņ~a.yq~–€™āKß í^!F¨' aķŒ]ž7ŠëÒn¯ëD:{úgĒ {úg\TČõžWįÄĒvDũčB–=1…t6ŪZ@\]SqÜ ČîĄBÆazۂŠkd,ę• ÃvžJ#•p jėÔˆ„*đ-‡˜ íį<ßĪ„R;2 Ū¤‰u°-?fPŽÎPy=[sf–÷ŗ5ä+˜A)bžâ oë%–ãvYķ’ˆvĒ`Žsˇ7Ņu:˜lBø))?åžØJ}Oö&ēá/čœ •ҘŊŗŒ,͎Œ”¸ŗ ¯âú×oĐ;› qÎĨå–?ė ¨ÃhŒ¸ķؚexƒ|Ģ?ē_ËõūĢŋhw-×.ÃÚÕįjĮz Dö:Ļ´ca J˛Œ“v,2y˙Z[@|ÆŗŪēĢY"ėŪ ˆ+ĻVūęšĩcíĮ;>؎Ĩ*¯øā˛!×lđŒ’uđ’2Fr !„ÛpYhץ˜föã #ˇ"CeÎN‘ķkĄĘkØō׎މ}ú×g\’2TBÄmųŠSõ‘Š_@\3ē\ å3CÍjkĖŽŊAâžqcô@<ú__˛_ ¸Į~ļ;úš["xN Úō­öõÕ}E Ĩ7Č'ļî(Ĩ uÎm4 ŽŦ\ VR;ē"sæĒ“¯4dYVĮøų15ŧ"˜7ŗ’¨í&÷2';Ž ž›ĸIėō7Ay‰ž’ßÛ7MaqšĀí߸ã3]ŪŽņ—ˇcÜO’sĨ••-F×kÖņxÍŠG‚íėŋO#֝2ēÛStaų‘îXÄÄŪhI2Cĸ÷Bî‘GpTϏˆ˛ Š–Îwũ÷ĩ™StndÁÜå^ÉÁĨ 3$z¤î|}j†D`öčc¨5Ų4üU$dÉB˜Oˆ„,9FĖ',…á2{ôÕ '@‚ĒĖį<“-)=W&^æ“ôÁŪW׀ĩ? šõs8XŌ9§–ArĨį›ôŠ2ęNĩ5Æ/s~ŧXe´;xoÛ+ũa~ŧcũŠ+ãÂtūõ#ß{ĶHo[ũåīÚęe, ßĩ îŸ-ƒ~Î[AíÒ^@\2å¨Åž€ėÕņUĶu]Čiu4§ãIÃå+.›_ÉëWvAāų+Ģ÷cV “Š /=ã’â|ĨhũŠ×ęx*G ÃŅŖĄŽRrąSÁ“zƒá•Š“dGđÍJ$ ŋ̇åJT9œHĘĨ`=E_*Ž<[ Ø6ŧĻ“ÖuÜTâôx†P‘ėj6äė <÷s"¨Ėg™Qō|>Ąų–K †vPv‹• ;TA(7ŖŨč><ŸbN¨§Œ[fą8 §Ī”G?÷™t¨čį&¤+ŗ÷ƒĄ×ŦFÛ:›C Ëí*ŊÎæsBî…h4‡´õĘĪÁëÉc˙eŽ´˙rsR‡#ãú1dú׏!Ķ÷đƒ~ ™žGÆŨ ž9é9Ž,$䝨ú'Æ0 šlŨ0DĴԊŠ÷W3:E:û ĘŨZ\!ö44ĻÁáUŠÄu8ŠģŠāM?’áŽö42Ŗ V4zōĢŅŠœ-lĄ1^Ęו0ŒKī¤!Ž+a@˙_`ŦđĒHˇôßU›jŋŽž”—]BĪRĩÄÖÁô–lļė⒘ž]"ĄmÂđ–lÖq˜@ŗM~LûLGsō{ÍĻj ŒYîņLŲH„Ąæ3eđîȎP SœĐzĢŠā“´ĒĢQ´xFw>S†iúgŌå  ĨYč9“ŋ“Ŋ›œ–…\LQ*Ždq3á1pŋJb’ˆ§yĖWÜLūū˜pæmLP¨ÃHe§•Ô]đ˙ȞkG[ļŲŗ)‡'ŒÄũ4Q.3âwyL`ߘã "6āÎ"ˇ}^GBĀ{]d™9pŽvŖv×ķĄüœŽĐbbË uIĖ-t¸bC´BミšĢ>ųĒÎyl™\ŽŲģ$Ĩš2įĀĩĖ"F0ču`|ũ|nYˆ\™ŖâĒ.o*¨’ĢŒ.#é!S§VgÃĶßīŽ¤3ëĪv#ųĀŲŧëĢÄŲDˇ~ųnģ xA¸ũčŅ;ŖvČĪųķO˙‡û+OĖÎÖK™Ũlˆ„×+ĨũuĩYöW"íĩŠķõeÃq˙_îmeŒ¸Sē`ĒFĪė+ŗŦŧķĖAjT誟ũŠ*axÂՒzšņįßūũĮ_~„ų~ūķšĢėž•?oãĖ;g3û—Ģ‘û‡Î?ë?˙ 9ÕŌendstream endobj 35 0 obj << /BM /Normal /CA 1 /LC 0 /LJ 0 /LW 0 /ML 4 /SA true /Type /ExtGState /ca 1 >> endobj 36 0 obj << /BaseFont /ArialMT /DescendantFonts [ 39 0 R ] /Encoding /Identity-H /Subtype /Type0 /ToUnicode 40 0 R /Type /Font >> endobj 37 0 obj << /BaseFont /Arial-ItalicMT /DescendantFonts [ 41 0 R ] /Encoding /Identity-H /Subtype /Type0 /ToUnicode 42 0 R /Type /Font >> endobj 38 0 obj << /BaseFont /Gautami /DescendantFonts [ 43 0 R ] /Encoding /Identity-H /Subtype /Type0 /ToUnicode 44 0 R /Type /Font >> endobj 39 0 obj << /BaseFont /ArialMT /CIDSystemInfo << /Ordering (Identity) /Registry (Adobe) /Supplement 0 >> /CIDToGIDMap /Identity /FontDescriptor 45 0 R /Subtype /CIDFontType2 /Type /Font /W [ 0 [ 750 0 0 277.832 ] 11 12 333.0078 15 [ 277.832 333.0078 277.832 277.832 ] 19 28 556.1523 29 [ 277.832 ] 35 [ 1015.1367 666.9922 666.9922 722.168 722.168 666.9922 610.8398 0 722.168 277.832 0 0 556.1523 833.0078 722.168 777.832 666.9922 0 722.168 666.9922 610.8398 ] 56 64 666.9922 65 [ 469.2383 ] 66 69 556.1523 70 [ 500 556.1523 556.1523 277.832 556.1523 556.1523 222.168 222.168 500 222.168 833.0078 ] 81 84 556.1523 85 [ 333.0078 500 277.832 556.1523 500 722.168 ] 91 178 500 179 180 333.0078 ] >> endobj 40 0 obj << /Filter /FlateDecode /Length 337 >> stream xœ]RËnƒ0ŧû+|LØ8i+!¤@‰C*í{I‘Šą 9đ÷ĩwķj ĐĖ“ĒŪ×ļŸyōáGŨĀĖģŪĶxöøNŊeBrĶëų‚đ­‡Öą$ˆ›eša¨m7˛<į<ų Õiö _íĖx„–ŧ{žˇ'žúޚ€›ŗsŋ0€yƊ‚č‚ĶkëŪÚx‚˛umBŊŸ—uĐÜ;ž\"”F&×jđ­=ËͰ žÂ*Xķ¯ŽHuėôOëą; ŨiēK DD˛B$RĒíeΈ6[ôŊ8q5ŧī_b_Z‘ÕY D‚<ä ‘Ô"öø ū‘”HR§ĸ\˛ŧæB’<%…ÍH (sļ!ō‘ÂJ"Iž‘|CrE‘”$’")Eˆ"m’%íPƚŒŠéčãĮģp >{f‡‡ĮÕ[¸Ũ)7獊ĪŖeŽ•endstream endobj 41 0 obj << /BaseFont /Arial-ItalicMT /CIDSystemInfo << /Ordering (Identity) /Registry (Adobe) /Supplement 0 >> /CIDToGIDMap /Identity /FontDescriptor 47 0 R /Subtype /CIDFontType2 /Type /Font /W [ 0 [ 750 0 0 277.832 ] 19 47 556.1523 49 [ 722.168 ] 57 [ 666.9922 ] 68 72 556.1523 73 [ 277.832 556.1523 556.1523 222.168 0 0 222.168 833.0078 556.1523 556.1523 0 0 333.0078 500 277.832 ] 88 93 500 ] >> endobj 42 0 obj << /Filter /FlateDecode /Length 304 >> stream xœ]‘Mnƒ0…÷>…—é"Z !EI#ąčJ{°‡ÔR1–qÜžf†¤R-ôÍøų=Í$ĮúT[xōîGÕ@āŊąÚÃ4^ŊŪÁÅX&2Ž +á_ ­cI7ķ`¨m?˛˛ä<ųˆŨ)ø™ozėā%o^ƒ7öÂ7_Į&rsu§ŦǏ†>žôŌē×vž l[ëØ7aŪFÍߍĪŲΐĨQŖ†Éĩ |k/ĀĘ4žŠ—įx*V˙ë¤ęzõŨzŧŊ‹ˇĶôV ‰G¤DĘÎHųi'ˆž‰žäI+ˆ˜`õ’7į{PAÖB’'%ČsrĄâ^Pą¸ÁbNEJ'3*RHI:I‚"[į2Žem÷YĢĢ÷qˏ[œī2Ycáž~7ēEĩ|ŋ˜p“endstream endobj 43 0 obj << /BaseFont /Gautami /CIDSystemInfo << /Ordering (Identity) /Registry (Adobe) /Supplement 0 >> /CIDToGIDMap /Identity /FontDescriptor 49 0 R /Subtype /CIDFontType2 /Type /Font /W [ 0 [ 500 ] ] >> endobj 44 0 obj << /Filter /FlateDecode /Length 223 >> stream xœ]AjÄ0 E÷>…–3‹ÁIÖ!ĐN)dŅiiÚ8ļ’Ų(Î"ˇ¯ė†)T`ƒü˙ßŌ×ūŠ'Ÿ@ŋq°&˜<9Æ5llFœ=Šēįm:ērÛÅDĨö5áŌĶTÛčwQ×Ä;œ\ņŦô+;dO3œ>¯ƒôÃã7.H *Õuāp’I/&ŪĖ‚  véč>íaū{DhJ_˙ĻąÁáE64Ŗj+ŠÚgŠN!šúA“ũ2\Üĩ¸›Ēz,îã=sų÷Pvc–> endobj 46 0 obj << /Filter /FlateDecode /Length1 47296 /Length 26219 >> stream xœėŊy|SÅú?ū˜9Ų÷ĻI›ĻMŌ4é Ĩ ĨPiXZQļ˛S¤RdJAŠėõŠ€Č&*Ģ„˛X "×EÜ@TP\TDĨmō}æ$Eāúš÷~ŋŋß÷ßīEÃû<ŗž™yæŲ朴5T‚ƒĮ 7|W—%ҟD=>xŌį˛qMh Ë6nø˜‡“7ũ:@ pʰębׇŊ~čxhÄĐACŽä][Šw܏h1 LcŒ›1}‘4bĖ„ÉGÖWøHKķÚĮD†jąĘßķÆ š2÷üЁúüßq ā?ëŋNNãt÷]Õo_ßŪ0ÜĐJŅŗJlO¤x•ˇ v…ö¸ž=˜eh)ŋņ#Ļđ1/0D `€thĀ48ŽTéBķn @?…ûYD#î‘ĮÃÃbčGæAēĻqņāg[a<ļŨŒųļHky_lßq‘čƒ°EĘē !zō<ļŨËûâ=ÆņûH´ú+0VėjĀņ–‰oÁ0ÄjL¯g_ÃFYŒÁüėw€äō6Øg™l3,ĮōUX?ËV#í‡ųu˜€ũ2"iĨ|1ÄrАay*ŪgadŊÉÂëЂU„žÄĩ”ā=īEĖÅ1Š‘!:a›(¤íķČ[0ŸŧZõHaŽ?—#:DhGŧĪŦ/Ā~I˜Ÿ…iÎC†Tp!RčVČŖf؏4×ß7ŧnÄ[0‚¯ųƚpū‘9ũ+Âsėt3pĖWnšē€TyĶÜnĮŦÛp•HG#âŨéQÃ:A~­/€Ā’Éųtq]1Opž=Å]°’į]$T„Ø*X+\…–X7Uļ ×1ųŨq ŌéĐTæ(_đū3Ģņž%yŊpüfHŗØI†æ"áX—ųÄyƒų™¸¯=pŦzŽ1Øŋ'ânܗJă|>8~:į9ßwŌ'˜‡mĪc›Xn•€kį2Éûđūx/OD×˙Ea=ļYŒ|=‡”!ĸų!ÉYX÷&Ū'!CÄ#š!. Ö#F#Z!^A¤ā؀㠒ŧĸĖpŲ”äeC| yˆs“d6ŧ†ÕŌ~†uf]ä^|—l+ŒŽĀÅīÉõ…Ë,ÎeGãŊšNq™i¤’|ærO~æëä2uƒĸîąān>IQļ)×;œ3ׇe´7ĖGēåx—Y>ŋFĘųÂeMâ ęD„æß´Ö IG îˆŦĪj¤ŧ¸AGĀŧg™ė´)kĄ#›…'āv:ŠĐLĖĀ2\ļ Đ ‡â dá^vÃüŠÛčrųq2J<ˆë܂ü<Ī!OËŲqšČŽQÜúNōޏ…>*Ĩ˙…Ūr0\Į)ĮÍu˙ģå˙' 'Ä-h3ˇ„ž‡B¸ž'šNČ g#ÅōjD%"Má#ËŖIŧ7dčÛc™Z‰~ČeqĸŅÎŖ.`yoņK8 ,†ėxčsR •ô8Ė•Gà ē mŽEOĀ,~¤ãn’Ŗ[dîvYj¤ōz;å6?"S¤2Ôŋ÷#8Á5Äo(GĪ“đšÜ>Kūm4bnX^C×oČį;đŌ…ōy›œŽžM>5ˇËåíTō-hßõįą qũÜ>rĮm$ˇsÜÎ4ļŋŪÔŋŠnF9ævø(ôčub÷âŋŠč>ÚaÜīžĄŦ(ô’lWhŖ` m”ebú3„z ×=ų†Oí FüijŖ/ —ƒēŅŠY0&bĪ6Höæø‡äGûHķSĘļà ą÷m 4ßĩD~âŧGŗ2äųJX„ëˆæĄ>b9bቴ1Ü/pŸ(<|æžh1ĖNaŧĀûfQōĐįūŽT†>•S^&ö…õ˛ “õF[{†đŊâëāķá{¯˜ZE4ډãМmÂ6Ņ Âvk%øá%I.xßŅ!/äƒAŽ2ÛÛđû­“úøÁáĮ‰RŒE¸|q^ā=eŅĐCŠ'~€5boč‹:´N^ ëdŊQįĸa#ŪãėכĪûŲ$ũ4܇ú5mĶ|´9 É˙P°×3í:B¨Dmąy8ZZ{ļąķ¸ū›ÁËeDö4ÚaO< UĖ…˛Ņ°Ë‹h'q܅X6õ7uwöwDė6⨠°œ÷-āą ¸žČũ%Ģ”âæĀã_øÖ ÷Â|”ã‘s )ú ‚˛—€h†”4‚EaHe†0%.ÁĶĨō,øˆnÔ(ˇÜ‡îe3a$ë™BsˆeFhĘ>D]ũžô0gY ,ây)B×ŋ cK^~ Šy9ũķËĄ?ËĮūķá!6*„({Ÿ€Š ÃŊÆ~âã('IØ˙ŧoäkč/ôAŨš‹é?C[y;iŒ]ĄžŦ#4•úŨiŽ¸mδōí^ÜSœ/Oß2_œëy6Îņoæ'­“ßûņ6ėYĀ3Cč4ÂĻÁît1lAŦĨ'ĄŊĐϐh`VAš€XÁ6č(ŅˆîčãsČ4D3–¯ fbē Ō×ÛÃyŒŨrābŪûu¤;ųš€ƒļƒœbŲjÄrÄģu7ƒõwå7CŒƒ[ķģĄ’ƒ\ 5pÜŪųÜĮkÁîB~"P—rČf@ų$Üŋd,OĀ{Ū–Įq2ŲnõŸæķŸ@ŽA†ÄÃ0ü7¯ąq?Zū œž‰:9ø†˙Gķû?îī DŠÄߟ :"C:r‘öAÚG˜“90ßķ%ü$xú•°ž’Ęoė_¸e”p×íåˇįoß×˙”§;ᅛŅ(7äáI˜ÍÁ °=âöŧâ˜Í!û'Öũķ_ķėĨ˙€ū&Ŧ”æ’ŒŨ–—uCŸ‰ I8W›ÔgĮü1Ôeo+õ×ÂbIwtŒä¸QŸƒöq_[pžâ˜R}ãū4îËíûƒķķŗ÷ũŅWŧH{"mÛHoČwÄ^Ü"ķŨÃō~#ĪmɅÛÚüĨéÆ1îkūūž˙ęÎÄ[ˆ7˙oÅ­ ˇn'NcR€qäqŒOîƒY hKęĶ/ĸę…ôS,CīLEh1mIJáHŸ¨û Ķãąüx!Ęâ`m$ތŞ=‘žŠČũz†û×Ŋ p%ęúöp˙ē͈Q˜ū1Ķ_ }érl˙=ö›ôP¸ža æ'!öcūĖ?ˆč‡éĨHŖ‘6AD!L؏Gūåú˙:ũûķĮK1fŒķtđg^H§Ũ~†ø¯iã~ūzûYŖq˙˙Ŋé™Ám4Ė<3}…q_āæŗĪŋ;ã4RÜĪāÍ`ŊC SjxÍcY?Kņc„Jį7)ŽÅqĖ”ĮÎ<~åą3_‘ŽC:_&JķéÍĪų|^ š vI!@Ųs˜RõĻlÁŸÁōĮ Đ%3Čō$YGä4 Ņú}‡~!A”‚[xT¨ ë„÷™†ucØ@ö{†=Įžg;Ų>ö9ûNÜ+ž!~/^•idq2‡Ŧ•Ŧ‡l´lŒŦ\ö¨lŽlšlƒl“lģė=Ųq؟ sūtęŅÎgĸĶëlæĖpf9[9ķmœœc3œœ/9ˇēDW”ËâJty]Í\Ŋ\÷ģžvmL¤‰˛D}ĸ)1:Ņ–čHLMô%vL”8ÔMŨˇËęŅx ŗ'Æc÷$yšx˛=ųž=•žŲžųžEž§<ë<[=՞ZĪ~ĪaĪģžcžĪ=ßxķŊ~o;o™w°w˜wôEņbĖÅVW蕿u´ÎY×ĸ.ŋŽM]ÛēuŨęJęĻ×-Ŧ{ē.T˙@CAÃ/ÁúP}(ğPÃZ‰skÉvr”\GÎŊ‰œûL€œ›œ{\xžĻcŨŲũl)[ÆV˛õėeVÃ>cŀ¸Oü@ŧáœK旕ũ-įŽ$T&ŦujœQNĢ͉œKCÎe:ķ"œ…œ{9ˇųÎõtŨįZzƒsFä\lbB„se‰C$Î9˙ÎßāÜRĪZĪæœ;‚œû 9×ęį†zG]$įČVGsiu-‘sūēöuEu}ęĻÖUÕ=^W_Cä\%į\čk˧Cfz„ž*¤‡NĶ÷P#ô(‘O’‡Éh2ž~-æGr™ ú‚iÁÔ` &§ÁT˜Âč męŋ¨?]˙Aũģõįę?Ē?Æ[Ö¯¨_^ŋĩ~~žĒŸQ?ģūąú‘õY_—|u:üT˙ÜÄĶ_Ūwnöš?ŋÜxîaĖŊ‚@ģzŽęÜô/'žuvĘšÚ¯›œ{ü믺ËÎ,;ŗūĖB€3/ōžg­gĘĪ e>“qÆ&ëLŌéĸĶ…§ķOįnq:ëtÆéÔ͉§ãN›O“S?úáÔÅSN}Å{zķԁS¯ÂQNũķÔ §ļŸ*<ÕîTÛSI§OšN%ØÚŽÛž4ŧ†‘ŪkōåĪÉWɟ•¯”¯/—ŋ#ß&_'_ƒūë;YO§Â`Žģ¤Å­ī)č7aܒŋ"XķÂø7?BW´4_ķ8b5FD]YV†ô›kŲũˆaaüO?Ŧ˜ƒõˆäēūģyÜÖĶËRn¤“ūmKÕ˙XĶų–ŦĪÃl˜#ÜËā˜ ÃBx6Á ǐ­ŗā)¸?ÃbxæÃ!8 —a5l†_Ḡëa+ŧ oÂ6xÃRG`(ŧīĀûđ.ŧGá[Â1ø^†áđ<ŸĀGđ1Ęęwđ,€Q0FÃ”Ū‡`-Œ…rãĄ&”é‡á"LF鞏Āt”ķW`Ė€GĄfÂ÷đ#ė%ËČ3„0"BԓådYIž…‘„Č*ōYMÖ -ZG”DEÔDC֓įáüN6ȋä%˛‘l"›É˛•l#/ŖÍ ¤šė„?ā8Š" É.˛›ė!¯ĸ%:˛—Ô=1#1Á9ø’D3ŲGö“hb!‹ČĢä5r€$¯“CÄJb`;H,ą‘7ČaGė$ž$’7áO¸_Á×ÄAœÄEÉ[ämō9BŪ%īĄÍ|Ÿ¸Iņ/9F> ’ČĮ䌒I I%ip.ãpÎÂįpNÁøž —Éō3úĒ_ȝä*šF~'?Éuâ#u¤ž4 i‚~ (Ą” ”Q‘ʨœ*¨’ĒHSĒĻĒĨ:ǧj¤&EͤĻ’N2¨•ÆĐXjŖqÔNãiuP']D]4‘4'™ÔM˛hõP/MĻ)4•ĻQOˆŅH/ 3…YÂaž°@X,,žžVĪĄį|AØ$lļ ۅÂna¯đĒđēđOáá(ęę‡Âqásá áKá‚đpI¸,üLĻŋĐ_éUúŊF§Đ?éuZGë• 4č].j{ŊČ^bŲ&ļ™ma[Ų6ô*ÛY€í`Õč™wąŨl{ũĖ^V‹~z?{•ŊưƒėuvˆŊÁŗ˛7Ų[ėmö;ÂŪeīąŖė}vŒ}Ā>dąŲ'ė8;Á>E/õ9;ÉNąĶė v†eᨗė+ö5;Ī.°oØˇė"ûŽ}Ī~`?˛Kė'v™]a?ŗ_دė*û|MÎŗkėwöû“]gu°ĒiɆŨ°ŪĀĶŅN؇á1xæĄ-ę&ôŠ…îBoĄĐWč'ôzÁoä[z= ûa\BÍ|ž$°„´%“Čč/ž"C ™F.‘ŸX9Īf˛ ĄDč/Ü' JŲl6‘=Ėæ°Il.›ÂæąųlĢb Ų"6™ũƒ-fŗ%葟|ōŗlÆ4Ģ1˛YÎV°él [ËÖĄ§~^ČZŋ üŒ(h|QL(^čmf+&Ęä ĨJ­ŅęôŖ)ĘmąÆÄÚâėņ §+Ņäņ&§¤Ļųš4m–žŅ<3+;§EnËŧV­ķījSāoÛŽ}‡Âĸģ;Ūso§Î]ēv+îŪŖg¯Ū}úö+é߀Ōû– ‚:løˆ‘ŖF?8æĄąãĘĮWL˜8éáÉSĻ>2múŖ3*g>6köœšķæ/¨Z¸hņãK–>ņäS˙xzŲ3ËWŦ|vÕsĢ×Ŧ]ˇūų /ŧøŌÆM›ˇ[ˇŊŧ=°ŖzįŽŨ{^ŠŲ[ģo˙̝8øúĄ7˙ķ͡Ū~įČģī}˙ØđáGrüħŸ}~ōÔé/ΜŊ;Ū‰īĎwbĮ;ąãØņNėx'vŧ;ūwąŖŋm[A›ģō[ˇĘk™›“•Ų<#ŊYĶ&ž´Ô”d¯'ɝčr:âíqļØĢ%Úe2ô:­F­R*ä2‘ ”@“BwQ™3ā- 0¯ģcĮĻ<ī„ƒn*( 8ą¨čÖ6g™ÔĖykK?ļv[K¸Ĩ˙FKbpæC~Ķ&ÎBˇ3p´ƒÛYCúwī‡éÅÜ%ÎĀ%)ŨEJ/•ŌZLģ\ØÁY3ĸƒ3@Ɯ…ĸI#Ē Ë:āív¨UíŨí‡Ēš6*5&՘ XŨãvk"%¨ĩ°Õ -N*`sw( Äē;đOá !âîũ ;Äš\%M›HûÁîānĐû¤&Đ^& kKÃ8GōÕĀBįŽ&ĢÕā2Ÿfˆ{Č ũ >†Ņ‡ãvX§žų+‹77ĩī7īæÚ8ĄĒ0f¤“gĢĒæ9kģ÷ģšÖů%%xėK=EeUE8ô"db§žNÎ)é spH'_ _Ux}CŨ…ŧ¤l”3 tˇs¨U†[cĢ @)Žj›Íŋ7tl…ÎĒ^ũÜŽ@AœģdPû3Tõ˜˛3ÖīŒŊĩĻi“c˜ą;túHBŖŊ91ôF”’šķT§78KøŒÜ÷ @œƒ8“~n\SK~ÚǎÄføSB°W`îČȀ˛}Y•Ą/įũĸcÄĒßĐļ—š/ũxkÉ H‰Ėcø x’ËÉ QÃúÆtĀį ¤Ĩq‘ˇĮ=Å9ļ‘ō9M›LĒĄn÷8ƒ ˛Š‘ˇƒJZĨ#û].žÁ küđf•Ũû…ķNx Žü龒-ã5kĸ{ķšĘƚŨËÜ(Éģ¤S_t@áŊņOo°DŽh –S=4\ߊ§ģS÷ūũœ…UeŪvęuK.\ßōF]$ˆjßOˆŖ‘¤ZĘ7ķL?M€yđŸLę!5rJĨTBœECYĮđĩDårũ—jBWx/‰üÕ-2Í@+ß­ųÖˇäo™žĻJĀ 3/íÔĢU•ę–:ĩđ€÷DJ<ôęįrļ@oÔLūĢ lÉQđ#ËÚķ(áĸHö–†q‘t ūpélÚ¤ ]UU‘ÛYTUV5¨&Tų€ÛipWíĨ‡čĄĒq…e‚SĒ](ZT‚ŧAZ5mëŊ`…ˈB^ĶŨKk2Š/‹˜8€¸"Õøkõ“Yū$ %˛sԃ™RvP8; TĘîė[Ļ]ē‡i‡{ÂÍZ…›5Ī7kĻÉMÂÔäÉŦäTĨÍ<ØÖ‚Ąû ãđJčaĐX+DCAY¤Ä/˜v&y3×`8  KĄƒŠÖ3ÛĒhˆ^8čOôR¸†^ÚŠ3fŽi{/ũ ļ# ú~ž¤_ z5@×ÄÄÄ1Äe„ŒžÃĪYüœĄg°ÕŽ(@ DŦA@\FČéx5ĐĶ\Ÿ¤+O (=W=…Ë:…W==‰Š“ô$NíãęÜŧĖŊR—I8<‘„5.’0Y2kčGÕĻ:jč×;>ĮÚļô (ö Ūüp"ŠeˆqĻN`ęT"–"Ö"ö9}N`Ÿ#ˆ÷' áG#ôƒjφĢöļs´ĩĐ÷é[`EĻĨoKô=úĻDßĨ˙”č;HĄoV'8 ­ëûĻcŊH_ߙdr„Úéd¯éˆD7Ä@Ä„Œ ‰ÕC&ŧÉ>8ĸlY ßIôEX¯˙(‡ßÛeĖÉ/ŪVwa /kœkŧÔī]ļŗüâ}üILņ‹wö"Lņ‹węLLņ‹÷ÁI˜âīQ˜âo˙˜âoˇ^˜ÂK ]ũJR˛#ˇÛhâļ#—F.=Œ\z}˜āOÆįöluZrlĨߗšæ¨Ŧ%•ûIeRšžT%•’Ę™¤2ŸTŪO*}¤ŌN*HĨŸTî#-‘•Äŋë–lž?†T!•ÛHeŠô’JŠL"•N’ë¯ĄŽę{˛$R(‘mš^!ŊĢMĻįčBŽēPŦ]¨öđz ’r~läL 7ŽMā4qgZA8ßŦUæØļéØņ ܆7ā,‚áŊbôŪä ŧ¯ˆˆƒˆËˆB†­qâK¤Ģ¯éˆÄ@Ä Äe„LšÎe…ą‘)n—&–™t7žŖoā'?.ęōĮėŸĄŖ°ÄNô ¤[B(æ‚…ŸōMF…Ok{~×ūņģ”m•ôqēâq#–Fč’ę?ã5dyĩwŸŖm4yJÉ/ņ m R>ė NŗÁNˇ ÍŦļ÷ÁnújoG-Ņņ^{ÚĪ;žŗ×PL^´īs|ęŦa¤ÚqKļėq|b_āx'ŊF%ûŊ5I­Sjē×ŪŌąíˆÔt&VŦŦv<ĘÉĮtûŨŽŅvŠbh¸âū ĖųõŽŪūŽŽxŋöū ŧįGũ~G~¸UīŗĮ‘Sđ…“i8ŲTģ4¨;AēaīÜ2ÂßDžLŪOŪMŪBž)o"wÉōxyœÜŦ0) BŖP) ™‚)¨æšĐ9ŋ?6Ë œđī `RÚ@ų•?+æv((Ü (ĄíÔŗé88:=ā \ëéŽ!*t ĸģ ˜:A§^í-}j䥁\_§€ŧøž~;yŧKt~ AīWCBŧhNU÷!Æ9‹ã8M™ŗ¸¤b,“ b LmŒyEūæRšúūú‰š%XÖŠgŋĀæø’@&O„âK:žâąė^V.+—uˇ˙ni,dŧ¸ß´+Á°Sĸ;ŠZ…ōZį*ig1Œk# okWĖŖqĩŒąOQ¸OtZ¯jÚļi[^…:ÅĢtü°Њy´ĩ+Ž–lŒT°Øčnž +&BLáČáøƒE&r†‡¯žŠ˙éë ņÜÖĄb@§@ZĪNŒswČåXZƗhÕXĻVb¸.l†…­xĄ ÜhČËōy™Riø¯û?1BÛs-¨¤ûv™%B ĄS/ŠĻ W$ĒŽÅp‰ģ‡Š\`ņ‘ŠÆ{H͆pøz1ab$áÄ ÷Â.ė¸ņƒ}ĐT‰ĩ‹°‰/A,ķB @č[ÄENƒ#Cy=§ô{l\ĀFØFFÂ68‡ČāOööÂ.āOXĶā0ŊX,Y=đ#bų?Hlh¤Ã:ôcëā(ļí B-XHLč;˜s„ą×ĐB"´…b ‹IįĐDgŲ,ȅÎđŒ#•Ą~ĄĮCO†6Ā °Wx;Ôj°Á`ü ũ$~: MąĮͰΒ'•ģÁŖTbËį`<ŦJ ]Į¸āaœƒ.p”¤>ŧûPø–ĐiB{ŧËķĄ@č0ļ˛C)Œ€•PKrČŨÔ%u  Ž1īēĒa~jāU8I4â•ІĐˆ…&pŽgŧO Á†™ÁÎhäR*äaÍXx Ū‚ˆ›ŧNĮŠ1Sô‹SCŸ€šCoœíKØķō;}?3„7YQ¨č/OpnÃ?áKb#é¤éCSéXēZ ą9~†ĀHä÷rŧû”š=TC Īŗ-ŦN<ŌáŽxáYx^'Z\Š“TĮČ ō5mOŌgéWÂ?Ø&ö‘|Žú~‹a üNL¤%éNî##Č42Mūēô÷ęŸoHk8„āüā˛`upWčKˆÆ=DgG¨|œũ üŒÂũ^†ˇ>&䝍¤‘6¤3rf EĘÉdääl˛’ŧ Íũe˛šô)šŒsÖRģ4įf4‡ļŖŨđs?JË1öz’îĸ'čuA.¨Ŋ-¤ w ĨÂPa‚0EX&„÷„/„¯„kB=~BLÅ,‘y™ŨͲ‰l5û–}+ß/ČT˛1˛š˛ŲĪÄ´‘ËģËKåKä{äŸ(ĘøSTØ ¯Üüǃœf …ÂnxœfąX<ąŧō<†](J*ŨHæĶédM'ËZĶÖ¤+\ÁŖũ?č›t ŊF[ ]H'ŌFņßTå?23ãŋųĪŪ€Kl?Ží}ŧķd™†ņĘŋÂ=ą5 ĪC^ėŸü6@J=@Z€ĪĐ$Đt@ŗŽéIÍŊˆ#™dkr°ŦE/€Üņ-3Z­h=• C’62€ļī´; ĐĮ(zđūoāîeüoZÜÁÜÁÜÁÜÁÜÁÜÁÜÁÜÁÜÁ˙‡@‰ôÂEäßę—Cģ]”œ—Ékč ˆėŧ*9;O V!ĪSa?mJ˛‚4ƒŸáZ~C~WÃÕü. ųP€iC=^šg¸Œ.Ŗ/Ô;…ƒõ~ū%{';ČßõO v§eâĮ`€ģüĒd=ƒIŽ0jHÖNXŖS õåkt÷ƒ`œ‚ l5>ˇˆUÚpí’áÚ%§‡ ĨÄKŲš-rŗdrüD9ûôû]úīŸ9%ų.ˇø‚Ũ÷“?ˆî§“ u”T-Û÷jĐtŪ6ž&…ύRe `Rō¨Ö„Ī@k„ûõ:‡Žęļšū~ü(7ŗ“ŊøÉ˛X-ŅÚ0“ø|‰w%Ošŋ—cÁîäųr˙ŪeUũ?Ēk8ųSđ— Gß—Û(“ÉsPRŗhŨŽļ÷zæĢô ė‘6Ķ/ß}d Ÿ_>îˇį—ßú[´[Ëö‰dûäo)ŪąËīŅ”hzéFk†čϚĻF-0í7]°]ˆģbĶPŋEã vCŧ!Á {-tä¸Á ¤ĘĐŋ-AePČdGė6ŗŨnSØm(s ›]Đ&j膝ŨŒÄXCbvkĖ"$ÔĐ}~=ĄU…õcœßO˛Î'HKŋƸ쀤cé Ęh-MY˛#ŧĄ(×|\H%sXpŠĄôŧŅÄ׎—yēf> lX_Ąq—[B))ī‰vys‘#-ZädãöJŠŒ{*ÆM&gōú\jõ<ŋōōƏ<ļŠėúãϯu|éĐú ÛļĩÍ|đŅÆ~jUUÔąĪŋßÖoķū ķ5GNö }Ã,ČI|ėOĩmĄvŽ–û'Å =,F™‡X&j§˜įjĢĖ â^ĐĒD§ĀŋļĨæŋĒËäÄ­ÕÎ ?Ūlá/åĩ$g—FÍbjéˆĨ#üIŅ v‘%¤jMcÔY)¯đJ:ā%ā5xŠwi͘Ō˛:öcRË˙Æ5JŽú/ehRCžÜҍW#qĩ4Ŧ Čŋŧt4sœ‘a>ĸ!įPHyTŽ…k„Ä2yîd#÷8ûäü îDoŸ]ާGĪØž~zVgŗI]Q3wÔČEæ]Žī_ž|dô°!- ^<ņzˆĖŠY1/đØ´uæÕtōôÁÍžíÜũÖđę!W5KxõņƒÁßžÁķŋÁekQ{´pÕßÂÔO3BŗRŗIķŽFė,tÖūƒ &”-ĐČš¨R rĐh´Ú#3 ´@5Z&öŅ} @×ģÖ¯âjRŖ#*VC‡Ŋ"Š*ŧ#[UCrũZš?Ņ-¯tåȗę)×8­Öœ Ô@T ģu5d‘ÄšK‘{>ßUŊo ’žĄSž–oĖã˲×ĖĮPđôz=ōNúBí¯)O[úįÎĘ›æ ,>>ŸŅĨ9‹müf_§Š,ÎĶøŊyšD;ŌĻyŌWaJĐĩį,cV´Û( ]Ö0›>÷ԛoî æ/{ęī}!¸Uãé†Ņ(4ÜģÄQ—ŋ÷Įw˛M‰¯Š_õRÔššSq eTŒ.Í&(3Ä u-ĒĢ€ĸgˆRE›ĸĸŽčôf]”Y§×ĸüųŖtĒ„hŋn-zdŪMĸŖí&TĶWôŒ|Ėe•×īf v­q aŦa†a‰Pc$9Œ!cˆĄ1KĻũ$ôäi”â–ÕēŨ'Ž[åņ/‰äNå°āJdЁî÷üļkÛĸž‹R6=N?oxĨÛė'ńÅWßn •†Ē…‡×¯ŦîV`Ą?o Nŧöá[OTŸã^­ r3õ9Ō ÚŸ<:–tûŖ;Ävpö7õrކȇ(F™†8'(&Úį(æÚO(>ąå¨Đģ’n§‹kļ1%Á¯-ÖRĨ8ōņ@Î;TbĨ˜'&&˜ĩčo[úŖaˇ§Â ņc#ƒÁ@ K›¨8ŗHž_U`hkaeÖš´Ķņf—9Q]IeĶK/5˛…ĢŦ&îŊdrŽĄ&nß܉`4är}%書&ÔíŒirĪč>m{?@ÛîžĢááf<˙܂‹ÛžhČíöx×ņÖ?2u3늕Ņ%ŖÍO§—˙¨ęŌŖ¤™F6ŊžņPũĨ›KjV/ßžŊ1ˆ•|JŧæĪneëlņģīŗôu´Œą wOĩMOXd[˜°Ō˛ÉļßöŊåį5gÔ]–Õ–mĄUęMæęFƸœ2gJB7Ũ@.väšH>.3r—AG-É5ōŅx›ü5áÜŨřkŧũFj\ę{ëvxéfqkd"”ō¨L’Ž64';™ŗ) ÷LF)Lđ‰}ŅfÎĘqÛ,Ķõœ^܂´Ø7fO=‘ŋšäŌ#S^ŋõ$}÷… “Ģ7M›žŽô4L}¨ķŒĪÆibúŒ&ŠĪÎÃĘāׯ~Üųō!ûŲ=‡W- ķp/:ÚĖ+ZúL™\IeųLČ'2†Ņ`:åQÂ:E$^-Įe`Ė&"ü×<# ƒBąCĄäčŅú—0@¤06Ø]~\<wC_øŨߗš N‹ËåÉŅfé u÷Ätp%ŨswŸ^ēŠŠ:‹'•x•iņŪÔ[‹ŧöž>1%ņ÷šú¤öš§¤ĪИĄžaŠ“lSãĮ'͉™m[ŋĐ5ĪĢ3ë@čÉ=ģJŸœĄ.VSĩܲv„öЉîÛÕž• rpƒŌŠ8}ã|ÔWKē@2Ũˇ'Ŋc’^Nđ<4˯7ˇ$ĶZ}R†aĒD-ŲqtõŽ‚–iIØ^ nēÚ¯t搜Ø~}ņ8čji—K ܈”^ēÚpŊØ%HŋtŠÕã<ō¤ ô8ũøqōÔoDFčˇ;ëāæŸĪ˙hßkČTō:ņ˙AŽôŪžĪ {›ĩAËcŊZN+ī8oŋ|”˙ųN÷ølæR°öžŌg-ŌĮ%ßUL´K^"‰/Ÿ ˙ūˇāęMGGžœ1ūÂ͝žēúŅįģīl{7xæË#iÉą¤ķ‚åígŋ;lū˛ļKßGŲ 5ā´ŊĒt$Á?8ŨaŽĄ,3Ė–Ūߔ4\1¨b éC‹ #Ôïš_ĩŋę”LôL'¨UJ‘1 Z2š\ƒi…L#Įc‡SŽ1c'͘ą…2A 2AVCĮų• Đ|įįŋÖ\KÔ@ˆÚoŌ8a¨\čQĖŽąŗLXĘĢ!į.Ö”ŸÕK5DÃķŊü˜œÎWĘŠü)ũ‰OËĀ1¸šļXĒpLAž 7;ŸÚ.ņ# ēŠyÍb|‘80]ô<ÃáÃēÇį‰aŠŠŪ) îŲ)ĐŊŋ]L/(äĩÃBčŽ˙%d|yŠ;nÁ%Dšo˛L.ĐŦiŋ/ļ4<ģîsōķŠĸD{–X{Ŋˆėv ũɲŊ/^ˆģ Ŗ–īŋFÉwĖô3VäîãæŽPÎVĘFÚ&Šã”ęYâ,ĩ,Ųĸb’Ķ,ņJe”)!--5ėņ Č%GB‚1^Y/WckŸā”ÎĨžÖ$ë%=¸ÖåRcŒ@땏1I^ē1Gpá]A–ŅuS„ĻŖnâĘ ‡ŋ^7™š’übzõn|ˇbØđ9KúVžž(øškfË{;=ļ:xŠŒšßÛžĢ^O/ nkKöŊ˙ÅŦäũ•Ãw”5z-ÃēÜ36ĩn­\ĶrtQ)ÍųÉbXč[qžBãĄÆ_6˜ŽŠGk•Š ã`B|%ĖŽ_ +Å- ÚŊÂ.í[Úā|ü¯ņF)Ū/¤ÉRŒiv§ãnmsßč>ą#ÄŅņ˜šV +t+íÉēŅx\f°ĖãĮÛę”<Â-}rJžA„ÅE%h„¸Ļ4xõ÷‚×Ią9Ŧ^§‚(bh´ČÄŌ.ŽÔ(ąĖį+åĮC2žX%ÍG˛Píå^nīšaāŸí:tWđ —‚Ÿ>ģ´?tš4i} ëĐS›ž0曚ĪEiķËu¯“‡>ē@zī8÷nĶĩOŽ^~b_đģĒũhƒWŖöGŅ#fûŊNi¯oŧҐ NTI”6Gŧ!˛ī í;nlzķŒöSü-„8šBĻLÁdą1ļ*SĢ4*­ ­šÅl‰˛˛8Áę"&^bvą¨Œ.đųp­iø3“HBbĩXņlĻ("Wf䈄Q„k5ųsK˙GK&TtúÄŅ9Á$ī‰švyæÁŽÛ‚ī‰ĩŅņ;üR0¸iPæļÍ ŋ{ņ›ß͏ŦG]āßÕVÃ}ūh™˜ PČå 0žP•2A 9ß3ģÁ”-ī%ÜëT9ĩTeĶ2ed՚Ö÷…7ŠnĨ­ēzŪwģĀ7ĪĀÉGģ"XĪ’ęW žúãÂląv[°`kPģĪd#ÎdÎD üiŌL– ŗiœ Ndž§Ô”ÚÔ7FWĩpÛčįŅņ.Ŋ}äÂõh Ą˜Új[Ã0ŧÃÜåDiõ#øI…3*:› JÕZÕ*Ē)U+pۜršŦ´RK´Tš3%Û"œZâÄ ˛L;NËZ—ÄøJËņ"‰Aéĩ|é‚ŧČ+M—døđ°`t!ÜxŨpˆ^?t¨A&Ö6ŧHû_/ĸ;ēāÍ ;fâŦøĮnžrƟ0ėly—ô¤agVv˜6ÍĶ”Ô0u{Â4>!Lclá'éZCļS\*nŅîŖXk!,Cüb8 W@49ąp)ˇž(‘„O=ՕčJKĘĮį7”6~ķŸ§8KŗŒqģŠsåļ4 į*B–_C(ōN…“{ ú’_'§BdĢd7ŠĮ7Ĩa ßĘŊėũo÷ë6l¸@ĻĮûČD˙  z…™Æ)Ø$Í\ÍÛAŠšGs^Hem]?á>6I;Y7OĢPSQ‘§mĄëF; xPtŅļĶŠ–ĶÂ2ų2ÅFá%šĖDõ:]†HÍĸHx͘Thzč{?ē<˙‹\j­V§3€BIËL•&jĒĨņpÔŧZt*jHsŋJŖT9ũšjĸŽĨ}Đ7Ģą†Ö ŖTę 8õã ÄPCûŧâËÄJQkčÆF.ąüaei~ .]ō…˜ļŨȜ/EĪXĪaŪøØĐ_r9oēä!‘`Pô—+|4Ą:P„N`ŦpBō„ŦKÁ:.ÆėĐŠxiä|ûÉWžŽ‰K:ãîÉÍĶeæJÉŨMą4rŽõ• /…ōRÉĖ‹ĩE.qĄ„71.'Iäž K,h‰¸/Øg{°ŸX[÷Ë‹Ÿę¯ąwërØš:'×āU¨MIƒŋßaRsŲËAQđxCŽĀČCA傠P2J•rœ2™XęT'F eęqęJĩ¨V jKĘĨÁž‹¤OŌ¨ōĢ7TJ 1f`Í "üdv)üEyŪ`OQžÂŸNfæÉcĨGŦ{b1™NōRwøÁĢڝ'יQ<uO&ãÃÉxLFķä;ĸ#ŧâFYú=‰q(„Ģ31ŽzK ĩoÕ‘=3Ų dMe]%z’Áčgŋ?ÄÁ ™MOĖŗ9ÎĮ˜™ÕVuÛdŨŖ{S'X­1qÔī7v‹ęfõÛú‰ũ”} ŊŖú[Æôąõ[h]A ą ‚`JP+ŖŊN4ļĘx¯÷r^ÅÚov›ĨÜoŪü8f”‡Į܅H~3׀§%0fSt›0˜Ė'-Ū%E[v÷8ŦŨø6‰˙ô‰›ōŨī?ĨGČōÜĄā §Ī×î~›ô-ø{đÉ&q;‰úŠāûLրû¯…čëĪjmĻ Ė÷î33ĩ&UŦ1aknō*lNÁļmÄFÄŪ:•—^ãĶot#’ņŒÄJÖtņÔå2bú†¤ŠOvyđɒŸ‚īį“Gö¯.íÜ|vpXĢ3 Ũ3f_°ĄaĢ@Í0+Z‹3]‡’ŠaÎ3‘töëMj1ĩ°÷w SŒq0SMčĢ&[6Ō+;“ŗ<ŸœmˆP}„bũg;ãŊázloˆP^ī¯Ā„Gw¯ũ^gOõûûxådŨũÕ|ũ3ÚMúũEŨˇzƒNŖqõfŖQoÔk”Ļ8ę˛YT2<Ŋj5bŒRiąÚbŦVp%J<‹‰ŅëuНn•Ŧԙ4.Š2IHJŒ‰đÎŨzã_Ž÷>ö| A¸5‰°‹1ô”Á†ŸĀŠ7^D~ÂOģT ŋ>Oohe4ĩâōMĘ%3ĸC5ąÅæQ‘LßžgH4#ˆšQrS(‹ÁJ”[hFqwÜŌNIO3\ëhÕá÷Ļų¸KJīÎĄĢ‡z?ÔˇŠĢĶ—dŨœe]Ÿy>˜!Öv{{ĘĒņž¤Žƒå¤ųėE-Õō†‰BVî”ģGHOė„že?`ܚA˙ĒÁÂ`V!L`Ė“œ#äŲÛ ÷Č;Į::$%÷JäâûĻ,ˆŌĨhŊI4IHö´Đgģ;x Ķû;û¸{{TŌŽÖ 3™ĸžĒĒŸn˜˜Tá™+TŠhĢô‹ s’fyžÔ.Ķ/‹Nđ$é´jŅ…`œB.c•OR"–a ×t Jņ% 45')&edYЧČđ{š&$X1ĄŠ2ÎkģWé…T’jËtyMÄkę%élķÁ‘Ē ?ßíōGŊˆĢüQ/î?$… ŌŖō¨Üš•‰ņô‹ĮßđŗŪHmļZđ(l ?YJōxE;đíéc7÷,Đ:ø`÷‘ÃũåĪ˙9WŦÕoÛX—×’|Ū¯ręÜēįŪ ūē‚|jxhqßv ‡ģ­ƒ|šĪûú‘īÍÔ-||æ}Ũ˛˛F§´Ū=iⱊ ßá2PīkĨķj7ŋV¤ Ȑū(‘˛†Vėt†¯Čœ„Ļ DĀônĸ°VągEXįščΗ~cŪé4ž>Íᱍ ÆŗĒ`œ¨Ũļíú¯\ ”¨ÁEŌ“įßüĶE’)‚G•ŽÉДi((—jjŽhÔNMą†2 â0šU:ĸŖ9ŒkœT4S** ŋsĒĐãUĄTÁ:%¯XA*K˜ĮĶ­–úSōR˛„ŽĄ”ōŖS,izųĨâAņŠ(ĸ§ŸŋS]ļ1ėéËųŽCø-öRLø%ŨM'Ū°77ŖĮŽŊĒ&ôsĩŌD8Á€§&ô“ôæKrė)ØŦ…ä؁˙ŨɖŖtáÁWōĶY„ļmxû#2Ŋ™#ą)YôfFfuŸVŽ›<™ĨJįÔ=ČŠ(ÚןŠ‚K,jšjJjIr…–Š–Ę–ÚVēSn”Ęå4š˛MüĸĢ Û‰TĄĘUđĮĻoc‚ņVŋS|Ž;aú–]T\Ô}cjÂ˙VĒ’đ?p%¨ †(ŊNĢ%ƒÖhŠŠRãÚ¨V-hĸTj"3Đ(Ĩ**Ę J3€R Z­S#˜57^(Ōj5P¤G“hĢÕæÔø5TSCžâT-UT ĒRŗ{`dkü*Ų.ŋĄØpĖ °‘_å„Xsô!ßc_×ĢļØ. Ĩ1b/•^*ÅD×ÂĄ¤˜6lmųužØåæđ?ĮŊ~žÎŸ¯8|3á×ÃŌÃūT¸ŅKFWÍc“Ø< nL\ž‰Ÿĸãōĸ„Մ.î‰ËS$ÆåáîŦļķ¨î ßaĪ‹Bã, ´:‹5?ĘdąŪĨ@_’/0LŠyŧĶĖ„÷4åŠ5ņŽģÄģōÕ*žĸ<Ĩ‰˛bY”ËxŠbęÖ_˙%7ĨŅ; 8f‘ŋ„CFū †(inPķ-Qõt7oO’?nh ž+Á%WķčāRZO_ ΟXPܗĖičR˙'U7Í)Néī"DôZC˛÷(”­Ö÷íN“•‹Ūˇ~&X,^~Qr#Iågū"L°ŧ˜PiĒtAFČF¨ĪČ™Bޔɔ2AéTŠÍ*•Z&Ȕ‚“´DĻQËš-ĸŽĄą~ĨJ…„FLWCcüJ˛‡_U‰ĮÁ˛Û¯UĢ5NztŖK$YŲ]M¸%‹ŲŖÕEäãˇehŧÃänË0ŧŋš/Ŋū4ņWP ´" ž˜ĮŸxđŌ)`E]ˇķg] RÃjCWA]•v—„ŸŠr˙ŦTĸ8((gvÄr×û×/ģŒm…‘ļnx÷Gâ*.lw?ąÕđ #t M›Vą”l¯ßŲĀ˙]hAĢ……čMe0H˛0ūhsøÍy^šŠNŽ"D"ËG_6ÔoĨb>ƒ|"“ TŅ’¤§ÉHšb?É9@f_üÕkWĪMwåĨ‡Í_úĨKē•—û|Qá§u—÷’AOÁģĮĄ]õgioœGčËāH4đ?ā!ԆįČū] ˆeíÛJ¯Đŋ‡! 3p°MÁ‘=ÆãÍ{C™ĩČ%ņūĮ•ZeZŦ֖–ĒMKÃĶ`tn\Ģ´{ŌJĩĨiŖ´#ĶĘ2Ē´sSWZžĩmŌFŋģ9eOėž”ÃąĮR>Šū"EŅÁBVGŒ¯IZvËkrëØ¤ĸÄ7L1Ō7I3OķŽæOíŸ>cnļŽ0CzRļ5ĶeŽ˜:6•ĻÚĶuē%ē5ēN\ŖÛŽģŦt:ģ`­Ą›ũ–˜§Ívģ “U™vA:Č0<ޤzŸßėį/„Ū īv¯čmž'én‚;;#ī`]›GōŦž˜Äô¤˛c2ęȨŦyKū6„ŋAįr­ôŌÕü† x pžņå0֖#5†Ÿ¤‡#Âvá§é<>ȕ>9ŲÉá×qm¨0XĸŖÍĢÛ+Čä:~_‚„ü!{GmßwEĮœŅ'‡“ŦÂų3ĻÄbú`ÁüÍÅĨ5qŋŨúĀáą2ĮŒąŪ?ĢwŅ–9]gv5ë´ļ$ęĄĻw•”Į”/ėätoŗÉWęæÜՒ|‘b7¤tIīXv_ˇģÆœ‹;ČOŒü›)'ü[‰¨Ņ'‰9bĄ(8ęp$ÚŗėíėãK˛VQų–|[gKg[ŠĸTÛO_jšß6Jņ v„ū!ËCļƒŽĪ5'­'cŋŠúŅúcė×ņį!GŦSL×§›3ÄŊ_ėŦ/‡‰'ãc× C´ŽÉ(ÄŲŅRŠĸí:uLŌjbPûņ ZŠfę x¤ƒ,ÁCéA‚ąÜZ WsŌ$6áîÜpĖV>>ŋ‹Ąá*ŌĘĨ˜ ˙IwÃęZ>Ę]n<bX†g¸“ŒĘnŧŲ#M_Ú5~ĮÛËũÁ_^Ũ?šf÷~bŌÖ&NÚ*Ö6üļ¤Û’#ÁËÁĪ‘ez/<úîoE-].ĄÔÛā¨˙nĨ†8ėíŖÚ[{Fõ´–E•YŸĨĪ +ĩ l…6V5ŠŽF‰5㴕Ú5ģ•{Tģ5‹fŽæk*čęĮęgč=áÂzO†ôD¨ ÆÁRX įā †zŊƒ;“]-ą3ĩ]OôIēÄ8œE’Úį@ÃGšĮtLNō9•7Ë>,ÅsåüÕŨøČŸ€Û‹ ŽąËĨņW/—"[žnĀđļô|c8KŦá ‘7>1,g–ŋ#ūōË'ƒŋ˙nÁļĶŽíą3úĪßŧaö¨ĮÉë+ĮH0ƒŅo,3V™q‚)é€ü˜üŦ<$gœwŨä‚<6!ģ8"X].ĄHIßjË?ĪY–Īņ—hqĨvåÜōŠ ÅLzŲÕøŌ¸åĐÃ3ŽOõÉŦ˛eé;œ['Nzaã#“×Í]Ŋ¨îų5D¨ęŪ–ęŽQĶ{G^ķä{‡‘gPP˛ĸ‘ggüC`ĻŊ…RąTŲ[=T-ŽUU+ ` šlú\ŧnžf“77ĩŠmnokębkkīnÛÃ>Č4Æ6Č>Y69úŊc Ņk­ÖbK™eœE°ØõK k Ô``qv•¸ā)ÉĶQ(\Vŋŋ(“͞Zĸĩ9øCL7›S<ˇŒâ°d’äū¤´ė›XŅE_—†ķ] čfŽ•û¤ ‘Æų åų‘wŽ‘ Ģ||Ŗ°…€˜å.éu;qIīäeÂũĩM~Úû]đ21Ÿ>Nt¤ūĸĒzÎāE 'iwMË> Ļm"}ŦĪī"´’<üĶāÜ^;‚<=ˇũˆšWB÷T‰^Õ ;ũ f%ŅĮĻĮfÄúcĮÅ>ĢYĨŨ¤UØ´)Ú@ėÁXËW—bsdĮ+´‚FoW‘hę3G1AĒ5fbEų™ÕÃ@ OéÉØÎæ-ŗĨ'd*ģ#{)Žõ|Lė~R .¸FT’Æ=7 ųč/•†Ãū Ŧ<ūĩŗöSüfƒQĻ”ËčR JSeú8‚a]Ú˙ć‚5>ËčÎÉĘÉÎå§IÔCކŅü.ÕkÖDŲfMę< ŽefĮŽ +•Î.ękzNUTöĀĸúa(Cí‚Ũ…īQ†øw ŽøËÔjŅÜDí1wVšeĘøØø&j¯š‰;OŨÂ|¯ēČÜGŪO=B}]õ[´Ž™ģIrw›äÎÉK›Ŧm"oáj‘ZФH]ä*Líåę•:R>Ø58ĩŦIe““É]?š/'­Yt Ũą+Å%—,˜Á‰GQnŋ*á |\ēĻûۊvģ^U˜hר,ŅYž,•'&æ+1XũÖ2kĨ•Y'č‰IôĮôgõ!=sč ôŨĐ*ÆúšLpq…ĈŸ+äU~ /į‡Ôküû/į#ßI8ūl9Z1+A#ųÎäđ—¸fZs˛ŒŌ Üu“zÛŽÎl?aúü™8uåĄīŸúâĐSk_û~ŋͧmÜ6uōÆ~ļîžĖ!ũs IūË Y´ŧ˛~ÔĮ&oŌ>B Ĩlŗ˙fųËĻļ˙sīŒd™Đv÷˙7ŽfÎÜš÷Ü{÷Üs枑:čūˆ€žęī–öI÷K¤ãŌûŌiI@Rš4"í‘îŗŠNJ3’\.ÁZ%p ū˙ueØxNæ…jĸ_Å|€;ΝäøãÜiŽAœÎ€+ŽëŗĻ:h&PfîTĶä ܟĨ @Å­“““ܧ¯ŋ~ļ€Ģ9ûŲÃpú2ÜNĮ¨Ą{Cë9[ĩí<ŽÅv‹ÍV(ÚlĮ1œÍƒ°ÃÎ@ĘšmvŒËÎ %n×>˜÷Ŗ*ŠŖZ–÷ŲqšŊĶŪmgí÷‡ķÖd’x]*‰=GQįzâ{ĀĀ´`nˆî––ŊĒhn@:EÕU#Ēr1–œB12…@’š[ °™-TЌŦ[&§‡+––ˇ-lY~÷Zî“7ŪøâÚ{kīâļžŊ˙ųõÄē˙Ųߓ]3üj¨Hā7ņ›%ÖåøwÛį<ģ‘—×=4(;}XĢ%AÚéI8k6Z@Ÿ%œŨ%<ß&­îđ å+äqvL~‡ũáq%_#T‹A~™Ôéčvôq}üBŸô5nÂv¯ô"˙/ÜĪøSü'Â˙áŋ 4Yļą,Įđŧ I"\HĸX-đ^AāYŽĢļÉ^› ":¸1ȗüūŠhˇ#™#_AhĢáĒÔŠ˙R´–{5bĒÁīC‡tƒžĮķÖ Îō>¨Í>ŠŗÂ~Xž ƒä)I0„ŗŸĻ¸ ąƒĨG3å0$K ĨAI,-íāI$_„Ķ›‡tz:8ĪJ,¤ģ1ŖČzĀC|?nfō‘Ķ{‡Ô ožč•BOíŲŨl>AiīrXôú 7¯ˇƒ Õį‡ü¤ņo›ÕquVÉt4#x˙Ũ'ĶÛņŗīM?pŊíŠ?ü˜Ū95Ā”_3Mrčo5hŖÚ}û“Č‹RÛ2sĶąu‰yn^dž+ĖMÉP5X%—­ÜvŸí}× ‡Ķ6ļÜ6bÛc›ąqä7Ö4458E°Ũ‡đqpC™<ĢÃåæd}Ŋ9+ŠņMQJ7NZ;“`ųX‰*Ņ‹O"iæĄåvXÆSÜ)éƒÂtÛ[ļĪuĻPÔ+%ą.ąleY _Pb‡)ˆųĘĸ€*Ÿ¨Æä÷I˜j˜‹Îę}4-ē˙ˆŋz_1.(@LKe5>0ņ—™rD´…EĒęcx×áŲ‰ 1ÂÔ)ō,āL˙}TaY“Á PUrĶŦč`Ö\*^OWqcÍQ5—4Yœ¤™Ōŧ…4šÚLē8į[Ī?˛}įŨå×ŊüŋūîpåÖ FūrōЁKnhįjžĶuÕļ+žÚ˙ÄT-ķˇąĢÚŋķđÔŨĖĄ]ģ.ũîˇĻŪļ֑_ˇ|čՐĮÆōæ1õ˜ú!ûkĪiösĪ‘9ģ8Ąâ{Ôū“ū?§‹^§×§Á‚‚yŸCv8g•Ž*v ˙í]~*H˛ĒøOû™˙ũūūã~ÎĪ2->kaŅÎYX ŗ‹Ę™3Ō…e…>î &.ˇŽøxˇ$‹˛ ŗŧZãæÅØ%kÃHÂLĒĶK­7a{{÷ę.UåÉ;.J?ĘÕÜŊÕČúÅ_›J3ˇ$âËīzuŠä€Ŧ¸xâ@ôãPŋ&Če ‘¸‰ī‡ø¨(ļĒíZģo‰•ēN[į[åßjÛ*mPûĩ~ßÜ—Ô¸÷ øĮqÄÛ[Ø^[¯ŧE‰ą†ÍcŠ\X nP9oͲđTUˇ6  Ē ƒkģč}ĸhP Î/ĀÎ*‚*DŅ´¨ˆ8žæ›ŖõũŸ÷÷Īž֑3PuBÆĒ’¯–÷ȜœÁžĻEĢFč+Ãõr×ņWÅë4`wķÃuOv’ų ŧ4‡´ÖÍæąjīÃíw ßzbûØû×nžŗŅũČÎ]ß4“>8ĩ=ķË.ģ}枇ĻĪ~ķ’öŠŗėï=˙Ę[¯ŧüsā×EĶQö$đKE%čB÷ؙzf˙û@Í@]Ļ2Sĩ§ęÛōß(wÕŨŨđæ‡åĮ•‡jŽ;\ķB¯”<Ø×ʂ›ÅÚjEæŠôšÎŪXZDŖ’ō@g ;pU`āõī ”’÷\yāÎxšŲ?"ņ“JraT|ŧ$Ŧb†¤ëöúZiÚ^™ĶŨŠqãÖŌX)SZR p%öō"\Tyü­cĖ–CBÕ¨y´$xb^P´˜´ĒhūęÅĮ3‹÷,fĢã*¤Wš*ŪĪ9W‹˛üčzōF[Ē‹}ÃŸŠˇB_ÖUĒwÉ`*YģK­@ØĄU+xFWIĢŽF„=Â>C°ÆÜ/Ž '^xŠŲŽüxéÁAs˛ĐW+!Ē;EŦĀgd/–X(Ô—ĖŒęBķŲyRānŖīŋĐ7ĩ蒎mą†›n:|䈧žŽėûÔ Œ™ČíXˆM˙ÅíSß^ßPDhšfÍIúŊąĪ<‰ŠČsđŨã#é§Cķ5okŊW‰Ÿ‚=>;Lx7ƒZ|ÕūBębâㅸ°ĢˆN{âb.bFŠî/:P4SÄA|›3äũC]:‘ 'uraëgYī,}vۑM@•*âT§Ãå`x3o| N)FŅmO Ü`nÖYájkhUHՄSlį|¨[ĩOÚŨ‰Ë.ģãŧÉŋ™ŧ(ŪŊ$ÍÜ5uø/­šŦįÎ[™ ‹˜ŧEÅ~ ŧņ•G—@ˆ^áĘd6;ÜA ÜĢV‘˜c3ŋ9 gle˛į%•ÍkEup€ĢCxÛȸz't¤Žąépp)ķQT#Ņų"´FŪ„71}âŌ dĸbTڅÆņ83!î’ÆåŊx/s {›pĢø éoŅ=Ōˇä ågĐQá üzA~Ŋ%˙}(ŸEgäŲd?ōÉu¨Fn“ģD6kĩ…ĀQ”!ČĒ–d¯$Ɉe žB^ŒĄ Ļ[ɖ› K,Âļ&+b(‚˜‘Žáâ#! @!IgB¸Âū›!"ûŦ(0Õ?Õ_ä˙ėTŋõ E.ørĪɗƒĀft6ˇ…ώĖÃ-˛9æ(įī§c˙pĒēÜ_˙Û'§\ÍÔMCÉŪĖ­$z7w!‚D4æ`HuyņnžĖ\ėŪâžÃÍē‰~JåķZՒR3ē ũ°ŧĒ•ãÉÃKÍÆ!ގKv§¨ŠČÃz…ąØ^ Î[ĩ°@Ŧwļĸ%Bģxžs%ģ† ëÅuöŽ5î‹ĩ-Ž Úa@Ō&øk„Œø$˙”ë í?øŗRŨ]‡ęĩÎ:W­Öä]†Ú´qņņönåQüķ˜ũåz‚ĘųˆŠß–>æ>vũZ;Ã˙§Tĸą6¨°`“dY´+ŠŦēŨ0ŋÖļ!M?6ŗ64(ģœú?ēQܚVo Tœ˛ĸT;œ^‡Ã)ē]ŽzYôBsdËI1XĐ8ŅåVœŲ-sŦæP’9LÄĒšHž—ėũ\u`’ģĮÁ:ŽáGC˛Ū-ã¤|=ŲGe6†¤n7Nē¯w3nreWmøj˛ øGāĪ=ŸŌe!°ūLŋĖ>ü' Đī˙՜vōgžWJ5ÂM{įėžĪ=&wÁŠvÉgŨōž+&ēĸ3?š9‰0|œ3'&QŗK׎͜ÄËŦ}ë´ö@H.Μ8(7‘ `^Īē-tŸCœ9yPĐÍRÍJ×$ÉS'žpéˇxlæÄ!Ą™`<„–1O™=åįÚŌv‡eĶ‘šŒ­LŦ7ŸĐ‚¨>䁁‡n›0Íā$JNuÜSHŊ’­eņē駟zŧ“kyüÉû–œ˙ÄūéɧŸ˙sPúŋ>å~™ILŨķĘkĖāŲw˜ŨGūđ:ųu °G˙Ú¯âņŖ. ģ*ôaCč‰@pŗë¯¸ŋīu~×uÜvœ?.ŧâ’\!_°ˆõHŽ"u nˇß€ī°‹MÚå\ŸĐgŋÂy7žGžĮ~”9ĻüÄū˛ķUõö-é Į/ՏdMãyÖLīā%šāárŠ$ŋÃår¨šäUæ]ŒKV_D/JŒZKīxҁÕųŧJ3<än kk×)˛+ĖKׅd0$GCüĨüúĸˊSg¯c*ēĐĩîŨĪ[/+SÛĻEũH=ķŲ9ɍõũ–õ[/8’\šĀņŧy„“@“:Ŧ§<“Ni&[ØKƒJEa…š>4/¨ŌĮ÷A\1/(…J˛ÉsDŠāq2$ŖĸXĒ6ō<†­Å.|ĶôŊ<ÔXŌP}øįĶßÂß|÷öéO˜:<ũŚæ [ÎN+S˙Œ/î›î'ÖkŪôeėŋ‚üŠđŪÃŽė"Ŗx¸$XįŨäÚ/ŗ!GĒ×5ˇĒä (’æsøĩZ{­RëXĒ,u,qŪëļ×iuž‹|}ZŸ§¯ ĒE=Ņ‚ ~§cÂ}÷š‚›ßpߎŨîšÍ{ü˜ũGęĶî§ŧŋ‘íũĮ”ú…wϤ L€ĸ‚=Ëđz<Õšė… —ŖÚ.{ívŲŖiŠbįŲ’€ •¨%LSÉŗ%LÉ1ĻķˆËŌBŪcLoČŪŠ…4æ*íYŅŽá Ÿpá ´ĒX&ˇ4—n…tĨYéVØK•šËsáá&ËtNëģÁxÔ)ō H•$ZûÕ3§äuüΊüęgB~âÜdE,æ?ˇ#2ļ˛tÖpŒôÌ|)3#ûĖĮ8o>zgŪ{ĸ-(W´°)ē­|Č>ōæ?LI§§ÖÜh#‰Ų%ˆŧÎ^YqŊ÷ŧ†Ž‹ Ũ56ûtüšwë+Ęë?œœŽ-¯jŪŊŠuzčqĩŽĒx‡Ģ”Ģ›ēwė†Ũ;™g˛˙ž"į:˜§o‚œøÖC;Æŧ$2^lĻĘüsH_PFÄ>ē€ųLÔ¤qP^‹W3ĢÅĩRˇē÷2ŊâféR5†#LBkqFŧVú&žYŧMúŸaŠb ž/ÖKAņ{âĪą@´÷¨ZĐʀ’ČkĶĩāŠ3í’Ėˆ˛\X LŪ=cÂļz Q;ŖŪ)3Į°k OōPá¸ß‰‘3äŧÚšĮyÚisf|ÆûîFI4CĨšÔĖ<2Egŧ’āôŨŨR§č7|ŪéGtcÔōTįķõô%!˛—eĨR™kDΘl “āęšŖ„=„G´"íŖ)6ČŋwČE¨ŗN-Jĸ¯ø|˛Ü*$EŋÉž ã…O‘ovˇ,Á|%ÉYÄÂŌ–yuĖÃé+Ļģ؁Š''ļãOībEūŽņŠ+¯•ūBŸöߙÍļŸ";xôŋmŊÂ3æwÂī<ĖûÂûæuáuķŦđŦ‡Ų/ė÷0÷ ÷y˜;…;=ĖuÂuæŦxÖËÄʗŲ,nö2Ѝx¯G —ąŽ/œėŒĶÁ`ĨÁ:ț˗†š vđ“üi vĸ­ÎĪé¯$’×ģ6X0 Žŗ` Úr fQ“ļ؂šŧ:6¤hk-˜‡úa Đ6m؂Eäņ¨,ĄUž* –™°ë ļŖEž¨+t~ׂėfíe vĸFųūĖą06Åw–Â6€ÕB;…yR^XLa–×RX¤p…%KF&lĘȄM™°)#æōę˜22aSF&lĘȄM™°)#6edÂόLؔ‘ ›22aSF–ķčĩSZÖPXÉ+wRÚ/§°Jh)ĸ°`­pŒÂŪŧúûōĘ´í^ ĶžLœĨyuĘķā*Z˙;^@á‡(ŧÂ ,æ_ĖëKÉ+W˛´ôĸ 4‚ 4ˆÂ(g=Ÿ^4Láõ°P%ā“ąjéh\Ĩ&Į0”Gi JbĐž •´<ü˙‰Š)72õĀũŊoŗNĘÖÂŲėo Â_3ZhA‹iérhƒķh3cČĐV_>)´Žt ¸g xn$)čW‡ZaĢ'ŗ~8¤C Ōž`L Ú šĻ=E,\a(1[Æ)FBÁ0Œ>N1FáN†ÖĻ}ŽgŦŌ”Âm›Ą÷ 9“1%éĸ-#7Q„Ž*M{#wHũz6Į?F{ĶiųŖŠRü¸Ÿ ×ã÷°ÕģaÕMR\fßŲōŝą8+“3_Ž—œåJÎ&îˆU2F9Md5Ģ%I*—åhŒļ'#%ÚˇZe{ˆĐö;­^ŖĨäžÉÍY. BM‚Í,åkÔânŌĸ$JëŅĢYŠĻŠÆÆččžZ'˛3'Ŗ…Ü‹S|ŗ8RĐĪk´a‹˙ĒĶēĨ÷Yž Đž‡hŠŲ~îD-’:1ŊŠ#I8ÁŊˇM ŗs9Leej‡NyąčRŠÅh:ĪLmLЖ&%ųÚÍi–÷wY’‰ĶŅŨ4å–ļfr,7Ž8ŊšÕŪĖ—ėMúKôEŦ>ļQ c”ĶstĶ@ŖPžåėũ…Ģ,…ƒTˇuĒģ(oĶTī2TC9Š“ą›ķĖĨ†ÜlJ[Z6kĖģq*‘0ē†ļ7GMđFčŨYM3{ ÜĄŗd"GEļoŌ~œŪSN¤Ŧ>Č2š˜Ąíŗ#ÎbĄ:§64;ļÆsėjûŠ{7DõŸHˇm˛úËÚZb+—ÁQ‡Xk=•AŠÎsÍĪÃĩôzöęīŠž§Ŧy§Øwädü˙jķMš Y–аėÛŦ2ąn„õ@G—Ōö:ĒĄũ­‡c7ô=H57Ë1ĸ›iĘía [#ę‚zŊ°zŦ†Ī  ˆĀŨPJÚ¯†ã%´|”ô‘ˁ5ĀÅU𷞖ö"ũ60™Rĩæá—×Đlš9bSr#–lgįšü1×ŧ$đ Eĩc˜ÖÎŌ“ĩüY}ÚFīN@ũą\Ÿ‘œ 5y7FÛÎÚ>ÚÄBÍÚkĶND-ۜļlĮÅbäl/ámŸÕą";-›Ŋ-ˇę™}fūg˛ē5žŗ‚†5ŗÜÜIQ;•ąėÆ Ĩ÷_ůėl'3ō°ĖZ‹sû°ô‹čō6jÍQoŗ$“°0•„j)Us9eZūsĩâܞŗ6”XË0õhÂĐkĖâvÚ˛UŦīFĒû‰<{>qŽ, ˛ɟ9æ*Ļ#Ąœ%ëV”Ρ?/sŨŌÅDž ÍöKf˙åt4oĩJåy\ šÚŠ<Ŋõū4§ČčâV¯’sđSųī Ōˎ&Y;<[3 uM;3F9Nđįč1Į•¯ŨqËr›ü7gՈĨŗ~Žũ)Šfõc-Ĩũ\Ée}<˛ļ–'hRcú•*Õėdúŋg1ú’ÔōXvu'õÁÆQž÷įĨŸÅgÎIÃō5æŽČY|įĘŅäÖŦgĄ8ĪĮY‰…ŋÄëÁ˙Öhgš|nsũŠš#2,o9+dYe–CéBDÖÆe¨ĩÁz¨Ãq\-„xŖ>͈ĜŅ:Ģf3ũĩÉVø3á6ÔŌj)Zą ųėÃÔ'ūšāoœū5Ōĩ}PË÷ĮÖ ­¤ŗs<§æ*ĩŦ-ĶjĄÍ5´Ëōŗ’–Oæ§š’Ļč(•@g× ĸU$˛"~ÂoÜM´~új‚c†Z"Ģ&ēö\EĩÄô's5˙g{§>€Y×øé%{¯éKú˜ÃŨ;1b †#†ū¸Ū;lč듉dŠôÉÔH2ÎD“ }$iÔW†3á?SЉ Ķ{’ą1R’Ö×& Ũĸ`°y!7ęËc1}Cth8“Ö7i#ĩĶX‘LdŒ8A’šĐĶahåŅA}ĀHG‡ úōT4Ķ#P+…›ņdĘЇĮâáD4Ņ#ÃáT8’éL4’Ö3Ãá„÷&ôä …^FRƀ1Ōéd*­‡zđE†õ¨…*šĐ3c Cf†ĄšĨÉŌšĀą0ôíÃ0˜lYfÜHdĸԎ0–šhÔ)K’;TČˤŒp&ˇHƒČ˜&Ĩ“ƒ0L:„ÁąX @:Vč>ž„Nĸ‰ąt†’šÎLČ|Ná¤I/F*MĐŠä@†ņGÆ ŖŲ@4<”$÷Į‡Ŗ@á°Ž$õĄčNƒV Rë1`‡7€w‰hLJGF `c"b@'&ģŖ„Yēą ˆ‰ą hKƒcG<ŖėÍXz“ļú‹@‹m†>–6LnŖcd°cÂ}0 $F *“‰&†é)äžI71ĨeTā2 _Mj#i0™Íĸé‘Xx‚tAZ'ŒņôHx†U`ˆ™hš &ÕGRÉx’bkĖęjģIÚch,Nĩo‚vDk7.[Ŧ×­FRI"Ŗų´Öú^zzLīMėãáÔBņŸŌ| e”Đ}Ŗ:U7ö藆3zŪģ^īl¤3bic|Ē5vu÷Ž]ŊvÅōŪĩŨ]z÷jũ’ĩ+VuõŦŌ—¯Ų°jÕúU]ŊŲ!÷ƒ(˛œ&b!ˆ8 :CĨĖŧäP*<2Đ P(Ē× QĐf¨JŅŪFŊš ‡Au’ÛČԃ–™9ƒ!Ü'*h€° "”ɀn īgĮEĞ2hĒšv NĐømc@ ÃLÂ,Ė#¨6(ŽšÆDCõáØXxhe8 Z•ßēQߘ z>‘Ĩh˛„S"Ŧ§GŒHt09—r¸˜ JچĸDÆ 9)j¸HqŠō–Z„/ *G AĐ ­7žLíH›ŠMu˜&ĮAgÆļÅĸéaŌā2Ųå†ņƒ¨F&tSá-Ííˆōcíā,qÄ⍎iÚ ØĘˆ‘JX¤ŦqĶĘéáäXltugÔ7MÜ9ä“z IŦÆĀŦYĖŅÃĸÆ8’™•1!,lzđĢŅŌ!įXļÂBũ„3í¤ÂƞåúBŊnYkÛ|ŊmҞ…Í­Í͒´q6/ZÔÚ Įļ–6Ŋmé’ā’ CÎdFڛšÆĮĮãYÁG’ņü9ač+Sáq ˜‚0(Ā´!š fhØŦ$ø2ISŅH4Ŧ÷„éÜHÊĩlņÁŨ4œ‰Įšâ™D8n4ÅĶW…‰h$…˙ÅãF J?߄\5Y|¤ĩÁJŌ0˜8 ęčBˆ°˜o‡ëO¨+ŊßCEâ§e€ũ.{}†}>O˛Oą?ČÃĻŽAöúŠÛ˜Ķ—1ÅĮ•q‹¸uÜî|8Ąv˜†ˆ–;2ŒāXD]<ō&EŨ3‚Ą˙ ŊÆXJendstream endobj 47 0 obj << /Ascent 905.2734 /CapHeight 715.8203 /Descent -211.9141 /Flags 68 /FontBBox [ -517.0898 -324.707 1081.543 1024.9023 ] /FontFile2 48 0 R /FontName /Arial-ItalicMT /ItalicAngle -12 /StemV 237.793 /Type /FontDescriptor >> endobj 48 0 obj << /Filter /FlateDecode /Length1 31436 /Length 15742 >> stream xœíŊy|TEÖ0|ĒęöžN:kg!éÛétY! Á@H"Ų$’NŌIZ˛‘t€€,Ž"ˆ(¨ãž€Šˆč`Ø$€#č¸+nŖŽ ∎Ŗ"Žƒ: Iŋ§ęŪÎ:3Īû=ī?ߏnOÕšU§ļŗ×Mˆ@ĀĢ€Áđš&wë§Ņķī°¤ üĩf‘Ožä;ûëé¯hĻÔĩÖ7Åu_Ú`Âįąõu;žÚ„´ãđyHiƒĮ]ûBím2Îø B^6…čķ?ƒÔĐä[T¤@‚ŋmlŠqCõÄײŖņųL“{IĢv…^ũø rŗģÉķøøĄÉ•ŗâ;ZÛ<­÷ßãš ųrí2ā{§ī|ũׇ6==?¤đ{} ÅĪ#mĪâõŪ1ģŽũKOŊū1}P0 =XęÆöN úÃØ_Ļ 렃ö §aĪ`QsAƒŖ-ãpša¸.ĒU§ĸ}ĐN˙DLR;ĖGđęâáeÍxˆŦ!ŨŋŖÛũˇŗxøZzē‘vļÍĮz-đߍôˇJí$ ë%­•ˇ <đ/„{Ö!}Ëįčƒv"émĐĸ™ã˙ŽWĄy \‰ø<éS¨Ôā>^†9|ŦPŒíWâ\3´Ûa.ļ×b˙Al+Įú÷ø\…øFįGü%Ä{u7Āš!~Ûŗq3ÂÜ÷ZöŌļûWĐí$įœ‹PŒk´cŨˆp5ŌņsŒäíäeC^öëąŋņ<\‚ o‡Zœã+Î3ä ?…ķŸW!ū0îc“ūÄ!…> hú÷vŦ:°ŠõŊ.„ąô4I—Ŗü>…Ë4ŸÁLzÔiäĶ\<ã)ŠŽŅƒ˙w¸Īš=8Ÿû ʤû!ˆQØˇT{'üہŽ@øŖ_íĸ~]ķ߃đ$ΚHčB-ĖÂņ™bžĪ ņ‡øÚÉ>qŪ •lŅŨ ×"ßĪq‹Áņ"|@^&zĀņĢpũ%œį\îdNĪ8Īt¤q#ØąŊE@;˜WûQŽ˙@ũūįZĢęáŧūæŠzÛ|zĻ‚āũv8‚páU„ŖČŗ[&">Ą iˆ׎F=JúŠ:ƒ|HúēÁõŸËJčŦr†rĄcÂfˆĮ[qžģ×> Ëž@xižāöÂu–ī307ˇ)Ž3Zč÷ØKˇĶp~NŽS}5ˇ=€Ö>DŨ ÔÜî¸îķšē`ÖsXpåú¨9_ÄūŅšMôÕũgõãūÜĸ~šT]_¨šr^ôÕá Áī]°ņ:Š ĒŲõP"ũji/tiFĄ,øWđŗŅ¯`ąū0pO:Ÿī9¯ž›ƒî}rĩæ0|#øų><€õBé}š(ŊO4š'ü×yUķ]!đ ęķVúxÍa`ß˙´ũ˙čš' ņ/5īûũxžÛ¸Mčž"Ãä@íģV!¤ęĶČŨú¤[7,ZŒmZn .­qAžtФHôNlŸ­9ėf¸Dú Nƒ5oãû×> “đŒšĮEĖßđĮ\öú5ū/uĮđüĪÁgŌ>¤‰‡ŋi^ãgAŒgĒcö¯äséæø÷I‡Í~lCcŽņĨōcÎ@^æŧĀ9ĩķDĖ>¤yûjā¨ŽŽĐUãē áo:+ļņĩnFųgbíķŋ&âõ*ŒoPËžCŨjēxĩæZ˙‹Ŧl8Ė^FģģÎ˙ĄæŦëøŲE~íGä¨#Ú§0?ãųÄíã“ā.íčÔžŌŋ Sķ)Ō„"víHBŧÔ˙…ęˇK˜ÛDŸ‹ú­ä2J>Ŗ›č˙PģIŦW"öĀķ”vXΞ…+č>(B_2]ŋuežˆĶ7Ąū}Œđđ…/ŲøR{ÜĮ×ãtĪāüÕ¤‰!Æ ą×œˇgZKØe°÷û9â÷ Ú/îĩoŸkásąĮ_؟ØŸĮqé>¸Ā ÁŠÔŊĶÔQ˙PËŧF™náqAģ}Ūčû*0g Ã{ôâŨĻo=ûŽë¯ąíÄ3đžĶkĀļEXīÆ:ĄۑÆ˙ļKqh+ŠŸZ†mWc7ļ㝨ī\ū Ŧ_8w !XžŦoE¸á6„RPęŗQö㟆õ lÃųÎŨc~ÄįÄīAø á4Â&„›pĖqėOG(Ãį% \ˇ/Čkū×ë_Žg˙mÍũß'ÖŖĐŋ8?&ũ×u@ž˙Ą>?vä˙Ÿę9čyĩ‡Ā9ÄŌ35N1|  o‹>ǐûeîš?ūH­E øÅ/y Áz úÁ3Üsˆžø%ô‡×bŨ5ĪAŸCšE}ņKą C”̝î>!f  éN€ršæˇčd%Ų@n#‘.rŒøi}™žJ˙ÂcĖĀl[ĮÖŗ‡Ø›R4Uš'͗n—‘vKĨ¤ŋkökū ųRs&auÂOrˆ)'ȉr˛œ)—säŅrĄ\¸…=")Xš.]%m”î”î•–vHŨŌ‡Ōš.ÍAÍۚoV%l–ƒäpŲ*ËȁTä@ļ\ rājäĀ#ȁíƒ80Ķ~Ĩ}cB‘1‰ *Ēkä_áĀ´>ltnvnīãĀkȁ‘Ŗû8āIžú ō-ųV:KŠgG!\g'œ-åđĘߓø#ČUd:ųƒ˙ŠBī‚|IōG{n3>{šõĻõĻöëMAeņû~č=×{ĸ÷Ũs9wėÜÛį^˙´ā¯Į”w2'V#Üņɕ'Ž?ņĶ'ÛN,Æ'ô¸'6"Ŧ;ąü“Ž¯ū¸ķāOŸ¸åãmßyüÎãGīv|+ûąõøÂãķņiøq×ņœãIĮJ•+zė/Į?ž˜å\Ėr.f9ŗœūæ‹YÎÅ,įb–ķ˙Ÿ,ĮårSxÉč‚Qų#ss˛G ĪĘĖHOK–24Ų™äH´Ë!qą1Ņ֨Ȉđ°PKH°9Čd4čuZÄ(ôGi•Ü•\Õ%%;&NĖāĪ76¸4TuÉØT:˜ĻKŽdō`JR֝GéR(]}”Ä"BaFē\âģŽ;än2wz9â7;*äŽSŸ,p)Y<˜ņÁnĮrItCąÜEĒ䒮ŌE ëJNJqž&ãĮ1#vMˆšë˛:ZwëX"j-Ŋ“‚ŪŒģęŠu—tÅ8Šų瘺Ä]Û5mzyIqœŨ^‘‘ŪE&Ô8ĒģĀ1ž+$MĀąL—vB—N,#{ųqā&ygúáuëģ-P]•Të¨uĪ+ībî žFhŽ[Üe]z2ē˙'›Pžf`o[Wí•ųãēukäŽÍĶËöÚyYQsāXę,­ZWŠK¯į\ŒÎđíķŖ(‡ō8JxKÕÕr—Á1ŪҰîę*Hėē.˜ŅißëÚī?ą%ōēYå{WQœŖÂ]t­š^Ž<ËPˇ \Yi]´Š÷ôDÎæ=Ģ=}Ã̍ž{DŲĨOîû/Ä^Ō0ē‹Dũ›nŌ_6ĶQ6}nš\˛ŽJåmŲŦAOJ˙¨ž>ë ŸPÎ⨊Ņ8&zQįõķ‡ō .ɉ˙i…&×vëô¨Šĸ…ČĨ]–ljJYa´Û˙ËAŨūoų(QõSˇŲ5:mđķ%ƒžm/hà KÉ´lÖÜu댃úJŅīŦ[WęK×U­swûWU;d‹cŨ~ú(}t]kIU@ĸŨū7Åu•ޝĀC4ŅãÂ`%Ũ O!B8 Áp,§"ĖG`āĸ[wmČquc5_Tģ§LĪ^ÅëË'g‹g×DĨ6š•Ú0ZЇįpē-ģK–đį-ģŗG+ĪŠ#”į$göĘqēe~Z”!Xf!!ŦDpņ-ģ#ã•a†>ė‘ŨąqŲ!‡č#HņŽ{Dlņ—ģÃĻj§ęčéqųä+œm“(WŠrž(‹D™%Ęĩ÷Kžē(‰ō)Qf‰˛H”SEŲ"JA!íųŋ_á÷KōĨ+ Ō ØˆŨē¸Ō‰Ë†9“˜våÚní&&W~Ž-Sž`ËFȑ/ĩĨcmCX–:Ņ–`O-ļåczNĀ€7u=X­haĄzW7yr_īsĪ3ēIŅŽÔËmã d4øry÷"HģRÛlĪâhY<â->ąËv6Ŗ›ĖŲeûŲÖ­'ģl?Ųē)q…Ûūe;iûŅvĐöŊí2ÛĢŠOØö#ÕŊģlŨļn Š6§vĶ'\!ļ›l3ps'mKlļfYt5Úąr™l58hnę\[9ē \eŠ,VšÔ†ĶôiÛ[›-Ķ&–KW–Ļė-…WOۆâb‰b•ÛlŗÁlČßxLˇq›nãVŨÆēãt/ŅmĖĶmŠÛ8\ˇ1Kˇ1MˇŅŠÛ¯‹Đ‡é-ú`}Ū¨×ëĩzIOõ čöŸpĨņÛ„ÖÂ+­ÄKIāĘKūFŲE‰žÂeĐÎĘhŲĖņ¤Ŧëp ”UË]?Ėtt#ÚĄÆ1žt…•AŲŦņŅ]ŖŌĘēuū]ųie]ēiW–ī$ä– líĸkģ Ė*ī&1ŧiusûQĒ1ĢoŽãĩõÍĩ¨(ē(llhAiņ/Uj™Ö˙‰Nô)›ÖšĨ\ž[gŖÃĮ™ø¸‘?näŅņ]w–Í,īÚ_Ņ•Í|EY×í3åyåû1͞¤x?ĻüXU”īgédGÉ ŪÎŌ‹+*ĘP4‚Õ~§ÛÁ+¤ĶEœŠô:‰(tA‡j§ĐEÉātŽ(y]ų§KåŌYO@‚ K°ž@ˇķ€Ŗ¤x§Ã˜ë€ 9 ĖÕU(Hl6$ąÛ šŠMØ$Ĩũ$*IfIĻX‰‘~›Bc–4fžRÚõņŒOK+ņr]™VžSã+0 ˆ:ĘŌ:VČŨ3öҏđ.û L˜I™ãĄ¨(:ÍRH˛´A]ZlŌ!pęKėŅ+âH@ļ ę l6Ģ]ã2Æņ.Ô^ŪĖS2ĩ+zÅ%ö¸d›ÚeÁæP\cĀ>}žü@t‰ˇ¸īŋvõĶĄÖ>(ëJYÖU„h§NW‚YJqļ ´™L%ŨūÃJc&6ōFÆúûÚ •šņôÔt2ÕFōq ií¸\h }í 91ļBŦ” Ņūŋ!|Áë^¯˙Ū×Ûâ˙+åīöĒ |žC°ī([ņģ,DÂģx'ŪåoÂûʗ°o귒=x¯^Š7û‡á ų=m…šxļâ}û0œ0˙Ûx—_NĖ …0ŧŋ9pĢ ÄĀŧ“īg¯°?ųŋ!Ĩ¤ũDà xš}‰ŽŅDkÚũ ŧGčå¸īPˆ„|˜S`îé1Üë‹p”¤h&ø?;¸`&ŽÜ ˇĀ#đŲ@=´ƒna¯hfûīõã*8“’ĄŧHÕ‹á^<Įiŧģ‡ã­ú3-Ũßû]īO~ūŪc(äÂ8(<Í đ:Ū†?ƒ‘Ų¤X̤‘ęũQū=¸įxČF¯vL†ŲP×ĀJ䨰“>ÂÖ÷žĐû#z>†ß Üu>ŒÆķĪE^?ã-?īáCÉD2“xÉfroĖôZē…ūˆ÷¸üæąGØ^öö1û‡4QZ"}Ž5ųSüeū˙˙&˙!˙'ČS¤Āå8į<¸ ÜxĒÅp-\kQZ÷ã÷؄÷ŧĄĐÂáxĢ˙žƒI0É&—BRGÉt@{É>ōy—VR7}˜Á;á\\{ ZCą4Mj—Ūí…ŪQŊë{wöžéöīōŋė˙Ú߃ܴ!ĪČŅ (ŽŧnÅ[ęŖđ;ßpŽÁߑsüZHą’$2ŒdāÍ?L#ĶÉ\RO|¤“ü†ÜB6’{Čũ¤‹ėÆŨōŽĻ n¤…´‹LĻQėŸxƒ_‘ėqrÎĀĶTĸ’4é1˛žAKZOĐ%RšBz\ę!>é]ŧFŸ„­ôžŽ6Bz W[„ĩ‰ŒEŦšāA¯c:ˇÂķđ Ö@7ĸÜ7@2#É$.zžBë%Ep5ÚIyDãŖ‘Ĩė sHũ˜\ĸņA†ōkÉn:…ŊNNĸå=ƒúRFhІøœ§ŗa2Ŋ‘ę5ī‘ŋ42EĶ€úŌ 6‰ÕŅpzđ‚ˇ‚OÁ´„#p9{æ‘ÛĐúĐ4˜D[āö{ōwØCŽ‘ęYîr •Čuh OÂn6Q2ÁxØÃöĀŗdû€¤ÁSŌŌLn÷—ôTÂ÷Ú­ŌļS“' ņŋÖ{ŒĄ‚6™ÂëbĢâdžÔÄW',ŖËØ:ēŽŨ÷¨ļ›Ym2ÄĮ…Z4L;$V+I6ŗĄ‘mxʧí$˙ā°ĀS†’—]&ˆI4EĮ:nÜ1@gNY>úUt*ÔZ`- ĄaüP(ŌƜąO„_U€CķōPž\ĀZVĢŗë– ŠŲōå-še‰Ö õ÷Úwƞ=¸üõēWī+7\g§×íÜtpaŪ”˛œBĪÍŒũÃ' ĪÔŪļf֏W¯-ÜĘÆu ęëo5[!žß[j]lĨļn˙ģ-ĄšūS˛d†9‹CļåÆp6Ô괕ZīŠ{ÉĒą†FEDĻ€“XLĖ%‡‡E„‡‡YcƒÉeĐMBwۃ‹bģ‰iWØeĄŨd‰+(<2Ē("\ŌÉvs(W C+rÆ 5ÅwĶÕģS¸v[Î(ē%´›Û\Ī™žS–ž“ČUŪ€(gQAXAhNΚ`ĄÜ 2<_…Ÿ×Į¯pģÎÎĶ"#¨äHLN™ģĄ›NhwÍÚŅxëĸŠ7yđ/ô GĪ›Į^W˛íÃßööÜšây7y$l¨~ü —úŽ7-ûí嗛č7[{oĘČ8ūÜãŊŸŪĶöJ­97ĩ([sL‡+š2– (ļHĶĖ ‚+‚ë4ŪāUÍĐ ü ÚĐEÆEĻ5ôF3˙oŖ.8Äb1‡†…[,ás¸z “)Á¨‹Ānj1›t4B§Ŗ&Ŗ‘ĻPBusƒÂ͖š4Å”zŗš›üėŠ 2P猠…ņY AAg :—n:Ŗnrƒkp˙avxø˛>Nt)1˛CgÅõJŸD†X Ķx•å0gÂ™Ĩ“?áLą(.á0‰žĄRÔĸ‡7ö¨MÅJ¸ >Û)œôį&ŸœķrÍōÖdFs–ōJĪëā^Xc ÂŊTV"ˇÃŖŦQÖŧüđâ ÉCĩ:VKržũqáøäė¸a{õžų|īoŨy!i4zū|îÉÚíû_dWũ\ĘĻžxbÃÛįVŖdzUÉá~Wx1)ĻŗČ,ÔIŖĄZ^×âJĶ€QFîŲ5úFž6j"(ÕhŒŊž"덠7,ĶģŒĐ V”Ķŋ\A™,s҇5MōūĶĪ贘XKOô™ØSXŸŠ>s*:Vœē¨ģĪ”Ôƒ§ >­]5/õ‹F÷~ūûíYÁķ.ĄĻ×ÎĶ8wį7ˇ=ĶĖ˙Œņæúŋō¤ą0FÂsŽéWf§Ņir9ĶGŖ#×fé ôWØëíRnzĒIĘJI6ŗp&8RŌX¸Ų˜›’––n4Ŗ‚™Ŗ’lVbn‹Õ%ŗmĖd-‰"QŨ䎄,Y›œ"'@šÅŅę ‚+4,, - ,áēō!KîP?¯L›üCå)‹đ!=ˆq'RtǧōäšāĖ´`Õypā$4ĖZ€˙Švj1bĸwČËaddîĐ~ËEPĮĐšXÉáčwƒ)úîf˜åĒ5ˇī™~ƒ{ ™}YdfQgÛ­ö}Ŗūš˙Åöō˜K†Dí “|EŨƒŋīuĪŨZuũô˛ß­Š¸qfXPpüe#Š’˛=•–ˇ]UÚ:ģĩ÷_+Ļf_•K>ą‚ĶŽ*¸ŧzūvžO#'rkœsÕÅōđ”Ā É>ÕŲš.ôņĐũĄÚaĄYÎ"įĨ‘s"ë"ĩK턅ED&†ã&ÃØ$Ļĩ…Sę Ļ‡î X’ÍĻՅ§€1Úb2ČaEC ÉR4dęĶC4C†t“CŽ @cXk7Ésņŋ0æķŸ9ĪI.lŽį<˙Õv™ +} 'Ė+ßŨ:„ä§UV!Í{Nöû5Õģ[ ¸žĀ*…{G øņ`†‚Ē × ôå6Å${o͜õ—ßņō´E+ŽãŨœ‘ÚD~ãžŋŠîÚųÕ[ō‡Ą]ž™:îø{7ši~VKÛĢdOâÚ[V“ØÅ7üöŽ:×ČëW‘×!`ƒmû!Ū‚Â0į#Į L,ÅĮ0oPˇųé`]TpDü0#ōŌā+‚ĩV’EėÆôČ9Æ:Ŗf4É6F–‘ņÆË"ĩŅ!!čJ# Ag3čB‚6j2ŋ\ô†%d~HKČæ)¤›$=mˇČšd9y?q e>Ãŗƒ*'Ÿė9)b!ō& ÍtšāJ%YČųâT´6ŒkŠpM"Ē C0e–ûŋëÕM§—ŧäY˛§÷ÍĮz‡§_}Ų˛ÚޝˇĀ;ņŪ]ŋ÷<ˇųŊäįRōû–UŗWm˙yÅ-Ŗoú€ëŪÕȏqhß1‡÷ƒų`@†Ød,ĸ8WĘ9W´)‰7Eß#EĮ\Ku°7æÅ–ĖŌM‹c×ÄJĀi!.X ‰‡$ ŠBE,d"™)ÅÅχn ÛFÃÂ$Ų¤ŗÚ$SX7ŊÍ!ë“ņrˆË*įBˆ%¤5äcäÔØ¤äą"yB[Wė\°į˛ĻRä • O*éĩāÕ4nÛm yv@ŦZ5ü‡%))˛]Ģ1ąĢŲ›Ö•Ü{ú÷‹^ŦˆĀĪ~|î;鯚Ę=ŊIčŠ×.đ"Ū°ëžnz{õréϝߘ2ÃsĮKÉŌ!AkoŨŒūŊŗâ š?B<īĒwčøī§› t‡Ą‰&ÉayaLBã“"YXdTT(â  21“!ŗ'h0ęj‚e‰ é,Ų!1mi¸/‚ų,čųÃ|‘‘†¨¨r0H>L%˛”ä9bÕđúz4ÂĨ–īųÃÉķÉŦʓY'•ŧY¸ Ī4ššbXåUÆRX¨Cā[ˆū0'ܑŸ“ΝV°/]ŽÎÁ*Ÿ{8ūa[tN{MÉuöycGæGDŋ˙ÚsėŪõw-Ŧ˙`ôȚļõįę¸öŒėŊBēĩ'rHü~HVŦiDˇRgsũ)â~lDÄ*Ef'{’WÔ8͆¤Î0gdÚ$LuĶ­VŖ1&Ŝã0Z1^XI€—čώ¸rĖYļ]tŠU›b3ĩ︐čhCLL9ÆP+2Ë`]i%6k–u•õ-Ģ4ßJ#M7uî18d fŠoš†PyƒØ_´$1!ÆÜĢÅh5æ“įĢ™ĪB*’Ī1ĸ|féI[ú]eåÂ6‘u¨ÎímQ Ž/åi Ds•,?…š*†GZޘk‚-/čĮ&,8ÍjÕF)÷´üü֜ŸÃ‚Šĸ˛áxë÷uÁtô>šč1eËÄŦ”°›7=ôŅ_/gaŌÖm¯¯^ĩ˙ĘŋE&´Wt5Ũē`ü5 ōĢBĮŽ š]phΆSí&é÷ŧ´ãŦ˙ņß7Œ_9#†ÎlƟ<}9Ņ.žîžKo}įÅ˜×^‚^0šD¸_*‘dąŲĖÔ@œúI¤T[Ŗ3TW¯[Ē_ŠŅį ū`¨V2IÁ4ūÆMit´CÉ!1t˜-fŗ%#=fThƒÛË͆ bą˛ĖEæ•æˇĖ’Å<Õ<ßÜb–0›¤Ë]ą€ Ņҍîa?į‡Ÿ‹#Ų:gŦøU8ŽÜã*åĀLē/ Š=–€Āĸû¯•ũ–ĩȎtÁŠQā§ *ˆC:æW]fƒ<ütxvĖģū6ųēŊkâ'Wīō¤ÎĮ s¤zöMmŖîėš™^ˇ>)w|ũî—{GĄōŽA#IB>3Б,—ņ1öûûžI†n˙a×åYŖr§VŪ60›!˰Éđ”áÁoЂF’CVa)T§sH$‚ˇÔpŪâ•P—"‘k:]ŗd°ŽĄáFã„̤ˇ%*šL!šR‡ƒˇ¤2NeZÚÂ4äFčŊ’krf‘f(J.’\câiwY˛Ō<Ύ­)X„9”ŽøáJ=$KŠ­*Š!‚“ÆOģbėEƒ~[ŽâŠ~Š— >7f€wJ[Hōst$<‡‘’´=iŊÅĮ÷—N9r6\J>ûgŽÃyČÛxÁÛ^×ė* ™ĒYĨy[ÃôÄĻÉŌlŌ<Ĩ9¤ņktxisô]ڀĄfŠ‹Zs@áÂāŧtŧâr™0ël”œg~ß; äZ›Â4pE‡A€iĀ™&ž‚‡äã2 8ŗxĶn{žR#“ Ā$āL­Č$PšÍë}ãx§#l0įúXwÁ›‘¯EļņKb*fī›x?,EÎLĀ;Č*ôĘŠd’kLi(IwŒš›Ō:Ĩŋe}Õņ7ĒŊĮzcGԎħŌZĩ%Ásôŗƒ¯Ģ ^™ŽWÖD}bđH}NpŠ^›Î7>ÕlÉeÃR)MMåœ$˛Ĩ#24>!Áa“#dŪ ›M s„GDDđ†á´iclAAÂ+hSm <Mī&īēĖ!†°ō „[Â)fž \f[ŧ%ĄŨĩÍbŖ6ŪbjI-'–~0' #[l–pŽÔŠŊ+€IŠ“{°PĐØ Ä"‚#~1/š5™č™š@,z0ÚīŌ¸SHÃP™Ŗ.Áú‹Ža >áø^§gSeÍęČéģkV¯ļnØ{[øøÂéÛ*{ī´ŒËüøÕ‰^)ųŠ…sŧWÕÖŦhą°g}vŽ3ˇ°zĶŖ==ôČ$[ŽĢúЇ{Ē§.@YZá´kZ’n¤Ž:h’>–ęįĐ+‚ęh§~IčöĐCč ßĐŋĖĸŦTŌ2jĩ Yš,­BVĒ“ļ`C›…¨žšu“^W(Ū7ĩ)AVŗã"wļ†n˛oWPš+ēmŌī˛Ōå˜'Rr`—ĩœt“Žđ˛‰îwŌ\<ĨÅj!VÂg‹ŧ¤0­¨b,'Ŗ…V9Î}°Âqž§ŦéģŽ÷q|¯Ņß2tÁ÷ĩˇĮŽŲ{s䤒›>ĘŠ—’÷7ÕŽī¸deĪrúPuÖČņ¯üŗ7 ēŗÛČŊ`aÉ~Å|d&æ#q⠁T%ļ&R­&.2"UDĖœ“0ĮÖYeĶNĐŸeQIJØĨ {˜fˆMŌ…ŲLĻ\Yšl‘AgŅĩ꘎=1ŲŖ¤Ē"SMë{e‹aã OKÃ-ųßMåķ„”ŋōSã|ížģž?üÕo{OßuÍë önlŨV]iģĩyöú…#Éí$˙mßžą¯÷ÅmW?ë÷eU-ģ´æĘ›Ļß˙–ĮŲ•xž8ãZf`7n×ßj´æ(ķVũËŌßĨŸ™6™ĻHŖHH:ɍDB™‰†„‚wš^kRÂļ6DqŽ!!åā ļäŠWYahp0œ˙!ĖÅĒ Ũäˇ(mEî æX„¯|}?)ėĨû]ĨĀmøo:šģęƒ­čåĖQŠë˰ŠzW‚ęõ*Ôđö‹ģ()@čˆÖ *ĸd¯ UcsƌfäMŊ,kÔüW æJÉ-[4t[â{Ŋ§zįp~MA}`ȝtøn¯)5ī9üM(֌§Ē1ˆÜažß~"[ĖÆÜiē#H2qU‘yB‹ĩS#˛šŨŊÅ´Õ,•˛NĶZK J˛':FIr‰ÅŖÅZ"Ö¤¨áDȰX[¸NcfŠ—]˜ųH:ŋüHšĖĶÂ|—%ƒŋųV/ƒĶâ¤ÎoŖ8ĮB“†åB”%Šžˆ"QĪeÎyNQĩ…i“ĪTöœŦD´íšŲÂSëŅIū•ŋ^V^z€’|*īNÃķ•÷Ļü­GŌPžr ĻKæĖ‹Äė“##ø+’ĄÉŗ÷ŋvΒ%IÎŪOR&ŋ˛į•w¤ŌĒŽĢ2–ŋ7Įũōšîk¯% LSšKĢÆeĨĻ.‹Ö2qŞũwUĩÎÉÎNŽÍ››;sņÔģ¯ŧōJ<`ģ˙z›fÄÂZWęe!u!‹BքÜ|Oøc†Ž!‡‡|ŽŽŽ0ˆ 0SzhF f ų6ũĶ.‹/ėé…pˇ;ĸÜÔMãv™}Ļgh*kIϤtTV‹aƒēé†ŨqŖvķėTĻ9yųÁË“ę›æĸPqâÚãԉsŽĖÍį— đ|Ư>Ęm‘|•0nLŖkxėĩâ7äŋ5}WÂÎeVgjáíŋ ™RâXAŊë‰fyīŠõ={[ŖäD<ßÃčĨ;Ĩd0Âޞ°ĄL*5ē‚ĻŨ¨[kXô(ŲjÜGLZÆ% 5Ž ĻĮ9zM„^¯ŅPžæ(īm”—Z#fĪå ˇčŠž›Dĸƒ.—Épō-a-dĄÄo:@&‹—a\ü=ßWöŋôã&ĸÖâ9VĸîŅÃŦš$­ÂÎú^û‘i;ēŽ ‰ĘNjõl–’{öU}´đ6ē“+)xž ]Ž^K’BB,`É 5†[­e‹/”#-ģt>=-hOáR.Sp.œ;]&Ū‡¤‰(~æ$ŪgNNą”xŠ?‡ĸɧøņN)†hņF×ŧfā+ͅ9ų$īVö|ģŽhuy9v]J<ŊdLŲĩŖz;d62ēįđ÷]ŖČÃöõôēâIæģÚz:ķĢ ›”ß›/-å¯ĨūJ˙>ČȏYā‚4ß@ā¯Ē5j̏ŅÚt×@´æœŠkaˆļXÅuđ‚ļJÅõŦëRqŦ3¨âFéyą2ĮMP\­âAPü‚Š›ĩ{tI* ķBRûūČËʐúūVēÆâ üąsІ-Wqyau*.APØũ*ŽAüz×BpØ6×AuØĶ*އđđĢTÜ%Qm*n¤n‹FÅM0"jŸŠANÔ*nfsÃŖU<2­qü¯ĐI ÷l­âÄZ' \#Ú[Tœˇ×\ËųoŊQőįÖå׉ö‡Tœˇß!pŊhNÅyû^Tų*¸"_WäĢāŠ|\‘¯‚+ōUpEž ŽČWÁų*¸"_WäĢāŠ|\‘¯‚+ōUpEž7ā•q¯LØĄžŨ„í˛zö ū×ī­ЏCŦGŒízë÷*.áØŋ œŋf Ž6¨8ŸŋGāáĸŨĄâØmxĞG āy¤ Ŗâœ~„ĀŖDû*ÎÛËÃį‰^¨â8O´Gāq‚ūFįôŠŦãŦ?`]›˜įįķÜ%đ$1ĪAįķ<%đTŅūžŠķöWž!æ9­â|žO9ŽĀũūëœK?ā\AčƒĐ KP@.ŗ ŗ*ԁj°–áq„YĐ đÉĐÍ>•JÆģ] ´!ÎK7ļ{…Œ-8>ąbŅîū˙8SVßÎd˜‰=â¯”(4íØ6 keŊP€ßᐥbŲĸuŽhÄzŽŠĮ=øÄ¨8_;B,²VėĄû<ĐÔˇ“6\WF*ˇē’BīEÉ8‚į36cĮWá=nąR:—[”‘MbF~‚Ü}“˜Ņ‹=>AŨ Öâ\÷Š+´‹ֈą>Ņß,fá5ßS‹ØƒW=K̘›ī¨FėĒ]ŦÆ{8}­¨•ũwˆÕdąÂĀ]yÅü>ėoĪ‹ÅÜ ęę•ļEĖĨŦhosûTŽÔā“™ķé|8§Gpŋĩ2wÚŌ!8ÍeÕ¯%-B.m‚Ŗb<ß)׎&uT`…1~‘ēĒW=)īS¸ŲĪ…:¤äŗ)­ũ|õĒÜmQOâôâŠ_ĒíBcÅî~Y'–ĶŪwøW5|žū9ÚpęnŨ*˙k„NËĒŪxV+ÖŽ­ĘøÅØãUeČiÅŋĶã#Z°ŦĮžE*ˇ•úmŲ-dĨh‡,xXŖžß+¤Ö(hZ…)ÚØ,F*'¨ŨŪ>͒ą‰*™&ąŽ›ŠÜÚUKnėÛG“xę×^ßyūĻũŧķÕ¨kT‹:§kéĻb{€ŗ\ˇkúNX't[:°Dđļ]čOHŖžOę|īŠŊs[JīŗĻvUËúũ‘ŌÛ$$â†Ĩbŧ˛k>očí×4eõZÁ­Va%}§ŦÍĮ/ũnÁ‰6u nC }b|`ĮŲ[…5 Ø[æ~uô ŠqW/ôß-v6Iœ­Qp›˙ĢKeõ€įåžs–2æß“M›°Ådž šyāLũíƒÚ'Ŗ-ô÷íļŅĻúŠ&ąŖ}zņ'YÖĢŪĶŖúÄ~ßĻĖ:cˆ ĶÄx’Åz“ąœŠk× mp™ësģPƒ:[&LAēYx˛R„ x"ŽOÅV>žËËE{ ļĖĒÛÍĨČųüN­ŗĀŒ73ŗ„ώ˙‚Č}íʎiˇĒúĐo?ōG‰“-ȃ6ĄQ ‚:pž@´č`ĩčíDúŽž5kúüŽÂģ1ļß_zT‹â^­ßĮ+žÅĢúķvÕßԋY<}ūšķļB]{žEǟ¯î‹”Ęšžـ.îķœÕxúė­Mø6ŸękęT[ų%~<į˜gĀ,ũæÂõjUũâē\-ŧļ˛ëjU2ÍęĖŋ$ĄĄâTƒ9ĨD‹ ĩâ•~—{XˇČ‚ܸjŖĘívÕŋũÚڙB÷›ĀΠdáQ3 –ŖDˇØQĢā,u^ao˙Yæ˛Ē‹Íün`]nũĩ‚ĶŪŽm@––ŪGŨ6@oûķŠĪ)žģ&1@¯Z͡Xȁæ@ođŨũ”-HĢø™Áq>Cßy”} Ôî&ÕÛ+üWŦĒUՏū¨0X‡ūŨ‰úõc’8û…’ ä…<zÔėQ9’‹ÖŠ6Ÿ'ƒļķøŨ?3?_‹ˆĩĒ_]$ōļÅ00ķûĪŌĖ§Ø¤GÍOGņĀ|ĘQáV6]#æŧЎsŸĮëē˙Ņnûš|á ƒs‘Á;ō¨ļãh`eÆakđ: r!ŖĻŒå|ĘĀ;J.Âpā÷ōŲPĻRĮŪؓĢâųƒĀGåÁHŧĪpāŗ7ˆ<Ļ×ËÂībņÍųĀ`‹¯žī×âĮŠ…u.îĶ % zUoË÷4Cxh%†NQsŗ5ëįöŠDŌ6Ņ㘉eÜāZÅoc<›øŸí;KĐķŋQ™…ĨOx.Ģ,{æ -Qō‰Ė>Ę˙Ũ‹@Ąõü¯ŦčË:OûæžÕŲęŠs×xäĮåY yrKs‹›ä -m­-mnŸˇĨYnmŦɔ‹Ũ>÷ Ęâ“É3[;xKģ<ŠĮ((žEvĻ<ŽąQžá­ođĩË3<ížļEžÚ -Í>OŸ¤­Snwã l÷ÖÉĩžvo}sē<ŽÍën”kĘíÅÎĻ–6ÜĐŅänöļûäšw›ģƇÚ}ۚvŲ×ān–ą¯SnŠ“Ŋ¸Jk›§ÖSãiooik—ŨÍĩ˛įī¨iŊęTŪfŲ×Ņė‘{} 8܃­-ĩ|4ĮŨ¸ŽwãfmžÅžfŸ×ƒÔ5ˆt´ufʂ%-‹o;Ÿ˜“ˇļĩ4ĩˆŲ2ē:Z9Ú O}GŖģMžäs7zkFĪÁá\yŗ3GeË)“Ŋ5m-\TÃb…Hā >y–xÚ&ĪjC­hrˇ-āŧøw6§ŦGõô & mCŌŲ3åinŸœ,Κ,O­ĢË[ö4ļ{7 Y攊ŗ&•Nš0nÖ¤ŠSäŠĨōå“&”L™Y"ģtFIÉä’)ŗĖFŗqV ) .0>1ųáōéÛÚdK}›ģĩĄSŦÃ͂s°ēSîléā#k¸îâî:šk…^ĸļ Ē Gmņĸž#šģžÍãáz)Wā°7*UK57Jé´ÎĀÅ\9=¨.ˇ6OĩĻĨŌŋ/Ž-õA"Ļo mĄēÇSã6[Đ>hh{`Sh}ŦčĖuW^änėpWŖžēÛQߎΔg7 č œΤ Å-ˇˇzjŧuۚ O.#›…îōąîÚZ/—1ĒI›piéŧšMđVøŠķ6Õčmōōá"‚nqKۂvEå…v‹Æ–Ũ3ՍŪöžÎĨ°ģ Õ÷ĸjí”SP94x!ÁIuũ‡ãžpa‡§],ƒ^´ÆĶÖŦž MŨˇ nohéhŦE]]äõ,VœßĮįt(Iú“Ú~‡ŲwFܖpĶ5ž~ķƒšÕ]×ũō´bË}T/ĸN„ë¸}Ŗ9Áė™ãä 9eTnū09ĨŒášÃ‡ ŗË°qøˆššXæįäËųy# F˜ >_ëčŦŦŋg6_ĶŌ4Đ&oĮÍârã…/6°§įZ*Ōz7ü؁N(endstream endobj 49 0 obj << /Ascent 923.8281 /CapHeight 660.6445 /Descent -812.5 /Flags 4 /FontBBox [ -1461.9141 -812.5 1722.6563 1134.7656 ] /FontFile2 50 0 R /FontName /Gautami /ItalicAngle 0 /StemV 84.4727 /Type /FontDescriptor >> endobj 50 0 obj << /Filter /FlateDecode /Length1 16780 /Length 6207 >> stream xœíZ t\ÕyūßŊ3’,Œlœ ,ƒŽ< )¸>ByôʇdUĶfˆÖģä&æ"ä Öoƒļ™ÚXUķh˜ķ§Ā˙4MŅžŖj@9Χ(/§¨ĸ&ļ.ĶÔċhēĢ냔Ī-Zç×Rjá—S˙îgĶyü ܟĄɯdnŖjötŨF°ŠÚ}ĩ°s`ēäŨ Ųå3`˙ :Ÿ΃€$|Ũ}!ü}|vŅEĘį—áC;šŨûĄīˇ€hUÎQu¯Î­§ęüųT“‡į|ĀJ:WŌ͐ŗˆĻš¸m•÷ŧPfm’ļüë´ øy’'[~¨Š&uJJö):OŌ\oØkŽ>şûˆsĢ:öc˛!}fŨ…õwŠēg¤g Ģ/wc$ŦTį\ĩûŸFƒėi °ŗ v–Nwäí–ā¤Ë$¸Ŗ ōj1K°åēÖw•Ō+gˆÆų븘ņ)ËūdŧÖä}ƒŠķ“ČÍČŲXúQÎŨÜĮ>Öš;Š6ęÜ} įîĀ'Ūlĩ}^âvŨØÍés9š=pĐŽ÷{x_<ŠsķȝņÚ¸Áž§Ī„ô۟ŪįíũܝK83Sŗ]%Ôāz;•rŊMwēˇPāN‰ģŽÃķû™MĻīđGiŠĸĪ ;sæ*^ĩŽädäļ@nžƒįį§bÜķ\G^ö=úä×@ßjÜ@˙i~öm Š'ąŽpØēFŌĶëŠ?ĄäW@Nāų*āĶa3÷ÉĖfx#paWdËé{ˆq%/Ŗ/ãžFû1­ĘÕé ×īh Ū Ûr;ĮõäÕZiƒčWĀ—Đõė0úĄ€*”Ŋi'bZ-õ˘X!Ũ}&`!l­ƒ/›F˙€įėaÚāŽĀū ÍĮÚ-ČųSnĸ˙4žJ‘ē]Q:§„“›køûŠÛJŊ ]¸‚ŊŠtߒ3ŸâT‚īģfãąŦŖĒU W›—˛ÉŠßRāŅŌ5œÖ¨ĩ,pę(û⧆ã g5f×5 ¨į;¨e÷ĮĪd×ō$pU6Ț:=3oÕöē^ͰWž{įšŧÄÖO/ņŖô˜Õtđ"fī^í1 ņ­ôā}üF¸ûÍØ"ē‘7Đî÷âųn×S´ ëēî Įĸ€€g/žx@ōKYčũą›irļú#š‡éy~€,Š ÷\wŅk8’¸¯æ§iŸÁũĮāŊ=wY’ž[œJftÁ´9˛ĪeO˛O“_k8҇ē/ÉŅi•̐V˛V:ˆßG~÷c€(5åÔĐc9?ā 1kŸ ãßާjpΚ\sĐ—`?uötœËU’&š9ŋęĻáˇ-0m: ‡2?õ4Æ$ĪčüÕG˙_>ŸĐS{×vü>t“ ĪĶ^zRÚšZŗĻkÛĩÚö#ö2;ȟ˜´y’áᏧČ3×ŗÄŗÂSëiđŦ÷lõ´y|ž°įķĸP‰ibē8[ĖŗÅ"Q%jE\Ü/.›9sęĖ3gũ4…ßō°ą—žI/ÂÂf­ކ…:6Mō{4Û3Ų3Õ3ßŗĖs§ŪŗÆŗÅĶęi÷tzļ‹X8]‹B( +&´@ØĨŨ†ļP$œz.UxėŸ}{đÜÁEƒ•ƒƒ ņKcpÎāėÁŌÁŸ›zô߈~3õ7 í´üæĖWã¸æŧúâĢĄWŪyå–W–ēíāčˆvílčœ¸\ âęĀDėļiė2umˇ¯šŋŨ%˜¨Œü ^ĖĪd|:/á3øYül^Ę/ã3š—ŸÃgņŲ|ŸËįņų|/į y¯ä‹øš|1_—ōeŦ‹˜É.gW° ëf!fv%‹˛‹ŗëaŊl;ÛÁúØ_°ĢØÕl'û<ëįįņĪđå|¯b×°kŲuė ėzvû"ÛÅndė&v3ģ…ŨĘžÄvŗ/ŗ=ė/Ųmėvvģ“ŨÅžÂžĘžÆĪį+ųüBv7ü¯go˛Ãė-ö6ûoö{ö{—ũ ˛#ė({ŊĪŽąãlˆ%Ų0;ÁRœ¸ÆįÜÅŨ<‡įō<>‰×ō|^Ā=ŧOæ§ņ">…ŨĮeßbģŸ}—ũ5oā먓ē(@&]NWPzpĸ4ŗ ąŸėö û{ˆ=Ėö˛o˛{Ø#ėoØ>ĸęĒMM7Ŧ_ˇvMcÃÅõuĩ5ĢWU_tá+ΝZąü3į-ĢŦXX>wöŦsŧ3K‹§M):­° R^nŽÛřFåuŪúvaÍnˇ\ŗŊ åŗWAĪ"´[¤úŅ<–hWlb4g58;ĮpVۜÕN­HŦ¤• ËEWXĪÖzÅ~mÛĻVāˇÔzۄõ–Â×+Ü5[=âĄŦ ĸŽ8P+,­]ÔYõ=ēöZčÛW_ã­1ō–Ķžü ĀŦšŪČ>mBØÜēĒ}Œō ĨY‹ĪĒĶũVĶĻÖēڒ˛˛6EŖĨËĘŠąr•.aJŸé&ą¯üĀĀÍû‹¨Ŗ}Įīõë—ļZ\‡Đ¯øĸ5e5Ī[kÍëûm1B6ŦromĩĀ ek7g h–{V‘W !8ī}ëđhŠîPrf!‰Ę3iÂz'ø_Y™ôåĻũÕԁĢSĢũ,¨ŖäQĒŽ\Đfąvšr ŊōŠ-rĨ?Ŋ’o÷–ÉRÕĩ;˙zÅV‡XXŽėĢŗđëÂâŗÛ;|y׍om­ˇ–VĢēHĩîÄZˇoQ%øõvaĘ4ljĩ*Ŋkšwĩ͂50›[•ˆ#fMĢą¨ŨįHY•uĩŌ/Q7Đ^k;(uy7ĩ~Ÿ–¤í[*Jžģ„–R›ôÃút Š2ģn Õßi•ļ—øŅŸĸĩ¤ĖĒnCúÚŧ­F›Ŧ’ˇČšwæĘ”E%…ØÆp§™eäšŗōD++ám˛Z ˆz\ŧĢWbĄåR˛ĸĢWŠV­„Ōl°âpHl”<đY5 r‰KҚ†’˛ļ2ûķ.•8>šgYyYēŠ@ČødÛ9Šk6ˇthž¨3jŗĨÔí8čh›ØO&sá†Dž,gCz‰ĪÂ䂯 F‘d‹…EMĸÕkxÛŧčĄęĻV›ĖĩĒīÚfīÚMÛZUĩ.iõd¯/ĪŦ9Xú+ōŧk›$×Y"1Đhš¯cļ|ęR›Z}j` Ū+ęÚôũŠū¯(ōė[ģv R×.lEÂ÷§ŋŠÄĒŋšÍ*jhURŋˇŅ?āmn]‰4Čô:6É5­ô4—(-t—æēĘJ¯ œQzõUeĨf ŦtīNmīUÚŪ€–ãž]ęvÍ.ĖN/åŦŦ´’i‘pYЧhXĢ,ĐĻĐ´ŌŪž˛Ō3Š—”Vn×*§k•gj•=Zeą&ɆŋŦT#0û埰…­~ôŒ)Ú.aÍÜ4āŨnUoŪž/_ėÂî´eû>ϭ︌˛2͚ē–ÖļŦļN×po^mąšVZk­Üŧ֚ÔtIë>Mģĩ­dí~mw6ģÂŽũĩXŽ]ûnSkļ]Ōē_;S.^_ō}Ō4˛Öļ_K›Õt–å_ÛÜjõŸÕf-–ČîŗÚ([°`AL~Ô°Ā&,°?Ä_įīâˇëaŧ'íĨ§é~ē °]QŽMY \?tī<1@cũäĮ {ˆ/=ņ*5i‡>Ų āŸęãnp7Áë—đļw}^G ĨåFú×ÜWq%ūKūæ‰ų]ŸĮũôuēŸŊ눝>§āœ‚Sp NÁ)8§āœ‚Sđ˜ü˙nŧŸŋNœrizu›OrQŽF.,]ôėEĪj•ĪũúŲs-™R6eVŲ”˛~NÃũŒNāįm˛¸_ṹh†ļ5ķ˜ÅD™˙—‹'gĀW88§™TíāŽ,7R‡ƒįdŅķčSvđI´ŋ0m<Ÿî¤}^@ĨZƒƒ{¨VërđœĶĩ‡|2-Ę}RūÕČ5 Oūܡ\ŖÉy›œowpNĩy!weņ¸izŪŖž“EĪŖųy?uđI´-įk“šŧ€.Ėßéāڕ˙„ƒzfÔ:ødj?ãōÄâE‹–‹õĻ/Ž…;ãĸ&„ŖzÜ ‡*ÄĒ`PDÍŽ@<&ĸFˈöūŠ‹õD\ī67]‰ ]…ã;"†hėÖģĖP—X(ápl5ĸ1(Ë*V,ŌĮą7†|›õŽ@"¤ĮbM80ÅÜÉQkF _<W.6v_ĄGEsĀų´v3&ôØ1BŠģ3Šãņ”‡üĻOÄ|Q3_Øb]‰ Ņ—2zĖđ ¸ŗ%dúÂ~Ŗ\ø Š›Ą˜h‰& Ĩ+œˆ͐! øE@‡a„„߈™]!K+‰˜!° ‹-ĘtE TUVöööVt;1švˆžpwå‡,7EÃū„/kFŠMŸĢ”~ÔÚŖÍp¯noØØŌXßXŗĒĨqãąą^ŦkŦŠÛĐ\'V]ŧšŽn}Ũ†‘Ÿß@ŒR{§î3dŧ2‘h8bDã;D¸SLX¨‰"˰:vˆᄔõ…{Œ(N ívbãF´;&Õč"?C2 ]QÃč6Ņą€ŪƒvȌB2>ĘŲ_ŊzÔ† eQáWîŅp÷xĪd1Ã]†bí…ĈŧߌŪfG"p72¤OŌAå•Ī0bÅZ‘NPF‘l"ŅŖzGP4fÄ'Ü ą˜JŒŠņĒԘĄx*bÃgvĸįÆeEtEõP\†#euŋߔ“Ĩ§ĮĒ\’ívRŒq.hv›2HQ,!‘ˆČ‡N>Ė]6Oô†ŖWÄâjZU_*máېˆ$:‚&† †!i×Ļ[ß!ęŲ!ŗ;’ÆŅ–ež;G‚ÕC;ĕ #&­ČŽ@‚Ŗ!'¤hzÜą@8ôcĢč1Ū‘zŒĘ‡äCŲ DāˇË*ų2AÃ-5žøø†ęŽ÷Ģ—Ž_l`ha3n•›Ē|9yrPcrŪōķëĄ*-l7ĩDLī22•‰ Lč1c&ē‘ö8ĖÅm…Ozw°R+ũ’’ŸŋÎė3B}†Œ‰P6Œ•ؐˆ÷á)†Œ ¸ĶĨ…Dŗ ‰Np`ĪE÷ÖčˆafŒoĩIú &bÂ?'+™*ĸ7:[øŖa9˙ū9‰¸‰žąʰ×F•>EiF åĸĮH˜ĀúäB,Œƒk¨bė“9ĢGušwČúĨĢį3ī lQŸŽXÃØOnv›! ūôŧ›ēØĄ–°O!‘öR".ˇ‹gˆ !–~™a ¯0‚2ŠĀ>—p›Bö›}H‹é¤%OčASČMAîlfC™^čRZ pNjsŦÉîęq:°#ĶŖļÍød'Ŋõlj”CÅp|ôgÍpPÅ׊2Øũ‘r–žĖ0žŒ,­ŊŽ‰ėû•¯q5ˇjšė(:œj…œ(;ŗ28’+ŸĘĐH&?ĖĮŠq4Ū#33…=¸ĘD‡7ÁĖ„ÆTlĮĸÜ[‚ĘĪXVĮŒÔĐŽīHטę9ėxSz} v:û܇÷ŠP]áéę¤íę°áWąO}ÜiUžáÎŪFjđÁ™ ĒÄĖTŌŽdDKH=E2+2*;sqFÍSũ";é Õ#gëČ~9â›Ü]BjÎĒFĻs2ŲÛ6ŗįFVFö•]1{^#N§Ĩûl7~PĖé~jTš_YYŠũJunÄ2ą¤÷ 샪ęėÉŽR”Æž?¤uĮԞ*Īx?Ųor'6āŅDķqōūHëŗ§Ũpjā5­i}ã+mgkä„ô)žC¤+¨ÉũÄĶũA;ĄĖúÅęŲPtæÆļНÕ7UāLŸyé5–9ߤžzĮĢą–ŗwęô) {JWûÛø™‰¨)×éÕ3ĻŗW$œĐ@_ ‡nGåáĮ9Ķģa¯2ÃY9Æ˙Š ŒqÖúÔZŸz'ņ;ęj8SiĮ¸Au~Ÿŗsz$āxÜŠ$ũŽLŗę>ĄŪæ:1g/“VEsÎãĪkS&ßĩĮÔdĪ9IŒŸEŨ™‘ sļøÕ˜>˙ĨĻ„’ļ÷›ėĘ%wōŠĢž•S#9‚ŽDšę4ĶĄõe$bjæãÍÎZԙÎ?gfuåyúŊ#=cgOžVPŅŦú””ß™đ°ķ~ō_ŠßTžÆ˛ÖGÎwSÉíȒō;ŨåS;áˆTBíSåŖ&ĪPšJW!ĒΛXæėNj÷øŦ3›Æ˜=ãO—KÃŲuŒL•ũjJín1ĮtK\u‹ŽtŠĖ›BúÍTëfĻ?ĮįBwōaĒh팏ÎI8k‡ŌUÎqfŨļЇoøOž›?ūˇŽmiŦ~]ŊIm˙žÁgendstream endobj 1 0 obj << /Annots [ << /A << /S /URI /Type /Action /URI (http://sump2.links.org/files/RevocationTransparency.pdf) >> /Border [ 0 0 0 ] /Rect [ 72 277.5 198 290.25 ] /Subtype /Link /Type /Annot >> ] /Contents 4 0 R /MediaBox [ 0 0 612 792 ] /Parent 29 0 R /Resources << /ExtGState << /G0 35 0 R >> /Font << /F0 36 0 R /F1 38 0 R /F2 3 0 R >> /ProcSets [ /PDF /Text /ImageB /ImageC /ImageI ] /XObject << /X0 2 0 R >> >> /Type /Page >> endobj 2 0 obj << /BitsPerComponent 8 /ColorSpace /DeviceRGB /ColorTransform 0 /Filter /DCTDecode /Height 651 /SMask 5 0 R /Subtype /Image /Type /XObject /Width 1600 /Length 43257 >> stream ˙Ø˙āJFIF˙ÛC   %# , #&')*)-0-(0%()(˙ÛC   (((((((((((((((((((((((((((((((((((((((((((((((((((˙Ā‹@"˙Ä ˙Äĩ}!1AQa"q2‘Ą#BąÁRŅđ$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™šĸŖ¤Ĩϧ¨ŠĒ˛ŗ´ĩšēÂÃÄÅÆĮČÉĘŌĶÔÕÖרŲÚáâãäåæįčéęņōķôõö÷øųú˙Ä ˙Äĩw!1AQaq"2B‘ĄąÁ #3RđbrŅ $4á%ņ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™šĸŖ¤Ĩϧ¨ŠĒ˛ŗ´ĩšēÂÃÄÅÆĮČÉĘŌĶÔÕÖרŲÚâãäåæįčéęōķôõö÷øųú˙Ú ?ųRŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ¯éēFĄŠČЧÚMpĖp.sôŽÛāįÃŲŧoŦæ}Ņéļä4ŌcôŋįŌžŊđ÷‡´ŋYĨļ“g  )`Ŗ{cÔõ5éĐĀÑTŽŪģ%Ŋ쎭ŧ´wōVŧ9ôGŋđÛÅė  ķũš_øVž/˙  įũķ_säҚßęØoåøüy¤|1˙ ×Å˙ôŧ˙žh˙…kâ˙úŪß5÷>hÍVÃ+˙Āŋā4†Ã_“Ą^ß5‡ĒøVŌXŽŖa=š$:ãZũÍSÕtËZÕ­õ+Xnaoá‘AĮ¸=ŌĒá^–’ų§øY_īAÍ#ķԊöŽ_ ˙á¸ϐôš‰ČĮúŗéū{}9ņęķq8iaåf÷F‘•Š(Žq…Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@i|ŅâŌžX˛(ķŽŋ{#c¯aũ:ė4ízÃP×5m"ÖGkí/ĘûR /˜Ĩ“ĄāvŦ_„ŸōNô_ú䙎/NņNáo‹?æ×/Ũfm9b@Œī!íUœdgŽ+čq˛äŦ×KÛäļüŒcĒ=ŽŠÃđŸŠôoYIu Ū­ÔQžÉÖFFô*ĀY~!ø“áę˛iē–Ą Ŋ‰CI6ŌKŗ=RīŠĮž)^úĮaEeh> ŌĩíuM&ú‹eqÔ0<ІšY~/ø*9ž1ĒË F*dŠŌgŒāã!‚āpqIÎ+VÂĮYĒk–:^ĨĨØŪHësŠĘĐÛBC2Į'ˇĩ§^eņCR˛´ņOÃŊFîæ(,VōiZyjĒs’OÖĩô/Š>×5K}:ÃTcuqūĨfˇ’!'°fP ŠU3M…Oˆz\Zŋ‚ĩ{Y•OîEČčĘ2?–?ø:â?*âX˙¸Å#_ ^$˙‘wT˙¯i?ô_ęņ˙s˙][ųš1ēáĸûIū)áš^Š(¯Đ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠûŖá'ü“ŊūšækŸđE¤ņŋâ5ãF Ė)aoŨUáËõØŋ•jüžŠ˙ᾒđ|ĩ1°ô9Īõ­ũ'Ãvšg‰uŨn gk­cČķŅČ(žR]  ŒƒÎI¯ĄÆ.jíųˇų˜ĮcđB*|løB‚š{=L'ķ&ļ—Æ7Z•åũˇ„´Yu?ąÎÖĶŨÉ:[Û¤ëČIËąŒ•SÚĩtŋ ZiŪ*Öõø%¸kŊY`YŅČØžRl]  ŒŽš&šËŸ…úyÖ¯ĩ7[ņ/d3\[iמLNįĢcÉõÍrrÉ+.몜đ—&ĢŽ|VŅoá‚Ä^<1Ë ”†Hãia;)*š'9<j{yü}đīÃëú~‹ŦøsI€ö˛4+c” ’IįžõÖxáE¯Åh÷ĪļĄn„× í€Ŧŋ+ũėää’sY7 lîPÛÜøŸÅ“i¤áŦ$ԉ…ĶūyŸ—q\qŒÔû9%Ļás/Įī§ø¯Yø]qĩĨĶīoZåÆ Rēä}@Č­/ŽQG&•ኝĻ_ŗäe”9Á÷ĀŽž÷ÂzuÍ߇fO6Ũ4'-i $å6aāB*oør×Äļ–V÷˛ĪZ^Ez†/$xįžūõN Šy…Ë>&˙‘T˙¯iô_ęņ˙s˙][ųšûÛÆ—qŲxOX¸œáŲ˙FüȝîÜIw3¯Fv#ņ5XŨ0Ņ˙üCr(ĸŧs@ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(Ų˙gīˆąxnųôZB4û–\ž#o\ž>•õeŧŅ\Ā“[ȒÂãrē6CPkķ´pr:×eá?‰^&đˆôũBO$ųo†‘~•ëŅÆSŠ ÎÍi}Ķ]/×MޝéÔÍÅ­Q÷%ōjūĐ^)ĀĘÛ˙\—ü(˙†‚ņG÷mŋīŌ˙…oÍCū~Į˙&˙äIŗė}eE|›˙ âîÛßĨ˙ ?á ŧQũÛoûôŋáG5ųûü›˙‘ >ĮÖTŒBŠ,@’OA_'ÃAxŖûļß÷隟|YņWˆ`h.oÚ8`Į ? üé:˜xęę§č˙—â;>ĮĨūĐß­Ž­‡´9ĨNn&˛ ôŪūŋJų֜îŌ9wbĖNI'“M¯7‰öō\ĒŅ[/Õųŋø ŒlQErQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEWmđīáÖąã[ŦYÆb´_ŋ;đ }kŪôoŲûÖ°¯öÕÕˏįfÕ¨?ŌŊ Y|å:’QOkŪ˙rũmäCšč|E}‰˙ /ÁŋķĘīūû_ū&øQ~ ˙žW÷Ú˙ņ5§öt?įęû˜šüŽč¯ą?áEø7ūy]˙ßk˙ÄŅ˙ /ÁŋķĘīūû_ū&ėčĪÕ÷0įō>;ĸžžģø áĸ+ ŧ…û0d?ĻŅüëÉ>#üÔŧ9o%ö“!ŋ˛N[hÃ/Ôv)e’k÷RR}ĩOå}ũ/!ķ÷*ĸžÖ˙…ā¯ųō“ķ_ū&øQū ˙Ÿ)?5˙âhöĪųGũųúžæ|SE}­˙ ?Á_ķå'æŋüMđŖü˙>R~k˙ÄŅíŸō‡öt?įęû™ņMöˇü(˙Ī”Ÿš˙ņ4đWüųIų¯˙GļĘŲĐ˙ŸĢîgÅ4WÚßđŖü˙>R~k˙ÄŅ˙ ?Á_ķå'æŋüMŲ˙(gCū~¯šŸŅ_kđWüųIų¯˙Gü(˙Ī”Ÿš˙ņ4{güĄũųúžæ|SE}Ē~x,‚>Å(÷øšâŧeû;YIm$Ūē‘'");˙OåGļî„ōÛü5øĩoŊŖåú+K_Ņo´J[N†xÉ#¯=EfÖÉĻŽ>tåNNVh(ĸŠdQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@jxgJ“Z×l´øA-<1Yučßŗük'Å ,8HΝՁ§•ã+­ūílL‘õ߅´+OhvÚmŒj‰ÅF7ļ9cZÕÃümÔīt…Úåū—u-Ĩä".h›k.gN¸$~5ŲڒְŗIE$žüW§*Žs|Ûîgk"Z(ĸ€ (ĸ€ GUteu Ŧ0U†A†¸†ú•íūąãhīnĨž;MjH Y"(‚}5ÜTÆ\ĘčgĮ_´„"đĮ‹ŧÛ$ŲevĸTP8\“ĮčJōÚúköą‰‘ŖKœ3Ž}˛ĩķ-ræq^ÕTë%wëvŸßküĘ†Ö (ĸŧâŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( žÂũœ<‹á8ĩkˆ”ßŨä†+‚Ŗ=?§ā}käp ņƒĐ°ūuú āxÖ?čʃėąˇâFOękžļ­#ÖËŌ*•ú/“ģ’ų\Ûĸ¸ŋëz†ŠâÚ_ÜyļúuúAj›|´1‚F@ķÜæģJ–Ŧks+…QAAEsŸ|H|%āÍKZH>Ņ-ēĒÅf‘Ø"g‘ÆæöÍSđ–âûKí>%ņ\:ĨŗÄĀŲE§Į G! ‚Ž>b#žšÉĸÄ9kc¯ĸŠâžkzޝĢxĘFį΋OÖ$ĩļZ¯—PBü g¯S“īE†åf—sĩĸŠ(((ĸŠ(Ž+^Öõ_ŠžŅíîviˇļˇrÜCåŠŪČ ŠÜFáŒö"ģZ,J•îģ-ûJø2 [Âī­[ÄĸöĪ,ĖĢËō1ųzWȕú ņüŦ†g'ņüûĢŖŖhĮ1JTéÔ{ęžJÖüíčQEšåQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@zGė÷˙%GLüĨyŊzGėø@øŖĨäã9ū•Ũ—ŧGįų2gąô_í˙${Ä?Kũ(Šąĩ#Ɩž›Äoã ģ}NÚĮíŌX-ŧbŲ6&öˆ dđÎy<×IņŋNŊÕžë–:eŦ×w’ˆ|¸aRÎØž68Ž'đ­ZĪuđķ]´ˇ†InĨŌgŠ8‘rĖæ@îIãŧãy7åūdĄt;ÛĪxNžŠqa}¨ØĮ7›ōYÔT2qšķīɯü<Ķ—]OÜjâÖhūÕ§_ŦC͉Ø)Ø$øÖŊö›â4øaa ­ÅŽŋ›n†0|šWj¯˜€õ€Āt9ŽÆ<šĪol<5đÃQ°ž“ÉķnŽâO9B°?#1.äãŽÄæĻ¤ŧėīū/x†ûIŋđՔZŗhZVĄ4‹yĒ,aŒ;TA@Ür3íéšÖđ%ŒĻSoã{¯Ø21ŋ”čŒČ2ķĢž5Õn´øāˆxVëÄlČßhâ9 0+´˜üĀōr:`zņÆü>Ō'“â=ŪšĻx^ëÂú3Yg‚áDFębûƒĮž}ꞓ˙‡†wƒôwYņĸą×gŅ´Čõ™ŊŦhŌË) –Î.8Į$õâē˙…zž¯pŪ#Ōuëҍ\húļŽė V‘ †ąÆG­?áļycŦxŪKÛY K­jI i¨–2Ŗ žŖŪđ˙Oŧŗņ7ŽfģĩšŽĩ5–‘ ‰SËržã4ĄYúƒ<ķö°˙˙]˙e¯˜ĢéĪÚŀŅteČÜ^Bũķ_1Ôæ[Ķ˙ūŨ!èQEæ–QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEKm˙1ž?~ƒx3ūEūŧâ˙ĐE~|ÛĮÄ_īį_ ž `ŪҊœ˛F?%šęükįúžũŪ~Ģō‘Čü8• ņÄŠe`‘ĮŠŖŗĀB 5ĘÛüDÔŧNgÔ,•A™­ė˙ŗCšųU›9##“Qü3‚Xŧ[ņæŠHŌ]YY”€ãË#ÔWŸčzĒ^Īpŋ´¯_øž;Šiđ¤ÆË¸AÆvc|͓žsŠĢjgÍî¤ÎŪÛâė˙o|Z,c]RŌ H91‰ŅļÁÎĖüØÎqßŊWđēøãXˇ˛Ô!ņۃuˆ“Imo§ŦˆÆWp`Øę3ÁúTe›Cø<ŌŨčˇŌ˛\ÜĶ–Ō˛4§§ŽįÜ âõČ<=}Šé2ü2đūĩĨøo"+$VrZÂ# —g ŗ‡°éK ÉŲ6úÄ÷Õ×âī‚ÃËoũĄ-­äk%–H”… !““ŽįwLÖŧ[á˙‰/‡|OŠYkZÔŧÃj-Ū"Rä+ßœŸnoxš Ÿã_‚įHdh#˛ž RUIQ€OAš‹ÆvĶÉņŖáĖņÃ+Áz™"Ą*™ƒ' ÉŖČm4ܗuúGŋäKÖëŲĢķęŋA|~@đVŗ“ôvųõJ—Äū_ŠX˙āSõ—åĸŠ+ ōBŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ˇ<Ŧļâ?RC&PĮœqXtV´jē5#R;§q5ucô3IÔ-õ]6ŪúÍÃÁ:RLö>ãĨ[¯~|[Ŋđv,¯ƒ]ilså“ĘŸP{WĐē/şj°+ޤ-ÜõŽuÁˆČ¯v0Uũė>̎Uä×ëŗ2znw´W+˙ ÂŋôĩüĪøQ˙ ÂŋôĩüĪøUũRŋō?š‹™w:Ē+•˙……á_ú Zūgü(˙……á_ú Zūgü(úĨäseÜꨎ>ëâW„­ĸ2IŦĀ@ė “üĢČž%|xŽ{9l<(’'˜6ĩËãv;Œãųũ*eBTõĢî¯=>åģųīąĖūŌŪ)‡ZņTZ}œ‚K{ØY[ ļNHū_đ^7RO,“ĖōĖÅäs–cŪŖ¯]WŠÍŅz/ęīĖÖ*Č(ĸŠæ((ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠQÁČë_k|ņL> đ5­ŋ˜ Ũ (čO8ĪøŸČŠøĸēoøĮRđn°—ēl„.yfãYUƒvktwāqĻŨ:šF]{5ŗüĶõ>˙ĸŧŖÁ˙<3­ÛÆ/ĻûŅ2ÉĶ?ž˜÷ŽÉ|qáĻPF¯mƒĪZįįŠŨØõVŦĩ„y—uĒü’—&šŋøMü7˙A{oΏøMü7˙A{oΏiáõJ˙Čūæt”•ÎÂoáŋú Û~tÂoáŋú Û~t{H÷ĒWūG÷3ŖĸšÃ㏠(%ĩ‹P$– xãÃD5{RûT{H÷ĒWūG÷3ŖĨÍsđ›øoū‚öߝđ›øoū‚öߝŌ=Ãę•˙‘ũĖčérk›˙„ßÃôļüč˙„ßÃôļüčö‘îT¯üîgGEsm㟠*’u{lzâŧeņĮÃZ%ŧ‹§JoîņōˆūîĪĐ{ŅĪļ đÕcŦ×*îô_‰/í â¨t\Y¤ ^^ ˆ ōcųķøøžē?øģQņ†˛÷úœ„ōv'eÎWE(8ę÷g•ŽÄFŖTéü1ëŨŊß䗒 (ĸĩ8Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š*{KYîåZÄōČ…FjŖ M¨Å]° ĸģk…Ū/Ŋ„Ks°ôĘ7ôgūŒ˙č q˙~ßü+¯û;üŒžx÷8 +ŋ˙…Eã?ú\ߡ˙ ?áQxĪū€×÷í˙Ÿön'ų? į‰ĀQ]˙ü*/˙ĐãūũŋøUSᯋ4ČLˇZ=Č@2HCĮæ'—bŋ‘ūaĪįEI<2A!ŽddqÔ0Á¨ëŽQqvkR‚Š(¤EPEPEPEPEPEPEPEPEPEPEPEPEPE dāu ĸē-Á~ ÖđtŨ.æUūđCŠŨ˙…Eã?ú\ߡ˙ ėŽ_‰’ŋ#ųéų“ĪįEw˙đ¨ŧg˙@kûö˙áGü*/˙ĐãūũŋøUfâ“ōxœß˙ÂĸņŸũŽ?īÛ˙…6O„ž3D,tkŒÕˇøQũ›‰ūOČ9âpTV–¯Ąęz<Ļ=JĘkvzâŗk–Ĩ)Ō—-DĶķĐi§°QE˜ÂŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š(  J¸Öõ{m>Í Í;„ õ8¯ŗ>|7Ō|ĻÂ~Īú™PdÔ­Ü.,õ?Ĩx—ėąĨGwâËËé1´‹+žÄņŸÖž”×uí?BS™ĸûuÜv0a n™ķĩxéĐōx¯s–‚qŅËVü¯dŋWŪūFR՚”QE1Q@‘ƒŌĸģ¸ŽŌŌ{™ÉXa¤rpĒ2xú ¯ĸęvē֓iŠiŌ,îŖÄåJ–SĐāō)_[į?ūéŪ'Ķ'ģĶ-ãļÕcVqå SÔņëüûú×Č7–ŌYŨKo:•–6*ŒWč|kûBéQi¯|… “âPé ũIĨ‹ˇĸį/Š×ēŊŦũVōŋâėėy•Q^!¨QEQEQEQEQEQEQEQEQEQEQEQEQEWž|øW­ ëÚô{­"HÆķū˙­ë^ĨAöNÖŌIUOĶ<×ߞ°‹LĐ´û+u 0Ē€=q’2kÖËāŠÁ×ë{/.ī×kzß{ÍßBå­´,Į(0ąÆĄT Šk/Ã:öŸâm WG™Ļąœ°Ų ĩŠž=A­Jߛ›RBŠ( Š+6ß[ą¸×ī4XĨc¨ZB“ËÂŖũĶģĄĨ{Īx{Kņ‹Úę֑NŒ Ę7&{ƒÚž;øŊā9ŧ¯´K—°˜î‚LpGøõãÚžŲ¯)ũ¤t˜īūÉtËûË9Cö€TđĮÄ xžũŦtmQfŧ ŋɒ)!fĒF ŠĘíã\é˛M2‘Ÿ1ŲIb}Ā \ÍÉ4účVw0^ÚCui*Mm2 #’3•u# ƒčE|ûOÉB˙ļ+˙ ­}đX“đ¯ÃY9˙E˙ؚž}ũ§ŋäĄÛ˙ĐVˇo› U˙uéQÚGŅEᚅQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@^˙õ‡ũv_į_ 6ņíoūâ˙!_ŸŪ˙õ‡ũv_į_ 6ņíoūâ˙!^ÎũÕ‰ūQ3—ÄxÁ߈^đŋÂíĪZÕR ¯ô‡1$O+*ųĪËS´}kŲt-cO×ôČu ę;Ģ9‡É"" ûķ¯Ų¯OļļøUeļĸXäō„´jŲÆ …Ú9īšę5=oLŌôvÕu č Ķ•ũĄŸå ôÁīžØë\|ž&ēņN:øÂëĄO‘›ŊBå- š0 Š9Čå@ã­r> đÍ׌~xR k´ŠúÆé¯mÍÂy‘ģE<ÁcqŨ0qøĨ?i+Ųj;Ŋâ„uRßNĶõG{ģ†Ų=¤Ņ†oL˛ú×5uâM#Ã|QyŽßGiiļh›fvÉáTXûWãņŠŧ=ŠéÖū;Ņ,ĘútĩRŌæfE™žę˛7Ė2G^÷Ļéz}ŊĮí¯^Ík‹M*ÜDO;wđĮ끌ûŸZ—'+wŋ`:Ÿ øßÃŪ,’â=Q[™mđdŖxAčv¸qX_ŋä–k?öĪ˙Cũŧ1|}ŅåŠ$IfŅ'ķF â@}qSüz˙’YŦ˙Û?ũ W^MÔWîLļ>(ĸŠ+Ā6 (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€=ĶöSÔŖˇņ6Ŗd万åĪr9Įé_CøĢÃVž$Hŧšxŋŗuĩŧĸ>i#βËÉÎ9¯‡<'ŽÜøs^´ÔėØŦ¸n^kíøĪLņŽ“͌Č.6ūö ß2žøõäךEĒøxĩŧt~Ķ§ĒķFOFZņo†­|O›äĶÄļ7ņj˜Hųž<áNAų~nqĪŊUņˇƒ,|Xl&žęúÂūÁËÛ^ØËåÍá†ķÁĀü̧ĸ“Š{įãáu„ēŽ“¨ëž Ô¯tÛ´ģ†KËà ĘBí#hœ}ëC_đ2jš´ú§ˆ|C¤\OˇÎ[ Ō‘ž(ų p;{×aEOŗŽÖ œŸ‡| §hšvĢw7×wšĸ2^jrų—åJŒąā8­M@ˇŌ<+m ÛË3ÚÁmöe‘ņŧŽÉĀÆyô­Š*”Ø.exWDƒÃ~°Ņėä–[{8ü´yq¸Œ“Îëä˙Ú7QŠ˙â=ÚÂA#Ôę }ņGâŸāŨ"`&I5GR"…H%OĢAũ+âíRúmKPžōå‹K3—bNibZĄ‡q{ÎÉ/$īŊ$ģęՕhĸŠņMBŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š(  š<ÂßUŗ˜ôITŸĻy¯ŋôK¸īt‹¨čå…OÔ üô¯¤˙gī‰ÖĢa‡ĩÉÄM|‰œārzéß=Žk×Ëåí)ē âŊןžß‰œôw=ŸÁ>ĩđ‡†­4K §šÚØšWœ‚įs–9ĀĢԞđş‡ĶYKy&™5[ųĩ „ÄpōãrŒōņõ÷­Õ!”2TŒ‚AĨ­9Rˇ8ŗøMaf“ZÛxƒÄ‘hō3ŸėČīŠ@ĒĝŖ sëĪ|Ö­§Ãí:×ÁˇžÕ!ļą‘Ļ‚æƒęåŗšéæ7ôŽĘŠ•N+ \āŦžÚŽŠg{Ŧëúūē,äAo¨Ũo‰$h$WGiáëk_ę IĻ7WļņÛlQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEHÕīô{•¸Ķn¤ˇ•NACŽj…tęN”šāėü„Õ÷=FË㗌í`‹Čä `4‘Ģ˙0j×ü/Ī˙Īkoûņ˙^IEu˙h×îŋđ˙šëđŋø§â­‚ĪQvz#ōĐtވ||ņŸ9žØúŖ§˙^IEvhâ:ĘūŠ7÷´Ų<ˆõŋø_ž1˙žÖß÷â?ū&ø_ž1˙žÖß÷â?ū&ŧ’ŠÚ5üŋđ˙šëđŋj’¸ŌK`ĸŠ+!…Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ixEž×õ8Ŧ4ØZYä8ĀĮšŦÚúĶöbđ|w†ŋˇn"Vģģ9™yUĮQø~~ĩY¸Ģ-ŲÛ‚ÃÆ´œ§đĮWįŲ|˙+”üû=iļÖŅMâIŪâā€LKĐ{z?­w)đkĀę &#š=kĐčŽ~[īŠęûW ”W’ū›ųļy÷ü)ĪĐ/ΏøSž˙ $_z r öķîy÷ü)ĪĐ/ΏøSž˙ $_z r öķîy÷ü)ĪĐ/ΏøSž˙ $_z r öķîyīü)ĪĐ/Α~ xTĸGv&ŊŠ9PŊ´ûž}˙ sĀßô‹ķŖūįŋč į^ƒE¨~Ū}Ī=?üŽ4H‡ã\Ÿ‹gÍ ūŨßC‘ėîqōŠûš˙>߈¯nĸŽ^Áí¤ô’My¤˙¯‘ųķã j^՞ĮU…‘ų_ +ž×ø÷āøįŸœđ7ũ"üč˙…9āoúEų× ŅG*ˇŸsĪŋáNxū€‘~tœđ7ũ"üëĐhŖ•ˇŸsĪŋáNxū€‘~tœđ7ũ"üëĐhŖ•ˇŸsÎåø3ā‰#*ēcˇņ œAÛdÁG_ä:ųēē)Ī™kšäã0ę„í…̝Oøč(ĸģī‚ž_xæŌŪa›xH–ON9ū†ĒråW2ÃŅuę*kKū Ģų#ĻøYđOPņLQęģ5–žz+)Ü˙įĶõ+Üô߂> ´…Rm?í,-&2kŌm ŠÖŪ8-ĐG jz*Jåw–˛gˇB’åŖ.ũ_Ģũ‡Ÿœđ7ũ"üč˙…9āoúEų× ŅG*ˇŸsĪŋáNxū€‘~tœđ7ũ"üëĐhŖ•ˇŸsĪŋáNxū€‘~tœđ7ũ"üëĐhŖ•ˇŸsĪŋáNxū€‘~tßøS^Üû<ŧqųWĄŅG*ļŸsĪŋáNxū€‘~tœđ7ũ"üëĐhŖ•ÛĪšįßđ§< ˙@Hŋ:ŖĒ|đeä!ą6Ŧz4xâŊ>Š9P{y÷ü>8øŠđgQđ”o}Ļŗ^iŨNÕå?ΧękČĢôzöÖÛImŽŖA*•u=Å|+ņwÃ#ž7žąŒbc$|q‚OOķŪĩ§6Ÿ+8ą˜xJŸļĻŦÖëĻŊWnÍmĩŽ.Š(­Ī(*kKi¯.cˇĩĨšCĩG$Ô5ôėĩāøu ë­vú énvÄŦ2 õüķ˙|ÔTŸ"ēÜęÂaÕz–“´V¯ĶūËÔŊđīö}6q^øĻvBā8G zú˙*õ> x"(ÂüLy¯Eĸš­}dîzę§"åĻ”W’üŪėķīøSž˙ $_đ§< ˙@Hŋ:ô(åCöķîy÷ü)ĪĐ/Κŋŧ ŖD×–&ŊŠ9PŊ´ûž}˙ sĀßô‹ķŖūįŋč į^ƒE¨~Ū}Ī>˙…9āoúEųŅ˙ sĀßô‹ķ¯AĸŽTŪ}Ī>˙…9āoúEųŅ˙ sĀßô‹ķ¯AĸŽTŪ}Ī>˙…9āoúEųÖˆ~xWPˇa§ŖØÍƒ´¯ŨĪáë^ŊE¨=´ēŲú¤˙3á‰5_ߘīËhĮ÷sÁô˙õ˙*âĢô ĮūļņO†nė.bå FqČltZøSŗ}?P¸´——…ĘgĪĄüG5ĩ)ļųdyØÜ<#Z’˛z5Ų˙“éčĘ´QElyÁEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP_||)EO‡ē Aöpq_×ß_ ?äŸhõîĩ…_‰|˙CÕ§Ŧöã3Ášĩũįň:}ÕԒŲX˛ÂĮå‹|,ĪŠŽöŧĪĀd/Æ/Š,xé¤˙߆Ž~'Ûxšōöōëâü"ÖK+Gegmfe‘Ŗy’ąSËu 1K–ãUŊßæ} EyןŨxˇ@Ö-†§ow}§LmãÔĄ„„Y3…9ęã‘Y^/Ķ<[áM÷Zļņė÷ˇv0›¯ąß[DąN¨2˅ēŒ¸įŊ+kbŊϜÉhwž<×[à Õõ¨âIgnŌ$dā3t\ûdŒûVƒŧ9â úvŗ­øžūęyPMqcF-IaĒ6‚ČĮ~+øĀ×~%ø.ēõŽŖq§ÛIĻĨÜÖHŠËp%°W'‘ˇžžĩŲü?Ņu-'NY5/Ūjé<Ŗ¸‰A…ä.ŅÎr:úSŲ~iÛĄÕ× āmVú÷Įž>ŗģē–k[Ģdļ‰ˆ–ękē¯ĐôÍ{Vø­ņÛIÖ_FąĪ5Ĥ’´žVFîÆâxôæ’ęUFĶŋ­mĸŧīán¯­?ˆ|_á¯_ JMx<›ŌXæF`2Ėևˆ|5¯ßjW7‘xŪ÷J˛$­āļ‹lkÕ›$œįŸĨÕŌ;J+Ë|/âũ}|!ãī~ĪĒk~’Hĸžųn𛐕ũqXūž÷ÅvV—˙$]Nâ?6K( €y.~ōaģ N9Ŗ”^Õiduž5Õ¯ėž'|=ĶínĨŠĘũīÅÔ*~YvB…w} &ģęņߌÃU˙…‰đĘ= XĶSyobI¤zÆ"W“nFvŠfĮĩI¯ˇŠü ŽøbęoĪŽiēž§—qmynˆT˜:”ļŸŌŽ‘*|˛•×_Ņâ ę ܃˙*üōŋoŽ€‘€ë_Ąú÷ü5úā˙Č×įŽĄ˙÷?õÕŋ™ĸ—ÆĮ˙wĢü‘^Š(ރČ6üĄŧQ§†Éũ ~…*…!@Ā_žŪ ˙‘§O˙|˙č&ŋB‡ßükšÄ~‹õ=Ŧ7û¤Å/Ęđ[UžÖ| ŪŠu-ÕÉģšŒÉ!ÉÚ˛°đģĒņī†Zü>ø­\FeŽĘâōO,oo9‚Ž{d+•ÍÆũŦ˙āĩÖZ?=t•ĶÉĩGÆD%ļ’GbŲëÍ>[ŗ%UF*ũĸčŽ;MÔnü{ā+RĐ5WŅäŊU’Iĸ€HɌĢĸ‡ã‡Ī=;æš¯ļøƒÁŪ6đõį‰á!ŌõyÚÎTškyv’Ŧģ;qÎũJƎĸV}“âną¨iöš>ŖÎ-/ĩB;ģ(ėęĀŗ8SÁl)ĮŊhøK@žĐÍßÛüEŠkBm›>Ú÷XŨģ@ë‘˙|ŠķŒÚ.Ē|Qá;˜|K} WšÜ0ÁŠ2ļlP12O^G&ŊWÚuŪ—ĨĨŽĄĒ\jˇ ĖMÔčĒĖ āaF8Ļö&.ķw[Š'–ÛÃ:ŧöîŅ͜ΎŊU‚ük3áõΧđ÷Ãסķ<÷wqÉ,ŽrÎÄrM_ņ‡üŠ:įũxO˙ĸÚŧ›áŸ‡|e­|5ŅŽĄņlú)ž]ĨŊŦl!Øä’ŨN1ÁP–)5=CÜ(Ž/á7ˆ5ü?˛Ôu#Ô –t+29MØ÷Ækœņ~•âß čZ†ˇĻ¸ģ˛…īÕŦ) ʀŗ æč>”­­Šu=ŪdWĸŧģĮū6ÔáøUĸkúC.žúĢډîš3(ąŠU%ĨÛÎqĀüjį‚Ŧîouoô˙‰Sköq6fļĀČāƒÁ+ʞAü(°{E{"îĒ_MņƒÄzdˇR5…žm,PōŖą;ˆ÷8ŽâŧfūË\Ôž;köš&¤t¨KļkĢĉd•@'j c“ÔāđLÖ˙/ĩí?âšá]sVmb,âžļē–%ŽU ÛJļŪ?ËŪ›DBvŅ÷+ūҍ¯đÂô°Ģ‚=žV¯ŠĢíÚ/ūI}˙ûã˙AjøĸäNcđRô˜Wž~Čę§Äēģķūú¯¯~ũ‘ŋäcÖ?ëŠėÕUžģķ2ˏō—ū’ĪiøˇĢ_é+ā˙ėÛŠ-ž×â+;Yöy‘6ũČ}ŽwĩæŸ>į?ėją˙Ųé˙|w˙ŠŌt¸/aĶnuFpڄņ™Ö%3„ŗd€žj-{j.Mų‘Ex>‹ņĶJņ> ŋŋá'°ŋ¸ˇ0\Ųå€ļBHŒĒ8Ũ€AĪōôßhÖ§{ö‹/]hļQ MŊŧMĪvgåIÆÛŽ5T•ŅÕךę“ë-øƒŦčvģwĄéú$4­in&•wƒšŗ„ ŒuÍ?áÎĩŦj7Ū-đļąŠ%ŨæŽŅ¤:¤ Ą¤Žd,ŒGŨŪ¸įŒgŽÕÆx[Ãäßüke5HgļŽÅĻģH"ßsē,€ĀŒ ŖÆšV"sæĩ–įˇé’Øé–Öˇ7“_Mk™€ß)ūķcŒ×ņKVŋĶ5ϟu-ēŪkÖö×ú؛9Cėq]íy?í×É‚›IDkđ}ŸĖ]ËæmmšÅ(î]] ėzÅãŪ6OxÆĶÄÅĶjđGuwVWVą¤nŽÁ~MŖ#ú×Ļx›OÔu-?ėÚF­&“9šŽ•ör 7“Ž}¨°ÔīumMZ+Ę~ÛâxÛÃÖ7ž%˙„‡KÕįk9Ræ4I­åÚJ˛ėíĮ9˙õAãßŪ‰ Ą]xŦxOJ[ž)3u)bŪãhĀŽ?Z9Dę¤ĩG øōî{xŽōÎV†æßMšš)Ē:ÄÅH÷Pü9ŧ¸Ô|áÛÛéž{Ģ‹e–W<ģ“\Ū̧ŪŲ|,ņ„—~&—Ä0O¤ÜŧIk°yœ2pĀņôÅs<;ãMgᮋw‹§Ņ¤ûĨœą˜Ö5ŽI,b}úSļ„šž}ēÛ_'~ÖQĒøĘÁ•@f„äúđĩôÂ]øˇáî“ŦjKŪL$ōÆvMØíšükĀkOųôßúâßÉ*vœ}FlږĢ_Ęŋô¨žEWQá…}‰û.ĸ¯ÃĻ`0MÁōĪõ5ņŨ}û/É8˙ˇƒü…a[ė˙]O-Ú¯§ū܎—RÕ/ŖøÍŖiiu"éķiĪ%¸?+8Äzâģšķ­[ūKæ˙`;ũ+•ņ—Ĉn|mŠhoâÅđŽ›Ĩ‘\GoįOu6>`Ō8÷4­rũĸ…īÜöú+Ę>øãûWÄ×ū˙„†?AˇÛ-ĩ†L ņȸ ˛@äjÖÖ<-âv{›¤øƒwhîĖa‹ė°¤*yÚ§<‘Ķ<į­+w)TēēG3ˆĸyČE- ¯&đ,>#ņū‘Šæņ]ū—ow<k§ŲĮ—I# WČ%˜•99īø _ jz¯>ĨÃę-¤ęŠŌC=͚+h˜Ģ Æ­`ūÎ:6ĸžĐuF×īNt¸UŌĖIå!ķ¤ ŨAoЧk&K—<’č{-pj÷ããŧ8ē”igÃÆčÛgä2ũĄ—×ßW‹øĘßYŧũĄôÛ]ņl$›Ã›n.üĩv‚>BYŽ ŖŽ7g‰U]’õ=ĸŠķ/ Ũx‡AøžžÕõÉ5ģ Í5¯ĸšâ%I"e}Ĩ~Q‚ tū*Đĩ^ę'Ķ|SuĸÚÆ‡tvÖņšvĪŪ,Ųãž´XjwWH騯5đ&ĩŦY|CÔ|ŦęŅëp-‚ęV—ûUd ŧ#$›xÎOė=øäŧ+âKÅī<ēˇÄ/øFõstđ˙bEH`áW÷ƒ,O\ŒŽhå'ÚŽĮ}ņTžŌ<'osĻ]Ik;j6ąŒā”iaô"ģŖÔג|o†ūÃá-Ŧ_kmOQ†ú×eĈĒg3*HsĀâĄņĩ—Ž<1á{¯Œ¤šēŗ sqa%¤knÃ#tk˜p9ÎZvē›Œ›ąė#¨¯~)FąxûYDUœ€+ī-6āŨØZܕ f‰$ vČ­|ņ_ūJˇ˙_ üéCã^ô/ūë/ņGō‘ÉQEŌxÁEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP_||)e‡ē!Rû8é_×ןŗ7‹!Õ<$ē<˛ĩŲ|ĒŦy+Žß–?JÂļ3Õ˟4*AoŖû¯Îū‡MāŊ6ōŠŋ.bŗŧūĪķŧlŠ6Ø6Æ# ‚pqŌšß KĢ|1´ŸÃ÷žÕ5ŊŨôÛŨ.Ü\7–ėXĮ"™H$ōzäûW˛QSrũŸgßņ<į[Ô|UŽü=ņŪĐîôWj‹¸d[‰@ šÚ3ąļî Îyī\ŖomĢø7SˇŌūë§Z–ÉĶíZĩ¸w‰Ęōé$§{0'#hÉ8é_BQB•…*\ÛŗËüEĨjû:&•Ôššč–Đ›D‰š]걂ģ@ÎFÕčēB4zMŠHĨ]`Œ2°Á(āÕē(šjw ā|aykņâÍÍĨÄ6÷WV­o,‘˛¤ĀDA(HÃx8ŽúŠWÚ}=đ.Ÿ{mņWâ=å͝Ä6—o§›y¤‰•&Û †ØÄa°H+ŠƒíQø‹XˇņׁüEâ[īļ3ZÜG¸˛0˜ÕUˆqÆ^s^īE>b--sÉžYk$9ŧm:inÄöZR”…đ|˜ĪqÆMsž4ŽĪÄÖvš­jĪÄŗķ ÷ØRŲbœ°>c\)ÁÁį'¯ļIųE>mn'KN[ž[ŦčúĀņ¯ÂW犿öM> ´Ôo#™Cmîv šÆqšŅøÅ§Ūę āΰZ\]}ŸÄļwy13ųqŽũÎØ(ČÉ< ô)\¯gŖ]Ę!`ē Ėpü~yjû’G˜ßĖ×Ú˙Å}–ßū’ũ?qžđÎ¯đSQđæĢƟ=ä×ADņ221db¤Œ…>â¤ŌđõŌhz‡ŲūŌYŌ4ˌÜyD"ŧ|¸“ˇã8Į2ēhÔ<]āÛŋ|<žĐėmu/6ęöâŅb˜ärû9Îæã<•ī4SRĨw{œÅM>ōûTđ+ŲZ\\%ļŋĶ´1ŗˆŖ˛íō¨õŠkķw_…ŸĖ+ßdfđ’k ž|”ãūú¯¯Døâ„đĮŽíeļÛÜū<SøâǞ÷ ˛Ö–!'Õ5ķiĨøŸOübĶīuđgØ-..žĪâ[;‰ŧ˜™ü¸×~įlddž;ân…ĢKĒh~'đŨŦwڞŽŌą‘ ˜duŒā`×{ Š4I,.¯€ĘĘr=é՚gdŠęîyö—ã[Ũ[Q‚ŌĮĀZũŗK>ŖlļŅB™ų›q?9•zú×-âôž‡âF¨ū*đļŊâmxb]-,æ‚Ū #.ælrÜāŨ=NJą.›j͞Ođ{IŊ˛ņŋŽ/gđĶøvÂķėfŌÔD‰UGnĪ“<‚Āt,{Öˇ„ôûØ>1xöö{;ˆėŽa°\U§Šô)'båec€øåayŠü>žÛM´¸ģ¸7vĖ"ˇ¤rĒIĀāj?+­7J“J˛Ôu-/Õ,ôŲ.&„Œģ~bä×Œņ’=Ši“(^ūgƒ.š5x6īÃß/´;]KÍēŊ¸´XĻ9‡žÎsš¸ĪĨw>?Öc†ôØë^Ô|CĻaZ ‹kHīTļ>lÆyB20{äôÅzs S˛jįˆxO@Ôáđ?ÄŠ-tKũ+JÕ-&ūÉŅĻRfŒų+a:‚ėW íŽ{ú_Ã;iėūøjÚōmî!ĶāI"• :0@ ōô5ŌQCw)¨ž{đOŊŌžčözÅ•Üm>ø."hŨs3‘•`Č ū5â?ĩ“ŠņŽž ‚‘éÂ×՗3Åmo$÷ąÅ–f'€+៌Ū%(ņŨõÜG0F|¤įŽųüŠ/zĸûƚöXYßĒQ^ˇOōG EWQâ}‰û/0?H$\!ūņŨ}û+xļIî´ ÉByĮ|;ŽĶķ'ķ}“ķ=,ĩŪS‡V´ų4˙$Ī_Ôė/ãf‰~–— cZĻ:``vĪī´Q{ ĶŊŽĪ8ø+Ĩßé Íϧ§K§Ũ™î›ėŽ›Jr@Ķž1V~X^iŸ ´=JŌâÎî!>ø.#1șžB2¤2?C]õ7qƟ-ŧ‚ŧōm>ôūĐúˆŗš:xđŲ€ŨyMå >ŌĮfücv9ÆsŠô:)'bĨkĄax˙4›õ´¸kŅ&‰îDlcW2ä)l`sŽĩËøÔę0üFŋ˙„—ÃZ÷ˆü941fGχ’n$ķQH‹wn€ u8öZ)܇NũOø}¤]GņŽãTƒÁŌxgE}ÁųÆŽūz6_ËųC‘ž Îfę–:¯˜ž'øEŦ]jn6™ ąŽã8鋅į=>œW˛QO˜^ËK\ņ+ī øŠÛāž‰Ĩ_ÚŨ\ępęVōxķpđÂ&ČRFrp č1]÷Æ ;Cᧈ­l-æēē–ØŦp…ŨÎáĀQÉŽžŠWŗI5åbž†§Į"˛:ÛÆŦŦ0A 2¯„ž)ēŋõĻB3ą¯ˇøáá=^Ū3urÖS7#Œ€Ÿé]ü,ĪĐfûáŋž ĸąö2č˙ŅY…'ņĶ×ĘV_Š™÷Ÿü,ĪĐfûáŋøYž˙ Ė?÷Ã…|EÆ]˙ø#ūĐĄ˙>ßūŋųī?øYž˙ Ė?÷Ã…đŗķ˙…™áú Ã˙|7øQ˙ 3Âô‡ūøođ¯ƒ(ŖØËŋá˙?´(Ύ˙/ūDûĪūg„?č3ũđßáI˙ ;ÁûąũĩzũÆ˙ ø6Š=ŒģūđEõúķí˙āK˙‘>ķ˙…™áú Ã˙|7øQ˙ 3Âô‡ūøođ¯ƒ(ŖØËŋá˙Ú?įÛ˙Ā—˙"}į˙ 3Âô‡ūøođŖūg„?č3ũđßá_QGą—ÃūhP˙Ÿo˙_ü‰÷Ÿü,ĪĐfûáŋŠjŸ<§ĀdmPJqÂĸāŸĪđÍ{wü?≿zS9öĢķ=ĢâĮÆÛŋ@úv„¯k§ˇ ˙Äãü˙žõâ´QZ 8ŒLņ 9h–ÉlŋŽû…QVs…YĶīn4ëØnėä1ĪnVĒĩšēŗ*2pjQvhúwáßíh֑YøŠ&E ö…čßįßS^ĨÅÍuÖ#ö(Ųũ|!EbčĩđŗĶŽe}͎ߪ_rGŪđŗßūŋųī?øYž˙ Ė?÷Ã…đŗŋäq\ĩņ’ŋČâÄâ7ėéĢËđKĪôGĘúWėũã;ØIĄˇļÜ2ؓøņČšŊ˙ åâßųëkųšû.ŠæįŦūßāŽ?k]˙ËĪÁÁ>4˙†rņoüõĩüÍđÎ^-˙žļŋ™¯˛čĨĪ[ų˙ūAí+˙ĪĮ÷/ō>4?ŗ—‹ąÄļ™úšDũœü^P{Eb9'öeųë?āŋČ=ĨųøūåūGƟđÎ^-˙žļŋ™ŖūËÅŋķÖ×ķ5ö]šë?āŋČ=ĨųøūåūGƟđÎ^-˙žļŋ™Ē×˙ŗĪŒ­á/ÛLG%C‘¯ĩ(§Ī[ų˙ūAí+¯ųyø/ō?8+ēŌŽ2Ņž ûȨč>õŅCå.J›ūū ׆ÅJröuVŊŲ˙“ō˙ƒnRŠ(Žŗ¸(ĸŠ(ĸŠ+Ąđˇƒu˙ËŗDĶg¸Lāˍ¨?āGƒô×EđWáûø÷Äâ ˇ.›m‡¸a‘¸v\öÎ{âžâĐtK O†ĪKļŽbP "ĀŽ:؉)rSŨnûÁ81š):tˇ[ˇ˛ōķgČ6ßŗˇŒeˆ4ĻÖ6ūîIū‚Ĩ˙†rņoüõĩüÍ}—EsķÖūÁ‘Ëí+˙ĪĪÁ‘ņ§ü3—‹į­¯æh˙†rņoüõĩüÍ}—EõŋŸđ_äŌŋüür˙#ã7ũœü^ČöŒr8ÜGŋđÎ^-˙žļŸ™¯˛č§Ī[ų˙ūAí+˙ĪĮ÷/ō>4˙†rņoüõĩüÍđÎ^-˙žļŋ™¯˛čĨĪ[ų˙ūAí+˙ĪĮ÷/ō>4˙†rņoüõĩüÍeë|iĻÂŌĮk Ú¯hßū|~ĩöũũĨeöūôŋā̈ZŠũé~–üĪÍBÂīMģ{kûimŽáŖ•J‘ųÕjûģãÃ-3ÆÚÅaŽN%- ęŧƒ×ņŖŋךøkR˛ŸMÔ.lŽĐĨÅŧ‹čAÅuP¯í/+IWGvío ĢI~+ēū´+QEŌu…Q@*‚Ė‚IāŪ’žũ›~[Ëe‰üA’GæÚpvoŠëė1īX×ŦŠ.íėŽ|N!Pívö_×Nį‘x[áŒzßĪø/ōi_ū~?š‘ņ§ü3—‹į­¯æh˙†rņoüõĩüÍ}—E.zßĪø/ōi_ū~?š‘ņ§ü3—‹į­¯æh?ŗ—‹€$KjOĻM}—EõŋŸđ_äŌŋüür˙#āOü'ņw†ĸi¯tĮšŨy2[åņõĪéŠá­~™ÍsFŅˊčx*à ×˟´§ÂĢ}:ņ>Ž=Ųē…|QßÔdõëO8´ĒęŸ_ķ˙?Āږ2p’mSëˇßūķuQ]įĻQEQE=ĨÅõÂ[ŲÁ-ÄīÂĮcô›ko-ÕÔ6öč^iœFŠ:ŗ€?:ûsā§ÂŨ;ÁÚ$7WpĮ>¯:–fÆyĀĪA˙ëŽzõũŖy?ęėäÄâ}Ŗy?ęėųËAøãMZ•ėã´Fí3åŋLÖļ?áœŧ[˙=m3_eŅ\ŽĨgŧūäŋ[œNŽ!ęįoDŋ[ūgƟđÎ^-˙žļŋ™ŖūËÅŋķÖ×ķ5ö]šë?āŋČ^Ōŋüür˙#ãOøg/˙Ī[_ĖĶSösņŨŊíæ'#ÖžÍĸŸ=oįüų´¯˙?ÜŋČøĶūËÅŋķÖ×ķ4Ã9xˇūzÚūfžËĸ—=oįüų´¯˙?ÜŋČøĶūËÅŋķÖ×ķ5Įėëãâ-ZČÃĸäŒū†žĪĸŽzßĪø/ōi_ū~?š‘ųŨâĪx‹ÂŽFĩĻMYĀ™FčĪâ:~85Ė×éV¯ĨŲęöRÚjņĪ ŠUƒ¨<ø—ãĮÃŖā_ ,”˙dŪaëˆĪ÷súĄô­čâeĖĄSŽĖ顋“š§WŽÍ~Vč˙­/ĸŠ+´ôŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ĩ4VņØļŅŦ'ŧ”œ~íxV<ÄÕīø^ëÆ(´Ō,ō §tŽv ęú‘_xxÁēOƒôˆl´ģXã*įÆKį>žõÉ_á.Hoø#‹Šp—ŗĻ¯/=—ųú'iߺߌîĄM´ ĖIūUoūËÅŋķÖ×ķ5ö]ĪĪYũŋÁ~Ōģ˙—Ÿ‚˙#ãOøg/˙Ī[_ĖŅ˙ åâßųëkųšû.Š\õŋŸđ_äŌŋüür˙#ã7ũœŧ^•’Ņ˜HÍ*ūÎ^.* ’Đã&žËĸŸ=oįüų´¯˙?ÜŋČøĶūËÅŋķÖ×ķ4Ã9xˇūzÚūfžËĸ—=oįüų´¯˙?ÜŋČøĶūËÅŋķÖ×ķ5SRũŸ&ņL?4Ī øhŲųēS\´—1nKv02œrĀ.@%‡5|Œŋdîz…f]kē}ŽŋcĸĪ9]Jö9&‚-ŒC*cqŨŒ du5Âøc\ņf•ņ"? x˛ōÃT†ōÁīmŽí­ü†BŒ)\‘Žk˜ņĻâé~8hQŲx†Æ eĩŧ’ÅŪĀ8ˇ‹å܌3ķ“Į=¨PÖ͍SÖ͞éEy§ÄmgÄž ø7¨jsjv×~!´1˙ĨĨ°HØ=¯úŗ‘Â6?Z“EŸĮū#ŽMY§°ĐôÉāvą°ō×'(|ˇ•Øí$ŖŒqœäŌåŌäōi{žEpß ŧc/ˆü oõĪ.ßTĶž[]MpG,_x‘Đq‚{sړáˆ5očzöĻč,¯.åū΅b ˛ŨXĒ’z’H=i8ĩq85{ô;Ēķ›ß뚖ŋŠižđėZŧZ\ĻŪōōæė[Ä&Ėhpw0Ī>•č㨯,ũ›[á]­ÉQįŨ^]M;÷‘ĖŦ7|(…8ĨfØâ’NLéūøÁ|Uk{͌ÚnŗĻĘ-īėf90Ɍ‚ņ!äĢwâßļ6•ö~‹Ēa1€ŸPTŸũW¤hļß´ŠŖ„KÖy@' áʃ§Å~؟ō'é?õúŋúÔÍZqkēüI¨”jAŽëņĶõ>IĸŠ+Ķ=€ĸŠ(ĸŠ(ėŸŲ;JŠĪáķŪ*ūöęvf9Ī| ĩíÕ䟞˙ü’ģOúę˙ÎĨņīÄ+ˆ|bŪĐõ}FšÚ=öĨАV"‰°ÜäŸA^U4äßĢüŲâR‹›—Ŧŋ6zĩå~ņÍėŪ1_ ë:Ū‰¯}ĻŲî-/ôŧ/˘ßˆ€pr|Îđˆ<{ã)ŧSkĻę:f—ŦĪiėöžk2)ÂÄŋ(,rNáĶëČÍŊ›ę{-eiŪ Ķu-sVŌ,î š†•å}˛?-‡—æŠd䌀zŽõÅ|9ņ‰ĩŸøKü=¯Íeˆ4YVī­âũÛ #-”'’1’8āë\'ƒ4ËņKâ 6^)Ķ Ôĸ6m¸m42Üf)ĩ7|›FAõĄC{ąĒ{Ũė} Ey—Åŋk^ŧđ‚iPhIröķZĒĒĩËlT1&Xõė*§Šĩˆ~đ īˆ/ītË­BŌXŽĻąŗļÛێ%Œ3dņœîô’ƒv%SnŪgŦQ\WŽ„¨üĒUē’šOSÍãøĢh~!ĶtĪøu4ˆĩIV ;Û[Ąq ”œl~^ŖŸ~xÉĪ‹,aÔŧ9¨Z\Žč¤‰ƒlW–üd›ūh^đūn/-/âŋÔįŒü–1.@Üzo;‰ ׁë^šŦ˙Č*īūšĩN%/dũ´Ŋ‹~Lüظ‰ žH_ãb‡ âŖĢzŋü…¯ëģ˙čFĒWŠt™ėÅŪ)…QL ĸŠ(Ņ˙gŨ*-[⎗ā‡3}Füˇgđ¯ŧ`p|Cû0˙ÉV´˙Ž-˙Ą%}ãŋŲø7—úîĸŽđZ¨!rîHUQé’@Ījķj]חËõ<ŠÉË/Dŋ3~ŠđkŸˆ%´Ķ\>1đUˍž{č1¤¯SËģvũŊ Īję>!xīS´Ō|¨ø=bē]rú(Ä2‰QаRØ%yę@ĪĢŲ˛Ŋ“šę5—¯kÚv‚ļ ĒNa×qØÁˆŲ÷M&v¯ã8<ž+Î5mwĮ ņG†á!Ô´­[HÖõĶdŽÚĖĀÖī'Ü*KFsœįęk?öƒļņj’ÃVļ‚ÂmvĘ {PíÎ\Ŧźƌ“Ŋ ĄÆžŠ7šítW#§EâmÃ:Ôū!Ömu;Čá’ky ´öĄ8+“žFkŒøqâ/xīDŌ5(Ĩ°Ō´ĩU7ÛīžõÕŋxcPvĸvŒäŌ—/Ry4ŊĪaĸŧ˙᷉õkīø§Ã~'(uM&ët,a<ûG˙VüpOã@ÆAĨđg‰ĩ?|@ņJA,CÃC%Œ!QKOs€dbŨpŊ1ß úŠZŽwõÄxƒÅ^ ŨƕáO ËŠKjíws‹k`HČUc’í‚: ×oXž-ŊÖŦ4Ÿ;ÃzDZĩ˙˜ėō\­¸ AËn>œqīJ;Š;œīƒ|wsŠøžīÂū$Ō‘âxEЉ'C4$ãr8ÁüŽöŌbÔ>ŨÎāy–Ŧ%SéŽÄ~5WázOqņ7XÔvĶ"ügå^r0OƎ›FŽ“[žˇXâĢņÜ~1\˙hžŸũ¤$ÚžW—æņŲŨ‘ĶĮzã<â âŊ÷ƒõ­jMnÉô‘ĒÛÜΤąūôFP휟ĀWyá]iž?Å`ž3ÕVíü>× }äÄdHūĐÃÉÆ1ˇ<įÍ5ՄiĢģž‡ĐtW˜|\ÔuŸü+†m?TēēÕ-îmâkĻUO™ €1Č8â­éš¯´ŊJ˙Xņ!ĩÔīŦŲm´ûH•`ą”ᐇ ŗŒņķjž]/ry4ŊĪDĸŧķá׎ŋ´>I­ø„ųWúJ˨­ÁIaÎėĐü}ĒßÁģsQđLŋ‰îžkíNGŧHČmácû¸ÔØįž~ojZŊÁÁĢÜî+…øŗã;Ÿ č¨ē,6÷ZíĘČöđNNŎ$i$‘€ í ¤u$ íŽgŠÖŪ[‹‰8bBîėp@É'đ¯ ˛ģÅz/Žüu¨\D‘Üéwv5ģɃĒÆāžĶČigđ§}XéÆîėõ¯ę÷˙‚´=^õb[›ë8Ž$X”€I8ükį?Û HŠ-_HÕ–D1;¤r@üoĖ×ģü¸†O†>Š9ŖyLƒr+‚WäGjņßÛ#ū<´O÷Įō’ŗ–•an˙Ŗ3—ģ^ūoҟ.ŅEęČQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEô‡ėk˙!OšūÔ¯]øÛáĢÍfÃDÕ4í2 f}ķíĻNŠËw.×L0#wLpkÄ?dR+_jļ20u 2gũ’Ā˙čBžš¯2üĩ§ëú#ĮmÆŊGæŋô”xĨļĩđâōXmô·&įQ‘•E°đŌŖ!$gs2y$į 5Đ^A/ü4N›8ŠO xnD2;C}Ŗ8ĪLûWĨ択rŨCÎ5xe?<=0‰Ė+ĸÜŠ)Ú `Ķ5™ņ3R˙„o⧄6ÕŦŽŦ Õ<Ģm6Úį‡û2žTōģŽ8ū.ššÛūDũ'ūŋW˙@zúžlũąĩXŋŗ´m,0ķŒŪy€)û0Š“æœ}WāLåĪRĢđ×ōGËtQEz‡˛QEQE}šû/˙É+´˙ޝüë7ÆŸ‡>(ę~$ņ†cņ†õ¨"LlíôųĸP€í*HF’;ãĐR~Éú¤W^ÍXyļŗ2°üsüŠ×ˇW•J\Ž^¯ķg‹F\Ž^˛üŲæ~Ôŧ¨xŠøDŧö9UɨlÖ‚6ī* -Ķß4߁ķ[ŲøÛΊHŧĪßH›ÔŽå"<0ĪQī^EhähįtŅæŸ š?Šŋä’)9n,LnĘ@|BĀā÷Ž~ÛÄ1ø+ã?ŽåÖtŨY ÖÆK)m,Ūq/—Vh<åąøjöē)ķ÷>ēŖÍ>"$ˇ^9øe1Ōü ¨Į#鞟Q/.XO lÆ[Ž IĖv¯ĄkŽøsĄj:|zޝâ…uŨfįíÄåÖÔmŠ{…įšc]9ĘėĒ’ædWSĨĩ´Ķ˟.$26NÉĀī^ â?ˆz=˙ÅOë–öÚÉĶ´Ø/åΛ0*dŒ*`mÉįŌŊū—'ֈ´ˇ$Ŗē(蚝žŗ¤Úę6bQop›ĐKGĮēžGã_ ~ÖōQŖ˙¯qüëėĘø‡öšÕ"Ô~'\¤,[Fą’=y?ȃøÔÃZСŸå˙šzâ!o7ø[õG“QEéžĀQEŠáoųôúü‡˙Cú&m˙Ãíg#K‹c 0ę.3ú×į“t,ĩ[+ĸ2 ™%#×kũ+ôs@ēŽ÷D˛¸ƒG$JAJķņ:V^ŸŠåâô¯ä˙ŋĖđŸ Åá¯ic@ø‰ā[WÔėÜĮĄ†ˇI}~Y7„'wb 'Œ÷ĀŪņąĶõ>,>đÄē-¤Ĩ|˜€ļkŸž?ۈÔĪL‘Ÿ—ŌŊĸŠ~Ķ[‡ĩÖį-âÄwø]ŦƈÍ!ŅfP d“ä7õŦŋŲÜIđ?JŗXœ]>†"0Ão0ā ųŽöŠŽm,G6–<“āwŒmeđĮ‡ŧ2úfĩŠmn`¸ķl$XĄ(å¤#hn|:Õī‡PMÅΊ2É ‰ŗiū[˛¯ˆ_8=ņí^›E7-íÔ§5­–įņ“ÃĶøƒÁ3ļšŽÚÆ™"ęV3ģĪ‹æqÎHČãÖąž ÆŪ!ŧ×<u YuˇXl÷¨ Ŧ@.p †=yÚĨiüCŸÅ÷īuáßčČļš…ˇ”uˇģ-wå_÷xÜXAŠöŽÃCĶ-´]ĮLą@–֐¤¨ūęŒSŊŖaŪĐązŧĮã'ŽŽ|7öM†ö+D.ŖĢN,ĸÎ zCČ ę}§QS“ģ"-'vxĪ‚|yā iqišM—ˆ Hûæš›L”Ës+}é%|rÄ÷¯ZÖ?äw˙\ÚŽdúÖO‹/ĄĶ|9¨]Ũ6Øc‰‹lVx†9?&eŠ’t¤ü™ųĶĢ˙ČZ÷ūģŋū„jĨIs+Oq,Ī€Ō1sRsQ×Ģd‘íEZ)0ĸŠ)”QEzĪėÃ˙%ZĶū¸ˇū„•õwÅĪ Īã‡ÚŽfę—rĒÉėmgFĒsؑÆž@ũŸ5H´¯Š:[Î@Yŗ'ÔāĪn?ûŧFG"ŧڍÆŧž_ŠäUn8™?$˙3´ũsĀQYCģđŅm5å]ŗXĮáĩ”ų0ŒąáęŊnøæ+ü-k-&M:Ö!“ėK_˛§–ß+*ǚāã€kÖ3EW>ˇ/ÚksĖ~6ÛÍ=įÃß"%ōüSe#ėRÛT–8č=韴 ÜÅĄøcRˇ˛šŧ‡JņĨũĘ[&÷ |>¤ÆŊFŠJVˇ‘*vˇ‘ÄÅâË/x_Ä'J´Õ(ŦåKĢ9 Y ¸MĀn#ŒãÔSū Å$ ü1Ņ´r-’F\}ÅvtRoK!9+YAņœŨxC]Ōü{¤AæË2鷑¯YDŠ|ƒÉĸsķk´ø]á×đŋôÛ “žũÜ^IÔŧōÎsߓ ÍĪgâxŽÂĶÄz z?‡ô›ĪĩČ~Ö&ūŅt'ÉF0™ČlôÖŊ>ĒOD‹›´TB¸Mgâü#z­Õ¯‰t=V `ų´Ŋ´ˇk˜gļJQķœŠíƒßŽîŠ”×S4ŌÜņ Ūø˙ã6“â› :úÃÃÚŦ­Íė- ^<ŠÃŦÚ7uö÷ŋûB˙É(Öŋë™ūFŊ"ŧ“öœÕbĶūŨĀė<ËĻ(îsĮõ'đ5gĖ’ķ_™åĖ’]ŌüQņQ^ąí…Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@T~Æ˙ōÖ˙ëã˙eZôīŠžÕooøīJņļ‰ å„ëį‰bc†Vîõ˙=+ķŪ´t-cQŅ/ŌëIŧšÖ|€Z6ÆGĄú×-|;›į†˙™Å‰ÂēÚSv—ā×õŗ?I¨¯øyâ­n˙CInõ e“’ü+ŠūÜÔŋįí˙!ūāĪ6§ 8Ę.čųš™Ũ*rp”]ץč4WŸnj_ķö˙˙ ?ˇ5/ųûČ…OöÍå‡ų“ũŋCų_áūg Ņ^}ũšŠĪÛūCü(ūÜÔŋįí˙!ūlŅūWø˜oĐūWø™č4WŸnj_ķö˙˙ ?ˇ5/ųûČ…Û4•ūæÛô?•ūæz įßۚ—üũŋä?Âąŧ[â]^×Ež[{é#†z})Į7Ĩ'eø9í5wéūg{â˙éžŌ'ŋÕnR$‰IÁ<“Øbžø™ã ŸxŽįTŸrÂNČ#'î ū§ŠüģU/ø‡V×õY_XŋžėÆä sÂũãņŦ ÷pô~Ō{ôō˙‚}. âũ­Mú.‹üߟü”QEváEPEPĸ|øƒ'€ŧN%›siˇERáGđú>=˛síô¯¸´-fĮ]ĶáŊĶ.x%PęUā׿Íu˙ŧQ­čZÄ0é:”öĐČ˙4jAR}prúW"ƒMՇÍw˙‚yØŦ3MÖ§ķO¯Ÿ“üĪЊ+ĖtjŗiĐŧˇ’3‘É …]ūÜÔŋįí˙!ūáŧâ’Ķ•ūæ|Û΍-_áūg Ņ^}ũšŠĪÛūCü(ūÜÔŋįí˙!ūlŅūWø˜oĐūWø™č4WŸnj_ķö˙˙ ?ˇ5/ųûČ…Û4•ūæÛô?•ūæz įßۚ—üũŋä?íÍKū~ßōáGöÍå‡ų‡öũå‡ųžƒEy÷öæĨ˙?oųđ¯?øšã {MŅŦu)abŧúŠēYĨ:ŗP„]ßéšQÎiך§ģžû~§uņâ^›ā lJŗjrŠX SËŸî{}p+áNú}KQšŊģr÷4˛7Š'&FūīSŧ’ëP¸–ââC–’FÉ?ũojĢ^õ Îō“ŧŸõeũj}6 é^sw“û—’ūĩ (ĸēN°ĸŠ(¯¨?fΊÖée†5ųöKËk+Ÿž;.}GLw÷¯—čŦkŅUVökfsâpęŧRŊšŲ˙];ŸĻąēȁã`Čy A§WĮßŧmâ9.>É.¯s%ēǝ†ã¤d×Đۚ—üũŋä?Âŧ,N>8Yû:Ģ_/é7‹ĖナėĢG_-ŋƒEy÷öæĨ˙?oųđŖûsR˙Ÿˇü‡øVÛ4•ūæsoĐūWø™č4WŸnj_ķö˙˙ ?ˇ5/ųûČ…Û4•ūæÛô?•ūæz įßۚ—üũŋä?íÍKū~ßōáGöÍå‡ų‡öũå‡ųžƒEy÷öæĨ˙?oųđ ëš–ú[ūCü(ūŲŖü¯đ˙0ūߥü¯đ˙3ŋ–DŠ6’VTEäŗ_.ūŌŋ žü1 ĖwbęUčuúŸĐg׎gã|Fo>Ä5k•ĩ|†D!r1ękÄĢÖÃSxˆÆŦžíūåøžæ“ÅF5§¤wKŋ¯ų~!EW¨{!EPEP֗Ú]Csnå&…ÖDaÕXƒųŠûwā§Å?ÆZ6÷3$ĩē–&8öČö÷ü+áʞĘîâÂę;›)ä‚â3•’6*ÃņÍ^‡´´ĸí%ũYœ˜œ7ĩ´ĸí%ũY˙ZĻWÎ|eâ OJS}ŠÍ1 ÆUG§ ¯KūÜÔŋįí˙!ūáUĖáFnœâîģHųĒųÅ<=GJ¤]×mŋOČô+Īŋˇ5/ųûČ…ۚ—üũŋä?ÂŗūŲŖü¯đ˙3/íúĘ˙ķ=ŠķīíÍKū~ßōáGöæĨ˙?oųđŖûfōŋÃüÃû~‡ōŋÃüĪAĸŧûûsR˙Ÿˇü‡øQũšŠĪÛūCü(ūŲŖü¯đ˙0ūߥü¯đ˙3Đh¯>ūÜÔŋįí˙!ū_P×õHėĻtŧpÁIü(ūØŖü¯đ˙0ūŪĄü¯đ˙3ŊÕu+M*ÎKĢųŌcRÄą_|zø˙ ׈DV,˛l؈Ŋ$n›žqõ>ĩņCÅzîŗŦOiŠęwÛ#dDH ׸ĪNõÂס‡ĸäÕYü—ę}æãZ§É~¯Īōķ (ĸģĪL(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠčŧ⋟øĻĪW´ËyGlˆßCÔ#õžōđG‹ôŋhĐ_éw) qķ&pCc‘_jü뭏 kēžƒĒE>‘{5¤ŽĘŦc<0ĪqĐõ5É_äũ¤7üõÜáÄá\åíiŊzöäü˙Ĩú?Ex˙‚|OŦŪč6ķ]_É$Œ – z}+ûsR˙Ÿˇü‡øWƒ,Ū”[‹‹ē>fyåIÆQw^Ÿæz įßۚ—üũŋä?íÍKū~ßōáKûfōŋÃüÅũŋCų_áūg Ņ^}ũšŠĪÛūCü(ūÜÔŋįí˙!ūlŅūWø˜oĐūWø™č4WŸnj_ķö˙˙ ?ˇ5/ųûČ…Û4•ūæÛô?•ūæz įßۚ—üũŋä?šßxŖY˛Đå–ÖūXäဟJqÍŠM¨Æ.ė¨g”g%ÅŨúīŽügĨx7Gš÷S¸De$}Y›°Į¯ĩ|ãŨxˇÄˇzŊé!Ļl"g;t×ęMWņ.šŠëē”—Ŋė×rŠ*ĻCaĐ~“^õ ;‹ö“ßōūģŸK†Â¸?kQŪ_‚˙?PĸŠ+Ŧî (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€?˙Ųendstream endobj 3 0 obj << /BaseFont /ArialMT /DescendantFonts [ 6 0 R ] /Encoding /Identity-H /Subtype /Type0 /ToUnicode 7 0 R /Type /Font >> endobj 4 0 obj << /Filter /FlateDecode /Length 7314 >> stream xœ•][‹e;n~ī_Ņ klËōB s}žp y™Ā$Ėä˙CôiīĒŊ$mĢęL3‡ĻZe˲Ŧģ´ęĪ"ūŠĘæn?˙ķo?ūū?ĩéūņ_?ūũį˙ā‡×d…}ū埞āõ'ūü۟~>ūōŋūøÍŸĘĪŋūŸŦR ŸĢŽĮ_dĨŋčԸɤĮÚ9ÕĘM¸GyũjÁFŋČF[~eˇ˙`gũö—ŋųŖüvŋÆķį/ųQ?8[—Ÿųáß~üs)…ūåį/˙ũãŋüøŗüy{čI˙Ô˙ôą~ÖĩqōoüÛßô1¯-˙{îũøW…nƒŽÆ ܓLŋųōķ÷˙{FâLųįĪ#ˇ õÁĪĶÖūk­tÍšųĶ—ƒiëĨ÷; ŗƒĄyõ%Xß×ųƒétõBfîņár­˛)]‡Į5ŠZœģƒ™õbv/ķ­j÷úŖƒŲíĸB)}Z™—ĐČėõäŦŒp#“Ĩ!C8—;ģ;Wc9×č†Îė÷8×ļ0ãƒĶViĢŧ9ÃëwŪŊ(e/aōį‘Ļ#_é׿f`úo# VųMX°Ę‹éëãI\_ԆáęŲTXPø=ŨĢ×k4ÂåžäXŗtĻĢ´]S”Y8§TķŧŠĖčWo;ßkŦ‹Kĩ0žÛEôˇÛsųŖ/á."CÎZ×eŽ<šMTŗ­ZŠWY=Ešžö°ˇŸ0Ĩ艝‘šÕuÕeo˙ÕÁˆ›ŗĩt/y ģYú„ŊzŠĪ–†ĶÃėKŪYO÷Öh{rÆMXc4Ų}5„‚ÎLé<ĮĩÂéĩŽē"ޞ‹ÁĮ Ô&ŧŅzéšļ§ŨŌ—LĨ\Bž™á#Bųš­ėŒ>T…ĮzĘĸFDvNõ"—šhëŲŌ‡Lm^B@ËņNĩÉm}ÁaÔÛĩ‡ųÛĸ>¯)Š<ĨލQ1šėš<>ĖWĄ–/#ēVKÉ#ÚĻ?´Ö‘1hŠöķĖķG3¯ÕGĘ<´D ė9ĶĢØÂ¨c[†wšļ—&Wē8;W¯r×āã5mÁÂ̘ŊĒS9Ŋĩk8čqîĸÅ4ÍaHLŋméĶ<Î"|ØąXX§ v'xktą2öl”ŨEE^õŒUŖ¸ä7kÆcb:_m”–ąjŸb…QKyĩO–ģX”Ūé"XX=ãÕžëĩv¯öÍāŠÉÂ" D€§Ō‡År¤ąWÆĢ\ˇÜéŲšXÄĪlŽįũ^Ä×Z%Į‡6Ėĩ•Ņ™ÅŠ˙Bî˛HQ9ŠÄd6,;•ŧ<Ú5k„ut—e@šEXœÃ^KL1geų­äÖŠS~ŦŊ¯^9ŧC8Ŗ/K˙rFYÂVŌqs0u\,öXvôŗĻY3Âã&L˅ŋķōÖģÕÉÁģ{û(oNđúąë-ŧzØĸ؟z­Ķuō äp^Ķ ¤yŅ oėŧĻ*tÜl`‚˙߄Ž}´tÖņ~ė:ŪŨĄ Ģ„Rœ!5÷ėÆ{_ĸu×\9"Dktŧƒ&^Á.ÛŽFžAņÜ`üŅÕÜéŠā öb¨Üéĩ¯ąĢĄ˛wvę†]× ƒsgpÃ;ī%:Gœ Ë`Ũ;qu^Ŋ”™Ũzkõ{bf$„3¸JYŲU¨3ˆN^œ!‘ˆ´S| 57§wŅXtnˇœą=ˆ˜uå[*žWą[ųuDhÎēĶĮĨūâ,öڝ‰ą6÷p< á/ÎÉ)™Å_ėmėēÔäačãÍpąšÚJYūâ.eä0 ĶØĀPđų)ĸĪį""137e1‰Ŋŗ§ąĐ¨ûĀŦkĻĪŽžė—>SŲFܸnīË߅đĪn–>ÁeW¯Lš˙đ5wöCbōģŗ{œÅÜĩ§Š@<‚kûu<}İļī…ģ𘈊ôŪÅ8ÃŽô Ÿ.&'+2ƒ +ŠiôšžSqËŸiVáwP4J'JņŖ­Tļø8Ņ+ēVxcsÆ?pszī}ėĢ֚ŪģēƒcĨ(‹ˆę==øBLÂĐ ¨.Ûė–€Ū_d1jÖĻž!ĖĨ‹ŋ˜ž`/œę%›k­Œ š5ņķzǏAw1üÅZR"Ã]ŦŠ-Â]\”â^Mp(…KGˇ’ĮqÃîŠl/ÔIxx‚eŒT›đė— –ŨKįĨܕŸ|ŊŦĄá%‹äáę ĪKÎUāņ'øŦõô“ŗ‹SÁÅņŗéŠS14{uæC1C‹5wá=IÄ×Õn;K ˛ęŦŲąP%¯…q$$aŸĒ•B7t|čŧ# ]VƆÔakÖôé8­mRĪX•XKŗ*ØžŖ\C+ŽnëxÛwˆ=G5}$BŦ, â#ųĨ ŖĻˇ.n+™r3‰Û:P꟡ŗÅŪŲÜŗĢčÂaÕÉ yēø­bŒė*oÑŲī%,֛#ŗ/JëģÍt/ŌríôJ5J_yĨøˆß篨é:,’ÎŪzõIƒXĩ}¤!.>P,ã^…¯‘[oSI…O_ā°j¯kxaą˛Ķ×.¨ íU„Đ¯{S“gvf¸)ÆÜĀq `Œ¤†õD֘ 7*˛§/Ģ‘Ã^ÂaĖ”žœ!6į뇉AÎS8lõ•jy4B§*žU×õÖāüŽËuŒzUHM/Œcøˇr†gÆ9-|1xPē—ēeÍāĄ‰šnDoëˇlŠ“ƒæƒq ÉÖ%÷ŲQ†uÛ+8fëžš­ķÆéÚ›ĐŪÂĪΞķ*­ĸ PŨ s€¸ĘŠZˇ0!Ȏ@sˇ§ t*ĪŨa‚;…(Ī)>ė5˛ÛBŊŗZg|d×ôō8Æp՝ZmĻøĀân÷ō0h)ŧŌŗŖtüNQãIsd¯ĸi'ĪlmoŠce4ðŪ}¨6ÖÆÛ+$üípFCj(ëiœŊRáAH°™â,ūÔÖy房ę%]4—Ø`é›čÂ<ĶīåņojÍüM¨7ÅuĻ8Ī…JŌ™â#LØÜ^ĄllŖ÷Āâ;WÔa7{}+Ģ ŅpÎī•?ķF>iy *ķ¯Âåaá|ūNTū„I‚Ęg˜WP™O÷r *ŋ`ÎAåķ:Ae>ŨÜ-¨Ė'Žŧ•ųÄmˇ ōk¯sPųĩÎ9¨ė3ƒ7 į3¨üÚ+XJp­4–p<;Æeˇ‘ ĨõŽ9¯ §æˇÕā7u{[Ņz)aķŲEMm ¸­š|ÛŗÁų,œr=4Ôņē&Oh’ęĖ`j=bÁįcĄ†ĢšvfjKÕnCĘâkƒj‚Z…ëhŲmQYū@žör%bRæ:˛› ē‘ ´œÅ+š}fäAmõņ ‹ũˇōŊÄyg-˛;s!Ęē—vŪ`ūāaĐO32#OĖũ׋Xšå Iŋ#î_éM/_â~ū*\âūUKp÷įü>DųãŊ`ü:pDvŪ9;ŊÄ>[Ãė5BW4í^^õĄ”Ģĸ.j$YnQįC]ŅcōãIäz{†fí.@’ ×:]NņiZ§kĪî%„đÃH`Pķ¤Ŧ}ÆGc`ÛĀxöGsÍj+=ē°†<"ÃrA -¯2WQŊG+ΈąĢ×:^ꉖŸn@B°‹Sō ąWûČXõæ‰ĪūôDS2Ģ„]˲|žˆ f§ĸ-oö;ŗ<ŸĮG!ļŊ1ÍúĘWY"ÃģÅ8—4:ŒÃôą‰Ų`üŒ÷ą&fc@-'ÔA+JĨ‘ŪĖÅö&ŧŅŽ,˜–=ŸĪ_mu6øÄĒē…Âú•ą8Ũ eáuΌa°Æķ“Ŋē¨ėžķÕ´ÜōLg-ųn-EÕÜ\,k„yL„q)37šöÎ+efT|k‡ū™ÃPđ-vhúļÚÍS'eÔAŲŽ(ŒžQõßM qĪ(“žžg$„°´ž1Ų ąhĮaīÚk‹ôŪö >ōũ–ĖĄŠī‹8= Kå0úIl€M2~W`qĪäMx—5?9Ęv´,å†O°æ3~qƒyŖßņ‹ä&ÄE™ËQųMãėb÷ŽCi¸°¤{Š ļüy/!¸ŨĒ6ߤ+ÜCŞ+Ø$b(tž):bdrŗĖCžŧĨ‹-=—Œf+šƒŨ2”ÅV&30m´¯ŲŗĮÕ§<œĘŠ:AŲŽŧ­ôĘ>b-k‰„@3Æ58ÁÍHŲZÁ \0ėCÍđÛ:žčD<ĶĄĨį§Ŗmąƒr|Då”jī+÷Ā`go…qG†,Îąpg!pņ8O$ËRÖ§ôâí$¯˛ÂčĮæ$oŖ¯ŗqK¯} Ž#$Ī’Wg$­ZY:#Š—ôÚGC‡ĶĨžĘƒPÁŅŌg1HË[GŠŗ¸u–wŌų‹ŌūN:Ęī 2B`şEUąhO~øŋ~éU|&nČ›ĒŸ8ķ˜4zu’ž`‚ąßÚŗŒc•„9D<—z"ZĒâÎũ-Uqį†Jí-ŧ˰O‡œjaÂĖ4t–•˛!™u CYŖ8 n¯wbõĢŊäÁ†Ŋ|ÂĄ"°\gÆÎ(¤Q,ûøäFÛāģ2:Cöz|b0ĻG|ÂĐ4uNGÆ>Hœ¯Iœą&΋…‰åƒhā”U‘´‰@˙č´ŗiaŽ!,˙Å^›PĒH)ÎhˆĐö§ķ}!i3ģ… ķŸa>k”éŧ—NDSsõ|_H¯ķjœ mũĢļôžHÄaé-Ŋ/„uwKCŸō† āaBXG<ĪeaÂä0 q0”ĒK”Đæa™;Ŋ¯ék3ŋ ”ąjcN˛† šßŸך߅Ö*Öü.„W‡v2Ūđņ=RâęUë1ŲÁø‰_/˛Å' ëĒķYô>‚?ŗZg<ËŖˇõ† gpz'ĩS#h”T8_ûa?2r3‚>aüˆđ›ä?rƒŋč+˜6â:^='`Ũ`ŧv@ŨâüļĖ*ŪŊ‚É1ĖŽH ŽĨÚéL?¤đ‹ÅåŨ§6ļĻ[n{…Q˜†ä`Ū”ęŅCƒņą5$œėĩĩ‘ĖP'V‰TÄv(ģOT‰l´Så-—|čf!$lø4X%°84đ~æXŖ9īÉŌl&–ę҇–;Ū)ĘųžZîȃøf‡ß+65L$3sœGGUĪJiˆ/G‘ÃĮĶG$}edo uxBfûū‚5AĪ4Ûm/_n‚rЇ;žũßO ö‚ ßė:˜ yDŠ—á`ŧ–CŽĪô-ŖXo—üė(ÄĢ´- à Ö-’ÅŪWlËNÛjœQfÔV*/ÕR`ˇN¨Ö›Ī>û„†ˆķ¯•Ęot,ƒ‰e HQÎd ĄĖˆJ*Į&zŒ€~ÁøBoíîž&hø"ōÁ¸Œˇ8ĐđB&ö, ÁĊUNÃpžIôvˇaw )‚ŸŌSdPÆļØ*@ŸŗęüĖŝ/CZwˇ0!„.ßūÅ^CŨķĨ‰„JĐpÄÔæ™ãc푯ũ°yÄūŌæų„ “¤Ôāûüį8āpāš4D!žã`ŧiĨĩŋË ŋ“Įq3>¨jlpŽņŒÉ°0qˆÔ {…ū æË Ÿģn"4žs^)&r0ĄxvE fŅŖ›69ׇA?rŖOčrĀ÷öŒW×ã<ˇ9z¨E1ËÎ8Ŗ ÷čPÆÛFa@TÃįY˛3ĩ†~÷bØ8˜ b}ĩ“°×W;ĄŋÁīäCBÛ;Ė›ēŲĮŒü3—ļí`BŨ,ļƒņF“(´Šĩ†ÉU- ô´0q؍ŧžĸ!†×^a”•!Lƒņŋ2úč@œŲSNÆPúσĮo넏˜:S-L¨ŽEâÚÂc§áãl_ŦƒÎ„ų>!!Ļ&ŠÚöėąËs…ŊbχŊbßāš;ã1´8ŦjaB8L$X×o(%{MTUNé37Ęí-…ī˜ĄŅÆÁxÃs#l¤~ÃįX_‹īY˜ĐĨÃ|í‘ŨW>äbqŽ5:ČDŊwoƒ lÔÃĶĒ|ŊŽ8aŗŽĄæšŌGģÕ-L(.Öīāõ–â3ĐËea¡āæŗdŽÍ Ēl8ôIā.,ö:|Q¨ŅŠsåŦá(eĨ,ßQ ¨ äįį…ņ;-L“_1’íFôˆļu0qH¨ÛZzö,0€‡}d’Ņˆî`ÂPcMģkÎ,†â6*ŋ“?GWOg]Ԏāúx÷ĮˇÜcŅ*\Ôcû„‰EK¨ ˇ0Ąh ĄÆWŠõŽžßk¯í×@&-ĄŊĐÂÄĸ%údŋÖ ŪáøHgŸqVûŨÁŧi‰,dabĐßĩø„Ē%dééã×^ĄÕsî\ž>KŋŖ˛Sœ1%:Hå{˜ĘĮŸŸVņ]Ōž ūáĐ¯ūųPĸKÛ&›['¸ķ#hūZ'˛{XįŨ˜üП7IčŖļš… ôŅø_Ŧģ›Ü:!Ø –‘ŌYÖ3Øũ:Whšāāûųėk}ߏīĢáąn8 h}š•ö ~’đácęŪ ŸŌ­„ë`Ūt`"˜îĨļšúmgœŅ߆•~dŽ(ŖĸũOgųŖSS—ƒ îUljëøâÅLQæ-Gw×̊]IÆŖ<é#đ"ŗG#Æš€#;Ŗũ♠8ļšƒ‰qõ…īÚĻ×Ūa›O z Ē{Ŧ:ß-bËX˜WĮG7kO×öĶÂÄo›‰K_ÎĄO3&Lp˙ęj†ƒĢɟš‰×š|õÂæ:!!9;Ę2uŒ~rĶą9ķ}b`—[ĮŸKÔādNEx_NčŗĘŲŠhŅ>ŽbaŧËE?Ī?Ã˙Û&ø (Ö×Ų"grC8ŦĻ" ŸÃĻM5ãyôzLA~~ī ›š[jFá gŲág:ëδ˙"Á™áņĘî‚aS§*—1;|p*[đ4r0a/|Ģas*ĐŌtžøŲ$a´‘yķ†Ęĩ0ÁwŅ&ęōĪ(đ]6egGCHķ0ÁGÜbŽUkĒ{ 1}ˇŽ÷kGc1[ÜŲŒČ:Îé> stream xœėŨœu˙đyūĪãŖ-JŠ( *ŨŌŨ-)Jˇ´()-Ũ%ŨŨŨ}G^ĀqĀu÷Ū~˙ģ;›ĮŨæĖü6>o_/wwnæ7ßß°;ŸŨIŽ'õöņL?ŋpõĮķôf˙äyŒŒõ‡ ĀUœÛš›ˇk7€ŖÚž|ú‰7ąūL¸ŠũXW ¤-ȉėŋĖē!í@~HųÎų ä8ä€Tā\RA~€sA~HųÎų •Čp*;‘Ųv–uBZ“ČēWą÷ë „´ ŋ?$‚ũā\°˙@*Čp.ČŠØ…ž­öŊ€•ų ĢķŖüš;IDƒ-ĀfČŠXŪDžįގ´›!?¤bm~Ŧ Īīroh_×ũVņŋĪë~(LUVy¯m3+Ļ*_ũ˙˙oXԞЊÖoP_ĨáˇVĖD„üŠĩųņ€fëŋėN!8î:Íĸ&+Í$kņDEÂ3sÜB:_ÜĸÉļ’Æ‹g ĸB~HÅÚü¸G3ô_ūMԞ+’BgŪÔ¸v[1*ŗØ^ĸ­OԔhĮųRp‹&ģK>§Ī*]X`ņ,ATČŠXŸ3õ_N$jĮN¤Íú)ĒšÕuYĄŽÛÅOT\FK8·W°h˛;4ËâY$R(?&QfcŽKȓ/$ÍĢ(ōc6Į]D~8 ä€Tʏ\…ãâhĩū@Ѝhu]’‘ŅŽ[In_Z4ÕÃŨ?`?Rą6?Üé/ũ—Õ)´šō÷ĮBõëĻ/´;˜)˛ļjĐIås˙ˇßt*‚Ļŋ­ąü”GD÷æ”Ö5UhĖ… [WŨØÜëĪšĄhėÎŧW{ÕVĢæ”ØQ;ÆdŨüIžD3´åŠ—”}f~S94‘ã֒Áe$„g3Ū}üū°[ČŠX›s'ęŋŦI>e9΋Š_7Ŋæãõˆrž{zyyģ×Q :đÔË'‹Ú' &y?~ŧŸü(ËÛKFU5-ÕSŒāį•@á?<ķną”ōÄK1y¨‡Ėņõōzœ–=E;Ę!zĄCÁ3™čõĀåÉõėh¯^[š9C•‡nĶoߛ(Õx÷īЌōã7o‡Ŗwíō@*ÖåGëŗ˛LƒUg r+Áq'hšfŽU*˙—ĸ~._šrÕ/ø!oUĢØ „"ÉkdŊڔØ^5ŦJ2Ũč\ĨR/ēU–ĢäcōRĢRû}]āQ…žæléTĄrŁĪÉ]3ŦZåĘEŸæL֎s6]YĄÄp9*Ę›CŲk[V¨>3V( ąņ9nŌŸAQŽņp›^„+â+;b†ņņ@rČŠX‘ ĒMEm Fîx›ãf?5J/šŽT<€čü•Īԛ¯žĶnūI˜fË×zĖ?9C§ * Cĸ˙dŨÖîĨ— ųßGũätD=¨WJúHՓž™1´#ns¯ÉquŖqzʇÄô+hæŧ;Dņ[ƌē˜A덏RC~HÅōüøæÆãĀL ũ-ŋŋŊmđęĩã¯JЋBz¯Ë VŸÁūRžÎ–Åļā‡t•åTAаÔŋTGęÖØ9T¸~~p5U˙™L‡5'ĄОō„ū.¨m3ĄcĒîR˛~ąą)ō@*VüūøĒ|Ŋ÷ËcŒîāVÉ/?–ëŋŪMwúüĸ4x.=ũL9¤yĒ2+‘Ū-¨áqDž'Įæ­Ā ?TzeЙ÷ÔĪŋŧC“‡¨æÖ׍ļ™,Ū¸zƒÔ§šĖ§ oŲØ ų ëŋ2}k~ųQC˙õYŊƒ¤ü?WYĨ7„>(°åO˙ ˆMUŒqĸĒūĐ×ōãoũ3ŌDę5ŊŨdņæ"˙ڂĩ@~HÅúü˜irœüōŖ”ūëãäĩmoīdՖŦ?)tˇzČî-F¯Ûkę!OōÔŋdbŪüX˜A+u¯Ē<Ŗƒ;ÕMīūŅdņFĩúû+ÍSz…kÛä€T:0_|~”]ŽŊIˆ"?žŅaŊîø)ĩVäųŠÉvËz\įŸ”^K›ô†įɏIš;ˇ‹\Ŗū&›6Oņ¤Yl…či5ZA ?¤"j~ŧTæĮ(z 7?jŋLWī†ķĘî÷UOühƒú+īÔnĮ„ĖҚY\˙D7Ü0?-U=éįɟ2‹bÔŋj?gŧōņĶJûs :¨~zŒöé†×øsœe—ņá!?¤"f~ŧP%¸ökŧīO†ë×éôĒ‹ęɯäÁAŸ,ŲՓöŠŽÚĮ?Nâ|ƒS=s&č^ėÍĘQŸŒ˛˜.Ē?ēI×ų3A.ĶZŖ…¯!ēat„}äÕPõdKnŒŪ6ē{DģŪ1:!ˆų ĢķCnúĖšÅ”:ô)’ņg’מ<~J M?a\]Ũ(‡)uéwÜKŌãûLJĖÉÎ9ĸ‡žéī īK”¸ũˇņ+}(†ŽŅ¤ F†äøuÂäJĒģˆ.?AaĖYÍÉå_QxŽĢ{‰ũyÁÅÉŒŪ Ģn%_šđÛļhĸēĄ…‰(Éĉ# 6ä€TŦ͏[dúÆÅ*Vôš^ųWڃŸŽčĶß3]1$Å[w˛yŗûIŠ!9Ī-°Ũ&IOĒŽ¯įĶH=(Kwh&y„ū\‡4Sn QMvŪDáˈnšåDd†ĸĨ—W ģ ´ØÄ„ 2ä€TŦ͏y0ÚôXVũ3I}ņŽĪĻ ŧõŊ ÆéˇøŸÅ†‡CuˇqŲpcģŅļãęĖŪ¸aŽî,ō_˙Q7ža3e”‘šš)ŦŅ_n⚍´0Y÷ĖELRsä†ĻäšÄJ™9>ô‡ÉÆATČVéF ĖŅđĶM9kķƒ{F9qŅɍLčRĸ"í˙–'Nų‚ų¸÷ŠkdžģĢû–2Ũ¨3ą:?¸ųî/bÍø â:>ū”Ö°.Âå!?@(Sn'[JŠf˛.ZRÖį‡âw]‹VÂR éÎjœč.ÁüŦˇ”čëųÂhė+ŗ0=”rŸue]¸„lÉIŅ˙ˇ1ĩ㛭WLÜu $€üA,Ͱ"=TVbēu'a÷ųÁ-ēWÍũf_ĶŖƒ‹C~€åZ$;û_ÖÕKÅūķĀČĀQĢĶCé:ëōĨ‚üį‚üÛmą)>ˆļũëHųÎų6ûŅÆø l9 ųÎų6{ak~PŌ×Ŧû ‰WLā8v"?ĀF˙ØD‡YwB›ŗŽ@HkYWŽļŋų‘T“u7¤°åë „´.…uāāĻ úˇÎvb°˙œ ļ_. ’÷ ŗî‡°˙œ öŸƒŦ?sP_TgÖũōœ ōlĶDø šĘē#@~€sA~€m& ”+YwDČp.Ȱ́ōãëŽHųÎųļŲ#P~œaŨ ”E#k(ojģBÖŒK'ĘøÍ⩚ȉâÚŊ>üIÔ7”öūΞæúæ*¯%=ĨÅ…čĨčĖŨ|˙Ō-)s\žA7īÉsm¯č¸ËDķU׏>ĢũĘVÖ7A>Čȟ=m/ĩ˛é-˛?8ŽUŦįVNoōlƒü0Ÿ@ųŅ“hĮĨĐcˇ-7Oåu ūũũGOĸi§ÅÍļxō8#sâkƒ›-į¸įTΞævæ>'+ 1đ“ģWBūëģ^˙ũ{Ęۋ|Ēmī6å<ŒĸLÕĒ‹[{…•D§ŒüųœįKēd]ËåũéŽōúté"Ũ,ųļA~˜O ühH4œãâéV!››šO;ŒđUÉŨ´ÍōvË|5ūĩĄĨe´˜ãŧÉ̊e­­%ú˜û”h‰å…VUņzūëģĻ ō z¯Rmō6Zį0zÖ˙›¯ėQŨ„ųŖŠ (°¤•uũÛÁȟ˙[jĩ7ķ*äI78n#%ūdŨôĻ ?Ā6Čķ ”ß)žķ†ŅUkˇ—čŦĻŖĻFųĮšüPüĘČ'?ŠÉhĮŨĨG,kk҇ÜÛDsŦ)ÄĀ% ÖwŸ˜Č_egt>ˇ>?LZhm~ŧų˜ŽqÜ Jč$h=ZȰ ōÃ|åĮˇD­9î0yËæĻŅ~SŖl.?8MPŽÍî•ļŦ­ųôTņĸiÖbā˛ëģb&ōÃ3k”ū˒"æĮ"Ģo&üPųÁ–ß\Đz´`ä‡ų„:ūЍĮĐ^ķ坃ÁAû‹pß-õŪ[L;Ú¸û!§ÛLØúdČà šÛ=ļģäC‰ū!zSåĩ‰6sŖn†ÜíŠ4øp` ÷ō˛ŖMŊ|}ܘ'šhxž5š›æ|ž‰ūH94Qš5Ępexîņˇ&ú;ˆžĢz=Z7¨ņē@˙Ŋ4/Ëxē•áĻŪ šĐL=hČĶ@ŋ€įc¸rׂüũCvņ•ųQįy ŋ@ĐFõx# đžÁô)÷C÷5āôķãķūA7˙ō˜ŋN[É3Á~ôRąėžUQ’¸ŌküŸīVŋîú((Āë;ŽĢ{=8Ž­ŽíúûBBWčvÕzޘ{€ˇnĀ–`ŋ€ā›Õ9î`H€Đå’Ęa‹Ņ—BnŽTĶčNŸĐŽú4Ā/ĀWošäu—ÎqÜČ´WÅ ÅȰ ōÃ|ÂåG ŽÛK›Ô/Ģ=LŒĨWm÷%äPD)~č¸ J‰•‘W}íduQnl2eNæ_NŽ‹K§Œ¸øøø$÷ĘÎl]{Ą+Kw›ČkDqņ‰DW´û_>\G”—ĻøwÔŦ)ŸgĖĸ´8ÃŪš4Fš2<ĢßžQ˛‰ūö%OÅ˙ĶH÷})QR\|.ÅüČŋū)!Nž}6“Ōb崇ôwr2ĨÆo䞊§ėÄGüļ>e~ü’œD”–tö_üx[SâãŊÛĨ›ŨJĨÆ=Đî§y{)šŠKUt¯ˇj@Ũ¨„øØJU,ģdMB—¤ĐĶDņI$˙™o%)!WšŸĒL|:­×´ŨÂGąđŸÔ œĒ˜}-Ôŧ~÷n*%ĮGĩå>ôIHŖūÂĸ‹( ^ņoN)|%Å+–Ú•7š9I锞¤ŠŦīÃõ?NĢ›s§CŠŅ“_?’ĖjȰ ōÃ|‚åGˆbÅ6›Öi^WéĐŽ&%? ĐÕmWS`EÕ°q˛ôƒ?ˇŸßҌæO~oÕc*ũÎŋnÔžé~稏]ģv*<ŗMŠœ}ÃZŽ‹¤Žę!štˇuÛN+üI{9úɲú§ļmûWmYSņUŦŖ7lš8^Ž÷ë")Ļ“ōׄÁÁF/ˆd&úÛMõî¸KƒĩCkęNmÛMōÃĪŋ,ĮũώÎá”ŋIč†bu÷îĐVĒ—5üčOåã×ĩ{Č÷Ņ5՚uÕÖLĩÂôW6ŠO>ØŽ‹ņWUÛ˛jŋLU¯MšÄßķÁ#jŖäK9ǘē@z7—ŧĨXŨvĐüBā "Zcĸ„Ę/Ņ܅„ÖÚ!=Ž,R=îŅĩåC9ĒVįhŠzČg‘ĮT+eš!|~,¤ÔŧG-ë$Đ5=Gĩ_ŧ‚—6?’úđ×{ÔK7wöīúM(ō#üKå iĘ)õ@•Ę_Kšü¸A7Uøú%ĨëđuM˙߲Ię$õŗCšų*ōƒ?Ųg5y¨^îŖūĒĮ˜gßņūĶŧO/­ž_Ē›X$Wüâû>:ˆōl#d~,ŋ ÚĻŪę ˙)ŨuPĩäĮûĒMĖ%Oķ×á7ŪW>´ķėĸzš˙āʇūūü†÷ËüēhÂcÕCˇáüĐkLJŅüW5ī}LJ™Ņ|øīt[ųUieīŽĘ‡÷ŽđWT{Oų]+ü€ŋ@×Âëǚžx¯'wōŗˇŽōĄļ˙ÉĪĶ=ËTŨ=NYyŋ9ˆ˛ô_ŽÕŦí˄¤ĩT=ižQž´Eģå‹[eúŧ˙MtåMåã4šGžŋœÖŽtˇŅ*ūĩž_ŠųĒ“é„Á•iŪ657cō™¸ ų|¨~úT”Gi•æ¯ģHUM@| Íe~Ŧ¤‡īįmgŗ.?ŧšˆßÚŪeęûŨ{kų-ßC–ĒjúdC ÕËÉüH]WŠŖĖJžÄ<ũøƒ˙Ų`ÕQķc•ūËKÚ­M7äüa¯3rŨÔCÚŅÕ÷ÔOÍĘ~ÆLē§øãę“/ŋĐ~ųīNOmŲXo˛įōŲĒĮĘSÅĐeîáKį’ŋf'Ė39ŋ>Ļ Ŧą4Dņ˙^É×´)ōc9녀š.?ęČjņĪ iķŖY0=>ĩktÃi^ˏXū+ŒšŠ‹ Ķqß<ŌŦ)÷‚ĢĖ$õ›ļÚ3ÕVŋ=Ų=Ô)4÷Ø­ƒM_ëĸ(`lŋ2Ÿ˜×ŋ"ę§÷ę#_Ԝ;ŊVŊÆŲCš#ŽJ“Ÿæ šYųąMõ¨—sBĶø4íÆŖē›2ˆ2“^ÔŽšŸĢ¯"R~ô J“ĢJĐåGÖÕŖ^~T V^Žd­zKŌ%ÚB”úËkÍéōŖ{LĻúYqŨņWĩV§+~ß%E­Ģ7ÍkųPRõ¤āü(t…ŌSR•R2éáwēi ōƒÛGĘãnOū§ix€zˆîøŨžôHũė¸2‹{iĒå:|œÎq’@~€mæ5?"ôŋrzNš;oUŸâąS{$TYōÕ|÷ĩ*?nSâĨŅ”ĢEũßZ~+˜2‡¨_jÎ˙'?.RŌŠŦúP/?Ôģ#ôōƒ;“ū%WōŪ:î‘[×ĖčyÛĶåG×hÍvĀ"į”ęŋėF eë] ėĩüPŸ˙Qp~|x‘‚Ÿxō|ô¯he˜KˆkG™ ûĘ5;§tųŅî̟uĘTüž˜5_3UąíŪO´vÉÛCq ?Ā6Čķ‰š/ôOû÷UíÉsgǧĘŧÔC†Đ•wÕOÕųQⷂoßõZ~Ėĸ ~3Ũzm~ŒŊÁī‘/š(9ô ~¨ųҟBø_[ՌįG¯ŦŲ\KŨabĘü8ZŠ[@ō6¨ËÚū2õĪeĩų1üŋl‹Î~S;ųųĄŨžĨ€KxæGŸŦfKČ3{âDíMq‘æŗX×BLHMn#åŲ¨&1äØųa>éōCņ֟Ō7)‘_֍Káāžęv•Ŧæ÷r÷V^e¯¯å‡ŠŋķŽĶæĮiÍ>gî^ļú›¯ųųQކ‰^ˇ›ÖōO*ĪN~™›O“u¯ųķĪ/І< ęí?ŋIų' ´Į_Ąę?^ĨÚiLæGš‡Ä_VwĢ&?F¤ĮWTžqė˙éĻ5Ėęq NĶ´§gæk÷a-"õĪÉDĄmMážxYubGYËyū`ä‡ųD͏ČFú/ËųđĢØz^Ú3 6ŌS՗ÕUô\{ēĮlū(ž5ēķŲ^ŗQ}žû4š;?`]Q=~pOû]ø4˙“¤ĸ_ĻzÕônˇŒ'i}æŧŗ'č7Q”øSŅåŅęS ŋõ ÕlšĨh#í8=äÛBîŸëĻŊjxi¯ˆ‡žÄ]ĘŊLšŌED'”%ĶŲO4c5ĸŖÍ´[dļ`ûG`ä‡ųÄʏÚ?õhNqŋwëĄw‘Ų˛kŋtžë§:u›÷˜įw|5'wˆvP1J;ŪšįF™|0—ŋĻ?œĻ“?´äjtŲJ^íUį–TČÉšök÷ÎëËéæUļ*ō#aW¯ûŦôU˙;vh’ŗļk]ŽqĮët°ķkģôEÉŪ°°Ãecčū”n=fŸĘ ¸á=”Õĩo”ŗŽk=ŽŅWép'í {ŅrēĒŦCwjĄ<5âOŠĐA˙’-zųÁ­ ´ƒC~ØK0Ŧ›*ių‘ŧˇo÷ŪKžjN¯ķs6˛õ{ôTˇRæ§ūôb`‡"ī÷l!OœÖžŋFĖQ ŸöcīÍĄDģđ§+–ō įĘų&Åæh~GôęŅŖŨ}ÚÕŠĮĪÚĢJ^§lOnqnÎ#ÍÁЋË#;/öϐēĸC^mJ×?hÂl¯ÍŊ~u}k ?Ā6Č퉔՟åfgådgë_áü§Š!rē\F;ä›ķ$ĪÎĄ—}õĻCŠÉRfĐtąÜl"yÎËn29QvJwåĀ/IϘˆÂr)GŽÚ„u†Ž(ūš­˜ŨUÕųŊeŠ?įĘüĪQNžTØHõŠė ˇ¸Ë]ü]ÉÎĨ¸d’å>RŦ„ŗU3 ü9T5ÃÍ KûSļvM96QņĮ,ųYî˜Lą´äú›Œôķƒ››¤Xr$[ĸč^îõ’œ2ލo.ŨâĪŪk"ËQ.ņ\Å\Õ[ļnÉdËgn#ÅpY.5”&‘Ę*I–Jr™úN_íĖÎHNNĘÎÔü#ėP,7Å$ōŦlY„æ—EĘKÕ׎Ŗ‹šms éž)—Їū•'W=Ōûc>Å"W^–WȰ ōÃ|"åĮ˙ūšw÷Ž›ÛŨģwīÜFâ—ķģZŒŲx‡ĮÅá˙2TiąĮÍeßq9sĪÍÍíŪ‘÷6x(Īđ_ˆß~ÉŨcõO\ķ#÷nĢvĶŲĮ}6÷ļûõEę cĨ¯ē+ęņØĖíUM~Đhų{/eôīųzÖŨmûpîģõwī+BŗôÕ ˇrģU3ԝT?đŽö\IŽūåH&pƒoģššßøY¯5ƒüāž˜yãūße¸…wŨį*_9ôŋBŗnēßXĄ9&úķ]Ē%ŽXäÕ?ܖÜWŧvŋÜfāCåŋÄ Í§šwe7ŨÃíū"MãĨæ]ô8ŠģÛဇwÕMéF)yëčˇ7ŨM{SÂĒķpÃ/zėĐģ ŖBųƒˇësVŲåox 2Ģ!?Ā6ûĘaŪĪö ÷?ˇWV^¤ŪaíÅö+° SĘ÷Ás Č{ĩÍđė}'WtŠ› ķ…üÛ (?曞•Ãŗ˙üčõ÷<ĩųŗîõvŸđ[Ü|Súš҉t§¨ÆÂ4…ü۔(?~eŨ Ø}~ĘŌû9nz|‡Wörō|•žéū˙a]ŠtÚųhaz,ŗ ?ĀFĄ‚ÄĮŗĒŦû!ģĪîtjĸZRŦíw‹ĩmˆ.”Ģņ[Õ3=Žķ(jz3!?Ā&}ë¯$?ÎVéezfŽÎūķÃÕŧŋæ…â͗vßŪ|ĸ@~€ Úģg$D|Č&ɸ;–uwĆü°;˙n3iú˟?e]†ŖB~€ĩĘũ’>÷‹ĸ7ȏîŖÉ/gž~gg‚üį‚üëtŨ—}Cušéļį‡ęæL=oĻĨîčc|ļ ųÎųÖčt=5û`wõ1+wlŽouŗ]VÉ3ūÄŦWbC~€sA~€ÅĒŽ•Ŋœĸ{ũN¤ņ¨ģôø{cƒ˛Ŗ˙vԃą\a]€`Ą–;ŖrŽ^ũ KŦMņ‘i¸ÍjôÅĖ„ŊŊĨė’TŨd]€`‘î%æîėųy†Ž°)?^;Ņ Ë–´,ˇõHBû/ąŽ@HČ0_Õ?ÃrCå÷—:QV§GHž{ĖG†įÆĪŦ#nw$‡ũā\`Ž.ÛBsoū^ĀkŸĩ2>ĪHíĄzR2ųM…_\"/íxCR^(ū_ĖØJßīĪ”ŌĢN› †"?Ā>UžHKYW‘Į_~ôb^ÖUXO ü¨HԚãĶ…wiΤ=$Đn”we4ã–Ķũ2–Mˇ„žrĘ­q3ĩC~ˆļiÅUD—kōæwdzģÕ=õM•7ĐKõĻĨ2΍f´ÁiĄļ”đZIÆķc1Åü+é–'ɑ`š +ĶŪc]ÆëÆ]ˊŪĶ™uÖj˙9Q[Ž;N;õ‡õZâøÖ^=ļ¨LøbՉcĮŽo0]šY§vŽû ũáËWV*^Ŋų†ęMõĄĐ%ŦĢ0jÁ3yĜŧĮR:Áō#¸2ĮÍĸÚŌ˛Üūčß˙¯GD]ՃZ Ø˛jŊĢ1˜Ōũ<ׇhqŗÛžŠ5bš>Ķ+ŽčãŊ'zŅ>õ83‰Öô¸čÅuã”:°įU:Ũk⁠ІÔ°DÕlĩÁÃč!Ÿ;õ‡ N)ūh˙ËäUA5Ŧa /ī3úq.é]ƒ&#úŽČ‘Fœ"sL|WųY5ÅúE;äEdÅī5؛öķÚ |7ēĒ"3úŪĸ]ŪR ķRTĐoĀ/ĮĶéŧz˛érz8§īRelŋĒ36—Nõi§ų1ÃÕ7~žEũˇOĘUķaŸA}ÖeŊę>pāí9ā÷)Dąč†IĶæG¯ŒŒ›û÷S.ru~T û+ô˙ũA S´š˛ëŊ=†H“7 βUė—Ŗø˙AjPĪUēĀöäZäčĢž'‚Žũ^„uό;C‘GÛŗŽÂbBåGDˆb=™tĮU¯ĸUü“tęĻ7^Mũüø$0SyĸžŠqËđëøfDĄMeȓĀ5ģuķ åã.:ĨW6­4˜}_ōU=žO´;2>PŦę<ŋ}Ęmá×pßyŅÕfŠäûĩvĘņm8Ž;ĐoméĪ+_MHųŖæzšîg§"?ĸē(ĢRœæ æ#ƒ”˙s QhOŪQ=YNų!ŸĨĐ^åcéGyōŖÜM:Ĩ0ÂsÕĒûõķkFfĮhŸK Ķ˙Sëlū”’EtC=hÍW=–Č Æüšę ūđžĻņž9˔m^hķŖéĒ5ZëFĒīŅÖ4Všˇå0ØąVûH ŧvūˆY'âŠų:ÍŽ§Étd|JĢyīÍLģÛÍôxvE¨ühĨÜ&ôyŨæú՚C“N'é^Đ@??§ûЧQ„fP"~û…k~Î}ƟęQ)#[ûeœ;@k f?PŸéåĮgD_čŗŒž¨ŸEåčöÔëö6ĮŊŨ§šAsŨûš:IåũŸ•üÖūQˇŠWä‡jßöÁ¤9ÄŦ ŋnî!͜ŅבîōOĒĶūÉ>ēČ÷Ēąa~T¸G‹ ŽRŸ¸iũÖœw3é¯Hח‘†ųŅ6û1˙¤Ēė°zĐvRß&įR‚ú†hķøüRžÎÁ/đ÷ŽŅn~Č(m~äëŋ?+'¨Ū?Īāŋs”Aˇ:Ÿ ¤üĩĘsž“/ģwĸåfPĐŧfĻĮŗĸ]ŋ¤q4Ÿ;ąC…<ƒ ōã÷ė§ĒĮŪŠ™šAm(‰ĸËŽû¸Í –íO#Ũ~p3ķC˙Dˆb×iXĨZJĨ.émgŒæøĢ/Hûë&ãiÅ˙—’öšPnēlrŋÚd(“:„ö‘¯.?rÆôLķķÍŸ˛W†īŽīũé°îUžüāŽRÄĘ?Ô,¤Ō!ŽNΙĐNī`åräĩtbwŨ™°c4+⎴ûĪkū9Ekšņ›9Ąd÷…ƒîA~€J­Atkę§Ŧ˰ČûcÎSԁFŦË0Ÿx×ŋęuBņ54ÆĮm™Á!DųŅ!9´¤ōq­vûŠ"?Ôû×õōcũũWü†÷w´Sš™õôFŠîGΟû)ų&jža I›ēüØIʛVŪÕÍUmûKĸL˙{‡ÕįļLWŸ¸XTīøŨėŨî”˙ .h‡ūËnäώų(o~TØI”āq¤“vP˙3ĘL>ˇiNdįZžL"J žŊEũƒd4i6ˆéŽß=¯ŋû#ĶđˇZž z¨ ?€ãŪns;#íXmģ8ŅÜ"˙nļ.+ãĄrŖˆCķú‰5û/>§Xķ‡é_É ?”;[éŌ#]wčOzÆ?ŅæGoĘqŸĶ­AĮ0‹ķ#T˙\ÅēᔝÃËĘZ`[Īō“_~ Tž\WÜā ģī:ÎŲû”(…ßSÔ))Tũ ­H€6?V“œV?¤ųˤqF‚ŪĢ1DŪŌ{7?8îÛvŗ÷xĨë-S{в ŅDÁmĩC*vYš7ŒH6\õj…¨‡ëōc^zĻVÎĪ ZJЉŒīķŒü€*S}(pUQÖeXkĄ'.ŽÅē ŗˆ–ÍWđë˙weÄë 6ĖŪDņ§ëˇ Ú!¯įĮMēö­ōątĐëųQ§ŗúršü(fúųQâ‰úO’_~”}’ŽˆƒDíA¸ŸÎW¯õk$´S•Øxõæ R9zų‘2‰û)9AŗMŠÄĘ šÆjDę֎ŸoĄŒ…%äɏâsFņOF…ú´˜v+ĒĢŋŪ—ĨŪåTo4sáÅ ÉĒEũ24ŗa|˙Gūpū0VōD0]žhá Ávåí?ÎQÄÁVŦË0ƒhųąvПÖ?\Ö0?.'žŒ'z0ņí×ōŖ0ÅņĀž ÔˏƒüV’ûęãŠķŖbÁųÁíÕ^zë?SE8Ô:ŋüāŽQ5. Dû˛ ųЎæ Qī@÷Öü§ˇ˙\ĩīc*]Uīœī§=ŧ¸ŊĐ6ö€âķŦĒķäGGyˆúĮÍOtˆ˛WÕ׈ߨĩH{ũ“9üß*ų:v."?ĀŅŧõÃĩ´ÔÍY—aŗJģR˛Ũ{ÚũO(Ņōc+‘zƒú9Ōm~įę¨Wôŧ0_ŽTÕ úĮ:ĩ$~—ē"?øC@?Ε5V=i˜ĸˇbØË5”¤ųöۚüUčž63 S ūáW\“šüU¤¸ŋh‹ŅÚÛ$ËĻ!?íÕųQ<€t×–šˇYĒî æŪؚŨâŖÕ;2zP´ęûÍ"Mō­ÎQŌ}LsŒVíÖ:Oíúŋc$šĢNô¨=Wģ=i¸a~ôH'õØhÎĻ9DT’vS}Ų“e”Ĩž"Āõ\ÖŅsՏ”ŲšôĖ讎|!?€ķŨčų&ĮÛ둟7–>“ūՀuƉ™q3ZÖoÚīxn¤zP•F ëöĻāF i6$Lh0QŅÚC(°vŨ7ßŦ[š^õŠŠ –Ŋä=ĒyũŽ3žu­_E=bįÔøą ÚĸGꄨž@6Ē×sGyũPOšˇēTãú­(ŧ}ũ†zģЇeÉ˙QŦŖß%‹ëÉN”kaw‹Ô'/Ũ„{īûļaÔžÎGęÁ…^„îÖŦązeũĶ­~Ãî'dĢK8Cq“›×ŸJD]ę(”úžÆnŲ’Zå9ŽņsšPOu*É\yÎÉŠę7ûˆîŠ¤Ė§S j7lذōMõĩë6Ŧ5+#˛lÃFÚęŠü ?ÖkØm~Œl?č ŅĢiÍ4|F¸Šü „ĩë×ŧYĻš¤–ųOŦÕr]QĀO^–ųė|ģ=œ<Æ rVģđؘ“ôōdUÖe#Z~ė ?ĸȐ°4zŠŪ|5"44$$œd!!ĄŪę-‡QôŖG?ôXŽĘv†ERNpøėЁ”Ļ:íû۔E”MQ!᚛|Ϝ(ŠÖîc^Kœ <:(,|waŽģJ˛0Å õî4.ŒÜīxQfãĩ?$Ją°ģ›Ã^Č)0rņoáa2 ×îĐ9BÁēąúĘĶ(>8$žhîü "‡‰ÂCˆŽ*: ŦõŖĀDŠ ŊÉWßԝ&K=Šė`b”ŧV')+Ī7,NYy’ö€Ÿ„žŌû>ūEØÄOúĨ$=^¤wF‹‹ņIÛÚü÷Žbá( ‹HILHJģŖx’œ˜v•įķūģ|SãŽUŸú͍ĖPuN))JÆU Wą<Ã4ûŨ>žRhŽoJ’įRíW˜)OkļŲ“sa æųA-Ŧ|‘’vqŠŽ¤˙ Ŋžļúã61‰)‘ŪāĻvzã+Á!?\Ņ7ķ<éŲę/Mčx–y‘×R{Ŋ6ËûŸ×Íøöų@h|d°{Ķ–­?žFWüp=Ÿī Ĩ;~gzD‡TlÔMzĩˇ˛é`™ŖHwQĖ&‘wŲB(Ļ<š0y5ëã&‘.ĻđO7dņ'+™ҁ•ßœ&wīũ•éĨÆ2?Ļ‘ŪĻiė A 5ŽĻŠûOŠųáRĘÍēK~˙|Ëē Ņ]ō„ž/ŦbzDiąĖžôH{iĻy9ŲNųáB>ŲI÷G:÷o/ĮŖ—GLíģ•Ëüxį<=]¨üæđŪ—ŗsZŗ+œōÃUë|57ų8ģ;H¯ÔŪrô…é%Ã2?8nyĒúš|Ųw]ém"B~¸†b+ÜČg} Ķ#:•žĐã…öŗ˛d›\Õĩ—{yŨ82˜iāDŽāÍĄôdÂ7ŦË`āĢÁ7)vŸŊôœq~(”¯RÅ9Ž7vųáôžč{”Ī9ëáēĻ}} îw/Íē %öų $䇓û|Ö \iwG"Ięģe÷ÉsĩüŲ‰uBB~8ˇ Ņôā7×NĨâŖ=čåæ[nßf]€ÎĢPŋkĖÎaeį@]ÎöÔüūį‚üpVEæŨ!ß͖ߒÆy}ģô)y-`š û?Āš ?œĶ;›_ŅĶ_ʛŅĨ”ĒøEļ‡Ũmϐā\N¨xīS”xÖ>/!ČÚûĸčîF'"?Āš ?œÎģ =ČeÖeØ­ŠëŌÃåLŽįE~€sA~8›/É{ všķÅŠŨÍ`W:ōœ ōÙŧ?ōŞuļģ ŠĄÄéhēš‰;°ŽÂ…Õ^IŪŗYW`™æËSŗŧ~g]…Ëë蛖ĩŽÅ[ŦË0×ĐCY)×zŗŽÚŠÉ>7šuf蓛ŧąë*@­éėō›Ėē Sš,ɑ…öc]čĩ—Ž{ÖķLjōɑŦĢ€×t>—}z,ë* 0æi–|ukÖU@ž͋!ŋ ŦĢx]ĢĨi2Ÿ?XWF´~’M° ėËĐ#)YgąÛÃŪõŲ“yi(ë*´ēäd¯ÅE_Aƒšq2ŋiŦĢPjĩ"‰<ĮŗŽĖ6čqZæÆÎŦĢ—×įtrÆųQŦĢ‹t<šq'KŊ3“ˇ6d]XŦ䌜 ŦĢWÕvQš“ūÅē Ūøģé ›p4ˆŖÉ•x:5ōMÖe€8úėËHŧü ë*ĀųųÅ'3ac}Öe€ˆžY—é?ū#ÖeCm~™ŋfãĶ6ūũgīĖjąõĒ—ôtžČe{ŸÄ÷Ō{Y¸íˆykÍy+YiãĘ}Ú3ë,äņũÚá)dŽč§Į›l˛ÍŠ8ŲĨ!E%(˜kRXû´éĻGáifŋ•Ŧņ`ae†ũ—3-ũįFŒ)f¤Å¯úgå,¯ YĀNty"#/ō!Ī=]•uo\^ÕŊÖ}€ŊÔbÃÕáōGķū'e'Ā4>%lH—ĩũkÖ=pmåüŦũø&ÍˎÁæW_ŅŅáŸKÛ °Ũü…LsøgŨgWöŖ-ßžÖÜWÃ}˛Ŗw}à#ĀÚ´DÁrÁl‘?ŗî5€ëę™dĶĮw›akmÖøŅŖŲØgî’  –‰mËēߎĒD„mŸ^ųtŊÆjM Ģƒ‘ŽŠ•o%kąî8€Ģzlë§7ĩ¯ēĨrƒžRüšJL{ė|˙\ˆ0°Æ-Ö]pM l˙ôzĒĒą)„-ü€qw€åļŋ•Ŧ”1Žuß\Q)O>ž]9ŽîŠē„#Ž\šÅg įAaĶå€ĀV ņéõãîSâ6Ö]ļņV˛ÖHÖŊp=xņáMîžmNãrqŸF ņV˛Ö]ÖŨp=Ĩ„ųôneŨ`îįaŪKÖÉú˜u˙\ΟÂ|zīáׇË;$Ė[ÉJōąŦûārúЇ5aŨ`ėŗ›Âŧ•Ŧuõp9đ•õëŽcß ˛'ÍzØ ĩd>ŊŖYwküB ˇ’•âZĪŌ*jū ŖŒû‹uO€ąFŒŽ]ĸņđŋŦ—€‹ųD¨ü˜Éē'ĀōĀÅ ?@ Čƒü ?\ ō‚üp1ČōĀÅ ?@ Čƒü ?\ ō‚üp1ČōĀÅ ?@ Čƒü ?\ ō‚üp1ČōĀÅ ?@ Čƒü ?\ ō‚üp1ČōĀÅÁũAČW#ÔũĪĮ°î0Ö0\ ˇ’•žÅz ¸šHa>ŧšŊXwĢâ#Ė[ÉZ÷X/—sU˜oTkÖÆžž+Ė[ÉZČŠÍæÃëņ ëŽkû…y+YI>‘u˙\N=a>ŊģY÷˜—&Ė{É:˛rŦûār>öâێŨįPņĨo%k˛î>€ Z#ćןu/Ā\â­d­ÉŦ{₇ đáũu/ĀÔād­Č2Ŧ{āŠ6Ûūá}Åē`ŽŲūV˛Ö2Ö}pMĄļ~vsûąî؅:6ŋ•ŦuûÖ}pMcmûėĘ׹î؉1YÂāĨâŋgŨsW5+×Ļ/N܍y‚…Fąî7€ëšjÃU°äˇXWväŸláRÁ\ X÷Ā•zeõ‡w{1ÖŃ=ųSōŗÃ{˛î3€kkũĖēĪnÆŦ+;ĶCˆÂ-đ—^`müŗ‹?ē/va]6؟™ļíPŗ@V@7ÖŊŽ+2õH %ŨÔ [°ŽėR™yĮĸÄJ }ûņ퀭_ī|­~VųįI›¯¸Ũ1íîÎ%ƒ›~ žjęÉÂŦJ{Uģ×[ޚņNŌēękÉčn§ļŒč]‰u/\ŨR:_ŲĻ–ĶŖōÕŽ+`ë ĀBĮåmmâ÷tĪ6B”Ž,đë Ā"^ĸ ļˇō}ÆËŸmo\ōĀą´ H-D;ß>¤áB´Ž ųāPēÆĮ téÃJ§h0-‹B~8’á)~ kl/Ŧ-pAȲŽn ŲÜųíĒBļŽųā8öÉN {Ķ„‘éĩm\ ōĀQTpŖyBˇŲ="W“+!?D­ ´?…oõû{ōi¡ .ųāz%ˆrĪŲ/īæp: ¸"ä€C˜”+օs÷ČáVÔ`ä€#˜M׋‹Öøš{uŅį…üpģißg"6?%5¸ŦˆÍƒ“B~ØŊâ7čoqįĐ,:f°¸s'„ü°w }˛Į‹=šŪYSĞ8䀝kđ*ĩģøsŠp]ļBüš€SA~Øˇaô¤–$3ÚMg?•dFā,vm Ģ"ŲŦŽU”hVāöė8ũˇd3—ë…ĢaųöĢÂĩÜURίv\RO)įŽ ų`ˇųĻN’vŽõЍ8 ä€Ŋj“&ųfŋžM‹Ĩž'8*䀝ú3Ũ˙cŗŨJ‡K1˜-8 ä€}ú›î}Íbž˙7“Ü*ŗ˜18ä€]ÚF'Y1)5ŠŅœÁą ?ėPÅû´€ŨÜ;ʇ˛›;8 ä€ũŠū8‹éMk>͞Érūāv§[Dęl+¨v‡Öą­ōĀیK +Įēnūœu `įvfĄüJUÖ5(Ŧ•=Á]mÁ(ä€}ŲG§Y—ĀŸéߌu `אö¤ęYû9˙ģvRôXÖ5€=C~ؑščWÖ5č´z–;†u `ĮöŖ\JLÖ5č+qE.éõÁą ?ėÆz\Ÿu yÆM Ą@Č{ąˆîØßũ˙Ō•ŌŦk;…ü°ûéhQÖ5äcTfD#Ö5€}B~؅oŽåŽa]CūēFF÷g]Ø%ä€=(˜6u iā›=•u `vāĮ¨˜~Ŧk(ا×i-ëĀ!?؛ņëŒÚBŋc]ØäsĐãŦk0áoēũëĀŪ ?{w9ĀĮpD܋jŦk;ƒü`ĢÔ ZĪēs´ OÉē°/ČĻĘŪĨ‰Ŧk0O ŸÜaŦkģ‚ü`ŠkbRkÖ5˜ĢČYÚĘē°'ȆF'øˇe]ƒŽec]Øä;ķČ­2ë,˛œÜ>`]Ø ä3ûčë,Õ?#üÖ5€Ŋ@~0Rå ­`]ƒå:ŋ|5˜u `'l~NSX×`úŠ#X×öųĀD§”Č.Ŧk°ŌMÚČē° Č&fø7a]ƒĩŪÜI{žd]ØäãčA-Ö5XīŨItņ ÖE{ČÉ>N{X×`›iIUY×Ė!?$į&ÛÁē[õ´ã–€4ûęiÖ_Ŧk°]5ųoŦkƐŌęߙu B(r–Vü‹uĀō@Rãâƒ悉&l§ãīŗŽXB~HiAöŗōŦkĖēÁē` ų ĄŖt–u Bú%.ĖY~L’)u‚6°ŽAXŨbúŗŽ˜A~HĨē; b]ƒĐ=Ëü…u Ā ō@"íSã{˛ŽA—hë€ä€4F'8į3Žgg]°üÄ|zđëD2/×Ŋë€ä€ mĸCŗ.B4cĶB›ŗŽ@~ˆīƒË´šu bę6u =ä€čJæÎb]ƒ¸žz‘8™u 9䀨zF‡8˙ĩj¯ŌÖ%€Ô"žܐu â+ļ‹öŋĮēō@\ĶrŨʰŽAŅ­/XגB~ˆé?‡éë¤ōkF@cÖ5€”bēJ[X— Žņ¯ÚŗŽ$„üOCŋ´ĄŦkŌ÷i8ë@:ČŅ´ŽëÅēiU8I‹ßd]Hų`ŽÚŋŦŋņø‘ŧM¨§™¨ÍÎ,kfâđVíĶžķΛĩČ<ˇO˙‰aÅ`5ä€)…:m‰"KŧÜŌãÖE3V{Ē—E‹,įė¯ßąŽ,…ü0á‡k2‹V…*g‡°.›Ĩš;, \•§3X— B~ˇ?ÖōUĄBŠËļûē‰!V-˛Ŧ’Ŧ+‹ ?ŒŠãiÕĒPéyKÖÅŗņÆ.ĢYÚÖŃ%F´ˇî›4/˛ëōY(w†E&[Īē|°ō `ulXŊtÖ;FŗÛĻE&Īē~0ō @zÛ´.$ tŊcŠÖظČŌFąî˜ ųP ÛžJ+íeŨŠĩˇy‘…TdŨ0ō  ­ãm^Ękŗî„ÄØŧČh!ë>€šøß Ûׅôu/¤53[€eVŒu/ĀLČ€ôÍ`]˜Ķ‰u7¤ôé-ígŨ 0ō ë…XŌiÖŨŌ‚,2ŋŦûæA~ P•Ą×7Ŧû!Ą•‚,˛ÔŅŦûæA~䯌 ëBJqĨ aų ŗĖļŗî˜ųŋáÂŦ iëŽHH Evĩ0뎀Yų[ ĐĘЅŽČ!ĐO6ē˙%란YųÛ*ĐĘđ(ëŽH§‡@‹,¤란YųÛ"ĐĘđ"ëŽHg°@‹,¸<란Yų*?.°îˆt ´Čų?ä‡Å.ų?ä‡Å.ų?ä‡Å.ų?ä‡Å.ų?ä‡Å.ų?ä‡Å.ų?ä‡Å.&đ0ë ėōÃbČķd+ë ėōÃbČƒßųC~X ųáb°˙ Č‹!?\ ō Č‹!?\ ō Č‹!?\ ō Č‹!?\ ō Č‹!?\ ō Č‹!?\ ō BåĮĨąî‰dp˙AƒüČßVV†ûYwD:ZdĄY÷Ė‚üČß<V†kYwD:_ ´Čî•`Ũ0 ō ZNgŨ ɅYd— ąî˜ųŋw„YÆô`Ũ Ũf™­cŨ0ō ž‚Ŧ ēŌwiaļųÅõaŨ0ō  ˛2ÜÁēRjš!Ä"ķ+Ęē`ä@:% °.ĖŠËēR*vS€EF˙°î˜ ųP#Ŧ ]ė C’m_dŦ;æB~¤z´ÍëÂĖrŦ;!ąkļįĮÖ}s!? ´ÔÖUĄ|%ë.H­Fš­ËĖûkÖ}s!? vÆÆuáõˇX÷@rÚ¸Č^ļeŨ0ō `ŸŪ˛i]čËē~–Ø´ČrÚŗŽˇü0ĸŲ օžuX—ĪÄjYėlÖՃÆ|tŌęuáų/YĪȔTkYDÖĩƒ%Æ- ļjUŧŠuáėtž%ŗf‘%_b]8Xų`BM–ŸÎZ[ƒuŲLMfy|Äēj°ō¤ōC.ĻfÉäšššrã”cädežU…uÉŦ}ÖjGdVNŽée–Ģü/;+❖ŦK‹!?ĖŌö×ŋÜ÷™ˇĀ˜9Īũg-ځuŠöâûĶæK[5ߨ"›{"zåüYŨ\í4K'ü0WÃÜm&ÆXŸŅFŠBȃđÂÆGh@¤Š„‡ü0—W@5SŖŧt˙\ŠJÆ,gj”ÉŨĨ¨D€ü0Ķ_ôģÉqŅ *qĨ“.˜įːGTb@~˜§BĐĶ#Ŋu=SüJĮAĒez¤éÔKüJ@ Čķ몊fŒÕ"q—č•8ŒĄŲËÍÍ=žˆØ•€(f霺ŅŦņËŠ\‰ãđ (nÎh52ļ‹] ˆų`Ī03ĪŒŋú?q+qŋŌŸæ¸5§ŗ¸•€8昚;ŅĖ1c:¯RÚ 3Įü.øŽ¨•€Hf¨ķԌįŧˇĪ&ˆY‰ã8š^ÕÜQ˙ ĄbV"A~˜a+ĩ0{Ü֙EŦÄaô õæėlâ4C°GČ͚Ķ Æ^’R9ÎĮ߂ë×AČ\„ü0íA˜É3ĪõÅē"V%c:M°dô­ÄĒDƒü0i2Mŗhü 4V¤JF™W–í¯yS¤J@<ČSY¸.专$J!ä¤ŦeŒGæ:ä€)ÛršZ8E‹ŒŨĸTâ0úæZ°ķœw+˛„•€ˆ&T§ŊOŗ>ÃĩĪˆķ ąøŽeÉŧüÁ~ ?Lđyūå…?ūTøJÆ\mųDĢĶp!wƒü0î/oÅTŋZ5•“¨uՊŠ*{ ^ ˆ ų`TEOĢĻģ“*p!d}mÍdŗ¨ĢЕ€¨FĸŽV‚Ëž7DļÔē īÆ+l% .ä€1MMŪķŧ +eũŦÑú[ŧķœWEū°•€¸Æ<Œ¨lå”īŊt{_ĐRÅ(šbí¤Ûr[ Y ˆ ų`Ä,k$RFķŦÄaÔIšnõ´‚<ŦĆü(Ø÷áOŦŸøíË VíFvpûɆĶh°p•€ØۙŨÁ†ŠëgŦ‡ŅՒËļŋî^pĄ*Ņ!? T‹6Ø4ũr™ëŨ鹟g[ę|E.~凂ü(ĐŨčJ6M˙^ä“ TŠŖø“&ÛÖĀΜfÂTâC~d&ũjc hĻ…8Žē¯ĖŊįyAĒߤō u^Ú~0ĐųT nÂįQ[›ø& Q HųP€MY]lnŖžÜĨÎBīB[loÄ#đ+Û) ?ōWv ĐĘĒlW: õųķŠļ7RQˆ) ?ōw?ØĻ‰Ô> |&@+bŽ0÷ܘÖVˆf@tČ€|MŖQ‚´3™Æ ŌŽ¨qIvj„=¤ō ?5‚…Z‡ŨLâwŒ#8,+.LCĘFĶˆ ųŸšBmCŠžz\ –ė\/ŲjĄšē)Ā~ō Ui`m­“÷Ŧ-{öÜ_°K4É]+TS "ä@>…—Ŧ­/ ֖›žõ‹pm ĻÂ5bA~ŧn °ĩq4CĀÖėTƒØ‹ļV-ĖMĀÖ@$Č€×” ķ´Ŋ[QÎŋ }Õ˛šér!āüxÍÁôŽ‚ļ÷uÎ AÛŗC?ĐaŧfŨ}įABČ€ŧjĐv[\įô÷B÷ōø'VƒÜMÂ6ÂC~äu;Ĩ”Ā-~æ%p‹vfMēÉŊTAč&A`Č€<æŅÁÛü•f ŪĻisEđ6ËûßŧMōĀPE.ÛūēĢ1åDhÕ^ėM/+|Ŗŋ˧ß( ų`huvZ-/ā ‰v§1­ŖYo×ēyŠãA~h&øÎsŪb'žēŋĪ7b4[…vˆŅ,ų īˇĄwžķžķ÷Ĩ];0O.ŌåˇfÔ§aō@ß$›īy^é‚žĶnGĒ_Šåę/ÄØ‚A~čŠüBŧëf\Oô mģq b늋ˆ­ŗâ(ÚĪE8Éֈ×8Ø ų ķđ…˜­—öķŗy6æg÷ŗųuT]ĖæÁ&Č­)ô›¨íOĻÉĸļĪ@Ŋ—7DmŋęĢ›ĸļļ@~h|)öá>ˇ_ÖyRۓ-ōž&æŠu@Ø ų q ]ėũ˙Ļ#"ĪAbÍiĄØŗ¸­9ä€ZƒĖŊĸĪc‹üGŅį!ĨGĪŋ{ŗW‹= °ō@íNę[ĸĪŖ’˙3Ņį!Ą9ō‘âĪd•&` äo —`.céBîŖÅ:ķ\_™ ėBˇSČ•ĸŨ%™Ī­ČʒĖG ;RJ1›_ rˇOČ• b\ļũuŸ“˜§(JĒI´gâÉ3n/ļC~(u”ėūĢr¤ *ņyų‰rŲö×ĩ ­ŌĖ,ƒüPøđz\1‰fU9đšDsŲ(ÕŦvËž’jV`ä€Â4F˛yM’;ÅYčuB/K6¯o"îK6/0ō€ã>rûˇtsģW_癉æXš„ŊN“¤›˜ ųĀq›ŗûJ8ˇ´[š‰¤m“rv>~"^åŦ„üāēdî’t~ĢŠ“¤ķƒ˙RÎŽSÆ>)gfA~ŧ;VÚV ô‘v†Â›+īF[ųÚJÎsیĶ@~ŒŖ?$žãŸ4Uâ9 Ŧj¤4g[ę”Iŧ-ņÁ$ä¸ŧãŽŋ+õuāŗĐ’ôŋØv’3œ7ãLāâĻĘg2ßßäķ˜ĖW5Ŗo1™ī7áw%ßQÆ ?ĀÅ%ēf3ãÁ•ØĖØf›SÚŗ™ņrŠ+ŋ8ä¸ļ-Ŧn([ŠŽ2šŗšąØyÎsĀ.t{‚ü—Ö5‹ŨqPd™ÍÛŪav1Ün9Npå'‚ü—v.‰Ũŧ?ösČŗĐ§Q[v3ߑũģ™C^Čp\ FŦ?˙ÄÛË‘OŒüÕûÉšu#XY]­_ū9ęclŪÃÂ=m*ūéá-ŋÕ|ËĸĸĘ˙¸č›m‹Ė/ꑱ?{_?2ĄŨ—Ö-˛/ûĖŨįaŦ:Χ1FŠIŪwvNé\Äēęā5ČpP˙žs:”¤rj†åM:į'IuĪ/-0ŋ¨îeKQT‚Ûz+n8Ûķ [ŽÕĨ^ßë¨G.Øä8Ļ^OeRŦkT˛}ÚXX]ƒ'™’U—ëinuû’%+ŠbįZ¸Č>ۗ"]u/–[Xä ųލę)éÖ5*'ĘYP]Ņ WwΌ{cŧ3Oâĸ"-ēĨĘ_f›R´ŗÜ„ž)ä8 †ŌlŌ÷´ŽŲÕ5ģ.yuLīŌ>,Ũī5ĩ8 NÖØ uq”ū—ųÕAāxÚŋ”|mCaîĩ3š3¨.žģ‰Ēn3(*ķo3Ųģ'T'_ifuP ä8œ,âC ÍĒîĶWLĒË4ēKøŨLŠĘ1ķ;ū&ÕŅtķǃ!?Āáø°YÛĐcsŠ{ķ"ŖęîU0RÕ*9›ĸâĖēĐÉl6ÅQĘŗŪoP ä8šŨŒÖ6Dæ\đPú ųÛ .Ē2ŗĸ‚ĖXd=ãXUįiÉQđ:ä8˜ÆYŦÖ6”az Vc6Û֔2 ž Ģ_l KL˙‹žcWŨ&KŪyđä8FÛĘUö™ŦnÃęŽTÔ8éNFyMH=S‹Ŧ§$§4æ/ĶÂ7B~€cų9Ũچ^™ÜœĪ°8ĸēų×TøËĸšZdî,Ģ[eų;tāX–˛\ۘÜ2iuķ/ę–‘Kw>4žČŪ`´kŸf՛ԐāXXŪÄ;oĸē;LĢ+ā1ļĄF&Ž„5X’k^$ļ“UīBā!?ĀĄeš˛Q0^]ÍpĻÅŊėoU ÷O+2žĖn˛­nĢ•oDPB~€CiÍvmC•V÷c Ķâ2&ä[U4ĶĸhŗŅEöūļÕļú­Čp0ŋą]ÛĐ/FĢ›ĘîØb•eųVÅļ&ējt‘Õđg[ŨĪŦ~/ōËLļkšb´ēŒĢے_Q_3.*Ņč"kÂļēûßZ˙fä8”l×6ô§Ņęūa\ŨŽüŠú†qQ)FYŖ`ļÕy|cũ›āPf°]ÛØy~ė˝(Öų‘lt‘!?ōĘ ļkä‡åÎ ųeÛĩ ōÃrČį…ü‡2ƒíÚųa9ä‡ķB~€C™Ávmƒü°ōÃy!?ĀĄĖ`ģļA~XųáŧāPf°]Û ?,‡üp^Čp(3ØŽm–C~8/ŋũŦ+°Ā ļkä‡åÎË'ßw€šÁvmƒü°ōÃyâō“āHf°]Û ?,‡üp^Ø˙eÛĩ ōÃrČį…ü‡2ƒíÚųa9ä‡ķB~€C™Ávmƒü°ōÃy!?ĀĄĖ`ģļA~XųáŧāPf°]Û ?,‡üp^Čp(3ØŽm–C~8/ä8”l×6&ōcãęōŊ˙āwŒ‹J5ēȘįî_kä8”l×6&ōcœmukķ­ŠmMôĐč"c}˙sä‡-āPÚŗ]ÛPsŖÕ Nd[Ũü|Ģb[0ēČžôf[ŨQĢߊ€üS‰íچJ­ŽÕKĻÅÅü”oU^L‹ĸ9Æ˙E°­î •oDPB~€Cy+ŠéÚÆĮDy~LĢ Ē”oQŒ÷Ę´4žČļ1-.uŒuīCPA~€caģ2Ühĸē-LĢ;ŸQ}˜î•ņ+f|‘UÍeY]dĢŪ…ĀC~€c™Ārm#nĸēĒ,ĢŖ^ųUũ)Ëĸv›úcYŨM+Ū‚ …üĮRæ Ãĩ̓ŌĻĘ `X]RAEídXTbS‹lÃ捪…o?0€ü3“ŨÖŲ&ĢëÃpkĖ´‚ŠĒĖp§Ņ9“‹ė+†GđŪˇėŊy ?ĀŅ<ļįĩÍ'7™UįųeU-aV50ŊĖæä°*.Ŋ‡ī;xōM VĢ›Ŧ†fTבÕwũ¤FĒ fT­1į_ô:Ģęp÷U!?ĀáĖĖf˛˛ÉúŨŦę~a´k˛ąĸꅲ)ę„Y‹ŦŖęŽ™U ųįßë™ŦmÖüĮŧō63ŠÎøYŪ\?&?Ú|˚ˇČēŊbQ] .]b+ä8 u Ö6ķĖŽnQĒäÅeo1UTĢdɋ"ˇwĖ]d]ƒ¤¯înuŗ˙EĄČpD“¤žĐTü8 Ē-quIfT×čžÄEåæ{5ā|â)qutÔėpƒ!?Ā!uģ"éĘæâ˙ˇwįÁU•gĮo[ĨՂƒÕŦ3RŦVa*ĩ@HmÅ瀞D,`Š R—ĸˆˆ,ŠØŠĨŠ8j­ula¨ZAdTöĨ5DJ ‚ !lėN“̞y_.Ī{Îû8ī÷ķwfō;/gō%wKŽh]ˇiĒëfŪ8x¨ÖcU_ų\|Į)’#ûņø ÍuëG‰ūA‘ũĀq*÷ŖŸ‡{Ģ—v¯k_P­ŗn_Íę.G;ę§˙Úĸôԃk†5’ŲI˙ŦŪŽŗnkõŅ?‰#Ą8n]2nÁJׯ–-]9˙‰ĢumĮÍų¨ÄqCJVÎ×I2ęÜo._ģĶí¨ĒÂ&õˇ:˛æũg|îøyšōâŧ7{[­Ã×ŅĮuč>`Č}GoĐĶÛĮ|ũ?voßĐ~ŪĪēŨ)YwīŖ5OKÖŨwO×öâ˙å§Z_ŪOō=îģŖrę@É×ę“s–ũ‘ĩŧæ†ûßlđˆÕ‹Dëîīq•ųm–ĸđI¯šžqO8‚K*2˙xm÷‚#hœ7%î >ŖđIß­GõTsL.¯č÷„ ʞŒ{Á4Ë;î >Ŗđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0Ąđ ũŖ0ĄđIŸ­Ŋâžp—Vô‰{Becã^pMM{‚ĪŠ&ÅŊˆF“6]sŗč2˛ė.Yŋ¨UcëNūÅU×fųÆ]”f]wí•mŨ¨“Zü&ۨ܎ë_î”íkēˇ˙QtŖ¸đ×ŲÖuīũá{ŗ­ģļCs뒮e‡Ŧ§uáÔnŨŗ|Qˇœķãž ‹ķGžŗpyIi6åUåYŋfÃŌųĶüI¤ëÎ|āũš+Š7eûÎ›Ē˛IņŠī˙Še$ĢúMšģxmÖī¸ąrKÖ#Û¸2oæøk"ĩ_Ή3—e]WZY‘õK6æÍ|Šc´ë’.÷õY…ŲOŋ*ûŊQR¸xƋWÄ}=€ŲuųUģƒ(íŽĘˍlŨ5s*Ŗ]ˇ§jiŋ°Ŗ.šPą=ŌQûjЇ5‰â¸ę5}ĩtk¤ë‚ę’ŅQKŧF/TÖD{ú5ÅãNŠûĒ€ŒŽĪöf˙R^HÖõyßÉēw†•ķ].F­ģ?’×Ú=ŋĶÅēōŋžÅē¤ûåŗNNŋėĪgĮ}eĀ׍¯rqģ׊ÁēąÕŽÖí|Å~ÔÍEŽFīļd]ÖēZ—×&üē¤ëēĘÕéᎊûڀÃMwuģ× ũÚĐo8\7û<ËU#÷šUúš™{6ģ[ˇæX|Û1eDšģĶßÍoô@TZ-rwģ×ųīĄÖ}k‰Ķu…vĮ<ëtÔŽCYęÉŊ.×íŧ;Üē¤íōđƒŠÛâž>ā`o9Ŋ߃āPë^wŧn†Í¨!Nžú8`ũÅaŽėˇ7ĒWūĢ0ë’îö=nO‹í¯Ė€ΏŊŨë<bŨĮ? ƒ`‚|ÔuŽķĢCYįR×ë "x†&Š:æúô‹Nû¯tv}ģךÔz]WO°ķņĒOœ ūf}dÍÜŧ”îŧeÚäÛē?ũÉq_$đ•9îī÷`–õē‰ ë ¤Ŗūĸ0ĒÔúŦ۝˙Æĩmm×%Ũmî?¨ē:îĢž0ÔÉ ÕScûF _Ž‚ā&Ų¨öĢ5FYĒ^ąÆ:>qŅ@åôųŧ2ŋĢqŋĶÚ͛­˛îcŲ(_?‚ ÜōĶBz¨Ŧۑcˇ.éFÔĒ˙wâžN ^;Wo<ÔFģ×đ6Øĸ˛ŽŧŊhÕd•QÁ@Ģ#KMSˇoŒŨē¤{[åôƒÁq_'P¯ŋÎũØŊĄaĀ•qĩŖ$ŖÎŨĻ2*xĪęȚŽ×Y7Īj]ŌĩųXįôWÅ}Ą@ŊëÜīÁDĢuŽß™ō•i’Q×) ŦŽė÷•:ã>ģŌj^ túÆÍJBŊ?ˆˆÂ+QĶ–Y­sķą‰_—'ų¨ų‡•F'Ø™û÷Ë|á˜ü3]ą{HįđƒÍ|Š ŽJ?m‚Ũ6ãZ(­[ÖZ°ęĨQAg›3{MkŨƒ6ë’î)ĨÃß1,î+RІZũØcŗŽÍrĨu; Vé> /CIDToGIDMap /Identity /FontDescriptor 8 0 R /Subtype /CIDFontType2 /Type /Font /W [ 0 [ 750 0 0 277.832 ] 11 12 333.0078 15 17 277.832 21 25 556.1523 36 [ 666.9922 ] 37 53 722.168 54 [ 666.9922 610.8398 ] 65 [ 469.2383 0 0 556.1523 556.1523 500 556.1523 556.1523 277.832 556.1523 556.1523 222.168 0 500 222.168 833.0078 ] 81 84 556.1523 85 [ 333.0078 500 277.832 556.1523 500 0 0 500 ] ] >> endobj 7 0 obj << /Filter /FlateDecode /Length 319 >> stream xœ]’Ënƒ0E÷|…—é"Ė#‰„šH,úPi?ė!ĩTŒeœ_3CŠ–@:ž{=aU?×Z9žÛQ4āX¯´´07+€upU:ˆ9“J¸•đ-†ÖĄ77ķä`¨u?EÁXøáĢ“ŗ3ÛåØÁSžY Vé+Û|UįæfĖ  ‹‚˛dzŌKk^ÛXˆļm-}]šyë=Åįl€qä˜ŌˆQÂdZļÕWŠČ¯’ŋĘ´üWߓĢëÅwkQxuŖé‚Ä+¤8&:eH Gâ)RåD RJžŒ|Y…´;`žĩsū—ãû„˛ˆÔ|O=÷HņZg¸™ŦAvԅō¤k˛į+=“„ž1?Q˛gt掯ą(ČrcËdīã7kũ$pü8‚åō•†ûbFŗ¸–į¤Î¤Tendstream endobj 8 0 obj << /Ascent 905.2734 /CapHeight 715.8203 /Descent -211.9141 /Flags 4 /FontBBox [ -664.5508 -324.707 2028.3203 1037.1094 ] /FontFile2 9 0 R /FontName /ArialMT /ItalicAngle 0 /StemV 87.8906 /Type /FontDescriptor >> endobj 9 0 obj << /Filter /FlateDecode /Length1 38652 /Length 19660 >> stream xœėŊ{\”Õö?žö~öķĖ}æ™a˜``†PPÄHFĩŧ_“īˆ×ŌēWŧdu˛´‹—ŦL+GÄBŗ#•Ũ,Ķ.Ö)M­ôt%­cv a~k?3 ZŸsÎ÷ķũũūųŊäņŊ×Ú÷Ŋ×^{íĩŸač  H/V2sbí€{"˙ąŽtîl÷†™Ė运Ō'˜8m^Ō3ŋ´› &N]0!ķ1y"Āđ’`RYÉø#9—×c‹]&a‚ešy'ō—‰“Ļ͞d[Ĩ€t°n™:Ŗ´åöđ#ŦÛ§•ĖŸŠyO˙æcāž^2­ė21ā.l?Æ0sVŲĖīŸĢø #æk›€ž˙Ã]ËĪmkĘũEŖūŗí̤N÷Ũ\ķÖīģ›&ĘŨÔũ1ĒÁōD)€ĄĒ{ķ@č%Ãīģ›3ånáôÖ1™§ˆÉäA)ˆ@A†ŽĐ€éą_¨ŽBsZ ŸĀŦ"ˇ¨baž8F‘0šî„…B,øŲŗ0 ËîÄx¤x],?q‘‹‰ˆ§ @” †ņ8–ŨĪëb3y; ­„ŅjĖG›°ŋ â›0ņ8ōÛØW°Cʁiߎõ1€l^ëlvÂØū(æ—bÚãHGa|+ōc°^z˜×¨ÖB§ ĶÛa;ĢÃķM^.Ŧ2øÎĨÛŧąûŒ´7ĸ–‰@Úą‚ŧ +ɛÁm˜îÁūWđtD~˜öÅv–a~ÖKÄø=ČGã8$¤&D<"™> 9Ô ‘vÄųßš7âM˜ÄįÜ:'xLDhŒũÚû|áĄ9ÁķH5mÆv=™P…t "1„…iŦ?”×Fņ<¨™\N§7ŗņ0ãĮ9LŦ…M<Ž  2ØÄ…-Â%čŠywJpãQŪ—Ą#ũÚK^XŒú•í/A<Žm~ŖčÃxŽũw@šÉÎ+:´ąûēĐ"'.Œ/ÁuŠ}]á;ëCôÁuŠBLåãÁū;r™ķu'#›s°ė9,3†Ķí pî\'y^Ûō†õpÛU Û°ĖZ”ëY¤ ÉĮĐEĪÂĀŧ7°(„„ˆEt@œGlCLAtCŧˆHÆžû}EáēŠč憸&ĘĮĻčlh+ëÚ3[Ãmņ~âĨgaJņŧMž_¸ÎâXö´´Í÷יĒč÷Ž÷ä'>OŽS­÷ûúđ1({uĢ…ō}‡cæûa+‘nB=ž‡ë,_ åráēĻČ÷D˜æļ™kē˛G ž°ŽßĶB[dŅJ'ÁvlŗX‡6e ôeŗĄ¯pŒc!_hÄtLÃų`Ųũ†Ēë!×rÆ7^GæP “Åzœį.”į x eZÁNĐv‚ˆâŽāˇ"ˇÅ]ôn…˙Ŋ¤>”Į)GÛŧ˙Ķô˙ čĮâ.´™ģ‚߉'‚AœĪũ|O¨ž'éw ÅôD"EJVO!uĒ Kxļ!f0?tũÍęq}"ŅÎã^ĀôâpHX ĢØ‰ā§¤ Ēč Xފ„ēmöE?†{8xûHgļŅŖktîz]jĄ-úz=å6?ŦS.¤îŋ÷Â8ÆeÄ/¨GOPŲÜ>+įÚhÄōžoÕΎáI¤Ģ[ôķ:=r~ę¯×ËëŠrļ }oŲ§8ŽU-ķįö‘Û8n#šãvĻĨüõ´Mũjēõ˜ÛáŖ0:ŧ¯¸ĮøexīŖÆõž-”zŸ–jƒ;Kp‡”üßbđiœ÷üÖ3uT°9|žļk9KCé k9GÅL˜ļgÛ{ķ3üU9GG*ãĶHģaਈëŽ6Pī–đDy⸧°b”ų&XƒķˆVā~ÄtÄ.e-ü\āgĸđ ʙŸEkáá$ú ŧn&˜•ķ"nÃąŋ­¤á™Ę)OoƒmŌ÷ÁF ­­‡ņ|­ø<øxøÚĢį€A‰vâtbĪ`™HĐbš-Š üđ´ĸŧîô‹PĒRPĄÎÄ2ŧŊ­J?XÂōØŽČBŠž×/. lSŠ„ĄŠ?ņ=lGĀm¸‡ļĒĒ`Ģ4÷\$ėĀ6žÄz#øX°^´r^?ˇãūZ‰ļi%ÚPôt°QØ…ķ™v!TĄŒvCŦBNQæžĪB6vß?ÂNđq‘D;Ėũ‰ĄšĨB4ÖbÚZí$öģĶ–âūMĮŊģ ëģÂv°īU˜Îëæq_†û|ŋ¨ü!U)~(cā~ ö/| […[a%ęqõƒ(‡eĐĪ ‚ē‡č‚ŋ;Œ5!(irˆ’xA†EJz&|@w :Ô[~†îgK œ„ ĄD13´gīã^ũ L0–GXŦáqÉBį_‹ž%O?ƒy:ũãÃh–‹õWÂt6*…=¨{–MĀĩÆzâ:ԓDŦ˙3ļų F #qo-GūˇāŗŧœŌGmđ6ÖÚ+õÚ@k Ž3í‡rģ×ĮËųkƋcmgË˙d|ĘŋręĘņ+ī\9{åƒ+ĮxÉ+¯<|åŲ+[ņyāĘâ+K¯üåJų•L€¯Šž<zĢvâÁ/n?ģôėo_ė8;c/"ĐŽž­>ģč‹9g&ŸYpöĀWigםŲqfÃé §ˇ^ pú)^÷ŒũtÅi´Ė§ĶOûOgžN<ÕûTÁŠÜS9§ēœĘ<•~ĒŨŠ„S1§Ŧ§ČÉO~ō›“įO~Ék|ã䡓;‰Ŋœ|ũä“'wŸ,8Ųķd“‰'NƟŒ‹Žū=ú ųočéũMõ”ę1ÕŖĒGT›TUĢŪV=§ÚĒڌįסRwo§B)ßģ¤ËĩŸSĐ„pMüĸ`k‰ ãáßüŅŌüyÎ:Äãč dCY1ŌqmsŲˆ !üO?l0Ž üwã¸ŽĻ%ˇō‰˙ļ¤öĖéMT€'`),î€ đXë`5<ĪĀvtĒQŦ÷Āp~‚ĩđŦ„Wá\€Įa'ü~†K° ž…ˇā xÆA)Ŧ‡ņpĘāMxۃwā]8 _ÃxŽÁqx&p|‡¨ĢßÂ÷° &C9Li¨ŊĶa Ė€ ˜ ŗ æĀlÔéyđ ĖGí^wÁ"Ôķa+,†ģĄ –Āwđė'ČC„0"B#\!“dyš ™HDEÔ$’ĮČãd3Úĸ­DC´DGôdy.ïd;y’ ’ĐCH"ɤIspžœ€á | ŸÁI8 ŸĀįäšH~ÂŗęgōOr‰\&ŋ’‘ßČī$•4’+¤‰4“4<Į€JŠ@ŠDUTM5TKÚSÕS5R•Š™Zhĩ’4’ÚHG’NíÔAŖh4ĄNK㨋ēéOH'’A=$“&R/õŅ$šLÛŅšJWŌUĸ,šéa‰p°LX!ŦÖ ÷  …Įđä|RxFØ%<'ėöû„ũÂËÂ+ÂëÂÛÂQÜĢī '„O…Ī…/„ķ¡BƒpAø‰ūDĻ˙¤—č/ô2ũ•ū‹ūF§ôŠ t‚O‚“ÚΞdOą§Ųö ÛÉvągŲsxĒėfļ‡ÕāÉ\Ëöą؋xÎėgđœ>Č^fc‡X={…ŊĘ^c‡Ųëė ö&{‹Ŋ͎°wØģė({cĮŲûėö!ûˆ`ŗOđ”ú”}ÆN˛SėsvšagŲėKö;ĮÎŗ°¯Ų7ė[öûžũĀ؏ėģČ~b?ŗ˛Kėō9Į.ŗ_ŲŋØoėwÖ{ †V“,Ø/Ākx;Ú ĩpū¯Ā ´Eƒ„ĄÂ`aˆ0B)Ü&Œ† Ãáō5­gwÃAØ ¸3Ÿ„ûIÜKzšä>oĸ'!Ū튋uÆDG9ėļHk„Å,›ŒŊNĢQĢ$‘ ”@Z§wą;ā+0Ÿ§oßö<î)Á„’6 Å7&õžļLĀ]Ŧs_[Ō%'\WŌ*éo-Idw.äļOsx܁Ŗųw=dōkķ=…î@ƒÂPøõ o@>>+¸ “ōŨRė.ôž;Šē 8›ÛŖĶöōô*ĶļOƒ=Z˛:ävĪĖ=ÄŪ( ĩtÛCAmĀAĸ=ų(O>A@đ”Œ 2Ē ?&>ž°}Z€ô*õŒ €§g”ǁ^J7ŠW@Ĩtã.ẁÕî=iõÕkędWœĒī_2fT@()ä}˜Sąßü€ũÎsŽĢQlÜŌkԊļš1BuŖÜÍŖÕÕ+܁-CFĩ͍įaa!ļuЎwquoėz ąß07öF—Ž eØĨ›Ī„Ī*4ŋ2OO)žėh<==“Ē'ãŌDW`肸ščh˙ūāYˆ.pWå‰äÅx Kō{ŦP=tÁŪ(ŋ;ęڜöi{dsH°{ŒĻ0Ŗ7´eĘZķN)Κ~C[%Køˆ<ˇ BÜĨnÉ(Ί+ĘēBuiW,†?…kÆãŠ”4ŊŠĢån<×ˆ^ôĢAÛ^ėiøáڔ’pŠä•Îr=iU5Ėoኊ”Ž"Ē^¸Ļ8ÆîJŧsû´šuÔã™)ģ‘ ø`0Ę擄[G|<_āÕu~‡‘@ՐQĄ¸ÆÅԀŋcja€ķœú–œČ<§Ē%§ĩzą5šVšõEÔžÖ&ŲQ0Š[€ØūMvY(ŋß0Oŋ!ŖGš Ē‹Ã˛í7üšX(ŋkk^˜ Dô%ÄĐ0Gc%•rLkaĨ0/ū“Ĩ_§RŖV*)ÄŨ; ÷ ……Úøø˙˛R]đ"¯ĨĢÕÂà tKŊ6~Ķ5ņk†§¯pĀĖGû ]]­Ŋ&U-Ôá-a‚ÃGÅģ{`îL/ūĢ Öwå(Œ øQdŊxÔŋPR8zMÁ˜0_ˆ?\;Û§õFCW]ŨÛãî]]\]RŦįq˞ęũôUújõĖ‚âÅŠ XčŊĻe5‰tkßÃ&ÁA„. ;"!Æ"îElFHJ9ž2ąqqQÉņ öšû3ũuHV+dīäŠJ´$S¤D÷ŪVĸ†„hū-ĄbŨBÅ:e…’;ô Ҥ´ĩx3Ē8Õ2ę{ØĐu?Ž 0CBƒ‰pÁ!*Háŋ`Ų›čËØ|H`€î€@Đ-uëRc0gôĐŌ ŊpŅiC(‡6ė5š36÷¸•~ ģ‡ũŸ/č°˜žÅ`Â0ąqq q!ŅŗøœÁį4=Ĩ>‡Žˆ<ÄXÄfÄ!Ä„Š~ŽĄLOņũ¤„œĪCPz C™žÄiÄĐD?Cî3úíÃšėœŒũ “Ú1ˏŧaÆf,ļŒ:úAÍoí\uôĢŊîTזéô# (vö6ū¸ƒň™ š‘ûĒë[„„u>Æ:c#ˆwC:ŒPĶã5ØM=VãëéęaŖīŅ7ÁŽB=JßRčģô …žC_WčÛHãĄoÔÄš ‡ķëČHe¤1_¤¯ėM´¸‚=ĖôŠĮ…aGDbb,â^„DŅ„šņ. 6ōQ–ŦoúlSƒ˛Ëīë…:ææ¯ÛÍČa°ŲŊŲGũž 1Ęßēû‘ãoéäxāģs r<đM‹|ã'#ĮßčąČņĀ7h8rÔŅĮ_LLrešBÜ=LtJiJiJi0:?đãc{¤&%%ļɟÚ.ÅUu€T$UCIÕ6RUFĒî&UKHU.ŠēƒTĨ’*'Њ#U~RõéŠĸ¨"ūÚkĸ9~Š:BĒž#U•¤ĘGĒŧ¤*‘TšIļŋŽÆ×ܒА…ėíÁ÷Ō›ģg˜pŒņ(ŅxTëxÜö‡0<†*1?r'„ GÅqš°7%/īĐ-cFžô5Ŧø.ÃkpÁp^C5z y 0a˜‡‹¨G\@–NßĢ„& ;"ōc‹’2œ  3ÂCÜ­ ŦcxЃxŒž†O>ņ4Ū+;åTš¯p¯“˜âČ ¸`Íŋå[Ėj3ŪÖ^øÕđ¯_  éĄĄëčŊ‹ ą>Lī­ų-ÖUGŽņŊäęI‚8†ZGrĀGŧHģBĨī N5§Yā¤ģfÔ8Gb5S/Íu€y­\ŋ9ĪšžuÖQdŋqžäúÄ]ĮHëĻėzÁõ‘s•ëíŽujL9čĢ#H¸•ĸû]]ĪQŠ.ÁŒM5Žģ9yÁĩČŲĮ5ÅŠd”…2î¨Ä˜ßäęíę‹íå;Įšü•Øæ Ž<įŽÜPŠÎŧÎ ŽtBjˆMÁÁļs*zâ”GdבIū4ÕÕ(Õ UU†*M¯rŠbU1*ĢÚĸ–ÕFĩ^­UĢՒšŠŠÔÖēāY*l•dNøī ` /SōwÅÜŽ5…[!!ôŖũ†õ$ũõĨĐoœ;py˜§Žhņ==IĀŌú īčšÚ¯NČNíP ž}ÔBÖbj€ŽŦ#xúՑ OZÃ]Õũ@ˆyŲÚN“—­-,‡mnž#ĪŌŨœĶ;˙O‚âp˜zõĮq ØĐoبĀÎØÂ@g‚ą…ũp_v?۟/äīĮĢ4’ÂQû…îäį‚Ą<]čž_XدŽŒTʁ›ü„åPc~RĘŠãĀÍˁ[*ˇ)T΋õą\"'XNŖ¯RÎĢŅ(åáåöT&äīILTĘØŨPДдģۖ9âÅ2^¯RÆVG”2GlUŧL ģRÄéÄ"qNĨ‰§RÄIĸ•"#¯é.˛ĒĩČ*Ĩ'\-㠕1œm)c8‹eR˙۟˛žŠŠdīM…Ĩcø= ØSP†(Ŧž;ɨįvī)- _|ÅãJ'qZR(ô”åJ=ųî=7ų“ė1<û&OūS0|Ԟ1ū˛üš›ü7xJō ÷öœ•}M_ĢZûĘü' æeņžúd˙Iv6ĪîÃûĘæ}eķžúøû(}ĸãƒGíQCĪBt;ē—ę´¨¯Å1ņ…=mōĖîŠōŪī¸;æãŋاC/\7:‚gĩīŅžĪÂ=ÅŗŒü˛ÎrÜ}S|˞#œ%c˛ŲĶRgĪŠœŽ‚ōüĐŋJüÁ¤Ųs¸ĀCajå˙ôƒyxo˯œ Đ/2Ŧ_ ũÜ=*Ļķ)ēĩ¤étčn†;`b7ž(­yZ.OĶhÂ˙¸ūs´ßUôĨŊÄGfCeĄˆë7œĸ)öĒ ģď‡ĘBœ`%I%•-m(Æ|ž-˜='Ė…å0;LCĩ°Je‹8Z°š*ņD!ĸŧ!ŠųĀüņ §ÍåÁox>§ô;,\ĀxŽ”Ãsp^%ŋŲÛĩĀ=ž|xÂ_ažbŖ1e ÅGÄôŋ’¨`-t„­xŽm…ŖXö6¸€8‚ßÂbX&|ˆĩ–  †°–ô΁1p†ŨŲĐĻÃLR\ŧ?¸ž„ũÂ[Á&ĐA4”âs4øŖø÷ā)h5„p†Ü¯Ų~ėĨ K>ŗ`“PÄHpbđwA<ĖÃ10GI=MÅÖËākâ …^ØĘÁ@đ0–rBL‚Mp€t&}hŧ8&8 xlØĮ|lu#ÔĀ øÔÁËđŅ‹ƒÛƒ! ŌāœO-ŧGę…æĻ%Íy\Đ(Ĩvƒ93āođ&'ō !ęÅ Ņ/ŪüŦĐ FāhŸÆš˙ ŋŌģņY,ŧÁz{‚år—6ŧ_hŌ‘ "#i;:ƒ>.Ė5öØ ŸņPŽō~[?ZķÕĶcÂlk”b›Ī¸">xƒWˆgę&•ä/äcōíEĮŌGč—Â_Ų3ėU Îú˜kaüJ,¤+Bn'“ČB˛‚ÜG6’Ŗä8ų†ö ÃézA˜$T/ŗžø c•ėqš¸ZúĻyTķáæ÷› f—Ãԇ%8úáqœŲ~8Ÿâsž$"Ņ#>ü­īr>w“ĩd›ōē{9Nž$ßâ ô i¤x°R‰ÆđˇŦøxč,t(˙JĨĮđ9N ŋ v!AH: šBĄ0GĩBXĪ>á ͎ą Ę9CÜ nwˆģÄWųįiĒŋā‘ūî•'šRšN7CķĘæ Í5ÍĩÁ/ × ŧBåâčKđ™ŒëŊ5n7|Hô(ģh’Bē“ū(™ąd2Š ķQ’KÉ&ō¤2öįÉA”Ō'äŽŲ@Ę˜;Đδ'„Ī´ŒV īu?­ĨĶß• LB¤"ôŠ„2aļ°@Ø „w…Ī…/…ËÂ|‚LË\,ųX*ëÃÆ˛9ėqö5ûZ#ž#ž—´Ō4išT'ũ„NLwÕ`ÕU‘ę^Õ ĒÔÅü-*ėƒÛ~ÔAÎ K„aŦŖ™, o,īĄ>…ņКJw•tŠĨ‰â|é&zņj˙WúŨL/Ķ›„¤“ų7Uųdeü›ßšė5h`qnīaËķ%=š›^ôPC”īM“×…t–*ŧŸ gˆŠm…“LK뤁>- F-x™uGAŧđ( A #ÁËÎĸu[(d°x¤‹ŅnjA›öîîhz0ٚĶõbZˆMø<Œv‚Ą•ãŋ ­Ø{P+ §u0Q4´:ėæĄ0:øl N„éÁûĄ=ڃÁ…Øâ8÷²Ŧų.˜‰7ĮOqo÷{Ķcbī`{ZM?ĨÃč†k×Ĩí%øŸįĄ7t_‚jö ƒŧāšā Ôîd´°aú§įp–?b}…zČlH÷{ 3qžg`Hđ頋haRp* ‚ƒđ¤J„Uj¸ƒŠ˙øĮ3š ž Æ] aŌU°Đ`^üīĄ‚zú˙Úô-čĶ˙KÔ]…ņNĶķōGxŨÂûwÄPk€Hlΰ!j$âw€˜dg.@,ĻÅ- Á•m$`[žå^ĮU$Á ÜĀ ÜĀ ÜĀ ÜĀ ÜĀ ÜĀ ÜĀ ÜĀ ÜĀ „Aų/扸€*čYKÉ9IUG7ú#@dįĐĒØ9QjIjÎĘ)Šđ‰” 9ķā{F\˛ éfO*Imrü‹üŦŠņxaõ†—^nv5ģ¯ë_ŸL“eĒŅĘ,>ífđ˜`ŗp‡Éč2Rãŗ–?ī?ÂæŦ$>™6ģ-RĻMKHjjÂÍIw.98zĀąæ!ä,ųâāū ÕŖ?hlúėĮæŸ›ÕØûÎæĶä8 Z¸O‹âŪ%ՑÁ~r)%Z’ Z*`¤ŽĒnƒ`,ˀŰ—f‹nëÃ8ŠKE—ÎÉ š2Š›‡rƒÜÔ@Ė–œN陝3#­’*ŠK—ėŽž-#§‹pôhÅj߀¨’Ûąß(úد^ŋƒōnrCīļķˇ0ĨũËEE8ÆPsŽ=ĘW,€ŠÄzˆƒ¯ũ]no’^I/ŠŪTŋíTŨĸ/Ô7NŅ7Ūiš3b•å å|ôų˜‹ŅúCē#hŒė”cå8Yú[đ"¨‚gATŧ菎ĶĘjI:âŒļ:Ņjg´@¨:Ú)âä:ē}ī 31×Į>CœU„¸:ú’ßD¨^[i˙Įã÷d‘—čpƒLēúõæ}yt,ASFĐDp‘{÷ŦVær ,5WžÔ (g^CSŅ9ŗÅžCx°ÂØ!Õ¸H>’tÅÂ("Eŗŧ‘ņžl”d—.ŗ|žEŦ™60ǚ¤bĒ+ŲÔî}bĶ…īúËŖdÄŋŪ˙đrß§_Ũ6&îšįzä–Öß}øü„)ˇæļ5ÉĪŦŖŸ6Ŋ8hé}õD={íĨˇšH•\ŊúđļM5ƒōlô§g›įŽižüū›÷՜åß2€ŌŒDŊŒ…¨ņ'M‰"ų*d~Tž{´e¸{Š0^5^=Ų2Ū=[=ĮšLŊÜųąú#›YU<[›äö¸ãų/'™“ãü†Áj0Xcȇcšėč$ŋFŒ‹âŦ´]ũ‘°Ī[)+˛C;!Ë2•×§iš°âHŽ_›gkŸa_lgö:š¸7õ͐´Z$ŎSXķPJ -b)B=SĄņđ$p…âÚeáÚåIŗœÍuXÛHMhÜëHģeĘČ#ÆŅ'Ö6Í;žô‹æs­úæšĪ›˛­8kûļģîÜɆ'§HīūãŠŌâæ_?¨n¸›ô# É3¯ėxõĘįE; ëx÷n”\° í}Ąx­Ŋ‘ÄųK;ĘéōDõ$MąŧRX/ŋ-ž!ÕËeZ,$#é`y’. ˙S˙OÃ?ĻgftZČ˜Ū`TK*•yĩ¤Wá)âV魘@ÁÍôV,Ą‰Euœ$HutĻ_jũˇ~ūĸD„čüŊĘTÂĐÁė;ÄõŒ°:BüēÁúzÕŊ°^Oô<.›TĮTtąĒJEU˜>ūD1„QüįhĸŖä†päåF7äËå˛a…Ø!UqEGjx—į˜srVȇ^!†(ŽFŋ€nXŋ@ܐҪj™IPĢ …‚āŋø"’YE’IßÕôČÖOÉO{'83Åŋ÷&›ķéh˛a˙ŧĩĢQ‰&ŋįâ uūâR:9–ē!ÃP 3avl,]›Ä]“†ũB­áMÃq8ûĪXŗŅkŽR¤dsŠĶíęciŊ-rdÔ$qJė]–Õ–MÂFã&į˛î0Ÿ0F€ĸeĢÍh]đtMrŠÃ›”œ#›€°˜ˆ8ŊĮ4˛Īt+øÜ„h—ŨįVuT\éG*ÚEĸŽhQQŗīŲÔÔĸĸ ÔÍYÄ.1OB"íœeIĖĖ`v•+"´Z¸Z˛ÚWon~í|Cķ'ė&Ŋ^=EŌn:”ųęĪ|5fÚ?–?ņ%Ĩ.4žBĻpžŒØsöö[îßÖ|ᾗšŋ­>ˆûv+jßs¨}H ũũ&‹ÎH,]œŖ]ÔĶ\ĖRür¯%: éÅŊ IYfMʒÃÔϘ˙÷ŊąžP>–—Ôįû+‘ņouŪęĻãœæœĨ™o\`ZĻ]izČđŒŠÎôņk“lÔëŨf“Õl6™Mz%†ÆGÛ´’Å,ôĸCŖąŲŖŖâėvˆOˆC•‡Ãd2Ēã|ÆGĨ"wâĖÄĒD!1Áá&ÜĨ(ōÜ´#$Sî ÉE—ŖÎ9ø™ƒt.?ypįcrnNGåđ =ÜNr7ĸõŒ ¨×˜Q~­ÚoĘ1ÉŨĖ–n˜VH*”ß2OûŖŖrĖ Q9„ŅīĖ‘Ŧ"2'ÜBaĻ9>ÃÆO)z$öĐĸ…ņ˜19t’ÅoĨՇßŊķȇ’Gô^zuÄôÛÚĮ÷û‚l]ļaāCO4§‹ŊĩāŅcŊ‰į4WNK×tÕŠšæ™Ų úLZÎ}1hsŋGÍN‡f˙ŖĨB)Ģf3æMę,ä8{ ˇ¨úĮ¸ō{'  UcboK^aL6øiĸäíbĘōä{ :ŽvôŒđNÕM6L1N°–9čî4ÜiZ$ĪIŦô.ĒuĢ ÕĻĩō˛Ä{ŧ÷6˜6DÆyīŒ‹QĢ$&P‰x0MBCŨūŪhŨ`ƒö2q“Á¤˜Ė$뉄–;ā÷ļ‹ŗ b\{MŒ/úVڑvŅņ> ņáŗ‰ęTz{x3œC3rÍ~Ņf#.qg׌bá{Š*HQEDvÍ ‰5ɗ˜äķuÎR|[x§DZí6fWV­zĸoĖ‹†ąo-šąsØā175OR>ņîŸ˙úÄoËÅĻįž lÍéJ>UuįōÆĮŪlūįFō‰<}ím=+ķ &zė%ŠŲO”Íxe|ųģKŒĢ×-š}Pfæ”ä›ö͝sŦröˇ8‡tôÎ(ļ|ß Ō8(_ŽĶÔŅĘŊîI}QrÚ=Kä÷EiyŽú…Ęi•ËUWn:WôYņ-ķZÜøÎxX›iDs,ĢnŽ Ī=÷û?štĄ5ÂjÔ Jö+.C¤5üÖm‡‹C5‰”‹kPæˇS1—A.‘$Ēģ˛SÂTzŠ”ĢÔIēCd Ū#R/_ē| țs::Џh€Žx|r'ģĸ"55"d/ė'%÷‘Ō}ŋb×ņWÎĐܸ5ø s˛î Ų$ÖŋNcФDĸSÚRRr ]"ŗcēĨܒRd(J™l(O)N¯6,oˇÉöHô3†Č§ĸv&ŋõRōá¨cÉD~žŦΡ—ŨåHMKÉĘa9iˇ°ži#ՅŠÔåŠsõ+ôoë3ü–jÎÎ2&wL˞gÄ[cÛÍhGÛ9;ķŒ÷7ƒFqŗqˇņ‚Q0ē;ũ6ĮƒčāĒ  I›átíJäđÆ'ÖŅÛũr’|˛ĪíK÷íö‰žN9܀ģâĐËČŠĪĄ[rHŽŨëHč˜xH:&Q—”'QŠSWîTp/ ] ô1.å6?Ī•ũ°ĻƒQŅPTQVsX[Ņƒ /×BEGŗ•§sVRČëNĨĩEFZmvOTF4ōÜÆc!!wüūÉģöŠėÛyĘgIfÁĘÅ bŽéĮW­Ü9XÖØ:íãĪ“1­|Ō6_ė=#zīZ6pÉ@ĢŅčÕNosa…Ŗbu?É­æ_l\vsWōy˛SNĐąoņíƒnž‡+¸WЅÚ+ãYųą˙Y"ęM‰bgą@ķ\ušđ€uötÎt­wIŨ"rmšŅũmũŖ‹ÔE†QĻ"ÛŅ“ÕS “LĶmĶŖë]Ÿę?ŗõeÄöĸžŠ=ë ēĸÜbGSGkē˜gō‹ũMƒÅ âgąŋ°ßeŊid…§¤"ÚH§QįH<Ž#˛Î¯+ÖUé˜n61gBĻāĨ´ž =ŲBä"a.’GDÅõÉŲŠYšäĻKÜRT(v˙™sBw ŧITĖ‚Šx9͌Ļ!ŽFĘāIHĐ2´zv¤ũĶĩŗöŒÛ]áoūųåƒShֈûæ>ûäœšĪŠš~šwĐŊG*›/4üŲphÄęŖīã(îžÁÁo„Ôúh8ęīŖŅ—ŗWD/û°ˆaöâˆbû#ôa“aģŧ=Z¯6Di'Ķra˛8G?ĶPexJŋOķ‚vŸ^oĶ/×EcÂXĶ Ķb“`"\YoIG§0Ŗ›˛/gīj`2éĐĀXœ:•ÃÉtN1%bp‰ēT!译[œ‘‰ĮTÄĨĘC˙ŦSLÖaÅĻTpxVøëpûđīŸ5ĖēÔ0KąŽ¨ĢxkŅĝk1Ščr(p–…ŌV;ʅ%äî‰ŊđügÍŋÎúvÕs§\ģŖ^šsûŌÉëČ2û‹ĮH,Ņ>Kč’Ũ[cĻL}íÏ_ũ jVo”ŌÔ,ŗĸYģ´”ŧ†,CžAėlíėŧ×ĩsN¤ãÅ2MŠĩØYīúH<ņyÔųˆķÖ öīŖÎ+dsšRŖšÚõ‹æ:¨ę@ lŨhgC?Z`čmŊÅy›v¤aĸáŧôĩíwrÉ(“HÁ¨C',%fT-AįČ$ā5›ŧ˛|ÜLdŗß\lŽ23ķlKâ!ôk΍‚*Æe7H%¨ĸⲇk^*”~î9.˛\ŽĢĒÅ7u|gžŠqW‡†jFŦm. ]Ë/>1gōG÷o踡ÉũėœšOî¸kūÖ可i|b3LJô Æß{SËģG^yãŗwŖĖúánŒC͊D™öw3’ŽŠÄ"Í]™0EœĄ)ĶŠeŧvË4ÉōŠøģõr´Ē“Ĩ[T'gˀčÎ!–1QC%–iŅ%ÎųŌüČËô˛C1ėöÁļbÛL›`sšÖË[đ%ŗ§V\ņ4äÁT.ģßĀm &)%+` †hÆöz}Yœúcšet—-SNTųS˛Úˆ,ŧS4(ã‘qš"uߌMáûinSEŽrPŽĨüęU1ĢEŲdĀ̍ŲNJWŽ[$^š“IÂŌ~Ü˙mķb=u‚ɕo´5ËJ×4}F‡čģŽ\ĩđ2ŌūD-qĄ-ГäæĶÍŋÉîŨ&‘—÷šô?!#đxĒÂŌ{ũqV 1EuŒJōG͌zD˙¨áƒ:ڐlDÕGą(>ģähWVŦÚ čMN-‰¤ŠÖ&H Ũl%Ö`„ŸŲŊ z?n7.’N]ŗ8õkŽŦõØ×ލƒäÄÃeĸUNSt*ņDĮ“ĪŌ†"~œį*ī?đÔ)Ŋ×ŋU6K•¤Æ#EFĮĖ’)†¤’Ô”%KH**ÖŦLŗ§sfįŦlîŅā>äÛ023ŌcŽŲŧ9"úžšũĮÄtÍš똰iMŔŦŪˇYĶö.ˇæĘÔĄžÍC„īP‡âđV~Ņ_Ŧ͉Ö4×Ú_W`•4ąQąi:Ÿ5͓ŖëbŊU×Û:R5J7I÷ģö—HcOZRwO÷¤ūIëĶļ¤ŠēÄwi——Ö[×;ž ŨđøáíĘUĨņĨíŠĶĒŌ>Kú&ūGĪ…$ŗŨ&EÖŅ=ĩÉΕbÁd7ēCÜ~UA=Ž]‹ü=D§Ķ¤-HpęĩļČLoĻÖëpˇŲîˇÛĢđŪ>ÛDŧāJĒq+dO|čĘ[ˆg ÚrKH Ö×Äį Æ×ר89]#įH!ĸÄô ŲŖ ߗ IčëE~Ëግ­6ėÍjÍUŦušÆÁ+˙°'&Tœ*WYŧQáĸ“ß6O&‡N7o],¸ršį6§Ž;›ų;÷{P ˛-]ŗD4ÜŲ]ŗDnĀŗ:‡hz§Mđ*Ôīŝk]âfņŒČapQ\âLąJ ŠŒ˙b*„6#oIŲ”ŅhĨ7ŠGWļŲ™ŦUoSSCšĢ¨YĘLø îŠåo­BÖCōĄĩöĀûAüģŋ‡Î€Öã;§ųÂ~Ū-ž/ģŠ]íöh1n xâœR¤S§CˇYōDGÉÚã^Â˙žõÚíŅFīzåÅ}Ņ>‡w} ‰AÎ4Ķã%ĮpŸ’ē€k‹Q‰Ū:2o|ŸëŠ~tĶ9<Ä.5 ,(Ë˙ēÎüÜÂM§¨’YyoŸĶbRôÖŸUoŽ!Cd‹Iá';Ÿ]¤rˇ+oėģĸ`m-Ė֌§&Ī}Ču÷‘ĮwîõŒé>ķ¯ĩŖÆ÷_Ōų8vܨģ_hJĸMÛíÁíMҚųķoē¯éͰ­ũJËīú#DAŠ ;ä:ų+á눋Âå‰ņ=Û ¸@&ËĮgAsĢ­FĢ͂F—H6ƒÖ`ÔuŠåÕü§čP’[^ĮEéØâ8ęĖ!ĐĖH[ØøZū`|í-†÷Rnč6ˆĻWšģŖÄŽÚ^›dÖhÕZ•VdŸY2Æ“ÖXĘ~hĨ*:Ų%| l#°Ûæ|^ŧu°Ŧ­M™Ōˇōiæ{hwÁĖ‹š*éōéĶzÜ˙n›—>cĘÄQđŠŋČĸŌFéûH}Õ#ĨBõDŠ\­Î’ģYēŲ:; ä~–~ļĮqŒf¨\d)˛ uL§iÆËĶ,ĶlãķH¤F ˇ ÃÅáÚÛõS…2ąL;U¯ĩ;™ĘŒ*gMTqQD$zŗŌUT˛Ęî_§3\Ņ0=Š;ˆČÁE¸ĸQč͝CåĶ t ‹.Ĩ†Ūîđ— h=øö× ‡iƉã4 ÷x„ōöÂīâ۞×ųÛWŊ~’Øîú~õ™æ†ũ5+–×ė]ļĸ†F¤us›ŋh:úũ_H1ŧûÎģīŋūÎėzEs9‹GšXĐ:æR/ˇ—o–ûÉ,ĪpS—ģŪ›™Û3vĻ{Ŋ[ŨÍŪ-æVû­1…ęÛõcėcb&̧čËåiö)1õî­Ÿ;>ū0îœõ\ÜYwĐmķ°T95˛3ë&÷fˇĘŖåķēīc›eŲˆŪ5ŋĐJ6ŧЂ1*ņ¸–ČZŋļX[ĨeÚŲ$"“fZŧzĨuᕖüŲVšÔšsÚ^i#Z6™-ŌJšc“dڈjÅön÷OZy|ōœ3wžˇƒųŠšķw==ģrOsšørõ!k‚?ŅܸēˇĻFaûŅÃīœxįČ'¨ÚËĐc~åe†{ü7uŒ 2#–Åząal›Í$Y­Qk f5Ņ)­&yŊš¨Ü$‚&˜˙G_ÅŌįp̝‚wÎKŗđNĨL‹* œé ŋŊ¸č0Ÿä,RÔbFBŪ¯ wÃ˛mŨËķnŋŖ{Īž7Ũacž­}ģ=Ô'¯xVĶGüŧĪÃûæ:ųÔK°&tĶÜĒÉO™P–°PŗNŗ4ņЈ]i¯ =ÚaOī—öą]ŒĄ#(•3ˆÖ1F=F3F;F7F?Æ0Y=Y3Y;Y7Y?ŲPëĢM2ņ÷‹‰íē$ŽÖęÆûÆ'ĪöĖNŦJ|@û¨ūūä‡ŌLߎ}F˙DŌöäŊž×}ļX<âü–¸œŅę$¯^ËĸŨžHĻëÍŨc§+*/jPÔØ¨ŨQĮĸ$S”+jFԙ(æŠē7ŠFŊDGāŊ¸-?Ą29Ž~‘ å-ėĩÚ˛”âŒæ,B:Œ‰Kc‘*æė sE“čÄ(„#+ĒŽŪ^ŖJLÁ’/:sާ”č ^ˇwēâŒú š—Q•A3dBH"¸M gZŨ‡N-׸Ščg7ˍ˜5~“ģ”~iP—šT´Wŗ՜u.ôĻ+üĸ ?Š}œ¯>ŗl‘#dAJ0¸c@“ŦŠ!b{ âŦ7zb ÁcĐĢÛicHr’F+Ĩ˛pÉąÜ,Ļr7%p÷ 5%uÉîĢVpgīęG™Iž¤čŨwÉūÃ;_|âhČ|øōjLĢîZ8ŋŗ÷76ęŅ5åža‹^mč+ËNļŲ:Æ,=ôĐČō7û”Üėœ2Ģ,˙fÃ›q˒}$ģRûŪ5Ņ1tĖĐl36B›˜Ųcá˜Ņ›o{–kZbđgš"nÄģeÕ~ĐâÚx|Ü]Ŧ÷÷@Ļ* ũ\ŊAK°ÉšT“ 3É @ ¯žUęMAąjĻĒJĩ^Å­čU@U¯:Ž’Tčdp.{&„6‹ōËč۟ģ”Ģŧ‚hĘåvœ™)ŋúėÂkŊā÷Esļ™ß•Ī-ŠŨ?wÜÔ´ĨK÷îÛ‘šˇuŗÜŊl-]CTS›×Žiz`@Z4ŸË=¸kÎ*ߤ~y?DķÛ?ú@Ôaãä\ôˇŗXŗR#Hĸ:ÂĻ'6nx3N2m^‡]9Dí¤ŪNėŖ•mĪŅč‹Ņtfô–č@t0šEã-§Õ ā @ãÖĮûĶ ŒjŊŧ4´œŸh”7xš!‹ ¨T4““ĸ7,ŠE5žĸLĩ9øš’˛m"ęIøULŠ/{vEMēp^Č[xâŽ'ÉēZyú!ënĒ}´ļī´A+éũM{×vę3dØŊ+i^Dķ›ĘBKîxą3^ÔĖ9Zž› æ :YjĐēāw{‘’0Å÷kââŗ Œ}ã× ? 6 0ö™_r‡,pc`ŌˇƒdO›ĩ}Ąv$I ÕŖ4ČZŽ.סyd] ž¯™§]AVĐåÂ*ÕJuĩæ1xXsŸöYØĻ}^TíŅž ¯k?ƒÚā+m#\ŌĻiAÔ:ĀĻMŸ6[;Đwũ[–čGWH‹×¯FkÕh´ Pŧ1€•Ŧ EĶ­VSJ$•V#;ę‰>Aí÷ûņæF5u$fŸ_*"į׏П$čžû€/YCtTSQSQ´Ŗá\QčĨNNëõÂŦÜ-V,R>÷]ĄXstŨ+Ž~ĘĻ|ĐO2#Đ?ĖŽ@?ūųæŠ;įu9RØß<ųš–Nœ1|.]ēÃÅ7~ĉ&+öšœÄÄWcģ3'Ų:Ō´[+ø ~5š“Ķŗd¨ô‹Íā°$é’ôI†.ú.†Îƍf]˛%9ĸ¯­ĐRQYn)(\ Í5,0ßiŊ3r™ĄÚŧÆ˛&b•õaíŨAų%ķëwÚ¯­ŋšäßŦAgœÅdŌËf‹eeˆđZ´VŒ˜ô&ŗŪĢĶZu:m„Åĸ×ë$Áe§ė¤‡œÔYGķö™"üŋĩŽ÷ëō,~ k9dĄ–:ŌķI€‚-ΘÜ:ŋß­O×Ō ƒõA=Õc‰ŊM8YšWã^ˆĻũü&ūQ;ŠœŌî/‹’ĪĄsíÜ<đ5āōWˇŊÛņ_Ya”ssՇûŒÃúCFz ôÁo@ü†tíZēņíkđô Ų9ڄė#ĒņžČsøŗÍBܜŠxÕÃKXDRčõ >W‘˙RŽ'aąõĻ´ÜžvŗOÔ5O{õķÔWęWĩÍS{$Ļ/™Õ<ņ991fŠ)–%7mœŗdá\:Ĩņ­Ũ= ‡ūôø_Ķå˙+ @ŗržāNėæ)ÅĶĐōŋĮÜ!ևyÖόņĮ0/QŠ ķ*8,Ĩ…y5øT ÃŧĒ ÛÃŧ–ŊĒôĖyŒ3vķz˜`\æ R­t1ĖaŒņrëŸ]_l -˙‡ˇhú)ĖSPYz„y:Z2´F!>´F!>´F!žĩ)ZŖZŖZŖZŖZŖZŖZŖZŖZŖZ#ÎkÛĖW§ĖĨÂëÛ¤•šßĻđ2Ÿ‹}ĸÂG oąĪQxk›ō‘ŧ0ok“ĨÔ]Ąđ1J_Ą6c۔qĩá•ō*|ŠÂ?Ąđí~įÕmƯnĶ—žMēže.ÃaĖ„2˜%PŠÔ Ī †Ã$…3`:bv¸”zalō<,ÁôrĨ„SĻbũČå+é%˙—-ul™†aÎTåoE‡ĘTbÚ-HCũu‚|ŌĄ}˜ËPR{`ŠH‡b‰8†ŲJ­ĄØ^%bĖÅpŧ2†é˜WĶZG2 ûucŠ’pOĄōå(!7Öāõy‹Ķ!Mé…į”(=•†Û*Á”PÍiJ‹|“pôĶ”Ë1gļRz’Ō—úėp•Ę K•ēŗ•üéJ+œō1ÍPÆPžËLĨm>ĸReT•Jo<‡—¯ĐĐøį(Ŋš•ڎĒ\i6æOWâķ”ļ'…{/ —Ą´ęģ%}ĒŌöė°DJ1’Ėõåfc›eŠTʑ†Ú. §ĖQ$Í×ęĒ–ĖPÖe–"ŅŠJ}>RŽĶÂĩZz(UęĪ ÷Zž)Ī IķĒ&`IŪZ(õĒ\ËÃŌžIšR~ŽģēǕЯNUF÷į:Ҟs*[įÂķĻ)í]mcö3%<ڒ°üKv‡õžEf㕞'*ŠĄúķ0§<ŧ†ŧĖT\ûŽĖĀp"æÍ K;ÔÂÕŊ\ĸŦUH;܊ KÃķ/WVmĒRfϞĪBÚ8]ŠšI[í.oÕ,7æĪ¯Ė4e4\7CëVŪÉS[Į1M‰]ÕŪŲ×Ų›ĘëæWîcœŌÂEŌã¯ŅÍ2¨ĀôÉÎQū:RË '(ēíVt`ž"ÛJEīf+Ģ1ąuÕųØCûīĨ´ÖŨTÖ˛Ģö(”;MY‘¸SŠ5oˇTÉŊĒiĄŪĮ+ԚŠė’­ŗhé›×Ÿ§ä—(’˜îƒīĄg+õ[FÜŌúLE‡Ļ)6´elū`Wģ]ŗjÜŪMTôŸ¯n7î¯ÅÖr[ŲC7$cK| f)û!´ÚĩikęõÕØķŠžĪ īûiJëSZ×økķCë21l ËÂöíĒ ĩ:Ī7 VęģÁ§ô7ÃAØ÷Es[$ÆuŗR‘ö¤pk` –ާGoD/œįa*¯ßÃūJzĻ Ãī>(Å|(ŠÃÁZí­üvˇĻ‡FZš™áĩŊēū(ŸĐ™7e0KŅŽIJé–ų´Xū}§ä.ĀōsZû,mĩĄ!ŲÍQę^ĩ}eáŨÁ-ÔU{˛åaÛ\ļ•VĘZm/—ma¸7nEæ†mö¸ÖS/Ôįė#™Ũš×jËÂ;ģŦuīĖRėÔė°Ũ˜Öû?“WËnį+kĶĘUkņĮūƇõ‹ëō8ŇF=.ŧ2ĶÃ-˙Ų %)ŗēVR!Ë˙G­øcĪ-6”[ËÅŖ)Á^§†Ĩ]ļU˙SßŨŸŪƞ/øÃZ”…Ŋ™ļ;'tJ”(#šŠH–Ÿ[åĘ~ûĪkîëâô66´Ĩ_žûĮ+’.osZÍjãqĨĩ–žÕFo¯ú˙^R|tĶ”ö[ôjÆ5íÍSÖОšm­I‹žZr– Ų™9ŠÄyû“ZįW[ížļÜ!ų‡vÕ˰~\ĩđ×ęĐŋ›ŅUũ¸E™ûWŽÅĮãg[YØ Í&äW–*Ģ:ũē5˜uŧ¯ļĖį7CąüãÃvuŽâƒÍƒļ^Ü^ũ–öB{˛,ėk\{"ˇ´÷Įu IëĒg\Ē´ųĮ}ܲb%×ÉzÂ˙Ņh¯Jų=\ëW\;ĸ˛°ˇ<OȖø)ĶSÛ?ģBdãy诰ÆÚã}# ‘üÎ9ú…KĻ+Š0 ŸŸ ™^Ģ tÆģ o}’â“ĖÄū:â3Oy:ü?­]ÍnEN´ž$’_!Ē$’ŗk˙H,Į‰Ŧ`y×HœĸŪžÚ&3Ķ“îÃŖpã8‚„xŽŧ_ÕΎ턐rđxϧ~ŋúēēv/ĢgûíoĩķŊ뜐ģ§ē;Û/§ ëģ­ÄtĄzq†žõs–ī'x؟‹“4č§ãz}nĢä“•Ė ˙/î‘Ę˯ūŒpMÚ!¤V#={^*KķÄp%ųa=´:,dųƒxYžŊÁĮ•íIWķĖXĻi’3úĘ',Ņ‘ĩ&9_Q]Ø!=5ÉŧGh$Æhė‹FV"TĐÛÚßß|ŒËö‹‚.ÜD2UFö›“ëMšŠRS1ĩ.åPgŦúL´åž0đ}ƒ`–kŠå*9†´ÅMē!)$ūŠƒAz)°I%^‰‚mbgŅĪφ0kЎ+ܗN\•51iĒ1ußDBŠÅ ‡ŌU*ü+˜5ˆß6pTid™3s/īÛÜ!܋ˆxšģ+V­˛ĄpPÉĀŽrâĻŽ0V–ádˇ°ˆŋA2%!ˇˆ"bŖt…›zŪÄ۟…Æ”Š‰œ-Đä׍ÛXÁŸf)Ã"’JÉUsI=0ęžâ†”)2åK37ßē Ļ9ŲhPĪ\Ŧ ͉ ŅŽ¸ĩŠD2„˜\Ã"^_zĩ6\rõ`‘ÚĪ›Â„ƒ/Ą'ŦŨîmĶÃSgƒ—=RŠĶ‰ūû&ĩ/Mx%˙ķ‘Ë$dđM9ŅË1}a}D“S:ŸÍ†‘ÛbÃŗķÉÉŗ“ŖÃÉÉų?ŖĪOŽŽĪÆĮtøüâøøôøl˛~ũū$G)–HKYÄ0’CÖḬŠ;Ī΃ŠķNũų§iGoDĶ C]SeĘ>p„R^ƒl†¸™faīž‚Zn@?•­Ít+AĢ 2ŠÍRĀ63`—”ŨĪYE”+=”ŒŸ6 ĻĻĮ.ŧ‘ĐĮqČŋ‚bĨ, Ĩ+S4f VšVŨÔŌeĨ<ī–Y §ž8؆bÍÖ͜};sŠ•2TtM–9Š1˜´qmČrPlĩ#ŧTáJ' Á‰Ęĩ>ŧŠ b+‡uҎāL3-\ĖÅl-ā.AnďRÕ-ß#tۑâq2ģNN:Ūë†ŖēA¯´Ē>ƒĐĮ­Â1÷M‘ĢWŽÛE‹{+}‘C%]#ģn‹Ģ–6c›Žk,‰™>ęŲ?›ÕW }¯č ÁI"p9>¤Įôpog÷íní=ŪÜŲÜŧwīō7ˇļvvpŨũd—v?}˛˙dũ~žR}0ĩm;,—…ˇžŧš'˜žĶ ؂ –.ü;ô =ËŖÁoČ& Î:CcŖ{#âÄÚÛ~‡íQžĘbTĻʔ<*ãK#}b(‹˙QĄåĢü~yõ8Ē4†!¯ƒū,a§cRww‡ų×xūKGåûą‹2ÉВ žü2ø}đū~ü6øé†-ŖƒÁōųOĩ͡|ņ-kjoíÁÚÖڋĩįkŸáēiŖŗ~Éīū|÷ûÁņäK˜ ã™Ø¸sįo+Fķendstream endobj 10 0 obj << /Contents 12 0 R /MediaBox [ 0 0 612 792 ] /Parent 29 0 R /Resources << /ExtGState << /G0 35 0 R >> /Font << /F0 36 0 R /F1 38 0 R /F2 37 0 R >> /ProcSets [ /PDF /Text /ImageB /ImageC /ImageI ] /XObject << /X0 11 0 R >> >> /Type /Page >> endobj 11 0 obj << /BitsPerComponent 8 /ColorSpace /DeviceRGB /ColorTransform 0 /Filter /DCTDecode /Height 626 /SMask 13 0 R /Subtype /Image /Type /XObject /Width 1600 /Length 39199 >> stream ˙Ø˙āJFIF˙ÛC   %# , #&')*)-0-(0%()(˙ÛC   (((((((((((((((((((((((((((((((((((((((((((((((((((˙Ār@"˙Ä ˙Äĩ}!1AQa"q2‘Ą#BąÁRŅđ$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™šĸŖ¤Ĩϧ¨ŠĒ˛ŗ´ĩšēÂÃÄÅÆĮČÉĘŌĶÔÕÖרŲÚáâãäåæįčéęņōķôõö÷øųú˙Ä ˙Äĩw!1AQaq"2B‘ĄąÁ #3RđbrŅ $4á%ņ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™šĸŖ¤Ĩϧ¨ŠĒ˛ŗ´ĩšēÂÃÄÅÆĮČÉĘŌĶÔÕÖרŲÚâãäåæįčéęōķôõö÷øųú˙Ú ?ųRŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š+Đ> ø8xŋÅņApØāl¤uĀĮøõ"ˇÃPxŠŠšĶΞ[ąIŲ\w€>ëŪ0Q<­ĩ–pf˜íôéũ zŊ¯ėãb"jÖe2wōããõ5ī6–ĐŲÛEokÅkĩFŠ–Ŋ„ŠS\´āŊZģ~Ÿq•ÛÜđoøg+ūƒ?÷ėđÎ:Wũ.īØ˙÷š(æ_Ëü?äƒÃ8é_ôš˙ŋcüh˙†qŌŋč1s˙~Įø×ŧŅG2ūX˙ā1˙ >p×?g9Rú6Ē’Č?‚a°ŸĄä*ņxkSđΠÖzŊŗC*úŽ }û\ÅZøĮÃsÛÉ›ØĐĩģ㜎vũķÅ)QŖˆ÷d”_F´ûÖÖô׎ģ 6†(Н-ŪŌęX$ûņąSīPׇ8Jq’ŗFģ…QREPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP_Jūɐ ąÖ§Āķ2‰ŸnĀ~UķU}5û&˙Č#Y˙}?­zYnõ?Ãúĸ'ĐôÍ^ŋ˛ø—á=ŪDŒ7opĨ,cLŽQÍv5äŋįÔāøŠā?ė( “Q’+Øĸ71ĻäP]€ä…œwÅ^mgÆ>ņngâ;Ŋ/TŌõyÍ ’ÚÜĀđ˂AÆNGĒ¤īßü…cĶ(Ŧ/[øŽåmĶÃ7Úu‰ųŒŌ]Û´ÄôÚŽsí\˙†ußŲxÚO ø°ŲŨK-§Ûlīm#1Pvē˛dāƒĐ˙:ˇ;;1XīkœøĢ]č^ÖĩM9Õ/-mĖ‘3(`GPz׈üQâ?ø‡NŅu}FˇŌŽEĒCw›<Ä\Ã N1Žŋ‡7ū"EĒÁđkÄqë÷6ˇWëjûĻļˆÄŒģ†>RN*KÅØ,wZTĪqĨYO)I`ØŒ’ š´:׏^jßtÃâQ.‹­ĩ¤s6”öî\DČH;ļķĐrq^ŗ§\­í…­Ú)Už$”)ęqúÕB|ÚGÃ˙­ãļø…­E ‡ĀôųrÚüd˙’­˙×Ã˙čF¸ĒãĖŪĒz—…Q\EQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@}5û&‘ũ‘­ûĶú×Ėĩíŗ‰"ŌüOqĻ]H;äÚ¤œ āŋÔĀĢŅË_ī%Ž.ß+?Љė{_ŠĄ•ū2xTŠF‰-īøRUsÆOlĶū&C,ž"đ EēĮŦĢ9U$(ØÜŸA]÷ĩĐá{ų“sĖ>(ę÷ÚŠ4h/ÛZˇđ¤Đ9ž} 0‘§á“į ˇû¸É=đkÁqŲÍņwN¸Đô^×KMXÅÕúJ<öŪ9˜öĮž {H$t4dúÔēw•î<ƒĮžÔĩKÄņO†5hõ(œÆˇ1éŌ‰%U?+¤‰ÔpOéPEoŦˇėéĢÃĢ%ü—-ßfKŧĩÁ€¸1‡īģŊ1^Î  ¤Ī4{+ļî8_Å$Ÿ58ŖŪVŌUB*’Äė^1ë]O†Á_é*†‚ä|‚´j–ĩŠÛčúUÖĄxáa œœdö‰âĩ…7)%ہ}Š~1~#ëx?ōđ˙úŽ.ĩ¤÷˙?Zō†bĮ,I>Ļ’˛Š˜ĨåŋVîūZ+~~cPîQEy……Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@9¤p¨Ĩ˜đ&‘TŗQ–'WÔ_žŲŲépkēė =ĖęœeBú‘ߨ~>•Ų„Âû{ĘNŅ[ŋŅy“)XđŨ ᷊ĩ¸„ļ:TÍčė0ãZ˙đĨŧm˙@ŋüzžÎ*€ 8tĩßė0ËE úˇú[ō"ōî|a˙ [Æßô ˙Į¨˙…-ãoú˙ãÕö}ũ–ū}ū/üÂīšņ‡ü)oĐ/˙¨Ž~xŌŪ‘´—e^HS“üĢíJ(ö8ų÷ø°ģî~zęš]ö•9‡Pļ’Ū@pCŒsTĢīOx?Jņn›%ļĨyĨHŽpŖzÜ÷Õņo<1sá/Üé—kū­žFėGoЏι188Æ֋Ņnžëü×Év*2žŒįhĸŠķ‹ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ čü?āŋxąĨéŗĖ?ŧ^đáœ~$˜ëÂ7öt 'O1ēãų“ĮÔövļöVÉog p@ƒ ‘ޝZ–œ"Ĩ^íŊl´ˇĢũßĐÍÍô>6˙…-ãoú˙ãÔ–ņˇũ˙ņęû>ŠÛŲa˙įßâ˙ĖW}Ό?áKxÛūøõđĨŧm˙@ŋüzžĪĸe‡˙Ÿ‹˙0ģî|^ßülĒOöY8ôoūĩrZ÷†5Bš­„öäweãë_~Õ-cJ˛ÖlžĶRļŽâ̑îcIáđĶŅÅĮÍ?Ņī÷¯Pæ‘ųëEz_Æŋ‡­āŊhIi—ĶnrŅ1íĪCî3üŊkÍ+ËÄa凟#×ŗîģ˙^†‰ŨQX (ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠŪđ-Šj^.Ō­dû’N ūu÷N¯yáûÛįšÛNĩ’s`d¤h[ļpĩņÂīų4oúøOæ+ėŋ‰ōO|U˙`ĢŋũõėĐ÷piŽōüĸdū#GÃú¤ZŪ…§ęļčņÃ{\">7(eŽüօxī‚ū$[éŋ´AoáīęV–Z|1Ü^ZZfd@n$gi$q^›ŖkÖ·aÖôŲMńą‘‘Ibr1×p ŒzŠ!QIn ´WžOņFÚĤÚ׆ŧKĨiĖâ6žŧ˛ŲdœÜä úŠŪņoŒt˙ ĨŠIÕũíûmŗŗąÍ–ã$¨Î0ÎsOÚF׸Xék ÂŪ%ļņë+k Ņ/P—N—ĖĮÎņã,¸={ķTü3âÉõŊA­gđΈ4ŦFd_ÛŒā—p'žz{ퟸž-WņՅž‘Ģę÷ÍâKɚ :ßĖ1ÆXĖIdŠœđjDšė=žžwũŦt¸Äz>¤Ēē&>¸Į˙+Øüã-;Åö÷†Ę;›[ģ)<ĢĢ;¸öMãp÷Áüywíc˙"æ‹˙]Ĩ˙Ų+ĸ›RŒû8ËđMūh]O—¨ĸŠđ ‚Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Ÿy’ĸtÜ@ĻT֟ņõûëüęé¤ä“}×đĶO‹Lđ‰(ZŲ%Ā˙hnūDU xšÛÄrëQÚÃ4GJÔ%ĶĨ2cįxú˛āôįŋ5?ƒŋäPĐŋëÂß˙E­y7€|]‡ŦøōĘßIÕõ{æņäíoæã-€ĖId‚ÎN {˜ē–ŽīÕŗ­pĸ°<âĢiO{§,ņĨh'ˇ¸M’Á ęŽ;ÅÕ~ Ic{w ^ņUäĖQŽ ąũÛíûÅr9Į8â˛sŠW޿пĸņƁ'ƒŠEđ0MÍ)SšNví+×vxÅdéŋn/n-ø/ŐC; Ëdģ1fÃ9' HŽĄcyüKlž8O ˜fûcé˙Ú"^<Ŋžao\į#=1[ĩä-×­ü=ņōŌæâ ģŠ%đߓŊ¤FYfÜš Ē=”ŸN+ĒđīÄ;-[ÄKĄ^ézļ‹ĒK–5(fu“ŗįøJ˜ÔWižĄc+öƒĶ"Ô>ŪČę –Ė$ŒúĮø~Uņ•}ģņˇūIϝūę˙:øŠŖ0_ēĻ˙Äŋ'úąÃvQEyFEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPWđģūGÍūžųŠû/âGü“ߨ*ī˙DŊ|Mā{õĶøŊņ;ŪĶôC×#ûX˙Čšĸ˙×iöJö[ N°Õõ=RŌÛËŋԌfî]ė|ß-J§ā`8=ëÁŋk V241XtŦ=3ū$Vԗ$&ßi~)Ĩø´'ē>rĸŠ+Â6 (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ šĶū>Ą˙}CO‰üšĮ%H5PiI6 û÷Áßō(h_õáo˙ĸÖ¸¯‚ņFēŸÄ9•Jū&ģFp9 6@'ĐnoĖú×IđĮR‹Tđ‹<-¸-ēDā#Č ÖŅ´=;E}AôËo!¯îžöäīfķ&ŧß18Ī Āö¯{íÛė،v8Ī…&­ņã ž œņūęĶü=â­sÆzPÕ´tm+HyG5ák‰öĄ!‹Æ *äŒãwBzėtOŌ&Ô%Ķ­ü™/îęäīfķ%n­ķŽĩsr|+đLš¤ē„žļk™_ĖpdÆÍœäĮģg^ØÅsrI$‘WGđëJŌüEđˇÄē~Ŋ{Ķîuģ×1°… oBŦ™$ ļ02zÍZ×ĩ|9ĶSVÔüIeâm*9#Šky­– ’ąû˛¤înAį<čMz §ƒü=iĄÜčĐé6ÃJš”Ī-Ģņŗ’pÄ㕁ŒVF•đŗÁzVĄ õ–…š„îĨžYU¨WrŋN8ŠörI[ëī ˜÷pE7ía$‘Ģ<>2FOTcpë‘˙f‰Š|n?âō|4=ņŠč]ŗhzsx‰uãm˙eĩûŸ{ŠŨŋnÜíûĮ9Æ}éo´M:ûXĶu[Ģo3PĶ|ΞËŊ‡—æ.×āŽ9ĒųŸ­Ås–øÛ˙$ĶW˙u|E_eūĐē¤Zw˸€–é„q§9ū_|iQ˜;RĻŋÄ˙%ú1ÃvQEyFEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP‚Aä_M| ø¯i.ƒâ „3B6Á3}ÜzéééĶŌždĨRURA‚;W^ėŒ•â÷_“^dĘ7?EQ–DWƒ# Ģ)Č#ØŌ×Â'|IĸĮåéúĨÄQõŲ¸ãō­ø[ž3˙ ŧŸ¯ø×ĄíđĪiŋš˙&Čå‘öÅņ?ü-ß˙Đ^O×üh˙…ģã?ú Éú˙Û ü˙ƒ KąöÅņ?ü-ß˙Đ^O×üj+ŸŠŪ1¸ˆÆúÄáO÷XƒüčöøoįürËąõ¯Žŧm¤ø?N–}BtkšKu?3Ų‡ųņüIuâ¯Üęw–•˛ŖĐœ~U“}u7›y;ĖüœąõĒÕˉÆFpöTUŖÕŊßų/-{ßkTcmXQEįQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE{'Ā‰Ģák“Ĩjä6á†rQēgüõü+ęŊ>ö×QĩK› 㸁úØĸž'˙…ģã?ú Éú˙đˇ|g˙Ay?_ņŖÛaŋŸđaiv>ØŦũwYĶô+ģÕnRŪĮ–Į`;šøŲū.xÍÔŠÕåúū5Ęë:ö§­JŌjw“\3u.Äæ‡‰ÃC^g/$­øŊžæ˛;/Œŋdņļ¸žSNˇ%a_oSõ˙Jķš(¯/ˆ–"|ōų.ˡõęh••‚Š(ŦQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQVôŊ>ëTžŽŌšy¨ũOĩ&ŌWeBœ”bŽŲRŠú/ÁŸŗĢÜ[GqâKà 0Ī’€äķīøWlŋŗ×ƒÂÍzOr$Åeí—DĪCû6KãœSíĢü“_‰ņõöü3׃ŋŊ}˙h˙†zđw÷¯ŋīíÛÉūæŲßôō?ų7˙"|{E}…˙ õāīī_ßÚ?ážŧũëīûûGļō‡ų‡öwũ<ūM˙ȟŅ_aÃ=x;û×ß÷öøg¯zûūūŅíŧŸáūaũ˙O#˙“ō'Į´WØ_đĪ^ūõ÷ũũ¤oŲįÁˤoŋî%éúQíŧŸáūaũ˙O#˙“ō'ĮÔWØCözđpŨ~}Ė´Ã=x;û×ß÷ömä˙ķėīúyü›˙‘>=ĸžÁŲëÂGŊ ؙ3\Ž?g‹›+Y.ŧ7vnB ų.qúŸįôŖÛ.ЃËfך8ˇÛUų¤ŋįē*{ëK‹ Š-¯"xgŒá‘†¨+dīĒ<éEŸÉY ĸŠ(QEQEQEQEQEQW4­:ëUžŽŌšy¨ũOĩ}āßŲҧļŽ^˜™†L(9įëøVs¨Ŗ§SކĨuÎŦŖŨíūoä™ķ•ö ūĪ^Ú75é=Ȓ—ūëÁßŪž˙ŋĩ>ÛÉūæoũ˙O#˙“ō'Į´WØ-û<ø9”€÷ęHę%Ĩ(ũžŧŪž˙ŋ´{o'ø˜gĶČ˙äßü‰ņíöü3׃ŋŊ}˙h˙†zđw÷¯ŋīíÛÉūæŲßôō?ų7˙"|{E}…˙ õāīī_ßÚ?ážŧũëīûûGļō‡ų‡öwũ<ūM˙ȟŅ_aÃ=x;û×ß÷öøg¯zûūūŅíŧŸáūaũ˙O#˙“ō'Į´WØ_đĪ^ūõ÷ũũĻÉû=x@Ą÷ĒŨ‰“4{o'ø˜gĶČ˙äßü‰ņũôŽg››Y.ŧ9vnB ų,qúŸįô¯ŊĩžĘæK{¸ž)Ŗ8da‚*áQKN§># R‚æzÅõ[Āų؂Š(Ģ9BŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ŠéŧāŊ[Æ:ˆĩŌ bŖīĘGĘĩôû8é1@§Yŋ–i{Ŧ|cņãųVRĒĸėĩ;hā*T<šŠ{_¯ĸIŋŦ|¯E}…˙ õāīī_ßÚ?ážŧũëīûûKÛy?ÃüÍŗŋéäōoūDøöŠû ūëÁßŪž˙ŋ´Ã=x;û×ß÷ömä˙ķėīúyü›˙‘>=ĸžÂ˙†zđw÷¯ŋīíđĪ^ūõ÷ũũŖÛy?ÃüÃû;ūžG˙&˙äOh¯°ŋážŧũëīûûGü3׃ŋŊ}˙höŪOđ˙0ūÎ˙§‘˙ÉŋųãÚ+ėũž|J÷ã„ŖŸŌ—ūëÁßŪž˙ŋ´{o'ø˜gĶČ˙äßü‰ņíöü3׃ŋŊ}˙j ßŲßÂŌBVÖ{¸¤ėÎÛŋ¨ŖÛy?ÃüÃû;ūžG˙&˙äO‘(¯Xø•đ_XđœMydMõˆ=Pe‡ų˙9¯'Ģ„Ôö9+áĒaÚS[ėú0ĸŠ*ĖŠ( Š( Š( Š( Š( Š+§đ7‚ĩj"ÛJŠŋ1_•iJJ*ėŌ•֗%5vsWÕ:ėå¤CfúYĨîąņĮå[?đĪ^ūõ÷ũũŦŊˇ“;ŋŗZŪ¤ō>=ĸžÂ˙†zđw÷¯ŋīí!ũž|X÷ã„ŖŸŌmä˙ķėīúyü›˙‘>>ĸžÂ˙†zđw÷¯ŋīíđĪ^ūõ÷ũũŖÛy?ÃüÃû;ūžG˙&˙äOh¯°ŋážŧũëīûûGü3׃ŋŊ}˙höŪOđ˙0ūÎ˙§‘˙ÉŋųãÚ+ė/øg¯zûūūŅ˙ õāīī_ßÚ=ˇ“ü?Ė?ŗŋéäōoūDøöŠû ūëÁßŪž˙ŋ´Ã=x;û×ß÷ömä˙ķėīúyü›˙‘>=ĸžēŊũŧ/$%m'ģ†LpÎÛšüÅxįįƒĮ„ŖkģBoŦûČ2Ãü˙œĐĢ.ĒÄË-ŠkĶ’—’Ŋūæ•ūW<ĸŠ(­<(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ+ę¯Ų‹Á6öš!ņ äJ÷Sļ!,>čĮ_×ķ'ĐWĘĩ÷×ÂØ–/‡ú"Æ0ŋg…mZGŠ—.XÎĸßE÷Ū˙‚ˇĖꨎ'ÁšîŖŠx÷ĮZmíĮ™eĻOj–‘ųjžXx‹0Č9#š5ÛT5cx˙]QAAEs_üPž đfŖŽ4᭕BD;0UÉôÉöĒ>Ō|koŠGwâoÚ^[:.Ÿ ‚Fą1Ä)ãœįíԇ-yQŲŅEp˙ uŨK]˙„ˇûVãĪûˆ.ėm˙vОÛĩ~P3ŒžNOŊ+ ĘÍ#¸ĸŠ(((ĸŠ(Ž k牸ØÚÚ?âPšŽ8jšģę}&Ôę{%´t_/ķß՝ĨQTdQ\×Äoø:˙ZææHB¤Qg¤v š=†HĪĩm%vt´WáM'Ɩڒ]ø—Ä֗ļ΄ɧç¤kž$q xį9ŽĘ†(ģĢØ(Žá^ģŠkąxŦęˇyąņŨŋîÕ6B›6¯Ęq“ÉÉ÷Žâ†Ŧ—2ē (ĸ‚‚Š+‡]ԏÆŲtq˙…đ˙ۄZ˙Žû@MÛąģîņŒãڄŽLĨc¸¯œ?j?ÛĮk‰,!T|„œ(ëĪ_×?õ¯Ŗë€øëËđĪUWÔTÉÛŪėoF>ŅēOi+“ų=O†¨ĸŠė>t(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ*ŅŦ—×Öö°˙Ŧ™Ö5öÉÆjŊu ŖY|y¤+Œ8qQR\ąmJJĩxS–ÍĨøŸf|2𝧄ŧ-iioŦîĨ|rIįįŊuĩCÄw2ŲčĨÍģlž Ye°Ppxę+/áž§wŦø AÔĩ)|ëÛĢHåšMĄw1œüs¨ŲĩJžŌwחČččĸŠb (ŽĮzîŗ˙ “á_ Ë žŖ¨E%ˎķÆ$[hS•CÃ9'Ąãú \™K•\î¨ŦO Økz}„‘xZXš2e'KEˇ!q÷J¯œķÅUø•ŠŪhž×ĩ-2o&öÖÕå†MĄļ°čpƒøŠ,ÖWgKEQĐn$ģĐôë‰ÛtĶ[E#ļËœsW¨w (ĸ…Ŗ­q 5ŊC^đõíÖ­qįĪĨunąSŖáFĀī֋åf‘×ÜÁÍŧ\F˛C *ĘŨ¯‰ū:ø=<'ã9’ŲqisûČĀ3ū<×ەķíyėŅ%ĮÎI\û|Ԗ’LšŽzƒíuęŋā]5ŅEÖx!EPEPEPEPEP6ŌKũBÚŌõ“HąlœfžđøkáKO x^ŌÎÚ%Y™JĀrIįįŊ|gđĻ5“ĮÚ@q‘į?Jû—ÅSXøoWģĩ}—ösK`Ŧ¨H8<EsTÖvė{8EėđÎky6ŸĸKOŊūŌĸšß‡Z•ŪąāMQÔeķ¯.Ŧã–i6…ÜÅrN~ē*E§upĸŠ(Q\'ŽĩŨiŧM¤øSÂōÁg¨_Á%Ôڄņ‰´(@Ę!᜞9ā{öč|'c­éöEâ=f=bäɔ-Vß ō•^9įŠ,B•Ũ‘ĩEs?uKÍĀîĨĻMä^ÚÚ´Éą[k s†ÄV֋4—:5„ķ6éeˇŽGlc,Tx÷ĸŨGÍ­‹”QERŽĸ€ŖēˇŠęŪH.#Y!‘J˛ˇB+øO­ę˙†nnõk>á5 ˜ļ*aBa@ũkŗĄŽŒPí$|AņË á/ΏÅĨĮī#āgúūy¯:¯¤?kČÔIĸHÎr¤ûs_7Ö´_ģnĮcĢs/´“ųŊ˙ĸŠ+S„(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ+ī߆_ō!hŸõîĩđ}ûđăā?čëXUø—Īô=\đjzĮō‘Î|?uâwÅsĩâɘúW/ċīÍsy§øĶÞĶŌVŽÖÚî5šiŅIcîa´Ā•Õø&ÂcņâgÚ š+{šm9WCTž=Ģ“đ˛iū°øĮÁŌ^=ŦŽļzŽ”ˇkw bTąUČaœh!ŪŪZūguā_xģÂzƒÛ˧ nĘY-hŗ%ĢČ)"āäŖ§īŠÁņ<ßüŖĪ⠝MÖŦŦŠÉseũž-ËE‘¸Ģ†Č`:^•6ŗĒę?đ­ĩ‹˙xnëG™eVŽ1i3MÛæJą˙{n@ĮN•ÂøÁ´]_Á:Ēhžņ~§ŦInÚ58§vƒžXlĀPcڄ‡9iž§KûHŽŖ}đžMBËPHt‚ŧöog˜´ˆP‡ę¸ĪAÖŊ'–ũ„7+â]j ZWpbx­ÜF¸ä<÷ŽãE•ÕßĀ™­mm§ščÃf1ÆYō<üŖž0k՛ī­&ô4Šũã~Kõ đ‡VŪ.ŋÔüymáÍNĮH˛Ä—Ž×3Z}ĸId,@ §<ŸšŊÚŧīāխůü&˙j‚X|ßŪ˘…wĄŲ†ę­%°æ¯$^øQâMOÄ:NĢŧ :ž“ŠMĻO, ĩ%hņķ“ŽĩĢŖüB¸ŧšŸNņN•mæ0Z 00ÚĘŦėÄäŒdú“ŠĨđ’ÖōŅž -åŠI|I},jˇk õSëŌ¸Īę_ÃŗĮzŒ5?,ޞŲųR›MáˆQŠÆc“ß&ĒÚčgÍî¤ÎšO‰Z”î|K%…ēkVŗĸ$˜a Œŋ;9ŨŒûgŊhxnÛÆW—6ˇ_đčēdyĄˇĶІByÕ˛2ŸĘąū<úÂģų/|9(:•Ám1aŨ œ pĀĖ+’Öė´[ÛÍ>O†×4Ÿ%Ė~\âÉí`HķķųģžR¸ė9āvā–3˛lčüVÚáũĄ­íü4ö°]ÜøpF÷71™Ū?´3ÚÉĘĒ€N>jŲŅõŸh´¯x§PŗÖ,õ{y¤ļē†Ü@ņÛxģPŊņÍŋ‡5;"Æ?^;\Íiö‰% HPU9äŨĢĐ>ø“SņŠĮ¯,ÚzVĨ>™q$ ĩ%hņķ“ŽŋĨQø/kqi>Õ°ųž'Ŋ–?1 īCåá†zƒŽĩÂK[ÛHŧ|ZÚHĻ—Ä—ŌĀ%Bĸ@víaœeIīŌ­ęaM5o™sUŅū!Īws>â*Ū-Ė`´`eÚĘŦėÄäŒdú“ŠÆ—âVĸŸåņ1ąļX‚o°Í“ä$ÂO-œāį`<ã>Ųī\·õK{øøįBņ†Šâe•Ö[FŠSiæ@Ē‘\tc“îk{ásĪĄ|&ē{˙ßĖĄqŋLXwH#i?ēßxΏQbTŽô} Ÿ ÛxĘîęÖëū­S˛ <6úzaž@ulŒ€@'ōŽsÅ\oÚ; IkŨĮ‡r\ÜÆdX#ûAbáA9UP ĮÍ\ö­eĸŪę:dŋ ŧ1Žé>%K¨ĘÎ,ŪÖˆŸÍŨō•ĮaĪč};KøhiŽūĪ7Ų?áō„ÛÍ˙jnî™Į8Ŗ`Ũ[ÍhšĮ‹4/ˆēw‡Ņˆ–+‘ȍĖ0EPɡžäqNÄ'dîī÷š:fĩâŸxķGđ÷‹/­5{]n)šŌî anņÉîeem#ŸzæüMĨø˛_úzYø’Ö æ°š’ŌFĶÕÅŧ`ũŅųĪO˜úU=:ŌÎį⡁.ü;áfĮN€Ūy÷×°H ĖА7%˛vÆKwŽīVļž7ø~å`”ÛĻ‘r(CąX¸Ā' >ÔlךÚéQ]AĻZŨܭÕęDĢ4ëA#Ë2yÅs˙ä–xŖūŧdŽÆš/‹pKsđĪĐÛE$ĶId摯Ĩ™ ­JÜŪ 8‹ßˆēOØŋŅ᡹˛ņ7…īõ9âyĐŲ}Ĩ"~›H08ȧbSné=45<gâ[yd›^ņŽĩg"~čÛŲ,%[=w+GQŒW—ü,ƒÆzž—­Á j–.ŸoĢŨš[?´IpėäˇRĒđ8ä’}iü-ąŠˆ7WŌõ/ÂohEÔWŅ´QŊÎėŠ‰æįWMđRÚ{_ j u°ģj÷ŽD*J™2cëCĐq\Í|Ë €×=sņ[ŌŦŽ­/Žü#Ģ<@K6*n_âH¤Ũ¸°Æz+V÷ÂZ‡ˆ~øbÎÎ篛­ė÷)…ycR<ˇVõ Þ3REâ ˜–)ūßĻĢ¯fš0tI€¤gøŗŠ­ 6ŠöĐî5Ŗ­ëē>Ÿsá ZĶNŽæ5œĪqiįąFPWjäל暭+ZņO‡|{¤xsŗļšĩļĩ Így¸ã’%ÜčĘÚG>Ļŗ~ jZĨ–Ŋáø¯ėuģ_K` Ņh`‰ŖēˆÜĮķˆÕBˇo=ČŦ-.ŌÎãâ΁nü;áfÃM€^ų÷×°H ĖА7%˛vÆKwĄ- ”Ŋí7ĶúągÄZW‹døáf–ž%ĩ†â]:æ[IžŦ ˇķxˆŒüĮ§Íí^ĪĨÅu›kĄpˇW‰ŦĶǏ32rqÚ¸NÚvøåĸ\Ŧ›dŅgF”!ØČēgÚģú–ö.œlŲÆ|g˙’Uâúōoæ+ˆŊžø¤|<‹ÄéŠéÃie˙ŲȰRÁ‹mÁ8Į9ąŽīâü\ü1ņ$ŅI4ŌZ2¤qŠfc‘Ā“Tŧso<ßu+xa–K†Ō,J„šo,qŽšöĻž„Í^Mų<—ēŸ†ŖšŅŪ kÛĢt–¸C"FXČgúםx§Qņ߀´ĪøHĩ}sO×4‹yŖ[ËU°îąģ„ÜŒ$Ķņ­ÖŋĻü)Ōeđė7fæ8íÚÛ 7 o°yžXoãč:dņ^eņ=#Wđ§†ü3âÛũUüou(g‘áQ"įæ‘‰9ÁQŽIâœPĒOM7ąé_|WĢ[xÃFđŪ¨éÚ(žĩ{§ÔoĶxāāG’ļpN{~GŖđeŸ‰mä’]ÄV:Õ¤‰û“of°•lõÜŦAEe|BÔ4h’ÎËÄŪŋÖ4æŒH'†Ëí)ôÚ@ųĀÎEr˙ ŦcƒâåĮ„4ŊcKđ‹Úbæ+ôhŖk­ŲS?#å'8â—AŪĶîe|)ˇņĻŠĸęĐč:Ĩ†‹§[j—A%–Īí\;HKH ĢĀä’sĐWĄ|+ņ¯­Xęö~#[sĢiĪc<Öã 1ĀvāÕ‚VĶÚøBî;¨%†CĒ]¸YŠ*e$Ɠám´öúīZx%‰f×$’2čT:ė_™sÔ{ŠQSMrž]û^ôĐž­ũkæĘúKöŊ#ū$c<‚Ü~uķmUŸŠžcüH˙…Q[xQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEWŲŗwŠaÖ|œōĩŲ…O]¸ūŋÆž7­˙øĢQđŽŗĄĻHU”üɜ•X9+­ŅŨÄF”œ*|2üGũtlũĨ¯"đ_Į? ë6ŅĻŠ(°ŧÆ?BĪĻkˇ_xe”Ŧ[āķ÷[ü+Ÿž+}YaĒËX.eŨjŋĨĨÍs_đœøgūƒß-ūÂsáŸú A˙|ˇøQí!Ũęxų÷/š-%sđœøgūƒß-ūÂsáŸú A˙|ˇøQí!ŨÔņķî_s:J+š>:đĀbÜ܆˙ Q㯠‘‘Ŧ[ãũÖ˙ =¤; úĻ#ū}ŋš%-s_đœøgūƒß-ūÂsáŸú A˙|ˇøQí!ŨÔņķî_s:J\×5˙ Άč1ũōßáGü'>˙ Ä÷Ë…ŌĐ}O˙>å÷3¤ĸš§ņ߆KbßŲŋ¸üxđū‘m$z#‹ûĖ Ÿ•OųõŖ=ĩÃT†ĩ*îôüĘŋĩŠbĶü(4Xdjģ#rŽpž‡đĪé_$ÖĮŠŧCâm^mGS•¤™É ŖĐV=tԃŠģŨžF6ŧjÍFŸÃeįŨ˙],QEhqQ@Q@Q@Q@Q@eūÎ>)‡[đDV û]‘ØTõÛĮõįčEzÍ~}ø+Åz„5ˆõ 2B}äÎ ú§ÁŸü7­[ƚœÂÂī*ũ ˙>™ŽGgŖØ÷éÔX´Ĩīõ]oŨwŋ–ˇō=r’šĄã¯ Ö-đyû­ūŋđœøgūƒß-ū>ŌҎÔņķî_s:\Ņ\×ü'>˙ Ä÷Ë…đœøgūƒß-ū{HwAõЈ˙Ÿrû™ŌŅ\Øņ׆HČÖ-˙ī–˙ ?á9đĪũ ˙ž[ü(öîƒęxų÷/š.i+›˙„įÃ?ôƒūųođŖūŸ ˙Đbûåŋič>§ˆ˙Ÿrû™Ōæ’šŋøN|3˙Aˆ?ī–˙ Gņ߆K6ąoėßáG´‡púž#ū}ŋš-xwíEâ˜l|.ē$2utAp;/ŋáŸĖUĪ|yĐ4›i"ĐÜ_Ũā€TüĒĪ­|¯â_ø—W›QÔå2M!␪ĐUF.oČÂĩHábÛ~ûVKĩôģí䡚‘EWYā…Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@_ĐoÛKÖlīW?¸”1Į§Ķ5BŠM&Ŧ˧RTæ§ĶšúáMjh6zŦÖT°z7ņ­jøŸá/ÅK˙Ü}ž`×:cŸš"~īĶü˙…}3áī‹~Ö VMI ”Žc“’?.Jäw†’=ø¨â=úß§UåmÚķ_ž‡ fŠæŋá9đĪũ ˙ž[ü(˙„įÃ?ôƒūųođĨí!Ũõ§ˆ˙Ÿrû™ŌŌW7˙ Άč1ũōßáUī~"xVÎ,ÚÄGąĖ =¤{‡Õ+­āūæu„ņÉâž<ũĨŧSŊãŗŗI’ėČéŸōIüEu˙ž=Eqi.áE8•î ę1ūz~}Ģį9å’yžYžG;™˜ä“ZS‹“æ{#Z4iēQw”ˇˇEžũßāŧöŽŠ(Ž“Æ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€4|=¨ļ“ŽXß)#ȕ\ãŽ;ū™¯ŋŧ/ŦÁ¯čVšĢ‡IƒŅģ×į…zoÂOЎūŸė͆šŌßŦdũß§ų˙ ÂŦų‘éākÃ•Đ¨í­Ķé~Šúé¯KvÔû^—5Āxâׄu¨U“RŽ æ9:ËŸŌļŋá9đĪũ ˙ž[ü+xõgĸ°ĩžąƒkÉ]}ëCĨĸš¯øN|3˙Aˆ?ī–˙ ?á9đĪũ ˙ž[ü(öî‡õ§ˆ˙Ÿrû™ŌPNIāW'}ņÂļPfÖ Úzüø§ņę;ĢI´ß +mpUîõ˙=?>ÔÔšžI•KZŪęķßäˇgûIøĻ{ÆbÖŌ@ööKŗ#ĻÎOâ+ČiķK$ōŧŗ;<Žw31É&™]4ãÉ&*ŋˇĒ敖ËŅh‚Š(Ģ9Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š+oÂŪÖšKĪîä/í ßū/ō< Š÷ßøfoĐVĶūũŸņǎėáâËHL–wWx+–B~œéG×)ws˙!ũ~‡w÷?ōTĪŠč+Õô¯ŲŋŗP‰/.l­I –r>ŧ ÂxŠtß,žŋäsÔÅŌ¤ųe-{-_܏ĸŊ÷ū›Äô´˙ŋgüh˙†fņũm?īŲ˙ŽRķûŸųhPî˙đ‘āTWž˙Ã3xƒū‚ļŸ÷ė˙đĖŪ ˙ ­§ũû?ãG×)yũĪüƒûB‡w˙€ËüĸŊ÷ū›Äô´˙ŋgüh˙†fņũm?īŲ˙>šKĪîäÚ;ŋü_äxīŋđĖŪ ˙ ­§ũû?ãGü37ˆ?č+i˙~ĪøŅõĘ^s˙ ūĐĄŨ˙ā2˙#¯}˙†fņũm?īŲ˙k~ĖŪ# ģu;"šų‰B0=šæŽRķûŸųëô;ŋü_äx%īŋđĖŪ ˙ ­§ũû?ãGü37ˆ?č+i˙~ĪøŅõĘ^s˙!hPî˙đ‘āTWž˙Ã3xƒū‚ļŸ÷ė˙đĖŪ ˙ ­§ũû?ãG×)yũĪüƒûB‡w˙€ËüĸŊ÷ū›Äô´˙ŋgüh˙†fņũm?īŲ˙>šKĪîäÚ;ŋü_äxīŋđĖŪ ˙ ­§ũû?ãGü37ˆ?č+i˙~ĪøŅõĘ^s˙ ūĐĄŨ˙ā2˙#¯}˙†fņũm?īŲ˙dßŗ?ˆÖ61jVnãĸ” Į&ŽRîūįūCúũī˙—ų Ew>3øYâ¯ÄĶjve˛ä´Öäē¨ĪãßÃVôęÂĸŧΊUĄU^áEUšQ@VΆ|5ĢøšôZčļRÜɐ”|̟SĐTĘq‚æ“˛&s8ķMŲÔW¸iŗ‹.Ą^\ŲZ’2r>ŧ ģ˙ Íâú Úßŗū5ĪõĘ]ßÜ˙Čåúũīî—ųE{īü37ˆ?č+i˙~ĪøŅ˙ Íâú Úßŗū4}r—ŸÜ˙Č_Ú;ŋü_äxīŋđĖŪ ˙ ­§ũû?ãGü37ˆ?č+i˙~ĪøŅõĘ^s˙ ūĐĄŨ˙ā2˙#¯}˙†fņũm?īŲ˙?á™ŧA˙A[OûöƏŽRķûŸųö…ī˙—ųE{īü37ˆ?č+i˙~ĪøŅ˙ Íâú Úßŗū4}r—ŸÜ˙Č?´(wø ŋČđ*+ŪÛöfņåÛŠŲĪĖJGĶžißđĖŪ ˙ ­§ũû?ãG×)yũĪü‡õúßū/ō< Š÷ßøfoĐVĶūũŸņŖū›Äô´˙ŋgühúå/?š˙ŋ´(wø ŋČđ*+ßá™ŧA˙A[OûöƏøfoĐVĶūũŸņŖë”ŧūįūAũĄCģ˙ĀeūGQ^û˙ Íâú Úßŗū4Ã3xƒū‚ļŸ÷ė˙\Ĩį÷?ōí ßū/ō< Š÷ßøfoĐVĶūũŸņŖū›Äô´˙ŋgühúå/?š˙hPî˙đ‘āTWŊMû3ø‘ccĨfî:)BürkĪ|gđģÅ^ĨÕ,<Ëeåώ%ÔSĀ8÷ÅTqt¤í{zĻŋ2ãĄ'nkzĻŋ4ŽŠ(ރ¨(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠčŧá{ŸxĻĪHĩÜ­™\¸ƒŠūƒÜŠû×Á^Ķ<#ĸÛéú]ēDą¨ ĀrĮšĪõī_9~ĮzlRëzÕû€d…Û;ŗũ+ęÚķ*ŋiUßhčží˙Þ=y{ZîûGEëkˇë­ŋáŠ( AEŒ|Gd-Đnŧ3âÍ&÷™mßąËØãÜWߞņD^.đúę m%ÄsImuk'-ČpČO|qČõ¯˜˙kŊ6_iבŒIsnĘøī´‚?ô3EéVIm/Ī{ūPnu´´kÎ×ŋāũt<Š(¯HõŠ( Š( Š( Š( Š( Š( ēO‡ŪšņŠŦô‹mĘ$mŌČ?SõčšÍ×ŅßąŪšŽĩ~ã2Ĩ‹íÁ˙įÄÔpĻÜwv_~‡.2ŦŠŌn;ģ/ŊÚ˙#č¯xWLđ–o§éV鯠–=ίzßĸŠâŒTU‘įB Č(ĸŠĸ‚ŠÅ˙„ŽËū!áŗ˙hũ€ę;ļ/ĘņœįvOLtī[TX°QEQE#2qĀõ ŽSâ‚tĪhsŲj#JA1ˏ™[}k˜9ņ}ŋŒ<1Ĩk~˛Ōmu{™ĸ.×ĸæBŠnaTũŪI=ø¯RĨRÕ˜ĒŌē´ÍīhW>ņæ“z?}lûwc‡Pqî+&Ŋ×ö¸ĶbĩņՍÜ`šˇ!ąßi?øųüĢÂ̎QÔĻĨ-˙ËCŌÂUui)K}ŸĒvQEšĐQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEšāŋ\ø§ÄļZE !î āgbŽI˙=ņXuī˙˛›ū-Ô¯¤P^UP÷É?Č~U†&Ŗ§M¸īˇŪėsbęēTœŖž‰|Ũŋé/ø;Lđn‡oaĻÛĸ2(ŪøÉfĮ$Ÿ_zé¨ĸ¸cdy°‚‚˛ (ĸ¨ ĸŠĪŠØÁŠÛiĶ]—÷*Ī ģ8"¯Ū*;ŪŽPEPEPErΝë°Iwáí+Ë,K‹¯!î@ā˜—iā@,TŖ‚ gYk6Ÿü ĢÛi7e͉,nRXĮ›i(ᕗ8?ä|ŦŽWÔél|EĸęíecĒØÜ]¨É†)՟@yĢwš•”ąGyyoo$šōÖY ãŽ2yę+Î>1Ú=ö‹á˙i_žņCŨÁ-Œą¨VļXˆķ.*Č=‰`zVߎ|7ˆĸ– Ė(ĸŠ(ĸąuMbâ=Cû7H˛ē€ŒM ’_*(Pä)wÁ9b¤1ÚÉĨøwÃú@ķŧN×°Mg,j­R"<ˆ…@>R;—TŖ­™JŲžw¨ŲYKW—–öōJ E–UBøëŒžzŠ[+û;ã/ØŽ šō›lžLö63ƒŽ‡Ģ’ņĮ† ņˆ´ŋˇčÚŜVĶôĻ NZ2 <œpáXúŋŠŦü1ā[čēlZNĨáûøō‰Lƒ÷rŽ1š =x<Šo°(]išŪÍ­épj+a6Ŗh—Ŧ@4Ē' ĮŠô­ō4k-3áf–Úū› đĶ­­ÅŨÚ\˛Ü4Žčßh•@††o˜œG[RAS•# úŌj”l-QHĸŠ(¨níĄģˇxncY"a‚Ŧ+;Äô,LĶZßܸĨŲklŌ|Ģ×-÷Wņ" đ7ˆŖņg…l5¸mŪŪ+Å.ą;Tn dŽüPãuĒĐ9ŖĒĐų;ö‹øk„utÕt˜öéwlw"Ž"čķú׋×Ũ?´f˙­YĨPZŨ<Õ>„sũ+ájčÁÉō¸>Ÿ—õĄ×€›åtåö^žŸÖEWYÜQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE}3û}˙}cūFŊ[ãG‚ėô›K{ˆ,īuyÚ$Ŋ¸BņÚFģ|ÉJŽX€Ã ܚōŸØÛëō5ëßü5ĒjRč!đÔQ\kšÃÍ ŦŽ.b–<ž ŌŧØÛÛNũ˙Dyˇˇûūˆķ­7âmމ¯ißâ ņMŨŌZŪZ\Ød‰\āK*ēHƜätæēßę>)ŋø×'†t]`éúkčbæWō’CyĨ|ÄĢ•yȓŽõĨcãm[Qŧ‚ŌĪáŪģÍ"$˛ß$Pà –Ÿy?>Ņ’åąŽ3NˇĶ/‡íwĒ;Ļˇ‡Ü]>Y—íļnéģâĩvėlÚŊíĐĨā­KÄ:GÅMGÁÚî°uģCĨ.Ģmw,KŠûŅB`Œ’ë\­˙‡5ÛÚ k8|c¨[Îú ŨĮr–ņŠ&ēĮ*89<ņ]´zeđũĄeÕ ĮöiđĪŲ…×–|¯7íAļnéģãŽ++ÆMŦxwãE—‰í|;Šë:\úĶû=ŧR Ė™#ĶîŽHę})'Ž=tę‹õ}sÁ´iŦ5 ›ũJ=FÚ eÚĢ%ڒېŒ7`X“@ņíŪ‰Ģ_]ø ÛjķÚą´Ķė AŦŧ2ė 9ãa>„ž¸¨~%GĢx—ÁŪž= ūÚčk–—Ų°Y%†5åÛa `žxĪ8¯P=jodˆråHķß |AŠãá ņ^Š…¸˛ĩqz˜ĮúD+.;np0?ÚZÕøTuš| ĻŨøĸéî5[Õ7RPžRŋ+Ņqī^Yâ ]§Åüh#˙„sÄwë×1ÄI|õ+ĶëüGīā(˛Zu Ų-:œ˙ÄWhžxĸD;]tĢĻЈZ¨|"øWá5‰é°1 02P~¤’OšŽĻūŌ+û ›;•ŨÄM ‹ęŦ#ō5äžÖüMđ˙A‹ÃūÖĩÉ4đŅY_éá^ˆrL{É9Œ… ƒŒwâ…Ŧl…Í#Wá,i‹~$C ,qŽ´*Œ ´JIüMx×í˙#.…˙\$˙Ų+Üūx[Rđæ…¨\ø€FšŪ¯{%ũÜqžõˆˇŨ@zO^ø¯ ũąŋäeĐŋ넟û%'ühz˙íŦWž"ģ˙íŦųæŠ(¯Dõ‚Š( Š( Š( Š( Š( Š( žœũēk˙ī'ō¯˜ëéĪØÛĻŋūō*äÆ˙ z¯ĖáĖ?„ŊWæzŸÆZiv÷0XŪjķ4K}q’;H“o™)QËa{“\—ņ6×EņöoˆcÅVw—Ikwkqba’s,lĒ:2§99¯Dø­áÍWP¸ĐY—í!ļnéģ8ôŖK[Č.­o#Šģđæģqû@ÜŲCãBŪvĐí.’Ū"ŅÄ×@y‚ āäķÅuusÁ^ĐfĶīîoõÔí­æ“jŦ—ŠwnB0@-€8_ÅíŦxsãM¯‰­ŧ;Šë:]ƆtÆ:zx¤ų™#Ķ€9#ŠôĢ_ŖÕŧKá? Ī…mtēõÄÖlI!‰]˛ī°0OšĐĩ{ëŋ›}f{V6–0 ‚ÚPC*ī`Yú$öbzâŦx{â3ü˙„ģR*ŗÚZ0ģ@1ū’Ÿ+.;eņūĐúץžĩķūŊá{•øĨ7‚íŖŒøo_ŧ‹Ä71€6ƑgÍBŊ0ō,cņqSKrcičĪTø[ũĩ'´Ë¯Ũ=ÆĢxŸj—z…ōÃüʀE z×YH ĩ›ww2nîį™üJ˙’đŋūŋo?ô@¯L¯ ņĮˆõ}GĮ^Ô-|â§ˇĐ¯.ZfYķ•“`1ķČČĪ8âŊoÂÚ˚ö’/fŌĩ)˲}›P‹Ë”cž2x=Ē䝑¤âÔQķí‹˙#F‹˙\$˙Ų+įēúöÅ˙‘ŖE˙Žė•ķŨmƒūÍūlčĀį/ũ)…Q]G`QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEWŅßąĪü…ĩŋ÷ú×Î5ôwės˙!moũÄūĩɍūÍ~háĖ?ƒķū”~ø‡ã𕅠†ÛíēļĄ0ļą´ķ d=ŲŨP9'ük•‡ÆŪ,Đo럯–Ū—Hģ¸Kcq¤\š{VrW'rä€Júæ øõáņ}wá]~ëJmgJŅî%]BÅģ4Rā *TåšÄŗo‚][.‘§YŪj "ų6öļĶ´ģķÆF>\w'¨Š\ģÆ1åÚįWâ/ø‚ÛℾĐôĢ+Į“K[¸$Ú5ŠB䕁9@ŨUÜI rá_xŽGáoéēTs]ZŊ՝æ˜īåžĖnRŽIdķĮN‡4Ë?áŖĩQßū¸ŋôĸŸâ/ų/^˙°]īūËE–Öč[[ĄÆøÅŧ˙ ĢÃŋgO ąīönņ>Ãī; øÆ6āg5ÜxīÅ>$đoÂ;ÍP‡H—Ä6ž^øáI Šß2§°oēŪŊ*Įø­Xøgâ߁õnSkĨĨĩärP”Ge] ‘ëLøķŦXë˙5ÍGJ•Ļŗ• ČcdŨļę5$ã āãžŖŠ{ōč?‹’ëOø&­—‰üqŽiē–ąŖčzmŽ“ö7—LM@ģÜŨH6˛ŗ"° .đ;ŗƒĶ¯AāÛx‹Ā6^$œĮ›s%ÚŠâ~ņyė={bēˆÕc5 ŠUQ€ ¯ž|UĻßhš÷ˆ~iBXmĸŠ+Ô=ĸŠ(éØįūB:ßûĢü{×Ä//…,lÖŪØ^ęúŒÂÚÆĐČ#?RYŨP9'ükÁcŸųëî¯ō5é?ŧ>ˇ—Ūņæ”úΑ¤O*j(…ŲĄ˜*™´¨8Čķ"“­;÷ũãÅ'^ĸ}˙öÔYˇņˇŠô-FÄøŌÛÃré7— mö"åËÚģœ!‘\ĘI#ÍX×üqâ>'ÜxCBŌŦ¯$}1níåÚ5‰ËZVå Ģ’OP2G+b˙dŊ´]OŗŧÔZTō`ĩļĨž ųqܜcÕéŋōq:ׯü#Đčã[4ģ´ˇ°ī øģÅQxí|-ã7KŠâæŅŽí/4Æ*@¤RŽIg¯=ëŒņc|@˙…Õ ũ<0o­įö~ņ?–`ČĪ›†Îücxë]žŊ˙%÷Ÿöŧ˙Жŗ>%ëvøŊāÍc\”ÚébÎōŨŽY Ev €HPˇŅtīĸŨ=ņW‰<đ’ë^ŋ‡H—Ä6ŪX’8’CjKĖŠĀ,îˇ¯_ĘĄĩņ7Žu3SÖ4MąŌ…›ËĻG~]îndJŗ"° ŒģĀŨœr>=ëZ÷ĀgQŌåiŦæxr4l›ļÜĸ’qpqČäq^ĀǍĄQBnj•+؍nÖˇ˙#—đ_Œ-Ŋx÷:uĨĶ” ÎĮ~ PŲĪLā{ÕŨ7Kø‘™f´đÄ7ŦĢyŠGw3\mÎ PS('jôu^ņžãGÔ.4 ÖãIĩ1²yL„Ë‚Ė0ØāŊkŦĻäĶÕ)´ŨŅÍj:}ũ§‹Nģh.īĄ{!flRä"FC–ķ1 XäIã9ŦHŧ &ŗ7‹ī|BV ŧCnļBŪßöx\ˇBų$œp8=kĐ(Šæh…65Öü9¯xƒáü^ ŊĩŠŨZ8mnĩ%™J4Q•%ãOŊš‚ô`äōqΤD‚8Ņ8PgڝE Ü›î¨2ėz“ŠoŸüõūúŸ¯xI×âŠ-j ԉ‹F%\í$`‘XßđŽ<˙Bí‡ũņ˙ץ[¨+u:Ĩ–6 ,ˆIėSë˜ŗđ…lŽáē´ĐŦb¸…ÃĮ"§*ÃĄĶŌvč'n†wˆ˙ä^Õ?ëŌ_ũ×đūI†˙ë˙Ѝtž4Õtí3Ãڏö•ũ­ĻûYvyķ*nųHã'žHüëũžĩm:…ū˛†ūŌKČáexe2) Is‘Į5i{…Ĩî?SCãĪü’˙×ģ#_×ß䓸‡ūŊÛųø¯ ņKäkø§ō (ĸģO@(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠúöCÖbŗņF§ĻĘÁMÔjé“ŨrķõŊ~mxsYēđūšgĒiíļæŲ÷¯Ą>Ä+î?…ßôoi12\G ŠĐHĀ2ˇųīĐū•æW˛¨äö—_=Ŧxø˜ûŽRøe×ŗĩ­øiķ= Š##‘íF(QF(ÅŸâĒ čč jÚ P`[ĸDLr2ŽFFFkCb€GājĐjúŸˆüVöŽ_"[ŦV™1ZĀ™!žI%‰$ûzWiF(Å ÜmŨÜ(ŖeˆÔŌWÆŋĩ~ˇĨãë{82ŲA† ˙Ÿ’ƒø×ž|[ø¯¤x/J–8gKVE"(#`N}O ÷ūfž"Õõ_Sšŋž“Ėš¸rîŪūƒØtĒïkQMlŋ=ŋĖŦ,}ĩU5đĮ¯wļžŠ÷˙‡)ŅEé¸QEQEQEQEQEQEWвĩ¯ˆõM2VUk˜ÕĶ'Ž2ķ|ųZžÖîü;ŽŲęē{m¸ļ}Ã҇Bą†"›ŠM¨īŋŨŠĪ‹¤ęŌqŽú5ęĪŌ:+Īū|NŅ|mĨDņ\Į ú¨A#ĘÎyčkĐ##‘í\š—ŠåÂj~ĢuÕbŒU–QŠ1@Ū!: ŅŽO‡ÖÕĩ@nÉ1ČČb9õÅsžđöĢoĒę~"ņSÚ>Ŋ~ŠŽĶ&+h%cRy$–bOĶŌģ˛g‡ųƒėFGãXâ)ē”ÜVũ=V§>*“­IÆ;îŊSē?Jj(í፷G jŪĒ 흄˙4é0Ŧ“ĮoĒF MŒÔzåŌŊ(FAČö¯>3R͝cË„Ô´Ų­×T7by…ö.ō1ģãĶ4BáĘ)p0ÆŠ1VXÉbŽUXŅĀäPhxŖxüˇDh˙ē@#ō§âŒP=bî[ *îîŪŌ[Ų ‰¤[h~ü¤ í_sÚ¸ CĒx›Æ˛ø¯ZŌ.ô›;;_ąivw¤ sēi™! aTsœ-z(Å4ėŠR˛ (ŤIØíįÚûö*HĸŽ DQĸÉ  SņF(ĸŒQŠ(Ŗb€ (Å Š1F(¨¤ļ‚F-$;ė€š—PQÛAŽ‘‡B¨¯/ũĨ5˜´ŋ†„.ÃÍŧJ.zįëúWâ/i~°–īUŧ†Ŗ]Į{_|mø/ü@ ĶIĩ$[Ąãyîä.øĪL‘P—ļ’„~~_đ˙đHŒ}ŧÕ8÷Wō[ÛÕíøžoEWŦ{EPģūÉ:ÔV>3ŊĶæpĻîɓŒ‘Gäkė ü×Đĩ[­W´ÔŦeÍŗ‡CüÁö##ņ¯¸~|SŅükĨD Â[ęH MŒŽžãß§ō¯6ŧ}•G'´ēųėy¨ûŽoá—^Īm}l­ķō= ;xcmŅÃ7Ǎž…Â.ō0[ãĶ4ār29Ôb‘#J!på¸ ŽGãI,Qʖ4p9”~(Å1ããōŨŖūéĘĢk7rØiWwvös^ËfEļ‡īĘGđŽ{žÕsb€<ķÂúŸ‰|k?‹5­&ķIĩĩĩû—ezGšģŽéfd„-ō¯ŽŪŊŒQŠmÜrwĩØíįÚûö*|QŠBQÄŠ4@y¨ú1F(ĸŒQŠ(Ŗb€ (Å Š1F(9`†b ŅG!7¨8ĻĨ´¸xā‰\ ĻŦøJđîŸ-æ­y F2w¸2šŠģdĘj ōgŸ~ĶĖZ_Ãø‡›yˆUs×'ôĪå_WŖ|kø7ü@ņéV¤‹t<ĪņüŊŗęEyÍuá)Ę1r–˙_đNė)F.sVrwˇeĶüūaEWQÚQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEkK¸ž×P‚[Y¤†Pãa΍ĸŠĘˇđåčcˆūŊõ×ÃÍFöoÂŌŪ\ģ`rŌą?Îēļ]ĪĖß÷ŲĸŠøŗķĐûe×üüÍ˙}š>Ųu˙?3ßfŠ(ûe×üüÍ˙}š>Ųu˙?3ßfŠ(ûe×üüÍ˙}š>Ųu˙?3ßfŠ(ûe×üüÍ˙}šãž'ęWĐø~F†öæ6Áåe`PQōMÔōÜÜ<×ŧ˛šË<ŒYÔšŠŠ+íŠü Đũđã肊(Ģ4 (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€-éW3ÚjōÚÍ$2‡ĸžģø}¨ŪËáøZ[˗lZV'§ÖŠ+įsŸâGĐų\˙ø°ô:oļ]ĪĖß÷ŲŖí—_ķķ7ũöhĸŧƒÁļ]ĪĖß÷ŲŖí—_ķķ7ũöhĸ€ļ]ĪĖß÷ŲŖí—_ķķ7ũöhĸ€ļ]ĪĖß÷ŲŖí—_ķķ7ũöhĸ€8ŋŠZ•ô>•ĄŊšļžVVų×ɗ3Ës;Íq+Ë+œŗģcõ&Š+ÚÉž)AÃ˙ȨĸŠ÷΍ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€'ąžkk¸Ļļ–HeV^6*ÃčE}mđĶRž›Ãņ´×ˇ26-+üčĸž9øâ|ˇ‡_ö˯ųų›ūû4}˛ëū~f˙žÍWŒxö˯ųų›ūû4}˛ëū~f˙žÍPö˯ųų›ūû4}˛ëū~f˙žÍPö˯ųų›ūû4}˛ëū~f˙žÍPö˯ųų›ūû4}˛ëū~f˙žÍPö˯ųų›ūû4}˛ëū~f˙žÍPö˯ųų›ūû4}˛ëū~f˙žÍPö˯ųų›ūû4}˛ëū~f˙žÍPö˯ųų›ūû4}˛ëū~f˙žÍPö˯ųų›ūû5ííĐ´”‹™Á åĄĸŠŸ'|RÔ/nŧE,WWwDŒJ¤’ŗ}5ÅŅE}^]ūīíō¯÷XQ]ĮĸQEbÂy­oašÚY!™XmxØĢÜE_‚^ŒĘŋđåčĪ­ūj7Ķx~&–ōæFĀåĨbuŋlē˙Ÿ™ŋīŗEņGįö˯ųų›ūû4}˛ëū~f˙žÍPö˯ųų›ūû4}˛ëū~f˙žÍPö˯ųų›ūû4}˛ëū~f˙žÍPö˯ųų›ūû4}˛ëū~f˙žÍPö˯ųų›ūû4}˛ëū~f˙žÍPö˯ųų›ūû4}˛ëū~f˙žÍPö˯ųų›ūû4}˛ëū~f˙žÍPö˯ųų›ūû4}˛ëū~f˙žÍPö˯ųų›ūû4}˛ëū~f˙žÍPסBŌR.g)˙–†žKø¨^Ũø†Xî¯.'Ud•˜ę4Q]ųgûĘ=<Ÿũî'EWÕjQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE˙Ųendstream endobj 12 0 obj << /Filter /FlateDecode /Length 14468 >> stream xœ•}ÛĒeKŽŨûųŠü^÷ ƒÛ]ÕĪm ėwãj0´Mˇ˙ŦĄšöžSԊ‘ģNBrØ{F„BĄKHĘŋ’üų‡,Í]~ũĪûãß˙ĀOF.úƒ˙ø_ü÷_˙?|ÍŽcß˙ķš¯†ņųūüˇūuũĪüëúįôë_˙Ÿ|ĻdųĘĘãWN}āSĶoã'ŗ^˙ÛiX)ié÷H÷¯&LtũLôũãOIŋr{1æ¯ŋūíüĩYÚ¯Ōzûõ×ûã?Ĩ”ęūõ×˙ũĮŸ˙úĮŋȟ[œõÕé_mŦ_yÕßūÃŋ˙ŅÆ|mųī=Ģü“Ž+}ČßB›7%ūô?Ō¯úŋįŠ uß_ ;ŨC6*ŋtm´NŨč÷ŋæÔ^ģ3ĻũŖ“ë+Ëoš1ˍ)ųUz[Ī1oĸ>ÆôW-cŅīÔújsîį˜žŨ˜–_ŖØ!~[­ŋfĘfHwCzzu0ĐsĖõcäüŌæģųĩWļģōßũUęov%ŦQ—ŌüŽV~õfˇ¸ökláŸįTū@÷|Ĩŧ+ÛVIų•v6cÚöcú+jÆôâÆķÔÜ;Š’×+īžézJy•ay°š!5ŊšĐˆ~ĻļWێ„vc„ŋä@ #siûĩ‡%Ąįž"ÜSļü3[pOģ1î)CŽĩČ 3×pcĻpaˇw4Ė5Į+ÉĸĖ\ū;KŽĢ×Eéŧ… …˘ŋø1í5GÎlޚĘ+­mYÕŅšæô*-56Wk{tļæ*ōi´9˜Đ¨"Ÿfļōԟ{ų´fšėšÖ&û*yŗŗ¨MÎb;:ģkQ{“Ŋ[:{ĢÂ?}VĘķU¤Īhn}lŲ:Ŋ]uÎ×‹ŪœēD ėQ7×ĩ^­+œ,Ŧ{ŧFĒöD˙‹#L!ē° ļķ–Ök•AOŊåōZ{Q ؄{„áŠleŋJJTW4áž2‹SŨ­h­Ę\ËЧøõôúÕjíâÎĢ šÉJąė8Ŧ‰ÔčŨJąė÷5Áöf§Úod~"5vugęöÕS‰Đ(v9ĶRģĄĄ—†=÷WuĶšJ•}QáÜË|ÍeVãÕ[¯]vån…ŖŽ¨lQˇ‰ŪŅŪÚË–gTąŽ^š7ē'1,kęÔ@čĸMZˇw4ŦfŦW/NēģŪg-!2kÉŽŌ´wŨɃ.|!layį/~L^暴ī-–ãĻZ`¤)÷& &{†đÎ\™Jø!ŧŗkĄ~”õEؑĘ~Ĩi-bo:Dŗ[z:‹t }ņŊ‹Ė(}SY8ÄbŠÕŪc/ŋĮ€Ũ<ĨĄđĪôÆŦ“ßCŦ‘Å)¸ŌË)“°`Q8Šō‹.VÚ+/w)qÄWœK|BæGĻOLÉGĪ`Ÿ–,wĩœžĮxų–s¯—į˜ā—@ö\č\ŠTúÁŲ˝G2¤`QŲtņŽ $ÜT+íã$eîbä—ič“SôɄ×Ízŧå¤ūVßf=Ņ'—ģYOp#g¯wJáãŅw§ë9¸rt=bĨĄÎ>Ũą­^%ëŨ;ŽGlWYی Ū‹čĐÖŌ6{ww¸äņę–Åŧ,EL°V3ۖ'ž@*cÃ""NÜņF—#ėSSĻGQDCÖY;]O×ĮƒAįębōŦ4k1¯–”ĖÂ>˛ČŎ´ˆs\>#œ!JËJīB̐ŗÜ“ũļÄí/}MFÂĒ‚0ũf ´q1$ôīJâō‹Ųíą;Á\ÅĨīi4ļwY‹Ü.~ĢøôŗØ؝dQŸŦO*yk—›“,}‚¯)Z´foŽ_ŗød—1|Ūü­1ŠN~­ēííōÛ§lg§,œ‰Z—¸šŖÆ>u‹Ēm… –*FXĘmą[QÅKk,vėbČ}=†ĸ‰ōēė‡ãT-‹Ŋg/Ep9DŊõjyĐYąMøk KĀāŲUŲTvtbĨ‰?_[ĸš´uŧOYmëœzvcY^öĸX`{Aį ˜~÷OâiS1×Dģĩąc0qV…ß­Æņ Ö֖ģžč][îēņ^åt‘=ËÉžā ¤ũZŗQmÛņĻíäœWoÚ+o:Ū´{Ĩ—ĸáÔŌéĨčužDķ Ā3ą{I)M˧ÕėšũÃ\¯?msū-ēŒ‰›l–䲘=ŖS2ה”bÉė…†°ØĘË~Į‡ßUÕÂĄ?“šŠŗ°g6k ` }`8…Ëâ$ŗžšĸČhū;īŊ‰á]jąĮõ“Ųō§õũŪ¨â˜ŧ*D^D ĖnÆKo šGr‰Ō`‹ĨkøĨÁWTåĨX<>Éã;ū#ōĸ€1~=U,5gČwDĒä\>Įxút1yv2kŌ@nMo°,cŧ4EØËŌ0ĐgâAZœŦķ’Ė/ Ô:Ÿđt.^:ÉžJąt‘D—Íļ‚0€á­ )å„Lˇ†g'JfÍtSú oaą…ķãĶØ8ޜĖEœ˛´,™C”§+k,v°˜ÅęŗdÂRXŖ¤N÷>įk¨tz|ĮĪ%u•Â×ŗöÛ9ŗŧ†L`s<į )j V6U•cW?āŧĢ*îÖL%ĶΈģ5-ƒyšRE vÍB:¨ÆB’ŊÁ<yЗģŖŪŽoKŧėÎä læí™ĐG,‘<ŦLvõæ‹Éqž_ĸŲ6“+T)-Sú ‹möBBŦaáķ Ú6`úfûj VĪdŧܲđW_”3š=)3ō4‘C G˛„:TĶžåJĢKŽ+QšŌDf4ĩzÎdžÔ6•+M\ĨšŊĮcŦQ(kˆXÆ#ŊĮ>Ŧ"G*,M§ąâ—§ HŒ>:?-1Ēc˙0.Ļn˅^äw ÛbY¤éĢĩF/ ĻšĨ4ØEÖ<ˇYũNÍĸlĻđA,yîÍNĢ7M1b÷¸kfū¤ö“øØČ0Ēė(ēØ+[eëíƒ;cØcą…)ÛĘŒŲŲ/|ŖRÛņÎt{ĖåcEߙnšü[~Ēī7†ķQŒ,—+YĨŪÅŋ3ŨÎkbË.Í+?_ä;Ķí|^CS<:UļžírōۏgĢąØķĩoë5vĄvķūŲrĒ8¯œeÃ1‘hdmĩ°­U^]Ÿ–™ņzךôĸrV˛lØōŪ1ͧkōOŠ-æīđÃcŒ÷ÄSJUMÂr✜…ÄKOǜHŦUCxî S‰fËzĐΈfĢÁ?˛1húeˆ”ĶI]eCˌ éy?äÁˇ5•a?˛ĪÄvjt1ĸúŠjũķQ ÷ÕQÍQ~zí™NĩeßiŌŠö&^v9!;mŧ_Ļԁ“´GĸG^ —šŨUđŖyV2#qAîpßņ~BEŲZĒlëĸkÚÊYeČ<Ķû{äœąÔĶ`'ĖŗTĘ ûŲ•v3sÅȃȎæn„§!ʁ6gS1 "Bo– 5Į>!…M¸°XķΖÖnžiˆčÃN–†!E ҇žíŪŊ;!–“˜ī›í/nunēwąvÄÆw‚ĐģIęļšš|Q \˛žé-U—Ė^vk‡Ë_ ;ŠÚQŨV=Š!ŽÂevjCŦĸNw>ņ*`eXxûų´š1ķL š’+ãfÍNsÜŧ d§•Úg¨×6÷däiyĄÔ€rŧļ™,™ũĢIÎØ9wÆÍZ24¸Dhbwįęt¤÷o„5ʲ!|§ãy}NvĻM īĄ g‰ŲDšMËžáÛ\PŖ™]ö&ŦQZ+”ĖÂ5Bˇ%ÆU]+ŗ%wņįûõFzœKdˇl=Ķšē°ĪXÕ˛ŧĪˈÄ6Ē'{A(ļS AŨ6­9ŗXĮÛīT™vQ^-OĒÛ;Ū~Į2sų qi_ŖYeũ6qG5O’Œ™âß,{îՏYZŨFĨX_ZŨf¯wY…7DîÆō]ÜūÕ&ĩFęb,jkn˜šY–Üiy—‰ųž{ŧ;!ĻĪrÜãzm-Sa8Äé¯ÛíܯYÔŌÔ Čķ‰Âŗ™JMŅÅîDŊwŦæQúd|s—gĖOĒõnR;Ŋķ<JíĀo-lŋĮ˙Â@ãhīøN%Ģzëã×##×ŪųwpœŲŽŲ‚ŦڊYs}§{=ÆøāLƒĩKŠŖÅ>ęhļã€!k ô=&„ŽZT\´ĶõĖs" Кõø0ĸše˜“JߞŌŊoęjŽ# 7ŊQŖSŊģ€œ åĮ36mFä":ĸ4KäŌ[^ ;u‚†%˛7/x›™•n•5×3ū}VĘQėΗ¸ƒ6홇ęQךqžKZs0NÖXRãÜŽū [đl˛§ŦwáŠQ ũ"d‚F’ ģČŦÚÅņzđÄĀ_œ6ęÛTK›āÕKĪ2ģ/‚ßrŊ˜œÉ# b_//Gہã’9‘’‘ũŠÅ6íTÚĸ6gjmęYVŧÃHöŦЁøĄ{5v=ë^r=­Ļ ą”„L +tb ėe…N,ĒS§5{žŪCĘZÎPؚE9 œîĢU”3ĖN×,bgîŨīĀŗyŋ™œįęz„ ܆Œ–^Ĩķ,Hžĸ|Ē%3ŗo&w˜F*t_ģƒS+ãÃ&ŧ҆ĨĄW(™™ÛY!ÁŨ+¤ Ę?pI’ĻūŸ×Ŗ%3ŗ-ÆķW9Ė KģsvĢB”čBU ĸP!†Ąĸ˜ WtįČ>Š™pEwŽBC=’9 ™0*eCÅLȖ„á;¨Ö Ōķ5íģŧö°FcôHÄtŌ¤26f‹w¸ŠØyjÖhŒ‘$=+eC­ˆŌG+br_ė,P“šŊĻaŽļß SdÍH}ąˇÂÛÔdÂĻD¸bNOkˆĘŠ“ p9ĖWk\€‰ĒöÆŠ ŖgX ūĖCڟ„õ<¤qzzČsIĒĶĮéĀŗØ3ĸסųN)x׹ÜCÜę>gK#)ˇkųū=R+?ũŊ˜u÷Ŋ7NœĄu3ÉRč“Ĩ…Ũak1ĸ%¯€đ™Šß%1ä;ßPvīxA)…>yŒņ>ä”Ũc[!ä”8eN,VÖ, Nķ_1ô ewŪÖ ewņŽzɨ›­1§dŠbZãNâSAŒB%ž™G]2…J$ßAҌãAīJĄfĻ6KeŋdŧģŦn¨\—.:ŠY ęŒ"ōT×Ī'1hfLČ߆”SD„ķŊ)Š{)BūöVŖ ¯%1k›īÄ@ŲWž”၈P§Ų–/qzäIˆ¨ÔŌ‚õķ’ßž[aĮētYßĮ‹ƒ<đrYß÷w8ũs ē"t'ÃBÁ‹ÅäˇųäŠZq<*ąžŗ]NØ–Č•Zš\QēeåJØ:č*—+âp ‡Ų[* Ô-{¤ÁĄ k(ZŲyÍMŦ§Ë?ãi_Īøį%W1šŽėņīw P‹ ĪvÜOũ†2s•˛x`—/Nk;ü¨(čũ‹†õ} B“ ĩú‘j Ɲĸ&ÖnõŠ?‘Tĸ6%ø+ü7ĮŋÅߊ°åô$ZwX B=Č*œiŗŗÕŒÁbĩî‡¨ŌšÕ.Įûg¨Lh˛ãåÚÛĐķP¸íļ|d¸Q͒'øCxJ.‰ę ˆŖæ¨"4{\ŅĪĢĐaT˜itjP™WpKfoHÁÍËĢæ!‚JÔÕĪÍŊRY?D‡õbUsŨI…ĮãzÃįQ;SĄņ'ķ#Šđ(…ŪI…ŒĖōÜãÔ7"Oi,Nfą€Š“=ūV X@ŋ‘=ę :ŲÖ,6ōîļūn1/‰øpëč{Ũɀëtâ7ų˛™/ëįs,ĸŧĮ„¨„ĶHfÍÁRÁŖ\zΞ!xĒãS¨>V[ū€ä8—ré:yVPÎiĮx0žĒútØBëŽē—uēéęzĩžčz°nNJC}`Zü;pĪZ2cŧĶēŠą6ŖsQɓ,…”Āųõ~¤O)€öŧüzˇcųÎWJ–åc._Ÿ{ŲkásŪ^6=¯&ŧQ—Ķ^!ßOŦøšéĩ¸ÁæÎ<&Jā ņBhˆ\žÚ;÷ˇCÕéŪá,]×GŪ€ŗ”¯œ‘{LHøĶ|Qz•ĩNkW{•CT ųĸ…-8réJ,9.šg¤‹ļÂH¨Aą+X|< Š]Áâ#™{ÅRít=xQžŠė8Ŧ’-ę-õAÉŦ8qŋQ”9[|Éfet¨ÄDO‹ŪöģfąģŽ°ŲŽ™Ūu$ōĨUč]×°YoTm‹•*÷/5Æ`ęO {o‚TđČT],–åŨ; (Ōō–˜÷a˛l~ˇ†0Xön…}5¨R{čĄFĢʝā<8„ŋZsÆZđš€5įTNˆ­]Ę2ĄŌ¸YOn}ĒP+KckÎ+ū(đ2íÕöOˇ /Ėũ{ßmž!ž}ˇy,īģ}72æÛw›Į’ģ l.ë<–$*,†–Ļ“ī ĨYsŽßņū&*Q×`úZš‡§‘ÛõĢŊöœĨĄĻ=Ãp|ŒųyĄHĸl¨zĄËҊ°iƄâŗī4FBBˆĘęČRRU%!„e­†Ė×§kg1öŅ&Qĩoļ÷ËŊ››ąO)Š‹oæŠ!ē!îfętÍUÃɃŽiŠ‹8éšņ˛T›Ĩ/ÃBNķę‹Î…œæ:6cCønyq2Oŧ ėĖØG3"Į¤l¨Ą5-„xĖ äđ‚ ģĨ­mÍf<WMHŦ/”5´â+7Ę5wņØ  9ĐYčI(Üܲ$Œî8Ė Uīi×Ķ{ĶU&Íd|į ͚…¯g ÖŲ\v˙ļ¤Xã…3Ēæ3NΨČgĖÅR9æ<âqĀ~Æų"3†Ö8œ§jĀ\ëŗ2&Ô¨YŲ” 5lļeÂ7J8ã/­Ķr 0$*Vt7˛gåK˚ÚŨô<[Wģû9$4‘Ķ ?eJāĘw´Â24ŲBžcNô<›(E”&g%ēdæJ͌ļĮÚ*Ûš:mÕÎå3ĖŲģuvąŪYfëvČČrĒø™Ų˛—oՅđUV§íL _ŠĶFÖ<ÄÂJĘwmÛÔŨí iŠę†nk!u˛KO*Ģ?vž4š€¸ĩ¯ÔÛi2^(¯Ŗ˛CLE˛‡îb2KATËņ–šę}ļāQ‘øUØIi8ŠVJ›ZđĪĒæ7.ĀH–÷āXĩ÷pŽĻāX8ûpŽĩbāX߆"Ĩ­ˆQõxWÎIŽršīøp‰ØgMģ'ÎcõŸ‚9XōĪ@3 5ĨkˇuÆrÚôļDõG/d|&…ĸifLđGŠĻú…ĸ™…pŖWF@…sPXų3ƒ]pŗŗ5#o.kjĪã;Áp˙ę$DžķŨIč|/毓}}w:Ͱt0X7ß i… {šb— ¤ūÚËöŽ83ĒķĪ8xÂH–ßũr7ˇÜ˜PîԁüFY åN[3¨Îō@ķæęĻō@ķæŒûLŸŠnė(ĒØyÃąĪ+ÔĮ~ž‚ŪlÍyŦ' EŖgŊ]ģŅ‘ķíŌŧ9w?õŌôĮj>`IoM˙8KB`Ūådūæ]ž^tw–ĪĩÂ/; _{s—=„nPh™čÔŧš]ø\đZŒ 5tŗ÷âcîI™°•´÷Íî1Ā^ ˙g&l x!œQE¯ Ã[FH õ5´Č”ŦšOÚPEŠčΞ,Ī3™Ũš,OՐ‘‘é\ˆî\ūY{kV6jžRî”å5ŧŗ&Õ]ŪҜ‹ķ5Õ@ËYŪ†§Đe1-zڃ gM˜ë5ĶŊã]Są ĪtîÄBuģB5,ĢƒĮ13`*čŊ€ĮąĶāôAŅ“ĸŠ<öõŠQė˛|ũ‰Ž<z/Đ(Vŧ;ĘķÚ(všķ ­‚ (Š"€[˛“SŪuŠČ/ˇ×4ētYŒU¯ ‹ĩwc=“˜,Ëą˜ŸKM?U@Æûj¨ķ;ˆ…O7ōŪËŠāö^€ĮũÕ-•|˜ŨU_(ÄÂZĀŦyŒ TákžŊŸđíŊøąh’Ųztp´KĨķąYŽÖÛĪc•›V]/&?á;8qF˛PČnŊŸįõˆķVĮgÂĮšŒŨcņgV(M{ęŪP$ÍšŲŠßˆuķXu§ÕC—Č=ÃĀ ęĩ0Ē”ZeÜ\ĒøŲU­9‚č åiƒōÖ@֌ܺKŸ÷ާ m]Nh¨¸-n_z)\Ųd_pƒŠ]sčr:÷×ãŪy_€ĩĶLØ{ŒĪÜ/čYĄčōõxk÷ąáŧw*WמĮÖ? Ö]MųÎKFü"ģäāNeņZ—#ŗwqÂĻų<īxGÔp‚.9j;;Ž7@¸eC Ũ°dŽõCā\ôļa6œžNtcJƒŽxĄSæĢŅ&7›Ę0ƒom;pž8ŠF§˜!g]ŪN |ę•Ú›J!2]1zĨŠõmg čāßå<ˆ8HšŠÍqĶÆį“¸PÆsgŧsĄŒžux8ɑ'DKĐEžLJŸą!š'ĨjĄš ÍRģÖ5}‰čšŸÎËA´ÄŨš€2Žhf§ĩg„3Ge÷>PÖŌØķ‰*yąÖSočÂefú€1žJĨĸeEkÛ]  O•ÜŠ ‘ĩ  Q×üËÔÆ5ŋæĀåJĩ(B7MĢpɚŅižÚÊxvH›Ž™}[aéŊÔL; < CdkW¯$ 5Ņõ dm8î ¸@†ČVĩ§­ĸā°SąĸŊRķ l¨Č­Qq ųky ē´sR"-;Õæ!&&āOį‡mũŽÔį“ųˇuŦx[įĒũ;VtsĮŠÎ•ũßĨ>ķX)Ĩ Zč<`Zęsŏ˜ĸËŊ›Ŋ÷81IĨĘ=ÆįL}GyÎÛēŖ<įmŨQžķ’ī(ĪķP•‘§$Ņ5“ųå9C ÜQž3Œ€Ø§ââLē\tEK>ELԜ ķšĢ6ØÚŒŅũh—Á8U‘æ¤yVaĮ~yvĨKFžęõ–&Zj3˛ž%ęOûT’1By˛5ŖÕgšĖ™sųFii›ŒĖZYŖeššŧ—„čĖĸ\X ZQ ƄpJRšôjpĻģĶ ]‹` ģ‹ã)DgĒeø0:i̜ķI :ŗĩUŲú*īrE˛w¤^ {ZÁIÚč´TíĨø%7,ķxû=ĄŅRļ$ .ōjĄßA]MÛT =̤dŪg)!ĒŌķ`$„ĪŅכŦ‘—+y.ÖGäå GžĢõás($.Y€ †=öāK øüîâ„ēšŠR;J‘íz:8/g‰ØMÉĘ9ŸM‡āĖ,t5[v~YđįB|Ņ8#U*VFûĘ5ŧ—ããĸqRĒ”:Ŋ :žĶåä0;ŨÂ7H0ĩō VՈq=pŸ—ŒĐŒc°ā åÜ)Ō@ ßX-ļŽđÍNv_!4“ŅEcŌ}!l;šō!|ŗfĨBßĀen|īHÛ\ø(˜A/“í`ŨąX(vÉãĪõ ŗ/âAņ͕ÎzŽÖGņĸzŸí"ĢRƒ@]{”‘Ŋ#ĩl'Ęc@*¸0ũČ\°X×9@*¸0ũČzŽęNgŅoŗ-NÃĮ?mÆy)žŊwˇÛ÷īĖĢCtā.m|XķīÚ(}ĸ×ÜŠsũíNŅ ’–=T: ´Žsk˛Cüʋ]'6Ŋ°ŋ••õ—QęëJœĢū Ü—ķ ]a)Íc_ŪĮéÚéÆ m”žĀŋÉr†6ēądU3hŌU %3Rg¯ RĀ€¤‚ŪéšáēÕaX#VÖ@b¨^?“=i¯D‘sÕ?"\5ötŪW˛ø‘}iüJ9Ī…žL —IæúČ{Œ ‰~ >ŽĄŗŋéWsÛÔĢ*ēB_|īHôģ|%‚œĐßŌéw:@íõ •>C$DéƒņF>œsp:ë{Ĩa@}œįXt.pM{MC“Ü Ā swr(ãųŠp1>Ž3⁰Oų¤ĢbĪ5/éB‰ŽHC…@=oũÆ$?“ųŨײO@`øÂ$'û&yŨ›‰ pmKæŋ"6kDäō†GŗE÷5͝ q°>ŠĢ-&ųõcW`{ŊB÷ĸ4('ĸD´Wą"Á{Љã–7>aäe§ƒ\úk¯é'o˛:Ū¤Ž>š“]¯Ö‘9>čyģ|9‹( hcš,†<>ŧ*æMé3ĩ)]ÎŌ.Ļf5žgđZėVô”Q(ŊØíB†ŪjŨŦ8˙ÄTĶNša ŋ7ÕÖß_Xąî„co ˆŖĸ‰Â÷ å“>ˆæį˜`Ŋ D§¯uLiSˆdH†uĖhģ’—ųLLpŌ7ĶjĻ ö_'“%kāfÆäįŽlžjČķą˛ēēõxdęõFĮĀpĢp+×1›//¤´Osĸ!{鐘ėUOšvtŪ× H|ūŽ–M+Üã;Ąũ‹hC“{Œ oîõ+ƒģ§:}nÔâ3ĩSfÂ}=ŗĒ6’™sĶŊ#5Šm3&ĀûjÚQĻKâ•&ŋ‘å|7›9ŗŧVV÷BũÎL"s}g&¯Š6ĶÔ˛C˛/QĒ]›†õĀnĶÆŋgöÁûūVŗĮšŊm‡´Ŗf]xåÖF™É°O0a”)VĖy_j”Q{ˇˇ¤B &Y^œ‚ĩÃę_Thˆƒ†„˜N)ˆ˛éė.WH)Ęo˜ŧķiĄÁejŽ‚ū$v8‹Ĩ §2JĢ5y÷<—Č&čīÅNëF$>īë‘ø,/Ņßrhį‘ķqiųuįÛRÔââäSČ)Â;¸“ĩŨŠ,÷ŖmŖžĶŠo˙,ŖM{ĪęĀ [›ŪQM;N|‡Â ä”Î8U-2íNr–a¨ĀŽ{BS€ ĪAejĮsTqVMH*īÆšįõt͡6KÂZ,Ę5EW‹…ŸēÛS¨YíŨ÷ņ™đ.¨ĪJ•ļö’IjÍš–‚!Ķ…Ü…s˜fMÎa ‰Õ‡ų“@/™šŠč‘`'Ÿ€0:¨:ķoĶ.Ŗ;ŽĐ߲ŊaĖÉw lbŽm‡âŧpnžęŊ'50‡âŧ,JCd ím ˎĢ÷›>?œßŧŽcîîũĻ|9—s¯cbŌĸķøN(ų{7ųLE$ŊÛå &DŌ‡YN|.F­Ã˛KūXė°+3IOfÍÁÅųÚ]Įü¯ŦßɌ •õŠđ%ĄhūÖėQ„§{í?ëŽ"€čf´ŋ6뉕 bËÍiŲįSÁŖÅ™†7ĘųNŅzšÍhx§ņ<žRtōëōîŽézézĢd:Ōõ´ĄØ™Å :9•aÆÄ'Ü.[Ÿ”  šËjt=ŠĨTŲ-ÕBėKx¯P–R*ôHĩģWz¤ī4žÅXU] m<žķĄĄ5ĮōŸR}4øxfUäú$Í|8ī(ē Q}†U¤FÕˇķQ ÉIĶ˞­Ž^NŖ˛[Š&'W“^˛­Öļ’Åׯ(ĐnMTúÜŨdĖwE÷=Ļ|ČڝyīwŪķLxĀŊ”(ŠQČīūģg~×"Ž;ķ2úJ^ũwĪŧŖU ËŪ‰hMŊuv €kĘ “zžÚ{rYƈīˇĸr´YØcŒ›ípl×Ļëģ°éjĐ´DÃÔd5ß8ŧ ~ĒԞią+:†Qč%F“ËĨYgÁƒ:†Tcxë&WŪĒÆKjŸô Ô/)Ö>đ>†ļv)<īĢŖÆĶ °āŧh“ÔMĮ {Ų)ވų įeqú\R¨äywHĄRåŨ “ĶY›XŠ3JĖø\ũMŗŗË~y/kĐšé›ėY„3Eœ|S=ĒõŪ­S%0DGPĒ^¨I,ˇæURĨ:[†4Ü~&á¨ÚGuĐšPĀ9įĸ{īčÃkeO„âĶzŅÕ táĩ|pxņnâŒī‘-ŧ›Tģā-„€jŖwk ļŊ­ŪōÄ mėíšâĩ9Ņøs¯í˜ÜtĮŠÎ ˙wŦˆÔ^ ôĄ3ß ž ģ6 ũÚ[n•ĻįB$ig˛- ‘—dƄ ¤×Tßų˜ŗ…úŒŨ'_3ËĶĒtīČZÉ~'āđļWĢÃî+”ēGiš5Gü\€¤.sėoN´ēøđķZ@šÉvÍĒëĢ æcL:ÁûKéåũåÁÖŪēk§sĄˆCۈ?Ƅ@ōSõú<Ū_C˛Ų™PÄĄm Īl¨ĸ;­PĄ^đ4œŲŠŅ}īˇá3E÷Íë•đLäīŦÖj—•–ŦĨa(ķwÁÚ6=QtÁܖĘ!~'˛tváCžŸY}Čn—|ŋīF,1~9ČZv[ŅĪܯ•Š<Đf-ū3Ū}Fŗ–îxĐã Ļ$-JŽhĢôĒ_a)˄đKD†ûNpE5,eÅnŦ†×€ō] Oļ…$ Ŗ%KFĄģ‚uœŠŦ>d)”OßÅđv.īi1|ßlÍZęžgf'*˛@ ÂUØ=nb€ÍmĨnˆ¤ΎLĒŅĒĨ*ZĮųHĩU‹"‰ŸĪĢ í:Ø( ÅLkkRí&N8€:ģ€mĘŪ‹ÅLĄúšĢP A}ÄŪ= øˆŠSáŖ>bTsŠXÕJ]-¨=ŲšŖ’ŊW{ŨcĪT¯ē{SbAõē?ÃˇÛ‰*ێ,én ēnģÜļJIxšmôHßneŸˇÛÖŲĩĐ Ķ˛Į;ą č4)Ëk'–ŧčš Ÿ(j)ģ"ҟ¯ŠvkÉn_žl^{ĮMJg~­i×üŖŦļQį‡}ūĀíøûáö´<ŧœ3&¸*€āÖ|ĸĮoúâĸu„÷ĢpÁš9ąx˚˛f㌔œŗÚAĸģ',ģĮb‚/ŗāuú F,ĪĨĩkôyÉ7ąØå„fĀ”°c‚M̘1BT]Ÿ,Ģ •æ(^•.§Ą0É„O!CüI;į’%‹[:õņā^tŲÖtÍŨĪöd§…øĶPĶŽŦĨæÍŨb˙qN—šd„>(5W“ėLļ$ VdF÷3{ęŅŊ˜đĐėr‚[Ŋc–û<Ž×Ū‰rĄ†ŸųåLÂ*ūkŅžįŖ@ŒĒh„ü,Pĩ]#äd=#ãM‚Ęžˇķ`ŲŲģV¨Y¯š2CüiŠeGÎbÉyŲ›š |ŖdEęíœIxŖdˇŽTcąm}ÃdŠsÃd‘ŠD'õ‘7Ũ–hĨ™éaŠ{ąŒ8AtiVˇEĪA{šÛ3÷™_]{š7ēœËģ ĸ°Md< *Â.ĪÁęėčĀs°†HđvŅ6“ßŪŗÚŽ–Ė> ŖO¯‰…zÍŌ0v‚DN’Ĩa,GĮÛkël_ '\:•+] 5Bö&ŋjÚķ Z„9;Su/ŠUK!‡LmšN >­GĪüvuĩ{*•ß}ĸŒŊa=y°‹Ú|™7Ų,!B‡ˇ×ií°0 ˙5wƇ¨Y/ŨōstS¨a ¤)¤÷™ĮPŗ>jĩ3ÔŖC/-jA!Uŧ ũR°ų+ģ˚?§Hkdކމ‰ÚÖ¨k_-SŨ5úBô–hKĶŠ.Čø¯ƒÚ#čŲ†ģËĄfŊ?•š†ˆf­Ņ8o ßB|.˜YÉé Į?3ûÆōᏠÄfū¤~āGžëđ“Ôš;×˙Ü>âöúĒkĘĪ!>ŖFŖYĒHÅRyƒA°1_•ī÷˜Pķõ]ųNvšđŽAv…@UˇÔ A(¤)&GāĸhīGû˜(›UX–NOB…eĩ'JÖšŖO:zĘhĨÚc.ÖHų ô°}2í/ņ˜>ŽX–ü3=Rõ$‡=ö=%Ģ=öWŌņr(XŸč™˜érœĐ2sč(Ķaæ; ~*|Ēs1ÎĐbt}9{|ĮSYÔņšÍœzh)#ĸr+^ųŠŖR™ŒÃ4˜ÕĢĨĄ_ķ–Ŋ'{\{Ė–†ÁUBßÉŦĪ=į’G­fÆøŽc¨Xרëy[U|ÚZf—ŊŠĻm×CÄy9Mf§ũŽ_N6\ßt=Â۞Dˆ›ĄÎŧwĘŠu"ŸĪ.'8ÚS‘n+“ę$ÖÖč\ PˇŖ2.TLæšøQ UļåøĐxgRyŲ2°nwgknĸ{ʝqŗVG]/Šį2"ä2^/Šįš*ŒønĪôC.c*c16TGQ+3Ézā(Ēr´­¤fdm%Ģ–Ģ2_`ĘTĐŨ`ʏ1Ą¯äL™PPÁ”ˆ¨ex÷^™Ũœ7š2ŊČ`RøÁ3y4Ā”6Ŋ9š<8Ŋ9ęjĢ€3 Õ\–>a.X,Õę[†ô›1˜DP7q8éãéŦ`=•¯y"ņĄŲÛåŨ2<}÷D%]GÍ÷´zûX˛HÚÅXL#U)oēwëąü\ëá#ÕSq†"õÔˈüöŠÁ2€ÂŗœmäS…‡ō—Sĸ$'ꕨhR´åę”z@R €Uęąq Ŋ •;H­ĩQķ˜fcpÛ˜fŗrÛR1͒ËÁį‡ī×'ÕęŲ9É—?Ã4ËéÃ~āם ĩž2ī!įLÆ}ĖŊ3÷1åôÎd||'”¨}e2îcčÉøP¤ŋ˛cæ -šļxŒņbE•ÛĪõpÚNTÅÉšĖ¯`ŖRōiēÚD39 ͒}ŦÆģ™ßjd5ßjd9ßíDĪ'qMŸŠsMīcēōŨNôŧ´P|Ėķii¯6čii¯=7KkH÷b§ÛņŠ>œKÂđvų´Į,kuūftÍC[>MēæŠ-ŸčeG&Ŗˆ¯Án;ČJŪ“ŨvE"›öVÄ,EåÃÅøYQ/öfė\ŽRfė\3Ž2>#& <“GL/´"Ό<ĩ"l0čĩ¨ l8ĢálĶ+(ZMX5WÆōp"Wŗ×+ĖՑRą ŽĻbĶ<žã錞=—įrŽ:Dמ>øš'ÚËžfxšZ¸rfC-‡Ķ•ĮwB2ŖBlĐĢ톨 ĸ NûÎT$ŧa8[ĸžíLŏ68ußņ]ÖÕĶŦöLce(ÁíöåC‰ …FÞičۓžZė,4áąY@ĪŠh3Ũē°Ø´æˆÅi +|‚Ãē°Tū3ā3Ŋ€=á‘Ëđ—oSß3žúí=ūO)rŠąw9ĪŌ2U“]TWÕgūę(ŧ/\4Ë9ŊæõĒt.Cqū°*'ŽĄ8ŋķšäĐ͞ŧãÂ4üˇąö)ü§¸XīŸ­âA¯gŖÁđœėŪh–ĸ"•Ÿī„f ZÖ…Vgؙŧ¨Åkŗk+K’ē2ŠnSׯķ˧žßõL4ĐĩŲŽĶŖÁÕĘüF¯–ēN†Đ(āĒŊõ߁k§ Xd=pÛÚĻzk đBŠ’“]˛VĻz ŽŨî•ę­™Á_Îņ˜?r˙>鍸Į|å~äã¯ÕŨu“vsÖ60fL„(Qüˆjæ UjĀPōĢšž;íc‚đÃ˙;WčĄÆļLKŸ€?ôākėcRŗâUk.BÅ(ÉvīÁDŽ锆¨@ĢuĐõ+÷Ü Ĩ<Āëéķ\‹ŒíáuŪĒÔŽ‚úķšáæ+[đX}W€´ŨûÕIÕņsđŋĒÔÎéãŋÄ ŌĨ!:ĩĀxf{‡á‚ž ė;0\Đsá1&‡æ`rÅz&ĮŽ­ûDPĄ7ž(Yžl|AriKˆb‘­ã‹˛5Ã?Įœ€áŸcBŅ(ŌiߗÆâ“S(´n'ėS4Ũhdē/ÄЛcųą=‘ãjYõCŅč˜n=>î8{sgžƒ”¤j†|JüÆË>9RQsĐc…mK3ŋ{+Œ„ <$LŎ EŖsN3ƗĀ(NiŲ”ÅjCļcĸJãõbą›ī„öČŠ †Ėą=ōļĮfWĪ(âęŅå,EíώŊŽ….Z‹’yWwÜ^ę¤㌖Ą+Zf Ûuzĸšąí8#–׎‡ĻA_Ų-'ŧkęĩv'ZPpnŒ™e/xNgëA!'R‰Éii˜ŊôLÉ3Ūb2%R­ëĖöĩŅĪĖnŨã° 4„jˆį˙ü‘Ö+Fīē†ŲĢëc˜x„Ã0ĒũtŲzDåh?]rÚnõ˛lLCÁãžėŖĢW˓^.dQ÷Á5ģfQ{yāiˆę1/Wüš§fĻŌŠA¯j.$H'Ģpŧ=§Áņl/NpĖĒ*ø%ÕØwv§°CQ3amžXĸ‰š‰AģæGE% ąŗHŸß!ų|ēI?p§Ž9ßy͏1ĮŧæĮ˜S^ķcHôĻŽŧæįWBņë;¯ųššĐü9fƒ.ø ‡MĨu"›¯X“„ėŽ‚Oö•ÖĖÖ_jdJ?…Ö)vˇēVíÕĀ–ŦĐ:Ō ūÍW¯†į\ĄV…ßØÖ6§ÔÂļŽčŅņ-ŪÍąžs…G $ŨäÎöޏ9âgĶīāmolÍM]}ģķŪ5ĨĻ™šbcãČųÁÎTėĻWnuŌ}ə–l×ã‘#œö¨l_ŠxĶfcëQĮdÚ5G§`#ĶîũĪ~Œ\A÷%Š—|Îîmh2m%BX3+Xp¨ĩ:‘ë÷_kY#ėS˙ä…d"b2k )ڗ°5w_ņxîŋų™ŸtÂ|’c>ÄÃ'9§yŪ>É91õÛ'9įˇ*Ī4cĸS’‘j”érđϞÕ4?†Øŋ[?ÆķžK+‹’G‹-ŗ!ađ8îĪ1]äBÛI‹ŽāXÉŽ'ļĸ[ˆYÛŖđN›ÂíKÃKEEy-ŒÎÚBnŠ;qLŌžžĀ…&kF6íž$ķ9ƒSœŅT{gt. ŨģÆ ßšÃ7wRW(“lŽ8[ŗÜâŧze<†LŲ4úf<¯á›bĪ"–I6”ŦđĪLTŦJ-ČfÉąJŒ9$ĘƒÚ’0VIƒzOvâ¨6Ռ U’ęŨ$zėvŲö Æ2ɉĢĖ×͐AÖŖ6ˆ}Ņõ‰Sî<ģ ĻĶ:%áŦ@VĩœSQ@;+ŨúÃåmorģ ãsnj„š˜ZrgA˯ØĖ93‹ũT*=ŠwķˇÉČŦI§Wņü´jČVyų*ZíŠ]œŲY´^ņF5ĘeĶj‹u Dģæ`Æ kŦQ™@Đ^ ĩQÁ‚æoy;…z5 9gSŠņ†ĘŠt[(“lĨ2"€ŗvm돴Nrt3ÆP5Į5/v]8lîEšžĮn{1NíÚŽ6ŗ{,<z^ĒJÕŠX\6ÊPā,ļ­ gS*#43,3‡mM„…z Ũļ'3\Åĸ›‰Ÿ( wšS˙n_1Ŧ>‰Áq–šÕ'>D82,UnBiķˇY¨5;ÚUœ 8 /sė(€K3‹•ß!ĻĸhU'C;ŅÎÍ. ļX(›˛piöĸęa—\­1KQĸŨėwBØĨ UŊ|āžß…TŌ‡šåwūEūüV‰”“endstream endobj 13 0 obj << /BitsPerComponent 8 /ColorSpace /DeviceGray /Filter /FlateDecode /Height 626 /Subtype /Image /Type /XObject /Width 1600 /Length 29671 >> stream xœėŨ|į˙đû˙~ŋƒÁ€!Ãm 6˜ cÇģ;× îÃ}˛ g°áÃĨh‹Ķ-”JŨ¨ģKōũ'š¤Mjąģ{’ôķ~ŊÖ$Oīžû>]¸or÷Į€iģPcūÄE Ŧ͜ŲŲOY˙c(bļPļ0°fŦ˙11S˛ú7nŽŌ닿V§‰úũÛôÛ`Ö˙˜Š˜‰YX‡ ˆÖ1ŗšą@ÅYGPÄ €@ūōØä‰!€@ūōØä‰!€@ūØDōØä‰M mÍ:!TzÂ:€"ß?ĀFb@ƒû`#pũ @bČ`#?$fzū¨<ķ¸Ãõ¯ ĀtČ35üß.™L.§™‡`*䉙š?‘īņEƒúŋ%p8E҇?o™Æ:}úl^ũëôAū˜‰ųã‹×T_ûuÃģs?˙8UG  SįŽŗķ™OŨëŖ]Oœžæ=KWØwBņsĖũÆÕöÁį?Üĸ)¸[PLՏģ7×zŲ{õS'§ešļ\Cškˇ1îŋ—Ô-™đČÉy˙ģ…‡ÕũĀŖ;Ģ˙Wø6˙ˇÅcēúiįgNÎ+k˙rŌ}§ĮÛōÛĢĄŅ‚Â+^qš ĮÕ¸ģ¨đ­Ä„üâ¨ÚĻÛäI†˜ŌŖ]mÖÁJËÄüŅ,HwąˇĢԄ˜NëĖčŨ6õ ũũđĖJ™ll­_=ËČ {yŠįËe ¸OŸ’›qĩõ#Rœ„ŨIsÂ*—•Z@LˆfŋčF”žš*§jmŌ*Ũ+ ļŒîn>D}tKögĻšë8’âOC' ŊQ¸úéRy EõŌūåŪĖôüņ;FgheĄõ6ŽĄŠNß&ū&䎛”Oū—š„ûĖ•žWU'ĸŸ8îŨÎ):˜–L]ĮįBŠž|œķ›AޜSîüQqŌˇyjzĮĢĐüQߓj*žiˇĶ{ĶÉ5r^dFöŌũmķāüōĮ šŅYOū(ŽlÜ#zÁŽGō­Ÿ›‘Ƀį=^ÕļA üąƒTåJ&Đŗę­o““ōĮÔ|ōĮČØÄŪwô|åÉ­#Q=Ž;M7sА?´íŋŨ”r%'ĪÖL o9įÜų#?e ĪÕũķŊōT¸<ųŖe~ųc]āzčÉī„ĶŽ[On B Č °…É&Ĩ"ųũ•ہōĮ zX+›”4FũēŪž#˙ŲĻ8—tŨ~tGûėÍęũ|lß`ŨšFũuxqö9g÷‘ÃWĶRö˙ķĪ?GWÜĩ+ y<7z÷‘U5ŗKŠŲuäčēŪ:[õ\xėȞ1­wŖ~=•îr W;8Jk›!I}9îĸōŌ‹–õûĢxl•&áԀãÎŌūœ"eū¨ŧá¨2ō_4-ûGéä×y÷IÍū|r‚ör:ųŖüŽ#ĘJ–| Ũ”ߎnîĖiįū?=ōûˆļ{žTŊúüĀ‘ŋOÅŅÁū9ļ°Xö6ĩįß=’sü…I|A™=ĘĒĪŠX™?>˙CYz˜ŋНĖUf;6ų?9ĩ{“ü%×SOūāŪĐVŽÛHwKž™ˆ?@X;˛LL ‡X/ ķGÃŨü1™ÕāĘ%Åhz$MUũ gÔģ— xđĻ.ŨĻx•ô´iÎ~_ų)2ŧ6ōŖĨĘĮ~oh f¯Áqzīø“\•@õ¸‹^ëÖ<>I'ôäæĻ„æ|×Säå@Pޚ?•æKöĐiåƒŪû_ŒPdŠ÷†7-|+1!€€î˜™>WY7A&\ŋęhˇüz„îe™|]&oí—c“üÕĪzŌõˇUOÜŠ/_RâU’æš–!ųCÎųy,[˜]öQך›;@Áęģ&õŸ’ëÖšĩ{ÁN%ūŒÚ…<ô†nŧCivÍhzžō\ųCezr8Æ~ČũâœōtYÎÉi˙äOMūhėK6ÎîßXg\ųcjНęq%ÅęÖŦ›?"Z-ÍLÕēU¯éU͇ø”Ö'’˙:Ø5§ËXņŠË–j,›û~žæą‚üÂébvú Ĩ˙0ÖÎÄûįSRSôns™vjŋ\CÔĪĒ“{yÕŠĐŒ6sÍĐ\ 2 $MT=jåĨOÃU˙Į‚4wŨ;\H$Jđ{¸Ģšf“ŠtGõ(RūH=øŠh^žōüō‡â¤ŽēšļX4bđĐĄC{ē'ŽŌûmí-rō‡{ÚūI؜ūWũ¯eÅy=ĐžíŸ+|Ļēŗ}:g¸ /Wūø#’´.oåÉéXoEˆŅžūC‡ŠļėŖķo$oĘdų„ķT€üáĪēâ31|¨´ÁeŌššŗ‰NПU&¯UO(DĶšÖ%Ksę5$¨ĮdįîP†Ķš>ÍŪËÉWģۖCū”˜sũŠ˙!Nūø‹(mŨëÔŪšËĩķGöįüšI‰Ęˇ‰dr%"Å{´wËÎīz¤¨oō”y­Õ÷“A›†fRhv…Üųƒ;C7žzĢÜŌ|ÅĶĐÉéDákĸsfXɛ?^’œ§|¤KĘĸ/“4C î&'5äĖ (ōGōDÖ͝@ã?ō“+LMÕüûH|o?R÷÷ũøu’æJž:Ôĩ+x°\žüą“^ņ7îŗķGģ=ŨUÕˇ¤Ēī鋛?RÔn<ų4ČUž“?*pÔôû8ˆ”ˇN†]žĄr5*ãæÍK:įųœīO4Ŋ×ĘÅhōGĪ=­Tõ§ŲŨusįūDņW}Iö §+WūiÎͧŨŲyōĮ Uˆ×’ûĩ›# ũ0†ü‚Ų$@ú õ|ļLēüņMPšzČmå˜9Ĩč_ūÉˌ͇ķ|˙ĢĩiOĨ•'„kÆãeį_ČEŊņ^RO +nū8$_Ļøų'ŗYKNūhG¤™ÛļŊŅé ü0×= íüą•îW=™˜}˙üßk‹SöDČ ˜;\Š?Iô`r‰\5ë䏌DeĪŗĢÔSS'h´Ķęõl)īŽ €PĘ8’?žÖeŨą‰˜?.æ:åėĨgĒ[žsŗ[ķ%Ũâų[LŨ){ŪīÁ1Ē“éúŊŠ’ų[eęîĢęAU(P=Ú{;ĨŠsË9šÅ?™Ŧž˙ŅQģ˙U~ēdQîíúũ•Ļę|™6ë–įäÁĘqxJĨžč^Ŧâœârļ×ęŋ›Â÷@nBT•/ų—âĻđĪQöŦúšæ/yå¯8{ëÜSáéŪ˙W~%ĒîĻ)øF?*únHŨ˛ĮaĄôŠĩ3~7!€P ’?":ŗnˆØÄĘm:ˇ¸KŋĩîÔYk>Vō˜ÕŽĮᤜdą’R÷tī4˙…ÖTå]ä÷éą+=Ē ‰ĀÛ4 I]ŨŽ>×â7ŲoíT7‰'PÔ/}; ŪâMŅĶ:|Ž,ŲF{ ĮŽķÎ?BúķöĢČõ›\íSɎ՗……îEųOb^Xk[^ČXÕNqäAa?uÔ^ĻCëūĮ‚ ˛ß8ēCĮE.ôXk–Áj›?OčŪIwŪZ­üņc2íŪ~–"ĒūTäQøļ~ē-ēHŽ|G„ö; Ĩo;vÎépv.u÷ŸˇŪËĒVūčöufėĖ–Ĩ¸b3R/tPÎ!SŽõøú˛+W˛Uŋ`ęŪ.įHÃĶig›.š/ÎéĶQķ+É&ÄBūĄ|'Hú Éē!b31ä?Okģđ JˆˆŒōĖ™ļáC’…ĮP†Ö'Ų%DŅ‘DWĩ–Tš 8õGũP@̓bÂåug@@D&%F=R]Ü9¨Ø'"ž(IqČå%Ŧß)PNqr TŨix?:ޞÂãē_‹Ž!yDLŸjWēI¤ŋs™Žrąi}ë•Ai1įĩ~Ĩ}˙|B"ˆ"'íüu2&<“"Ŗŧ{j•iįî'åRF×C(2Fuû*ÛEôŒ˙ã΍Tž Œ ™ĨŲk3%úúúúųx˙^Eģb­ü1/>\QMĖNÎ?Œ(ʡ4ĮíˆQüáÃb÷ū̍-2f¯fŸúŪQ1”mä4Å\ å4+ƒõo' äJ7ōGAg1›abū¨áLSĘōû.ū~ž>ž~~ĩO_íÎŊXXQ{ÃrĢ^{ë¤ŗo™Ŋžžŋ}TP͟Ŋō÷ņņ 8üS€â1H=ÍÆgÛ<ũ=ļļåxŪŽĨx=øqåŠK_úûPŸŊjœ ôõņ xTwoŸâņAĄÃÜėŨšöë|xú+pˆkėæįãŧVë7ēũw;lāw@wÎÛMūŠˆüíĢijįŽûÕ-`ong€_°ęʤG\-¯ü_īŌôöj­hОŋ€Gšom›(%Ā_!đMĒÖíqN;ôyŖÜįõ,Î>Pņ'šŽ(™ãĨ, ™:ØSņ7öķ™ĢŲ§äÅ_Í×/谑nÔcĸ!ú7ōeŒ@ųc9놈ÍÔõĪ—Ũ;{äŸÂ× EūĐ?Đ2ēųÃxÃ“ÔķÉ4ÂMį+Āø¤7fÕlœbägÚûËČ ”ŅåeŦ"6SķGąQĘŋĪO‡csöiē߇|͛d¸Ö”ß†:jũflŌëŧ›‹ĨŽ+ũ#ŲÁ?@(Č25(}7aäģúˇ2“ŨĖj3ĮŊ#úŅ„S™Ÿĸũ4õÔŗĄ.ūFP ēSØÕAũ&jeöŠ^üĢzĒUŋĐŗj6ÆEĘ=ú]LČ ä™“?¤Đ^kézŲTÖŅŽŲíĢĘq‹m#ÚmŊûråÃiZmŪá;¤‡d,ÜĮĪEŧÄũWÅĪw]Iēã/węßH0Č äYzūøĐO–Š–õĻ5ëh ˇˆhsņr]Âiš1{ŊįI1\Å:ĶSs˙ÅīSįãĘíæûP¨ĒĮ‚/…ˇø¨æ)F+#éđ)ä ō‡,=XĢŌˇ•čd>kÜnķüSģ…ÕŋĨ>S^ĢßÂáĢgŌ RŽÆ”w9D[üBAū0ō‡HŠ \ļ~ÍØĘÆîÖxę†õ Ú@ՙ\šyãĐēėÎŅ-f¯[?¯EaģX7ä ō‡Æ!ØŽˇŠånģ?@(BæAügļCU_LåWŪ›¤ZĖŽë:ZõP{jMÕã¨FLJīųņÍGŠ…Ŋ=œŸĻŧŋHkÅ1Ÿ(ū3¨ęåˇŊTũbŠÛŠæĻ(7„ßļC_Մ‚uF)GÃqĨúķŋ¤:xõQĒyéŪëÃß mĪO‚ôéX~ɸAüm‚o‡ŠŦ1š/íĪ­ ?…jŨŠüpĩ‘ë2?Ā& €P„Ė^ü2=Ûųåë‚'~0îõÕÃHâŗK4ŋĘį‰'LJvqĒ<ōŅž†ä/ŧĮ߆|K=ŗFY~ ‹ęeĸÕĨĨų! ÅÕĶs—Q—ō5ŧ¯^fģĖUĨøÉ­‹} ŗí‡īj×đŸŌ˙§z,Éoû^)mKōõįK˙§ŠĄ˜vŧ˙§.Uí]>Ū˙h晚ĨY ĀĒ•Â÷îhĒŧë„Pų‚üa ôŋëW ä!€Āũs ō‡?ĀF €P? „üb({1ÜekwI‰üBAū0ōˆĄŲÈh"÷‚!ōųÃ@ČļŦâqû_‹kôž¸ßØEĖMôe‡Ū ĶeiĻ‚üBą(,eŨą ”?ē…oWüŧúLßôJ;S.q8Ą|îđ¤Į-gÜnUŽ{(~ΛoÂ!ûžHŒs­]Réa|Lä<­‚Ų)ą1QQą15­˙ē?§×ĢåĒWˇĸĸS&r\]ᨍ87UQƒā¸ØØč¨˜ØØ„kÚUPŧ…?Ķ.¸kIÍ.ûãâB9Q+ 8e]QĢrŠžv×Ûԃ^Ũ9Ž}˜öjŊ\›Ōˇ›€?@(ĘSX7DlåÛڜ–Šwą=şT€Ŗ•š*ĐĸÚKHļœûĖŒ\’o ŅŽķĸ(ãx“d>IŖŊ¨Ô'¯ÂH;­Ž|ã"'ß žę×ˉB•ǗĪåiÁ+9ŽŅĢL ķRĩ đ÷M§H?˙ĀįZŲ%GĸĪĩ žķË KZ¯Oųû)' Ļ_r 'ŧđMĄČĀ_sŠĶ)=íjž@sÜc l§UØĐųŦQĶAŌGÚ@Ö ›@ųã lĘ NĨ5zˇģ/ĀŅڅEP‹Â”äŒE\Ŋ§äfÜnŨ‰frœŊ0ú€ãčÕôFĶ]ŽÔŅ.ŦĶl1Ņzũ^›oė"¨æ×ę—Ģ(sGûÖsü‰øiŨ?ü>æECå“E˛įÕË~ÕĒe=ĪĖuMZļ.ĢUŅsšģI'LÍ|DÁ9/PøĒoZô=G:‰ŋyˡis܂eŠtLOÃĒG*WŠēO¯ĩ§3ø2ųŦҧۂäāvúeŨĘģČŋ1×7•VčÛ°nq}[ G„\€ZÆÄgĖãę<Ļįú7ÕĻøv;Uyvžcô=I9AZé˙æ*ž “?ZŊÉ>ķöLæ—Ām‘ĻÎÜÛŅĒåĨÚGĨÖĶŪįEÆ,ŨJĶmnģNūØD[^SÍĢnáI]UO¤įūâx™iž6øĶ•ŌõæRᴟãČũ+­Bä°R˙ ’?J˛n‡ØĘ[éAUŽx-ĸ2ŊZ…e SŅИÄ^wÁØ/Eí‰ę*ßböFĐ+yt~œrįv9ųã–zĻNî(ãŸ”ŠvQüŦâ™Ĩģžī‹ŒŸt^wJŽíĘíĐɧŠå9ĘžŲŗ’nǟųRŽeĨŽĐ✭âĪlĶ›?¸0e ZKĪęk•!€•Z/HūØĪēĸ(ü@Ēse“Fj•ĩŲęčęt Û_kÕ¯Į珏<}ļ]wĮu÷žn,7îö˗‡+s\?w×%šewŸūs×÷‹Ÿ]]žmœũz×ķ§éäŦ¨ĖÕQ5qĶ‹ŽO:p\Ÿ=uæ'ΟúÂåŠëŸWKQũîœéw=Ŋˇ]{MÕöÁÉũ8îí։éøŨú\Ąž~C 8î,íÕ´ô…‹ËÅaF>vq=ĨYrŖėĸ[ŽĪŽĪŪŠĘ‰g.Î)2¯'.Ž×>Ė.-ĩūū“ Ž*8”–…w䟭ĻÕû¨ōĮeÚĄģOŽüņÁå]'”|úÎīüjčJ+é‰ú؟´Dˇ.­üQkQÅ÷Ŋųã2mĸÛīj•!€•j$@úˆëČēĸ(LĄģ•¸w’brîkåLō”¤d9Ņ3uÉÉÔä¤Ũûį“’(%Yņ‡ÎŒ"šĖq3)ū°œRevęMÆĨ%%eQ˜f0ZhО–Ääää”HÕę°SRGY§ČąÉéô—j›CĒ#í–QJ&ŊQO‘Üä‘ĸXŽ•ÁÚ%vå¸ķ´M;¨ŗDŅ…ˇö‹Ē­H3´Kũz]ēĸŠø)“Ŧžë]/šŌ“’2)QsŧyLJrĸLŅ˜ä”¨īÕeewÉTŊVpū˜L×ÕĪŪiĸū6ŦĘû4Ų$[Žüą˜”Ëč䏆t‡››õFķ˛UP援P+(mŅÎM´ū‰¤hšĒü_°Š´wûÂųŦĶUōĮũ‡ąvåátBņ31ĸOvÉ zđķāŪũWŅSuIą>}zūĨ}FG{õģœB7¸ÃÚ( ZĮEŲīī,/õā?‰F÷ėšđÅņ+–põúõ˜+kÕGĄ _ō]ߤü’ĶŧÛę×öī99‚î‘÷ŪĄĢ(МĒŦĨ'šŽí=čĪDĘécÚĖ?ē8ĮĶŊíīCú:‰Õ÷Tn°*§ĶRßūIŠüņ^žŪIę?AWĸ%Ũ{ūtEžj§Ū¨Iß>mĶ7wëĶ7ûŽÚ%JÜ;˛ßŽH*8P§D-Ęüą„|Jå*ÖÍ•âã•ˇMtōĮD:Äu ‹i¤yũ+ŝœīŋÂōGw#“ÔĨ^‰c”oƒÚ앏Ngē—ί~1 €PZsŸ ?:rmY7DlåĘtRņ3$$ģ˛ô˜_û{ŽÎÍéīB´OÍGéåƒ==˙”SßՍĨĖūĘĮ€xõW™ŽüÂWTŠ×9\Ļ{x{â/’-ĄšĸƊ/ nĘsãLuŪšE÷+(QÎ)Í;EņcŌŽlå=eëĒč¤lÅ2íŪŨo’'Ģ_$ĢķGš/ŠŽRmĄ+Z]ŧSĩ{„ĸTÕ-ŒŽiį‡˛B;q\ûKŠë>ŊXŅæ/YGWĪŦUWˇ4WūˆTåf”}†‰K› zôŅ:Ņv[vÄūōō­Ļ)00ŦĶŪä:ßĩWīŪŊ{u-NŠĶû…ĖžēûhįO^Q{ՓߨFvaũ$ĪÚˇˆné|s)ß}Ûc Īõ¤°üQö7E(j'|Äb7ų˙ųS§Žšģ+‹ųĖ7%„Žņ×ÅGś•>2ųéŽQčr†í™hųŖÄTG"YJŒįdíRŨüNĒ_îĸNšĸxõ-øœüŅũY’Lõ?ÄĮØüĄsš|Ii ‰*$ü‰&ŸüņÁž¨4UÜAZaü1=í•úY!ũwwŌqåCŨįĘĒĸU#AųãoŽk”ģwšvūXM¯ĻÍU˜}™VĖY؄/œšé;Îė]äūeŽč˜ĸ+ē_Š Ëƒuū™,â öš5.ä×Cū3UũųMÚõėۃ2ĖIŲ—TjŸOŒYß0˙Z=QįOė=å˜k =Ö*ŅÍÛI>ĨŲכbsÎú\|ėPÕcvū8L)NĢ”ƒšĪ:›<§7OŸŠ¸žĐ97 "oū¨@~‡†)ÎđįŌ<ÕĪ É=čžęûī7#Ž’?ŊXŠ(åô[ÜFŨsŧNūØĄķ^V/ĖwDũR>‚ŨöTöô ō”/tę*,T:÷ĖEÃõvŽ`_Ą˙.X‘•ĪéŌp­× ĸMNIZŗĮqŨĪdyo|;÷Ņl‚hųãÜ.ūvCīÛÚ§"ŨüąFņ‡öö$ē˜3éFîüŅY6Cõ¤t>ųÃnĒzĮkyīŸįÎÛôŨ7KÉ<÷ĪŅūÚ]ÁųŖ[lz2ÜYšķ‡Öß)9ĩ?˙ä/Rßņ(ŏ?˙"Åß$Š:˛ÖöŌôߍIRĘ/8pž´‰â ÉU.†ŠŽßĩ ˎū°zš”2AšųKøņƒĘŗúrír?Wų•Ējž~ũ’˙ĢíĐôd[Cáę_}HQüÉq|oˇkČP”T]˜v#ŸŽė‡#MČą'*æŠčËs‰á›kKĐi‰•?Š=ФzļYŽ5Đí[ūģ.QēWMâbøk*Ū)üįųŠô˛ŠęÉtō.ŖŲ¨] Uį”ĶˆŧV_Ųŋďûh@û4Û4ʕ?¸ŖäR]õäWr*4ôōŒÆ0€oÆtåÃÆŒ„|ÁCÚŖzėAZÔ4‹×ŸĸUãĶ7æĢÅĐX~N‘ôPŗŅį~´Yysb›fūDŽãŋpåÜĩ?/šeĖČ×6ʞĒq´Œ˙ž2šųÅ×ŋk|'5ķëēōC_žÍŠĒ]Ë&:RPà ƒüÖág:72ßßô>šjdöČŧ8(ߚúŸ$¯Mb6‚ķšm6dĄC Šī51|Čŧhę6|øĐZ|Éo´”+WŽü[Ų{ŲõHLZ;đSnd¯ ôíƒTĀÜÉoÃā!k¯§PÜėáš[[×ČuېõÁtHũz %l1rŅĨĘmĘÛ ™E]‡•s̏ū#rS~@_—”Ū¤ĐĐĩPžēú&Ī؃§^Nĸ¤åCUƒ†úQęų)Cf‹ĸÄåÃTlZŽŪ+(cû ávŲs|œĻ˜ŋÆŲ]Ī$ē=`˛l=O Ũ4tâß2YÎ ąŠtg’Ũ ŠįŋĒ3&îUU€ģé_õ¨ü#†÷ōÉúsāđQŸjUkÔ S4eX7Ջîčnë˙ã”Ķi˜ĒK˜ōōaā‰ÉCĻߥHõWŽ”ąäĐí!tūũėZŽp”úfô=@ä°ÕfŊIģÕŧĀ_Wí{*!.9ÅÉqņWW+°ĻڗRcÖŖ ˈwũĘĮUŲ}AžéoĮ—ŧũ$'E˙Í-Í$šRÚ~›ßøßž›Ī?ŪV–5¸›ŠŦ&+荜Čũ?üŽeŸ)^PVöē÷ŗ”¯•KYŧūDyķAãfÎTS܆ô4™,=Í'īp9ûˆ|Œo°‡2ŠŠSüQÍa>'TcŠ{ēâA‘@Zį4gö^[”}ŗ")KUķĒ´÷RūÕdaÚC&ZEʔEʏĒ^&*7ū]ņdŗō‰jčcĮœēĩvŧĸ.SŽäœ œ'Fšžæ]åãKe.XE_WuËōĐ,JXņD’ō¯Ą5ļdDvÕúÖÉŖ^0ōX¸J‹}SôÎŗŪmĐ@ũõÔ7ģxķķIo6ÚRíūųšŪģí¯î˙kņUû+*ööü­ÕzMņÂūÆŊXūēĶ€›Ęß^ũõ5å–WĮōûõūûę՝㸏7\žž2ģ˛i'íīĄu¸q‡Žž_ÂMš~åúĒwÔ]VüwCgŨŧg^ŧv0˙¯Š:ĄŸŠŖw›ŧÆŊzũį*Ŋėí¯íāŋŊ|<īüÕ3?5āFŸŊļŋĮUøũßü+ˇ:åėõÅúK—Ļqī˙qÕūr/ždôū̧sß ™qáŧĸ!=øÅ>V^WTĄ\›°õq{ûķĒáU˙ŊĒŽûúz­Ũ&ŪPūŽĒÎûÍOŲ_ļ?ŪFįÕ+—¯ũĸŦĒí‘ĒܗkO^=5VkŸ&ŋ\°ßŦŊˆGÅKöĒŋĨŊũt#˙"ŸīĄ§tÁ?ĀxK_̓|įÄIŋcäģ^˙fւáúį߅iχéü@>‡U †‡ŨÜüUË4JųŒôÉŧĐĖ'š'Œ[͛éqËle<Ãüąƒ´fÍ ‹ZĖõí픸¨›š§EōĨĖŌWä†ųcXŠæØÅîŅFVa€@ūƒÕ^.węÅ0€Vw2ãWįžHČú0ĖÜŧtz´bÚøIķÎSŽyŋŒ†ü†ZáFĻéßLTÃÉw=›ī?Âa™?¸17˛øžĄVŗ lōfbš )Ģ;‘•č—B—čßΒ1Í\‰Ž3~?¸wiŸÂ&á0ō ęō z0˜ujŨ3"׎;‹Å6ųôûųŨŸĨ3ɌģE¯­øŪ/ō؈âŦ# Wrʛ,Ū–5î;Ébæ”ÖŋĄEš”UđÄ/Vä|˙€ÂTYęGõO?!Ŋ~7˛B7YįÜŧ?Č[ŗ@åƒXG–l…;ŨĪg†j‹0éyZåU,\ŋëWP ę3C2žõÕŋ3­œĶcXßwä°¸øx‘9I9Kĸ)ߔ¯¯É: #!€@ū€ü-zM§”b…~ãČwë Œƒü6ųōņÉüĐĖ'–|åJ[ëéąËķ,ĘjÁ?ĀF @Ĩ–ž$§ ŦŖ0ÂPGYājëOü6ųr›æOŽcĘąŽÂ(ÅF8RĐ ÖQ ųlōč¨ĩ$Œœ¤^JíîdÆ­ļŽĢX˛le%,(ęüY–dÅszđ#ë L4Ō‘|Ö[ØôŸä[,RY8sņ"ĢŗPũ°<Šõ?&°ãŪЋÁėįØ5U‰~÷(lë(ô[G™é*ÉęgÖ##>:CũŒX˙c QuY =Ę: 3õpˌX_ŸuzTí^ˇRe^ÕĘÖĻRÍÚü¸RõÎŦ˙1ePÎą;›u˜p‹<7° ¨(=5$ËŖĪûŦÃÄ[˛bg[û…Ö Ę?z4ŠupCöfŖõ͋`e–ģ̓éŦƒØä[ôWąDTcVHÆŗ~ŦŖAkįô˜…Ÿ°ŽĀFUXčMÎcXG!’!ˇdAëj°ŽĀÍM&[È;)įæ]Ë:[Sg~hÖ[ŧrĨ­Íƒ 뚛ĀŌ•XęNN“XG!aˇåĢ+ąŽĀVLņ§gŖËŗŽBŇßĻ eŦŖ°5–ŧ!'[ŋrĨ­ÃĖØÕ˜ķĀL+\éÁ ÖAHĖΑŧ֕d€5û†<ZīģĻ*Ņ÷…Īg€ĩǞ4€c#Ŋ3Ã××e€5ZņŒî˙Ä:†&:Đ+Ėj`¤˛Sƒ3ŨûŊĮ: Ļū÷ũãĖØŲX‡`E*/öĨGvŦŖ°oĘBÖ×b€ĩXæAĻŋÍ: Ë0؁^¯g€5¨Šœcwë(,H[åÜŧ¸“P¸ō ŊčņXÖQX˜Ą˛ĀĩÕYG`Éæx‘ķäĸ7ŪC¯ąŽäˇ†u–Ēöü7YOúŗŽÂBĩģšyōUlé ršÂ: 6ėŽÜouEÖQXš‰äfW4æØ5U‰aw(x ë(,Iĩ%!ä„>Wúuē“Ŋēë(,Å z0“uVbŒ#Ŋ^÷>ë(,Á¸ųËčse¨ŊRÄ\ÖQ°Ve‰?=Á: +ĶĮ1+l}ÖQ°´ü9=(Ęsėšj’ŊÄŦ&Pd•WÎąÛķ\™¤ŨĶ˘Ÿ>f•ųĶhÖQXąAˇdÁëj˛Ž@jK=čátô#2‹rnŪuŦƒRÍYÁ™Ī˛ŽÂ´sJ^ô)ë($ōŅBOz2Žu6b˜Ŗ,pu5ÖQHaĻ7=™TŽu6ãíąˇÉo5ë(ÄVkūæØXûûą+˛Ž@Do-}NNĶXGaƒ†ß•ûŽÆė“`ŗÆŌ‹Q8ˉĄäĐ{ŧˆub¨˛$˜œąŽÂ†uš›ĩúsÖQmųSz8›u6nÜmō\‡ņü`SÆË<û•a…Í+Ņˉ"0Ÿ،*‹ũčŅHÖQũŗB××b€”sėâʕtĻ8ææĢWqZPĻûĀ˙cFŅŌÖ%3ú§ĘŦŖ0ÃĮ ŧÉy ë(Š !˛ ĩ˜›Ŧ֒—ôčĮXGQ4Mr$¯ĩŦƒ0EÍŲÁ™Ī0ۃī•sķÖc€‘J/|IOĮŗŽĸˆ~[°Ē*ë(ŒņŖ7šLÄL%ŦŊ7æųũĖ: CÕ\đFŽ9v-Ä÷÷3cV4`€!–>#§YŲFŪ%īÕąŽ@Ÿąä1˛ë(@KŠ!(dë( SyI9 aäŅũnVäęúŦŖôA™Ōz0ûĄrŽ]ĖŪg™&ÜĻWëôoöĄū7Bé˛âG Ö úĐŖ.Ņ1†ˆö:=ķĢBj$ķė_Z˛ĀÁ8Å{:Ë#gū§ā ę˙p䕁ī„§íjK9XĸâsÎf‘Qîūōmž5U^äKNŖ$ŒĶßQöf]ūŗš|šņĒqoųõ…U$,Hw#ŗ‡R`~W¨–=ĮęPÖ`Ē#ŊĖī*ÖDOãßrw;ŠÃ QųD†ņ' ĨįßëVôņ´ĀLwĖTbÚ?͈š“kLú—ä&Ŋ˛Ž`z€"é3g͞‡BT?­z*Î÷ĻĮc™5Œ5ÔQ¸Ļ†VA7“ß ^͘5˜iędōIƒ(zzv= •sė~Ȱ!`´IˇÉkMöĢaafŧÜ;0l0Qõą' ĸ8ūˆjŽŨÁŒ›Æëā”ĩˆŌ$ĘŦwÂËό›RģlÖIƒČ÷ŽĢ¸ČžLdŨ0Éđ;r˙•59ކy$ˆž°n Hk’i7Lĩ\äĘŨĸg1S‰ĩ*>ú.=ü‚;hîæ°n HŠÁkŗĪqvÜÔŦÛfé9ëjÎÍ^h Öí ­3û¤AäXŒu+Ā|§x'ėeŨ—g Ę :X!ہ Yˇ$ķg ÚÄē`ļ‚ŧ0o @Ņņŗ gWŦ›fģ#Č;aëf€d9k¤ÕaŨ0S5ķīž+%ąnH&RŗFúPÖí3õŒ䝐Åē •ĻąÂœ5æąn˜iR’ īY+Ö ‰ôKæŦąF˙ĄĀĸÍMä Éē! ĄōĮjÖ 3ÍAūŖ ųŒƒü<ä0ōđ?Ā8ČĀCūã ųŒƒü<ä0ōđ?Ā8ČĀCūã ųŒƒü<ä0ōđ?Ā8ČĀCūã ųŒƒü<ä0ōđ?Ā8ČĀCūã ųŒ3ëׂŠPë׎fŨHÕhAΙ3X7Ė4&AwBVmÖ ŠD rÖHīËē`ĻNÂ|’ąnHÆUŗFJIÖí3 äĖē ™É‚œ5nŗn˜í¤ īÜ(:>äŦņëf€Ųú ōNčÍē œ4äMXˇĖ'āđŧ&ëV€tĻfšÖø‡u#@;Ė#Č~fŨPą{fŸ5B;ŗn ŠŲī2ŦRęŸbîYc?ë&€ ļČÍ}'`đ9@ķĢ™'ģŦ9eæ;áë€ÔΙuŌxũ5ëøA ™7ČņÖ Š;mÆIí=ëđA0™ņN¸Y–uøĀĀą,SOÎøĖiKŪšlęA~•uėĀÆŧdĶÎ‡Ģ˛ŽUl—iũšå+XGĖünô9#áë A7âŒ~'Dĸ @‘ÖrÛmŖNŽûĮąD1|—ŗ1cŅ“îíųžuČ šŽn=´^Uh9iõ?÷îë÷āĖ–ēÔŅÚŗ’ÃJŠCAMĐHëUÎ“6ũkĐ;áČúZWŌÚķ3ĪáR‡ |°˜ü: RS•ktĻž 5 Õ÷ŌÆ‚ÔôŲkڎ~X6¯Ė:^_ ēŪ_Nž=ôoŠŲsÚTN ē* Ÿ TX¨&1éBÎv×˟æXHgbzčXĢ›’–ÔSĀęĀâlĸg]­°úēˆkXÖ§ė rö C‹´ã}AkKr‹Ž}"tŋe~'t ˛FĪ„Ÿ˙˛Ō1rēN°CB‰PmĪ€ôÃ˛.sâĸ‹Pí‘qčŨ `“6Ĩų‰tŗû6]¨-NÍ ‚*č‘8˙ŋšžĖ: JÅĀRŠstņsą*_GĪ[‰U7ŦŪ#úMŦŸĒŖ›XĖĀÆô ËZ(bõ_‡ŅRĢáŒĨ¨Ū"Vo—3^Äę@rk˛<ˆz€OÎŌI|đ´|ÅŅÕ&ĸĄãSúĨ¤¨G)]ŖëÕD>Ä[k(¤–ČĮŗųĐŪ÷D>ÄĮ˙Đ‘R•¸X‚ô÷ ULˇ@îß_‚ÃL‰I%Áa@t[ŌũēKr ēWéĻ$͜’;5Ōŋ•ÚģĐIb*y…ŽŠÖī*ˇ ^ÂLÍÂûÖUžŗ„DĮĒz’ 5ˇ02ōMú| ×:4}Ŗ„‡ÃÍMŠ"áá&ÆÅΔđp ¸ÕÉū#$=`C{:QEŌ#‚!J¤;-%=b/wŲ6IBĒvÎIŪ•r yw”ú˜ Įįî´CęcžˇŗŧHnOÚ‡íëM‹ßap\(ĐėÔ !įj7ÔÂøČŅ  f[ŸáׇɁ\ĸķL ų;NÄ3XÎŽŌíŗUģDËŗ:øęÔ°öŦŽ š´ôĖØÉėāĮčaf“ŒHrAcõņĘØĀđđcIBÄp†‡Ÿ1•ááĀhs˛ŪHÛī*ˇFޏ†eK;ĻtņĻ-Lc|å@įXĮĀ-ČÆ:†"¯Ëkb˙=đ=•ļë0˜Ŧˇoš%|␊9Ũؚņ#ë8e?Ŧôð ˓#ząŽAåŗËtæŋŦƒ(ĘŅŖ¯YĮ ŌÉ7ãÖ1€^u¯Ņ%Ö1d[—Ō™u EVwb×ī*ˇ#äŌ€u P¸žY+XĮ eØë´ÕŦc(ĸąN`ƒ–yÉūX—Āĸ-‘veƒŽŽp+ŋ(ÚKOšŗŽAGoŒ%°`_\ĻĶ7köšä@)į}Ĩ^/ŗļŗŽ!Cäø-ë ]‚ŌØwÖĖkxpę2Ö11ĸg°Ž!s’Đ Ā2­Lņg3ߕ>ĩoČ.Õ`DRūˆĖé3ÖAä̃{úaXžÚ7,xņØ ūŊYĮPd´qŖũŦc(Đ zԂu  Ģŗ­eC!F„Įc,Ą4~ŒH´¤~Wš-ÉđÄ:ĐļFeá͜^§ëĨYĮP|p„Yö˜ÍĒŪ´ëmÖA€FũStæcÖAčŗ!ëÅÖ1ØŧNδã-ÖAčs„Ž6cđšŅâbŦƒĐ¯{TúbÖ1ظqÉņ–|íJc…ZfO€"g%šˇbƒA>ŧHį겆Uū‡n×f„Aę>ĄÍ˛Ę\§s~ë#Į†tŸūŦc°Y­^dîcƒĄĒėŖģ_° ¨kį—i‰c b‘4‹u 6j\xŌ4Ö1aIrtwÖ1m[ŗü­lJˆt­>ëlPŲ“äĘ:ã|éJ{Ūa@ŅUë4]ŠÅ:cm•ģucƒÍiu—ūdƒŅNĶíĪYĮPTuyM YĮ`‚1ÉKXĮ`c&%dö`ƒ ffe@‘Tbųtd„i.͉zŦc°!U÷’ƒuÎ/V˙%m-Ë:€ĸį­ķtŌZŋũ—\FžXa3ēҝåYaĸjéēut9°!ßFĻ­bƒúøŅlÖ1؈ÉioưŽÁ ?¤¤`‰cIm¤ÖxÁ;Gíst<Í÷Ū ēkŨ ‹ˇz€u ¤tnXæF؞鋙ŧÍõÕSÚoí“Vü—ž˛Ž Č’lũŽrë˜j͗ā,ÁܸX[˜“rZTÜxÖ1 kĶümc÷éŒÕ _ą •wĶCëžvĨņŨĢĖ=Ŧc(Ū:K—ŦĩßUno­§įÍYaĩĒ?ĸmąB 5NЍ*Ŧƒ°u]"˛liđ]ĶPZđ?ÖAX';ŠėĮ:K‹ĩændV`MĻĮ`Ö1ęŗŗt˛2ë ŦŅ!ēf[_Ũē<Ĩ_Ūc€ ŗ§[6w˛Ũ@Ą6×&Ņũī5°ĩ“mųãt—u 6kpxârÖ1ˆ ŗ­dƒ•Yh[_Cy?F'ŲąŽĀ6­O ´ÍU?ëŪĸëŦc°*ĮäÎļ9tĻŊ›|7ëlPųËrÛ]8c}ēWÖ1X–ŽōīŗB$ÕNŅũĒŦƒ°5CƒŌ—ąŽADCĶÖąŽÁJĖJŒļcƒˆ&'FũĀ:Û˛21hëDõÕU:QuÖāŨmÍ:Qõ}•ų+ëlHåët‘u ĸû™|žgƒÅĢûŌ ×4ÖAzЈu ļbĐëôĸpqgˇl1ë,Üė䠉ŦcĀâ„ĐŅŦc° kԃząŽAŸ_ŖsŦc°h˙KcÖ1HĸĶ ÚÆ:Pë<],Į:ŠŦI}ƒu ŌÆ3ãwÖ1HæŨÃĮfá—ö3ë$ÔĪ/më,Ԃ¸¨ĸtQgnLØTÖ1XˇYávŦcTĶģô/ë,Ō>zŪžu ’ęáKkYĮ`ÅžŧUú]å6O2„u §ãkÚÂ:ÉĻ'­XĮ`­zŊÎ(z' Ž˜ŧ”u fVDätÖ10°,1|(ëŦ͞äČîŦc`âĶĢtæ]ÖAX’Côø Ö10ŅÁ7ũÖ1XĄO/Ņ%Ö10ŗ1Ëŋ+ë,F;Wúƒu ˜$§ĸ™:ĖĐ×SžâmÖA°3Ė'sēķæÅFL`CķSũą.!€QP@Ö10UĮũ°Tū¤Įļ9WģĄž~M˜ Āp .Ņ™"ŋ&ßē?ôÃęņBž“u ŦŊ{˜Šv 0BëāôĸØī*7ģ7Š XĮĀØøØØŲŦc° ’#Đ Ā +Sü‹Æ|WúTģ!ŋX›u UøGū¸ë ,Â÷îY臠_m{ēÍ:‹ą5ïčöÃjķ”°ŽÁbœĻEcęH3|īFEaŽvC Š[Ä:F&GόgƒY*ķíĪ:Ëö3…Î:Ër‹Ž”gĨŅC[]äÜ4eüŠĀŌYĻû䝊Á:Kŗ1Ë­ë$÷Ŋ3í(Á: ķÎē„kX¨L+0oG}Ŗ3氎AbÃR§°ŽÁōüg…ua€eZEî-YĮ`‘Š_ĸsuY!ĄĘ˙ĐíęŦƒ°HŸ<Ą-ĨX`nĐĨOXĮ`Šļ¤{÷dƒdšžČú‹u –ĒÚ_t¯(}”0HŸ ,ŧW0ģˆ„ĸ2n|XŪ+Øâ¤HŒŽĐą5#׎ åHWŠÂXērGčë,[#7ÚÍ: Rã$]ŠĮ:Kˇ•\;ŗŽAt-îПīą•>M7?c€ĨčėEE}ĸ'C´KYĖ:‘KJ+z}•7=3pë,Âû‹ČģhĪÕn¨.Ķ?Ÿ˛BD•ö’ž†ĸąm(Ë: pžÎbČša>\JžmX!šZ/hgQjoŠšéz%ÖA°Ö&u ļjKjĘ^Ö1Î!)m,ëlÔ׉^ŦcŅ0Ö1ØĒ?ˆŽ˛ŽÁpOˆĻ°ŽÁF5& g€|ˆFąŽÁVí%ú—u †{Aô#ëlT3ĸxÖ1ˆ |Ĩ*oŗŽÁV•Ŧ\éCÖ1îŖJ•ßeƒ­ĒōqEÖ!€0Ū}wdy‰°UĨŧ ī¸í{ϘD‘iž!mvÉ7ëJ €múø\æ…n°vI €€ÆĻ]}Ģđ-ēŌyiB°Q‡išž-ŽŅLI"ШÔ[šJ&.]ņšnÉ\:#Y<ļ­Ë˛e}XĮ`¸ąK—7bƒmØIët *ŦX’;]Ą9’Å ˆ ¯>ÍUäE42Wҝ´^Ē€lÛnĸ“Ŧc0Ü3ŌûŠ ą€ä*iJ›Ģč?ĶH€ *øļĘ]öŠhDî˛ßiĩ4Ų¸?‰ŽŗŽÁp.DSYĮ` fæôŦ QTî˛ĪjI€Ēŋˆn™§pŸÃŨīķŪJ.E@ļnƝۋYĮ`¸ßīödƒ č—ō¨Bî˛ē÷nåũ"ÚČ'¸ą$áNęDˇüØ->ĪĐĢQPĐ'nÚ5åIQcÎųlÃ7ö ë!^$6Ē…oė{o<2íψĄh­4bëúąū5E ĀF=OélÄÖs2”-á˧Fm? é‰H‘Ø*{ųŖļ˙…6‰ €€ŌßFî1$ũvYQB°QįŒîŊVˆ €€F¤ØW6vŸ…Ö´ø*s;hĢąģ”>™ŽŽ`áž 6a¯?éÁ#°U+˛ŽũĮøŊžÅv>ᔋņm`Ę~Ķ2ĄC°Q3So˜˛[ņŠß  €pę:$ô2mΞ †`Ŗē§9›ļc›0÷/… @8˙ŊHÆô)ÔVķi&Æč×$ĘûkwmœęT[ĐX„s–æ™ŧīÛnņí Ā65 5}$Į衆  š'“6JËā€ę`ŗ<#™ą÷Ú'X$ZK;ĖÚxĸ‰—uŠŒËé3ĖÚXĸ™i—ĖŦaD֍₄`ŖNŅ3k8–†5wĀⴎ{dô¸ÁÜVĐ_B„`ŖvĐnsĢx˙ĒlˆĄ§g¨—ĩŦ§ÍÔ`›f rņéaühjL­€Đ:BÔŗã 0ŽŊ+D=ž1čę–äi’@K,ߑ Ļ"Ķ&ÅS˜Š:…š2É€8>ē›fę¸ÁåqQ ÖĄUvY×ߥÚƝ"ĐWXzÕOœŠ/dū$NÅP˜+b“ĪĪúēÂûZĨī•ûė‡Są IŠëZĀ,´ĸktŌ#q*n`Â2ęÂ(q3e„XuŋŒÄ—k‰5YrbîĖiZÎŧA÷W´”4&āZ…ø 5n0ˇ^Ņ.ĩDĒ@ŸC4_´ēø‡}"ZåW““očÕLJ¨ĩ[îL—ÚJ(UņˆũV´Ę‡ĐyŅę(ÔVQ';ü*ܯ™ˆÕƒŽļö”°ßķTŖŅäÜQôx@­îŗôoDŦ~ ąv€-7-‚BĩIuĩ~ČÖņųm+gčÖŨé߁b†9îČDģDŦ˛™~ĩ~€| • =n0ˇé)ˇD>(•;Oģž0b‡ę[ŌĶÎb>)•/ųh–ČGČŖ]ũK˛NbđšØĘ-ËL<`ô^›ãŗļcHˆčöH° ÚI2oEuŖõreā‡Ž_é7ņR´ĩ}@§LéRõõßô´¯āŅ€ŽÅ$Á,‡ĩîĮ Ģ#HdžKB )ŽsšĻKq˜ĸkhzŒŠŸ>‡‡gÎ4ČeDú-)ÆÛ|ĐJ‚ÃhxÆIԉķ"M“æ@VŽZãę&ėÕö.]/oō1˙ī4šô0yoĐgdš“4ĒĐHš#pÜ˙Î҉õ_įÔ^ĘJUôۅ÷Ÿûē=xx˙ūíŗ+|eđŽ“Ã’Ö›uėyQ †O [ąíŋ]uŧ¯đāžƒũÎŲ=Í:´íkįYSĸCõM~,ČÂę8Bv’̉W|YÉfmūWŠ|BŊ¸õ׎Ũ{ö]Ŋį–‘šįšŖq)öŪ"wlbf_œŖũlö^Y×"cã“ŌãŪģwīū™;ĪŌĶ“ããŸ/–ęi…JúŊlQhŊĐ ôTi득“đh_7đpV¤ãęįuwîįøōŨ—ũå@géû|˙] ˆcYWôlŅzöÁWéæŧzĸÎŒ_ >ø<Šœ×|.@6¨ē{œë ę33ãA3萤Įë“⌞ĸyU:)ŋøsū¨~š`Ÿ+Å9Ų˛{ųĶ [M×jōëÎ×B)䨚ųŽÖaĶ ō?$ÆÜ˛VĪ1UÚ¨ˇŅIEÔÜ Š?ŠLĻŗŅō}w,%ņha=¨‹ĩĐ7-jGA›4xD?J/rkW>›‘š¸Ē]aˇč_LJÚ)ŪOÖj—¨sågŋĖŧ{aõDōcΑøÅûō(ŸŠ§wŗŠö˛ķCōûMûā¸ą‚EĶ/4!ßnX]Ļ¤:čŋŋŪüB$ũŽū?:vĐ*ɏy3ŅAduC_6”ū¨ÛiŖôĩ\ŖÃiĢasKļŲŸz*īÕŋJÎYBŽũë÷úŗŧĨ%¤í1l…Ôv[É’€ņXŊô¯ô­ú0ĩôG…ĸ¤y@ƒôÁqĐ<‡ĩHÍn“3oM’mÍũ˙ėaš°3čÖLtËĩŽDí•))FŦšÚâ1*hŨ‘ĸgcqØ:/c¤ëņEŅŗ„ÖLŽ[ųLÆT&ļ<ķ’6ĩCũŋĶBtî;&øŒG#2Škŋžë™vąQ5üģTȈŦØđ¤l&§Ŧå¯˙ĸ(€ŠR§°:´OĖ÷ŦmQv’SWc÷đNhŊÜDÂ÷ŋ^¨3dŊ4úöJGgÚ#`@ÖëŗāXVƒų†Æŧdtd°}ī˙›)TŸŧJkÎîā–ĸôúŔũæČ‚‡iž˙Jۅ (ÛB:ŦyÚÁļ˜RÅzdĖ,ō6ĒdBā§Ė>2éēÁËĀemexôΑ.†ĪĖaŖ:f›UG5'–÷ؘ[ĪôąĘÛäEų˙ˆĸB⠘Åp9ũÁ:fú¤{2ŠnaÆËúžøDhōķáũP™šĶn”đŠ.ēËŽĸßY‡Āq=L7eAJ€5 ņ–`šZũŽĻÕIzZ‡z1h°ã“db~ļ–œeū0ĪĒŽ)Eõ&úĖTÖ!(ÕyšĐ†u `K*?NvžŠŪˇ—waíü…˜dpЏwė…¸úōĨ[hĒą>­Ÿ}Ė:•o"<,âĶ"؈ķivŦCP{Û)v8ëøđiH>3L™ ’ ĩD Ķ_ęs“{pYąîoŧKŗŽA­cĸ+ëĀvüÅēSˆ˙0ą:Y°CYvŦCPßŦ‹ŦCŪ§~q–ŗãø´K…Mē`¸M´™uZ焺ArĶ2ˇąAR[hë$į’8uZfĶÖ!€mX@;X‡ ŖC¤k Ö1HĢ'ƒ5WØē”ØŸuŌ*ë6’u :VĐ^Ö!€-˜–yÂÜ~Ŗ’yƒuŌrû’uR ÷ĒĘ:IũKsY‡Ë^2qĒ€bŸũu š­•1Y•ir ēũ$‘ąŠEĒŖö/82özúPÖ!€ĩû&͝uųXmQwdDöUrûēĨrŒŠĐwŽe9“[į Ëē¤Vįk×P6 Féq0k ë$ŗØÍįÍN„sŦCĖŲ5Ö!䧁×,™f¨p'ĩëōį@ũX‡ ‘ÎÆŦkCÖĘėX‡ ‘f2 íōu˜G‘_2ĖđPfąß`¯Ë{˛AO‹Ѕ-īųŋb‚4Ú%>{‹u h›æVu `ĩöĐ|Ö!¨ēGx[Ö1Há;ÚÄ:FæQ‘˜HąŽ°…~ĮW˜”y•u`­ū$s'SSë7žŦCÂåP˘IzĨ=œY‡ …ĮI–dX“šĪ"Ŧd‘Ķ4™uæĒųWFčKY9Č:ßL'Ä[Č]"]č&ë ĶĖĮ˙ Ö1€š™9˜uĒy?֒–M0A×Ŧ}īŗÂę”Ú—ūĘĘ/bu ōbIb)|›ōÔģ:‚8Žg­a‚Ájy[õíÔ~Á‰;Æßĸ Ë:sT|"ˇžoPŗĶY‡Öb ­b‚ĘE4dƒéZ°Áj…`‚^'Yî°ķŧfÚPWGÕRŲÖ!ĨwōCĢōąGÔ÷Ŧc°Z‚ÂßcƒŠūwÎĘnÜ­ĩ°UHÁB  [ŦC0Ō0ēĖ:•ŋ-ˇōģ7LuOrŠÅ:ĻņŦC0ŌšÍ:°|ŊåšAĄ~”e‚iΉ?EāIīW*ž¯îYĶŌ°}æøúüŽ| ôT.6ß×GųŅķĻ—ŋÅŨîmEV:Ŋß>ųoŦC0ÚqĮ:°tmÃŨ+ŗŽÁxŋŌjÖ!˜â[Ú'ú1ž–€iíŗ‰čĸōQą§ŧFĘĨœ‚‰:Чi~ĄaŦC0ŏt”uÆ+į˜`•l0DéP‘{ÖŠX>ŋ¨P‚uû4Ęä]š’ĮŌ–ŋ+Äā#Iī¤<đk ú1œ‰ŌSUŌŗųĀ ž^käY'•DYœrŽ :ŖxđL'ËTãšˇ”‡{§Ŧo˛3’¯–û(ŋwŸ”m)Lą ųDWĒA@`ŊōBüʲnč¨:zįųĮ>ŪđŠ~ķ:ŸbŸWw¯čÂē™Ü§ãw]xšo;=’„hžĪķ[‡ļ‘Ē9sĨ¸…ĒČ?r)”ũn˛ây!ûŧ_ž|IåŖNū([Ž‚˜qšhĸL˛ožMæ¸î*Čŋ2˙ԗ^ųŊûž]Úû#ûÎ譖tx™_;_Æ Ņzo—‹ģ'ÖfŨLPĢuÎ=ž$r÷'ĻíüæÚĢD š) ¸-Í|q•ŸēJpEΘ¤y^ë%ŗØ„Nū°PwŊëIrģ[Ū™ŧûR_Ũe{•p’Ŗŋͤ$›ė3%p\ŊƒIRü˙æųögÖÎæGR¤k§ģ“m&)ūšŠü‘ķ-įJ_Č?+7vƒã3ųš ÛuČÖąĮÕéÔIĩfŽNūhŲąs)ÕÆUGš}įĀ@õm˛:wü–ĢŊčŠÃžq¨ŌrĶåÛ7ūš'Ͳģ_H2aûûŌŊųŌĪvАHz=“ŽŠ'la3+7Z’O Ųdg-â35NŌvfžŊE“Ī‹~ .WūØKÉSUOēŪQ7ÔëSåË4­Ļ?WŪ•&{eąNū'ReÕž.üv.CT5 &ōiøBUrAuMĸėÖT~ƒđuR´;&ū8ŧ}Š’Ŋķ”2įˆŪĸü͐´Q˜žąYR\ŅŅņ”ÉͯĩŌūV¸\Fä&-Nî%ōTųcBö‹—[EųØÜ]qū߲ø/E;Uƒ}´O÷9nßãJ7(>¨(‡:~īĢøzļv…ĸÚ°ī”Ûô'ōwH˙sé9ÅŽÛ”Så”ôīúÆIräM„ØyęŨ#Rŋų2vŠÜ¤|ŊMęvĻŽdŅNĐØ&õ˙pߎ˙bĐÎ_‰Û&âÖ¯Ļ8Ņoę?dČĐ.ߎܤø°ņŗĒđO"Õ(ŸVŅ3ĨTũDŅ\ĄųŖ‚Ģ:MėŅ3åMvEū Ę؃Wš*¯†Š.Š}ž%Í48ˇ"Ä­ŋęī>{q۔Ÿ&î Ú)ū÷|(ĐĖt˙ĮÉ]ōv.“ąh§ŗ¨mú2z“¨õk8Ģ[“Ŧøŗ—/LĄŦŽĘĮ×(yzÎļ?gPŌ(ŽĐüaGô„ßø!ĨĪāTų#aŒōõ§/)´›j§~ƒI“ū'Aû8nIĸ¸°.°xķŅQ۔Ÿ‡LÚųŗäíĩö’tģĘë”Äí.amŋˆŲ¨Ų$ÍÔÎÚ-ēĢ.\}!šr”§foÚ!øaŅ…äCDķų­WfŅqN•?|ø{bO(ŦŋS; ڕŖJǍ]xW°yķĨKŊ0ÚlڙlL‡@’´ˇÎsd-”ļáŒÚ)ęēĪâEŦ\‹"øģ{xx¸ŋô Îĸ˙oīžŖ¨ļ?€ĪķŊ' >TAC¤ˆ€Q)*"Eũˆ"ME^B‘NhÄJ$*‚”P’ ĄEHH •ôž’M;ŋŨŨM6Ųdۜ{wfÎįˇdöæī%ßŨ™;Åæ%šũ āi|P'Ā[¯ŠüHxBÜŧß]ĒĨĪPão*Öíi9P¸÷3–rëɡ>GĐdą;4i ĪüHāˇ^‹z á8ā!ĖęœČĩN´o`ΰēđ‘Y~,Ķ@Ā‚ îqn 9úüø ė…ŌÍ*ĪĪ gęw,/*æĮ§{ZŲyŒÜÜ}Ģe>į~-Â*ĢëA4Õz‰ÄÆsņHfuēs­3ĢŦkŦŽō¨ÍãUšū đƒöN0€øÖÖW›ē“'æhĘ\ ąŠühúĪ.īgAŦnNųüØ šč>=ņëĶųm:žâ:ûđvë”w‘k+˜ÕIŒÖqņÄîŦęÜÃĩÎ[XxēÉjį¨6?Š ĩŠC‹u\é˙֐÷^Ÿ>C!t—@ĄŊŲøtŸ~}u¸UšūÕ'éëŅģ×4 ŒŅmS>?”Ŧ܎Ώh€]Œj\Ôđ3—¸Î>f‹˜tįZ'ë’‰ Ŧį:â)¯ŗĒ“áÂŲÜéTV Ģ+ļ›Áb—ĒđŪ ē“ČOęîĮˇYSe~NįLWب°˙ãË;†vŠO2*Qđ¸‡ÔpĮPäųU5´OŋåuäZ'å{œķcĢ:ƒŦwQ,ÖyÔĄ§j ĩ\Î?ĨÅÄûL1œĶÜpÉ ícŋŠÂ( Ül)œ1Ģy­nũvũîĐlĩā°ö&@ŧå›;oD{›ŽŊ/Ū 2,mŌ}Ũš|mōüų9ŗkˆCjøŽįXiŨD*Ģ‚n‘\ë¤ü`O-ųÁ÷›YŦü˜kpVĨ9úŊ:8įG(NUQ~¨å HųŅ <Ĩ]ĩ:šü Jģ”LP~°GųÁR~|ƒPÚUĢ—Åk’HŽōƒ Ęö(?XĀɏfˇ1WŒUŖßãPŽBųÁå{”,āäĮĀúâJ÷Ί”LP~°GųÁN~,N|ŖYkyå\;Ę&(?ØŖü`'?‚îĮhVÍEc´JųÁå{”,āäG>ģ…)ÔÂeŠdĘ&(?ØŖü`%?å ChUŨúጰž‘Ũ(?˜ ü`ōƒ”üXœņ$BĢęV'~#BĢ”LP~°GųÁJ~ŦĖxÔúFÄ.5ãļ#´JųÁå{”, äĮŠŒ:­Ēۃ”N üPʐōãq„VՍōÔĒCųÁN~¤!4Ērĩ(?œ@ųĄ:”, äĮ*ɏgēöhëČën={6—ŧ3ÎŠŪĐ(å”ėQ~°€’33Û#´j¯öF'üҁWŽHN^-ywœô|Ę„V)?˜ ü`O-ųÁųúQŊJęœ=ĄU;ÍËĶÕ÷×cŧtĀNÉû㤑Œ‘ĸëG1AųÁŪŽ#ž2Uū\ëŒ}ŖĻâ-­ÚĨņ=(XáV˙!G^ģ ĀSâî8m%Ęųįnp}W0j˛¤G$×:}XÕIL–pņ¸ļŦęÜÆĩÎ0”ëŨ ø/FŗöpHuôĩޘ&b´Z/ëėûŖ&KšņÍIZ·ŊšŽx$ŗ:ŋáZįU”š&Gi׊jÕKīׯ$?Ę]•ŊzšĮÕĒ åōŖ†ų÷U¯æLÕCãŽŌî ŽŗŨ5Žų~K<YÄ¨7×?ËŦÎá\ëÄųdí–đJģUi0íĐđKžętų~@Îôš_”ūüÛ9 „įŨ đ4]ąÅ"ŋ°đ€uoןs<äæņÂS~<į~,ü–˙ę7 Tŗ˙Jx¨˙žącWSŪo™ģ?a+×Ų7Ĩ&K|šÖ9–YÄ¨!ס 1ĢŗŨMžub,Ę'čž|œ†ĶpĨ^8]Ŧ/čî.ŨÚ)ÆuÉĨhÕ Đ?™jøįüîeqŖ”•†&N‰˙Ҙ=ƒ ÍŦĶ?ŧ[žø8ĪĮĄũ*Žûŋœë× ƒîą™gÅôÁ)ʂwK8Öúŗ:‰ÉvŽ#ޞŗ˛û9–YŒUÔÉŦžXM[v dz÷…ņžÚG“ķŗJ2īŪ(Ũ ā`æÚū´qŖĸw,Āš_Ũ 0ßÔÄ´~  Āũ#!oĪûũh“iŧîņx€øÅŖ; dũޞwÎ÷ŒZ)gß)Ŧĸ,ĐpŦķ7†uŖ wų8˯_fäķĢķ ZUÉa=ĐÚļ`ĀiŨíĶQ¯éŸŅ~Ž0ÛB ņ­tw˛ PŋŧüE€õēÛwŌ!§Ŗöv!€Ÿîq{mb~œƒÜQēÛ~ąpįEíí-€gõm§ãė¨D›‹šĩąÚŪČoō•,Æ*ĘŽ_`ĖbX'1 ā7âã–ųŋ`nufEĢęŊ´´hWĐ<2ēéīM2„˙ŗægÁkķcĄūÎ6(Ö}Ūx VüÉ1.Â!ŗŗūņ§ >1āgq‹eÅ û[Wdü\úęāšxĩTđhø]”‹ŸëŊ˜ÎmöŨA E æqĢķf ëŨ#Ō{‰ÛˆŗũĀų*ˇ:ŊĢę™úbķæĻæÁaņ’š­B!I÷ÉĮR~ˆĢƝ†’ĨڛUâ^ ah2\r†fęŋųŌj*æĮ6€6â3]ĶáÄC‚ ũ@ŧŦ.z-å ŒAũš†ÛėûąĒŠ<šÕ9Čzį†}œ<ķ ë}“ŌNu&āĶcP/6w*fûeíØ$ŪĢëYcËų!ŪY,~mw `ŒøDÛpˆzE˜Wâã:~b~DėđØĸåᑠaMa…ö˙YāÎ l/n2"#š+fûÃ9Íž ĖĒ*ęy‡S'ØÖILZpZ]áÆuvŧͧN¤ƒ¯Œ:]+>Ū÷Wy›>L>?S¨*?ž8ŠŊų`¤øÄ˙‚!}°Ë°;D뀘fßė$=§}æˆî^v¤ŗÅm„5¨)/ƒųLž¤×pËĒ` Ÿ:cē1Ž“˜´ã1âëŦwLb}yÔ9ŊŽŠ™pđEôß"˜åĮ(ÔíŪ¨Ąļß ŲKŅËXĮļæA¯ĢŧšʄDŠŽ^O`?â;ëą¯s\û:ˇ08 ŽšGfęĄOPÖH1ŗ¯4?ö@ŽîŦAĢųqŖ4?.Cb˙˛Ÿ?v‹ų‘aé0î×WøčVLNĀß÷Û`¤o\žįKčŋGV0Ÿ|éß2¨ĢœjŠ™×™J§rÕį:ë_ÉĨÎ!QŦëÄ˙ôĄ×gW^AÚšMČīÂ܋ųQíHÕUf5?~1åGãé(Ė֘ōcŸ˜LûĪÍ5•şK]‚šküŌōJöāgfë?Ŧ)ŖØVÎtÆeB4ģCHˆeŗXxčHë=Âq‚éé.×Ōy[0ĖÄũŖŗĮ_= qēŋúVķã+ĶG–‘épžĐ=މ›r ķcĀËįžûĸē ´3žŌ˛ā'Äj´>)¸š›ŨŲŲB—+E '_ã]įĨúũÃōģēœ3ŧę$Ĩ>üƒŲ€Į˙āÆ¯Î‰Į™ÕŊôiļĩŽÂR/rÅ+aM¸¤ģĩšML ,îØ#čN?׈+a .ķc(@øúgžŽ†5…YĄ~†Ķ>ÜĄųãÛ÷,W@ĐĒ÷-ģ]gX¯mSFㅑĖę<Æō,2RŠjƒ7Į• ŋo(.ņ˙Ŧ×:k ۙ†˙EBqÉą Īą.-û ÆU†Ĩ„Jč¯ģc5?tKĪîÕŨŧi´ˇDÕŌŪ>b~Ô 8¤ÛĸÅ)ȝ ˙Ä"Æ`ƒ$ĐtĮ­Į; ˇũŠžũx?0˜}9{>bžø¤™Ö1¨ŗ$uëČšÖIĘę>mŨųŋlwîBnÔY;ļ?ĀŅWÍVôūrc =u^ĖģeOŪ˸|#ģ" ûŠļÚo7Ũg/7ūˇž-..œŊ&ßx&›/@Œļ‰„’HÃú%Ŋ´īɯ.˜ŗ Ā[÷Xûüą%sįü€~E‡z×wáūË^_´+ĀžŲwŊđŌ9{fßļoúķ(Ģ‚7–zŲSįŲ›š {ę Üü5ãÕ߈ÄŨbwmžZÅÍåŨ[Œ…÷CÃŊ†w~ŸÔ?vH7ÛĀ´øČˇâų‚đŌIņ™†u˜í¯Ûnŧ~moãŅâHû¯ļTâ]]@ÕQ—Œø0 õÔF—1# ûíq-"ŋqwŨâY.Bį°ļøË;&;|“ņ+Čfšlķk€ĨdˆwĻįŨ5ŦOķ¯ņž)9—55mĶÛ;&ãÄpaíŊ܍⏏ũ9++ņįw ?˙÷đm˙dgÅz"y%K’^ÅūR˜˜ÃîLJžžK‘ÅpÉP~¸–Ŋwy÷@Vĸũx÷Ā&”D™(?\ËØB×øæ[^š7wlBųA”‰ōÃÅD\äŨ9œÉģļĄü ĘDųáb–æaīAWŽžiČg'J…ōƒ(å‡Ģ%6X]xwÁ6”D™(?\ÍRēŽŽZ‰gŸČåQ&ĘWĶ&"wdÂ7].gŸQ~eĸüp9ˊßäŨYč•ģ•wlEųA”‰ōÃõ܏͌wdāņkÉŧģ`3ĘĸL”Žįĩüŧģ îđ ī.،ōƒ(å‡ Ú€ž –üĩ‡ũŧģ`;ĘĸL”.¨~@JCŪ}pquÃCšZßĘUP~eĸüpEŊōŽÔåŨ׿ ŽqÛP~eĸüpIŖÁ‡w\ÚVXĀģ ö ü ĘDųášæ/:N,˜.ŗ˙;”D™(?\”ĖįŨ—5Šdo Ū}° åQ&ĘWõ;Ø}eČGjctY­Įė}Å7ŒŅD”D™(?\UÍŊ°ÅžWt:ųNWPõ;mᕿį—üŲ§+h(?ˆ2Q~¸ŽÕāeĪæīŪ9žw¸˛ė:”jøūĢ/X(?ˆ2Q~¸°ipĨĢ­Û>ĩūŪ­1샪đøq8üŧ­[ˇų VcöåQ&ĘW6).sžm[ΚĒũđŅ3í7Üū ؒ?DØXp{Ņ#6m=#:s.nPP~eĸüpimÎÃq6!oho=r_Ãî‘Äē¤íĶūˇīyH˜`ÃÖŪpMnęQ~eĸüpqŸÅoîPåmŋē Fˆ÷ĶävíßAŧ}ĶBŋíXåĻĪ,HČ\ΠK(?ˆ2Q~¸ēî>šĻEĨ?ũÛm¸l:UdzŅĮL:%•%kw§˙ ҇&UžéÂ`8-×+3R~eĸüp}ū(Ęõ›ÜĻÂ)sĩšč›’›íÕĢžéŠ'.æ1íšŗ’Ãڛî×íæ‘‘—ėõÅsĪbi:b÷Ũ‚kĢŗėš”(?ˆ2Q~ČAŗĩ!YpÕgũŧ‰ũ^îÚĩë˯Ž˜ļt͝‰vÄų–¯ĻžEéÎj…ŋŪ+÷‰âĶĄudŨâÉCēë+}ųƒ™Ëw‚‚Ûû{ĸô€ ĘĸL”ōđä Ųû/E¤Åé$įÄlũļ NŒ}ínŋŒĐę×°´ÂsõÎô<“ ¯4ž˛C-ûāY„_ĪåQ&Ęų¨QģiŨ—.]ē|€6ũ¯å­~.)ųož˙ ÛŊˈU/§Šä'uˇ4k™ŽŌŠ}ŸĢ_KōßĖåQ&ĘĨir#Jō6= ÷ü"yĢA1]$oĶ5Q~eĸüPœaE'$^vė6ÃLi|ās‰[tY”D™(?”gøŨ'e{c ūĐūw7L•˛QÁÔ3"”D™(?hĄFĘ/›†ĻEčo/dKšŧīXk}#Ĩ ü ĘDųĄD ÁS˛ļßK˙øĩ*’îÚUö_ŨDÆ(?ˆ2Q~(ŌlØ'QK#ķ¯ĩ1Ümc%juü*QK˛@ųA”‰ōC™ŧ5{$igrnX™w5s%iu…~ŠzP~eĸüPĻûր_=ë›YķaAHŲE ›JqĖT5đ–˙9ö ü ĘDųĄTŸhbũŖUssÉárOũģ;ŲjŖ+%˜lBn(?ˆ2Q~(ÖûI Ŗj õ9đŠđäŽĸ/:Õj˙Ģšj@†(?ˆ2Q~(Wãā¯^hŅôäŧO-<ũaš­×D´hL~L''^.O”D™(?”ėø:ú‡Ëmœk`ųG'KüÚ:ØjW/đwôĩ2FųA”‰ōCÉų*5ë‡^9+ž¯ô’ä35Q juL˛fI]‡^)o”D™(?”­åo…×zŲũĒaá…AUŊĒíĨĸđQÕėmĩíáâË=ėî‹P~eĸüP牉w×ÛwõŒ7Â-k—ÁNˇĢÕFķãsŨíz…rP~eĸüPŧŽžĩŌöÍõĘŊˇ¨ĩÕÍ/ĘĖ÷yÂöf܂ĶClß\Y(?ˆ2Q~¨@ģ3ųš‹Ú[ßNZL8 ÉļÎ÷L8ũY+[ļ|îģÔ‚Āž66Ģ@”D™(?TáŨƒ9 ĢŦî{č×Ũ­ö0zÚ=n¯ŗÚl“y%įFŲÜĒQ~eĸüP‰ū;!3ėŗ*.,UkĘųģph¸›]­6í YgÆUĩÉ@˙øëƒ‡íjVi(?ˆ2Q~¨Į÷×ráĖį}šVøÁcŨ§y&BFä÷5ģ:"ļNč\ŗÂOūŨyĖžˆô°/“ˆōƒ(凚ŧĩé3÷øĮB˜×¤9ŨmŲŖü ĘDųĄ.øōĮXČÉHMJIJLLLËĘ.„Kŋ-āėG„VũWHƒüœô”dQjVAîš5īŠūŖ‡åQ&Ę52eåöāĢ×Ŧ]öéKRļ[øÔŸ.^Ōēē}ų¤§¤lYŪ(?ˆ2Q~‚ōƒ(å!Ø(?ˆ2Q~‚ōƒ(å!Ø(?ˆ2Q~‚ōƒ(å!Ø(?ˆ2Q~‚ōƒ(å!Ø(?ˆ2Q~‚ōƒ(å!Ø(?ˆ2Q~‚ōƒ(å!Ø(?ˆ2Q~‚ōƒ(å!Ø(?ˆ2Q~‚ōƒ(å!Ø(?ˆ2Q~‚ōƒ(å!Ø(?ˆ2Q~‚ōƒ(å!Ø(?ˆ2Q~‚ōƒ(å!Ø(?ˆ2=ąŽw˜h?wˆjÉy…w˜ø*å%Ū] ’yrä×?î¨ÚöŸožŲjeĪã:ņ.ĨJ ?œeĩÎ=ˇNlŗVįēqíy—B\@>Ķđ´2Yė˛íPÔūíR6čš~\WįëŦÕÆZië<ąWÚ:׌íâ|Ä~ífü˜ē<˙Ū%YÔųë]šRÖų÷ė!ŧK"=6Æũē”ķ Ņ•%#¯ķŠą›nņ.ĀFŊ)Ũø›,”ūAÎҎyWUÁƒîŌ˙#ȒŽ2ŠZˇD”9ģęķŽĖÜČD”é[æ]ᡞ_Æ|B”÷S-ę|ūĸÜęŧˇéÉG›Tb5Ú{‹›ŽôÖüXeÂÅæŧ‹#ė OD›PxBÚ]įčŪvĀ•n#N,؇øüæHŪՕōCŦķĒë}WGMΛOˆ’ėŨ1ų}ī.;$Ē?ʨ“r~DŸˇx×gtĩÎ0×>äŒHnę|B”2ÔŽ:ŋāŨ_GEŋ‚3đ¤Ŧų¸ŖUw…ĸĩČ;/ķ.0Õ/w>! ī`GÃät€€šĢMĐFŸ @ß3v„w‰z#5Øuîį]"aČíö|BtÁö:ۇōîŦâ?ĸbŪdŪ5ęÜ@¯3ã}Ū5vŖĪ'LÃlŽsīŽ:e0â ZŸ0Ä OņŽRf3¨ĶīAŪUVj¤1˜PxŽŲz`}äoˇ‘ÔFĒW'ˆÅ(ō?ëņuöå]&ae%‹ų„§hŽuzķîŠs ĻbNō.“€‡x—)|&éš%•ņâ]&aE.kyTæĪš6•ų€Īp)k7ōˆCđP~Čå"ĘiŠ9?ūŨéŲ2ūĶšėŖļY÷å‡lP~ ĸü–šķCM(?dƒō凴(?ԁōC6(?Q~H‹ōC(?dƒō凴(?ԁōC6(?Q~H‹ōC(?dƒō凴(?ԁōC6(?Q~H‹ōC(?dƒōŅیņį:YåĮ9Îu6:Č˙ēŽļ]0w7ö'ö\Pŗ.Œņ į:ßgTįiÎu6žg4ĄđØ´ Lģëŧģé´_ąį‚šš˛DÎuöbS&xpŽ“°ņč F ĪOļÔųt īn:mö\PŗúĄlņ;Îuvdķ9ŧx"į: #ŪLæĻų6ÕyŒw76y&¨Û>6ƒØÚzOPŨˆI™Eu9×I™Ša2ĄõˇŠÎÕ%ŧûéŦį‘g‚ēÍb2?îđ.SXÍĸLá]&a¤s“ …įJ3›ęė‘ĘģŖNō{y&¨ÛkwX â:Ūe C˜ü;XĀģLÂJ‹ų„ČËÆ:垓Qgų…ÁæŧÆģJAđcPgf;ŪUVŪa0Ÿeŧmccy÷Ô9‰ļ}MGՐÁšžŧ‹ÔzŋLØÎģHÂÎ ĪA›ëŧÍģĢNŲ†8ˆŽ'ú&uã]Ŗū­¨ŧk$ė|„>Ÿ0Ų^įŪ]uF!Ūøғװ‘÷ÁģĸČe–Lå]"aiō|BThĪŽ:ĒŦ™6úÄčõtÜA´ũŖ2Žá÷pëÜĘģ@ÂTƒŗ¸ķ Ņ>{ę|ō&īî:ĖičIY} 0ĮĐī1Ūõá~ãā*1I˜š…:Ąđ\¸ßŽ2[%ņN ;1÷>â*›^ķŽŽÔăx=jņŽŽ°ÖöŪ|Bägo=dųIĢp/Ƙ úc ĸk}Š3ëcņJŪĨ˙ i>aÚôˆŨuÖ?ĘģĶö+Ą…¯ڈą@”ær{Ūu•ˇ ãŗxū_ŧ×g!œŒ .B˜Px4A]ĒķĶŪ=ˇæds‰GšTŠénŠgHʉéŧ‹˛ å~ŠwF˙œwQ„Ÿ…w%žPx’ü? ÉãBīîÛ,áäd G˜Ø¤í°UūR`ˆī—Ŋā]‘eíF­÷Ī‘¨Ė‚+ŋLëlßŪHĸ0 Î<‚}l¸ BÍčYĮ‰:žąđ¨ .ģ˜ÃkZÚ‰ā˙´tk8endstream endobj 14 0 obj << /Contents 16 0 R /MediaBox [ 0 0 612 792 ] /Parent 29 0 R /Resources << /ExtGState << /G0 35 0 R >> /Font << /F0 36 0 R >> /ProcSets [ /PDF /Text /ImageB /ImageC /ImageI ] /XObject << /X0 15 0 R >> >> /Type /Page >> endobj 15 0 obj << /BitsPerComponent 8 /ColorSpace /DeviceRGB /ColorTransform 0 /Filter /DCTDecode /Height 1600 /SMask 17 0 R /Subtype /Image /Type /XObject /Width 1586 /Length 81837 >> stream ˙Ø˙āJFIF˙ÛC   %# , #&')*)-0-(0%()(˙ÛC   (((((((((((((((((((((((((((((((((((((((((((((((((((˙Ā@2"˙Ä ˙Äĩ}!1AQa"q2‘Ą#BąÁRŅđ$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™šĸŖ¤Ĩϧ¨ŠĒ˛ŗ´ĩšēÂÃÄÅÆĮČÉĘŌĶÔÕÖרŲÚáâãäåæįčéęņōķôõö÷øųú˙Ä ˙Äĩw!1AQaq"2B‘ĄąÁ #3RđbrŅ $4á%ņ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™šĸŖ¤Ĩϧ¨ŠĒ˛ŗ´ĩšēÂÃÄÅÆĮČÉĘŌĶÔÕÖרŲÚâãäåæįčéęōķôõö÷øųú˙Ú ?ųRŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ŊīáÁõ­6 gÅM,vŗ ĐÚĄÚÎŋŪcØWxNŅ/üOĨZKūŽk˜Ņž……}ĪŽęŅx|čvŅÚīŽúú=9ļŅ(ė§ ņŽ:×§ƒ§SöŌWw˛ōÚ˙šˇü1zØįŖøIāhŅTx~Ų°:ŗ1'õ§§đ?ũ ļŸ›w4Wo´Ÿr,pßđŠü˙Bí§æßãL›áĨø~Ũr>ō3>‡5ŪVD:Ī™âËŊČÁ‚Ę+Ī;Ūß$‰ˇn8Į—œįŋN){Y.Ącį/ŒWÃÖk^yf°™­ßæx‡¨=Åxe~‰][Åwm5ĩ‡†d1ēž…HÁųõ­[­Ļ¯{nŸv)Đ1ŽZĒ‘û°GæšícÄZMåĩæŸâÉõ”kø`šĪėÖÁ$”FTMcc.î79ÎÜ`“]įö%š×¯uVy[ģ8ė¤#nÄg`G|Ÿ0÷ôŽn/‡ˆēmž›/ˆĩŠ´Ë)!’ÚҌ!Dá‘I†`6Éũjœek!>#Öõ;?ęi­ëZׇôØäEŗžĪLIíZ"Ŗį’föļ탴îMXŊ´Ôĩ/‹iĨęįOļ: ŗËsqÉ#Ÿ>mw†P¤$āôë[úį…n5ioŧK­[Ø]‚ŗYÄaØTŒ2Ģ4eÔčŨø­ ;Ãöš~˛u V‘OØ!ĶÖ‚‹Låqß?9{ \’oQÜĄđ÷PÔ/´ģču{•ģģ°Ô.,MȌFfļAįWÄ^&Īü$ZžįæOũ×ŨšfŸkáËMV>C ×Sę34„|…ūfācŒ×ÁēÕÂŨę÷ˇ ÷e™Ü} X›Ŧ5Ÿuų0Žå*(ĸŧŖ@ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(¯{øEņē=M‡FņRË%Ŧ#l7QĖ‹ũÖĮōū^ Etaņ ŨĩOt'ŸnGņoĀō a¯Û€{`•;ūĮ‚?č`ļ˙ž[ü+â+¯ëô˙‘ũ˙đä}Ύŋálx#ū† oûåŋ™/ÅĪÅ9× l áQ‰?Ĩ|IE_§üī˙€šî>5ØKŖxe%†ÂN&¸•å€vá”Q\•ņŽÕôKd‹Q°QEÎ0ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸœˆŌ8Hՙ(É5Ųiŋ (Ūi8‘¤‘ŽTd“ė(Mģ!”WyĨü$ņŽĨlŗÁĄÎąˇC&5sū¯Žŋč ß÷ØŦũŦ;_QÄ#<ۊôøRž:˙ 3ßbøRž:˙ 3ßbkáõGōžoEzGü)_ĐŋīąGü)_ĐŋīąGĩ‡púŽ#ųO7ĸŊ#ū¯Žŋč ß÷ØŖū¯Žŋč ß÷ØŖÚø}Gü§›Ņ^Ž~ øčą\ũW¯øwWđõĮ‘­i÷rtbāĄĻĒEģ&D𕩮iEØÉĸŠ*ÎpĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(éßŲģáõœZ4~'Õ IŽî û*¸ČAûØ=Éū_J÷Ēķ]Y-˙g§šŨŪ)cĐnÖV9Đįœ×Aá¯øv˙ė]žģaqĒQL 8gg sÁČëÁ¯zĢTįė–ŅŅ^{ŗާUE`ë~0đî…|–zÎĩcetęE4Ą[Ą>ƒŽõg\ņ ÚEsŦęVļPJqÍ Î3ōúņéQĖģŒÕŦßhšˆ4š´ũZŲ.-ĨÕ> ö5ÉøĪÄēuہ jēV¯ ĶNšh’ŨE>Čö >p͑Æ:ƒÆ:×Oá˙čž"T´ŋōˆ$ S=2:āāķėh…[KŨz…ˆū!øjO xģPŅämË æ6ūōTūUÍ×°ūÔjÄd M¤y>ĩãÕįc`ĄY¨č´zOõ..č(ĸŠå((ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ+ęŲŖáíļƒ‰õ8kÛŦũ˜8ȍ=Gš¯•ëîÁk3 1”Ō\ŠS‚§cr+ēĩŌĀ.XÎĒŨY}÷˙+|ÎōŠäŧ;¯Xé ŧ9ŠxƒRŠÚ'Ķ­wÜ]KËģD§Šå˜ō{žĻĩmüOĄ\čRkPjöO¤ÅŸ2ėL<´Įb{GyĩŽ•$͊+Dņw‡ĩØnĨŅõ‹;Äĩ]ķų2dÆŧœ‘×}̇øwŦhž1ņ&Ŗ~úė÷šŧˇ mkėŠv‘ɲ3å) Ā‚–’˙@„æ´KŠę”W)ņ^âk_†ž$žÖY!ž;$Š˛:‚9¯hŪ.đöĩ¨K§é:ՅåėJYá†`́Á#@õŦ>e{´W;ŠøãÂú^ŦtÍG^Ķ­¯ÁĄ’`Iäôę:úՏøĢAđߒ5ŨZÎÁĻÆŗH8HqīE˜s.æÕbøģÃZoŠ´iôíZ–9…r>hĪb rŪ+ņF›ģāmYu˜bĐæ–ņ¤¸í†Eážn€÷éÍv:ģĨx†ÉŽô=BÚúŲ\ÆĪ† ÜC‚8>´8é¨áVŌ÷^§ĀŪ1ĐĻđ׉ĩ "ã%íe(˙ė*ƯPũ¤@õ, e#?Ĩy}oMŪ)ŗËÅÁS­(ĮoķÔ(ĸŠŗœ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠû+Nįötœŋđ\˙č§Ģž ˛† x UKmONX€\ųc8ôã­`ūÎ0ļÖ|!‹,Ąu8O/rúW¯æŊęņS›šÚZ¯GЊĶCÎüWâæ‹[Õt+‰ ĶāŠ!&°–ęMA^2O”ĒBā”ũâH< Åĩ[ _øú÷[šĐ5KM4%ĩØļķPŽ0ņ¸e*3…Æp~SŽõëā‘Đš2}MbéˇģĪ'žÔo;đÁ ¤pũ˛$Úŧ"}›…öĘģj(Lnwô<Åū2mBĶÅZEÄÖö&ŨnmFœút×7J…B„`AIĻ\ÉeĻÛøwS˙„šįÃēÛhV°<˛Ų}ĸ ã![rũāʼn Áší^ŋ“ëFO­;éˇĢg•[_Ūx‹UøiŦ؈îMÕķœDč­˛ r…o™CŦädWAāä)ã˙ˆiUkģ6`ôHō:íjĻŠ¨[iz|÷ˇķ,6ĐŠwv8Rr.›i-_ü ūŌ_ōUõ/úįō¯/Ž—â?ˆOŠ|iĒjÃ"9å>XôAĀŽjļĻ­sÎÆIJŧœv˙-Š(Ģ9‚Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š(  z^ŖyĨ_Ey§\KmsĘÉ`ŠõM;ö€ņ}­ēĮ:Ø]°ãĖ–,ų^AEtRÅU¤šbôķIūwŠg´˙ÃDøĢū|´¯ûößüUđŅ>*˙Ÿ-+ūũˇ˙^-Eiõúũ×ūō"=§ū'Å_ķåĨ˙ßļ˙â̌ņ§Äīxž#§{˛Ķ9û<bŽ:ū5ÄŅJXęíZöôI~)" (ĸš (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ ŊŖjˇÚ.Ąî—s-ĩÔg*ņœŖE&“Ņ•J J.ÍĶĻūŅ>,ĩĩXŽmė.ŨF<ĮB ü[˙†’ņ/ũôĪÉŋÆŧ.ŠeéŗĢëõû¯ü?ä{§ü4—‰čĻ~Mū4ÃIx—ūzgäßã^EĘ?Ķaõúũ×ūō=ĖūŌ^&ãf™īÍ/ü4—‰čĻ~Mū5átQėŖũ6_¯Ũā1˙#Ũ?á¤ŧK˙@Ŋ3ōoņŖūKÄŋô Ķ?&˙đē(öQū›¯×îŋđ˙‘îgö’ņ)fš=đßã\Ž~%ø“ÆcËÕo6Ú‘oڟ­qTPŠEj)ckIrŪŪ‰/ÉQZEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEėŸ žj'ą‹SÖnNĶä‰BæI¨…oC:íōėˇooëņi7E}bŋŗĮ„€nõ2{Ÿ1—ūįÂķõĒßÕ˙âkĢû9˙ĪČ˙äßü‰<ūGÉ´WÖ_đĪ>˙Ÿ­Sūū¯˙Q\ūÎŪxYmīõ(Ĩ=™XÏėį˙?#˙“ō!Īä|ĨEw˙>꾝e™…Ö›+mŽåôaØ×\u¨ÎŒšfRw (ĸ˛QEQEQEQEQEQEQEQEQEQEQEQEQEOci=õÜ6ļ‘´ŗĘÁdą=ĒŖ&ŖvĀ‚Šú3Áßŗ˛Igū*Ô$Šw›k`2žÅĨtßđĪ>˙Ÿ­Sūū¯˙]Ë.—ÚœSųū‰¯ÄŽsäÚ+ë/øgŸ ˙ĪÖŠ˙W˙‰ŖūįÂķõĒßÕ˙âi˙g?ųųü›˙‘#äÚ+ęoösŅåĩoė]Rî >Qq‡R}đ­|õã j^ÖĨĶ5ˆLs§*ÕuėĀ÷léG4חOŊ'ú I3Š(ŽB‚Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ŸáĻŊãŊNŸŽ8õPrE}ŊwŠÚi—ÚFšČę÷îđ[*(Úž\läAĩ+ãoŸōTôúî+ęˆ­~ž%đGöB[ĩņž¸XūŌO–šĩ`9!A'ŒãČõčž\4mŨūˆÍüGwErūÕõkģũwJÖRÅĩ 5âÛ5ĸēE*ț—åbÅHÁ“YÖŪ$Ö4˙izFˇu ^EŪēvøæˇu¤Ņˇ) FxÁĮ­_:ŽæŗėõkkŊ_RĶb}ĻĀDfܸ_Ū)eÁīĀ9Ž'Oņ~­ŠøĒķMQđæœđ^ŊēiˇąĘnæ‰7ƒŊGÎ2W ÃuäSSūø‘âøŧ9ũ—îŦ™æŋWōÜ*FSĪ$ąé’Kw*HûŦ ūbž•<š ÉRE}ã ëGÄ_íõ‡„@÷ļVŒ…%N@>™¯„.ŋãęo÷ĪķŦņ–•ŋ?ÍĀw"ĸŠ+Ë4 (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ ÷Ų[E‚÷Åwú•ÂĢŊŒ ÄčĖqŸË5áõô/ė‘˙ž ˙ŽQ˙:îË×ī[ėŸų= Ŧ5{kíSU°€Iįé˛Gû— —ŒHģO•‡ãZæZ{xŽOˆž<‡ÃÃK…{7iīŌIഌUF\tÉb{Ž tZ&ĩĒø‡ĀözĻžē}ŽŖ)e“íAä†2’29J’ SŽG^zsÕß2,utŒp¤ž€fŧúĶÆÚ”6Ū'†â;XÔ4‹Dŧ‰´—o.ā6ņ°Š.UC‘“Æĩ]đNŋ}Ž™¤“_đÆŠj°’ÉĨE"Ȏq‚Û¤n1žĒOĨ5Q=Xé´Vß\ŅlĩK! ļŧ‰fŒH0ÛHČČįšōړE‚ķÁj›]YNĒšFÎGįũiū×|QĄü-đŪŽöúAŅĄŠÚĩa'ÚZ&D$nØ ,­ˇiãžkSö’ø[z?éŧ_ĖÕЗ=ĶęŸâ˜=Ž(ĸŠđÍBŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ÛāŊÔVŸŧ?$íĩ ĘŽ}ĪžČÖtfÔ5ßęet𿙐ŽL›áxđloĪá_ÛÍ%ŧņÍ ’6 Ŧ;_[|.øËŖëÚdšũÔv´jšcĩ%ĮņØũkÖÁĩV—˛_{wZméũu3–Žįs'†¤’÷ÅS}šá܏+%žØš=Áŗ×œŽ˜ÅahžÕm/ŧ?%æĄŖ­Ļ‘)•`°ĶLbahˇ;o?7͞}ąØoJ ŠØ{ũĄ?Əí­+ū‚v?÷ũ?Æē>­/å‰<Į/â ëšüéoŠjē[iiwĘ2iėˇH@ęĢ'˜Tî ’>ĩĐiz;ŲxŸ\ÕZetÔE¸X‚āĮå+)ÉīœÕí­+ū‚v?÷ũ?ÆŖ¸ņo K>­aj2XÜ'­ ;ü/ņc+IŌĮ„ūĻ›ur˛ŽŸ`ņŧÁv†O8í_ Ü0iäeä$~uôĮ?‹öz–›7‡ü/)–)~[›ĄĀ  ˙ų?=W:Q„#Enĩ~]—¯ráÜ(ĸŠķK (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ úöI¸u}vܰŧęž 7?ΞŽ‡Āž(ŧđ‰muk™ĸ8xĪGCÔÂēđucN¯Ŋŗēû˙ā“%t}ģĸčͧx‡Ä“N˛.Š4ŦapcōáXČ'žvįņŽfO^¯…tm2ßQ´iôëÉŽˆšĩ2Û\y,‘îÛæzŽ~—'˛Ž[mJ {‚>{{‡ę}9ëøWGũĩĨĐNĮū˙§ø×ĻđŌūVgĖsš?…õ›-GVÔeÕėũåœ6°›m?ˊßËg#\î?¨ü8ĨĶ|-Ē?Š-õŊrûLyí –×Oąh<Ī0LŒÎŀÚ0=kĸūÚŌŋč'c˙ĶühūÚŌŋč'c˙Ķühú´ŋ•ū!Ės˛x6VøieáQ{–Ū+hÍɌío*DrvįžĖuã5ÍūĶ71CđĘd‘°ķ]F¨=O&ģ}gÆ^Ņ­ZãPÖ,ŖEÂʏĐM|Ŗņ§â;øëVŽ+5x´›BD(Ũ\žŦh’úŧ\æ­Ĩ—žšÁ˙†īhy­Q^ °QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEņ#€í­lŸķŅ˙:e~Ō}ØŦ‡ų˛ĪGüéŨ†˜sMĸ‡ROFĮ`ĸŠ*(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠPH9æÉ˙=ķĻQT§(ėĀ›'üôΏ6Oųč˙2Š~Ō}ØŦ…f,rēîi(ĸĨļõc (ĸQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Koo5Ėĸ;h¤–CŅcRĮōĄámįÄ~ ąŌlFgē >ƒš¯¸|đ˙Dđf—Ŋ…¤OuæÜē‚îŨÎOAYΧ+˛ÜíÃaUHûIģ/Å˙]˙|@<5ŽƑ˙€íūÂ3ŽĐ"˙˙Ûü+ô;$t4dúšĪÚKú˙‡:~ŠCŗû×ųž?đŒëŸôŋ˙Āv˙ ?á×?č˙€íūú“ęhÉõ4{I_đáõJŸŪŋČüņ˙„g\˙ E˙ūˇøQ˙Κ˙@‹˙üođ¯ĐėŸSFOŠŖÚKú˙‡ĒPėūõūGį€đŪļzi‡ūŋøR˙Â3ŽĐ"˙˙Ûü+ô;'ÔŅ“ęhö’ĒPėūõūGįU捊ŲGžīOģ?Ŋ$,Ŗķ"ŗëôzîÖŪōŠî§† ČĄüëåŸÚ'áu¯‡|C EåXJû. #cЏcUŽö‘\\\Š7uŅūôąāôQElyĄEPEPEP€IIāZqx{Y™ÅĨ_:„@ßá_Q| øM§é-ļˇŽÛ%ΊrĸHŌUʧ§ŋįéíąĒÆĄcPŠ:ƒĒßÃąęG+Uo›˛Ōß=ŽįįŸü#:įũ/˙đŋ“ūŊo8ūČÔ3˙^īūú“ęhÉõ4Ŋ¤ŠúĨĪī_ä~x˙Â3ŽĐ"˙˙Ûü(˙„g\˙ E˙ūˇøWčvOŠŖ'ÔŅí%ũÇÕ(vz˙#ķĮūsū˙øßáGü#:įũ/˙đŋÂŋC˛}M>Ļi/ëū>ŠCŗû×ųž?đŒëŸôŋ˙Āv˙ lžÖŖBōi7ęŖŠ07øWč†OФ`pā0ô<Ņí$TĄŲũëüÍ×VF*ęU‡‚)ĩö?Ư…o‰tK­GIĩŠÛZˇC"˜×h˜J;×ĮNŦŽČāĢ)Á¨5¤'ÍŖÜãÄáŊĨxŋęĪúũFŅEĄĘQ^đGá˙ü'~%dģ,š] \0ęۊ>ĩ2’Šģ5ŖFUĻĄƒąĶo¯ÉVwëåF[ųUĪøFuĪú_˙ā;…}ųĸhšn‡gŽ“eŦ1ŒŪ´˛}Mcí$z?S ´ÕũËđŗüĪĪøFuĪú_˙ā;…đŒëŸôŋ˙Āv˙ ũÉõ4dúš=¤ŋ¯øpúĨĪī_ä~x˙Â3ŽĐ"˙˙Ûü(˙„g\˙ E˙ūˇøWčvOŠŖ'ÔŅí%ũÇÕ(vz˙#ķÄøk\“¤jôîßá@đÖ¸æ¨ā;…~‡dúš2}MŌAõJŸŪŋČüņ˙„g\˙ E˙ūˇøU+Û Ë {k=ģ‚XĘįķ¯ŅŧŸSY"đî“â+-5‹n"qŒ˛ÃÜ=¤ƒęt‰ĩ÷?ÂËķ?$×ČŌ/˙đŋøFuĪú_˙ā;…~‡@āŅ“ęk?i/ëūęúĨĪī_ä~x ë‡Ļ‘¨ā;˙…đŒëŸôŋ˙Āv˙ ũÉõ4dúš=¤ƒę”;?Ŋ‘ųã˙Κ˙@‹˙üođŖūsū˙øßá_ĄŲ>όŸSG´—õ˙TĄŲũëüĪøFuĪú_˙ā;…đŒëŸôŋ˙Āv˙ ũÉõ4dúš=¤ŋ¯øpúĨĪī_ä~u]čēĨœ~e֝w yá`?NŅ3Ī ģ8ĪN1œWË߲×ü•hŋëĘä+éoųŦK˙`˙Ĩ"šåņ3ÖĻ˙sOúęΰĪœBeŒLFD{†â=q֒[˜!‘#–hŖwûĒÎo ī_?xwC›Sđėš–¯â iÚėWI{y{§ip’A›Î€]˜Åv9¸đŪŗ¨ëÖwz/‡nŽôûuKëíRd†HÃÂ]|ŗĩ¤āƒ•“ƒhåĒí{wÄfm+K´†Ęō+;ËûÛ{5ļ–d+H¸$ ãĸŠ+¨đŠ( Š( ­é 6Ģfd˙RŽh˙ō˛˙Žé˙Ą ™ü,Û­hz¯ĖûˇĮ××:'‚|ũ.SŅĪe ¸Ppql0F9V#ņŽĸâx­×tōĮ€]‚Œús\wŏųd˙¯Í?˙K ŽGŚdēĪÅMJ RīAŽ(ŦĄ:tŨ‡ÚĸhÎ|ֈCīá¸';V hzu&Ôß_éžŋ,ąÃ’Y8ĮVf~uÕôiŌŪųą4Ą}ÛĀSŽÛēuâŧÆßûIđŪ‰áÍSûÅ =ŨרŧЉg BK g.`rŠŖscŽĮx.×NģÔž!čÆËG:BCg1ĶėØOiÅ$,@*9Ž2@AëNÄûN‡Wđâ]GXŌėuũCÄ]ĩũĒNútqİ[`Ŗ ŋ*8ų˜į“KŪ7Ônô÷đ➜Ä.ĩˆ-Ļƒž&W,ŧŽ3Čኧđ‡KĶô˙‡~–ÂÆÖÖ[Ŋ:Ú[‡†%F™üĄķ9æ<žO­WøĩcoŠÚxbĘņ<ËiõëT‘3÷—l™Øô>Ôē†ĒÎÚ â A,r¨8%0Χ‹snķ˜RxšaÖ0ā°ü:×  iēo‡ū(kiVÚ}›čļˇCk ‰ŽíĒÎÄk÷z ŪĨkúáí.[BÎ[K¨g/ÜĩÂĘFŊJ—ÜĨĪį‘ŠvŠeŠî3][Āę“ÜCˇEw OākœŧņüHŋĶî.–=6-Ūä#íUYy”ļîŧ„QŒãŠā|oa§hēΈŧEwká/ZVkĢmD'ÛmöÆĒc›p<(!RKcŌˇî´#_øÁ+jÚuŊėøzŲĸ‚ę%xÔĩÄü” Ā Û'֋ ͡cŌŦŠ!‘‡QČ ×៌#X|YŦGÂ-Ü ø¯ļžD–ūkhFØ-õ+č"LđˆˇRQėWÅ6˙‘Ã[˙¯Éô3NõäN%ß5ų3Š(­Ī(+ęŋŲ_ k3cŪ'Ø ¯•+ę˙ŲūDÍ_ūŋGū€+*ģ#ŋņOĶõGĻøRŧÔn|R—ŗ™…Ļĩ5Ŧ¨"ÄBđ9囓“Ít\C8o"håÚpÛ6ž+Æu×ģOüCûīox”ĨÔČŦÆ;b-„­…!ˆNpAÆykSÂÚމâK RßZđŦQ& kĸéâŨ¯#ō÷s‰X6ŨĄÚOg“PŅŧj=QK›y&hRxšeÎčÕÁaŽšk‹×u+ÍSĮÃÖ:éŅāļąKš&ˇ<ĶJō:,ŧV„‘Œ’ÃĻ9āĩk {ëš‹áí"KJÆKYíî#[įLĄÕ’5îŒÁ”šęsČŽÉØë)¨]OņŒyŅ2_0]÷” Ŗ€qÉõ4’šS“‹ēėĪužâu q4q)8F üiÆXÄ^itcvōF1ëšáüqyĄ^x–ßDŋŅtmSTŽĐŪ!՚4Š(™ö|Ĩ•‰%“ĢĐrGâ´}>ĶZđEՊę:5­ĨŸ‰ĨûĩĖžu„áWpˇäŠxōÎĀ…GXK;#Đž%kSi˙5ÍKDŧDģļƒtsDUö6áØä~uÔ­ÄO$QMȄîEpJũGjņ zęÍ~|@ŅĄŅ´-6îÎÖ)&—D*mįW'i8PC€§*s€AĪ5ÖjŪŅŧ;ãOËĄéļļÉy=´˛ÛÆ#iŖû,­‰Æ˙™åŗČÍ;ĻÛŋ§æy×í}“Ã˛íayW>Øú×Ėõôßíƒ˙ ī×Y¯™*élÎ|ĮOՅQZœ!EPEPĒūĖņ$ŋŦˇ¨mJëžÄÍ}Us¨Ũ§Ä;LYˆą—Iššx°0dYĄUlã<a××Ëŗü•k_úö—ų úrīūK“ŸúŨ˙éEŊsËâg­Iūâ×VuŗsŦrëq8ˆ0ŅåĒŠ‚zg“ÍyM͍Ļĩđ_ÁšÅĨŊũ؛MˆMsČûâ%uÜœ0āúŽĩėV6vē}¤VļĐÚÚÄ6Į (z OAÅšJį=oĢĖŋu‹ĢĨM:ßN´š4}ĒĢ#É8cžŧ„^ũĢĻ!‹ĖĻĸŠ+¨đŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ûāoˆ!đßĝ2ōíļ[ÉēŪF?Âc5öúÛÛ=ŌŪŦ0ĩÁ‹Ëssˇw]šį+ķŠŊáįĮmkÃ6QXj RĘ1„.ؑĻ{Ö ītzxZđpTæėÖΧ§ßųô>¯ēĐt{ŊJ=FīIͧÔ#ÆËŠm‘ĨLtёųŌŪh:=öĄũÜ߯ģækdyUyā9“Æ{šđáûLéXđíîëē˙…/ü4Ε˙Bõ÷ũ˙Ođ¨×ŗ:yaüŅû×ųžîtÛkmll­Mĩŗ#Á…vDÉ÷ .0Ĩp1ŽĒÕ|ũ˙ 3ĨĐŊ}˙Ķü(˙†™Ōŋč^ž˙ŋéūkÛđŖüËī_æ{ÜvļņŨMsĨĖĘĢ,Ē€<s´3u n8ĪLŸZ…ô­=ė&ą{ F˛˜ŗKnaS…Žæ,¸Á$’Nz“šđ‘ûLé]ü;{˙Ķü)áĻt¯ú¯ŋīú…öah˙4~õūgšiú.•ύ];Lą´P† -íŌ1°œíā2IĮ­GĻxEŌŽĨšŌô:ĘæPD“[Z¤Nų99ežyæŧGūgJ˙Ązûū˙§øQ˙ 3ĨĐŊ}˙Ķü(×ŗ,?š?z˙3Ũ›KĶÚÆ{&ą´6s—imĖ+åČX’Å—9$“ž¤ķ^aûKxŽßHøyq§kԈ‰<íÎIũ+ŠÕŋiˆÚՆ“ :NG q0 ~ŧÅū(Õ<[ĢɨëW 4ͯEAčŖĩ5'äLëĶ ›M9tˇį}´0čĸŠéķdvĶGīIĶįĶâ Įk-˛›=üdš–Ö6•Hääd~uxZۋÆģD.Ú1No( !KuĀ$œtÉ>ĩāŸđĶ:Wũ ×ß÷ũ?™7í3§ygÉđõߙÛ|ëĐQ¯f‚ûQû×ųžÛŦ_iŪĐ/¯Ų-í--ÖIŲQB+9%‰ĀîĖI'š$×įî¯xu Vōņ˛ ÄĪ._˜“]ˇÄŋŠšįŽČ‚č­Žœ§+kāŸV=ëĪĢZpiķ3‡^ŠĨMŨnßåoOÔ(ĸŠÔā ú3öIņŧęēā&…ÄŽ70@¯œę֛uĻ_Cya3Ás nIāƒQR<ËC§ YQåŗŅ˙^[Ÿĸ–ö–ÖŪŲíá‹ĪË7–|Į ͎¤€O< §Ļh>•q-Ɨ¤éÖSËū˛[kd‰Ÿŋ%@&žrđŋí%ykfø‡I’ ΁ö÷ ņ[ßđĶ:Wũ ×ß÷ũ?°ÕnM{9k'ķKķŗ=Ēßô’IoĄéPÉ#‰]ŖŗK8mÁ‰’9õæ´E­¸ŧkąBíŖ4Áō€’ˇ\IĮL“ë^ ˙ 3ĨĐŊ}˙Ķü(˙†™Ōŋč^ž˙ŋéūkŲ(¯ĩŊ™ô S:^žt÷ą6†ÆBĖöæōØŗn$Ž0rēĮ$æŧ$ūĶ:V8đíîëē…/ü4Ε˙Bí÷ũ˙OđŖ^߀Z?Ėžõūg¸ęÚ&“Ŧ4-Ģéví&#un’˜ÉÆvîOAK.ĨËg5¤ēm‹ÚLæI`kt(ėzŗ.0O“^˙ 3ĨĐŊ}˙Ķü(˙†™Ōŋč^ž˙ŋéūkŲ‹–ÍŊ™íöúoĨÉĻÛé:tZtšķ-#ļE…ķ×(ĶųUŲ­mᚠg‚)e‹Â6 Š*OC‚FGbExü4Ε˙Bõ÷ũ˙OđŽoÅß´vĄg%ŋ‡´Ņ`\g•÷ēũ1ÅŊ~î:ĘI/T˙r/ÚÃĖú‡ˆ4íÕÖCbŒķŲÛˇå^ S]ÜÍyu-ÅÔ­,ōągv9,MC[Â<ĒĮ—‰ŦĢTæ[lŋ¯Ä(ĸŠŗœ(ĸŠ(ĸŠî~ ø‚ |EŌīŽŽÛvc č­ÆkîE‚Ökˆ¯–(qHî‚Ū[H ×i!N: üâ¯_øsņĪZđ­”Z~ŖÕ,#á7ļ$AčzƤų‘éakÁÃŲTvkgúYßh:>Ą} íū“§Ũ^C.â{dyŦFGáNžŅ4Bú Ûũ.Âęōõ3Īn$|įåb29ô¯ ´Î—ŸŪįž'_đĨ˙†™Ōŋč^ž˙ŋéūžŊ™ÕËæŪŋĖ÷oėËąCgö_ąÂQĸƒÉ_.2¤*¸Ā €F:Vëįīøi+ū…ëīûūŸáGü4Ε˙Bõ÷ũ˙OđŖ^߀íæ_ø˙3ŪÖÖŨ.äēX"[Šcy‚ėĒIU-ԀXāvÉõ¨Ž™`m.m Ąĩēgyá0ŽÉ™Î\ēã X’I={ׄÚgJīáÛßûūŸáK˙ 3ĨĐŊ}˙Ķü(×ŗ Gų—ŪŋĖ÷ ?AŌ4ĐŋŲēNŸiĩ1ö{dÄ_” ‘܁MĶŧ?ĸé—r]éēFgu ÃÍoj‘ģvP ¯˙†™Ōŋč^ž˙ŋéūÃLé_ô/_ßô˙ 5ėÅËæŪŋĖ÷qĻØ‹ lE•¨ą”8’ßÉ_-÷’_+ŒĒs×'=kÍ?hßÛh.Ŧ7(šÔˇŠ!ĮËÜãĐb¸mSö˜ˆÛ0Ōŧ?"ÎGâ`TÂŧÆ^+Õ|_Ŧ>Ŗ­\e<"Ž5ôQښƒ– zt2iËĸZüīļ† Q]'ŠQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEt^đnŗã]XiúŠ–@3$ÄqVnßĪō5ī:gėģ›u:ŸˆHœõX!ā~d×Ģ|đ­ŋ…ūé"UŧŧŒ\Ü>9,Ã8ΎOÂģŊCQŗĶÚŅogHMÜëmoųi+B|)üĢΕiÕwNËĨ&UęVw‹˛éoĖųëūvÃū†Ÿûô´Ã.ØĐÃs˙~–žĸĸķūfG4˙ũįÍ˙đËļô0Ü˙ßĨŖūvÃū†Ÿûôĩô…^ĖÚÎūķæ˙øeÛúnīŌŅ˙ ģa˙C ĪũúZúYÕlt]6mCVēŽŌĘy“HpĢ’Īâ@ük—˙…Ģā\˙ >“Ûy˙ ĨíŌc^Õí&yū˖\îņÁįŒB/ü2í‡ũ 7?÷ékŪŧ;â=ÄļŌÜh:„ĐDū[ŧ'![āūVĩ&įüĖMÔ[ÉũįÍ˙đËļô0Ü˙ßĨŖūvÃū†Ÿûôĩô…¯?æaÍ?įyķü2í‡ũ 7?÷éh˙†]°˙Ą†įūũ-}¨^[éö7ˇ˛Ŧ6ļŅ4ĶHŨĖ~€O‚hî ŽhX2˛MBåĶJĶdæ9&B^AęŽ=Īå\÷Á/ EâŋˆúVŸtĄíUŒķ)čĘŧãéœWß1FÄ‘ÄĄ#AĩTt¸ë֗7$캃^j~ÎŨßč|âŋ˛í–ŅģÄWīˆ–—ūvÃū†Ÿûôĩô…Īy˙39y§üīī>o˙†]°˙Ą†įūũ-đËļô0Ü˙ßĨ¯Ąt­FĶV˛[Í:u¸ļft.pYŖ}Hü*Ũ;Īų˜^§ķ?ŧųŋūvÃū†Ÿûô´Ÿđ˖[ŋäb¸ÛŽžHÍ}#E+Īų˜sOųßŪ|ß˙ ģa˙C ĪũúZ?á—l?čaš˙ŋK_HQEįüĖ9§üīī>o˙†]°˙Ą†įūũ-đËļô0Ü˙ßĨ¯¤(ĸķūfĶūw÷Ÿ7ŸŲvĮxŠį?õÉkĪž!üņ…ė¤ŋĶ&M^Ę0YÄHVUîķŸÃō¯´j›_ŲžĻÚ[L{ä} ĀG>QmģŊ1œŠjUą—Ū5:ąÖ2=QųŠEzoíá˜<1ņ6ö$Ųkv‹vŠ Xœø‚ķ› gŊžˇĩˆ$ō,Kž™cüëžáÎô=:uTéĒOĶšØ|6øi¯øú醕ŠĘ3‰næáØzŸjöĢ_Ųr!~×â9LßÅåÂūš¯zđO‡ėü1á?JĶâÅJ,ØäŸSšŌ}BŅ5H´æEô°ŊÂCÎZ5*Ŧß@YGã\.­JžõíäyŽĩZžõÚ]—õŋā|ķ˙ ģa˙C ĪũúZ?á—l?čaš˙ŋK_HQSy˙3'šÎūķæ˙øeÛúnīŌŅ˙ ģa˙C ĪũúZúBŠ/?æaÍ?įyķs~˖[Nß\ؘTŌŲvĮøŠįūũ-}­jÖ™6ŖĢŨEgc<ÉĨ8UÉ 3õ$Æš˜ū(ø"I4ņ.ž]Ø*į’NéTŊŖÚLkÚŊ¤Ī"˙†]°˙Ą†įūũ-đËļô0Ü˙ßĨ¯¤*(.!¸W6ķG(F(Å6ÖAĮqéJķūf+ĪųŸŪ|é˙ ģa˙C ĪũúZ?á—l?čaš˙ŋK_BÜę6–×övSΊuyŧ[Æs™6.æĮĐsVčŧ˙™…ę3ûĪ™īeČŧ†ûˆÜMÛ̈́?–+Ãū!ü>×|~°kVãȐ‘ Ė|Į'ãØû:ũŽWâw†m|Yā­OMēEfhYâr9G ÄSU§OŪŊĐÕz”Ŋæî–éūžgį•ŦĨXĢ{RW¤{EPEP]˙Ã?…~ ņķ™tøÖÛNC‡ģœšôQüF¸ũOm[[°ĶŖ$5Ôé#ļæ?­~‡i:g„<- ˛´:m„|’0D@,Ä˙3\¸Š˛‹P†į*ŧĸÕ8oßúīú oû.[—í#˜Ëܤ Ö¤˙†]°˙Ą†įūũ-{į‡õũ/Ä6˛\跑ŨÃųlČÃ`rb+Rš›ŸY3ēdĪ›˙á—l?čaš˙ŋKGü2í‡ũ 7?÷éké )^ĖÚÎūķæåũ—,šŨâ+ƒĪ„ _øeÛúnīŌ×ŌPjVgVm0\'ÛÖr`ū!bĄž™~ī?æazŸĖūķ᝸eÛúnīŌŅ˙ ģa˙C ĪũúZúBŠWŸķ0æŸķŋŧųCÅ?ŗ.§ihķø{WŠöEō&MŒßFá^ŠØ]éwķØę6ō[ŨĀÛ$ŠA‚§ü÷ī_Ĩĩķ?íákuļŌüKoKƒ'Ųg*>ø •'čGëZŌ­8É);§øQ¯8IFnéééØų‚Š(ŽķĶ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ |1ŧŌ¤PŖI+°UE,O@sL¯uũ“ŧ+oŦøžķWŊˆI›ō•†G˜ŲįđõŦĢTöpr1¯WŲAȓÁ?ŗ†šŦYEy¯_GĨ$ƒrĀ|˜÷ėį]wü2í‡ũ 7?÷ékŨüGâÃbŽjVöFmÆ5ÎˆQ“‘“ĐdVĘ9dár¨õr‘æšUzĘOō>p˙†]°˙Ą†įūũ-đËļô0Ü˙ßĨ¯¤(Ĩy˙34˙ũįÍĮö\˛ČĮˆŽĪ9„R˙Ã.ØĐÃs˙~–žĸ‹Īų˜sOųßŪ|ß˙ ģa˙C ĪũúZ?á—l?čaš˙ŋK_BéZĻ­ĻÛßéĶ­ÅÂ "•s‡SЌÕēwŸķ0ŊOæyķ.Ŗû.¯ŲÉĶŧDŪpč&‡ ūDWƒøëÁZ߂5_°ëļĻ2ÜÅ2sŖÕOôë_ĸUĀ|pđĩˇŠ~jËĩÕ´MqnärŽŖ#Ÿķۚ­:~ķw]JŽ"Ĩ/yģŽˇ>ĸŠ+Ō=pĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ô;HÕ­4/†ÖšŽĨ!ŽÎĪOYĨ`2v…Īšėq^$×uũWPđ+jū:U”úäÅ)žYœ~ę\,ˆlbx' ×O‹~C¤ĨĪ’—úbƓ¨Ũąļ‚­Žā;Õ ?ÆzôžŪ›ŖÚeępŪ\Mo|ō™ÂĮ"ąT1ŽŪXkËÃŲS§čxØ[*Q~_Ąŋâo_Ō¤–M3ÃKŠØA›,ĸũb€2Â8ʝÄÄŽMGeãK{ÍsAĩŽŨ†ŸŽiæöÂõŸcŦb(G ąƒNpÜ W=â¯xšûÄ:ŗĨ†ŸĢé÷(ĸÄŪjrÛĮeûŊŦFYí͸ķ푁†x“GšĐ~hM#F5¯ Ãg<70’â ¨b\ Ÿ0ŒqübĩIhjŖ#ŠĶŧToîüB-ô鿞ŌnVÍfˇo1î& ĒŠ€…‚““’Ļ9›ūoúëŋø ˙ŲT>đËčū ´ŅõÄwa|Ë›Č ‰$Û|Ž >g-ÛĄÅI˙ŨĮũ ē÷ũũ‡˙TčOģsGIÕ?´LŖė7öž^9ē‡ËŨŸNNkŒņA?đģ| É˙G˙AŽģ=#L“O2™5=Bû~0.ŲĖgĻÕ^ŋŌ°ĩŊöķâO†5ČD_aĶ­ŽâŸ-†Ũ(@¸ūéÍ ¤Â-&ięZ÷ØüYĸh†ßĖūŌ‚ęo;ĖĮ—äų|mĮ9ķ=F1ß1ÎqãM/[øw]Đ,ėīŸNŽę mŽnžá0 #tōúcŊfØx;SÕ4OYxœÚ[Iâ)ËŠą‘ĨXTÛĮ傒T§< ãļpJڍ(ÚīúԓCøŒ/u>ĶRĶ­Ŧmõ&1ŲË§Ė›ö— ,kūŦ•SŒČ$fĮƒ|eĢø¨YßYxzĐ.‰)vu4i•2@f„'Āã~F}xĒžŅu›kû$žđw„`kr ē´¸g }č₤œãžMfØxKY“ÆzVĒžĐü=-ŊɚūûMŋg7ČQƒFbōĖÁ˛Ä‘Zv‰MGRŋŽüeĒk~ņŒš‡ ߇ŌÎōĐę/ząŗíFI$HŠČ§w;;Nw yĒŲøOGmISškhŽ[ąlĒ<ąÉbŦ qīá¯XøG]đ}…Ļ•wĨ\Áy žĄ5ãE$i0r✐ÎWvā1ƒíZū'Ņ|DöžLŽ+Û;[s îŸũŖ%šûP#‰QKģXmčw{Pí°;hŲ~"Iiĸęšž‰-­Î‘žĢoö€âÚ9‘:8_Ū&×Vä)ÆsŒsĐę"ū/Ō„×É˙˛ŦŠŘ°í%U÷9SŌžĨņ‘­Zøļøj;KÉ͗Ø.ė.į0 c^7IļÖVgČ#7¨įÎŪÖWū´<šÛÛÎū_’yâËí/Iĩ}_BhĩĢˑig§[]Ŧ˙hrģ˛$…P,žŧfM Å’ë‘hž#Ō‘ŠÜDķÛî…ĖW„ áTî]Ā•*89ķŒŨWJņFŗ—ŦIkĨXkēE鸴´O4RÄŅäŽI6.Ö`͂×šŸLŌüAŦxˇO×Í-íåŪŠ{mig PķĘ×ŗœÄ‰'€Ōē]ÅW­ŽÛhŪ&Ņ×GÔ/#y,Ėwks ÆĖPûT‡îÚWĻH' Ömcf#tsuāö5ŠaĨø‡[ņn“Ŧø’ŌĮK´ŌVļŗĩģ7/,Ō!C#šDB@9,Ič)ģ6Æų[lFņč ›ÆßŲ§ĐŨ}‹ĪôlmߡõÛV|[ã)t/išĻqŠŪj6ķK†@Ÿ:2 9# ¸bK“ÆÜI¡„ŧtß îŧ,t5`{xõzįÍMųēōūRG-×Ĩz=æ‘u/Ä=7YQØ­ôۛW%žmōK .Ļ#n~”5&ĸŋ{rkA \čú“ÎŅ+Č-bķŖF#•Æp{āT?đ“Ô]˙ĀOūĘ­ęš<ˇ×^tzΊfģBųVĪN;üČN§˙ÕĮũ ē÷ũũ‡˙TčJå3üIãi4sGŌ­´KÛûŊRŌ[ˆ"‚8t1üģåQ‡bXˇq‚H[]ņėúe厔šm›ëÍj—wVķęio˛ą /œËķą!°ôœqYtKŋøN´mPIæÚYéwrI#1¤w„Š 9ļHĮ=ĢÅ^ŧ˙„ēoiš>Ž­ŨŦv×6:“Ę4eŠI…;\ āņMr•] _â5ŧž˛ģ°ĶĨēÕī/[N‡LI“pš. Ŗ+°(-ŋ‘´\U _jˇŋ.Žš¤&—s‚Š;Ąp’/Ú Ü*ûŒ:TˇūÖäĶôMOKĶ4=/ZŌoŪî-: ŲĨã1H EÕbwĀ ĩŖ éēüū:kvv1>”,…ŊŊĶ\2¸˜ŋ,Qs{=é蓰ũԝœŋkßų(v?õâŋú¯đ¯ü?ũ~C˙ĄŠõīÚîTo‰‘ŠËĨŠ™fĮō¯!đš âm!˜ā ČI?đ1WOũŨ˙Ûߛ4Ŗūë/û{ķgčŒŧK†4x.¤×÷ˇS%Ĩ”$+ÜL˙uA<(Ā$ąč5ÉéZ†ŗ{ņ‡M]{DM*hô+˛žUāšIžßŖRĮ Žâē?hˇúƟĨ^hsBšŽ•v—öÉ?LB24n@$Ga‘ĐâŠiˆ¯ŧuc¯k–~Ÿo›qgä[Ū5Ãîya`Ŋ Ážã“œ ĄnS[–ãŧKâŨgÃĻîú˙à øvŅŗ5ėWęĶ,yÁ“Č Čqŋ8įâĩtĪ­×Šĩ ęÛėˇ1Es™7 ˆæ0ĘĘG=<ךx‡Á~2Ö4 oLžĶô­BūëÎHĩkŊZlg%v[yec`›W‚$÷ßøÁ¤]\ž…>‰pļēÍ㞉ŋ–ˇ¸Cæ‚9Œ'š?Ü>ĩV[ËĐh1:ŋ‡ ÖcŌ/šÖęyVÔ[¯šŌBŽU%`Bí@į‚95sūoúëŋø ˙ŲU™´8ƙea§^^i–ÖhąDļeäU Ēw)āÛSūģúuīûû˙ŠĐtã>>Ūũŋāg‰&û5Õˇú•ōîcØü\GÎ3Ōēë=åü6ĮÃ~$ļžß:âĖ,iîĮyĀŦŸ‰ŪÔuī…ÚĮ‡´ËÉ/oîŒf9o¤QŌTb *Œ)ĮŪž´îšl;Ž[]#MņĩöĢâ]OGŌü?4ĮMÔ­ÕĶÜáHļŖy™+–™ŋv ũܖ]ÂĄ°ņV•Ŗx/TÖáŌĒ.§sn--pĪusö†„Āų¤`ļzœfļ<Ŗ]iž'–ķËÛ¨ęī{ÆĪîĖ0 ĪĄĘ7ĪIāŊV_]iĐĪmoŦEŦÍĢŲ;åâŪ/xƒãœ€qĶ9į'ē?woB”Úžģ}ņ3Á)¯ø}4œ ׍Ŗž[Ųƒ•8UÃÄ{×Câ¯kkĢë ųŪ´ųįŊŽų|åŒct‚ŧÉ#p8ãĩU†ÃÅŋ‹|5Ēë:n™§[é†čK­pîdˆ*°&5gđÆĩtũfīEĶ´Ûkk+˜cÕ$Ŋ¸šI2 ģ ãäĀä÷ö§h…ŖŠ6§ã隆—auá8ßę3yPZ헐¨yN-đGRHíŒ×1û\˙É5ļ˙¯äūFĩü9§øãOņ§Žę~Ōoõ{öų˙ÛĩēŸ’×É8’N~brköģ‘áÅĸžų6\)Š¯w_š&ĸIÆŨ׿ŽhĸŠôOX(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ+ę/ØĪū<üGūüĘž]¯§˙c9SÉņ;‡˜6ÛíĪ?ĄŽ\_ĀŊQĮŽūõGĸÃĒx‰~6_Æt+3Ķ-ã2O]ˇÚfũøWŪaÖ<˙ųm/øX„Ö뚆..ŧ-w7Ëv‹4ą!;ĨŽ>dā‘– @$™Û]äüCŋÕä ũŸqŖÃb0ß6õšfn=1 æš›MÆúO…˙áĶ!Ō$´ŽgkŦËtË$0cj–ˇōČixûØ$qÍeŖ0ŧYĐë>0HåĶ-<;h5ŊGSˇ7–ŅG8Š/ŗ€?zōvŠÜ `IāuĚŠfž“RąÔ4™ėõŨ>5š]>9’_5;) Ue%Xs´‚9ŒäÜøWRđũ慨xEmŽÛMĶH–ĘöcžÁFYļ×R;‚c͊ĩ h:ÅΡĢëúüéúí¤vđXIæũ’,Ųķw.ė~æsJĘÄÚ64˙á ž˙Ą[\˙ž­øũ\ŌõK›Û“ú.ĨbĄKyˇ& ¤ú|’1ĪáŽ*Ÿü#÷ßô4ëŸ÷ͯ˙̚^—sepeŸZÔ¯”Š_*ä@_’59üqÍK°+ø{âŨcBøMáëįđ´ĶhV–(nn…â –5ûŌ¤ ĘO,§ĨzFŗĢk oi7†4h5ˆgŒKæI~-”)Á\|ŒNAĪ@=ë…ŅôŲ|=˛đzŲčPąũŽMT^ģų1C–78RqķāœSÄ×!Ôt¨4ģH5ÚiņÚ-…ÎĨ%˜ŽToõ¯ąJ …X`m8œÛŗf’Qr-j_íŦüaâ5Ķ.äIīŖ°šĶ¤Đš”Å Ŋ••°ŪĀäfĩoo/īŧ Ē\jēaŌî qlgY™W)bŖąÔ ā÷=k•Ķ|ŦÁā}K,cŊ´ņ j“¤Ä/b œ@ŽįÆ2,^ÕŪC…[Y 'ũĶXסŗ•ŧÎ|O*Ĩ$ŧĪÎ (ĸŊSÚ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€>¯ũ™ū(ØĪĸ[øW[¸K{Ûo’Ņäl S˛qééĪŽ>ˆR§ ô"ŋ2A ‚ rŽĢMø‰âũ6ÜAgâ-E"Ę[žkŠxi'ûŊŧ˙ĻyĶÁÎ/÷vˇgĨŋ?øčf+#Sđ֗Ējöz•ũŗOsi´ÂWōÃ),ŦcÎÖe$H$˜¯…?ájxãū†[˙ûč…đĩ€+áīøZ~8˙Ą–˙ūúá\ÎąŦjZÍŸVŋšŧ”g <…öįŽ3Ķđϰõ^ŽÉ}˙ĸÂ֖¤Ŋoú#_â?Šįņ§‹īĩŠÔĸĖÛbŒ˙cîëø×3EÛ(EElBT⥐QEE›^×î|/âm?Yŗæ[YCíūōôaø‚kīŸøĪGņž ūu…€ķ!ĪĪc•"ŋ;*æ—Ē_é7~™{si7÷āĄ?\uŽjÔß4wüÎLFÔ|đzūgé^(Å~~Š~7â[üö‡øQ˙ SĮô2ß˙ßCü+aW˛ûßųßV¯Ų}īüĐ,QŠüũ˙…Šãúo˙īĄūÂÔņĮũ ˇ˙÷Đ˙ =…^ËīäV¯Ų}īüĐ,QŠüũ˙…Šãúo˙īĄūÂÔņĮũ ˇ˙÷Đ˙ =…^ËīäV¯Ų}īüĐ,QŠüũ˙…Šãúo˙īĄūÂÔņĮũ ˇ˙÷Đ˙ =…^ËīäV¯Ų}īüĐ,QŠüũ˙…Šãúo˙īĄūÂÔņĮũ ˇ˙÷Đ˙ =…^ËīäV¯Ų}īüĐ,V‹ŧS¤øOHŸQÖŽã‚×!Iųœöu$×Ã?đĩ"—\[wmNEÛæ<ÎꀀÄ$Ēd¨Į5đ¯ü-OĐË˙}đŖū§Ž?čeŋ˙ž‡øQė+y}īü…õjëĸûßų XŖųû˙ SĮô2ß˙ßCü(˙…Šãúo˙īĄūŊ…^ËīäV¯Ų}īüĐ,QŠüũ˙…Šãúo˙īĄūÂÔņĮũ ˇ˙÷Đ˙ =…^ËīäV¯Ų}īüĐ,QŠüũ˙…Šãúo˙īĄūÂÔņĮũ ˇ˙÷Đ˙ =…^ËīäV¯Ų}īüĐ,QŠüũ˙…Šãúo˙īĄūÂÔņĮũ ˇ˙÷Đ˙ =…^ËīäV¯Ų}īüē“ÃzZx‰õĶníŠ2í<ÎƟ(RQ ڄ€ @æŧŗö…øĨaáß]hzUĘM­^Æc"2‡ĢA˙ׯ˜o~$øĘöŨ šņĸҎP˛í?˜ÁŽNGydi$fwbY™ŽI'Š&Ša§'j›}˙äTp•&íUĢykÁ ĸŠ+¸ô‚Š( Š(  –H'ŽhX¤ą°taÔr }Ķđgâf›ã[¤“Įŗ¸ˇfÁ$qš}A¯„Ē[[‰í'Iíf’ ĐådŠ˛ũäV¨ûK4ėŅ͈Ãû[J.ÍVūļ?LąF+ķæßâwmáHĸņ& FdÜ3ÍI˙ SĮô2ß˙ßCü+›ØUėž÷ūG'ÕĢö_{˙#ô bŋ?ájxãū†[˙ûč…đĩņÉ)Øā#ŠÂ­iaä¤Ĩ> ¨ád¤§S§Eũ~EWYÜQEQEQEQEQEQEQEQEQEQEQEQEQEQEčüy˙ŒŖŊ¸ ÚuÂų7JŖ$.xlwĮō&ŧūŠŠU"âĖęĶUbá#ô§EÕŦ5Ŋ>+Ũ*ę+ĢiT2ŧlÅ^Å~oh^#Öt 7čڝŨ‘'qĘUIõ+ĐÖ˙ü-OĐË˙}đŽ7‡Ēļŗü?Fyī YhŦūm~™úŠ1_Ÿŋđĩ"øÃQˇ0]ø‹QxP%+ŸÄ`×(ÄŗbI'$žõQÃJO÷–ˇ—_ȸ`į'ûË[˛Ö˙‚˙‚%Q]ĮĸQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQ^—đÃá¯ãxžÚō +8.Kúíëj4'YÚđÂm#Í(¯ŠŖũœ´Š$ÕĩlrB¨Í;ūĪÃßôÔ?%ޝė÷üņ˙Éŋųyü•č¯Ē?áœü=˙A]CōZdŋŗ–‚Ұ‹WŋWĮĸ Ųīųã˙“ō!Īä|ĩEz7Ä߅:ŋĀēf[Ũ-›hšŒciôaØ×œ×%jŖ.Y˙Ԛ{QY (ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠĐđũöžšacœ}ĸtķ8¯ģüí7Ã:>ūĻfM>ÕU Ũ!V tČF95đ˙€?ävĐ˙ëō/ũW×ßĨš|'-ēŨ]Žŋ• >Āėb˜ ļ9'€x¯WųpÜËŦŸā—ųŗ9|GuEsÖõ[j:.ŋgcŨ­ŧ7I-”Í$rG#:˙ŠÍQÕŧEâęÚ}kOŅ˙˛ĻēŽÜ‹KÉâ1#„GÚ血,š׎•¯:ĩÅcĩĒIŠÚžŗ6”Ž~Ûē\ēm8ģ2ŠĪN¨Ü{WuãRãÅZ–¤Á5”ĸ‡TŋxŽ. E}éĄų>lĪCPj7ÔīaĐėŦĻžMŲ¤–ōgŽ(€žn>UbI-Āã€OĩK¨ēŽß\Ōíõ­īMŊ@đ\Æc`GLô?Py¯ĪũJØŲę7VĮūXĘŅūG÷‡‚ĩģwKžMBŌ;;ëKšŦŽ"ŠO17ÆØ%[x<ŠøgÄßō1ęõķ'ū„j1MK~Í~)ßōCŽæeQ^Q QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEˇā™Ŗˇņ~4Íļ4ģŒąô…}ŋâ­mfãA’ŪXiÚ¤wĪŋ?:,r) Ž˙8üĢātbŒI A}[đwã™ĢiÚ_ˆŽ’ĪTbYN`8=ÖŊ\Ui{ēwˇ{ÛoKV3–ŽįĨ&‹pk5ĒtÕˇģ]­Î˙ÕĮ]ÄĸŠ+Ë4 (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€%ûDßķÚOûčŅö‰˙ᴟ÷ҍ¨­}Ŋ_æ{‘/Ú'˙žŌßFƒq13I÷EEŪ¯ķ?ŧ,‚Š(Ŧ†QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEU‹+;‹ë”ˇ˛‚Iįs…HÔą?…wßŧqqJēČd U‡ÔÅoO VĒæ„tīĐNIEz/ü)Đŋīę˙đĻīĖNIuEz/ü)Đŋīę˙đĻĩœOöuK;tö_´B'°Æsí[z7‹´oP{-+S‚âíSÍō€efLãrä Ëî2+ÛŠ5Îã˛[zv1KCrŠÁšņ†kĒ˙fÜjpÅyæJ°`ĄĪE/ ō3Ū¤×ҟĐW ^^* iF;\Ō.č(ĸŠįQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEV¯…´yŧAâ?Jļ˙[u2ÆĻO&˛ĢŅ?gņŸŠú&ŧ˙úތ$ëEKaIŲ]xKÃzw…´Xtí*Ž${ķHŨŲl×âŨj×Eņ˙‡eÔ¯E­“X^† Įû­öŖī7'z×Gĸø‡IÖė&ŧŌ¯á¸ļ™&uČō™y!ÁR¨¯QÔᓿz™ØÕĸ°ôohZÕé´Ķ5ĻšddfQ՗p‡#‘žĩž4đėzŖiÍĒDnŌa"#°Y8K´GSÅ.hīp:ōŪ´ņ…îĩ{h5{2‰`ƃǎŽō­='ÅúF‡¯xÆsTX5MÉîr‘ũšpÚšŨĪ9÷ŽÃYž Ī _Ü[IöķYHņȄ2ē˜Éâ´Ą%9rŊžy č~~ŅO›‰¤ûƙ^—+hØ(ĸА (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ šĶū>á˙}CJ¤ŠČ5tåÉ5'҉ŸnüF´[˙‡Vö¯Ÿŗéë${wnO´Cģ#ĶÍ[ņ Lß<*ÄÅVßPV/ •‹žŨđ>ĩCāΌ­|Yāë=ŗ/ö¤k ĖYųž„cņŽöŊĒÔũö˙Žëī2LņoˆŪ%žÕŧ?â­*äM‘‰­áŌáŌ&žYpÃdÍ?Ü @ßĀČä‘ĪEã™Ŧ´ßû}wUĐõ‡ŗHĖ‘iworŠĖĘŦ<˛ ›!Y[ ^‘“ęhކ˛öo]GsÎĄ’ûX×~ę:́‚øÚŨĪ<~YL1úō§“€y+cÂ14~.ņģ4l‰%ũģ)+€ßč‘d^sø×YYž%×,|9Ŗ\jz¤Ëŧ O'–=”z“W M´–¯ú@ŲņŋĮ?ų*ž!˙Ž˙ĐWZž(Õä×ŧE¨j“p÷S4¸ôÉŦĒķ1sS­9GkšG`ĸŠ+œaEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP^‹û?ÉXŅ>˛čŧęļüŽIáŋéē´y?e™]—ûËÜ~U̓’…hšhŋĪBeąönˇhŗüQđŦīņoc~É&܈ܘö$RĶåšĶ§ûũīKÆ|ŲGœé1E(,á‹2׎dž¤Ņ“ŒgŠfû…Î;Á–ŠšīŽe’ õ@Ĩ™Ö ļ‹Q’ß™Ēž áø+§Į*2Hē1 Ŧ0AōĪWw^qņĪÆVžđm帙N§†ų€<>ØĪųŊ%QIėĩ~›ą7Ąņœ˙ë¤˙x˙:ŽŠ+Óæ“fÁET€QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEŖĄkZŽƒ~—ēEÜļˇ+ŅãlgØ×¤Ûü}ņ¤Q"<ļR•,Öã&ŧ–Šč§‹ĢN<ązy¤˙;‰Å3×˙á |c˙N÷āQ˙ ãúp˙ŋŧ‚Š×ûBŋu˙€Įü…ȏ^oÚÆGî›˙nâ¸?øĪ]ņmÂËŽ_ÉpîĮ÷Q~Š8ÎŅQ˙AíCūũ§øVŠ?fۋ{7—Ãēšē™FD7ˇ°"ž•Ŋ熯Ęâîéüģx#id|ĩTdœzKiqŨ¤76īž ‘d°Fåayö4]÷ Sz8¯ēߕ™ų×Ēi÷ZUüÖZ„Ô-ĩãq‚ T¯¤k_[Åũ—¯Á¤Ōąˇ˜Ø×ÍÕŊ9s-O3EQŖŗÕ^O@ĸŠ*Î`ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĢÚ.•{­jPXi<÷S6ÔEĒ5ôßė•áËq§ę^ ™ŽOŗÂO;T~†ĸ¤šV‡N‚­?{eĢūŊJũšåšŅ$ņ˛mæa“˛ÛėI­Ÿøf}ūƒÚ‡ũûOđ¯sŌõ+MVŪIė&ķĸŽi-Ųļ•Ä‘šGŅ”Œô=Ēåaw՞Šũ˜Ģz_ķģ<ūŸF˙ öĄ˙~Ķü(˙†gŅŋč=¨ß´˙ ÷úÆņ‰ô˜WWŧōd˜3GDōģŒļÔRp29Æ9kÜMAjâžäxĪü3>˙AíCūũ§øQ˙ ĪŖĐ{P˙ŋiūīëķG~•OFÔėõ­*×RĶ&ķėŽPI ›JîSß ˆĸī¸íå_r<0ūĖú7m{P˙ŋiūÃ3čßôÔ?īÚ…{ũ]÷X˙*û‘āđĖú7ũĩûöŸá\ˇŒ˙gMCM˛’ëÃē‡ö‡–7%]ŽGļ85ôæĢŠZi6‚įP›É€Ë!ļ–ųäpˆ0<ŗøķWhŧē0åĻô”Sų[ō?7î`–Úy ¸Ŗ–6*ČÃÚĸ¯nũĒ<9oĨxÂĪR´c]F"ŌĮΧ׈Öđ—2šåbhĒ5Ũ=ĄEU˜Q@Q@ $2O@+Ú>ü֍˙AíCūũ§øQ˙ ĪŖĐ{P˙ŋiūî–Ú•ĨÖĄ{cÁîŦļ}ĸ=¤lŪ7/$`äzf­ŅwÜvƒû+îG€Ã3čßôÔ?īÚ…đĖú7ũĩûöŸá^˙E}Ö?Ęžä|åŦ~Í‹V:FŊ+\ÂÜÆ6“õā~.đΊáMbM7Yˇ0Μƒü.=Aī_~é:Ļ¯d.ôéŧës$‘oÚWæGdq‚ᕇáÅy_í9áČ5_IЈ×íšs‡Wī°đE57ŽÄN…:Ņi$ĨҝËļŋ™ņŨQ]'ŒQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEôßė˙ ī˙×X˙•|É_KūĮķGöoÁ¸yģŖ}ŊņŒf˛Ģ˛;°Ŋčz~ƒĒÚhsüEÕ51ŲÚjžtŦNŅiAÜÔ^ø—ĨŽXX^G¤Â5dļûąܨáKš5a Aa¸c<Œß—Á­¨éū7Ķõ)U-ĩë“$OË"˜"@H=ÃFN=1F§ø‚ëDÔô_ E'÷ˇÖÎÆIp:¤~XØIÁûĮŽj4:=äôūĩŽxŖYĐn>ĶĒh0.ƒö¨íÚîũķ ‘Äi#E° neČ HŊcxžķÄ1|X҆Ÿ¤i÷4ûÁneÔ/1K[īfÄM°ƒ€ÎA'#ŦSĀ~$ÔíώŋĩŅ//ęÜ&ŗqu+Îc &ԌĄ|Š ØĀÆ9ÍzĄ¤\ÜxįGÕã1ũ–ŌÎęŪ@[æŨ#BW?vŲįŌ{ŌüƒŊx˙†|K­hú‰îŦ<<ˇēnŦjR\M%č…ŨDîėbM„>÷+’+×댴đÕü>ņ6ÍģÔeԞŧėáädÜqĮßā㞴‘sNú:†ĨŠËĨØŨøsMˇŋ7*˛mēē6Á— ä#’yc×šĀšņ˙Øü#Ģj—Ú[A¨i—kaqf× LˁOœ@YŖo `‘ÅGâ/ë“éž‚Ém/-Ŧ­Ėēt÷r[Å;yjĄˇĸ’ÁHo•†ėõŖđĮ…õm Aņéž2ęææ+,ļ‚hc?'äoá#œãĩ= nW.jwúÍ˙‚|JÚž›§ÚCũ›9†K=@Ũ ?vųĪîĶãÖŗŧ'â­RÚÃÂúž‚-tÍN(­-Ž~×ēa']|ČļĄ‚7F$q‘ØEĸx+Qļĩņ^Í?IŅcÕtķk›§ĖĪĩĮœÄĸ…'rŽz/zŨģđíėÚ‚aV‡~u×9c‚Šm$goÎ:ãŠ4æn˙ÖįūÖŋō$éŸõö¯“kęīÚŪh×ÁúLLĀH÷DĒúákåĒ=}Dc˜ođ˙íĖ(ĸŠØķŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( žģũ”äž\˙×ã*ųž¸ũ“åFđäjĀē^ËÜdqXÖéëú3ĐË÷Šūũē'Iá¯YøcĀzÎ̍dÃĩ~ŠŠ@.íxęĒ ’9$Ôđ*÷„~ C­k‘éŸØéw<-4MÕŖžF ČÅB•`@ÁƒÁĻÜŨx.˙I¸žoU›Sĩ”/˜ŠÆčÍõ8Čč{šŊĸXëĻéîáŨ(,NĢqc#M!r04 ;IĪJ—ceÎŦē|M­XjÚt Đ ŗ°Ô.~É Íĩ÷žÉ) ‘6.ÚFAlÎąm¯|B>2ę4<ÄtÛdgūĐl­ˇÚ'Āy\šį)œ|Ŗæ9ã7Nđ7ˆīÃ˛ęv'Û4ÛØn.ĩCw-ÅÕÚŽíÄO“%ŗˇv9ĀĀŪCŖÜ§ī5ĸcûÚ\j7ûŌY\äcĻwõŖD š[ ũáõ¯øyâũ[DøQáũBO‡Đ- HĻš7g ŋa‘aۂšéķG8¯ikĪÁúšü‡Âģ­Ž¨–‘Ā[Ė>VåpO͌ãŌ’ąSN÷]™Õk÷ZíŗÄē‘i¨d#\ßp§°ķßŌšģˆYŅ4k›M0.ŖŠŨËb+pM˙1^\0āŖ€wc­Iã] ZÔŧGosmm§ęÚ?Ų|—͝ޤ‚4—~ī4…VY2¸XqˇŽĻ¨é>Õ´ŋĮŖI¤xoT&ōâyŦŽ -ļĮ™~íąˇ#‚ŧcƒŪš°›•Ũˆū#j:§ü+™ou.g‹Qą’8,nžĶį š„ŦQ9' zsÍniŪ'ÕWÅVš/ˆ4Ht÷ž‚Yí&ˇŊ ŪY]ęãbí :ž2z×;aāMR ęi`ˇ:Ĩėm´Îöö‰ąŗ…bŖ–ØÍ€ dũMuÚÆsyãjņžË§ÁyÁ˜†&QŨŖũÞ}(ĐKš÷ôH’âš’C‘äʸɨĮ ĶЕÍÄÔĶü]sÚŨ§ˆt“aĨŲAŖļŸí)4?(ÛTîĖl •ãĨđgˆõ¯ĨĨäÚF›•q•fˇÕEĉ‘• ĸ0šėpĮÖ˛|#á]WIņ¯ĒŽ›Ąi†ëNKxm­$yÎW‘ˇĘÛļw ž¸ãļižđžĨŦõ§Ņ´?Å s ĨŌ§g7ÅĮ×ËEĀ?6NN@÷ŖA'= O xĢTđ÷ƒgŧmMĄZjw‹=Ųģ ) y&^8ļĘģņË•n8čū:y˛ĮįGŗž2‚Õ]ũËņģüŽ÷ãoÄãŋ+چM.СSÕŊXũkÎ(ĸļŒTU‘įV­*ĶsQEFAEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP^Ÿđ'â"ø_’;ũͤŪáfĮđ7f¯0ĸĻQRVf´+JŒųã˙~ŠéΝŦZ%֙{Ė27ķĒöõūōūuųĪgŠ_Xįėw—ÔE!_åVŋá"Öŋč-}˙Ûükg#ŅXĘWu÷?Æëō?Cˇ¯÷—ķŖz˙y:üņ˙„‹Z˙ ĩ÷ũ˙oņŖū-kū‚×ß÷ũŋƏg?!ũoŪ_r˙3ô;z˙y:7¯÷—ķ¯Īâ-dŒZûū˙ˇøŅ˙ ĩ˙AkīûūßãGŗŸ}oį÷/ķ?Cˇ¯÷—ķŖz˙y:üņ˙„‹Z˙ ĩ÷ũ˙oņŖū-kū‚×ß÷ũŋƏg? úŪŧžåūgčvõūōūu‰âčŪĶäģÖ/Ą…d&ā] đgü$Z×ũ¯ŋīûRģŧšŧ}÷wNū˛šcúŅėäÄņ”Š7÷/՝?Å/Oã\jr–ã÷vņ˙uJä(ĸˇŒTU‘æÕĢ*ŗs–ė(ĸŠfaEPEPĮ„ĩë¯ øŠËVą?žļ6?ŧ;ŠûĀ~?ĐüeĻEq§Ũė$6ŲØFî0z×Á-ŊÄÖŌ -å’'ČTūb˛>gtváąjœ}œÕ×âŋŽßŠÔũŪŋŪ_΍ëũåüëķĮū-kū‚×ß÷ũŋƏøHĩ¯ú _ßö˙g?#Ģëx~īî_æ~‡o_ī/įFõūōūuųã˙ ĩ˙AkīûūßãGü$Z×ũ¯ŋīûÎ~Aõŧ?y}ËüĪĐíëũåüčŪŋŪ_Îŋ—ÅŪ/ĶtXK'ڤøq,!ųâž˙đ§‡4Ī hÖúfmņ.>QËäžäõŽjÕÜ$78ņ— rC>‡Å#ā‡Ä" ˙Â;(Īc4üUā‡Ä2š>”{ĸ˙âĢîę+oWēûŋāœ˙Y­Ũ}ßđOÎoxCÄu]Iē˛ŨĀi*OĻᑟưkôŖ\Ō,uÍ6k VÚ;›Y”Ģ#¨5đ?ŏ7‚›Žë[<˙ ׎-tš‹-’5Ë/ Į÷$ΞōŅôģ-O†ĮLˇŽÚÖ‘Æ rÖŽã.Hnqb12Œš)īÕŗáãđCâ”ûyŅņT‡āÄ ›ŋá›Ļp&?ú}ßEcíę÷_wüŦÖîžīø'æ÷ˆ|;ĢørëėÚæse1č%Lú‡đŦšũņŸ…´Īčwfąn’Å"ŦGĖØƒØ×į÷Œ4)ü3â}KGš9’ŌcīīĒA­čWs|“ÜčÃâGÉ=˙3Š(Ž“°(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠr#HęˆĨŽ’O ŽëIøGã­VŲn-<;wä°Či Æ& ū•í_˛ĪË/ėĄâŨ^žæV+f˛ ˆÔp[§ŸÃņĪŌ5Å>hÛŗØĶúÅUĢŗūŊAbĢGWfžįųŸœôV¯Š´Yü9â=CHģ9šÎc=7Įņ5•]Ņ’’R[3Ō„”âĨ˜QEĘ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€=ƒöVüYƒ#Ĩ¤¤~k_Oø§\‹Fø‡ GS[,é—ŌMįOåÂY^Ü)l ›į“ë_0ūĘŋōVa˙¯Iš×ŅŪ:ŋԴΊ>ģ×LQÚÅ§ę ŗMä…ŗoķ“‚ÃqĀ῏%­i]2jõįũt;]/_Ņõm6]CLÕ,Žė"$Iq ĘŅĻNæžj Å~ņōAĄëZvĄ4kŊãļ¸YW8Éô÷¯8ƒ^ŗƒ^ņ§Œŧ7`ڇ‡ĶL†)š+õŌ;îeųNā¨ĘĀ#ã8ĒđxŠß]ø—āI­umüũĸé1Ĩ[IûĨ6’9l§fÕ<ڎA{=ĪSOhwmhšĩ‹]]<‘ÁwĘŅ’*į'iRĄžXũ°UGŽôÆn6x'ך÷ŋƒšMž“Ģj1[Įöë­_Pķg#.BŨHĄAęc8é’OzđoÛ ūG/ūŧ˙­LUĒÅ.˙ŖRUā—wų3ĀčĸŠôOT(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠúö8øˇ\$ ‹EÁ˙ú‡S×ô)§]OSŗŗ0DŗËįĖŠąŠĢž*Āzkå˙ØßūFÍwūŊ˙B5îé:‡ĮYŌylô f€H7*3\\āâ`ŸZķ¤“Š;˙Z#ƚNĩK÷ũÔë+đūŠ–ÍŦkZuŠŨ.ø ÅÂĮæ/ŽO#‘ĪŊOŦëúF‰eæąŠŲXÚĘÁcšâeErF@žx¯'×R=+â?‰Ž5˙\øn;å€ŲÎöĐ<Āą…1‰&€e}ä #!ŗƒÉ¤Ķƅá]{Â7úŊô—^ƒJšĪMÕ/áØÎf$‚6€›ŖU°TāķÍr"Ŋš;O‡Úäzī‰ œgôŽ+āæ„YWåÂŠíž˙É)đŋũyGü̆øā k? 4kũWÃú]íä­qžyí•Ũą<€d‘Îđ¯6–”Ī"ƒĩ+ú~§ĢũŋIđå”6wú˛D ļ3y—÷[¤1)ģ;œœPIõÃ^x¨OkâgĶßMŽDŌ㉠˚+sæ}âŦP>īõŽk^Ņė5ŒKËhĻŽËHģžŲAUu–SŽœ8ô8=Ē=n ˏxļÛI+P—ĮjĀãlĻ[ ‡ÛV)E øãé/§čŪ$˛ūŲ]FέāĮÜK"áN~îāGĻs]–ŋâÃŌDšîą§éĪ0&5šc.R<ŠņëgÂŗü*Ņ4}%!ƒRąģĶR[„‰í%[¨–C #*sŧ=Ku;šî|cãH4¯{'Hsj“G}ĒŖ:ÎY˜l‰ŨåqĪÎĖ0:ÜzáŌĮaˇĨË6ŸzŖÉ¨ŖIfĢ*Ÿ´*€XĮũāãĩLu 1Ē 4ŨCũ a7Ûxķ<°ÁKíëˇ$ ú׌øNž÷á;jz2,ē–‡­ŪjV  p—o‰c$”uØIÆáÜWmđ×ÕÎŗã2Ö%XŦ„ƒ,áĘĮ÷˜Č˙đ!IÅ"eŽ|ûG¨_Œ:öhÉ˙žŧÎŊ7ö‘˙’ÃŽũc˙ĐŧĘē°ßĄÛ„ū =Š(­Ž€ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(´ø=â„đ‡Ä+T¸8ĩåN}¸'đ8?A_ZÜCwmÅ´‹$2(du9üͯGøyņ‡Å …--'KÍ5q‹[‘¸(ôSÔ~ŖÚš+Ņ“—<5îŽNN^Ō÷_ä}áF+å1ûQj¸įÖy˙¯–˙âh˙†ĸÕč\´˙Ā–˙âkJŋČ˙ķ9Ŋoä|ĖúĒGHŖg‘‚"Œŗđ—Į˙Áâ˙ˆˇw6/žĘÕEŦ.:6ŌrÃÛ'ôĢ?>6ø§Æ’Y4‘éÚ{đĐÚä3Fn¤}1^][QŖ.nyém‘҇ÃΟÚTVļËõaEWaŪQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEzŸėįâč<)ņ ˇ¸ŽĘũ>Í#ž1?)>Ųãņ¯¸‘•Ô:Ęà Žâŋ2ĢÕŧņĪÅ^ĩŽÉŪ=NÂ>;ŦîQčĶëšã­F\ÜđÖûŖĪÄaįÎęSWžčû„ķփĪZųOū‹U˙ĄrĶ˙[˙‰¤?ĩ̃؃˙_ ˙ÄÖ<•‘ūæaėë#ûãūgÔēížc=åėŠ ŧ(]ŨŽŋ>~'øŒxŗĮZļŽ„ų3ˈŗũÁĀüņŸÆĩū"|Yņ7ŽSėúÂÛiųČĩļĘĢŧ‹ų{WŸÖô(ÉKžq͆ÃĘ2ö“͞˙?ëķ (ĸēÎāĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ėĪŲoÆ6Ú΁âĐå•FŖĻe<˛yh˙„lqøöž+ķcCÖ5 R‹PŅîå´ŧ‹îÉįč{ėkÛtÚkÄVĨXŪĘ8ķUĖyúŒķįBpo•]]Ī.ĻĨ9>EuōĶīˇČúęŠųOū‹U˙ĄrĶ˙[˙‰Ŗū‹U˙ĄrĶ˙[˙‰ŠäĢüđ˙3?g[ųßķ>Ŧů”˙á¨ĩ_ú-?đ%ŋøš?á¨ĩ_ú-?đ%ŋøš9*˙#ü?Ė=oä|Ėú7ÅÖ:æŖfļzõŒSĢÅu<ąŗKļč°@īŊ‘’lŒÚo†<<ģŒVz^ndácgč+æSûQj¸8đí˜?õđßüMyŸÄOŠŪ&ņŌųÂÁ`E­¸*‡Ķw÷ŋ—ĩ5NŦŊŪ[zÛôe*UįîōÛÍÛôlÅøâøJŧoĢë*Žęrcûƒ…ũ5ÍŅEw Q]Nœ8¨GdQEQaEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEŋā= ŧKâí/I…š˜+‘Ų{ŸĘ´ĨM՚„wbnĘæŸ‚>x‹ÆY}&Ī áŽ&;~=ëŋ_ŲĮÄ%Fí[L¸ųĪô¯ĨôÛ};Gˇ´Ōlŧ‹uHĪ‘ne\n`:œddûZŋ^˛ĄB:(_͡ÁĨũnĖų™ō×ü3ˆ?č/ĻūOūÃ8øƒū‚úoä˙á_RŅOŲP˙Ÿkī—ų…ßså;ĪŲÛÄĐĀĪo§\8˙–a™IüHÅyO‰˛č ^u^‰đ…ø¯ĸqķ8˙Į uāŸäLļ>•ņžĒēOď Jmnī%–Æū(­íP4’6`l  Į$Åtņ5žšŖåŲßZ]éōyW6wQ4mˇr𤃸Aį5Ĩ§\ÍņAÔ֖Ö7‘I.FŨĄÚ=y ߕPˇĶõ›McĮˇš|­ÍįúsĖG—#Ĩǝ898ÁÎ+ŗŪR×B ēg‹ŖšÖ-´ŨCHÕt‹›ĩsköčã 9Q–U(톜pĨGkã{,:vƒ­ŪZEt֏}QCĢmr7Hǜ‚Bž‡Ž[KĶõÛ˙xRö÷K×ŖsK%íÆĨ{(&ŲĐlŠ6Úæę}:†~(>ÍxĒ<ëĢļŨđ¸~ĻŊ+ÂĶxƒAQhæž/,6ėys†žeøĶđŽoÜĮybīqŖNÛQØ|Ҏ÷Zž¸đŽ´ÚíŨÃ@ 0_\Ų… ģpŠVwNûsŽŲŦ–j ü@“(>]š™ u—iüĸX•É=oŗęŸM5úŸ QEÖxEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPØ?ŗü“?ëî_æ+'K‹XŌžKâ_BmuƒФFŨŖ7îŦŽ n$åÎwq‘Ž•7ėŗŠÃqāK‹aö‹KĻf^û[Đ×ĸÂ!§Â'/‡L—FÆY^Vmëæe§3vãŽ:tüëÛĢ.Vŋ•~HÉük%ģ\[[7ˆ56pŦâÛId–Qœeō‚ŒžO5ËÁĒø›Pđl‹e6Ŗs5–ą-•ÔöđĮô–ŠŸ™QĀA&JĮ@HŽĮZđ­ž§­ĮĢG¨jšuōÁög{–0ł¸ ƒ‚X‚0y<Ô6ž ą˛Ōd°ąžÕ­CŪ=÷ŸãyžcŒ6Xįpölúõŗq“`pū!ž†ëáw‹-­ĩmnīPbķ`ÖãXįļ%×oʨŋ)Æsķƒƒ]Ûëņ‡†Ŗ—ÄZ†§Š,ö÷p^$[, hö"ėų“įƒZ_đ‚éĪĻęļˇwz•Üē˜nnî'3,g( íÚ đšõ­­KIˇÔ5=*úv•fĶfyá@VgŖ;˛9sĶãéIAîüŋ1ÜđÚįũ_‡>ŗėĩķ}ûZj0KŠčzz03ÁĘāv Fé_?V‡ĮäŋĪō6 (ĸ¸ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€=Cön˙’ąĻ˙š'ō¯ŠuŸŠŪõÔ?ô+jų3ā&Ĩ—ņKG–é‚G#4[@Xq_hÜčö÷ °ÖåVPÍj¤l+)BČg?ģ\r:žĩĪ-&Īb’æÃÂŪĪ ĶußøJ>!Ō­|CįÎĪ%•´Ø ˆXäWo4đ0Į ‚NĀÜxŠãŧÔã…ųb]ÖD)8!¤Ä.FsüLŠš7lžĶpö:žĩĻÛ\ČŌÍgewåÂîÜŗ‚ČO}…yį¯4ûŋŲÍ­ŨjVڞŗ`÷kŨCiw˛9Ę.Åf$6Đ*Gwæ‹ĸT$‘Ák˛ßxŗáׂu;ŨRúÖíõkHe6#9ēXüŌŦ§6î $äÅzî™jÖ6[=ÕÍãFģL÷%L’{ąP~€Vŋ‚ôËiZ$2]­ž›uÜČ —Ž_5C9ē÷Į~õŌŌl¨E§vyĻŗĨŪjŋŽ!ĩÖ/4¨G‡ i^ČGæŋúLÛ@gVu'=9ëWü5ĒÍ7€/[^×eļ’ŪöæĀę`F˛‘ÃF„ĨKáNIāfē¤Ņm“ÄŌëĨûd–ib˸lōÕŲÁÆ3œšį=1ÅeĪā­2\é -ęÃ5ëę 2JXgiŒÁ‘€ãkž2œĶ¸šm¯3™đîĨ¨ŲøÂ.ĪVÖuK+Ũ>âx˙ļ­<ĻIĸh”o.2Tųœ‚8Ā9æĒø/PŊˇÖt¸ŧ]ĢxļĶYŸ÷om} Âæ}­š"xã#Ŗp$ךë´īÚZkj“ęZÅõüvŌZ nîˇ~íʓ€ ~Qʁīž0ËOZŨYÜŨęÚŪŖ”‚kkkÛŋ2(œēb8,[ŽzŅt.Ihp:vŦ¯ƒüeŦŲø›QĶÚĮQÕn--­R/+÷rČßŧ …˜– ü@cõØøúåī~ ę—sOĨyŦĻY8üër XÃáũWGY. ޤ÷O3Á¸fgÚqįļs\ĪÆ›_| ÕmËü‚ŅláŪFXā(üp*fî™ļ.5"·hĸŠę<0ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ŠøyãMGÁęjqŒ6Í}ŲĐ×ĶZĮ_ßÚ$—×SiĶ`nŽX™Ā>ÅAČü+ãĘ+ļŽ2TãÉ%tļî‰qž§Ú˙đ¸ü˙Aø˙īÄŋüMđ¸ü˙Aø˙īÄŋüM|QEkõč#û˙⠐û[ū€˙č?ũø—˙‰Žwş<5§Ų?ö›ŠŪ0ų>B‘ƒęsĪá_%ŅG×Ōøa¯›ŋų'™§âMn÷ÄZÕΊŠĘeēˇ1ôôØVeWŸ9ʤœ¤îŲiX(ĸА (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€  Š,LVD`Ęè#Ą¯Ļū|°:|1Cs„hģ•ĮûCˇÖžaĸĸpR:pø™QēĩĶčĪš‡Æü$VÃØŖ˙…/ü.˙ĐĮk˙|?øWÃT{'Üéúô?“ņ˙€}Ī˙ ƒĀô1Ú˙ßūÂāđũ vŋ÷Ã˙…|1EÉ÷¯Cųß˙û˜|ađĪüTvܰ˙üM/ü.˙ĐĮk˙|?øWÃQėŸpúô?“ņ˙€}Ī˙ ƒĀô1Ú˙ßūÂāđũ vŋ÷Ã˙…|1EÉ÷¯Cųß˙ûsRø×ā[+f–=an˜#‚6,0+æŋŒ?/<}|‘Fk¤Āså÷›ŪŧۊqĨgvĖęãœĸáküßõō (ĸĩ8BŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š’Ū nfHmãyes…D$û īô߃ž8ŋļYâҤ‘æ°R ™N1Ũ›RÃÕǝŨyEzoü(īĐ)īčŖūwŽŋčŋ÷ôTûXw5úåüQæTWĻ˙Žņ×ũ—ūūŠ?áGxëūK˙EÖÃę5˙—ņG™Q^›˙ ;Į_ô _ûú)Ā˙’âRŖôÕyŖÚø}FŋōūG™Ņ^›˙ ;Į_ô _ûú)Ā˙OöRœzJ(ö°îQ¯üŋ‘ætVΉ<1ŦøjãÉÖôųíô2/čk­4ÕŅĪRœé˖jĖ(ĸŠdQ@Ķx_ĀŪ#ņGÍĸéW˛mÂ~fēøQŪ:˙ R˙ßŅPęEisĻ8:ō\Ę:}ߙæTWĻ˙Žņ×ũ—ūūŠOøQū;Ũė•Į¯š´ŊŦ;•õ˙ËųgEzoü(īĐ)īčŖūwŽŋčŋ÷ôQíaÜ>Ŗ_ųy•éŋđŖŧu˙@Ĩ˙ŋĸøQŪ:˙ R˙ßŅGĩ‡púåüQæTWĻ˙Žņ×ũ—ūūŠËמøËCĩk›ŨcŒŗÅķãëŠ=Ŧ;ƒĀâ?—õ8j)XbŦ āƒÚ’´9Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ŦfŸ‡öz‡aņ%ü .ŖyĖÆ|¤ėGš¯s¯6RbũžnZ2Q—ÃĶW‚’Ü×Y6ģ§h>Ķîu{ĄrGh6´#”j"‚Ėp Āā\‘Õsw=ú­BNšÚ:/ëņ~fí‹mâëBŸXˇŧķ, b’°‰÷ÆÃ#GˇxnGĘW<Ž9¨´¯hšĩĩüÖrÉöķn"kYŖ™įĘeAã rFM;ķ.įAŠJōΆú†‰âꚔßm¸×!Ô.ŒKĖqÅnåÆŖpƒ°Œ§ŪÉ$ŒįÆ)žĪđīR›Íō‚KjĖûļíi‹$ŸLfĩą*w‹‘ŲŅ\îã_ë:¯öv~^ņ‘¤Ž9 –!*Ž­: ī•'ŽzRjž7đū•Š› FųíæVTg{i|”fÆMˇË^ŖĢwĨfWd> ö¯‚ü_ĸKáŋj:EÆKÚLcÉî;Ęž÷đ÷ˆtŋ[Í6‘rfXdōĨW‰âxÛÃ#€ÃƒžG5ņ÷íøąĒ`”ŒœģNOԌMĒPoųvųî1ĸŠ+ ō읃>4ņĩĩÆ~Ã3\ŨGoƸZ÷ŋŲâŨp‘Čą˙ŋ‚ŗĒíŧ TĢĢôģû“hú‡NąļĶlĸ´° ļˆmHĐ`V+‘ĶŨÅm ƒGŗ!sĀ>uĮ?ĨY°ņΆīõTĶ­55’âGhĸo&AŽ3•IJųnx<+Áô5­ąč:œÎōz--sZŋŽ3ĸ ō–$)įøšīŪŧīDņωáŨcƑkwōFëŦŧģR gōĸû<3lVØš ‚Ø čhĩÆæŖkžŸEdę>"Ō´ũ RâčŊ„û|™mâyüÍÃ+ĩc NG<•Ÿâ­PŅŽõ[mB?°Zn2H­€¨ÉŽ)Į8 uŦ>eĩÍĒČ Žį´očū 3ÁĨÍtō,&OßYO+Ķ ČŠQŌšO…~6ĐGƒü'ĨĪŠgPšŌh¤(Ķm˙WæíŲŋƒōîÎF1NĖ^Ņ]jyGí=āM"[i0Ŧ1\ŋ—s (~ĖŊ|ũ_dūÔCū-T˙õõķ¯ĢJ;4ræ)sÆ]ZģûÚũŠ(­O<(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠûv(¤¸ũŸg†ŪYdđüȈƒs3XęsUŧkĻL5 kXLŗ˛–ÚâM+wŸlō,e\Ē‚Å~B§##ĩgūÍž4ļÖü4Ę5-8y{ō騊ö 䃲ˇcŪ¯RNkijŋ3ËėīŖŅ<=Žë>Oj7Īn'›Uļˆ#J‘˛ŖÉą uÚxĨđ}Üú§Åŋķ5;ģėVŒŨÜéeo´)”ž¤žŧõ¯PĸĒæ>ĪmN?á¤Ac⸊H‹ëڄŠJîVˆaž ŽAĻ|`ĩ{߇畴p=Á’[e1",ŋi‹wļ3Ÿl×eEÖår{ŧ§âÛi$ņ߀åŠhāēģŪꄈÔÚHO`N×ÅüAņŠŠé>/ŅŽ!ÔíĨHî-­lmt‰'ûRlų%iö˛lįi_\ŠöJZ&Tī}w<ĮÅ˙a˛Ô4ûīíhÚĀĶŖ„]ØiŌ]E,y$Fę#u%[')ųúķÃá}[Xģøe}­ØŸÕôģŨGÆ0ˇ´2[É{áČm`šÁ ˛–š†ÆåģMmsö˙…„Á7ú:Éį‡÷čŋ7§'÷¯C¤ĸáėõģa\_ƒ,Ú+ĪÉ-ģ#\jōfB<ÄЁŒõÜ?:í(ĨrÚģLōí3WÔü5đŋÁ Ŧđ‰ ‚ ˃e%ËY¨ˆœ˜S I*=99čjh0ÛęĢņ õģ}jķJŊKUy$Ķž ŽÉÃ4qĒ8ãļxī^ģE;‘ėöÔķ_j畯ŊwcĨĢë:Ų ÖŠ§ĩ´°L b‰æn]ĮîņŽŧÕ -6hžøÕ,äIĄŧŌä’! „NŒėF2?ˆ“õÍzÍ\=žšŗČ˙j/ų%7õõū…_WŌĩWŒígļĩđŌĢ,‹ žčŠČ\}Õú×͕tvlįĖ_ŋõK_ŊŋÉ ĸŠ+cĪ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€.iZ•î“}æ™s-ĩÔg+$gWĢ韴/‹í-„W"Ęí‡I<7ãë^9EDĄjΊXĒ´—,^šün{ü4wŠ?įËO˙žMđŅŪ(˙Ÿ-?ūų5âRöQū›5ūĐ¯Ũā1˙#Û˙áŖŧQ˙>Zũōií✌Yéū˙!¯ĸeé°ūĐ¯Ũā1˙#Û˙áŖŧQ˙>Zũōh˙†ŽņGüųi˙÷ɯĸeé°ūĐ¯Ũā1˙#Û˙áŖŧQ˙>ZũōiöņIRžž8; x{(˙M‡ö…~ë˙ųo~!xÆLĩ|Īœˆ##…rTQW¨Ģ#š­YÕ|Ķw (ĸ™˜QEßø3â׊ü'ÛXŪ‰ėפ7 ŧíé]‡ü4wŠ?įËO˙žMx…›Ĩ˛8úéZ÷õIū-3Û˙áŖŧQ˙>Zũōh˙†ŽņGüųi˙÷ɯĸeéą˙hWîŋđ˙‘í˙đŅŪ(˙Ÿ-?ūų4ƒöŽņN9ŗĶķūá¯ĸeé°ūĐ¯Ũā1˙#Û˙áŖŧQ˙>Zũōh˙†ŽņGüųi˙÷ɯĸeé°ūĐ¯Ũā1˙#Û˙áŖŧQ˙>Zũōk'Ä|cĢZĩŧ3[ØÆā†0&ū=ĢɨŖŲDO_ē^‰/É\M-Ėī5č$Žw3ąÉ&ŖĸŠĐämˇvQE (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ (¯EøGđŗTø‡zíũ“J…€šé—9?ŨQÜ˙*Š•#MsHÎĨXŌ4:ĸžĶĶŋg_Û[,w0ŪŨĘ2ŊÃ)?€â­Ã>øū÷_ø˙ã\˙[_Ę˙ķ9~ŧŋ‘ūæ|EE}ģ˙ ûāúŨāS˙!ũŸ<H˙@ģãūžŸühúÚūWø˜}y#ü?ĖøŽŠûwū÷Āôē˙§˙?áŸ|˙@û¯ü ņŖëkų_áūaõåüđ˙3â*+íÃû>x‚?ŗî‡ũŊ?ø×šüPũŸ§ĪŠx2âiü ]ėĻ9b?Øoņũ)Ŧ\~Ōi|ŋĖkĩ—}?F|ÛE9ՑŲJ˛œF4Úę;BŠ( Š( ŠĩĻX]jš…ŊŽŸÜ]Îá#,OųëÚžŖđ/ė×ĻCcū/욿íÔˇˇb‘§ļzŸķÅcVŧičõg=lDi;=_d|ĨE}ģ˙ ûāúŨāS˙đĪž˙ }×ū?øÖ_[_Ę˙ķ1úōūGø™ņöīü3į€?ču˙Oū4‹û>x }‚ėũnŸühúÚūWø˜}y#ü?ĖøŽŠûwū÷Āôē˙§˙?áŸ|˙@û¯ü ņŖëkų_áūaõåüđ˙3â*+ė{öpđ…å›.”÷š}Î>YĻAŸpŲâž_ø‰āWšķiēē‚t$ɞŖßÔvĪĐ֔ņ›åĩŸ™­,T*K–Í??ø9j(ĸˇ:BŠ( Š( Š( Š( Š( Š)ŅŖHꑩgbnj’}6ŠúWá—뿎ē|Œî%ˆĘĄÖĘ‚ūķzũ?Zôqû>x?ŗîũŊ?ø×+ÅĮėĻ×ËüÎ'އ؋kžŸĢ>#ĸžŨ˙†}đũîŋđ)˙ƏøgßĐ>ë˙Ÿüi}m+ü?Ė_^_Č˙ķ>"ĸžÜŗß€Đ.ĪÖí˙Ɨū÷Āôē˙§˙>ļŋ•ūæ^_Č˙ķ>"ĸžŨ˙†}đũîŋđ)˙ÆĒę_ŗ¯îmš;X¯m%#‰Rvb?‘GÖ×ōŋÃüÃëËųáūgŔWĄ|[ø_Ē|;Ô#ûCũĢLœ‘ŌŽ?ŨaØ˙:ķÚč…HÔ\Ņ:ŠÕXķD(ĸŠŗ@ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(ĸŠ(¯ŋžiÖ>øWĨ Ū+h ŗûMÔĖp íÜėO§S_×č~—Ĩ[ëŸ ­ôĢŌâÚ÷Nō”8`Ŧ˜8>ŧ×+ãŠ~Ąįãī žÚūŸæS´ņãŨ­ŊÄ>ņiw ž]đˇŒĢ#–O/ĖķBœƒ’œHŦøˇĮ:g…ĩũJÔĸē2k2˜ĸš$8ˆdPd9ȤQœŧ⹛[ÆžĶÅĮˆF™¯ønÉUgŊļ ėqpžcÆr¯‚vzúSž'h1x§ÅÚN+*‹ÍRHä#ũ\›íJ?ü‚ˇáSƝäBŠžģŠ^ÛéēuÕõė‚+[hši]ŽĒ‚Iü…qÖŋôģ†×^4ûŖŸlYdˇtA>V@‡Øę}kÔuŲŧqāĪ čŽ=C]ŸÉÕ#(í›7ct ˙mZÆņ@ đâPF­¨ĀíÆ…Ô#Mlûžá,ëŖÜJ*pk;Âzíŋ‰ŧ9§ëVQÍĩėBXŌ`€}pHĪĩËŨ]øûû&`ú7†D~Aɤäão§‘S|˙’Iáoúō_æjylŽCŖs§×ĩ­?@ĶÍö­r-íÃÁÚYÛ…EUŗĀMAáßé^#Šá´Ģ†‘­ÜG<2ÂđË# Ucä¯ÚCƒBøŠĒÃjĄ!¸Ûrtēū ŸÆŧÚŊ‹öŦ˙’ą/ũyÅüÚŧvē°Ī÷HėÁģŅŨ÷hQEnt…Q@ųû!h–÷Ū1Ôõ;„ö(‹<᜜Ÿ¯Н¤õŋÁaŦž‘§iš–ŗĒEÍ=Ŋ‚'îŗ´ģČĘ œ.sßæŧö3˙‰?ë”_ÍĢÚuīx’ÃėÚ÷‚o´á6 ‘‹Û QÃ#FģUŅĶæFÛÁ⃁^sŗĢ;÷ũäĘδīßôF埉’oę:ĩ֙ШgŦ­=­ÜA%ųyÛÉV† FxĪxĒĮÆ~ƒXĶcždfF†áBÉ)Á  `õčEsâKĀ>3ƒTĶŽ›Ģé–×6ˇvÂA"îû>õtnęĘŒāÖG€î"đŽŊ RG™¯hę¨ėpâŖI˙8Ėo˙j|ē2š4}ΞãĮl?­|!ē“Qž9™L1áYļąÎwšÆ:WtZjž)ņ…7 uĸũŸĪ‘Âė:=ëŗ<šņ¯1đ…ŧĶx›Áž ŊËŊņæĄŠē‘ĘDÖÁ`B}ĸTüIŠôiŧAƉ_đŽYiwY:oöëŠ Ûū‹ōíÛį?6sŒ`uÍ77Mtíúž•sâ+X<]eáįŠswui%âHōÂ#*yÎrÃĩmW“ŲËŽËņĮD>"ŗĶme-ה,n^pÃĖ;‹"`ūuëVąœŖk˙‡ŧgáīßIiĄj_Ī^{y9e ģnwcA{W›ūÕÚ,˙ ÛQdįOž6Fī†;Hú`×a ˙‹ß{˙bäúU5c~Ō˙ōGĩ÷â˙ĐÅ)čĶ]׿MDĸâ×uųŖáš(ĸŊ3Ų (ĸ€ (ĸ€ (ĸ€ (ĸ€ (ĸ€ ôĪŲĶDˇ×>*é‘Ũ¨x­UŽļ0Čb¸ĮäH?…y{ėŖ˙%]?ëĘ_æĩ†'øLæÆ?ÜËîûĪŦüKâĢ]öĶOŽŌ÷RÕŽ•¤†ÆÆ0ŌR9,UQFG,Ã'“ÅMáŊvmbK¨Ž´MWHšßa+} lāŖŖ2ļ6œŒäq‘ČŦŸøoX¸×-|Aá=FÚĪY†Ũ­$Šö&’Ūæۂ°RHnCR(đ—‰ĩyĩų<;âŨ6ŪËY[oļE-”­-ĩĖA‚3)` •f\Šį rŲ[CŽÉĮBo‡ū8Ķ|ok6›ÕģY\y"ēUW×ux҉üa&§Ą—Ũ-Ŧztvō\¨mÁ%•[xÁ Ģ‘øįĻŧĐūĶâŨ+[>Ãkqmäyyßæ˜ŽwgŒy]0sžØŠôũHÔm’âĮS˛žWI”‚?:ŗũĄe˙?–ß÷õÆš}ŧ_ÚGÄÁũĨøū…ā?Fņ~­âyn{đvĀäyvûˆiLcļö›ÔŠĪÕ~ũŋĀ^ đ×öŸ—ũ­wquöŸŗįĘķg3mÛ쿯qœŒõĀé]‡ö…—üū[ßÕ˙?´,ŋįōÛūū¯øŅíãŋ2ŦĮ~e÷š2ÆK}ØŨv:dc8Ŧ¯čđ‹xGJĐūĶö¯°Â!ķüŋ/~ įnN:úšŌūв˙ŸËoûúŋãGö…—üū[ßÕ˙^Ús"}ŧ-ne÷™~,đņׯ{KÆĶõ]>c=âÆ$ōØŠF ‡†FV ŒbĒø_ÂĶéēÍöˇ­jÚēŨÜIlgââ… +h  bNXäúVņÔl€Éŧļū睸כ|OøÍáīéŗĮcyŖŦ+ŧkv,GAūyĻǧîÅßĐĨ]?v.ī˛ÜųĶöŸŊ†ķâÕđƒ}žá|Œ2öa^MW5}FįWÕ.ĩ é —W2$cܓüĒz`ášęáéētÔ^áEVĻÁEPŅąĩÔI¯x‚Õ˜ ¤‚7Aę9ūbžÖôÍĒIw øē[Ĩåí.ė#ģ…NĘrŽŊ:#“Å|+ā^ø3Å6zΟ‚đļ$Œô’3÷”˙ž WÛžøĄáXE-–ŖEG™k;„‘Ļķé^uoŨTr{3Ɲz5%)lúôŲ+~Í;Á‰eáoN}FâīQÖRSyŠ\ /$—ģbāU  ãUüKđúĮÄ~Ņô]Bîæ?ėåD[‹oŨ썈Å"ž¸WF`F{õŽĢûBËū-ŋīę˙Ú_ķųm˙Wük?oæF?YŽüËī3u/­įˆ<=ŠG:Á‘įâD‚HöFÜučsíU´ døĶÅ:˙Û<īíΞ˙Ŗų[|&#ŪÜwnëĐcŪļ˙´,ŋįōÛūū¯øŅũĄe˙?–ß÷õƗļŽÜČ_X…­Ėžķ"īÃhņ۟âOĩíû%Œļfōķŋ{+nŨž1ˇĻ;õކĒ˙hYĪåˇũũ_ņŖûBËū-ŋīę˙´ÚBuāūŌûÎ'ū˙ÍâüY`%’ŨlüŖŖdF.|˙Ŋķģ…c~Ô7q[ü"Ôcr7Ī4Hƒ=NđOé]îģâŨB´{WV˛ˇ‰F~iW'Ø ×Įž(˙ÂĀÕĄļĶCĮĸYą12ŋMävã8úŸÂâũ´’Žģ~X”clÕü­ŠåQEzg˛QEQEQEQEQE럲åÜ6ŋŦÖg gˇ–ķŨŽ?%5äuwFÔŽ´mVĶQ°Įum –6÷ŋąč}Ģ*ĐsƒŠÜĮMÔĻâˇ?BŸ?Ųmt[ģKˆķķ"Ās´ō0[ûÜķÎ t?Ú_ķųm˙Wühūв˙ŸËoûúŋãGˇķ úĖ™}æ3øgwÄHŧSöĖyz[iŋeōēæQ&ũųöÆ1īžÔīøoûsUđíīÚüė‹ĶyŗËßį~í“nr6ũėįž+_ûBËū-ŋīę˙Ú_ķųm˙Wüi{h˙2Ö!üËī-WœÁāŸZkēĻĢiã´ßHI’}K$qōB­į "ŽĀ ’Iä“]įö…—üū[ßÕ˙̍øƒGĶm^â˙T˛ˇYŪe΅Z Ēâ!¤ŋâŌi 5—ÕŽŌæâ+ΑyBWÛÔ&NÜúd×įÕ{įí ņ†ÛÅVßđxjFm1_uÅÆ0& đĢūÎyĪĨxuábõ›ëúÝØ8?zŖVŊŋ ˙˜QEÖwQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@˙Ųendstream endobj 16 0 obj << /Filter /FlateDecode /Length 1548 >> stream xœ•Yۊ\7|Ÿ¯˜ˆ,õU !ÛąŸ’÷N°ķ˙Ō™5ģęãéMX–™ŠÕĒŽŽ:3ޝīūxĐõ÷O—Ī—õŽ :ŪøōĮå×ë_ëÍæzÄ>ūķąÛŠ×õúåũõöĪ——Wīûõã?X†‡Íëv]m-õáX{Ŋã|[üÃŊ0&“ãͰūôÕž6ēũƒ^?\^Ŋë×!ÍĖüúđá2ž' +x\>]žī]ä‡ëßOŸŽ.mčĪct¤˜A;ŅC)†zeŪb<ĮHSŌ-F^§ĻfŽ?™)Fz›„Ú?ßKsŒ6īsËšsމæŗīgĪ1ę-xPYCŗ†­¤ĖĮ{ ĢËãÚ:ī§z“B&nÂxËxäۚքu”'Ū´{‚˧ĪB8%L›˛Õ}ŌÍ&Íę>iD›ĖQĨCÄ-ēnë¨Ĩx‡Î›"Mbŋrų1Å (û•į– Eķqy,-$å“KhŪzˆ–G÷Ņ1­°ƒnĀŅgŲ4{#F™ķœŧS Žræ:.[ûDĨlu  uV­ÚA+!æU™uöYæĖŽ:ĮžN¤Ädl1™ ´NŸÕ]° ęĖåŊ3đÃĸåŊŗQC:RÖÔŖļcãTgŸÍG”ŒĀ ŊŨĩ§˜‡aųęž´‘ŗŸ+í%  ŒĢō‘Ą]ŦęwĀīL—{Y›cŖIԂ6niHĻ[°YŠŅ„!ĨōäŠNÎčÉ{ˇÔ8§­ ā!)gŋ8ŲÕ+€ HCÅŊ€4,fÉÍŌĀ}–¯ 3Ē|té ö đēô •ĻKŽ8—CR—\ŲŠ9Ÿ ͝MgĒ ¨ŦmPėT˜FŽ 7ĐŗV0U˜ëiÚæci4…4ǍP1rœš¤BÅșyÚæu0rĻīČČ(Ôš4ō,)^žčQŌ“ƤՂÅ:Æ$SŠFŦęĖĨąá¨ŗDÕ9FŠ:[UfÃÄqöa†‰3û>‘3Ä Bv°īj$§Ŧ >“+^1[đ‰’u͟čûšŪĨ§6ķ´}“cŦNQ–ybj)Ž,0Ü@~e —ņŊĖ’Pčā ŖŨ <*ëŸ*ëäã[{×ß9ėVÜÕø‹âŽËŒ{EPģc‹É˛Ŗo[į$āʼní{ŊË1i?dėSĘŲŲ-ŽĶCÆ>­“øk9˛U÷-Ÿŧ—ŽnZæ,rn1'ˇĨ†ųB™­Ŗ>ã…Xa-ë °kˆoëXމt ėû{M¸—-&7͘Ž†čą­“Ŋä•tĒŽâ0eēÃįä(ēŖ!tT)Æ-|TP=Œ›LĒŽ‹:‚ËŊ`úģ.sfpŠ0•ë†4´l/ÄØŒĢk'@LoĒú~>€˜ÎØb˛&_Šų…ú`D†¤úäŊf€O{O@U;Iuvƈdâ˛Ŋ#RnĒú.ä—+›ŧʇA?Ķ÷Öɐ_Împ y6(•0§ÃŅlôlj}ō|ĻkĪc”Ą°zēö“‘„Â6KÖ`(,;CÖz’›ŽŊ_B0‹Ę^æĶ^` že Ás'į“ Ø°ũ&˛ĨĀõpĢnT0ŧß2ez¨īdx˛ ´-•„€Â4ë2–m‹}™“ ‚ôîsĨgOļ,iÉaˡÁh–;|ÛM߯ō2ne—o‹„÷“o“¯ąîWp.ĶŋSjÖNˡM“ũXŲá,OTŌ“.Uc"U ߯VRØaÜĻ—ũ§´L˙´2Њng,įfûôOęHe=đũž˛ģĶõh Ęt0qXæc-Á4*¤Ē¯g—\^ģ>>ę顚O˙ĶŅ—qãYī…ŠÔG/§öaî&•SÛúzžYw˛ n> stream xœėŨxåú6đ÷œ˙§ĮŽ M‘&ŊJ‘ŪEéHéUšéĒ€ Qą Ō{K#ÔĐCBéŊ×ᛞ-É$;ģ;ŗŗåū]dg§ŧĪL˛sītÆÜĖôesæÎ;g!˙?€}ĖöĢõ>Xá‚߁ƒöĩ/đ¨Öø`ĪnZWnhŅŋZWVđüLë Ā ­@b8#$h‰┐ $€SōBb€ũ!1œŌŠĪĩŽÜĐōZWV8ŪGë Ā -û[ë Ā Ø+XąGë Ā 8ō Āq §”71ēûg§VĨ€û@b8Ĩ<‰QŸü'|X^ŗZĀm 1œRžÄؒfxš9ŗ*;ZZFã’+š¨7McãŠˇ™Ąęf“ ŖuŊ 1œRžÄø1ФūåaęĀbSšę熇VU˛ŅبŠæúŠ~d?Ō<3CĩĻ CGŽ5øeĘģAb8Ĩŧ‰‘\J˙r5a!ņĩt] ˛ë)Úę‹2†™B˃F˜Ē Tĸ °/$€Sʓ?c•g§ĸÄ5û”I;ŗŋ™2mzcîu‰ŠĶĻŋËØg3ēęl2yLÃ:ÍúŒd3ĻĖčÖHlĒĮŒiSčŪŽ5}oōÔ&bwI Ņ4€Ļŗiô‘žķé ’XZŅy3 މā”ō$ÆVã^Š_č v,\xY;%)’“’Ķrī%&Ņ­ŪO23šõ9§ÄĄĖ¤äŒÅâ8ÍŽf&ĨÅ÷+ØHĶĀԄ¤´´ ĄãhZRnĸní˙'%&%'Å-:˙MKIIûA7RWšĘÆQũ4Ņ-Šú[Q˙úcžbųŒƒ–Nɐ˙ų¸˙7YĮ ī¯Lz†m‹_W~mEÖ{•ĒUûßQíy¯ÜÜũ[ƌæģޤN­Zoį_× ëWŽŲYj] ‘ˁŊYéÃØ;BĮË5JūRIėą?ŠxÕj/|MįŠņ]§i^͚›h•د5 cRuũ4|(BĒūV‘™?ÕÚųM 1œ’!1jįåÖ4ŧ_k"cīŽÔw-ČldåíĐŋœ—۝˙qUØzؕRœ˙‘v¸@#˙á˙_g¸ŌcCĒ.1ēÎįūK„ŽÁԟ˙q8Uė÷ė´Ēėõy†iT^ܔIhIˇÔhą‹†9—ā`NɸWĒæģ_%œ+d¨…Y§SôÄŲ…ČąĶĻOīM|Gô…ąĶ§O\`ôtīœGoãŽoõ‰Á›Ŧ v q27ąIQ ęe†p–Ôų{ŒšCb8Ĩ<Į16Ļ–‘*Obœyhx'^ AIÜëēúŽ‹Į/Ŋōp0e,×wš&Æ": {uA7~ú›–ĪÆüŒF懁ÄpJ…]›—­ÆŠ&‰qúÉ@‘ käĮ1ü˙ h°ŽÛ$1ážtģbåmTᮗđseJmËGÍ 1œ’ŧÄX’S‡?ąIė0‰‰4‹˙ņq˙˙zęĖ˙XVāÆT/†F ?“ÖęŪXŸōēîÕ2”°g÷_ ņ˜w‹GõĨJh~ô‰wëĻ~ÆzëŪ~´Ĩô,€#Ab8ĨÂŽųÎĢm3=—_­ŗŽM/†6ęôŽŽĮÚÄXģ´“B‡/M`ė ڜėîQPŸöũŊÅSeëŊ×r[zŽ­„iß[;uz;ä*ßĩ’~y–Õŋ/YB‘ÔÛ[čØĮ­—'Įë÷d…ŋZÔü‚C@b8Ĩ<‰ņmvaƒÍˆĨ4!0>ĐlĐ6LĨ?wĘ#”čÉŌ‚cßßq™åV_Ą#Z7…š×)ē×tVč58 —÷?-YÁfÃņŽŧžžË°§˜žķIœŨ މā”ō$Æ[ēyåG’Ã=WCˇÃĒzõĒUĢU¯nėķfŒUĢIüc•j”ĶuT¨^­7…Ęüë˛ÕDÕõ{Ã*Ô(QXĨ…ŪŲĒRM͌–PĨ°Áa 1Ā´ųæ|b’böL~CëeĘû|Œļ‡Äī(lP§00á/­Kķā>ZL#ŗĸW˚۞ö <ƒO™¨kÁ‹ũE‘ÉÉÖ0cŸ}Ģ 1ĀmĖM7Ÿŧģīi]Š*=ĩõ­đž|¯Ęä ×ÜÎíUā.~•—œÄQZ×*žķ @b€›X+;0ˆŌģi]­yH ĐÜCo ƒ(FërÍCb€āY”ôÖõš…Ä 1Ā-Ė´,0ˆJk]ą9ŪĖ °5{ĩŽĀnYšö>UČbØÆ ,/|ãōé;Z~ų(DįŽŋÄŨ›™vÜ mgļ†aī/¤×ÎûW˜_u’„)œÄCj>ĪũwüŪŖ¯,m:4$ômKĮ‚ÚX¤uÉæ 1@…ī•ZCtS˛Įã§*ĩFūžĶĸˇ›vrØ?ëdQJ!ŊÎũfv|• ē}͒Áû\ŋ“˙­˛Û…}‰Ö[Ú8ˇÛZ:4ÎâĐŧ{ž#Ab€ MŒ/šĪĖuÉ>ŗ‰ŌŧũxįŽUČ×ī(ŅĻŨuíQČ=5MTîŪ­s!Ŋüˆė|éi\r^ą`đEßNšEÉߕŗCˇîĩ$Æ(RŽ=^ąt(hå‰ņ–Ö5›ár‰ŅiŅ;æR@Í}ėŌŽk*,1„Ģ MŒGųßĢŅUüeŸ!ÚǝŖéīŋS7ãsS:uéTØŗēĘ}đŪ˙Lģ‰ņn—:†w[v­'1n­.ųíģ54yŗöG Œīˇā˙¯Üõ]cŨ]ē÷ĸĩëfē¨ĖûīŋÄ˙\WđŅdÅ>f­a7‰§‡q‰qK|ÕÜ0íü_X_|˙CĶÛž=û^ũÔwũ°+DĮnoÔÖŽk]ūĮīébåéwģŧWͤ§7‚?-O Gŋ$Ã$1ÆG”cëŽV*b`ŲēÅ%øĘėÔÄ9ĻŨ=ĖÆ>hen´ąĪãôš…7S‚<ĘzÆSažÍããŖ —ēNĻ$L13BũŸ§œoėžlōîBc÷Jš’pY˙úƒ ĄĢÍV°Uš7×ė`ÎM:1Ę$Å‘Ą&GBī°ÉYiFą÷ÂRˆâ˙ÎF†‡÷ߖ™îĮ=ŒāīŦ?ėazv‚î–ÉkŖŌŗĶ㎚ŦœG„?â~5?‡F˛īâ2ĶÃL~ˇúÄx}[DFV•AÂ{/§d%ŸaĄ‡ģē§wtfԇŦëŨ´ė¤;ãų÷žÉ~NĘNŧؑëXų$ĸáéĖ´ėŠí YŅ˙÷āi៕ũ0‰ƒš1C&ķ/ŋ}ųōš˜ĖôˆåŒErË!íÁY}C_†‡:':;5f<;žš•t˙{_ņä1KœÆ‘"Xh QÆÆÚ^OÉĘJŠ˜Å؎ĮÜęįADkvúq˙›*ģĸ2ŗâũ„§ĖĖ‹xgVDfFė|×đ ¤ŦŦ„ˆ‘& âAdcÖ3"lüį22ãw›<@āzčé7Îed&ú°ņáYéŅ ų÷<"3˛2â¯ŲēŖŅĩˌ¨%‰îæËŖ‹Ö5›a’ŠģF…m¨â÷OÃāÕ^q=SÆČƒ×d2íö l'M57ÚXú™í"q˙•܃G3Ãõˇ8ßŊ͸û÷ė¸Â&1 ûچG†Î‰ëŗwŨfĪԔß=|roęoģõUîOw =Æūt&ķ ÉņZŽņĶŅë‘°î~|ŅÍ0ö<Ũ`_Đsƒ99éÄ(O´~ĖmŒ”uŒÂ¸ˆÉy§‡øQķd͈Ns/ļņߎ?dl*QVh˙Č‰ŌŸ$Ũ7~憎æ˙ęi7EōO6YcЉQ*ˆ(1Œë3{ë]nAOøv ˆî…ņĪ,îŕņ$h‰ØŌ!Šâ†Näū&ˇä&0‡#Љ (ÅX.ßbÂ¸ēø•jWĸܰ4ĸ•ŒØ íĄ(nŨO“˜8KQú†fqÍR4×+û…q ņã?]â~´â7%ÄÁ“YEnŸDq/GķĮbxŲâUV>œKUî_6'ŠUD(6’ëŊLÜ×ÁÕFMgŒZąDG)_{Œ})*8į Wũ]JäúBØžI MŅũzˆ‚ÂsŸd/ōĀM¸vbüËmOk\Äž-ņk†ŦŅ#ķ<ôb -`ÛČėŪž~ô÷Q$ŧ^FŨš˙ëÆęöųļŖá+œÂOUÛÃ}ƒ,gōFŦ™[öŪŽ}Šû˙ŨŒSēîėMŗīëû}F0VčVũ*}bü™ĘXɊE7ÃĄ‹ėsnĨįÚ¤Ŗ‚_ļĄˆÄHŋāĪģÁŸúsûp}ÎĘsĢúu¯LŧÉ­P'|ÂG…Ī_\ƒ_á}ĀĒ'Pfg.§éQq֞{§+Jšk 䆎dl?×g*{š “?E11öųUaãŗ)§c\'_æū:šP2Į­8éˇų—#ũXģXJŦÁØnâŗāŋŪD§ø~Ũš÷5ûFm§p zđz&åDŊ-–ÂZGQ ÷};7đ|aÎNŗŠG¸uōXCˆņ§õĪūĮ§É8Ö/˛› ĶžĀõjÉ'ÆdŽÖĮ_~ÉVåĐ\á°Îŋė3.iƌōüŗČ¸<ōáV˙¯ąųš”ô4˙ė2"nķâ¤p–ÁĄ˙_”mr܄ëߒõįū?ú2ŋ§0ÜøQáø“üo‰ü^ã÷rŋȇ9ŅuØ{‰”ĶU3Ąë“æøĢ>{píÄøžĘ°Ŋ âw°ö;rŅQŗc]]ŋįۛ< ĩr'ŨsU+ujđčÂÛītl_pēd°rīNŦÔŠ“Éé-u;žSĻϏ)jkøŽaGØTĖE† 9]Ŗ;ˇ)2QˇdĪ’pÃĮYâķũXČöƒn…͏Súŗų;„YhĐénJwö(WéÔÄ;ō­w:ŧ¤›^NĩŒ‰Q¯Sûõôë¨ųnmV¯"÷ĸZ’xũūędq>ū‰e î‹Ãtl3›´ë¨›ŋWÛt29ËąxûVēĨ؊éŅÄēītÔīß­ŨŠ&kÜ_ęY$t5§oĨįÜe~ŽTQ‰ĄĮo×^ŋųū,üđĪmâ7*Ūį‡cqÛĸܗū{đû$ėĶ˙hhƒ˙&hH ū[n•ėolKHŒJŲ”ŌHlb;ëI‰ü_RRūÄā¸Lt„ûņ-Ņ7BbÜį:ŪæļŠņkõx~m 4˙J2ŋcíŅ~ŽcBˇ×ëĖå÷z5qŌ|bđÛcˆ…‰]664SØb‡„b_äÖņƒķ&÷ąĄÛü€ĩĻq˙Í#ōe†ãBbÔäļJˆKcŖšŽÜÆcÜfÉ,Æ^žŅáYc{úÄČâj|‘K㉞qÂLV"ádĒeB‘ĪwæŋäŨ#.ŽÉŸ˜üŅąÂ~ÅnÄĩc&ŊÆöF•å_žĀUSõH2e ĪņfĶš¯Bwúé†û3‘2ŧ…uoŅĪeÁéūžîÁũméÎŦßĖŊLđo#v4¸ĘmĀF¤‰ĪüŽÜæû˛ãh"˙úŲÔ´Ųˆģ™â:>"pX‹v[øo:Œß"ž7ĸáŋYē3Ę>ĸnl8!?žWԎûŧ|@yΰܜU–ßaĢOŒm+͟ Zˇ†ĢÜ| …eÛö2˙š+ŨĻžwdÖ⡏īåĖk>5‘„15I?͝EÛ_„?}VáQŌŦ†3Čԝŋ"Ÿãđ—~éƒĩl:‹ú6o+nˇ˙IŖš|îĨ[RĶÎNžķ´D›ˇŽ&Vk­ÛjÚEs¯%߲¸­ŗŠô+âZ4b7÷ą›%9ãŽÃēÄ0=ŽÁ%kˆBLC<6%$F áCxа…âŨŨ8CbđßkFé֎"!1žÕUō:‡īØĄ{ŦÜ$Ąc¯đ%—-&°ĩ”q°~ĢblįĪâI=…>ÛÄ>Ã˙å;ļ‹ßáûš$F^ëÉpōĪ$ÖŖī  '„÷v‹›˙ėžw!‹JXŪķŅu~ōËąēÚ{čøOƒõƒEņ{:šS[Íx>6‘ßß0”øŨ­4úÎ8L_Úb2Æ1Ã.ˆ$ņ´¤hq‰ß¤ÃëuK¤|ŧ¸c<[ˇaÎUūGÅ,ūŖ1ÛpŽÔjm„U^~đhĒđíđīxá1Œ†#ßģõÃŧKü ++t=m˙C°ÃøątyĘ'ߑ71~WaFUg…‰‡DycPÁÄøTwQÜÄ}ič–%Ž/K%Æ\Ķ w‹û^yDT_Ÿ¯‡įßsÛyW6åôäWšÆģ•æO “† I ū›I}bp ULÚY(Ļāzđ !1†ˆģ°øĨxȐ%ČPVßŗú=zú(;1V‰•rE-{đߚFsÛs ŦH û\NfŊB¯āû– WûlMļ’ęŅhî˙ۗ„÷čūÔĖ$†Ø÷Ģō?ž[ㅏOĻĶŨÂk!1Ūą11šĨĮ ģĩ˛|/^<˙8éüųŽbŧ‰ŅÃdcbdŠWHč|õâž+įcˇÍ!ŧpA?čxŨĨëÉīâÅ ÷Ō.ø‹Z&‰Qâ"=öÚ/ŗôŗuuÁÄ`÷ųiF‹{Ē'č˙V\ũ”Zæã?Woü?ĶŌ{ĨÄÄđ×Vyã a‡Ûqsw6qįM>˙WŸęk.1ø•ãdąĨ™üu>š#ųĪ€Tb4l°&dĪ8ņ\ŠļüÅ3}bđ§ēÎeė͐Ķ?T(ņãAá,ŠpĘū˜? ͯ§WÜŨ;)_b|OdrˇFŠÄøIŧhoģ0¸‡Đ§‘Ü6ĪQa)¤ōãŠįJåō§–1vW8vfšíˇy_—™É>`Kãp$¨U*å|&öāįøŦp˜iåšo]}kšhŽ}ä;¯)†ĶI8ߨ—pZ’ˇxáÚŨęOȄ ^ŊfšƒH¸P(SÜÆØ\•û¯ÁéDᨆ‰1R¤aÅÉS§N‰?vTŅëfîģŪgŅ×}ķ\LiLŒņ”ŽláČwŨĩü\Û;ü-zĒ%‰YĄŋ]ŨVũuĀ=Ns ¸•|ÂsĨĐm’ūIüycׄ͜=ŅÂÉX{$cŞÎÍԟˆöÔyŽ:vqæã9Ũ7d.1rSŌŲ?äKŒŋ¸‘ĩĪ—üZëüœīˆ?v0™û˙‡yËM¯ē0›Üú0eĶ<ņ˛n%ŗt! ƒÉõēÄāĪÃŊ9]ē0|bD,]'ėÖ×'Ƈ\ņ›æ\ÖņÜ˙æ}XÜÍĩ“čö‚5iD_åKŒ9Dá‹ûę’JŒ¸ávÍáĪ&áČ}Ėâ/s3xfæqî~˜›ęˇŗ^Ņ%ˇ â×Î÷Îų͓•šíŸ¯į_˓P–$Æ|ĸœ‹ų%3WėAGæū+.Î\á¤.7æN‰ņmJYũËb$ŦN/ûágÎT…ė]bŋGü~§Ä‚3ün’[Åufæ¯ü˙eĨ^ËË˙ˆâw´ĶĮȧéF9wīÜ(œlYŨX¸É^)ŽOūX#įķ<ŋŠŖ†ÄؑÅ_Ē:]ØΆ grņ“ķÖ>ŊuĮ1^ē"|RŒíĖ2ė•L- - GNŽ [Ãh÷ĨxÃq cb„Ŧ˙åÂ:y7RXhņ}GAቱQw†Ō3܇į9ĶsM>Uŋ ‡ øeŊœč˙āŦáW`â÷dÎîŠu[X8ßoCļ8ĻÉ:Ķ„-ƒƒDü10n &לęîD¸$]GüB˛Sxũ€L÷n’¸ F0[¸ã—įøĢŪ„S°ļŠqÃUŸ) Pą:WŁCųËØEąƒ˙Ū!îĪę.D@#~ũÕ?|\ōĮô˙ßzHÂÕH{„1Īúōƒ×M&>œäōoŨžDéÍÄōYuuWđąMâž y9ËJ - K¸ôtžŽāËŌu÷]'ōW„ī•bė+áėZqæ' ;ɸWüV%å:˛ō\č†Ü'1jVŲ’Úļž~Ķa%mmÔf°ÆdėzŌäš}&éÖwŋåô­ž‚úįŋôîŒW˰×úUøŒûËÛßēN÷´ˇZ1ގšÚŧNĪ“I܊ēXšØŖo˛×úĐürų'Ā ¤‡ægā(Í-ũVí)ē-ˆUļŨ,[Y×īŌŖöNĨņW~”­=–×j$žƒđRƒrg"J4ˇĄjE<ę[“[yėæõ ŖĖęö  uÖÍÕ&eЉņūŖä^ęט­ßĘŽæņ°z]ūäūz5§PĪ:ēģų&Ŧ×jUÎíJ|ÜúdĪĢŨ;˜č•Œ=ß ÂĄÄâ t—žxį9n_îĢæķC9ÂcΝ áœęgîÜĪ“ŖBôî,áV—Awø¯´“CųK!Fxú}†„ÜwƒÜåĪJ{jüū ˙ŸÄķĶÚ,÷ŧ˛ŲôãČ Í}ŨŲĖīģėq÷ļÉŠIŨN—k´ôōíƒtoN:¸įŊßōšp÷=ņÕđ]×~"€KŒÃ%ÖŨ<Ã_œĀŨ ŌŨT¤ņڋ'fŠÛģCžxlvEąĮĐŽũ!œÂåÄŠęp7€ßŸŲs_ĐÃļ÷ˆ@~Ķf]p0ŸWĮo #Œ<tv&û%ˆ˙síļ;čīī ē8‡MŊs‡ÛĀ)š60č×2ėPāū" Örõĩ€ŨâĮ}ڝ@ūܚâ÷îs?*MÛ°ķKĶ…}7ā^CöŅŨÛÂqģwß7ô9w;„ßōšpīUîķÄYœ}!hw/v"čz-!1Úô>pûáŪWWnßíÎܙÛ$Æ 9ܡƒŦŦlũVņǏŦĖŨŸoąŖŠYéˇô7CĢq5'+ąĀ=dzs_˛oÅ/ūÛš ŲYéoåđ_äKŅĄøėŦÔûƒ¸÷÷ō߈ÆķNÄIŨãí„ņ;VĄ¸íôÔŦŦŦ\Ũ–„°W7;X×ķÃĐėŦ'üÁv.7—¸yÎfW^fŽî -ofg&ūNüÁāė3ÉŲY)ˇÄm†Žw˛˛“ļŨ]Fp[čYY™”ĨÛá5*‡[@iŸđ§+rggå ŪŽāĮ •p)!W¸ČūŒėÔ Ū”s‚ŋƒo3;Y{'%&%%Ū™ivöXßĖBnūíܜī|ˆw1<(^§V„Ũî}ƒ îķ×Që‡w"ėĒuÍf˛1hčƒ‡ ¨ŋˇ%Ģ0čķ†ž-†~`2hĪĄĩY~Ĩ~1x؇åF <\ˆ–˛†qŅŲkŋ&îÅ* &žčÚføā/FŧŪcčā!} L‚ķR˙—ÍÖßrČā!ŧūēōš įZūXß÷ųžÄ÷ßöÅ !C†‹{šš 2˜›ģOôCuRŸŊ?d÷ũŋô‡ŦūÌg>}4â-v›?JũŸÛŅTbČƒ‡,Íõ:„Ÿô`ũVR‡ĄũkąĻ#ÄáZ {U:Ŧc Ä6?ŪېļlđĐ!c=˛eljŧåīŠ×Î:_b<ˆšģâÎ“3ˆ <œ#/$F'­kpë-O ŸŖew.wˇsÅ^0?Œ…âtčĩÉĒ^ô€ŒũgbŽ+ŪUŨųcRŽîŊÜĖ€H $†¨ūŌk~–'Æųŗ#ʛŸļv”KŒR{/žį]^ĨÔÃmYwOˇHŧn­Ķę= ˙Āx|¨ōœ/1XįWR˛â˜=÷g‡x(Ũ]QƒWašŠįä÷ŗ- ŒĀ’kCbŧFž¨uũ…R.1ꆑxןJMŅ4ú$p¸ŌÕ|C˙žW፛ŗÍ¯7¸æã–01äzvųU:-÷ŊiibđĮ„Ģ­ N÷Ĩņ<{ĨŠ&<.˙ÃĐl6X8ī3FƑo…Ä—ĩ"€ÎŒ×Ŋ^jibčÎĻ,íHŪH tėü‚ų\\RąÕÁtÆôi†÷- Œí&Ŗ.ŋE^í=f!1@H p=å׆¤ž–÷ŊūFdŪËz‹-ŋ–ë=ŨŽs 4€ÄSĪ#$Æķ‹‚;VYšīŊÚŌĢ)>Žtß$h‰ޤųĘ;Ą>CĘJöÛ*;0ŌÆHN ÁâëqžĶäŨTH}H Đ\FÛeÁŧGU)´˙œTyünĄ“hē00ĖgJEЎ4€Ä×Đvqđ}īąU‹æíũiæķ"rMŅ[­—>ô`ö’_Õ!1؊X˙uÍ愐āZ~đØgBķļž})".ąPŅáF˸Đģ͊ gĮhH 6čˇÃi ÍÍį|āėŪ^|-ÂwjqŲÃWmPŋ äO¤åǐG~ÃŪ´Ļ^…˜$F¯ķ˙ĮĻī}Ur°Zį7ÚĄ˜ÖŪ­Y/Κækāų>kuEŋ!XsÍmOũ­^žîđSaˇÎüálĩBú°ßnŨč§]īį€}õ ōŌ4Ÿ€múûžšāĖ­c†ÛCŧ˙öo&ëõĻ—eÜØc´Ī­•Ōw80ƒŊxzˆĄ“ėąāí‰N­úR˙īéš<­Áš{^ÃKiŅ4Ī$1Sv„áÂŖæ-QBjĩkÆZÉv‘ŲSÉ&ŌVn0ŨiĘC˛Ķ]$áaMŦIųė‹Čķ@o14 °)Ž=Lē'DąŲ}¨ā=e=&ŋ}Otw ꐞ{v˙-ōīWģ“BöEd‡Z¤ WČī@NDI‰^/ĶMöųēs~171g„ÄįõŌō+YžĶ4, öē$Ī‘æ‡SƒIbėĻOŲåÔBîc4šđãøŌ OˇĖZÂļĶįæëK?°ŋt÷j(ũŒ?–ņŌû;š)×ūđÎ žOįémī—Čw}KĮzÖ?Ûļˇ}1‚ Į?Â)E˛æwh0÷5]ŧbæáŲõlņ|72ė—ëŽBt^OÍYÅ.^ BWõĪzxūüũŲßūÉOä+tøĶĪ=7äŠ#ĩūėƒ:÷ŸŪ.đŲnÚdÚôąâōŠšū§“$>/Qx,5ģũXøQę“O;?'ĖnĶ—Ũ§ß^w]KtüĶyŠB5]Hø%—ĸøĘôgdŸ†ŒâVĻ4ĪĐâ­ŗÛ\N‰ÎĨĘęģ g†j]…„ ˯fxΰ_{&‰ŅˆûÎ=ōņõ$üH6ėj9ĻOŒūüĩ›’xØåũūFāĢŁ .HÜ,auɸx× øßģ&RfÆJōw`iO ųwˆ}*­Ī:`6āü@ɚËū۔ŊŠ{ÎQÚĪũ_'‚>åģzć= ßuƒ%~›bW‚~´ŅÔY÷*GÂËgų˙× ģ˛>2ė>hhg4˙ÄŲ&'J×"Ũō™ũø~rŽx\ä'Ąé9”d*D—ų#íÍIÜn'l0•ÛšÅõ¸ ƒÉoë8ÆNv0t–‹ã˛¤ģärpbH p"o¯ŧáķEķjŖŪŌk >ö ÂŽĮˆ‹X7Ĩ{Ųpc­ãúÄX•S›ą§#7Ũ„Õá<ŌõĢųÕúÍšûÅ׍ȸũļAāŠ.1Ļ|y(ž|ã&MĒ˙œņ†Õ30Ÿb÷œÍŽ~lŽáļŪŧ,>Û{‚˙cœ!1ļĐÎ=Ÿc´hņĄŦ úãįƒL&Ū}ÅOjhč,yĪx]æ3KIÜąvŽŽî}ačs×W˙Ēéŧ ¸¨&“Ûcų˙›…Ō™ŖYˇ„´cÃĖ|ĶNjūÖg{-˙šŦEáDā,Z-~č3ĸ˛Öe­Éĸ[QŪSíąž(,1Šũp72)õà @†Äø‚žfl0u:ē ķÅĨë™|=wŸ8\Q‰a8—øēnLvmëį Ķö m7&Õbė3j)ŧ'žZxb°qū1Ųׅ͒Lņ(vŦDb,HL|tëžņđtô{L[ū>I ēąŪ×VV~`Ü6Ō'FģĢŲAˇHl`rސ“ÄeUnŅ•s“J˙ŧīü{Ĩōɰâ0žãCb€Shģ$čžĪ—…^ČåHZ. |ė3ąđÛ[)¤Äxa*c;Ė 3ī6$KĄ#;stŨˆŋŧ.1B"•e,ÂtãųÖÂë yöJMÕo’0æúŪ'œm~,j_č÷ē ûЇ&äÛ+eLŒ!oŗâœ"ūLâ4ņ0DÚßē~ÆÄ˜K[›2Öΰ1‡ŧt[+Ä'l ĸn†z ‡ÛEēÄx>9°gIƒŋē&‹­o6Î<Û$Ŋoß̧āSÉŋËÜBŽ.qnH p|­=ô™ ú:X9m—=đĢîmD IŒÎÂEŦģauyü‰îE÷”î,Đutļ1ÄŊRī‰×DÄęãmžWŋœ&üëÂŽ‰}ĻWš_‰' ÕũIō ÆJc*š­ŋڎš˙=ˆŋHz<‰—{ëŽEOŠ÷Ž×:–ôהd^~„ō×ũKä7åŽcœ7Į^ §ˇÄΝ´™ûŸß“õ&]Ūų%˙Ũ ļNnDŊd¨I—ƒ‰ĪËiâ^Љ´š˙ņX8ū˛ž—JdÜ,)ÎŽĐ@ĶoއėtûƒÛŦēķāŦšûĐ IŒ.”ÚĢXņ†Ä/č/—`'Ÿ0ņčĀĻ×øõ"ū’EĞfŦ|Ŧw*O͟ZúFĘ>öâÉ$á.]SŠ7+Ŋ’Žđ¯ŸábâšbúÃ7Ķųĩ¸WĸdįéĒŲú?Ĩ?X…5$îS NXę•wŌÄ-›Ž4åéSsĸÅKrëėnΈ÷@IĄ_Ē4Yˆ™öéįü¯k2 Û˙W‚]fēĶÅļĐĀâ/6=Gmš™c5/pŗZėŋÃųëUŽÄRáĨj›č'~°ŖÔø?Í/äīëĄOŒˇhGÕj­Ķ"<‘’>e OQîu´öR߄9ĪmEbØ[ŨÅWb|ĻŋŦuÖiēúŪŸaÖ.Z!‰ņ>Pdxzr_žŖÁ㈰z)|į.ŸpûöÕUü—m˙xŠ<čn …ķߜ‡&g„%QxF”į3ü€K(*6WwŨw0…ÅåĻSô:VųA7LD´î|Zv<ũĻ˙ƒˆN’E\Ą[fë/u#+,QÍBšŗNõŗtŽ"ĸ)…"ws¯F‡ĮSLx¤pöUÚũˤ°xŨfEī„Ŧ°œ‹ņ™ą#ķ‹âg4F|ØîKžFq QŸą÷(õQDDxr.ŸFo^ŖØ°”lqãáŗôİܘÁ&5écĖÎN K§Č´(~›g2ä>ÉJ/hßN1tÕė ´x¸K€]•]æŸęå`ĪJĩĐ[ëīÅzWåļW…ųžÍ>Xå1H|ũüŌÕ+WxŦ™ÉwûÆķטŽmMUX\QÆô[7ûuCGĢEĢz{}žzIÃfĢ×~ÁŊĩÖcՊUĢW SüōõŠsÖN7”0Éã6Úc}CnC` ?Ŗkûęzt^ÉŊŦŋrUmöėŦ•+=xŗžú4šŗvĸūŦ€g¯Ë{Ã*ÚR%gŦŲM^ĪĪāäÜ&]VOÖŌvąGfÖS¯ŊŊ‰ÆËXN‰jé:3Uë"P}]H†×Zzˇķéqē7ˇ+^‹Ģņ”xs2Y~x'­ąŊ„ÄG´üyMÔēŔYD^Jß°ĐŌÄXwW÷⤜Ãļn§˙_´Ô Ę'R[‹§Vŧ‹öÜRM1 ō´üöŠnÅ-ō,ęŦŗ41JĻ_á/@n˛ßäK 7”?6˛7MââĐVËiT=û䨐āPƝš“âh÷ŒRHé•×sŧ”ÛĪfņ3ø*z W›Ũėb~P÷ķŌnŅ\hQ°G7~™EŲŋ G…ÄĮQw坯ĄÅ´.C=5—]KQęŪSV<ĩĩâg†ÖQĻu×ķzéÛĀW~ŗRe‡Ŋ/ũ!1ĀA4[ę;\ÆSļÛÛKnÄúLUā|a<į4€ÄGĐfIĐß1NqÛ5_æ=Š‚Sņęk~…yėŅēp{­ŋ ŧī;ŪUO.‘ÔvyĐCŸqUm™4° ‰šjžāÖãŗ“*j]†ũĩ[üĀw”õĄŊR ė• 5üæjäŲé¯j]†VZ­žûČwˆ•짐 $hĨʒˉ>3Tšƒ†ķh¸î~¤įĐâVŒ‰Ä 1@O/ķĪqō{F)ĨÎúģIg†[<4€Ä ,ŊFg&›ĖmT\w‡<-ŧnŅķcuj(Â$Ø×WŪ&¯qZWápž÷¸Mgž´`„s?Mûę̝fĖņ€ŊL9rXĩ@e<‚2=Gi]…ƒzvÕ ō”ŊéĩęØQģ[`ūO@UVŨ‰÷ū”Öe8˛Ę+ŽĨ{MĶē m5Zî;TúÖ=`ĸū˛ë 8'ÜWËĨmšZÍÍ4[éå|6°Y›E÷}ĮÕĐē įŌzYĐ#ī •ĩ.ĀŽZÎ |xv"ļ.ŦĐneđŸ1Xtā/¸vnĘëZ—áŧÚŽyč;Ŧ’Öe¨ŦÖ"˙Øŗ_)đ ÷Ölíũ'^CpL\Wé%—Ķ}žŌē QÃũØ3CĩŽ@Kü (Ēú†ģ™§‡i]€Â–Ũ$īIZá‚ĘŦ ļøŪSŽë•Uä5Fë*\ÖĶĢnŅ™ņZW`ģrÁɸg”ĘĘŽēží9Eë*lQgEp´÷đ’Z—áęŦ¸–ė‰ÃD⤚. =;— ØM“e7cŧ&ãĖep6­ß~pv,nbg-—?ņœP^ë2dkš€‹‹‰ˆ Mđˇņ[Eë2dh:īf¨ßėŒŌP{>#āØŪZx%ĘīĢZ—­×Ū{ė3¸œÖeĸâĸËIž3ŸŅē 5Úđ ōĖ/i]@AKüsŊgh]äQįÛûɧ‡h]@‹¯‘Ž sD•ÖßĨ3¸÷8ˆ˙Ŧ oÜĨÂq=ŋ:Ná.- š2Ģ3<ą6rt¯ŦžIĮi]¸ŗ*+ƒãŊFž u GuĢI‡p—[ĐDÃeá~Ãqyąy{åÕØŖĩŽœGŨū3jf˙zōĻŌbņí‡~cĒĢ[*(¯åō›{Ĩļ4Ūč;Ŋđŋ ™žŲÆî3ę™s)Š”puÎ+æ&Ōjáíû~jÚŖ^P^ÛĩB÷+–įŊé>ąE˙]Ȕ˛ēžFŗ &įS˙djQĶh>7āĄßäjö*ÔĐeķŖ{‡îB?ôļ"q!JZ‹[ŽāošúŨ…MĄŅüĢáį§•ĩgŅ Žŋ 9Ú§˙rŗ‚yÁģQGë™›yĘ˙Ė_”ŋúÂËq~3ÍîŗgŅéĮ¸“܏ƒ ҃†ZĪØhˇ%ŸųųĮ.žčR†īL-ęUfė{Ń(HëųÛLļė3?;ĪȋüÉ{ēF…ƒĒúĢD{ĩž-°‰ŦƒŪFąÆ1—Ū ŸÉÚĒ Q%1¨ŗÖķ6XdéG~­0Ú++oįzÕ¸vPĪ5â‚ãĨõŒ€ ŽYú‘ŋÎXšUÉ^¸g”KÛ¯F\prĩž1°^ũxK?ōY_FûŒ,Ŗuá ŽLŌB0Aë9Ģ´øŸs|te­ĢĩUS!+D[ĩž5°Ú,Ë?ō‹ĩŽÔ÷šōQĄsPëYĢÍĩü#ŋ^ëšA}㔏 o­g Ŧ†Ä)c• œ,āŧ ‰!1@  Bb€$„Ä)H (‰RP¤ 1  $HAb@AH ‚Ä€‚ ‰!1@  Bb€$4ĮōüZ­kõV>*tNk=k`5+žKŽÖēfP_såŖBįW­g ŦÖ1ÕŌO|N­k;P#, ´ž3°ŪcK?ņQZW öpI´āáyŋNlƒĨŸøŸ´ŽėaąiÁšĄõŒ€ ĨXö‰ĪŦ uÅ`1ę$Æ8­į lņeŸø´Žėc‘*q[ëŲ› æ>ī >ŨÄaGĜ(§]Í`Omö˜ũģ)%`¤Ö3ļpđCĮ ãŋŨU„“ūgņh"öGģŅëwõ‡Áųįņ>3Cėūy~m­įlr2y°ĩŖKH —Tņtô Ĩ§9<ōdeĨ§ N‰āŠ&FûŸųĄ,Uėhødå§ Î‰āzĒŽÎ”GÆ/¯Î”Á 1\θčc˙UkÚO‰ÃĶ8ŨĀÅ?ž¤ęm=ĨTsōāȐŽedÆaĩ›8H žĩ ΉāRŧĶ{ĢßHW:Ĩ~#‐.d$ũkŸ†ögãpw„Äp%Î$}j¯ļúƝ~Í^mÃ@b¸Š‰IûėØÚK{đpˇƒÄp UOF ŗo‹ÃžœÆ%ān‰ā&Gí/cī6ËíÃĻÜ ĀT96F‹vG„Ÿ*§Eģ $€ķsPŲÛÔĘöęÁ$;ī -!1œŨs'cGh×úĐÄ#8iĘm 1œÜāô#Úp"QÕģ’€Ab8ˇãYŠ<ÉÛÃRNh]ØĀ™ Ívˆ•õąLlf¸$€ķNîŖu ĸ~‰žZ—v€ÄpZSh]‚Ņūô/ĩ.T‡ÄpRĨ|"ęĻãĸũ´.ԆÄpNSãö•Ōē†ŧž=˜„Úē8$€3*å1Dë } OwiH '4>æp ­kōĘąÜiʕ!1œNåcq{kŽŅQĮ*i]¨‰ālÆEžxVë ÷ĘąČņZ×jAb8—7Ž$ŒÔē†ĸ O<öĸÖ5€:NeTÂq­K0īHęp­KU 1œÉŠôAZ— ĮįtRë@ H į1’č"īĸ ‡=8ÖCb8 ¯ÔŪZ— ß'駞ĶēPĀIŒÉÜĢu –Ų—(ÆĶ ovH Į3!öāëZ× ŽgGĶē°Ā.šN˙ķjȝÂîZĐÚ0hšą.p FaFG3ŪiĒõ×;oēTBŽū1Ŗ‰†•BAH ;øøJ2™‘øĨ8ė˜Č“.ũ(ĸ'Âu7´˜nnŠ$_čŠmĩ@uÅ™[1Š.7â‡Ū4QëzÕ6i ˙Ųō–Ę1ܕĘq 1ÔÖ(TŪĒ‘(Š—ÖĩÚO÷TšKå^S­k=$€ĘĒ'Č]5åvÖēZ{y/GūR‰­­uĩ ƒÄPŲ-ųĢFĸ87yūĐkņ–,•@­Ë$€ēÖY˛j$r“ģ/ļlŠ|¯uŊ Bb¨ęmŗ'Iåķ™ÖÛC J.Žûs H U­ĩpŨHĮĩŽØNZēT~Ôēb 1TuÛŌucĸÖÛAqŲgéh]2jj`éN)ĸZ×Ŧž>/z^뚁‡ÄPS_ N"ÕYĻuÍę›nyb|¤uÍĀCb¨iļåëÆMZ×Ŧž –/•ņZ× <$€šfZžnܨuÍę[oųRĢuÍĀCb¨ ‰!‰áŦjBbHAb8+$€šRÎ ‰ &$†$†ŗBb¨ ‰!‰áŦBĘj]€+CbHAb8Ģk•ĩŽĀ•!1¤ 1œļ1ԄЂÄpV8Ž &$†$†ŗBb¨ ‰!‰áŦjBbHAb8+$€šRŦHŒqZ× <$€šĻXžn\¯uÍę[iųRŽuÍĀCb¨ŠSēÅëÆYZ×Ŧž–'Fc­k@Uq–Ž3ši]˛úÚ[œŖŲZ— $€ĒZēn|ŦuÅöpĮŌĨr@ëŠA€ÄP•Å;`Öj]ą=,ątŠLĐēb 1ÔuŞUc´[܆ĄL¤eK%Xë‚A„ÄPW/Ë֍sĩŽ×>ĻYļTúj]/ˆ*[eÉĒq¯ÖÕÚË.K–Šœpė$jÛ.ÕčĢu­öã-ŠüŠu­ ‡ÄPŨršĢÆŋĩŽÔžäiî:­+$€ú>ž!gÕx´ÖuÚטr–ĘÍŪZ× FH {ą+)ģ¨õbN摊Z×hĶO¤æĩT˛“vŒŌēF0…İ‹}ßVũ|čB Üø‹ZW¨ŋ oõ+|ŠôŽēn‡ÖBH {Xžlnˆ€íQ‡c9îonˆ”Uö¨äBbØÁgTŅė0i+Õ¯ÃąŦŒ1;H4ܴ֑ 1Ô׀>2?PĻ~%ŽdÕ5?PŸĖwÔ¯äBb¨îÕÄųrû4­Ŋڕ8’ˇŠĢœÁƕWģ ‰ ēëģä ˇ8ڍVŽ˙‹_$oĀ×Õ-,€ÄPÛákr‡Ü}SÍ:Ë­}r‡<DÍ:ĀH •­{\Lö°įöĢXˆCŲwYö Ĩ|§b!` $€ēÆÆv’?đë6ĒW‰#YķŦüÛŎS¯°@U&Ztzh›„ņjUâHFĨ´´dđ)íT*,ƒÄPSųPËFī'LuŠŗđDâ)ęBb¨Iūņ]ŊĨņ˙SŖGR&ÂâÛŅūĸF!`)$€ŠŽË?žk°ãžâe8Ëc”ą›nķŦ)‡†ÄPĪĘhkÆētXé:ˑ‹ÖŒ•ž\é:ĀrH Õ ”s I+.ÄĄ|ũŧ5Ŗ•Ŗ!JWCb¨Eæm0 z•)ZˆC™ŪĀē?ÎhĢl%`9$€ZZ;fīL‹N>u&M3X;ęĸØ˛JVV@b¨$ІcĩKĸË)WˆCIûÆúqŨé&* ‰ ŽũVœ&eäĒ+ĮĀlÛ}nĸ⨐ĒXaūiAEōwÉŗI÷ÚölÚ2Ũä&* ‰ †ŅŠ-l›@ąĐ5ĘTâH<âlœ@Ûø ŠVBb¨āŨ¸ĄļNĸeâ—JTâH†¤6ąyą•¨Ŧ„ÄPŪáëmŸČˆD{^iëÔQļOdiė+ļOŦ…ÄPŪM+nƒQвHųÖpoDŽRb2;î)1°@q‡Ŧē FA˙¸ÔĘŅ_Ą›Ÿ¸úMT@ibžQhJ—N)4!pø–RSJZŠÔ”ĀRH …LKąi…šĖ S›+v÷âdõeã`#$€˛šĨ÷Wnb5s]äö{Ŗ[+7ąžŲ͕›X‰ ,[nƒQЧÔPÉÉiĨmēÍg›Zûē’“ؐŠ ū[Ųé}í g“–ŒY ėw)vP,‚ÄPŌžkJOq ÜaęÖĨ§čš7Qq|H ­ŽW~šĘ\ÜĄĨCןäËŽx'€ÄPÎĐ´ˇ•ŸčsĄNūHžeQO+?Ņ–‰c•Ÿ(˜ƒÄPL›Ô‘jLļ…:“ĩ—>T[ÉŽH褯dĄHH Ĩ”Pä6}‘Ö^ ÛÃÛÔW //­Î„ĄpH Ĩ\Víö+žŧĒÖ¤ÕöߨejMzgˆZS†Â 1r0@Ŋiīš­Ū´Õuķ¨zĶž|RŊiƒ$$€26‡Ē9ugŊÃÔAUĪ [ĢæÔĄ $€"F%ļTsō%lPsōjY÷¨”𓝙ŖčĨä`@ í”Ŋ FAÍRx‘ŊKnĢnŸR}u€ŧ (­ôm0 ú<ˇ™ÚM(íŨ„aj71;á9ĩ›H ÜúWũ6Ŋ߆ĸĘ 5OuÂsœĀFŠŊšĒ—1Ø^MŲj ÕĩWS_¤žc¯ĻÜĀ6΍wŒ‚úg7ĩ_cļ¨fŋå6gBËØ¯1÷†Ä°ÍãölmUĖ‹ölÎjŠvŊCû>œ0e'H ›°ķ LģoØˇ=ëîļo{—ô&*N‰`‹ĩĄö~ ˇ˙1;7h…]vn°¸sŪDÅų 1l06ĨĩŊ›,ķx“Ŋ›´”Gė3ön˛YĘh{7閐Ö{7Aƒ;áĩŠc˙F-1.ž…ũũ$€čÕēo7˛Hõ­ĩäyįíZę”V%oe +ö|͞Z5(i]Ķe-\(Ūô=RĩaŪĨRCŲĨĄW;ßRyãî¯5,ĢõíēNû0\­ 18åŋ<ķ06)Ņ2Љ yßHŠžģ§˙Ķ —Öķ¯āˆü•ŧ[XjbÂ#ŋ™õ,mēŪLŋPKJbZž…’˜üKO…ʋũö…Ä(-ÅÂR“bž_AáŌ\€ą…q¤˜GÕŦŦį5å*ËüÁĸ ?e*×ö­Ī”\*Ŗ)WYœ]¯5tvH €z!Ę­xg”+íOe+‹ųH~ĶŨŖ•m{‡rKÅKŲĘĢ´3Ņ!1ĀíõHQvDZ]ĄŌÎ+]M–Ûôdś>¯ĐB)Žtei]*Íõ!1ĀŨUW<0ˆ‚•)í¸ō•QgyM÷TĄi?e–Ę}å+KĢŦLiމîîĄō+ "Ens´UĘre™¯¤BŒũĻÄR9ĨFeĄJTæāæŠą"ęo{eMĶTŠLÖ=˙UĨélÎŗũD•Ęhąí•š$¸š,uÖ@7m¯l¯:•Ņ›æ›n•ŖNĶm_*—ÔŠ,Oؐ‰îm”:+ Ę´ųF/ƨTڏæÛūAĨĻsl](Ŧ]ļJĨáF†˛ 1ĀŊRiD6ß*C­,ŖkæÛTĢm›¯UYŦVev}.–ķBb€{{ŦÖČæcß[ÔnjŠ›m[ĩĻm>ö}N­Ęî>okin‰îM­EØZŲ1ÕJkhŽé–Ē5}ŌÖĨĸđŖF1J]CãڐāŪÔZQ˛­•yĒVšŲItR­i[—Ę]ĩ*K¨kkin‰îM­%ŲZ™z‰ŅÜ\ĶH (ܛZ+ $†$$†“Cb€{Sk„Đ„ÄprH poj­€’N‰îM­CÃɅ 1Ā­ŠĩBbHBb8𛕴Ž@Kj­€’N.¨ŧÖhI­CÃÉá8¸7ĩV@H IH '‡Ä÷ĻÖ ‰! ‰áäāŪÔZ!1$!1œܛZ+ $†$$†“Cb€{SkD)ļVæĨZifīDøŽjMûÚēTÚBb€{Séņ¤D÷l­lŸZ•QmsM7P­i›ÛzG­Ę"pĄH poĒ=oÁæ'*mRĢ2zŲlÛĒ5ŊÍÖĨâĢVe!˙gkin‰îmZk šļVÖW­Ę.˜oûĒZmwĩuŠLV̞}ļVæāŪ:Ģ´Jk`siŅ*•ļÄ|ĶËUj:Õæ…Ō4MĨŌzŲ\š[@b€›‹Pgäe{eßĢSYŽŒĻK¤ĢĶöۗĘqu*ŗų°“›@b€›ĨĘ (ŗŊ핕Wg#cƒœļÕ9ˆoûBa-Ô9Wa¸Ĩš$¸;5V@?+QŲl5*{$¯mUÎI˛ųØO•M/O%*sH p{aģúIųĘŌkČkēMœōm˙ŽĖRQáB•ĮĘTæāöj+~UؙW*m›Ō•Åu—Ûtˇ(ĨÛŪĄĐBQ>2TVĒ4—‡ÄxYáŖŠ ß՛ŸŠhe7-¸°šî E›Î\ŦÜRųUŅʰKĘH Æú(ˇūņm§deö*WYä ËڞĻāydû*šTŪ9¯\e•ŦĖÕ!1xÍ7{=ļ}ísīÔŌrŠ—6įHí•E]ÜŌÅōĻß˙ã’;§ÍúŸŌ ĨÚâ3 ėL|ėû}[Ĩ+smH šNË:1U ŸĘ¸Ž[-7ßĶŽíĸ}ŊWë \@.ŸŸ´Ž 0Ãũĩk;đsíÚ.Úō#ZWāzr!1¤ 1Ü @.$†$†;AbȅЂÄp'H šRî‰ C Ý 1äBbHAb¸$€\H )H w‚Đ ‰!‰áNr!1¤ 1Ü @.$†$†;AbȅЂÄp'H šRî‰ C Ý 1äBbHAb¸$€\H )H w‚Đ ‰!‰áNr!1¤ 1Ü @.$†$†;AbȅЂÄp'H šRî‰ C Ý 1äBbHAb¸$€\H )H w‚Đ ‰!‰áNr!1¤ 1Ü @.$†$†;AbȅЂÄp'H šRî‰ C Ý 1äBbHAb¸$€\H )H w‚Đ ‰!‰áNr!1¤ 1Ü @.$†$†;AbȅЂÄp'H šRî‰ C Ý 1äBbHAb¸$€\H )H w‚Đ ‰!‰áNr!1¤ 1Ü @.$†$†;AbȅЂÄp'H šRî‰ C Ý 1äBbHAb¸$€\H )H w‚Đ ‰!‰áNr!1¤ 1Ü @.$†$†;AbȅЂÄp'H šŧŋ×ē‚ ŧŦ]Ûˇ{h×vŅŌē×Ŧuö×døâīŋŗØ†kž-ë‡yęYSãK­Į¯“ÛÆĻŨˇ7Éöûo>¯cĻå×˟ŋoīü#ģíīž_Đģ†5 CR͞ŗ(ĒąĮũŋ-˛˜ÅCę+VŒĢ¨×waQoīū´š¨ŪcZ=§õ(ë՟ÉžrŽ,ĩ´ČևBÕĢ'×A-|Ŧ^˜ė+ķmųíĖē”Ą@577(RŒĢXįoķ}¸ŋŽÖs   i Ŧh,7Õĸ"÷Ģ]Očpé†_VŊeΓĄ6˙*¨ņ‹l.ÆUL‰Qf‘ūŠõŒ(æœ2 Ëm—_cƒx;ÔķˇTË­cíĐ2g›ŋÅ Jãmc1ŽâoŖhd­į@Ĩ)öа˜¯Ü"ßŅŦžîöi™sÆĻ_ã!e‹šgS1ŽÂWÁ%šŲ\ëšPÂy?û]^5âėTĪų[Ž—h§–9ŋÚđ[\Ĩt1ž6ã*ļ+ēD#_Ķz~l÷ŊĸŸ ‹ÉÛ}oŋTË!ÅeģĩĖécõoąšōÅLˇēW1Zá%z\ë°Yš…?’u.û@ûÕs'oËŖė×2'Čę_ãå‹I°ēW¤ô"ĩū€ƒØĸô§ÂR#dy͎õ Č͞O:låoņ]5ŠųÚĘb\Å$ŗč­g ĀVę^h Ãiķ56Mļc=ûL[ŽbĮ†y˙Zų[Tv‡ģÎ%+‹qG_ĸņÕ´ž'Û¨°˙ÛB¯›-rŽ=ëI4mYņãÉfÄ[ųkT|˙‰ ŧ•Õ¸†—”_ĸ“´ž)ÛLVūSaĄŒÎf‹üÍŽ™ļŧÛŽ-sžļęˇXSē:ZUŒĢxG…ËZö&lō|§ü§ÂR…\imâŠ]ëédŌ˛ˇ][æŧoÕoQĨĢUÆZUŒĢ§Â=ŦõLØÆž_ß%™?ŋöž]ëųؤe/ģļĖy×Ēßb{uŠcU1Žbļ Kš€“sŠÄ¸k×zLoTŽÄp_ŗTXĸļ]Ö 9$FH =$†Ōāä 1ôJCb€“Cb€ÄĐCb( ‰N‰QC‰Ą4$89$FH =$†Ōāä 1ôJCb€“Cb€ÄĐCb( ‰N‰QC‰Ą4$89$FH =$†Ōāä 1ôJCb€“sŠÄ¸g×zp_)=$†Ōp_)pr?Ē𹰐ų{×Ū´k=Ļ+mģßģÖü­ßĨtP§÷žw­ĀŊkÁÉ}ĨÂĮÂ2Y]ĖšÍŽ™ļŧËŽ-sĖ?^JJí$UŠųĀĒb\EGžņÖ3`›6Ę*,n~%ųĩ=ëÉ2myĩ=[ædXųkŧŖJ5ŦŦÆ5Wá|Ķĩž)E*˙ą°Ė9ķ5vHˇc=yvŧeĮ†y{­ü-îTŖ˜+Vã*|_ĸ õ´ž'Ųw„É2ŠŧeĮzōVšoĮ–9­ü-öRŖ˜5Vã*Ļ*žD¯k=KļjœŠøįÂ"ĘČ(r´ũ깟ˇå™ök™bõ¯ņēōŤY]Œ‹øŠ]¤õ,ØLãķkålb0vŲnõä?×7Øn-“-§ŗ~ĸ|1ËŦ.ÆU(Ŋ‘qVëP€]׉ųWc+uN*hū–;$ÚŠeÎß6üPē˜ 6ã*)ēDÃëk=? ¨ĨčįÂ"7äŲĮ>õH| eŸĻ‰.ŲôkôSļ˜'6ã*Ž*¸DĶ?ŅznņV ‚Ÿ ‹xË/ōĶd;ÔsHĒå~vÚʐlÜ{•,Æ˙9ĢqĘ]ôõ‘Öķ ”íŠ}.,‘ŊŌ’_ģ¤v=Š‹¤[~ûŦÚ-sRØü[œ§X¨æląšWą8W™Eę)į 'Ņ`— ¸-ú×˙XXä'gÔŦ'ŦˆëqÕm™žŲĻ_ŸŪĻĮJ“đûŠTã"~‹°}‘k§õ\(ŦÍüŋˇÛËßŋ΍mU‘=×īPĨ &V5Ķr˙5ę´ĖŲ1õ5̆”r“ŗí×øĪėFŠã*ęĖúĶ–_īęŪZĪp†đ94¯ŦTŸįŧŧO5^ūáãuv]Á!^)^đŊZÜŋ>į|öÉiÛãėŲ%ō+•aŲŲŗ+ĨûLđķũ•ąˇ.x/bôũ>įzŗâŧ.XÚn˙s>˙ģūđņÃØy¯ uķõŗ¯ßųĶl´xŋĪéĮˆ|ĪΡ´¨ŖŪįģé_÷;įĨį}´ļÜ)ĖöķũNxņâŸŊæŸėįģÕŌ"Ā™üCŧGÍ$zÍåû,^fq¯ŧ 0/Ą}ūˇz_įÖËlQ˛œÆíˇŦ\ķ<%Ũį[ĸÆÚsķQÄčņDSXéĸ‡‘4›(ŌØBäÁ7™ļy‡ēAôŊėI.K~3tŋ ßužhĨEqaR Af šSØF$†g1ĸķƒGtĶŌ"‰ĖáÖ ĄéD~ũ„ĩĖ!ūÕPūÕéüũ#ˆō'Ævĸ?šMfN˙RNëGŦXŠįŒŊ¤ûŦ'ēÁXŲYĶf1z4Ņ$VƊĘIfė "âļtfM›õzŪĄŽm’;ÅÁ\ÉĄaÜ˙ĪrŸĪøĒ›ŲqōI"Ž=:üÁƒ{)D ÷< h(w ŋč˙0^&Š0?øFĸĢ– NäŅj62‹ŌÚėĮ%F–¸­đ+åäL nmV_|UĢNĩg„žDŋäĻ\ŨJĻoÔËŗ˙Ģ@b”­ûĻąãÍ:/HT\ŧ^iũˊuß0éQŗ‚é`Õę–1ŧŽZ—ąuBbÕĒcč_ĨN­’â+9‰QŖ–Ä›bbÔŽ&v‰‰aPŊNÕ˙'ŧŖDŨĒÆKkŦSũyĶ)úoĀX˙\ĸÉĻīWŦS,OËÕë=Ĩ{UŦn5ãÛõ*įM Á ĸߌ]Uj?jÖ~ÚđVÍ:ĪäA"1jÖ1”ųl­:U ƒVĢ‹Äpu¯‹kĮhĘųŦ`O.1Îäævâ^ŨŖ3‘Bb\ņģ¯h˙:iÔĸŗÜ¸—/welŪøÄ˜{\R´ŊÃmąœ[Įú]>'l›||6<)ōühaj—.lbģÓÂŧjl!_btņz’uiĸØŅđDTâÍ˙ķŋh<Ōąėâ6÷Qōã=BŦT8™vŽ/˙rÆ%˙ŋ%DũĘV]ŧ¸šcÚ՘ÄĐ…ąŪ<“tg´‡õũũüų÷&ŨŒO|ė#l!}~):1ūĄ¯pDÆ$1Ž÷mĮŋuūœ_E}ûū6?k:ß1îŌųŋšÅšE"$ÆãæWcü?å{‰‰Á5Uëø* 61&ä_~CKŒ ›CŖũģđC5ņ‹Mztǃ0åa7ĸcī5Ž€Y(ŅlūįŪ´ôŸš›.\ü†ûQûpdâŖ-ėĘÅ5Œõē|ųķö—bī-FØü()ææqäĄ!IŅĮYBūÄ8C´]xŅÁ˙ŌđIqqžĪ°!7âîîÃsāĩøÄģ{Mˇ‹ $FŸĢq‰÷ˆŠŋî~Bbô]aqŗĒ‡cƒGŽAb¸ļŽCÖs˙ĮPJë‚ũ¸ÄXB{hlŦÜʔ˙&Ī­—íwŠgkš˙s¸‡Y/ņ-/6]Üí=Gׇ_ŗrŖ^ $î3Ũ5ļ71Ļém4Ļ@´Û0×äáŨpnÕZ‰[!ō“›Įõøˆ?ŗ†$:ĘuÔMi÷ēæca”ĮBb´r“Ø?•ÛDęĨ4‘oĀ$1v‰‡pÚŨ2´ĪÍÂ}aā-\Įjĸ;Üqh.1RĶ…r3}bp­¸pÕM߇ ‰+ ”Â5üaŽØc×cĨn ÛÆí"n{ uĄqsƛhc Âõ åcc‰ūČ&ˇ‘ĀW\öÂņ÷EÂäâ2 MŒOšåÎ ãˈ 'žfümĨ˜lū"Ö-Ė:Ÿ3tˍ?tž[W3dĻÎũ2Fb¸ŧ‰Ü–ÄÛ\bŒ;Hį1mãõ‰QžûÁ­˛V äžQŌļī›Ôͤđ×øĩ}\­ÚīųoĮĻ ‚›sĢ”qŦG,ŅDqÔøîė+î‡1œō$Fe.f°÷#‰fq]ܔŧJ=uüÃģ…8)w[X1rĢßUŦsûƒŽīÍXq%—ĶúķëÃ=tĄlBžÄX-t4áGÃMΠÔ6Â:‰ßS.&ˇ.[˄ÕS¸8ęîĮ-ņ‡(ObŋģwžīwäVęÜÖL؜|‰Á_n‘Íõ Ņeîõ7Dß ‰ņ˙Ų;đ*ŠŽĪ÷ž¯Ŋ€€HQA¤WéŊ(Ŋ HQšQ¤ƒ‚€(M zīŊ—PCOBzo7Ŋ÷œoÎĖîŊ{“{I !ņüž'ŲŨ™ŨŲŊ÷ÎfΙ™DáÉ*# 2ą“m ęKŨČčČŸ,ōĐpe¯ŌŒ•ęũųžÁŒíÁ]V¸7•Ôß_4cDķãhvňĮŠw4@#Å&[/?Íhū†PŒL´pޞAč€|`Ģ+ī8øöoŪū7˛Õ&|„b4Ī€¸/™đ\“Š‘Č?ŒŠ15øã%ņ ÄKY#_ÍČ'+ēín˜&?N‘+|üËęl×xXqÅX)Û]Ē1öŲôŽâ•D•f â ŊŊxe¤QÜų//L­LāŠ1ŗL,¯|GÃÔ*ĻŖ•ŒŲu6‹ĘæĻ(–Ĩh•-Đ[īlĘKMƒaÂH1xė…yÍ`ã appĘĸųæ €~ŧĻīĩnËĻĶĸ)°^–ŪŠbü¤8.5âš(= '5Š‘ĐSķœÍ§K–]gZÅX0ŊJo"Ę~&6Ā=ģbˆ[^~ĩÅhίŦÛŪUĻwō†eķļĀ~֍ˇņÆåšĐÄííŸõũ˙rÁ[”Õŗ™ĸķÜđÄU1ĸeŽŌëĩM†”5[ļlĢŽ%új~ŧzō“#…Đ™ËÖĻ-ŧPAyæÎđ}^MĐ'Čĸx_…?ž´—ôZíČO|ĘÆpŅÂđR ‚(îŧ`n Ãllz܃í/ņ:e­X­ã‘V1:X+eĘˇÆŠQŸ#^Âü bŖĨŲ`‡ūYÕ ŧ lƒĸ7˛(ÆÜō‚nČ}Qæ#CˊÁķ¤\1ĻŌĘАÍÅČ-IŲ} ō2čŦUŒNŠüQĻh†4ˆâ€ÆÎūB1¸n°RFŠq`‘b0ixIۃ^eŠwíßhËšĢĪ?ųķekâxģŦAų™AŌđ#cŠĸįŗ(´ŽŋZrC|=¨(Œæ#ˇÉúdŗxũū?úÛŅցüŸPŒ(}.|ƒī(ûÕŲŠh)Aw|DņcŠŲXŊũœgã¸ŦPE1ĸ_gŠOŽĸíxąķøD‡U1°äŠQAU WŅüPÃFë›U1>Ā­ Ė&Ųŗ‚îēŲã!ĀĀũü—ŽrŽYgUŒň(?JėlF1@4z$ŧ~reüma&0öŽuvE–Ŧ†âHĮŽBĨ–J}#U1Ä-÷‰ÂTĢlčÍ8,W#jęãO{Ū—1˙׹qĶõŠ(;̎jąÉJ<ĐN(Æx%_I1ĸ˜ÃËĘ­fNĄƒÕã…l3(FēĄz̊ҌĄŊ'ā…Ī@Ąč;#í!čĒY.Ō?ɅbÔį›7}j`ĩU>ÎYã*ßŧĘEŽÎDE=ƊņMštmY›$HPÎčào] ­":û}JO#4:)ÆBHiŖƒ3šÂOt`ĸWę†P ,M[̊!nÉË䟞(cU†lį…÷&cÅØ {¨ô|0`]¸Fģģs Ã#S ŽŠĸCõģ—U1Z7M}o‚}ōC¨úÄ^)ĄŧÁ×C=iƒÖ÷,dņŽŊ.]Á×dūm’åŨĪ’bDņf/ ƌ›0áįēŒũ2áŖsŗŅ›‡™ĐÖ ЈąæqŠb$VūŒuHøŠą›Ō|*ã”ôŒ.k fàdųŖčķ)ɯ×JTį#š6cũų9ö6o”elŒÍęáY­ÜhÉ?‰`hĨÁ!p ÅØ#zÆĻCü ´;2ĄVh6RŒēąĀ‹Ā^š äĒ(øšbx3ŅŤøJá°^ņŽc¤ßũsÍÛDY­UŒ˛‡iņå•ŨYĪĨg–ķũyZ?%žíĮhĖįY`Šbŧž Éß0V˛*F;æ ™\ĢÚožōÚqÂđԐŖbŦ—æ›ÃĮ—|‰ƒâņĶ:xbI=}‚,Šą ā4ė;š´‹ĀžCœ ōöJtgHŠ1ā§IŲ&!ĸ8đnĸÚ3=“ŊÎ˙ŋĨ=ÉcŦ˜ø &ŠŧūĐŲŲÍ á?ã ]ŸŅaüPwáFt{pE1>įĮÖ˜ŠXy5Ŗ1žŪˆī9V†ˇ[ΎĖaLļ †ôãŠŽčŖŖw­ËˆQžÂXpĀnؘ`€ŲƒkTÜė‚ŪghC×ũÜĮ4ŠŅ2~\?¤Î|KvÍgķ}“keŦö…ā{Z×@Já^ ›ú.Ŋb„˙4ßå3R K€Ø)ƒFķōõ{cÅĀĀé“y!;‡¯¤˙öí÷ŧå6_/4mÄyyq)C[ēâ]Ë? ÷ÃNîO1$ØÉ?WąSRδ8D~B؂.-å7Åw•šs6 œ„æ~žE‚ Š'âĸ$‰?ą×SĸRŒ&rš’˜Ä+ÆL—„#ÜÄüå#NGE]Ô2)ac폈ŊU›UßgũC‰ 8į˛ŧūėë?šMLŒîŸŦxëŗM΋›•ŒsW]ˆK0ĖÆwDŊ}T,úäTZęëˇK™áęM17Ÿ“ˇ%\1Î4=á.§Å`soD†ßÆaėŸ„ØkâĐžøøƒ¸í¸Ũ?æŅâ×dŧ_ėâŧ–˛8~‹ĻÉQ8nõ<}_Vč˙ņˆ}¸]ÃN5ΘÄqė=ūęCąöށŠõį8F_üZ„J,´ Ø^vW<ũKbâĐą—ã|-ŀŠģą üÚÉQÉøä#ųÄŸW×cPū~KŒE}+ĩėnLčeYĻØëŖŗ2ž ŊŨ†;11Ž;†ŠĀé¸xQqˇt‹ąyP +™ƒÃY\T2Jņ÷§CbVĘą•vÄYuũ)1i˜ŅՋ°îIŅĘT´Mļ<Žõ;ŪEė7Üô(Ö˙d7M‚ĩ qŌXôNJŒđŌǎÁ96ⴘąÅÉđ¨sũßOŒîĮ“íâ<—0ŽÅ&Ŧ`AĪeZÁ]õĮŽj­ŨĪ—ņŠĪ• H_ŠÂCy)Vrn‚ Bāzģcß%jį‘}Ίq:‹;“Áûа÷°ÎHņĘČ‚ Ų 8VŽ=WÅĀ.ûãC…ŽË՗B†‚  ãī„dÜũIsčŒŅœĖcHģ™ĨŠÕ&–yžŒ°ÅYbīOÍ9&AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%*ŋõ6§DЎ "˙x§$ūëãÂū~‘ŸĖŽtyĖyäđ˜ ōĮ‡øß%˛°ŋßAä'“n°ˇ8%Ū}‹ ōŌīđo2×Âū~‘ŸŒŋVØ9 Š1 ;Aä'ŽvˆbŒsag€ ˆü„ƒ(@H1ĸXAŠA ¤QŦ Å R ‚(VŒ?]Ø9 Š1w ;Aä'ÔÆ jcDą‚ƒ(@H1ĸX‘U1j´k^8!Š#¤QŦ0VŒÎ÷â!ņ­ÂĘ Qė Å ˆb…‘b”‰wœÔļžÜƒ˙Ŋž?÷ø_‰’š‰Vōíė÷ûO.ī`Ø5q‘œŠĄlß(‘íT ÍĄ’åļÔÛ/?ÅMžĀYŽĩhAŠAÅ #Å]ÕŨū°„ũķåŽrŽÕ Šļ&\ÆßŊ‡sLäÆĘÅÜP;CÜĻį5ƒā7ąũ >ËŠ#ļ†[A;Ü´Ī•æ–}đ>{\>?/ų‚@ŠAÅ #Åč}ÕŨąp€-ƒĨyģØT0y¸F3˜›‹Ô­WĨkm(UÂRØ'p3Įdq‘ŦQǎÜ÷ĘXßšīqؓ‹ģią€ar§ŪĄŦOđÁWŪúƒČēbÛn|Å<Ūâ‰\‚ē,>ēV~^ōƒ Šæcl`3áĪŧ]liÅ` ~ĪMō™_iB†°×á|ŽŠBũYĨDOą;Ēãf4ÍÍíLa™ũ nxgļ.žÂĶŪÁ§Ą2흒Ÿ—|A Å ˆb…‘bô3(Fkø‹ũŖÅū°ĩW`_-[ŦŦSeŅōfú4īÍ_ÕJėT[ĩâ2,ļXŊFÛĩ¤ĶØŦ5]”@û%–3 Ĩc›e–SęĪģŗĶkÔ]ēôõŒ=OxLqA7ĶOāėĘÛ—ÄîöLąéŊø˙n–+,ŗ!üOū8kV¯îÉ*Îąl-•ŸŊĻŖ’žĶ: ‹Õj“a°në ō6`íLvUQŒŸ--,*=Gëã˕^ŧr…å׌}ešbõOėĶ•+-ū¯a–åßõ{[š@Í?×üŨDŲoôĪ|öíŽ-ŗ?ÂŽS6ϝhCŠAÅ #Åø^ĩ‚f0› …oÅūn]Ėš :HÁÕg”x !:.ã^ˇĐĀđ Ō Ī~°ÎŒ …mb)DÆÂfyâCgLYŗŸ•aēčŠ$ōŧĨUŒu`ĻãčČXŧŒVŦy#ŖSč# ,ÖAô ļ3 ",›Ž € :D3fnFlܕéį†Âdũ]nC@Jħ"ĐÜâc‚=äŠ{!áđÜ__Ēŗ@oƒÍäķŦ ˙/”e?DFt’ņÎBX@ĸŌĀÚ ÁI™ā÷ųūqHd8žŠ§P1ŦũL?^҆ƒ ŠÅč7ge´G5T†ŗîĐV uLĘÔõfKŽāū_°¸äkßÁAqb Ŧ)ÅúĨ‹žŖ×ØßĀpėŦümNņ?‰ XųĨ0Cœ8 kũæ+•H1˜ ņ#Ūz×]ĩ4{œd,ĘBŊÄbČ0ũˇnķ6†"@ĩnĻ&$ÆīxG†<1Ŋåĩßb^™)‹KöpĢÄã`C9Ö/ų–ŒVōĩ˙Âåbk!Ŧ̜š÷ËFFļfå‚›rŽëĢÚ;ÖĮW`“2ɀ f¸jŦP×Ņŋž÷ęÛK‚›ņŖKą>I"Ū$˜ÅŪŋ ŸõÃļÍßN÷Ž{Eŧe1Œ"Å âEĮ W–N ŽČJ zI ´IŽWûšŪ‹Ū›_Ą&˙˙F hküÅš?ÍÚ1°p ˛_IôHųX‹ũū•ų˙Ņ˛ėž sø˙ķÁJĸNMëSYáfŒ­Z3Ö]É]gëĨÛŧN(}Gķ#øÎ)+%žĖW“ˆe¤ž}?üĒėŦ"ųČ÷ˇB5ÜyęoÕàl|ōßĘŅ?b?á"”"öJĘ˙ŋâ%úīÆČ^=éē{—ŖkL=‡}kÔÅԙĸ)A+ ŠQ˛Zˇ+ _šŽÕ6c§ē;ú‹­¨–w†ą2°Xl™UŒ…üßhĨ„Ž4×6TˇÅ~ī˜Ģ_ËXŗ ˙!ä)-÷ƒpcį"ƒ%“~dĨRNúD¨ŅšeH¯áôíúü˙?Šá&uėšÁōŨS¯1S”ā|€—ģŖ¸[%ęė6Œû‡tšŧšĸbt€}"°ÖßKā{öŠžŗč@ŠAÅ #;F/E ˛ŅVßŅĪÛŌd,Ú# Ÿ Č^!튁ãÅč“x|áØ ĩīiøęķē4›R¸;; ‚Í\$gÜ/ŠM 'Ã'íY}_–Ÿē× FŠ­î‚>&㍀âĐmŠą2…R_ģÅLJĢoŽũß=ĶÃē^ņ×ämüņZuã#wń ĸn°â )A+Ėy×ŖQŒ0 7̈́¸aęr ÔUėB1~Ÿ=Ŋ0ū*Šå„˙]íĉeĸ#ŋ}¸č隝ņŒŠqRl‚bačķéqũķų[Ģ{U“-eŽôŨT&Û>âP¨‡ūúÅø›Í‹TžĻŔ›+wûßú˙/{) –Šî{"@h2üōÔĪV4!Å ˆbEîŖLŌī? Â˙ļq"`‹“r\H–~ˇB1ü]ŗ§‡Ųü߲uņŋ‡Ŧņ_ö¸š%ēxf¤7d;†1•ėÖæø0‡āÜėËøH9āĩÚwšzŌīŠ>ŪåŒpôƒ*āgeGŽĮpB&6Đëk.žĮ (ĮË9+Ÿ<ōöš;V2ņ¨øŋEēpáÚ(.pė^í?gænr‘ ö“rŽT4 Å ˆb…‘b €>Ļâ42Öônj+hš4i¸= Oã#§|kŖ¸uá\Īũ­¸ûĒlīËē,‡ßQ2ŽÂ–A?ŨŸ8ãÆH^6`ėE1‹Hŋ™S۔íoŲûC™8 đ^ŽO ›Mv†ŋÔđo!§™úxH÷‡^ßĩ—‡?‰Œ˜3ĐZi7Õ:xŦī?ü]Æ^îzžiWĸ—ˇkt벆õŊ÷øö¨Ļ\NUŒo%ŽĮz„¯WŦÕ7øÉŪ9Ž/p|ؐm™§•ÛģĖėŋ ŽãūĖčOĘVŦ”ã ņÜ$įXER ‚(V)F`*Ž5Ô*õ<ü@){YųķÎŊ՘S"!|fļôn˜zd:˙—ŽAËh€‡–~b|Åš+;“œP0ú`Ŧ€…øßÔ,=āa.gK8Āmũ ö^äUšŗL>‚jg)s āņPšo¯œš*效Ÿ†˙áŠÕÉ0ŅÔÁ#Ēb´Áw;Ž7”“ƒ–ųķ§X§;Ø„,ģ{ä]lĒæü– bŠ<¤QŦ0žíÜ.í]SXY)ĘDæjz’ÜŌLŒNĖ 3E{äcÛđR9Gū&ąÅĶŪįƒƒ ŠYVTúÍÆãA.Š´ĸÂĐÄOķķrßföxÚ¤!Ē•Ũ0ŦŪ,ŗáÄĶŪæEƒƒ Šĩjë^ČD ŠkÎq ˆ“ëØĩ+9GË]ÎV`ėØĶģqŨŧ/ˇŖÁÄŧRÆtĪëÜģ/.¤QŦ((Řqßšc]xsx'ëN&5Ë9ZîØGgNęN)ÖČ7nÛŋ˙e ›rŽ[ŒĘ Å ˆbEA)Æ ĀĪÁ>ƒķīj˙<ō>ÚæŌŋģÔ3":äz÷|ËP‘€ƒ ŠÅX1؋fŠđQÎqФQŦ˜PÜįŠ “G…‚ ō“‘ ;D1æbag€ ˆü„ÚDBm ‚(Vg;Qčƒ ŠEI1F>ōqaįČ¤QŦ(:ŠŅĀÍšqaį‚ȤQŦ0Ģĩ`o.Ē+v˙ˇzë–-?™‰—GŪ߲eļҁŠû=7+–ŪŅīNÂØÂöOˆû ||(7ķÆų…ąŸrĪםŋˇ-[ļM·lQH1ĸXaN1:Ā#6ö‹ũĒüŧ“’Ÿé63ę(;mbßԜø şĩ[Si˛ŊOŨ{BY x ĮåĢvė 9Gb‰Õ?ŠŽÍ)Öiđōõõ 8ŸŲ*ĸ˜SŒ9—5yæ'û{ļÍÅîŅûöëķ%Ûįå˅´ÔÉ~¨:˙›åčđĖŗŧMp<ÕŅÁäęđF vēwéiî4Îéū3NbļčC.æ1Đ훪Ã.3§Ŧî;}ĪØ#{į2據īČ+ksĖ_„(`Ė)Fŗ 6 ‹íIyĻÛĀRũîļØ7ĩgb}š:åϐŽ2øG†˛6`¯†‚ŦŸ)kYøÔ:7#ģ'Ôø$2*§X§"rŠQÜ1ŖÁܤöė7<#ÖP|#ī]˗lÜؙsŦ<1äņŅŦ‡~ ^Ā˙o0ąĻX^xŅęĮŧrL3 ņiîõ€īͤ3pāfĸī0į@ ¸ō˙Đß7ŸÜ`c[@ŋžņœ1§õ3°^°JŪũŒŠą@ŋģ͸jëÁjϜÍÅ%4ŠĄsd¯Âm5ä[N˙ŽAĨ™ŋ.§X¤Ļc=@Ēg&Ā@įfŖbˆVŲÜŗÎ—lØlĪ— éŲ Ų&ļXČ7-Æũhf%Ë\R9 ŒŖŲ¸û›‹Ģ§ÖøąÃžæfæFŽžČŪŧÉzĢq?š\ļ0“—Hc&ŧb>ųc€e8˛ĄÂH<_ĖÚ1R°â Ņ´1ŪYę´U]ãz˜mԉYž1kdMßz‡GÄŖĩ68ÛøņČˆÕ…EĘĄÛĸÚî uĸƋōdŸ'mUCŊŧV˜ËiÄ!ũŽÛŽArÔy1ĄéÉĄaáž5yāŸč°°ņīŦņZ)ã͸~ũ{5Ņ\‡đKĻæ™*ķ§}DđúûbŽŨĪŊÃÔ5ŋ?~Ŧ ãŨ—ŗŒ¸á0GMt)¨ķˇQC¯ž´¯ÆL@ŠaR1^Š˜ÆNg/sŽ)€{ģ -]QŒęMšhŋ` fO×L¸ŅUnV[{đ+%¤*Ƈ›Ôü?SYjؤÖĢŲ6ÂOū?ƗT8pXėŧÛ¨IíˇÅžĀŖ8o7kôM°fŗŦK0ÖoZQ*ßTš ˙ΰō{ķĢ †õš}`ØoRī]S‚|ÖŦJÖC5›~Ļ 5•Ŋ^ĩ›1ļPUŒzM+kc|ĸ TâoMŦŌėsũ41Šuŋ¤î6l\Úpüfå4ąJ4kd4ĢŖ*†áļMŧ­ė–nÔ¤N ąGŠQؘUŒôUŦŠĻe`PŒ§Ú_OÂ-Ëxhß6KōāŋkũÉôT\hĘ °Yˇi‹ėJŪɁ[ŦÅ:­HÔEV!b‹šĖÁ¸zĨĄŖĻcËíW ŲyÚoۚ8/ËM›Wá/ëģ­Ģá’_ü~OøĪŲÂõuް\Ä{×)ķÂúĮY~ŌČË÷SŦÖīŒ–ĢŪU\ŊimúIyĸ9X­ß´É¤Å}<\wÔÆĐEwŪļŅ~°ÄTžOEΞ8ŨÍĖũ0Šol9ÆØīWMœäŠq%C,?å—tB1>˛OH ÷œˆ§Ũœ×|jįĄ])k›ŗ ģ›ņ+wP—íņëūØed¤h'ŅŽ‘ŠQæhhBb¤/Žé{×é^}ž)ķĐéü~q‰Q~‡5—ts^Qīn|\ %›˜厖˛ĻŽŽâin:=îû…K@¸#¯´l ŠOŒ XΨȇŧiāį<—Íqy(ĖŸņOŒ÷Ũ‰UĻĘn|az¸Ŧæœcƒ¯‹ĩŗ:Û3ËЄh'Ū:Ųûˆ+Æ}×ęėŧķå÷'†õûÕåá|"'ũVG‘”×ŅcŦ5ՔĄŽŠ|¯účjTĸîėšą##îĮŨĩÎnėFb$/uĢ]ˆI ˜7_*Fģ1‰Ąļbõ—<ÆíÄČŋÕÄe‹ˇö'îßq>υ'FØũin›ā5a‡‘bÜsŪWúb\ŧ¯üÕMrK°kÔX;ŸũÜ=1´ûœG÷b¸æQ|=;ä#üā—{§ŽŋP ˇn¨… MŒķ—ŸÉÆĀøÄčQHčã.;ņP~¸ˇ‹ËdEŗŠá=™Us14¯ Šq9ÅÂÃ÷?‰EÛxŠkö ē/šã˙ŋ@™n\öL ļˆb×GĩĶY­eėžū÷˙KœI;Üīi.†āūSõųY ųiM~ÉpĢ<Ģ%Vđ>•ŽÍß­ |ŸŦãąā>ž]Ú-ĒÂhõ@Øqš­˙5˙˙]ēƒX)ü¤¤Dņ˛“Rõį×ÛD.ÁkĪÎ$cnļâ īČNļĀ‘GkÃīÆs܀7‘Î'ˆ\ß2Üâ'\!˜ß‹i'H?‰ii­ÔåUî¨ģxO ūŨ˙^FĪ„ŠxėG†&Ę€H‘ī[ōõh_ü&™Tŧ˛ķĮøîhV-€ß-B…bôUbŒå1ŽåģÃÔÔNĘ[Ã%ÁŌĀķ$ōW…įvü S“Ag¤EpĨûĨ|?"!ÔŸf §<Ø2VžžĀēō3į/?ē™ÂŽÁK“Ũų!ūæąx:ŽŧdTMc%ˆ~††\¨ŗˇ‰‚"ˇã1ôŠŅ(]xĮ6,Oö°ž‚ėëæÁâ&•X™ Š_”ρ°p W×;Ų’<)g2}ĖôˇÖRãÔî‹R ĒIī ķ}C˜%ģöręG_īTã-6ëCõ@„ĸ =6F€“ ¸KŗIĀm–6<ÂVøČׅ‹fŖÎi€sĻNđŌcâ ā5˙YÖ"cāGxQ–čŧāˆĀĻfÔ×'šÎ:Yüļ…cėUĀ/%×đú‚íz[QŒšˆā×KĶąŧ%ÃЙ F˛é!ĩY/^VšįŠ]jąÛ|cÅJ‡ˆ’Ŧƒĸŧœ[zą3ƒEcēĨc t!€?kŊ(Œ?Ņâžh¤Á"ŅMt[ŒP'u¯ÃVņBRßÃU=Ōúĸ­æ1ZÆŨĩxĄŊ’Mā  W”gŧĘ 7,æb˛‡ėũp€oų×ŧ˙Ëžâ':˛Ēü~–ú O“ųhÁ|q.žI=Y?žīQ %[,-ÅŦxAû%^›+ƛ\“‡˛AiTRŧhxdऄ’zŧč<Ü!e—éqŊŲ ƒWƸ>ß­„/]Ģé\EÆŗīRÚŗ–™ŪŽ-āOÁO„ĄQjõlŲ<ā5N€ƒ 3aÃëüĶ\Įj`ÛJ*Æ{â“9ÉĘ'ĄļuMEŋČ9ü“|ĮH1ZĨđ×ÎØ? qĐ! œ<+Æ8ƒ'„Ŗ‹øũzöKŖ ÆãT]%¤õ•Šy™˙wŗËcFëh~Æ)F†žĢč;đķ÷÷ķĮo&ûM|áPļäũīCf Ŋal…^18Ķ@ąU”€(_xËlČ9N1ÅŦbübÎۅ+ÆÔoąl> 6cDŖ.ö#Me/S*ģŧÚiđ8劊ÛHQÍn*J(Ž™ØĒäÅîxĩņÅėnjņæޤ‹Ĩã˙yC:cKdŠ=eLM}ī;ŪĻĢ(-Sßõō]FŠņŖô•Ē6Š•ˆũļ°cĖeLQ Ū$ÄØ>X[GÅ@‡a¤÷SīĀK@ôž đPŒ3L´N2VVš‘ŋŌY/ƒˇHv3qÛĄŦŖŒ0dĘW†ßA1°§ÆāũŠh5"é&#~)žš b Ā/ũŅ ģ(¯¤§æ^Ãkɕˆ ÅĀŽjū:įąŠ_Gŧõ,ЁFÉučŗ°M6丸ž"Cü4ĨbpĨôSžq0ZßąĄg†A1ęÆC ?4i|=ūˆŸLč -Ļ’ąÃ2y]ŒË:uJ=GōŦŖ ˇØŠ_¯I-īiŖ1kŠÖÛÂU­ļeķŽÍŗbh-߯)†aģ~phŨÎZ\ÄhæĻŽĐîˇS íĄQŒÉ ŽúúܡÄôëÆä!Į•˙’ bxįÅû°xa^1EAęw&NpŘY*†—‹ąđk•Õō=úh(/–™(OpUĮÉZÍ벚Ō'âwėßŋ—KÂ{¨Ixîˆ(íUËwŠa{|dqģ‹Öfĸķd ?âˇÍxP?ôcŋʋėŠÃJ˛œ[y)†­loé+%ã&ČzÂ6ŒWMI"äKÂeČfĪūŨŧbŊFęü?ŗ™TÕ Ģq+ã(/#÷ėßķŗōi,¯ęĀ´Íëî#| årECJT Qž÷Kņl^čņęČ/~°BÅã˛ņGōî°Ŋžō"ÉXĀ3v øĪlˆ*>ž}cÅŒõ“\*.ėŨŋ›§ũY(ÆbŒ ÃFiėÂ>ÄëR ߏ4(Æ8EØTúoÆH}cŘ,:{āˆ‚&ΊņYŠplúEŦW]5ôA"˜ęŽž&6įÜd{ˆęxŠ1[OĢQææeĀbŨJŅĐģ]1Åŋ5v_|ĸ´\ģÕ͒˜} ›Ē¨ËËãWQ^U t'oqčŖŪyČņåg+jÅ˙Nžđcî˜&[YāŠ1‹ŲÃŽՊŸĀ’0Ųĩ/;ę=uzxę“(%Î4PIŦĸW KŅ9$c†ržī“ÁŋËЊpZLŲ­ņęááĘØLŲg¤ŅZÅõHšäGYÃQé2úā’^1‚d+DāĢĪíĄøũüĀÛH1Ä÷G(†‹>úiዜՌ2(úIņrÕB=Ķ^š …´Ū¨ÂRؗˇÁpģÃ^qkĄFuŗYjN™P TųKØŋ÷ģlv°cY›QÕ°Ų—ĸŋėßB1~ÁR1¸˜Īũ?Ņ“ú–üüXˆA1~Ņ*Æ0'å2õŒãŗX^Ŗã´Ö ‚(hrĢÛõcž÷ōú̐*†i7ŒūÚLü˰7ŽJ'QĘ ´ÚąØÜdMVđ‡Ė*† ö°&“Là áåĸMüšŽ-ĸ™^N3ÂDa-XžĻÚbÂÔßË*ajc“ÛÄ­d%ÕË3kz¤N,˜rÁī'ĮĖč x)Ž[˙L*Æ˙5ŒßdĘ~r6㚠įĸ)‹°íÉ×įĪÖ+Ēz‹.…ër0āt.ĮĪ"—?Õ*Æ~E1dØŦ¨/=™ĉ Œē‰†uЌ¯‘Š14‹bøāŠ8U1N`-/˙ļÔ3Ą÷eĢã´^1ü5Ĩ›¯×ŸÂĖZ­Ņ+ƟBĢf‰-ƒ—”dtlDˇ;‰Nn§Ī°ąb¨2ŌÖ )]Q1„/`Ĩ×l)*Æ=ū6åÅw Å8ĸų<ņˇv{Å#Å8‹áO&j}‡˛*ūĒaß_*Ā­ĶxŲķķ„bˇuŠŽÅ8ÁlE[Ax8¨Š1YŖhķˇß^5ģb0kČāČŒ¯™°0ȍb|pņÖĩ°žiģL΃ŗ-xČSöžģ÷íßö}ö4×3!âē=D5•ÁŋāĄŊ°\6ē6[ÆŪHŠŋ9ÉÄ­îƚ)ĸ÷š91‚¯e„5ā{ŗm­!čšÍåÄ&¸ŗngŠ2›ČN°]ģ;#ûj à ėŽÅČīlķ[7¯Ač5T‡_y›úæÍ›×]3E‡Å¨Ė€MÂLL !žĶ›:žRnŪ‡ĐĘJđ-0ë;\\1Ї… šEBŠ ĮcŽ Sؗw°ã^ŠoÄØTY†đWøí øME1:%CˆūW QĮ9.ŠqĄ>ĸ¨WŒl5Äv‹Ņ/ÛoW’v4€iÅĀQÕ/Į̊!{ģ–čc{Kį/Š›”Qæû°„3Ĩ‡A:cÅXd^1vdųŠũ¯ķŠp4ô̘WŒO"äåx<ēŦ^1ęĮĘ_ÚT K­ =‹bø ƒų×ŲŖw:a„[Yc͆ŪC%čĄ?aŦÛ¤šåæotÃZڇQÅčž*>íoz–ĻžÕōŅęfQŒá-ƒ´ūgDÁ“Å(uáö­ëWoŨ˛SzWú[]UFSlÛÛ6ˇīGûf×˙Ëė/wØ\øå5<čŌEŅ'õÅí×l× ļŊvŨ֔I –Õ(sšxėar”§íĄÛ ?Ã_īÜēzũ–­ŪuĸîzÛĶ#Õ@Ãõö'žÍž¸ūŽ ŗnØŽcÂŪŧ}ëŋvAwŊÍ9!^•üÍæęôŲ/€,5Ŗf_Ž´ŗaŽō!Å`ĸ4OiˆUū¨—›;sŽŅÉŲ‹;aZJÅø@VĮåd mÄwDÁĄ (o´¤öâ >Ũŗ_žRčŅÍË÷îŠbđ#­DĪ°š6O‚K⛱åvŊ_MīĄiÅĀ| ËŠqĐ4%ö'r1{Mšąę$ŖģTŦˇb!ŖĢIŘ,}†v_ÛĐ%‹bđØąÚØB1FJÃÍĻ››z˛áëo YpŠÖØŧb Õ_ĪjáĒĒ*š:Ј…ŠŅWô†ąU6›dS ~E^ëŗ1´Šņf´xŪRU1ėdŒ]l­”´3Ö5ČĒŊĨü–Œ‡ôŽhSÁ>oœFī+ é_ã× c NÂSūĀۘŗ(FÉ`¸(>‡–SūZēÄ KŒĢšķ^tžiļķ–qōĶú¯ãs˜l¯w¤&|Uđ÷)H|îvž3Ļí )Â6ü:/ ´3SĸbŒÅų …W*Æëq\$žG'Nœ ;cļŽÚË UÃėdĒbü :}ôcašEÅȜ÷ƒô°ŠĀ÷ŋˇČPLžÂŖÕw¸…Ėģ(YöwIL(ƇŧĖģũÃoØõŖ¨$;NøÜĀã‡ų<%TÄŖpfüĒw-/ʏ›ā*š"ĻŖĪįéQh‘čE1*ĻLũ˛ąbŧėĀ›ŧ#įķÜG›sÚĸą“uŠe@đÅĀnŠ5Ŗ×ĻCt+bđ÷7īū8ƒgũĘčšüų)›bØ~ŋ&CęZÅĀb;h揎ŲŽŒæZ˛jüíĩAžKcÅĀQ.ã&¸KūøˇÆū¯U .QN?,ȨŠNžōŊ‘uĖ7oĸ@Â˙L~Į ƒ’ā‰Ä:üQؙ|VžI1ƨ=ôËą{ŋÕ¤ ČÄa˙yRš§įՁ ūü įh9S}‚Â;9ĮÍOĻÆ$›k&WL+ÆWáâįƒ…WøÖČ7{Ž(ģČŗa8,|ąˆäŠõeƒ‰ã Ix‘"=ŨŽĘŸåƒrB1qœâR;ėõŗ°ÄÆŪÂÁ LŒ91%Mé›2YĻÆåOZ&†Érō€ėa3^A<3~o“kzEã&ũĸđZ†ûãqâ@ė}ũ†ŧ*öˇTW#Pcųfm‚eŦé;á€1- Öä°,¯ŊZ4/úËčh Û­”?ËŪ¯RbųAœßÃEcųflDœˆŠ-)+Ų#ČÄĀΚX”ÍęŠOfâ˛ôÖ_8OÄØā ´L •ÁzgEšíŠÛŦ„+ų€ 4Ú¯Ōąß {™˜DđߔP^ēđ‘Žá%Ū˙au&BÅtB~&\eú › ļ&ϊa.˙0ļY^„uVžE/›ãŸ,Īgã™ã3Ũ91YČi™¨ČIÚåŦ°Y­f4ŸÖüČ-c÷ūë–4ã+UÁâ‘îēX6äĩč č7´§&EDbĄä‰ÅŒc8N1āJ¨×ڏ§D„YČ"qŠ]ĐŅVš$ĮtaJ7üĐ#ž!6sqö lŠ›īzŅ]yZž’ąN'ƒüv6íŠsOԌ‚´2ؘŊž!ˇi8ĸƒĸ>dŦ_d0–āëÂÂE}Ä ]Āž&c""‡ņĀT‡P÷YãũūA‡ģžŠÃyģ…:te‹ÃCåTĖÏęNŠ~Đ*QAčĖėC"&îņá_ˇCŊŽ ī7ģŽcNfF„âøŸ/÷ętGJęc/Eož o„z—Ŋˇ< ņ9¨5މy(ķŨÖĄá 4'Ģ˙íúp™˜*jOX˜:Z~ØEËBæĸÃ[ž>īzˆĪ)áˆp@ļYût9䎪qH×'īāHĖĀî°0a+üåĻîŪ ŠĶ( WŒžŖ¯‡ž“ĒÚ÷B]÷ˆĪĘ)$G”ŗeá:9rs='TáUu§ŋîņ>Bô{xhâ9ß1öĢŪŋāŖß”Õ鎞χ†ĪÕ\ÄKēfŊŧ÷07zl+ėŦ>Īļ_WŸ?w]Šđ j9q2ūģjcäÕÆ)”,ėœ{ōŨUž˙ā>Ë9ĶûJ™§sŠyŋ<âiIŽEø˙bÄ Š‘š ­ĶFäYWmĩöØÁEĻ&÷$ˆ‚QŒĪsŽ•ŗbTûܖų €įŠUŲof‘•B ×- $/KPŊhuž‰"Č ĢøģõΟ,ž§bëĮËĪënOfW^ĀÄ@¤ĸ)Q€„b|™›x?Ā“ĮžøCüYų•˙N˜øœ°†ôÛ&įŒ~ūÔI˛b˜\4ē°s@cō4C1AQÎæM0äŧōEjcMGü (á›ŗFs<į‹ž b)ņ/ g^Rržč )Q€b˙ūČŗbŨé‚H1ˆ„ƒø°#īŠŅаķü´IÅčŨŽąīMÎ"›gÚ͘Ö>įXlÄ´éī%ëËX¯Ž9&k4”ąÎŨÕPõņ34“ĩ– ލo*M6úM3Ėúæ¸i??!Ǥū”_ĩ.$û7M~õVļ‚N3ĻRõšnjúö,üÂXš1O^b™ƒø°'īŠan™ˆžĸ¨/§ø°úyäø•~īš:ŧ5)7zãa‚œGMÅ?ērŠŒšŸ^‹¨]–{RãâŌBôSŲ^ņ4Ėũ„…šžtŽęHę„'BŗqeΧÆ%dØ5TÃ=áĄalķž„$0ŗTų‰$Pį“Ŧv?>S3’ƀ%[fįäU1Fž8ö>0đÄŅõ\YOÎâĪ×ãäŅ­ŦŠ0zĶŅ zÉĀoĮOĪëļ=i(Ö=|ė/ą×äā‘#‡[å"=b9ĪâĸcĮģæ&úŠĘY->vbœŠ¨ųLū÷Ëņc rŒ˜_Ŧ?*&ļãčօ/˙sėÄ9Ļx.<…b˜˜čŋhPƒÅ„˛6Įi`ÛĀhS‡_}Õ=7 åĘhV3āx$Ôø$2į \2šąđš8sA­Ęũn‚2ũzohúÖĮš83ÆÎÄļûæhS}đŊNĐųÉ÷ŧš6ąĘgãĸ*+á° +õŠ1æ}ēušét/UtļVv$wi"GO÷1°m‡aOŒ“WÅX'—|–,ĢūT‚ŦžĶåâJŏ:ų›vŗN]ČķÄĖ1 Š_ā ‚bo^u¸šDx´2bįŽvzÚ'EĪÚāvV–ĩ+Pz9â¤zĮäôĀ·p€ŋÄ2YœšîsNō< ÅxÁ ÄĘA—}m#MŸpÍŨ÷ŒÖĻu-Į"rLä”\ŠšËe~>‹—sBßķ’§ĸÎ3ƒblˆÍžV!â¤q¸NŠŅJNÚX,õÃpŽė˙Ŧ՗Ûkž”ÔŅZŲņģō¤h*#`[mrņAyUŒÕ€Kī)ü!&8/ąvåZãHSŒ–ËËo:eđ>1%ƒc-V›ųâ˜'\Ŧl­āåC ÜģŒ{Cs‘žG“Ķ*ß0ĩîXöčYG5NļX=0ˇš}ZŽH]ēÚâųÍÆ:wÕūõįŸĐŲūaĶ,Öäeå„ãĮŨ˙LdĒ‹Ķ]ûŅlĐI;eĒęÖûî­ŌO#1ęŌmĨŠfeīnwî:,Ėv-æzģÔÚ•ĨNž=uīú,ĩž_nÖÅû§&3ŦïOŲ/R]z‡ų¤Æâ°éÉ ŽĻ0öH–š\lÄva˛øuoäõfE1ęÜŗ Lŋ{÷Ž=vRŊwīîŨÃŦŪúģ{1GWėmRÃlīŠ•ÜGīĖkĄ(FéIÖ÷Î ģ œé˛ßa%;۟īÕŌ‰i!Ų#QŪą*đ[/s0ßÁÎ?ŨūŽŊėįh´éîu ԌÖ_{ī`ĶÖ¸{îžMR¤­ũUåL§Cö ûß{„kLũߗĩŋDj!Î4ãõßĐÚäƒĢäƒbd§`ÃĀĄ/s3Á¸#2dW ą–pĖķSŒįA„˜ãŊ0ČÖę,lūMŠ1žH*†5˙ÖlģŨnžĪ°š îröũEp1!e°ŒvîŨw1—öņ+ļāpéĘ5Šņ(*-Î*.ņ%ÜīĄnAt#qĸVbæ­K Ev ¸^‡KJĸKŒųę æVmeûšbØ—š+bqÖéÆX)ŠŅøÚ%¯ ë+WŽö䁏Ž]ōˆ_Q6€Ķ9Ÿĩ>ŸzŅú„Lz|Ŧ3ãĨb|éˇ.9Ģŋœt>E'–7P –Ež­Sãë=2¯^š*ģØ3ÁúJ4ü&#Ά”Ë~,žî˜õ…øđ ÖVōĖtđž pD[VŽ(!Áœ˛z0ˆŪ­L>¸ŠIÅ(sū˝ė¤.čĒp8ÂęCžą8}vŒPŒ{5o†ųžÂyáĨb”>ķƒCîaĄ.—qŽAuŽ…†;ȕ Ģ_¨ŦÔÛīV`XĀũõš›?ãÅ_ÄŠ |wäYĢí#<•ĪŠųÕŨƒ J´ĨŽáA÷„ądķŠŗŊĮĨ3ΜkÄVŸ>+zšŋŊĒ{|(Û2 Ī‘§PŒļ…˜ŨgblQT ;ūSNŅO<ę -ŲĮq]€bĨÎķ™XąÃ™íĢí c55×+å,Ļŋ”‚Sīū d5÷’˜üe=4!QLÎKSܛvģ0ærHŊė‘ĐËäĩ—ÄđË\3:,zĶ\ĶôJ­ŅÄX°’'í#C:}¯Ô&\o†yJÅØ#‰3ËÅgą˛B/š˜å\ˇlŊžÜ^§pįĪÜ.‹šŽbųĪ%úY˜ŊõŊR•“ų/¸?´døvkZŽŦ_.üÆJA360ūÉķšTŒNH[ZôAŦ­Æ‰õ|¸b„§‹S_¨Š!Wmm­Lܓ\I(F˛XItōwĪģ xuåå!Ü΋cbŪAe —%Âb‚Ĩü}éįče¨ĩ^æ×ŌX/‰ÕƒJ¸‹twÅęAÃ6&‹0.[tL^RųŖÄnXĸąbÄĖWˇĪĨB1žVr‰›ĢD Fŗ 0¨šš)ÃUÆGKZĶTš/ 9kÄŽ‡ ÅpĢ0ü°LFĮGũUyƒXcŠ#YJ6—h˛Å÷Ŋ^M­Å#Ŗā÷ĩŋŒbë Ļŗ• ŧƒâ*ßņw"ן[ÃÄ2ßČ=í4ÛA˙îUz)‡t¯ˆN~z‰ĨÚČ OÉîÉv*ˏ,ƒŊq=>\e‰rŖeVž/OĄņi߈´ÃSã8i˛,øYReQ5*Q¤ãÅüž÷&Z’\uHš¸€,/‹@ǜ„¤Ŋ H ė§é" ŋڝ’12GZÅÄc`šôéŌ1ÉŌΤq0O_.¨ˇøÜđŊ‘öXËņŋ‘¸dZ éíĶSQķŽ`ώÕE”Ŋ°ÜpdK<–lC:ΊVëYŨKYŽËÉėœ#VP“a×Ö4´LˇÄi–ęX ZĶE˜ē–ķkrbįv2 …"TĶ&—g>×(ƛá"M"ūĖÖé-ßëŒkbŨĨ=˙fšZ+‡}õ"÷ŗx{žûōöL*ÆûX(}Ģą%—ĐĪFë(Ę,4>ĪNŦą&Ŗ"`ųɋÅũ¯ŋbËËGĄāR“Ũā—/†Åķ"‘ # ešcš0*$žŸÄ˙÷Ģ}^{¯&OŅ„uM\8Ċsĩ" KšÉōÜcGO´3:\đl„†h°E,x6Āû”d\ \Ēŧé) å*I¸âiĮ00VŒčwĄŽāĀBĄ6ˆ}Ÿ(z&¸pÁƒO+pŊŧ¤O™Ú˛iķæÍkũ„bđbúø+íbÄōļq)ģ.Ņâ:X.¯b#Qaŗ*Īéfą8\˙ä-Wņrš¤ámųã<æįC¸h˙§†ŋˆ5Ÿ÷ÜĨnĸ éŨ˜É—‹Üžyõc_ąū`_ŒbuŠÛūé”f{ÜØĐÃün†!ūuŒiâ{ ,Å>Ž8Ė:ã’tÔ}āū!.iëgČ%ŒÕė#a:GrŽYŌQŦF^=RFāj†Øëz}Åëƒų7‘ëæŒÃ<ꉓ ņ]pÉîۑŨ5Z đyķŠņäŪܘ1EąaŒß=ũ.VÎ9(߁XļŖBŠXšĶŧb¸ÚŠ”ËDā}ü͌Ē"ü›qSuöõá Ī:đæāĒb5SŽ6hßūK˜ØļĸIC”ĘuŒčiŠQ; R:0ėĘS#ŊŽX‹5Ž>sƒLŪâ⚃hĸ_è,ŠÃ[2ŧŠ–C–ˆ’žņäÅL4ĒF ňã5úĶĩ]ķڒeÎ ŽĨėĪĸ–]g~ȗÅw\<oa>A1tīŠĒ|ҝ—ŋ<2ąžX÷‘ŧQúįB1D ›+F vōfĖWlšx1ŒĨAF}ĄØUW˛ëÚâķCĮr´cč#ŖcÕ2!Ž^ßÃt­bŧÕiEqĮHÃZÉü<ĪLˇ=ck ÅHhÄīŽ‹ļŽ”ŨŧŽØ;÷y—\Ū^ZÜû}ĄķôŊvR1ŦÅ˖),ȎQ¤đ7xö(Ģ; įípérX>MÖŧÍ+†0`KÅhžŦ[ڒY$ĒĢĢvųõlxĒŦ ˆ_Ė3*Æē8a4î¤˙Ū<'ŒcY˛v•9Ŋb ÖƒēúfNķŠĮüDĮÖ0č/ŗ¨*Fđ--vô7’WbčRv}ķJ“taČPv~d­œ3(Æaą?´ĩK—–g­Î!VG r˜U 4-eMVÅIœ°ŦŅ*Æ^˛nSėSSd‰ÅSFUfgNÍ[øģvw´äeŨãÅ͍nÆW7Ŋ°–õI‡ČŲ üāķ2[qUøaktdÚZ'˛‹íƒÄKC?dĄŋ*ZļĒbˆĩ72!­yũxHŸšpÁ‚t˙ ;‘ļņã5âŗØ1x ø˜ˆ˛ËÔĸÁ{…^ŽS\˛œAãũmŦ#y[fÎÂų–Ēãņ—ÃˇF‹õÃCåmÚnlyÖ-gqĐڕWgŊÁĘ8‹eģŦâĸSCõÚ:™E1 Gũ%SZbCF,‘•U1ęņGØ vX,rÅxw}ˆ´FŦŠ!ž“7{;SŦu͝žúę3{,>WčoÁØ+Ë}Ĩ į°čÔ¯Dĸb|)Ū`ŒÆĩ)BiSí؊Š‘,ŽūŽtĮ’8¤?ųlÚôU‡”ôŊ—!nĪĖ* ĀR_8aÄhŖëéXiĄ2|8“1˜}T+•ēo>ũOϰ ?´} ŠÁX!CŠQ¤đ3ô˙$z;Cc \NüŠąOB=䩖™3đLöäŌëUö|_ã"ŽÆ #ÚuYŗW]-GA S9øØ%,ŽËD ŠĨž9ađ—ø>āQFjü•TA“JwJŨ;žÎīųĘ,l1HᚐČã™čũs_QŒđ˙“§č/ąÚQŋ¯î-‡ åTŒ&/žŪ^_lúč[mcÚVÎekŋU˜oŊlŸ¨cЖĘņņ؃öØH1.ĄWĢŦĶešōXƒw-5ÂĒsĀCgŽ+Vf<&iCФ(Æ)€3ė7^Ŧ9bÜGnŸ idW{~é `˙EŧĄSę‹šÛ¤—YŗdHlĢ(Æ|E1Ž)/ŋéÃuLšd4ŖĪÅYƒĀ@oø{–† Û‡ß=ãĒlŅHøŧ"vnĄbŦáM'y Ņʉu^b'#2û)ҟ¨Ą7čëÅØ~ŋŲã0æ,HQŒM(*Ēb|ŸÆŖœŸĒW üM+†XƒĪQQŒhbôå ë1FŠÁúŨ=€iũ ŠqĶåqŊŇęļ†ãŖ¨+Fķ}›ũ6īZųē îģ­^˛ļVÚ=íäū„0õĮd—~ā˛Ą§_å‡m‘ēmŊ˙Ø ÷6|ÄKŊ„ČŊ;ŊŽmŠ%šėwˇŌ%ciŧxÜÚRgÃe8eÂ?û –˜8lĖ)xŧnĮŽ­vĘ ˆíáäĨIū^Ē˙GÃ÷ĘîÚjvl”M÷ ;7%ųlŲ-$ŪrĖŧ¸'&NėŦ!¤ęzÅĀŽ^ŠqD{¸R‚F1.Hg'cÅ`Ŧö„]B+Ô^Šģ¸ˆ—ŪūjŒ!™B1ˆęˆ ÅčČŋöíßa…Í+Utččãl¯~_Fß >ū˛˙Öß.>Ģõ]îmōsČ>‹ß¯oČDĢooŦK7Üártč?ūžmx5|ũˇBÜ7bˇøkˇƒŊŧÛ{x›ũGl>6q؈îųú"ˇËÉūAŪ!ęøŦļB­Äo䪐 īĀ 7)+ŽÁøtĄse¤˙Íčŋޚ}PoBÍÜ?ābˆĪ.u2Äy.ū¤ŖŽģŧĪvåÔ ŸozQŽ×úy†(=Nå-ũįā7°lĮŒĄĢV1ĸĐ # DĄ„_TųPaWāŠ‘ÄŋkŸ§eą|kãŨ ãMĸVįfwG+7*LJ(8yõ˙ĻĖ(*Fë¯ŋŠĄŋ‚ ÅāŅöĄÕ?č˛xŗâ“É„ôÚFŠ‘Ž P§āõfŖCC3xFcŊb¤ S+›€Ŋˇá€^SŠ1VŪ—ˇĖ Š.š8ũĸK̇žøŅë8=„Ą‚+FJWūŨHÅŧōˇ‚l,7NŦĘĘGAb#ūe‰ eR1Na×›Aę0’B`gŪãY. ĸŽ„ė-¨gĨyĒŦŋˇŗČ9ō×&†1šUŒĖq5yS™ą`€ë Úō°ę]ëÜĄÉ-á¤QŒÎ(2ĩëīe¤‘bTį틚3ŅŊ•KExŋúmü´ļV1xĩ¯Ã¯~OšĘē2V5#Ģ˙æÁp;;^‰1Ōģö(OØĸ!z×^3RŒ>č×ŌíŗĶ"ĢeƒvÕîž]10¯ûÅāåxt×Ļ×@ °@īZ—öÍlŒÆ+. jÛĒ™#ĀEŖ¤áõŽĘ—Õ&2–Õ›!ÃOAĄb´N†äõEVÉ@žŠßō‘°PkēÕ=c3Øü-¯ßėĄę]+ߋ]áŅ ÚU\ļ,hJ1Ęđ ×ü–7ĩ î&üBv5úđW“VQ=4_unŨÆįŨÉŅģÖŊccŪúáu˛&qš æD~¨•č$ģđ!t0Ą}"ģļđ‘c3 ‰?ķŽo^nŸŖbŧŨDŌ4į¨E ížüa^€˛ŗųŪã!ģŌL¸˜U éŅ]÷ä/čŦ(×<AÆëė•RÍŋqõ˛(ûVĶļą7í”Xš†`ĸTŒĶB1Đ6„ˆN?KŲ#ôWˆ4ŲÛ¤ū¨…Ûĩ•č:{MæŠ_ė€P QÄfĸb°s2r46÷ē&ŠũëZg]Š\ēĸÁˆ+Æą"’_"b‰pÄë†đ`)ઌųöˇđeƜ##f ž!­Đ\k‹nŊĨú+ÜUÛ R1Øīr¨céZEČKâØņ!ęŠp\1ĸBđhŽj&Ŗ`ķRUŒšō›˜(ëîDëųu#ÅāmåÂ:Ÿž0XÍŅĪōî.˜DE­š‡|$#$ ąo39G ‚-Ũˇ?|:ŋ,Ёßx9ŽQ3ƒåķGŠ˜{2 1ˇĪÆŋQ1ԑÁšŠÅČEC otJųSl¤nĘ1îŊÍ&šˇ|ī;Ųs?ÕĀirSQIä…x‡åî)†$.V‹ey×혀Į>\Žc@žãņë „Ô{˛2;ãj¤ÚūŽšY’đ|F}lūũtØ)íĘm”Zy<w5v?æÉ å ˛yu^ԀŲ+ûÃĀgūv1Ļa˜R:ˆyĩM…CŌ#úŖs ā8jœô"’øÎqŧÂK uÆĮĀoķ„ĖOQ1ėšYgÄÔf€_XÚUl•ÆĘ˛‡™ā#įiŨ ē­¨čRÚåfÜjŗEĖ]ëŽuL˛G{5š–pLÅ{ō ˛—’!x§Œđō*ȸ#†Š)†+†÷ĮVŠ)ĘTëlÅ#Čŧ#ŽŲO-âŨ°ŪJ —Ģp<Bz˛ũ" ^DøJ1ÖîV*Ø´ ƒ$Ã2Ũí2áŪøĘ tzĐŪq_Žę_NO8"Īt8ЃDÛa2°Ú2ė„aÍ_ Ģá΃pÎr€9{=| ą…ņöúŋ‚ķĒgŒ-ŧ ?˙FÅhdĄP1į¸E‡‡ãkz˛’§g čn\¸ä„Ŋ÷OÅ}ĨЇŗ 0xvÖA^§í7K‘s¤'ņ;hōhˆ|ō$2V*>EŽ^7āv7ëŧ*ÆÄßSöU*ėŧ? ˙FÅ(ž,ŨĩuûļZų~Ųå—Ü9ÔäiSÅ~p†7ˆ>é¸äŸbTuJ{Æ+ä›btNē‰mĻ™Ú)Ar ˆ*Æēø0l؎Ϊ` įĀg§Á-3ŋhbHqQŒUęOũXÎqķDū)Æ u"â§&˙ÚÔוû~ßĸ¨C¯Ã e,Čé<61dĒŠîŠ‡rˇtķ‹C1˜%„xq1Š•Šžb°i.čå™īž[ķÍdÛäÛgŊÂBÅ>ôėŧ´Į+ É>‹ĒŽ•sQ>^æcĄ÷xz3,/‚aX†¸Éņh÷\,ŗø1‰ƒ(8L—† ë5zÎųČ*ÔŽ—˙}~ėÃõ?Ë9ÖķĄ|ƒúųö„˙ũ¸~ũOō’āũõsmķxjqŨČhØ8-÷‚á¨MXņįĮq;‹Đīa")Qpäu >‚xņŠŊ4ĐsEÖaöušŒ YĮbt9āTdÔ+E ¤DqcĀõ€›ƒM/o•™Ŋˆ^e"mõYŽūģZš8ņâA–o"č=Ö´;)QŦør‘wđRsÆęŪ—Ŗs‹Œ­_˜IÜīhđƒņ˙+¨|į¤ÄŗķVPzŧg?'H1ˆbDĪ+ÁˇŋR„÷Į-ŪŊ×,[˙0õŅSņ_¯ ō7ĮųO‘TŒšUküžés՞Ë_Ujņ\TÍ1ZEūųW3X6?íPWŋ˙a‡æ¯šKÖĨ†š3ŦyģV†@ŗö&'@|ˇmÃŦ•Ûˇ×äŗqģ5kåfščjĖUŠÚ0öŋfĘ]žú0ŧTö(fãeõŗÅ–/̧Ü+¯–{¯Œūp.rGΉ÷—¸‡Ydĩ^ä3}Ž$ÜU°ˇxVФbĸ°ÚéFĩ<†īōxĩ•ķž7]š’Ū9Fģ‘ü ķˆR oÅBæméRî\$@ĐRĶŠžŗmũ 1ĶĢ ëc€Ä>ŲãX†Dí•Åw=\€ æŧ2¸t˛w:Do4Äô'ÖyNÄAēÉSCāOöˇaâ Ļˇ™yŽ7ŧ#Ÿë-qzâ&Έ‹ē+ãÄEzå,Éņ|hs5ņÆ3ŽŠĪųíĖ“—ŲsĻH*F|(kcX3˜=­ōx5Ã2CšĮ;ĄÆ'‘Ņ9F{œŅŒ…'ĘũÖā5Ļî\Á*ô;ūŋ5ėŧ §35ÁWēƒ&#ĩˆ•Â9ī†;vį’}%§#`ŲŦ8ū^0¯SŖ‰n8ģ[ēu8­šéŲ€ėk SÆ=y^“áēPSįÆņ+m‡‘jp4ĖĮ´bŦUģuÅ´NÚ~Ūyힸ9Ą_įģéBqÖfãæÄÛK´ģ>Ë>ÎËb.ëAtIĨ.2qn$,f–†•or¯¸ŌŠß‰“8æ WĢÚŋ{÷Ž‹É[vīŪßu!€˛h"@t5SŠ âųŌņ\īû|o9ß#mĪKĪ÷–šĨH*†o_āō0’ŽĢ[ļû[ôΎ8|đāFCū°ƒ _úíüÉ*ŦōžƒŦõŽęĨé‡÷+"ŪüĀŪ‡°ëāÁƒ¸ōŌG įUņÃû°ˇ˛Ëą‡šąÚk÷ôTM:ŧX]%›] *Éü š5iąéŒžLæ…Ļœüā‹ÄųbëƒsV[H‘8¨×҃“›}dŋlŋÜûŖÜ}øPļø ŌŲb-6L<\Kø™7[Ú¨ÁŅ`ÂŪaR1.ˆÅ˝@ŋNė0ąTį/R â…Â"LgĘļ Š} îŧŗNIÅpŋÁ ”­JĀü‡(k^NôrõķI Üā1@Ļ ėg-]BÁ |Cq•eÆĘF‚‡k&ˆ"ō;ĪĮ‘đČÍÍ '>ā…“´Úģ‰Õšöv…ƒ§Abú…™āę<ڗŊķŊ­Ūč;€&3z€+ÆYjÖIūClí“0V9Ų…‹ĀwŠbEéތq1Å7srwIķU/`!ũ¤ŧmwŠŅÛÕÍü]=wˆëx,fĨRņZėôģƒ2„!bŽTŒĻÉbŨvŅÃÕG]Æ!ÜÜ!MÔŖ,Ũø-bŨÅ4،…^›ŨqøĻžķH´RęÃp6ô ē…vŲ_€IŸŦ,`ņŨ•‹k•CcÄë¤ċÄākIWr´õķ&íxņ\§Š¤b<8Į šr۔€fŨ•úU’ģJUāLŠĪŪņHa]Đ<"[1æ(Ö}÷>ZÍ˞!~Íę&—đŋ:§[¤tcŨĪ`ÁÕFŦ‘ļW5ôâǧ~™Č–É`ÚįnWŒĢŠŨØE,cĖâDN_=ÆËŨ”?Ôį“i„ėR“]SmęķRPČ&JÅøxųZ3Lí•jočZāÉOŗDJ+˜*wæđŠöGMųX“!BÔB]{Îu?c˙Ŋ)Wbc‘đc­ÄTÛĶ„ÛÖx¯iu^pá¸°Ė•ƒVlHēÁyÄ+åØÚŦĩ"“Š1™gé‘Å í!­b¸ĘC¤DáRju˜ĮĘBũļ:­ģ?Ĩ03`‚"Š_|ĖXCÅĩ";sęœūT9×S¯i¸čæd\Jš3C,McĸšŠē˛æƒŖD ‘b„BsõŒ­Ŧsë”9Ä>ū‚ąĪ _Ļ&ēđ‘ŠõĢú™š1ģnĩ'øÉžûo¯Nž~Õą…<å(#T¯7TؚŽũS˙š .Ÿ"– *FWČ2đ4EģĐą[°âëÚhęÄCɲo3ŦiĐbM¨Î[\t´%|eu´XáŦ4—Ycĩ"ƒŦŨįĀ+&žŽŋœ˙hî­Ŋ–%ŠiË÷Ml§dF_2ü´Š‘É 'Å “á—ÃŽĘ9ZSqŽsÄf÷ÂŖH*†7“Sî5ĖĘŌK¯é§øŋ%beP^ĪÄÂ˙‚ĸ3.ĮĻĨÁLĐ(Æ;ƊĄīŲy9íņžC‡í‰†É={y§@Âõmŧm sđČōŲčt2§ėDBšÛáYb÷ß_Ŋåģ›F1Úė IMĶŽí†Åΰô—KÂYFedFZxëŊĖ:čc™ØÔ eEžøb“.zšZ9¤C¤ÕIáī5&66‰ oeâņZÁæę%ŗ4ã]ģ9FönŨŠŦĐ*†a>R ĸp¨šÂĮsuNބΉ¯Īø9MĖ9Úķĸč+ûjú§øĩ?Ũ §azĨáŅJ?+ã+Ļ*FMˇ$ëéƒ;ƒ\–ķ ŠŠ÷r}"Ü=9î{ž%§íúÕc7_bėŦ⮚°ElĖ+Ģ;ÉŌ>:- ƒdĮ[=ŠņWϝňļaõ‰tqŠ ŽâŸę¨ˆDåîߔ­œŧE§WŒ+™ˇįœÃE@QŒ8ša-ûÔaî"}W‰›ŖŠ1}cĀÄħæ–ÔŋÕ—ˇV%YĢ)^>OR ĸøî˛˙i]Åß|ÖįŨ­˛`(ōŠąHôˇp_u\2(ÆŅ´¤ø _ĨE7+ÃĐÆ8'zyTÅØŠŠąRtQ™U æ§ôˇ”{ꌾ-nÚZ¨o+m¸Ãbã$ÜŠL(Æ$—Ōü˙›įP`j€pČę‘]1>K— Tã;pP†MK”/GáŲ[C¸zmũHoU1Ļ ­hĻ(FŦ‡Ē€ ŋ\˙˙6ofåü|yņĻ)^gG&d(ŗėƒxAøü˙Ųģ8'Šöāķūß×ŪE@Pé  ĸ(HGA¤H ¨ Mš‚H9ēR¤#íhŌ{—ŪšÂÁÁqëŊßķߒä Ée“ėîlrŋ¯/›ėîĖ“,Ų'ģ3;;Ũ÷Ū|ƒ^ähģ+ėlīįxG!rûŒąEžĪ×Zs E ĶŲxÆ|—ŋņũgæÅ~Ėī§÷Kŋđ_ĸ!ōŦ…bÆH“zī ?ŽīĖš$:įbˇŸä.XÛ¯Z‹áÉ=l/–c[ŧA؈{âč&ŨäNWĶånX램=¨uļš÷;bžˇĖlúM|𒎂ŽĮ‹˙ŽĪ™ûŗ6ą\ņnædņ!ÅÔUurļ˜@:ˆsG’t!Ewų!ū„đgPNˇdKƘJbûu{ų´‹ÉW=!Ũth–+GÚ}wų2ƄÛrĢžĩŒQņę-ĶQpaú)‡žR`Ÿî<Ū‡wV™x÷îtō€ŒA[GŒ;!7 4øiø|Z4ü'éÕ*ŋ?nõ‘t›–į†zĶĖžÕ†_Ž"üŧíœykÂČuq´g¤tvĨxŌtŪ˙Íē)‹6ö{œÕūųĮ”SCF›¯iŪAË>xoe–ÕŠÂūnēŨ@7‘÷•iarßĸĩtzÔØ ´RžĩœV [žDG(ÆØcÃG ōŋ˙ÃhiW:›čĀčŸ÷Ég+Ũ˙ŗŅŦÅŲĢÃGĖĸÅÃĮ>#­ŋ+{ÃĐŲAä?\M?UšąVâËWGRvĻtųZ›ãŲ8wq ũ"­5(..—Vn[ ]Ÿ•BŅÕØhĸĖl"˙˙š*ę˙/eŗžŊŠŸ Qp­ĨŅŧĀ9#$ÔMQÖf%ŦĩöĒCš‡O•'lõŽ­1öŸ‹ēå<˙xīŽ ŌD¯=;˙”_ÚŗsũK8+š{fZtølŪA8¨öö”kßņŠÚs3FŊ¸äÍ ŦĖg rLĨSsf~ūāŒ‰Į)âäˇjUƒ{đąuŨ—zÂĘ×ĀđŠ 쑞Ęö×´ãšCØ+^¸tí÷ę+ė~8xúĪĘV^uęБSÖn|īd 0°ĘĶBo/ŦÆ; g5ÚqY˙ám=9cÜāNë  ĢÛĄđ“_<Á; WTw3|ÅûúÖų­gg ā ŒŠú„ā;ķëōŽÂu÷„\ÔĩEc2hĮ‡wV´;rۘ—v;Žę¤;+ßĶ­:c€†pŒ†ķú¤›÷귋ÕA—ąį­ßĄG}ŪŽ|!c€Á4?}–SĮT Ÿr?py=jBÆ0˜*¯?Å;!c€ĄLŊŋØū¨ĄnéķŨ™§žŌžd cy‰(j×ĢŧŖP 2Į{éô×ös_SÃbWhŨų CU3ķ5đÚö9Ž•đá'"nÛ_ĖM c€QLKYb)7×t7Ņv[ŖRÆ&ւŧŦÎ{-#IÃˆĖŠÜšÆĸuv—ģ{ƒ=sČÆĖfD$„^&jeyą‚8¤Qž2šîž[ĸŒŧ¯ĖŠĄŦ…“nbĪÜõoĒy’É7‰Ōv=mm­ŋ¨$ģl’rD–ôú“!c€!´=Lę]—jl˛–—׎x[Ŗaļ/ëCĢŦÎ{9,Ėęëđą^RA*D¤ ûöv—KŒfī¤ÛüUŪ|8‰wáĢŨ>ĩW΋­:ļڒg´ŠéĻaįŅlbŪŠ–Ņī=Žm}pšÜvĩęĐáĶ~–°‡Q+S!ĢiĮ—ŸNI.ke­ũô&KŒĢa~:6ŗNÁĩ¸d āīĄŲáÁ ËđŽB?÷¤žą˛CS‡­ŒQ7ëëJsÕĒæŪA‡W)ʞ6ŨåĄ á÷Xšä›6g77Ũŗ.ąWž—į)Ÿ°kŪeŽÛ_å1éoˆéÆŖŦlâĘgM…ėO—nßdõ°í*ĮîDZ~ c¨ĻÛÁø“ŨyĄŗ’^7â–Ôͤh[ŖxÆ֚&›Ÿ~üķįĒËįĮ:xM˜ĢIžĶäÎlČŋ<ÃLžØ‘ĩžlî¸öøw“Ƙî/úÁäŅQ7~ž0q´xÊAĮŊÂƌ™(ŨŲ§×„‰õØ+C&˜F&Ļ˙ĜQÁîœv°[,O´qkô>ÂO{KBĒ6`ŌđÚũ^’ŸT>ŽÎ9ŖĪÄqã'š–[“ Ä74ąŦ8ÕjJį—Į5gm~‘žąžÄ3~Ü$ņŨvņúéž˙Ī–Š<ÚŌĪšÂyĸΤ_?Îyē-Ũ<–ÕļhVÉ\‘|ëmgå-Ŧ–9lyúK†‚û3šd āĒüĖā†ģkˇÚî ģ8Hƒrmļ|§/~ž›ö¯īÜI‹ŠLΐnÍū‰‹LˇėnK\Ί¤¸„úšMŠŖ ķ‘íķŦ8ŖltFd4.+NΈČN‹ˆŠˇ\h}Ë>ŊŸ˜-ūä^AkŊ2â([ڟŽILŠĖö/m*;⤰ƒŨlŽi&e[ôŌ!˜Ëö˘ȏ Ķíŗe&Egž6gŒžėhĨĮ™væš2Ɩ¸¨ųϝëS˛…ˇ˛.3ÕGēǐ1Ū¸—IņâąÅö8é=D|*Î(—™ácžéî¤ääȘœH?• tĄžŦJî•WOgIotMRŦ$á”ôō:a™Ãw-KũDsy2pÔc_čY:œTE¯›‘ķĒ]Ē͌‘=‰ÕL1å¨Ëכ–.ņöHųYé\ģÛÉ-ŸėA‡Ų¸˛Â“cØėéĪh„8ŖJæVē{‚|>ŋ >ĖJŊ$īŦ[ĶÆų–Ū•ËŠJMYöûÛĪ OÆŌēÅÚDšwÛ‹gŽéw˛q餐XM7AßŊVĸėb’îŧ°ˆūŦüâ÷Y9Į%n߯ú‚i­\ƒ•.ÅhŦ4õĨ°ßŋBŊ*euŸ ƒĩH_[Lŧoë#ĨŲí“Ėôj§nb/ĩ 25žLĻ…ĩž+Ęrūė’ĨQåŪ~–+cŒH1ĩŋĪží' Ø&=ũ-Žą9ãÍ˙ņß~`ũŨēd āĨÚ4˙{‹<ãkä´BĪcĩˇĶlfŒ¯k°Gŋ2Ũ Ū%ūpČ:ė;ĢÚm^ÜX…iAw¨9ŧ­ĸë1z%Üŋļęûœģ)Ė1īkϊį­ŧI>‘tB:ŸaęÕv‰˙íØSŽĪÕōŨDĘŊÍ#ÆōÛúkZōëĉ~5ŨđĶ)ž‰ˇvŽo&MūDoŠÍL#áģKT!gAëŖ[cû„C›Ti”˛žôVĻÎ2ˇrõÛęžÆ70‚¤üPz;OQ Ōž•'dLeĪ=Y‹Ø 93ÛŌ,ņaēĪÉõ ÖŪDcÚŪĸj%o×đ1€ƒ˛ũâžešÂĄķØ3=WĢ4eWđå}&3ĶrmĜœŗRá5XC2ũø> ū5eŒ]äˇlܖtSvŗ1âv›gLĸ´ôŒŒŒôTkÃJu_ŧ/šŽ‹šbIG-L#2Í÷îîœåldŒėŧ#uOfÎå09Ŗō5ē˛hüŸÔQ|27åå_ĪéōnŲfVMj*>kF÷F4OĻo(÷ĐR6īJoKÕQÚŌŠŲ¯É™ĩ™]ČN˜ggŸBŽŪ~ēėjocEãŪúƒDtQŪ†›FNvĀėņ ōôÖĮÅß÷‚%WÅŋbįÖNūDŅ›É÷.ßF>J.íû`"ߓD>ÂīyyuKÚŲ”(<ąžģėH~V_Īã÷€eąDAŋÉĪ:Ũ$Ú2…(TWŠčƒÂŸŊŒŊšmz į-+ĘCˆ6c%Ņņs$äœ:â7YĻđˇ7cWå5åÅŊ"‰ĸÖ ‹õŸUŲO”ŧŅTŌgŠš;&t•Öj"Nū&D“ŧŨū{ōV&ŽāpLûŨtĸ#ī Œoü픕.:åÚØĩ›ãÚŸũčÖUWJ1€\-œÍ_ŧƒöķB5Č ‡˛S×å…{hž'öŠ ˇ”r-cÜnšHĩqą„ģZãāåđ K#í/æ.1@{ŨöGŸėųī(ÜGųąąKí|ėZÆđ5]čüCÚģŽÃS›Å/Ÿ‚‚q uōđCŧ#P2hŦšWđí…nģûáåŗũ!œû•ėZÆø4éî´žs>Bödüũ‚•[īĄĶ]u ėš’ēŦ¤yNŲŨÚI[tÜGqûųBō§•uŸžögqŨCģšîMšdíĘĮ”mí…ēG nāŖHeģĸ ųΤ7ī˜ÁYkßÚ_ōŽŒŖ0Aư¯H;Ū2Fa‚Œa_9ī˙ņ°1 d p2Fa‚ŒŽ@Æ(L1ĀČ… 2†}ŊÆ;ãBÆ(L1ė{íF_°Ŗ0AÆPāŪ2Fa‚ŒŽ@Æ(L1ĀČ… 2¸b 2F!âDÆøŠwĖzĢp¤ī k…ãû¯yĮ Îrâ÷A'Ū1ëíą†Īđ°Ļ:ŧ IiÂ;fpÖ‡ˇvüģŧcã¨īđ>$üŪ1ƒŗē:ŧĩƒx‡ Fíč>d/īˆÁyßÕ}īˆĀHV:ēÁ-øÜØ?ŽníĪyGŦŋY¸Q-€MüŲywĀā‚ēYŽmíĶŧæāĮjŧ#0°%ŽíCēđŽ\ąÉą­Ũ†wŧ`0įŲ…,ä-¸æš#[{2īhĀhJ+߅lį,¸¨l˜ō­foxĐIĨģ?xG .{ė˛Ō­=w¨|ôŠË;ƒ›’¨dŌ–wœ †™iJļļaŨÚËģķŽĀčŠÍŊhg’tjī A%%į]ĩŗĩãNüĀ;H0˛jŸ}ždØ+˙{¸o‹ōŧã5Uk÷]Ô<Ģ[{ĐåŨ}›ŧĖ;>0ē>ÁE­ĪhŠ{{œoؘņEh)]ˇ˙­­9Žęč!ų3[söėÔ3ÃyûŪ¸īã6g ūNĮ@@ģ˙ą9Ģrd1œ…“xGā>Ė*k{怐Įt tĐ,å)Û3ĮŪŅ/pK^Í=´Z¯8@‘C š{q‰^q€[Qāėš‰Ít t0ˡĀŲõp#%°í9zģā&ŨÕ'ĐÁëd§ŗôR}ˇtl“Ŋ%ü íˇô×I%K%ôŌ:ĐÃö]J–ĘÂŨ”Āšļąo(YŦ{‚ց€ēÜW4FØ‘ZnéúLe˝YŽm ‡Ā1Ę–ķ§mFÕ,ˆw†6Ũ_á‚Īe5Đ4ĐÁŸį.X5ëMM1Ē’=yG`dU#Û)]tú -ԋTœõ—ŖĢäwlŗōeīŒĶ.ĐÃųĨʗ ]ā–ž ^ų­ã*i ho„#—î÷Š)ŖY ā–âlŪÚM‡4 ôđPb'G?¸]Ģ@ ŦØ˛ŧC0Ŧ5ļīŠaUtmâ=ėÜáĐâ˙KTÜÄå9ž›YŽwFU?ÛÁ¯Į7a˙Õ&Đ^›´‡[azš@ް ŽŽqrŊq€âž•âÕÅZÄnir€ÃĢԈjŠA  ƒ…—^ĨA,.Ájėø:S•^đÆRÎŪ]1Ŧ™{Eũ@ ŽsŪ͉ ÎŦumšÚq€.-pbĨ‡oũŦz ˇŦ'ī IÁ]1Ŧi˙šĘ€Ūwjĩ.q/¨¸ĨČ~έˇĘÁš`%ãē#ĮÜ)ûëŦ“+>ũĩǁ€ūŪīäŠ%Ŗ;ǏŖĻŠĨ]õĢD5´O|ĘŲU‡GŠˆņ=[…wtãwį×=šJŊ8@~ īŠaÍĩ9ęÅáÚūÃ;ã™t˅•ŸĨ Ē:˜įįÂƝF$(ÜĒ'ļpeõ‰Áj:¨›đŽ+ĢĪēĒV ā–ŽŽsmũāÉęÄz¸°Đĩõ†¨¸ĨЏV@Ŋ '. >FērRÔ*ųiUwôTˆCwŰfãQ5”Šéîjû Ņ2Ę_z‚wÆ˛ņˆëeÄēœt@{\ßŨ?įäõnčŋč] ĮGŅo¸^ȀĐ]/´×åž Û~žízā–ĻĢQĘIĪAAnj%xšp]”fS}U)歘6Ē”šZpQ•bÅāN…Ņķ™ Õ)hĻ:倆ĒdŧĨNAËΊSŽņ-˙wr|ĩZ%ųÍTĢ$ĐĘå?Ô*)h„Z%\›wxG`ŸgĒVT̏ĒĒ•šč› ZQŨĸV­,p™.wÎĪąŅŲĶA'¤ŌHŅūŨę•naÍ 5K ī¯fi ļ{T,ŦT\Kã{›Qŗ¸Ūņj–*kĨŪHŅ HU‹3Ē7æņŽĀ(ü§Š[ŪĄÍę–jē;ZŨōÎ.Rˇp–đļå¯WuupMÆįږŋsģļåWki\ÁK!4ŽÛŗKã *FôÔ¸žjđ€Ģw¨¨ÖU Të*@™Ī24¯bL æUđäéMûퟍ}G5åœöŖöu\đčîDyĀĶ(-F ˝zŧę#Ѓ3fŨŌĄ’úņØĢx¨:ęQKŒakutšūŦãszÔā,ᏇKđŽĀ†ƒŧ7áÅ;ēĩᏇ‘cyG ŗ=´ƒWÕmˆxU]H•æø‰†­ āūöĐ?ŧĒn}ˆÎxfŒļØÚîīų˛ĪķĒúeËđĒē°*û ˇĒÂÖ€ÂëëĮyGîáfqŪxēŌU_âUõcU+ņĒē°ĒZ•[Տ{âÖ.ņ2īô…ë1 \Ą˛%'xG ¯]´WÕČzCÆ(,ĸjķŽÜÛX "=ŦŊZ¯•Ūq€§ëfíjŸbßč8īÔË䂠šæÉŪéúFŅ čšžzŃîé[a˙Ë%nƒŽš'_ œ¨oā‚éšöš¯āÛ|Q×0p=†ŪôžãŊ˜ž–éÜ×c´ˆm­kāŧ/Ō_Īyōųø9O‚–čG…ņŖôŦØãĮëZŸīâœé×roíŸâuC;­˙â€ÆĒ&÷ŗ5ë)ŊõŒ<ÛŽã6gmö’?ã€Æ|WŲž×1ģē~€g›^ÔöĖģ ô œļųBAsg‡ęx¸˛š4›ēč8mbä œâ°N€‡KXāėTQ§@ĀYŸĻ|dg‰ģŋëx¸›öڄį„ë‡Æf6â€vJFŒŗˇHƒ¸îzDžmŨuģ‹ÛŖCZ\‡wÚ9ģÎū2Cb^Ķ>đlßĻžbĄĐŠÚN[Ŗ¨Oã û? R)ûSK5IčŦy$āŦū‰5-wv“Ɓ€‡ģ;EŅbcîWŅ8pV•ô^ĘŦ5\ÛHĀŗí?ĨpÁvõvÅŋywÚ¸7]é’íĶß×2đlc/zaĨ†qčĄŌ‚ŧCĐÄĨ?üc´‹<\Ûtåũ‡ĒÄ Ņ0pÖ/Ɏ,ŊûˆVq€‡+1؁Ĩ;eŧ­Y$āŦ¨ĒCËĮMĶ(đp—7:´øô(â§=ōŊc+ü—p pÂæް́ŗĨôz-Ū¨īÜzG×č•ųǁ€gUÆŅU‚Ũz\šá yG ēõ~ޝŗÔGũ8ĀÃ5Lvü†Š‘€ŗ†Į83Nčy\Č sæŪ_e=Žz āŦ&Q=œY­d¤Ũa r;ģËŠÕVúĒ8퉀ߜ[ą],îI Xđ´s+^öV7=UyŽwĒ:rĐŲ5'…ŧ¤f āŲ:%;Ûo¨hÜ(U#ŅĶáļŧ#P͜ģO9Ŋî6÷îųzz,û+§×í˜Ø\ÅHtõ2īÔÔ)Εģ„ų-Q-ĪņĐ˙ū÷0ī čÖrVžz§¸j€ŗžHčįĘę5“œ˙Ũč‰jÛyâ’ŋ¯īÍ3Į×|U’w4†˛ÁßĨÕ÷P)p^đ2×Öī‘^V…(=UŸ` ęĪÛĒsâ€*ŀ3NíQ§œÁQ…¸įãŒäíÕí/ÕîĘŊžš‡bX߆Ģ3äw‰čąĒ”Ž›ĒVIÛ mã^ëūM”-9*zĩļĄ×ģŅ.õāÎĨav•JŌŅP/Ū¨ā‹˜ÚĒ•usąjEAŅj ēüaƒ•í-8!nâBß=wˇ…ŊeĘžŲāÃV*ϏPˇ¨^3ÎP7ŧÁü‡ņŽĀuī„ P¯°WlôrCŨ—Ũŧîw;æîÍĀ@˙Ģ— |Ëöĸ;CēsƒWÂĪļgÖ˙aÉŲ×}îŪņšáģcdÕē™ĮT,lû9 ĨüVŠYZĪHWģÛÃŪū‘K†|X^~Víķ_—ŪŋēØúąA‰ŗįuŦô&ņķŦžū|ĮM×âĪ-ųĩs5éé+õ{Î9uetĮJ7¨ņĄĪĢY\đ"5KEū9¯ny ū§n<4؟ēļCū‹ Ü0ãÁŊw•@Į{Œ–ˆŨúā‹Õք…Ŧė™˙ÕĸŖ}ĸf:?F¤a4NoŦjyĨĶģĢZØ÷sØ *—xÂí/ä{īlę6Fė}*uo•ŧ/ÕItj`ŊģĮķŊĐũ|æã˛~z:}ž3uJæp• ėžZUåĩ6ģP÷>OĐ UũÖ¸ĐĒŠĢųTĐĀE/.§ šŸ—Hī\5—Žä~VĪ'ar ךũ‰sÕ…¯ú÷BZčnōMôŒ3ļPˆ%˙¤~™īd|Ē~ĄēŠ—xĨ‚EöfįúÅŊĀؚîląLūįpÖt;KČÚîlEF°čŽ…žSãrPĘg‹ũe÷]’û6Վ§‘öĒ~ųŽyT•@į?Áâ÷ū4M ĖVĐ7÷é#÷Õë­ˇ/UˇėAģųá,€[Yv]›rWjSŽöÖ$+ëĖ:-QN,NēPYŨ¨ī¤ĮŨą”UšņĨ ĩņôVfGMĘmû@˙ĐH÷ôŌ•|A“cÍ=}áæã ũ,d—đˇĸKõ}SŽą§‚OU¸üIöÎ]ĶŖAŗ5*yxŒ‘ŧ å• ŧ#pŪk¤Ų­ęKÆĢxU ~ü÷)_ļĖĨ͌eēxĪ‹§Ų{ņ\&ß Ä-ī&´ëˆũeœ´áĒfE̝ē•.Õî⎆—@ĩ ŖōÍcE.?øˇ‹5>}{A¸CãĒTē?ËÅ*9˜ôí ŋĸ~,xĐöŗZ–>õž–Ĩkâė!GWpõūŖŒu#3@Õlˇ;ŗŅ-^áNŠá9ãŌ×ø0mË?~HÛōUˇķēŖĢ—ėz­ņAjO×kÕSåđ1š–ß>Eģ@AÚĻ6Ō¸†P÷jĨ˙YŪ!(Ķ=ĨŠũ… äĘû˸dJœÆ¨įå:ŧ#pĘ+ŅŖĩŽĸzrg­ĢPQÃąõŗÜ{ŲÚ šWņ¯CíO< XĮ;§œßŠ}’Ęk_‰ZÜéˆčæ_ŧ#PnPB9í+I§}…ØÚkzÔ˛ú†ĩ¨â/WŽÄĶ[ŅäVŧCPĒé1V r›Ā Ņį×˙•tŠÆuåŗėŪeĪH~ķãRÉcuŠĻozI]ę(ŒŪKíĸOEÅīÕ§"W_É;ĮÜÆ;eÎęõ“á/7ÉĄĪ”å€ÃÂfęUĶg‰vīhm­ÜėŽE߇đŽ@‘™ĄēUuÅ=ڔ7}Ã;GÜĨ_]ŋF>Ŗ_eN;1‡wŽōŅŧĢ› ڑVã–=¨L¸ãö̝”Ō!ČbVž?¨˙ÖžsĨs*ˇlŅĸ^uų~ښ Å­Š!ūâߒ5Ē×kŅĸ™AĮ–=ékkëŨRĮÚ ‹ö™uu­ĪīOûËčė‰vc˙ēp3āÆ%ŋ[û†ŋŗd/īˆV*¨]›ŠWoú\ŧpķĻŋ߲žõyGô kú6Í Áīwĩ=š>Pß k&wסB{*Ė ¸`Ö×roą*í§ž 'ˇhlÉkCR䎟ęËw liW‚}DéŒ-ŖƒŒ5k§Û~līVuËĢ9á¨Ŗ›.9;>˙KIŅ;—R70Ænˆz î8Jv4Ü͓ǀÛ{#æ[=ĒŠŋ'•Ô7MÍČ~WéŠ2oÚ&O !Ķũ?ę´hūŠĩ%s2†Yōsž<ũąųžå>nŽÚiœ YjžĒŊ#EĩÍ;_Õ!ÉŊbT‹,ũHS5#đūē\§6'Mĩ¯ąčvÕ"Ģų‘:y“iīē$]ÜŌÉ?˛cw S#Ãî îĪfĖ'ŧyތq<\ŗ´ØŽĘ–zĖ Ÿ˙} Ĩ\•ČÖxĸä+juãúķ´J Æ'¨ē9CÕģīģĒF–ų‡j‘x‚ētĸŲŽę×XŽÚņ1§T*țļČ+i•đˇu:­ŧ …0öStXXXxŅ ÆŌ¨mތD? ‹úŅ•Q3ãIhk#]ŋ3wô-ē)L¯§ëĶ.ĨĩFųUo\ÅeĒoNĩÚĨ;ĢûËD ×€ÎߎՊ"üņ˛ĩŦRûkL”­Öˆ¤į'¨T7Eœ9waõ‘ ē)îãOŌáīP 4ĪŋBį…ŋÉôiތHÄã ē(Ėh”œYO,$Ģ:cĩ’2›ˆäâj^;_S)Ā3?ĒTĐ<õ7'}ŠJdŗÔL§ËLÛĒķ héŖLõNīØ6Dũ¯1Q”:7˜­pĢŽũ…ņ6G–$72Χƒđˇ"…Zfß‚ÝÂa‡`—xáM‡Äéę,wĢRĶÄcę”ķ›3ļļ ‘ŊŽEhœ.V0žŠ#õ¨%Q‹ī1mV%ļ/ƒT)†‰;ûĢSfÎümɁl˛œcyu ™Î ’ÚKĖšCGÅB¤KÍüÄ^ˇģˆNMQņŪCĢtZ*L“ÍЯ؟åšD–ô˜ Ą€B‹4ųS†*}׊rõžČ›LũWfJģæŪĮĨ“ęŌk͈ÆIļ3Æägi>ŋ!ũ°]/žcšŅW­Y„*mü“ĩ؜ÔŪåČŪO×&2Ũ‡u(Ėîiķ=Ļyj¤Úí#,}ĨØiöō‰üvūū–œžŽ2§“3Æãόņ5Î—1ļ9„hŦZnRĨW˛V›sË‘i•ËŸVácŗkČzÔ`tõ5ú“*ƒĨû~ĸF)ĸœŒqG 1âõuI:ëõ/]1ÍŗŌŽ!g /Z˙@ÆüI—ÔŠp­g~Ękĩ9\ÍOĢĐtš{6î. ˜¤Õ÷8ūĸģVO…B$–ŗRoĨЛl6I—?L ģÂßĨ÷†i)+ãMḊš2Æį>Ō…m=čŦZNWcH–qZmNrõ%oÆjŲ >7Pä VßcęęzpÕükē^ˆĖ›.Ž7näˆĨ1t•ąQŽąAYâ)§ī(Ãō+ÕJÆø„˛Ú36—nŗ<Ŗ3ųUdŦÜ šŽV„ƒO¨PČÍ6į×.FöŠ]ke*u2ûkõ=&ƃ*íķ–ë…ČrF"ŧW]xz™Rbi‰˜1R‰.FÄ Ōå+ørF"”¯āJGa-¤BvŠeųŅxÆļRflL稃J%㎨PČ&Í6g#ëŖYdTøÜ@í2ÆwŽ÷qˆ×H~đ‘lû^ē‘õ#^×v´f{ī|ÂŽųúÜô÷1vņNVīŽpâugcîH īÍVų]ô*'rgø°ãÎáoˇĨ>7õS+>Æ&¨Ņōc܌ņĩf‘é“1Ēuq?€;3tƸT¨Æ ëzČZšYF—jŒÍČŖG¨ęCmY÷Đ'].4däŒqpŽ oЍÜv}đ"d €BĄK€yđŠ{L—$ Ríjیœ1b?Vá ē‘?]ŋ" PøĶüŋ<‘éŽ{'č/íë5pÆhæŖËÕŧÆŅã†ËE chÆęíˇxéûŧiĸU ĶÄĶjßÕgŒĮÕxƒîÄõ;ô"chĨŅt–0rÆX6^7čN|\žŖ2hČĀc•—oН\ųÔՐ1@CÎû&ĒņŨɕ€Œ2pÆ8Pčn¯vÉÕŨ22††ŠęT€8c,Ũ Æ´éãéû÷(„d˙ņšFbīōÚČšž^ŸzŒėˆf_d—G"rY7hËhĸûÁĘzL5Đ2‹WnWpĩí2Æ7.Fæö#–}MŸzŒlf_äކVįn95ŪĄ whĻŌE‰ękHŽAW].BģŅÎŋp1˛ļŲZEĻŪđÁ`ĮT­žĮqĩ\ŽíŌ8Ū`>å˛&îQŪ19ʖļL–ËõSŋœđrļ)c<ūĒeœĢ'+hpA×UכúÕjsŌ{.FV=ZĢČTš?0(Q_Ģīņ×cû"ĒĒë…ä1äZbŌĩQÂÄWĮ’éęIĶ™–ö§ŊŸ9šu^ž•sßˉɾĻF÷žž‰ąX–œ1ĨDk-N=ļ"(91py•ƒ‹xĘå2Ēhĩ9c\íĻVĄé0–˜„jô=^¤BlUnÉø“˛6ŽJĨ5ŒM”bœ&ŋ<ˆ".\\t‡’Å'͈ļ,Iĸmâ´°ÔÖĨ)1rÆ8I~KļʡÜOw×˙qYŧ钊ŠQ;JŅjsnr9˛)EŽ×X2s‡éT€-Ōæ{œÕLānĢqīk‹N”\“ą’ąÔDxr—z™_@4ExˆĨžŒ5§Ŧ…éHęÉØ[ŠŲb“÷}ŠcĄ´ĶlAaEX3JW;Ŋīy+•8-C•ŗ+ķ´ŲœôšË‘}ĻMdkUøÔŠárŋĒÉ÷Øõߤ’ķ×Õ)G˛¤[čũ.Ũ:˜˜_@÷Ň-äÅØJō§Ą‹bŖ€Ôŋw´˜1J†KY‚ĸ>Ŧ=ÅĢ•Ŧš”´TĄÉæTŖuy­&‘ĨĒ(6T‹īqĸZ]÷ģ<:ŸE4uHm,y2†tÃԍ´QėB5Xœ~“Â^`i¤8]CĖm)uæü1į-cÅīQôōŽĒ…%šŽÖ)ZlÎT5îˇ^ōžĄũĸBd Ü*õŋÆŲ­U‹nFځâ*•`:ˇBIĖZÆø‹öŠ/ËבPÚëBö*O ŖŗåŊíbŦöUá1a‹jíōƒ:ŠU–&§{Ų¯VfYęGĻíUžđ ­j3{Ģ]Š‰;†Šr 9Ҝ1"™ĩŒąšöˆ-ĮόW–ŨΕ1ēPüS5DÕ¤nĩ-Ö^Ė dU.Ķh8írÄ$5 2SũėOö•"ëŠzS†Ēí\ölwy Hđ{ēĒ_ãæę†÷î4Ÿ;jÜ+ã*I#ũÕ#ąqÄFÆ8NCÄéętëļM>+ULĖ Ķå…-—™ŧ~ģSŅ €cCʸ^Nn^ęnN×[ŊÍęŠYĻō”û W}HËTík3í9õėäúíéÄĶoRĢö ÚÉlfŒYâŠ)Æf“ĸ†Đ qúG1c<BԁĶđŦŦĢTŒ°Œ # ĩô-e!GÕßŠŪų˜?Õ đņ)QĒE–y˛‰Š‘€>žt=4֎¸´; D…üA›bÁ\/Ŗijö׌5N§†Ėfƨ'6}WIĄNŒŊx& ĶqRīÚy[™ąļIŠĩ„”ŗBXüų ą7ŽĢæėsŊ +üz#4ÎõÍrxˆÚ›ŗÂ€ŨÁ‘vB‹Iĩ{l˜Īd—‡mWT­^ JŨbž)x‰ÚŨ ųâp œA×.ĨĶtq:$'c ¤ņatxņU2]ŋ˜B ÅW:' ĶéGŗčC&öÍMšx‰h4c5‚čÎĨ‹ątP…NŽPĄëjŧąĒ4 +Æis–¨QpÅĪF|]ąā%Ô T×*ƒ_ŨãOĢQJëq){ģH“ūŲũ˝öĪö–dooI?dwīÍ1ąKY|v=ņɈcI‡Ĩ>MĪĖ>‘~åg5"J̤F)Ny=Ž[Õö$ĢÛwY%Ĩ§Ą!@šÖéüę~ũn~•kgœ*‰Đ9UbųÕmG’zUô ڜ<ΌÁŽ˙ÆąrÍÜtųžSÎCÆ qÍŨ#8VŽ•Ļi+GÆ qÍė˛â; šŸÉ+GÆ ņÍ­#æYŊēĻđŦÃQođLđî†oÆ`‡U×8"ķŦÃQuˇņŽĀpÎŖ;s­_ukÎq­4Ä9c°aQ|ëWY—´’\ëGÆ ņÎlĮĢTW-Œë9)d Đ÷ŒÁnyķŽ@5Īû­ã2†Ã*}Č;÷Á?c‰ō˜Ū*'Õ•Ę%Čë§ÛMÅÜ˙ŒÁj$ΞŋĐĢÚĮQ0ƒIã‚ 4d€ŒÁjF.ŗˇČ‚@íÃ(Đ'qvwx~'ô¤`Č !#d öáG œ_üîõDÎû›Í×7¸ĀÛņFčØŒ2DÆ`lGfû“e4ũ¨[,Ö4Œ/ĮN' ŗŊĀņ&ü!c8îÉGyGā6 ’1X¯ø¯[QnEöŅŌÂã=Un]áŦ+ „?ŸŨJi}v§_î -d ĮũáÅ;ˇa”ŒÁ^ؐ˛ĢMū‹Œēˇī=i˛WtiŨc˛øúūĶŌcˇ[ģ?0÷›S1ôŽČd Įũ‡wîÃ0ƒąkīF¯ŪÆtķÎĒ-†ŦšvaTYķėŽsŠ‹ą‡Rû˜'k¯ô93˙Ģ‹ÉOĢuüugŦī|^=4d ŒÁXņÖķ}¯ú%$&ÆßöšzõĪšoũüøé.Ŋǧ +__“ûYŨ{nøøßOLLL ōģvÅĢ™ %Ģ 4d¨Œ!yŊQ—Ž];×-˙Œ–Q͝.´W† 7oōēõĀK%j´ėÖĨKˇŅz‘à Ĩžã€ģ0^ưmjĐŗNŽY7Ļ˙æëÎVû ÕtvUŨ!c8Áģī܅;e ļúÖN­W:`.cū[œĢ´UvįVäà •ŠķŽĀ]¸UÆ`û¯9ŗV‘Ģ;ćˆ?œYšvōOÎŦÆ 2hČŊ2;vŪņuĘí”˙/‰ūŊ÷Ģã+ņƒŒrŗŒÁN8ēFņ°õĻŠG‚ˇ;ēōGŠN˜pƒŒrˇŒÁļdUrhųF4'į‰ŋƒgĩ†į;$9 ÃGû'Pxš]Æ`c¨—K/ĻšŸnMëâĀĘkâš8°´ c8á?ĩရpŋŒÁ:ĻŽVēhÅ ŋ2y_éģYéĘå.ųUP•1 c€†Ü0c°§ÃÜɚéésxí•S!ĘzËNI]âPT†€ŒrĮŒÁ؈ČmĪÛ_(ôĒÕ<~ģh˙âņū7?r<.î1@Cî™1XÕÃ|ÕūžˇûؘUtnėî÷ \š–wü2įã Ã)˙ōĀM¸iÆ`Ŧũ™ûķ^´5ŗÖŦ[ã XšĘîŗŊ˙ĪÖܡŧ#÷Ös)8n1œbˆģa¸ˇÍŒĩģ˛ĨŨĶž^šËžđŊíŦ\uŌŨĀšMū÷āŒ—ŋYu/îōąšÃ 2qīéëaq;G—Sžvëš~~gŽĪ,2ûĀĩäÛËŪÕ.V c€†Ü=cˆÚÍ[ˇįüߛ7˙ų]'Æļ­ÛmöÆSgΜ=žáĪԏMgČNŠļwîÁ2X c8ĨôLŪ¸d ‚ŒBÆđ(Č !d ‚ŒBÆđ(ČÎiԏwnÃŖ c8§˙DŪ¸d ‚ŒBÆđ(Č !d ‚ŒBÆđ(ČNzĢ6īÜ2†GAÆpŌÜņŧ#pČ4„ŒáQ1@CČ4„ŒáQ1œUũ9Ū¸d ‚ŒáŦÕ=yGā1< 2†ŗž}ˆwnÃŖ c€†1< 2hÃŖ c8­ŧ÷ˆ(l1< 2†Ķ.7â€ņĩBÆđ$¯8ctäAÁ^ဥŊZĄ|ųō/uIcÂCų Ĩx‡Ž)/nΗĮ˛ŗ ß`^-˙–ôeŠ^Ŧđ"ß8@‰/öEP~>ĢŪâ8§Ķ?alNŋÕ5øķũŽø‚)ĀÕœâe~ˆ´ņí=U“whā¸/mmΊú34Á‘t!Ûų˛ūq€BG øōūĖ;8pÔ#mÎKŽį AzŊã´ĸČŠZŧC0ž—nøå]Ë;>pĖ™7į2]c)ęTÂLŅ5Nëę>Ė;ãšbįģûīÁėlÎÉzãįl ę§gœ ĐzģßŨ–ŧCåæŲŨœŸéĖVįesîŨV4ļ˙ŨŊĖ;FPŦjœ6gsŅŨâĨ+øîöä$(ĩFÁæüF¯`Žē”1čCŊâ´åŧ#0%_ŨƒŧƒĨlu“ÎíˆNąŧ“ęZÆXĄSœ6ŊŦ[np•|uCKđ”éœĨ`sFŅ'˜!Ž% ōÕ'LPlŖĸī.†ds“•lÍlFtĨŨ[RVŸ8@)e§š{ķ”Y§hsÕ'W˛…¨Ž>q€Rį}uĮđ”Ų hsŽ×'˜‚¯ U€ûĄmËyŧ#0–ŗŠžē¸ˆĪM(ËŋęŒËŖĄ>qÚö‘Ž—;2†GAÆ !cxd ORŪ<ņäĶæŠĘ|"1)tŖ’y"įŽQ42€ų1e‰ięŪÍrōÄĸŒÁÜÂa…/cŦČč+O4I8nzéķ¤]ÜÂQ2†ēZôäj“hģ<ņ|Btuyj“žƒ‰æWØ2Æv+O|FūĻ—úĐ9^ҍC]=GķŽ ˇæ™&jŊi~Š5§Pd…-cË;8pÔ'ļ›ĻŽé~Že˜ŊŗdVŅ;LpĀ’Ģ_܀xΘeŊÉųöˇ<‚Y—æhž¸Ņ…Gœ \‡ŋOÜôĪåæĩãķjķ œÕlķIßŧ›ķØ|^›ķÉN;Nû+tķŌņ)•9Å Žø/ī įŲ4Ū€›hĖ;^ŲÆ;pEúōŽĀ†§ņŽÜÃģ\pP CgŪ€{:œw6´|‡wāfv〠-žį¸‡ã­xGā’Č7xGāŠģḅĸõŸä‚‘UŠöšĩ—Ÿ¯^ETĩ¨•yåĒU{)įY*5„šzÕĘų–ĒQĨúÃU\ļZĩ2y^x­jKPUĢ?c'îüĒ.ÆaVĻZsi¯V­RŊ„‚*U­.ŋŲ’yŪž-ĢV/æ`ˆ Ģ^ĩĸ6%[Ô¨ZŨ4õhõĒU­/SĒZĩōâcņjŸg%ĒW}5īė˙ÖČũyįyžjXBâk¯M“ÜņëųĀŧ]‰É‹sžeÄĨ—a}Ķîä[Jxũ­‚jۜ”´&įYŅ>Qq7ūzSz–Ö]Yüo¤Įeä<[•OÚī‰Ž‹KņRPĀ­øÔoĨ‰))ÉKė/~=!e¸ƒ!*4"5ņēøXYžØÃ}•­özåU\HHũ^žš–œxĖú2 R’ö KƒãcBûŽOM<wvÕ<Ÿˇ°ąK*¯ÜTœŽdĨ'—fT^Å*ĸCōÔ›Îrĸ“ŒM>Ŋlę6čZŪŲÕķ|ŪÂd ˇIøĒß´6c4QėŒe­öäŲoį›wŒhuÎ3Ą„2Ė {c7Ņߖ'BĘØˇbgĻüĨĀ›yö`âûĸ•âÔqęwDš3ÆŦ< Ņ– ĸ1ÎÄŠ”đ!o ÛAÉâ§šKyáĩ…/Ģ­˜–ûSŗ"@ü0JypF5d đP7ņŽĀ°Rãž4!ėH'į›'ėĖūbM‡ˇ“ŸåÍõ†ëVÆüzöåđĻUĒeYjāEōf iaÆę -sWÕjxžû´×<°œi˛ĮđN–—ÛŽhÉĒäĪŅä+N]ĄSÆø`аorå°#>ČķŽĖ ‡kiĒč6ėĮNO›ĻËīËØ]SÆøPxÃųš=jÖī}Ëŗ2#?>ū5ŗ¯ĨƝr]…7"ŋPsČšÛ : ī’{Eīœ#ļ†*Ÿ¯ØĻƒ‡}gčƒ(ĶįܗČGzĄüCŪ͙]wxĶÔĩ|‡RŨ‡ũøųãℜ1ú”[ÁĖãšÃ)x›öVûËR!äg;cKk¤ßęÂb‹„'3¤W…Œąâĸ°H). eŒÁD)Ât‡XéÔŌ%Ķëâ) ʔN'cäŨ˙TqzŨÎ|CÚūžis ųiáá­0ņœČāģ”ĘØĸ'Nˆ+î—û!KœÜ"­[í–0Ū'ÆXE)uĢA4NĘUoIĄŨy‰ĮéÅî O‚›äzĮų3ÆˇÅå}jŠ/ ’O™-•æ¯'ĮJãķxiÆų\å”ŧ&Ŋ&6(_%zUüXģhņĩ\Mŋũ¤=ö$&üŊŅ„#Âaã…?oĘˆ“…Eū#:ĘÖų‰ÂŗŒŪæŌL‹_&”V l—+ĸBĨš>š^ÚhJ0GLŲtˇtFPüĖY*Ũĸĸvé'—ëÍæŨæ “Ÿ˙ÉäŒŅK|ræ%fÉkĨ >”€šJŋÚË[ˆæ2vYúËŧ¤ĨI;ŗ+Âņgž”1ž—~ ×wž{…]īeųu Ü#ėŦâ˜ôSÕw&Q[š:s\œ“1„]+œņĄųi’ÔŽQYx¸š'™˛(Fj™đĨŖį„åĻJ;Ú¤ƒ7‰¤FÛa÷ŧ;ŠōgŒ÷Rč;áG0e֑öŠBÂ8ĩWØoŸebūK ĪÚ-dÁ¤\ũ„„°åŖ€)RÆxYH~˙ y.Ļc­…Ŋéî“ŲDÃå$ëāU)k°ēÂßs{Ŗō¤ŒKÂģÛ!ī ĪRÖ]ņķišAYĮ„Øs#:Τw%ž‡ ”ũž1ü…e—ˆíoäĪD7dÉGDĄ™!{CsŊᜌ!OGÅĖÚÖRĶ㑔}ü€ÚöåÔŪHŪėeÂä=ŊŽníK#ę*}œąÂ[OÛ1ŽZ2ÆoD×kG”°ë”đÂ9cĐíŨÂ&:ĮĖcģp`*ž‚”āJgĶãmgŒ ņąĢ°—úæÁŒ‘ŨˆąÕDb7Ģ\CØ˙í`ėôČ+õĨ×˙öeņâ^n8Ņ^a‡“M7„ŌĨķ\ƒōdŒŠŌŽ)jŖüÛXÎ{‰N0Ö,Í\vvÆöKģØxJj8C$üæ^IPŒŊp;Æ(uFÜE¤Ãé•äõBhÂĪ÷į¤#ĻĖ÷váësV2Fzš 5CƝđŒMŽU„Ũđ-j øņfė}ĄžÖRÛą1ļmcŦqzÄ%Ëގ/Æí•ÚáŖĢH’W͡˜pDö­TĀHKuĮ)ŧ¨Ô’%< #_ņüŨ¨ķÁ3bƸË9(|<žđė`ąŖ<Kb;xwË'~JØe'Ë{xÉ˙ŸđKąš‰ŌgōŖ°3Â<¯Ãõô9ŒõKÍ!$…†Œu‘ÛˇR#wÃ,ąÅ]LĀé?ŧYŨÔō-|Tŗ‹3eŒéˇŠž´%ÖČCxŖĩ…‚zš2FGŠ´’ÂqxœcÂ^ČËvÆČŠ ;‘ ü°žWôÁŒ!öģŦŸBI•ķd a—čSąŠĨÄÄ3L:zč˄ŊÂŽ†]÷+ߛ~jįÎŦÆŅl)gd‹g<äŒQR؇‹î{ĖCHâÎí ûXøų+O¯ĪŦQ&˙¸Î)NČ•Į ģȇoĶ„ō™—˛LÚÍĨ——vƒ™ô“9øYË*‘”c +,—*'NņXd‰´ ]DtXxōA˛˜1„ Ž AĨRš?ē—eė!'Փ2†¸‹¯"•RQĘĄ9pC֞˛’Š+#ž¸ę&ŧų†âœ1R˛Ū'ĮØOnų’›đÁ2á ¤˜ôÉ~ŤÂ[Šû[nųžG´›ÉŸt˙œˆÄ+SڐxnĪbŅ4iƒũ&Ââ™ÛŗD¯ICę×°\ĘK>Ōrό!ËĨ¤-f ņŲiq)cYt“’z+˙ĸÜEßÁö—)„žŖdÆ&ų[›9Ú˛÷ŒhʤŒ1åĘâ>å‘Ęø,OÆh%L']-ßY]˜¯Į¸"îYã„ßöĄĄ!D?6G>ß.ž ų;wīØr[øy/ū—3†š÷“—9c?iÅ,‘,vųL ”ē|’ôk™}ž?cŧņnZV3á â•äŒQcôē[â‘L )cL^y!ƒëZVJ ŧrõęÕË!bƨŸ)¤“°ĐlJöüŦõī;Ŗ„Ŗ­ÛŌ™ąīíÃ÷Å÷õ™ø†¯Ėj‘įŖ{ũĮÕū)ŒO¤ú:&íö3„ÂB…OÁōË_ˆÛ‹ÍĨK{i“øa×3Fš4Ò1ÄũåŒ!ä'aũ4騋ä“SBáË-Ĩm•3†ŋÜ."˙mȉ¨Î/î¤åų|Är‰ą;”-^Ę'ŧãˆĐ°é|’1F‰KX2Æt–“1ÚĖÚ•-ũ#0÷•š er)c›:J(F8Ė™iퟀ›øMßV Ļĩí?˙KØ=ĩũüÁšÂN,Ũ? āö™ Rŋ›ËŌ9ņ$MNÆv4™íķd 6+MjúŧjjŊ{& ŋŽŗœ_ī^bÆ8%ŽŊ?Oƨ%õbúčĸ|^_Ęæ+,†æÎ]…âåŗ’ŠącŌûų3ÆÛėø™n1ųcĩØV,8PuKÆ`i””Ķ_*R:ũƤ†ų5bĶ…ŲgŦÎa/™%ĩū•ûˆ×cŒalNēô†/”SõBą1\ØS+i§.3Ë)ėEËr'é4 ¯Ū›ûGÜw3XË9Œ•žˆŨ¯ŽĘéHfʡ‰&ŠOgšģˆļ ų†r7{ˆâ)Ĩfŗ,š?YļĨôšRƐŽd´’1ę^?‡h"ŋœŒ1P:p’2FÎQڞ坜ŊPԃsÍí&—ÅŖÆū0gŒŋÄÃ(­EیÁęūvClŦ ˇĨ|Įb''ĸŲōôU"ßíÂaX$+(cT[rļ ŲOlũŽĮ8ˇũžÜuLĘbDwļ <ĄxūfîdŠ•Ļ]L“†Ŋc _ų<ƒÂ ģ­ûšŸī‘vį_Į‹KŸ"’öđŲŌhÂDYöƒ°›`ōU ‚k¯Ę¯‹ãN\’.•Žî+͸!Õ1YšîNôONųũ’äUũÄN¤â%âīcéĘî#ŌąĖ6ųlPg2ŸF­&^Đî}š÷ˆ[ĨZ?J?홸G.+í2Ŗ~‘Ž„Œņ‡ØúōEŽwgžōlžtŨë$]Ļ-eĩbĶ1m;D™âEâŌ…Í‘Â4N:'#ē\ÎRLŅÃâ ãĨ_ÍŨ™FÉכäô¯h–üĢ_H8b÷×Âû‘^' ˆŠ+‹J† GZō%ë—–?Y1UĪŨÂ\QlŊģãΐķnŽĄĢúˆ¯Ü™‘o ,ņęŧlS/¯×.Ik Į_ĻC@魝•Ũēcs¤¤øĨØŒļ鲥x1ŊˇØž/7J Ĩ+&Ĩd ¯ÉĀ#uŪ°^Ü#üoķÚÍyîfŅvãēųyŦ1Ī{ĸđÖûwáĮą÷FöŸ)FÉ3„5Ÿgm\']HÁzũĩ~ÚģĻ×7‰;ÂIë7HƒY|ø›÷œMe°áaī!š+h>}ũĒĄĻ‘–¯Û(l4Ę{ú“_HG ƒŊŊÅ ßŨ´VjugW¯˛ŦŪuՊFl´÷æœÂ{o~\ČSĢVˆ!lÚ Ūnåڞėņ ë<#f /ÖwĶOyŪāĸuåũgĪ ŪōPāí–x{UbĻ8VfŊ6l >Š6wc?öÁFųjÃ/V¯ŸšwÄŏWx ‡>­_ō"›¸~CĶĢ_ŽX7öĄ<Ëąß7Hũš†nˇû`Ķڕrĩ׉׾Ÿî=_l—īīŊF¤ņ,īYō¨&k×n5Æx{Č)ėŠÉŪ Ū“Ļúlø+ßĀį=ūúĢ=+ŋiũėÜ/>ŧtŨē?,ĪŪæ=_>ŗļxŨFņLëˇaũ¯BZŋAÚĨĮ†õ⡆đ!/|}ša͎ŦĖĻ ũاk–ÉÃH ˙¤Ņ ĢMôūŗ3ĐŨˇ¤+ãŧ¤Ķũj2÷Žņ‘Ÿ×”e)DŨė/ėd °íÉŽ¯ķ÷ė9Skø • ›sŦYŨÄū2`<_Ī?vé˜ötĖÖË×PģL§=ڐwā^ဇ+Į;pĨķf `ÕKČV´ŧČ;ãy cŧøyįj–'ģ´Ö; Cx´īŒ'_Æxûh2QÚÕ^ōŗ­)D1ÃW'¤q %oƨ•HėO”-ŨNį4QĸOÅáŧäĪ—‰V36,“^`l<Ņũ&ŦyfÔ评|5Ē•ąÕčž˙ŅßÕiLĶŲ´3ĻE3á–ÎcFĐÅę6Š1røāŌ‚ŋҍ~úÆĖ¯đ9Ų“ĻßĢŗĐ“k Kˇå_0ĪB9T A4J&ÅøŨĐ+õUŒĢO¸ŗ?$ĻBbRRb_ę钖—{û'g¤ eau=“ƒģ ÷t~œœ˜ō¤zöLZúÄ%%%$ãžËЉ&\/ÉĶMĀŧÎ$%%‡oú>%;Rôiœ‹f Qč8ԓRhmƒŠ ˆFŅ+FĄŽ=–¤Ö_ßEȑg‚ģn™ ‰-Ē×ĒÆ=uŠÜNÃžŒL=ƒC‡Wųä*´f!“nw¯ÜŨ×Ä´ûwģU­<.qO‰:Ĩ÷G ×K&í*SĢfąmp€{ŸÅ¯ņá č'~ŸTŸ,ČЧq^š.Áw÷†Á ŠŽUP1ķœxMm <^1€Ņ€tƒÉ„|5Lį[™`t’Ë5ØĄsŒĢĖžĸޞOŪ$y/{Qņ°ĩėëÂŨ…ēJqņĮôc‘(ËĄ&ûōŊ-„UūĄiöŖ>—% L—`ōŅ?|æPDG1ûëj[€äi ŊR:ĪK9e&ÖĒ„ ĪHŊ|$^0jÔ¨Á÷_PwŊ´mC¨g`â?Ųn_ÎÎngđī4Ž—‹‚AîŪHī˙öjŧôR šŌoŌ*¨‚h”Lã{Ėmi”I1ŧî靊 āCŨŊŌEĪŲī¯ąöœ?$LŌyc'¸‹ŽPņ~[6[Ûôļ wiT A4ŠšŲĩ™áŠŅIœád¤ âTKŸl.“=ŲįJč úc,Õ9Ŋ/YiŗnׅÆÅ’”jŌoÖ(¨‚hëÃ=Š8!Īo ž›w ׁ DEžėķrÂ[ėkßÖŦw—xĘŋa–xaG”.h/õp}܃~ūŸŗKCŧMšĐöP{WûÂū(~áG^:P1ķtęĨļHžÆÜšīĖ´†-~eĖŲļžĪÃē­›‹wĶÆ°AķĖ]-8ä BfŦw— „Ģ]šãŽĖWˇÛŸą­[š1÷XV§UĢæÃ`<ķ† 霚}„á&kŌki>k>>ڊ~€'9–×@Å@Ė3w EÔ$“bėJ2mEÄofŽ.ÂXCšn˙5ūĢwųŋÂ!ũå„l7~uđŊå!_ļņ~„˜Đ!ĖŽ]âŧË´āwS— îHŧ¤_Žq ´šB8¨‚h”LŠŅ |~czkŲ&åGĶÆ 6jėĻx§aãwôž2M\ūC˛S“ŠC“šĸ§nãFh üh׆ŧ!„ŊåÖČ뙀f&ג|.M*yWÄũ×\ *‚ %ķųŊ¯'LSËYëÔ6Á^_1ōÛél–ĀķË:Ēm"‚ ļõ ž|MĮ“HķG~žŒ Ĩ–ãĘÉ08˜ģ*€Ŗ+Æ{ĢÂĀ)ĪĢmĻŖŌëËqD):ĩĩå‹ā Fø*E’Ī!ã\ÎO\1††XÔ ŽGqĩ-uLÂLl܆ šžķ­9[1X§” 7ËŠ!ĸ)P14‡C+ÆLĢ ļ‚åôҍšÃ‘ŖĢÁ¸m9AA´*†æ0ˇš—#đB’bĀOjۋ ˆ$n9ŅŲNB€ÚØÎ,i‚1ŋĨKîķĮ>ĩ-@ō2ąĄ5¸ņDĸb€c/ūA<öJiĮĮ(-U0āēÚ&#"T ÍḊ!ĩS ´’Ú6#"<ĢÞSjĮßÛ7kÔqcģdÅH6ĩ…=‚ ZŜbü|‡}'eÉĸOXˆšĶ`9ú•ą¸ųåN1Kˇu{ņõė}ŠF€jü<$”ŪÍD¤ī="1uššûŊ4+îū~MæÃēwÂ0ō>ŗtÛbXAN@g+MÔQ,ėčķYÜÕŦ߸qãĻfŲâ”ŋ—q~í-ØĻŋ°VZ‘´īА)ĸŗĮ9s;a|ždüi2Ėqc/*†ōüˇĮ˙Ô6ÉØSŒ“PšDI{›i ߛؚjÕũ0ÛØˇ¯ô&–nš ķÉa¨kUöƔ~9‹ģú‚š­¨Î$|D?ׁáĐŗáV¤ŧ"åŨPb,NĢi&V˔ķ4įŨ&ÃP1Ņ(æãrŨŋwģššU'¤Œ›Ž{žtÃō†ˆåОRÉĨWƜZ V͞ØÎR¸‘î &RąĄĢQĻUŨę‘*‚fjnt‹ás˛;͐š‹iC'@_úō_Ž9_oĐ ĩŗĄ[ ęŠÜˆnHÍãˇočJmÎīÚ Am~SW˜Büfqg_ČÜ0rĄíĒö]ŪŖ†%-āÂÎō¯"¸ÖyK­r#ąØu]¸RĨĄk^ˆĒ°˜DŠáV}mŌ§õ¸5aĩkÂ^RļŽLŠ ˆF1§ŋSŸ)€ú!ũĮ/ũg,¤=˙{c á¨.Ū_ņw€ģtË}eOlGōÜ0ĐõÁėNŨ+†ô|D}ĪŌųĄ­VyĻCđP1čgĒ{“õiė7ŗy/čOv͝Đ\ žûāfÅÃÔŗ‡†œ'ũégâI Šņ!)UD—ZŸį>nō™x0āĨPŽÛXŲvˆŅę\Lx~&`}vš, ĸŸ3™˙ę3"*F=]Ĩø ķ•:|ö4į"?_ļbÔ>â"œU› T A4Š9ÅØEČßĪwËÆÛŌBâgvOÛÉ|ˇRn:=A€{CZ]oš‘âB‚> ?0Ŋ˛­4§í Áí˛ƒ:Jģú0{Š]„ }Ļ}ÃĻãŦKlJÛ ŋKz—ÅsOŠf¤×cŽ[Hˇ…Kí†B­•Ëiy—ķk÷€ōöĮ­ëDōi’2ĸņ\¸ÄŨ‰CŨhŠZGķ.ĩAĐD§¤e“ )ŨZļi]ˆy>Ōבs˜ˇdĖ"ō˜>#Âqc7*F.°AÔÜb|´†‘?ë|Ë Dįœíčg­ŒÕĖŗ؎ÕMÅũ>ĖČ~Æ7g°†C€÷´a’ō#ŋ‘ ãUũëcyŧ  “pS}÷b¤Ķ&ƒA‡MOÄŦ˛§i´Aô,åéŨā v:Ļ¸0&GÍMųņûŦŗˆ^1@đŒøfđ†Ä_ôâĶyČ>`“/ĐHrLøžË7ōč/Ôø÷ųSj‡ÎSŲ)åēqŒ…I&ēčÖtĨÍĻLEöŸ1[8×ĘmötĘlaĻų,îN4w›É<ŗšrO]æž>MčpkÁŖÍpåWžĀ,a0¨ĒšĢ‘gV î)ų÷´ËRaēQH™ŠFž*Ü3S˜rPv2ķ|ī‰Š‘ ôr‚3`‡ÅēõËA×WDŽ^ã_g^°ĪĮšįŌ-ūÕ&c’éÛwō‘ī§—Doß}ŨĄūMŨŋ°ĩ.ī× Ÿ[mŧ ;ŲįMŽįĶŠú„ōšŽđ1ųĒ5ŲÁ'>=ēÁ>_ õzÕmüŒņđŲ§'īRĶM:ū7n€ūXBQ1ZžæíÚ}Ô:y ķy$.}Ȑ ˇŠû÷xv%lœ˙פÚē[˙ģrËfÎN‹â„īđđlÄ]]¯œ§\éĀ=ũpŪéW—¸įîéxūÂųķÎ}bíbWîé|Å(ڇžÜ#Ė>k.xúĪ€{.äžĻBH?îipá Ļ×ū—{Æá4ķ\ú‘{ęŸdž3¨âÜX§+A?np†­âu3/÷'„ˆÅx&(ƌŒô;gŽ‚đĻJæ^ķøGØ‘ȧŋĶÛé#jq1ڄyÄTâcđžpņ⅄‡îÜĖīœ—J3_ē™ļŸė>=“œ0ė (*Æ×ēęŽ÷Ō-žG|Āõ'_ę|üĪ_ŧx.Åįâo<ĸ‘btÔבK,Âq{ĨvĄb ˆscbŦŠ×w ÜŧÉŋÎŊbŸ//sį]ū%(ÆWŲ×R+FØĖ" mŒĒėP×bŊ}…4ųėZãē ŸrlFbr—ûŦĮĢôŲ‹—.]ōHxt‰]ŸûƒßšžßY-ÎųĀÖLiÂŊSTŒ60ŗHĘÛÜ×%•@؊=<™ã4åķ). IüšT‹ZŸrwŲ,o‹á¸Š#ßâäXŲÆH(Ģs.†6ôĶ5OÚ˜Ņœ~6Wļ}Ä'˙˜X”ąCč•â]X›xÕÜØZ2ø‡aÉ|¸œˇ1ž…Ž&M¨ŋØÕ ;yâ\bĐ\BųŒ¤7<ÖDožûž!î aæcøAgŲįŋ7ūũZŒ0ņDúyÉ0ŒĄĮxüíēd`ÎĢ­^>3 )‡KS¨>ėH°lwáųũ T $'\ĻXŽƒ abTCbķj•EŸ_đŠŊüÃøö…‚‚zWč,„”÷pŠą ęgŊŋø”× ũ¯Đ O6‰ulŽ_éočĪ^ļ‡BÚĐĒ5{ų< îĸ¯Áĸ2äŨī ßģό¸ËĮÅ-0&—wÛ w‹æ§UũZ™÷"o…ņĩĮGË™Ä‡ú˜•?đ_P>ul…5 k |šĖ‡—ûŋ2’”cđ]ųN—aĒáŠN1:•‘Iĸ6†„ë=t‘ĘŊņSłÜŲ–—˙*ōŧeģ÷čGsP1iģ\m <ŒŠŅ,-!“B› ŪŽ&%&Ũ_øëySĪM]OËČč䄸YYī– |jU* :'$%¤=1)<R7‘Ë'‡ŒÕwĪŨ­ /É8CS­ DHOåKôļÄ$&$ŨiÃ/wNK Ūʛ<ü’“^ūõŠA>yä6_đxĀ §ßP% ŌRÅąķŊЉ w+‹,0œ“ƒŠ ˆFąB1Ū3fÔČŅc‡ŧŖģĐ~œŅPCĮq ž†¯’íūĘŖG×ĄŅø‘ŖÆōņķōÃÆu!˙4΅ļ>ž&ŽÍ×´‘Îãh&Eû9Ļŋ)+ ŗ¸)Ą`Î⎛Ž߂t3öĶöŖĮ×î=z7iՄAuôņ5z\îŦ4hôGÂÅÚĮŒ;vîŊ8ŪFx§į¸L{#’ÃPyûī•MGōQO}H˙ą´ēÍMڎŗnĮĢĪ#éŨyJ1ŦŲÛA­gw;7Œ—ĸãá:“áĻ—ÛĪ?ÃôĮUŒ=’#ŖĢÚ6#"…Ŗúq Œ+fÖôi“~ĸãūoYƒz°y˛ĐWĄŒ=`ˆÁ㸊ąT˛bDHŨB!¤ŠĘ$WPH1:gˆU‚RoåŠP.áҐĸ¤x¯[uėĄ;\$ī>zŽTÆŸÁ上ŅV˛bužņĖBâŠ+fzËׂ—7ę*ī÷WŠÎ‘‹%JH |d.hųŲäã&.WÉ0Ãr„÷ĀHVT1ē'ũnÃ]ÍũŽūWvSœœ÷šYŽƒ Ę`N1N@’øĒŒÉ°|?5”˜‹ Œ’x!Ģa0šŸfą×b6,!GAØĐnü@?ß ¸C?Û õņU0ņī5Æ2U?šs|č­SŒãq’ %äķėĮxøˇ‰‹-S.‘Á°[į-c aŽŠî0Úļ'|*¯%‚(†9ÅøšhՑ”×-…C/)Fq–ÁgdWēéVŽS Ų-îxë1˙šœŪ”57fpOÂÚė÷øŸdŸO_ØBômŒƒa’ ĩĀcSŠŅ(i/Š ëu^‡WŒwõŪ‘đĢœĻ ĸæã č+`B*O¸ÍĖŽÂfēčŒk{ āŸë'hũą¯œú?H6Ž­—&Ώa[ˆ˙Į×y žfôâs˛% ^}Ácūđ˜ĻVULz.|Aö'ë Zcf×ē^0ü‚{‰ĐÖđd‡øšüQ“_ auĸ!ĢØņ°¤đ1Œž ,9žAŖ Šą@7“‡ukųŅīÅdũŧFĘÆL`§ú2ĒAŨĮŠąÛŠû$ģAŋxyī+ŋ‡'Īęˆ]ų”o`øß›7qvđ9BŖ7§ģĢŦƒīvŪ/9Ûöđ(rG6KQsŠąÖ[Įtģ{'>›ŅwämÆģŋøUÄ?b@¸;xI"|r˜V]ŨŧÆLÉNFõ‡É}Å/ėŨûƒ•} f›–íq‹Vīn=ŸŪ§ž7{÷:÷4eõlA ū„cĻĮˆG&ĖNk@eč „“†ö€–d˛¨T‰ŽXīājΗĀNE~Ŗįq(EĒõēËŽMŪÛV&ą¯IlDPŒĘz^‰ūjĀĀūĖãâãŨŖų īąģ´Ķq&Ā­ÁĶ"cšž•}õíxö‰';ˇĩÖā>+@×Mw3nՀūŋ0ėûŦNzĮ:ęį•.á)Ä,&oÃŊ•ņ÷ ûĢ;žbėHŪ阉ßŋ”Į’ŧÁÎFj[€äYĖ)FĪŋhŊEpÂ+d7Đ÷ÖŋäHũøVi¯\~íø&ú˛oĪhÁŋˇÛå@ wGķhËÄņĢD6ķDß+u(\ëš`ūul!ûü˜)oĜfžUŦ ãËébÔ+|Ã>ŸžaŸŋŌÖR8ĖCŠ—(-æķ-!W ]nŸQ) įu9šbTžw§žŊiŒ_%‡)y„áåÕļÉŗXĩ#ÚoÁHWRQj„^1îŪ$ŦKEøoŸėTėŖē€Ęã.ɏ”¨ Ãô‰įŠá%*ƃ–\}đnŊúõKž‹ąŠ Œ–áģˑ?GôĶ_™ Į6Žŋķ´7&ŋËž*Ĩ2ģr…ģģDŧáH„ūŪņņ…ȧī‘üāŊŽÂų{ËØQåר鎁~Ģî!zÅ8—ēzB;R˜kI 'ŋ$ŽcŒ™ü=įĮŌĻ,?œōĪÜqâ¤SSŒĄQ›dHĨéŗkeeHAEąJ1ĘíđŠÚĨ?,R¯Û#iúg‚MĖ`UčŅPîię™öōˆë)Ė+Æ^Đe¤ĻĨĨĨ‚-ŗ•îņw{˛.\×ÅŗļŠ.cÅõŲWK&V.pmĶöí[BŽo†ĄŖcB§Îô~āÅî(´ú@Â,´u°+R_÷ƒ,x™pŠĪĪĄ&}ôˇpĻ^Š,”†Ec)Ǝđ!–#YÃī!ŊåIAŰF1ŪNHÕNsB¯é.čŖ„­?Ĩ“…‰Đ–čŖ| ī úĸ,ÎâŠQX^8ŸŠWę7C ũČģûJ›'[ÖMų…÷įÍō„)ē0A1rÅ( ŗŲ×B(DHEŋ€€˙”¨€‡<W >ĀũAԘ?“ĸú 5ūˇėk*4§Ÿ iŨ ŖEÅú&)×öģ‡Áė}YĀ($´ŗzôëËhjš20Ōāq$ŨrûN šŌšŽ'ō!ˆÆąF1Ú C´Ÿƒ‹xA¯S|„?š)z&@ĸSŒ‘Bģ#\ė•ĒĪæÍyUžš=¸bŧ¸+„üŠĪfđ•ÛMM¯.ŲßâôÍĸĸ]ĢRų1z-"Ã?ևí`ō@Î ĩņ5_^ íŪē žGŲ8†¯0€ãģųáBq“ģ?æ‡]哿ĮlŒ¸;Ôp÷`h ē„sÍ ÅŗIḐČkŽ rÔŲĩÂv[Žd5­ž_ÁŊ÷­a\Kĩ-@ō,Ö(FGˆû”ŧVé ppÛųČĢH~îūá‰Q´Il× A1ZĨũV˜”bŖtâZOûåcîÍЂXˇ…ǝéˆđebīú›4â"Ü´hč\ØZ†{˜gDÆõˇÉäŦ0Č2 ú“7Į§ ãÚmāĐëų‹câœ{ē×5)MH7qVØŠįq$Ö_¨đ?NŪÜ.ŦDœ—iŅF~2 t'ŊŒ.@J-LûŒ°^¯ĶHx'VŒõŅ#,G’Â_‘ŸË› s˛¨ģÚ ykãSxūIŠ|īŖÎžęûę<ķ´€ˇŊŊ¯°…5FBČíہI¸ƒzÉˆķ‹‚ČÔWđ$–Ã̍”ÜY)|#3 #piđĘ/üü?ōųā~Âõ+ĻíŨ'&¯gbnF’o4ėbÎq/üüũ}AœzõüC!60ĪhHĶ7Ęß Čˆ ļŠšîׅësā 9 ÅgāūQb/WOđņöö>3˜{ŽųEĀ+˙ĀžĖCŸT˜_3¯†§&ųÃõ”d‹[ŧeXšá8ŠQųæŲĪЛ”ļČr$AÔÂĒ‘īš¤ķę5b[eíÚĩĢV­]Ī^?^ëîžî@â3ÚôøuíJ÷šåŨW¯ZΧ*•™ĩ~NC2}ƒ8äŨlÍ/úä&¯˙åõĄk×ĩ&īŽZģzåZ÷%úíÛ/_ķĘ/ŠdMyÆm˜)ô^ĩu_š†šēÖũ31d”ûâj_Ŧ]׍{ ,øÅhú\÷UĢ×w=“Ä(´¨+š’ĘîoqwGøĸõqް[ÆÍUîîūV+Ž^ŋv Íj[mB~"–ŦŸđž˜ĀkĶWBfŽYkzŗõ€Ņž+ĸ}"•دJÔ%REDėÚí|õKŅ63ĮxN@G0Ėä9ė%|OūVޤŸ$M5ō;†blL‘ĨđŲ9–ÔJ™„ąģc8AŧöåĪ øÄčPęÛēŌÆæ´ĄÕTûüŊL~GPŒüOī[ģį˜dÆÁrĨ’vÔUÛ$Īb—b4ƒSlĸå)¨l1ŽcSsLŅwõ‡Ŋlˆžëũ¨ JäåŠŅ7IŽU{æ¨îŊ‚É;>SæĢm’gąī žŪ|QÚŨĻr™ŖQʇąbVŅy{ŊāÅ>o÷æ&ŅžblKRxąŨ†ŗQ{Omm4ōĮanō˜ĸejšš60Úߐ4÷ã é‡DY‡ÖŖÂīr–cŲĮäØÍJg ˆtđœoÍĄqÅļ3riūđļⲄ ˆTP14‡ļcS„é=„åæ?Dā>Sf("û:ą’g¸/ƒÖˆWۀ¨wÃģ~nå5)euneå`ô÷°AÛšCÃmŒA¯”XĩgŋĢäbvŽC‰šj[€äYP14‡vccÔHˑäädhĪÜÍAAÅĐZUŒŪ>˛íln-?¤¯Čí,1*†æĐ¨btM΍9RYŠpķM˛EÄ$Õ6Ɋ6côR'ãŋēĒ“ąvisĘrQ‚˙ ˜2ū; ˙@Teüdö+Œ—Ļö… ŠAÍ0(z‹Ú&ä@Ũ€›īĢm‚ ÂŲ:LmræĪ(v™å…>Į5ß‚hƒŠwÔßGĘS#6¨m‚ŠTû;×CD1Eß(-ė#e‰6Ī<ĢĒm‚ Hg}ėĩM°ŽŋBōtĪ‚ ˆÚTŊö¨ēÚ6XËÄwĩM@Éģô Ũ¯ļ ¨˙üÚjÛ Å˙Ŗļ‚äyÜãGĒm‚4~KúTmTáŦæ§& âäŧķØá6Ÿ KÕ6A ŪRÛAō8ŊwĒm‚  ŧĄļ ‚ y­ZŨGĘ{ãqtA\¤āīÔļÁVFFįåÕ|‚ šLī8-ī#e ×û75ģmĸ"T?^SmÉŗlˆ˙Vmėc{PoĩMČM u) ļ ‚8ĩ:jŽA­KęãmōŠ­ĸ•vQâô0ƒûėI›3ČlYsfXwĩK ĸ.-?‹Hķ$_žOˆZš ēĻÚJū1g‚ãr(ĸĩDú­û\í˛ ‚¨FšŠVT•ÕļĶ~x)ƒZčø§ĨÚÅAQ‡/ÂŦŦ(=ŠĒmĒ­9)Ŗ^PŌžSģDÖSīWĩ-@ÄyžauEųČA‡0Ūŋ/¯`PĢ]&ĢŠ5Km q:ĻI¨'oĒm­m\•]0ύ](AÜæŨ`IõäŸjÛk ģ HĢŖvąAr™uŌęɌĪÔ6X:ĩ¤´ĸŦįēÚåBÉ]ęÅJŦ'/¨mątūQD0ēŠ]0ë¨ëā .Ņ ?J­&ĘĒm˛drZhb{Õ.˜utsȞD9yķÖe†įaan˜ŧl%_ūáyᏯôŪūÎ{žZ%žIx^ņj*1šŊŽ\Ö{Ēy]ž|ãîœwí˛į+îŸpãęnÁuËķ„前Ão\=(ŅB_ßôRŅđ[Ut5?ZĒAiRōŽb4žĶ’—Ú6KåŦÜBĄįĩ‹f Ē[ŽãÜÜøŌ\XVÅøîND\‡0QpOP\ČņwNyt4<ėũxi>BžôŠđŊš@ ¨BŊņŪã舞ôûæõ;ÍéW˙{11OG¸}§3!î^÷ČܗąQwú°ˆĩ.‡ĮŊē"všo ‰ pĪÔ+EãRLĻŽ#đôĻ [ücâžķÕŖ'ŧū&û"ã^Ŧ2˛-›btķŽŠķûG¨ú–žŒ‰‹xš‰ģˍŒņÃA1*ō‹‹ zdÜŨ|Čë4Ų÷rũkĖ×É+*Î˙L3æÜáu\žĐŨûÆ?Ė˙úú'1ą/¸ōģ:܎‰žÕéWŒ‘Ū7˙b×VûÅÅø­ÂœĶŸEĮEúōŅ #ÅXsë6o!ėķōę§ËūŖ;ˇG´ŧq‹ļŪëÖ<úõ Ī’*Æáq/â‚Īļ":Åøč΍Ģ,Ü+4.Úīڇ„+ƍ OccžÎäÉ ķ‰Ž}y¸ŧđžFĮ…?ûͨŦô™ķä?œ~šáÍ~ŊŽŪŅ1/ftđöJ˙~n-{÷dX\Đ!.›O†Æ…Ü"ÜŧüU\ĐŪjwÃUēb8Ü"ž3ōʄŽĄ[—ĢmĘĄo‘Ÿl\ŌÄd`Å8ė}āę.H›)” ū†]’RĶ| ’QÂ_NNŠE<ûļ‚>& ęDČāM…Gƒ9°ŌØ _P%Oå!đ=‹|“:RádVŒ#§øÜİԛ+m,Ĩ˛[ØqõĄzžßÖ`U o]æL1ÆĨIaáiŦd¨ûƒ—´dŠČŖl8 a Í6$ä ą'xâW¨į[!•´.ÔíEĸŪÖãÅaü÷`?Ûö0/đģ˜W RāË“<š @ÅČT =ŽĄy7ņįÚe*0ŗbP_xGļoNz;Böŧr#[Áx|øíP€-¤ ­8˙ M—x,ĶwׯāšÜßŨAđR÷§¤­\V’Æ´e•­ņá'R…Ļą›Į?ŽņCĢö_iĨۋLa ‡ôYQÅØ7^'íĶŌģÜgŠŅ›WˇĢ¨0Ņ`VMÍ%Ķju¨ūĒGŽSsŨŠ=Č8€;„´I…ĮXų‚Ëđ ;Ā•¸WŒ?Š.ä#ËëęzFƒ‘ņ@Û8LQú‘´ú¤×iĀŊu‹uJT.U`ˇõPÅ<ņÚ;į+Æ/Üʅ†>ā´>ûn4W ČjmažÉ>Âc‡qÅ ž˛×xtA1žĸRÄ;—~$oĶæ"W úûOūŧše@dfÉ9.ĄMÉg\ŋEĻōŸíņAqĘũŠF“‚ql0Ē:ÛŊt6˙%ƒÚ’šôĢ1Ø_N>¤?aEB&$!#P1ô b8úäsLfV ZE Ļ_?ŗuGĨéášA1æˆSj’xíA!+ŒŌúYĖį) ĻK„ú~,*{i_Čj˛æÂģõVī“'ŦēĒC}jT1~'l°lĐŧQsjž0Â& Íäu˛ČJŖ?NĒ?°>Ģ+méģņ˜/øŊąĩHuĒlĮūG\1.lcFõ3tÎ3Å`MZ#>&ļR÷ŋ|Ōuq~¯ėĢŌ7wæģÄ*Ö¯iŊú:! Šņ6Õ Zå÷Ūē mE `6’;eVŒ#üiĀ}öL1ž˘˜]1˜r5M†ŒÂ™xįRËßáŠđ&!¸’m˜Dī¸ĪíOB?ܔą.o û}ēđÄBÚ"*UŲD˜…%(FÁš!¤˜đŧØĖÕc-VA›øĶrJP1ėÃ!XĮJ˛uœĻĻCgRŒž´BeßuŌ!Ē­ox=°ÃX1ÄĒĨF+coöōlĖęķ|*Ij-"*ÆMöFJH•db°ô™ŦÖ[ đ`ķŽ­gųË2ËēdĻBfÅø“<Ĩˇ_‚ƒĸbPzn|(D bMBęŅzT_ÕgQ ú—~ëŽ-÷–đā.Ģo“ Z!§1˙^^˛™džŋč{„8´rdC5ĸJĶ:ú2M…ĒãzŽ|y ;NŠŅ‹5Æø0ôCƒb4„ĖUég‹¯fpe0VŒ\'Úvã@ČŪ•U1X–Š}“I1üčc?"6ˇÖ ēū)ŸáK‹˙÷ļ[žŗ..ü6}œõ`ÁiaûnĻĮ\1Ž ?Ô{Ą:Å`˜ ėt2™%°cÛŪEEĨĻ+ øP1rC*†Q"’;fŋœI1†}g¤B|ÃĪA¨+Fˆ8OĄÍĻ”zYĮ•¨>|ÁY‚N1Ø0ÂZMáķæqËĄ÷\ŸWŒí´*†ą´ļbŠ1üŽx áŠÁFÅé­QúŠ YÆ1üõYė¤ĩ°n;Îļl} ¯— oÜOųå?‚*÷ĻC\™‡úTū䊱‘LĐ5ø‚‘^Ŧ-˛Ch@ą(:Åhf\žē)(Ŗ˛Ė• Ō& ŒFŖuŠA[hG¸b°qŒņ:Åā“`°Ö ąbôļā>ĪÖ<Šŗk]yöÉzû­8€ÄíŲĢTY#†+†(Rė ŠQ•z|دŊCŸÚ+Ānn€Š‘¨zC1ŌG¨mĘÔéĀ^ÆieŸŅ'{`&ÅĸSŒˆŽĢ[ąĐX1^ ]ė…z}ŎĮ˝;f C¤ĀãĄ0LY1FŗæmĨøø‡rú0‹›ÄĻö€ŦŠņ9„€čōăUPlp×kĢ8× ‘Uuˇd™+pį$ËâĖjԃžßŲĪVļ%=Å2/g×ūęÃ˙œžĘ3ŪWÆ#Ļäc!•\øl+A1ūoęÁōŨ-Tėlô@§͍Ęã–p˙Īö‘Ųc @Šš} Ų냊õ_\1XģnŽN1x–÷Ų5V âúĮ+^ŒĄzÅ¨ÃŗO¸vŠŲ–iø6ę`´ēpāŽĢ¸Ŗ?@ エ~Ɗq‡ IŅfU(˙Ųū=ĮRŲÔŦZ€Š‘¨zC1Ū{ŨrgĻrT˙Ž„¸FŲC3)FĢĄ_žQø’ú1B=°ĪX1NŠŖ!įyŗI1V #Ō„ ŗ)čĀcŸ0”P;ÅX1Øø+ëR߯ŋ—Öuuc2)Æ~&5á›ãķŠ›ĐJ¯2čcļëi>Ũ-Yã8&ŒVĐ{ېFąB…ü›~_ŨҐŌ^Ÿ÷3Ą#Ģ6ØËFëudVŒøúö9ŦfKkfæydPŒĘÂ#lŌžŌoÆ]ĸFdQŒęôY^ācį:tŠą—ëí9á.Đ)ÆYę)č2+!oˇŸ˙’¯zΤׄ†€žúėI4šöƒN>ÚũŅEÅX ĀfŗAƒbÜeŠ1Üxk=Ē´Ŋč×7€Š‘¨zC1ō3‹đ ž’wgošĪîĻĪ <Œ[É:šF ˙ķN¯ęU1 d°Ŋŗ3ũ, æ&Ŧ[×Ī•ĸ7Œu^%­cMÍ„|” I˛Žā{^ҰÎčGa#ßlöj/đ9ĄYqV§ln>ÕÖH1>Zr„=üŽüygR wĄö˙ëīe.dėZO֔9n|"tŧ !U O’+F;avÁLČĻų蓭L[&7×ኯķ bä*†T ‡€žķƒŋo?+?u0¤ĩję _†˙ŨŠlŽgō/}hũR›īÆôfŨ˙ž†ø÷.æ/T9™Į1X?}°¯/ũ—JbįĀSŒ"´r>Üį'6mvPfÅ`Ņ=ž0ŸN4ßeŊ÷ŗ?*}r‚bP}Hé(*•0īo~J]Ž÷›ž!L{ČĸĨiŰĪ,`\Ņ˙ŋoÆŗqųf„ĖĻÂ1õ›KĀãméz˙~l†ėkú„ØėÚ3ũ~He“„ ĶlõaõÍĒŦæåØį…™U4‹;#Fų‘bВô›’Â*ūą}FŌĐūYcŊ˜füĪÄ~ė}Z˜—D­Í˜Ú‡@ˆŗkī ĀAFŠÁæ)Ŧī?ċ/eRŒš´Ą°Ĩ›P™¤ŋĐÄSãųSųƒ%ėëË&@ŗ'Ƀi÷™ūSŲ㚓Y1Xtīa#øXՇIļš÷úL?›“ƒŠa¨ށx Ž~ƒ~į7›nø9ãĒ2K' äĶivrw°PŠÔúü…zķŽ0ģR$˙91ĄXž•uĐĒūãpv%" Ō?į"Á^wĮ˛‘oBž ąų"čy|†įŖ™ƒÖŧå“ų<Ī‹Ŧëdx‹”r‘÷…PÅū˛,Xp_pŅWđ÷¨ S€X_Vašķlžߏōo {!o'Gö3$DC§ŪJ=­ÄC×X?Õ ŪRām­pö-îâĮ‡ŧ™€R~ãíķ„ųŠ„đ´8BĖöčNđ!í b"%AoˇUŒžGj(ī({ XČbĻ-îe{ųRōô…%ŠBV^ųyŗ‚ͨËČ a+¤ŒQ„ģ(ūDÆĢZˆ×RØ˛MļĒd !•šĩq‰ŧD]l…ß=Ąwėļû, ü ž‚Š‘¨zB1ę.Ŗļ ĒĶãHxā‘Ė•?>4ūmã  1Ą"áO+SS÷§÷ž'Nģéũà >üjtĮŋ‘AuåÎsŅ ƒí{ūęĖĸÆÜĮt§ŅÁ€ũĨb!Ž}?ŒbõíØpžÃāOa¯Nnũâxˆßr2Ō°ŅRŊ¸hրšË&'‰f‡˙ŅŅ%.ō$WŒīúžēd4`LæGGˆėņáØ.!U–ߌđ;Ė­˙ÛĢđã=‹…F^eår>ōɯäI$;ŦčÜãááˇÜ]ŒĸŠ1ŗĮ?ŅWs_ŲÅ×"ü˛Ŋ3Č?‘Ņŧ˛ū‡õ>zAˇā‚ģ‘WF—Œ‹^C]L?rlđ‘đS|š\‰-ū‘g‡ŧū$ōa˛/*jM$VLä ŸĮd€j@*YūėÕžģŠēâaÄŊųÄ+’ÆŪGfŨˆŧÉį8¯‰ŽŪĒÉ%jkķ匐*jĘJ~yå˜ãQģEs-\­ŊŠdËæâÁāÍZ4-s\Jņ–Í›YŒ„ Î;3×JVŠmĢ­,[0^ļVģHÖr žÚ¨Íąä8‰Vīq3"-Á_p…&†~h1zīô„0› DĮb_ēUĩdÄHĩ ĩA¯dŒK¸W“ãpŅč‡{fíMcAwŒh"$Z~=čdŗâ`|ė™dą’Lt/Ĩļ™öPxMŒ\Ŗ)úĢ]DƒĖ›7= `ÛÜy žŗöĻ1úcÁ Ą•ÅčT1mĩArĶŪ ÷žm’[BūŨ=Pm íįëm§MĐPRŸ [ĸÜ9ôKCĩ ‚H'āKŊgđļ‹v…iķ kUē͛ŅÄ8~vÅ(õãüēŌÛĖúeF]Ôņ žbĮ° ŠŅyÎ/ãe_ߍ dŪ}s!GËMCTäŖGj[ 7īįųoĒ_sĀņ-é-ČFŪ`Ž~ĢÃ'`ûĩ1ÄĪĻ+yômü’pDBÂgĖŨžžđbļ Ŋ„}ڞŋ“[ÅBĩhhūÕhĶ“\4DMē>VÛšY¸Am ´‘b°Ķën°ũq‚÷ g8 Ĩ>ūáÜNJWŒÕĄ„3ô.\Ū˙ ōuBjĐĈŽõč—įīū‘§âÔŦĖ!đųü\ŗCUœO1#ŀ VÅIã ņ´ė„†„ėįG§~pTŒŅĀUŒĒéCØąyÉ­I‰ × y+Û"œXZ4˜+Æ^~ĸxĨØ íÔ)%‚äĢsÜŋŋ%”Í-CTÃY1VŒņôkĄpNŌf€Ia_aĢÔhҟčšE1Ļ RËš´p*İĢŠn×ÚÉc €_Ģ\-‚¨CŊô&9†¯ązZĸCƒŠáŦ+[^ņ­âcbc~'ŽiŅąą1¤?V‘*FŠĮʅ4χ’„čĮi`…Ÿūz™ÎĨ§4bķ#WŒFԗæˇĢŋDÜÄßŌ.IūsrÅ•q>Å(ĸļÁX1Ē͝ģúÄ5ŌĘĐœč¯‹O#\pÅ3Å0ŦrĨmņՉl°;`)%*Æ`aä{Z4ã‡û#ÎÍR‹5es¨–†¨ŒĶ)Æ˙<e7…1VŒš„Ÿ÷Û=ztīÖŗ3iF¯ĩíŅŖĮWŨ{ë˙ČŗĖ•: p¸3‹ŪŖ+;ī'i^gvŲxRBTŒņâėÚĒ͝ĶKņŊv A,PXŒŗŲ; Q§S R#ŋå8yŦŠ1`#ũ*]‹~ŧķR¨ö5Ȳ(ÆT€ŊÔũnúq`ũŠLČSá¸÷í‚bhNHņ~ĄV¯DäqNķ¤tÎUÜÕq>Å@˛*F{Ú¨JˆgœßüTįƒ„|™tŖ˛.~ÅhFÅåLŧßĪä?ė~@rGB\$Ä5’+ƕ—0ŠŪņ ;!ˇŊîˇPĄ ĸ8 }­‰õi‚›Ō†¨*†ŗ’U1Č&* įoÄ6&¤BĀ­ķ‡ôņŗŽā[uÁ‹ęB[6o .x˛ņŠI„”ĸM ŸķĄÂc5@ú• „íĢhx§Ü.%‚ä Ķ­Û¨yģclm¨Î ­ŋ{ëĩšc7¤d}J¤ũ î9`ˆ˙@œāJ…4ļ| ÂļŲÁ\‘3“ā õ¸ņåâË} zV$ķX^•…Œ>Ëĩâ!Hîq×Ũʈŋ(j‡p:ÅØÛOm 4ÂÚõ[„ũĀļŽÛZR¸Tôģ+žŅ…ētãŒFņÛlŲ N\ĩaŸ=đÆ¸ +Å9PŽË7 %dņ÷JĖ×oũ’ZdöÆÕ<¨Ûę ss׆õ[j)WQ‹_­ę“btЎ<@îØ8b4-Šļ‚8ÍR­ßjs‹ŗĪ—r:Å@‘Ÿ"ŋXŦ˜šAÄ<Ģ$íīŨ1ļąR†hT Aŗ|#m—Í 9nXčđ8›bŒ^Ļļ‚8>ÖŦŨ3æąĨ ¨gSŒ†ŊÕļAįa­Ųs÷Ėņiœ‹†hgS AŲhķąä{6ø(`ˆV@Å@1Ããå6ÜôrėvhT AĶŦ´Š~līËmˆfp2Å(iË‚ ˆ ę¤7˛éžMÎÛ/ådŠQuĄÚ â,ŧ´uÚSäLYíĐNĻ‚ 2ą0ØÖ;Ũā]9 ҍ‚ &¨ EmžwŗäIš*‚ ˆ žÚŗ8ØI÷=w2Űv{AYîgĪŨíãœsßsįRŒB˙Ēm‚ NA‹ÛæIéØ~K&C´…s)‚ ˆ,Ü_ggĪ•ÅŠ ’•OíMáËđærĸ1P1A˛Đ6Rú~RYŲq[C´†S)Æ˙õQÛAœûeHä‰.(v*ÅxŨ_m qVH:wĪ]ŖŨäHFS8•b ‚ØO3ö87ÅVį[Į‡Š ’‰įëåJčg™Ō ¨‚ ÆŦx WJCyš’ŌΤė[qƒ BˆKj3ŲŌr&[RÚĀ™ŖöKĩ-@Äáņ_!cb/l)gR A{Y!ëqH­ā=9“ST A=ÕĄ‰Ŧé­wŽķøP1AôŧŗOŠô“Ė NJ)FŅÕļAgėŖĄíRęʝ¤Š8‘b|qJm qlęACŲĶÜ~Cö$ÕɃØ~Ä"‚ 垇˛ÎQ Q•p&Å@ą‡ųŠlM÷šĮ‡Š Âi˜ÖJ‘tw8Oŋ”ķ(Æj€ ˆcsW‰>)F€ĶŦãsÅØšUm qd–ŊP*åņβīšķ(‚ ˆ´Ž˙Hą´7ŨQ,éÜA„r•‚‰?]ĸ`âš*‚ !ĢeÛãÜŸÅ8Gŋ”Ķ(FMĩ @āiŨAŅô7ŪS4ųÜÂiãt[ĩ-@Äqš÷ĢÂmČģéøUŋeKk×9ŋ] úmí÷šÜ(ĪiAD¤îŸū ĄgÛKĖ{ŌŨy˛N{4īuEžŽ= b âdlM—§Î8RRBÖ_>‘3k˙aŠ=#qÅ(RBm ŅīߒŗŌĻŧjmuŪĶeÎ6(ø lÁIcŅĩ-@Dŧ w­ Š­Ė{™ėYÃE–dœD1A8Wä¯ĩ!ØēŦ‡)5hkŠ*‚ NÄJ%jm8mUŪ2 yg&MSsĻP1q*G+Qk4˛"īmĘd}Iņ‡&įPŒĒj€ ˆ&XĄL­ G­Č[ĄŦ5ÕČpÅxPCm ŅĒļS,g=BĄŦa…ōÍjœC1Ū˙Ú ĸ%)Um[ŪuęoĨ˛žž ÎZœC1A(ÕĒĩÁō)zr/Ņ“œ ÎZP1qæ*UkÃa‹yËŋD$=œĩ8ƒb4}_m Ņs”ĒĩáO‹yËĩ•U6P1äåܡj[€ ˆ&@ÅPgP A*†Â b â4 b( *‚ N*†Â8žb4k ļ‚hT …éúDm ėeÅ|ĩ-@D# b(L÷Ûj[€ "¨ ãøm AT …qüq AT …qtÅč9@m Ņ ¨ ãčŠ1tĒÚ ĸP1ÆŅADrŠņ—ÅŧQ1A T …AÅ@Äi蛡Tĩ=×bŪ^Je” ÎZ[1VMRÛA4DĨĨĒífķūKŠŦ/äƒŗĮVŒNÕļA-q[ĄZ;ÆrÖŊĘڊã˙rĮV Acæ)Tk´"o…ÎO̤øSŗT Aœ‡üÁĘTÛX‘÷Be˛> øC“€#+F1ĩ @DkLQ¤ÖŪjUŪĄJdUKŲ& GVŒkŸĢm‚ ZãˆĩöCë˛î¤DŋÔ8e—DY1jVÛA4‡üƒßAŦĖz†ėYà EŸ•dY1A˛sBæJûŽõCĪCĸäÍ:}ē‚ĪÉP1q2ĻEĘXiĮ¯‘’u‹s2f Ū_(õˆlÅaŖÆPĩ-@DĢ, SíˇEęĀs×ŖÉōd~jŦ"ĪÆ.V1ÚíRÛA´KéūKׯˑå°Í=Įë× ŠkSŪŋ!įŦ×\x´:įfv—ųČƒÃ*‚ ˆ]@YÕ˛žûˇjYÛ*‚ y¨­ZÖ N¨–ĩ} b ’7AՎƒ*Æõöj[€ ˆƒƒŠ!UŒļī¨m‚ *†tT1AėC:¨‚äMP1¤ãˆŠŅY›•q(P1¤ãˆŠņÃ<ĩ-@ÄņAՎ#*‚ ˆũ bHAŧ *†tP1ɛ bHĮác˛‡Ú â bHĮáADP1¤ƒŠ HŪC:¨‚äMP1¤ãXŠ1j¯Ú â, bHĮąŖVĩ-@ÄY@Վc)‚ ˆ\ bHAŧ *†tH1†MSÛAœT é8bô¤ļ‚8¨Ōq Å@‘T é b ’7AՎŖ(ÆžOÕļAįC:ŽĸãËŠm‚ Î*†tE1AäC:¨‚äMP1¤ãŠŅkŸÚ â| bHĮŖÚj[€ ˆķŠ!GP AųAՎæŖ†Ú ✠bHGķŠTZm qJP1¤ŖyÅ(Ŧļ‚8'¨ŌŅŧb ‚(*†t4­šŠm‚ N *†t4­+{¨m‚ N *†t4­‚ ЁŠ!Í*Fņ|j[€ ˆSƒŠ!Í*Æ_ÕļA§C:šUŒREÔļA§C:šU AEAՎ&ã8¸‡ ˆŌ bHG“Š17“BDiP1¤ŖIÅ@QT éhM1ŠpQÛAō¨ŌҚbīËŠm‚ yT éhN1ArT éhI1:z¨m‚ yT éhI1Š|Šļ‚äP1¤ŖÅø/žœ„ HŽ‚Š!­(ƁŠj[€ HŪC:ZQŒĒ…ÔļAŧÔT-k‡UŒ.ę+FíŸÕļAįĨhaĶpÍo:DļmPķ1“õ‚cČUīȕˇ}˜1ü?ŨBσŠæ–i-ÖäVN‚ä-J÷û÷îËĻyo.äĨĮÄJvįŨbŲ­'f2xņÜ\Đ­­e(¸=ԜéųȌu/ĸÍūŌûPwÅUx#ĸų6ƃ­d*oWŪ]ŊmΞŒ“§ü6QīŸ › øEaãž|ĸp‚äUz…Ø^iSR§Ø‘÷?ve × Čö$˛Ė>ÃÔn !‚ØÂ4ûę>Ę&›ķöŗ7ëWŽ2> œļûĄõUȲe[JA„L°ģîXecŪūögø†ŦOÃJūĩßp荌iī•V&]AŌ^†ē`˜MyŸ•#ë'2?k˜-‡á‰˛Wíß^;EAcîËQųAčÛ6dŨM–ŦaŽėĪÄųä1ü˜ÜvUŠ'wŠ‚ F “§ōƒĨ6äũ@žŦ#e(–Ø/áĐT>“Z</-A͜“Šō{*=ëV2e #å,9“"“áČbMé~¯’ŋĄ,i!‚ä€Lu@ÉYo–+ëÜŪI¤§\†?“ÅœįËȒ‚ HÎČ3î͘#9ī[re¤ĀƒÉ‰Crö.Í.pU–!‚XÃ˛U~{¤f]äĨlyįō2žķ˛ŪÃv#ŠöbŸŌuAÄV~—­ōģ-5ë*Q˛å]W‰Gcž{˛>Ų6Ø.Œ ž'kĄA,!×Ŧ€;RŗŽ)[Ūš<ę+Ķ/Ę$Û ‘ˇ<‚ VŠa**F—`% „ b ¨6 ģbTąa‰ ôcÂ=Ĩ˅ ’#¨6 ŗb´öH7Ÿ×$ļûÔŦôãÍÜ)‚ ˆ9P1l@>ÅOȘë`l?†˙LfG.mC?æwËŨ˛!‚˜ÃäSŒŊ'ãčį"2ŠM•ˇ~Ôg}O˙=T~ļ–íP\A9@Űųã¸O*0Åø–­˜wcPųęänYAŦÃäSŒąäà ˛jîڏ b¨6 ķČ÷Øįšk?‚ ˆM b؀ėŗkGäŽũ‚ 6ŠaǝųFQT @Å@$Or@ļĘīŽÔŦ+ˡa#%yĘfø÷šk8‚ ˆ=Ė•­ō;$5ëū˛å]H‰Gcž ˛ūMîŽ bŨdĢüfKÎûŽ\YŋRāÁäÄßr.įAß‚ Š#[å÷…äŦwʕõyžKNô‘Ëpß|šl9‚ ˆ=xĘTų=˙¯äŦ?“)k˜ĒĀsÉš ?–Û†#‚ØÃ™*ŋ­6äũLžŦĶä~&‘Ģ[ĒCŽ[Ž bōLü‰(eCÖ#eÉVÉūL,ņž<†{äēá‚ vņ,•ßt›ōž*Gց2?kØ ‡á€ÛI!âh,—ĄîûËÆŧ#ėĪ:ĨŦOÃJŽŲo8ŒRÃpAģØewŨgs÷ʇĄöfô•œÂzŧė~h Õ1AÄ.fĨØW÷m´=ë‚v.Ęđm&ßcÆû Ļ–á‚ vQīŒuŸwGģōūŪŽÍB’–Čôlák{ļ—ú]EÃAėãŨįŖmО/¯­gwŪ_ī˜aCŪ/ū-CÁíĄÍ&o[ZgĄgĻK_ŧ‚ä2˙],)endstream endobj 18 0 obj << /Contents 19 0 R /MediaBox [ 0 0 612 792 ] /Parent 29 0 R /Resources << /ExtGState << /G0 35 0 R >> /Font << /F0 36 0 R >> /ProcSets [ /PDF /Text /ImageB /ImageC /ImageI ] >> /Type /Page >> endobj 19 0 obj << /Filter /FlateDecode /Length 8164 >> stream xœĩ]íĘeģmū?W17Ę6”BNš“ß-zĨ Rhz˙PÉkíeKļ´öNߗaXŖmËō#Y?ũO‡~įņ¯ŌÂĪ˙øÛ˙ųA_‡ūáī˙ųãßū7}) k¨–›ĻčˆŅŒ˜8ęÅe3bâĐ|ÔľkĄYŊž=!…)7 ,˜Đp (ca*8œ€ãƑęŒŠĩÚÆŠ‘bj3ëjūhŪ …‰ōĸL\dJX’I…ĘŲãPQŧ9JfÃ"oú¯R›nux0˛7ƒAFp9WĸŌÛás5ƒnFp…čĸåčƒSŦŪ˙0{8räF–ũ™18a Ļ>P0čōŋØĮļDj$:SƒŖ;ãË ĮÚĀĮČܤL;RŽvĀJXwy‘…ƨŧKeĶãNÉņAĻaĶ™u~‘"!QHaPReڒČzdč†XœŖ-ƒÃ„,¤BÂŪĒ>YŅr¤¨’Ír2& MžĀs. bĀčTĀNdDˇL.3Đ< J‘ļĨ˜}~IoĐ+āÃüû”Ŧj‰%ŽŒhâîU3Ÿ§č•{ŗĢæ|áuÆĻĒYØGD.# 6ÆĻÖ\˜eĘ1¨TÕzʧzø˜˜ĖÂ49˜ČFŗYˆœ!™uz„ĢÉÔҕ̺ëBä”čírjB™ŒôOņ+ö¸\5”ú†ÉL-…ÉHI”ė´Th';įCÔnžÆPđ– Æ/įĢÕō€Š•kVģqŧ:’¯Ū˛q čuz‘ŽpÂ6göCȎ’ŗ?CŽ”<$ŗ.אŠōž’Æ!Ζr1õÁŧ sS°|+`Ō į ĨúDĀ„ē eåČßl+FûĮ5d™Ū‚ "fĸÕôˆ4<;0ģĸsļÂŨoĮŲJā8Ũrļsg”ŗãlāšÎ’Û`ZTcŒĻ 1-j5%ŗ. =¸k‰ātöĀdĪôĖĐ/z}ĄQHįƒŅ@ #"  IE1Ĩ†-kg6ŖĒœpLr!ķˆ!ŦÜŲf––à >Z(Lˆ$ŲjVÂĀâ< Š'ŠĻ>Xĸ¯Íꊄ™o3õÁ´Rą†ņũ¸šqķôãÍŽ@Âī‚7ĮÚT°ģZ4Įڄą'$Ņ gÀĐr˛Á€¸B° –=B,†dՕ1°¸Í12Ķ rlłaî9M1ĮČLŗÃ˛ÛĨΔ¯đ.•\!÷ŲáfõVĻŒĻ™ž•ņ#ŠBp¸q‚l8Ã7ā!NķĮИū—1ô€{čˆZšyģ´ “ž=XūG%dˆ–ÎČØøŠ¤GŌ_0yJĻBpčŖŪĖz:%ķ<ū"uH ›â!b<ž HĮétĢ$s„LΞmö‰h¨V`>]LĀ]Í.EbŒƒļ9pAŸ‡ĖD„ yčlf…3īļŒÂæVÅíZđĖĮ˛SÉ„ÃąĻ&"—‰ˆ˛…čg™…'ФToõTŽ\ũĸ §Y]+­Ã„§Pܞd$— 9§>Ų=ÉH. æŸŦéŌ­<&`)RbčƒÃ#†ƒjš0“Õ“Y(-¸zŠĻ† Ų~LfáZ´Œæíîĸ(ŧģ–5ÚZ(âSĻOKĨd5+ z.dKåĐįĨĸ‰Â@ķR%ƒÕí×õųđŠš”DËh}æ`2ŗ,3°}ĩēĸS;ĢNír2ģ4ô‰)0ģ´/Į J•i^Ęķn_VŅúŧT‰fwõyŠĘęZbŸ—˛ƒF yŠžäb4ŊēŖõ%—Šąā‹šÃ‘ZŠœCèA!o–‘T !V-v~˜!Xá bx Žpå~Ø}-N7 b Ê3ũĶ< +>›2Äũb(ĻΙ&5Ŗ‰åÎũr.V‡÷s‡Â…C–ž$lׅĀY„Â…ü%ô- —ē0ötĒŽc§/ŗÅbvE_fĢÕô­„a%§–: à ´lŽDÚjŽž!m­™¤U6<‚-‘áˇāB€h­™Ņ A™Ų7ĐL´čõ…´adņ§srCĐLt§d†i&:ŲhŒ>És4¯´ÍcÛKŗŠqų(یÍĄQšÉ• e+ °,,XpäBēā7|`ˆ[ŽņC w5’–P¨ėKm“Œä Ž!%ë.4ãy^{ŗ‡Œ¤7¯UŊŠ*Éļ0Rö wS) ųķ×.UŖ&ڔpZAsî€bu- ˆDūÎ)hîŅĘ9ør–}–c×ŗ=]ŸRŽ”c5ëĒãĢYNm´šõD–‹•­Îe ÁexĢĒā#6ĢDĢ+M]ŜŦ^}7AIVĶ"ŖœÉš €ch;“sŊœD{ZB˛ē=¤Š$RØgŲCéŽô÷×gaÅĶ‚&Ø2´Ī2˜Đ 5ģPƒØÂūúNŸeiËG.Åj:˛Ō7‹M*/û#3v˜HØí}ŗ€z"özƒ VËcĤ:9æ$åĀ^Į€ËU–Km ­ ą˜u!1ķŪZhZš>40ĢŽĨ™ål*‘Kd´d—r°eĩ3˛3AU”–ã0}JVŗŽ&ˆÁl5+ų€DĀîRÎ_)†Ē3ų•b¨`N1žR ]„Fâņ`á%˜wCuæČ…IĘQÎĖREaÂŧ)P°ß×Ú*–eONÎ #Ũ‚9(-+msáÜLkFdēDÂņ&q$Ëą$ápŅ™cIÆņĻôŨbēm˛Įą|ŗŧ8cTú†XŊU´^wNčUŅT5pG_–i{Qá^ŗĐ(Ú^„\ܲ`_Ž“™ˆÔ'ˇŅ@R-„ŽĪV—÷ĩ¸ĀÅH 2ÅŨW= I&Ž3åpŠ™ §Ŗ2U‘*7šÁáŖÄ’ŨģpË2€ĀŠ2õ\vO6J L¯"ē†y 7 h8`~BŽ–‘ ˜A§o{ė›] iãPåCÖBŗˆŽÅd&Ø.fœ„Bč*Ļcu翚Š2å `ĮĀŧ7†Ø2?ŗ+:‹bX“‡Õöí $ŋ)ŠpŖá—…ä'E Ž+æŌ{‰o]ķšÉhüS×ĩÚH|ã?ŨTĐÕíŸvß4EîOdú˛ąø¤iÁuĨ‰„­Äˇ.‡Á&̉o]Ûūmg"ņMüvWGWšÛI|SußČ(ôic'ņIUD*œV—Ü´ËqÄņO§ēiuɝ9ųO7œúĻÕ%wÆÔaˆŖ/ÉOŠ\Wh~o ņ­ËqŖņO]×ūig ņ˙tSAWˇÚH|ĶaFŖ/ ‰OšB×ŧēäΐ]ŽüĶŠk^rgHūĶM§ēyuĮ!5E؀@_6’Ÿ-„ŽaM3ļ†$9a4öéÔ5ŦiÆÖė§› NuÚfl Š(–í $žiŠ0÷¤/ ‰Oš÷§Å?Ûk]hƒ‡„’4ė၆ôú_ãŌ—ëŠ+Iīj´˛dÜĒ2}ŅāÖĻ}Ĩ2ąĪŋŨĘD9™<-įÃ,ŗÎˇŖËđˇĖ2éˆV_båČYāX‘ôôÍXCf™ÜĻc\9™å O‹ÍsåL1ō´œĶy™ûGĸvņŊ!#÷lĩ7­C3˜Ž3fŲålū۟“UˇL5Uēē%ąŪZw$Å#•Ė­ü‡78āš,˙ $iŅc@˛–¯ÂcJÎÂcJíčâxL´ÕõŠ“pŠ¸G<ŌöME>ā‘f¨Ÿđ˜hīˆ‰G¤ĩx¤Il‰Į(gû~_­VŅa‘~*zęˆwĻ$Î OāHĢ1#\Úõ“î(úLžûULf€GLŌɐÖ/^Ō1™K#LšXĸŖúO˜Ėĩ&ŗYNŖmQž˜Ŋ×*a˛Z˜:zÔú2…ŠI:=Ō˛ķ–}ĀWBå. MŗÖ´ˇ3+JB¤S)ZQ’ëc”L›(9ÍZĶĄ=`6ôrCSFŸ=% }¤}€6wĶKڑ8â—3˙…änÂ?Éét¸Æų:#ސ<{īép{|rđY @[™+“YwŨ•ëũy/‚ĐÂėøÍäLøčîĨoÉ÷ ՗–S)ņ CfŲēäé"0Z~›d䑊Wø˜däéLÃÎĐ0dÖ Eá:•2dÖÛčōƒĘdÖérŽúņŊI9žûōdeåČé{Čt’˛19}õqžÕĩlLtW 1“ŒŦ‹v÷ÉFģ0ŧēĘm¸ôåč‘ÛpŲÅââuۀng:–ŌŨΨ+xZer܆MĘĐ*S¨f]q˜EÉrb?,ČûbŲŊD‡“Õ]tâ$5ģģč2Hŧģ.¨ÚŽrÛ4ķ zuúū×ŗĢÉõ%SšŲ•]鰍ą\›Ĩ§vKūüuõŖ“9ÕbęĶ™SkÜhëĐšYڐšŲ•îj/vĨ:īú€F)…˛Đ´CŌÄŽ":HN–ÃbžtÔÜņ—ŨEųhũÚũąŅŽÎăÃ;Ëš}sØGČžXډė/&i:ŦIĶ >HšëDÛ<`M{ˆž`}0Ö'ŗaMģˆž`=HšČIĶaû‘E֙ö÷?Ā:÷Ģ´lXĶkO°Î86<ÁšļũC°Î|°x›í]šÚIöžŲgÎĒ"{P=Ãæ7ÕĶ‘M—„srVE6튞…lē&ā ŲtM‚"Z­:‹ô€lˆđˆlHސ-dW‘Ŋƒė~wīGČžXH€ošv´ü# še 2dt2dtrË4ä–1hȨkĄ ĶūžBë:C¸ĮåwŽ2rFĀĶ!Ãxģ$}(tV-f]D1úĖŠœåBŗtŨ1=É,šaVéF⊎åt|F'ĄŊדŒƒöÉ3>öÅ3˛‘Œ/[,í4ŖøôBöB!0Ŧdķ!ŗždƒÚõEõ!ŗž°~ҌIFÖE h§Ē6Wpĩ~ŊˆĄŨvŌ2Sya43Ā>ëm”“ŅAúmsCfŊ^+\'ĩ§ē–k“ŨõڍŅtb"ũęIfšƒëĩØa´ĢõĶ2LŸõ•:-S™>ë35¯…ŒIfķLMņšZ6¤gj6ˆtŋƏfĩ‹îÎęiŪ,ē:ëŧ9ŲP9Ņ•`ÍKžú5ô&4Ĩ)LhĐk7øCŪ Ãî 1ĸÛŲúÉ >ĸėĖūp Ĩë_¯É5kīiQĸÛxČ,­÷ƒ™a–y+*zÚN3~ķáÁ°øõlgXNe;S‡¨lG ƒíčqa°øƒíL2*ÛŅCô`;zülĮ¨ëf;†Î7ÛYœõ ļŗõ#ÛA|1ՙFM•ęč1dPŠKTĒ35YĨ:z,TĮĐįĻ:zŧTĮ’yQF/ĒŖƒhPŊ”AuôV Ē3YYĨ:ēs ĒŖ.ƒęLåŧMu>õEu˛˙žŖãzđÃâ7ĪŅq=xŽŽëÁst\žcčsķŗƒįX2đˆëĪŅq=xŽQĘÍsôV žŖãzđ׃įč¸<įC\wžķŽ/žƒ!dŦ§|1Ձ{ƒ›NunƒęÜ2Õõ=Ķ›ęŒĒtĒŖĢ3¨Î¨J§:z9ƒę@V|mĸ: ööí¨ŽŪôAu†ŒNuôv Ēę͞ƒę ę ę¨6œ¨ŽjÉęŒr6/œih¯ķNTG5s Į·Ø*S_š ēøŧ”j*gyĻ?š¯x ([sŊCQ0 T`1(Ę-ŗŪxpS”!#×↧ē¤ÛĐõŦũy“ŠœE†nąë.qËŦëĸtæ¤1™å™_ēŸĩߊ0éŗŧŊK=t5d¤ûa–žĪkčC!öGĨ&}$Ä,|LfÛ1Ë/¸ÎoQÁZ6úŊCĪ‘ãØā R ėëlP TũubƒjČ›Ø b&6¨ŽbT‡¨‰ ęu 6¨ë<Ø  Áī°ÁMüxdƒ'NžšŽÜB^UܯüįšÅåŊ{ŗ!Ÿ¨Á•B};Ô؁:œLė`BgŖá:;PGۉčú v0dtv`ČÜė@u—›¨Î2ąŊ”ÁÔVMė@´'v ‰¨šŅÄF9O }˛ƒĘ÷œų  æBļēŲjČ蛭&uŗÕ$Ŗnļ2úfĢ!ŖoļšĘY^"ymļšôŲåũ}ŗ•!soļ2tĻTÂ<›}TČÂ<+F˛+ہöĶ š¯‹^ņ=rę]č"vŅōåŅG’Såę,ĖSœū–×TÎ;čĄlzīį„˙×g8úÕczīŽc“ÔcSģÕc:˛ĮąCŸûØÃ$Ŗ{0dîc“Œrėa’P=ĨÜĮŒVŨĮtԎc“ŒzėAGö8öđ!˛ûą‡}%e'˛ŋ8'›`­ædK”x#'û¨gNv \gJVę×Ĩdē뎔LÕHÉt×)™îē#%Ķ]w¤d†>wJĻģåHÉ,xtŨWJĻģîHɌRî”LoÕHÉt×)™îē#%Ķ]w¤d‹ëÎŲבZØã§´MqīG”Fm5î‡=oМd–‡A*] g™e‘Ãė9¯3•#/š­t1|O÷F9ōĸŪZé&ËĖd”ŌĨītOęT—<•î2=ĐY,}SËģgEm™ŖŋÕØī—œĘ‘hĮA:ö—”tû;ŒĄ5ËÎ@ûš+˜}A8Ŝ'˜:#N1— VŋCĒ”ËESgz‚Ėë¯åŽX€3ŪīPŽx_,žEûšĮęú4PŠY]Š"ī xĮˇęŽîw|+ß[-ä-˜Āœ/B™upôŌ/g9Ú_¯—6&™å‚fš+NŦĒåB šęqŪTšõ÷}ĒĨō4 ™å‰Szß'˛ēV×Jį°“:K1H\„q–÷t Ũ˙j×é•)ŅréÄ4ą´6b´œŽēđ´6ĸ[°_uauŊ’ZúÛu“ŒT&WôŪ¨÷ˆ=ĀÆī ¸8eĻ'ŸÎYũ!ŗDíũ‡ŒŧÄĸĻsŨmŲ<įt>ô2Š##z Ŧ¯ģM2 BĶR\Ŗ. -$Ąąß—o”C,z’rËŦW­`’|˛LH­ũ FŨ„=°ĮēbÁVO@x]Ģ ŨĒĖfezā:zSeē|ؘđ ;sŊƒĀĒ]Û0å(CFB‡ļŲô›´‡ĖîûÔ7|Lål.ą\dŊ뇖Ü2WgšÃž-LfM€qxđ1XMīI2$Žōr==&Đg­n™ˇŽĀnގgéõī9sŲÂ}ĖiŲÚøš2ËæaįDČ$ŗY7t…ž(däŲÄ{ŠoČŦËöîē}*GNKԚ`åa*GNŌŖ=ÄLå,Wœ—+ÄúÜį)‡Ė2-y¯ úԂ#.ˇķzV‡ĶâĢ-ķ:sičƒN’úëÛzģ1Éė­n§‡„Ģ Ū2aß!P’ˇēkÜę2ŠŗŅ!NėĒŽ)ĮŠĸeâ˛áPŽ{,-Š•vĩ`Ëŧf% …ŅĨK.fŗ’ēĘôÔĻÍŧĪ7O=īLJ>cȃļņׇÉŋā>tkvķr„Į—mhëœēŒ]ČēŒ]ČēŒ]ČēŒ]Č:Æ.dŨ?Æ.dŨ?0]xō×cŨ?čq¨'˙¸îÕ´°Oīų*ūņnz%Đ>öwTŸ&Ŗí\=E2‹˙ĐvŽžĸNåH˙Ąí\=Ed–ËVҟ]7qlųOauŋ=yūĘ÷ž~Cƒî@cö\w 1{Ž;И=×hĖžë¨ŗįēŲsŨh^˛ö9mŊŽ×ˏîA4ur"Iu ë Poy„Ldzؘ<7ωKē-ÜaÆäšĄ3]2ņ0ÂFÍĐīđĶG K&ū‘Ļ“×F˜‹Ĩô=û"ZyMåčû"†Œž/b’Q÷EL2ꞈ!Ŗī‹2úžˆŠœå^Ęhú¨¨ëLĢąCzÛ)€ôųCĻøkķ›Ņ."MũÕxCgē„ĻTnįeķí™vŅ´Oëõ™ũÕīÂ,‘ˇKîuöũĩ>ŗŋú=—øé—ĐôŠĘМåėg¸^ōÕÛŪ/Ąé/ųNåHf•h"œclŊ¨†&Â9ÆŪzE;ä.ߥVŨąŋY ŋ֙•ÚwŗvЙÕhˇÎŦTLNĖJ×g0Ģ!Ŗîø0dî:NnúĨzãØņa”rīø0Z5¸•DÛě^;>tôOÜjÔĨîø˜Ęy{ĮĮGČ~qĸŽė/Ūņ1ÁZŨņą¸÷;>>jā•´žCō7äŦĒëN9Ģ Ē)gU]wĘYUםrVÕu§œučŖįŦxģœU­ëÎYUį9ë¨HĪYUO™rVCĻ.žkäŦęØ6åŦēÎ#gåč9Ģ‘ĻœUF’wrÖOäĘYĄ}Ī,~FÅ/eÔYüIFÅŸd–Yę×,ū,ŗœķC×ĩ1™å-S.åĖ2KbKī¯B˜e–Ä Āâ*“YvĪRԚ¸V]´@ØpI$1‚`UÉ,‡"HŒ™É,Ik;2x0ëj8ÆČd–Ã’ DmŦŋÖ¤5.šjõ{ŋ9ąųfŲ9Ў0ˆÍŦ‹„įM—w`ūD÷JZ5ELi2¯HžLt­0đ,™8E‡(€!÷1gėŦ–ĸeä€^šŗËVg…Œ@ÅÖĪ2ËölÚŽƒƒ3+GZ‡ļâP–jéSh{U1A*m¯Ē\įwžXBclēælžįÚ¤ŒšŽ†¸ĶuLwē>7\K× ÜŪéēĨĪ+]7\äN×-™WēnåJ׍8t§ëV)¯tŨjÕ+]7bĖŽnt§ë†ËŪéú\ÎģéúgĐ~Ĩëđ §æ \ß †Åī ×÷†ë{ÃĀõŊ€aéķZĀ00{_ŖbĘ´G\_ĢŽīkTŒRîkTŒVŨר¸ž¯Q1p}_ŖbāúžFåS\Ķ’Åg¸žX|ĶYIÚ7MŗŒūĸi´ošf@ûĻi´ošféķĸilī}™Ļ > /Font << /F0 36 0 R /F1 21 0 R /F2 38 0 R >> /ProcSets [ /PDF /Text /ImageB /ImageC /ImageI ] >> /Type /Page >> endobj 21 0 obj << /BaseFont /Consolas /DescendantFonts [ 23 0 R ] /Encoding /Identity-H /Subtype /Type0 /ToUnicode 24 0 R /Type /Font >> endobj 22 0 obj << /Filter /FlateDecode /Length 9223 >> stream xœ­]ŅŽí¸m}ŋ_q~ Ž-ɲ îtææ9Åũ€ĸ @ $ų ōžĮ\ĨEÛgŌĖ”‡’(Š\‹ĸĩ§ąüįOSų¯e ˙ũ÷o˙øļ˙›<…×ŋøį˙|û¯˙Û˙å°Ė/ŲĪ˙ņ§¸åq—Ÿ>ö˙üį_>~ūūí۟˙2~üí_EÍÖņcōĮ4ÎyWõĮK÷ūo–øSųLlŽK~ũË}ųĶqčį˙(ũōûˇ?˙?BrÎËĮī|›ŽålųcNaũøũīßūmĮøīŋ˙īˇß~?ũ“múü“°žūDūŋ͇eŲf-3˙bdÂ2LãRéIÉȤiØb¨dæŅČä4LqI•L42Ë:l9T2金YĶ°ŽŠšs˛ëÚļaëuMŋÖ2a,ëšæjŦŅĖ'Li˜Ëi™¸™†8Ž›7ŸĘXa^ĢĩĪFfÎCŲĄÚÎ˙adrąsQVÉØųämˆq\\=Ë<„9lŪž†mÆeŠæœĖœã˜†­x•ˇ_q‡5¸ÛÃ<ä´mŪVćbŗj¨°™yߊŧeÅ\ļ"NSĩVĪRļ=.ޚĩX،dŒ“ŠqĻÕՒĻm§ŠZøœĖî_Šc>˙ā§ÚKš`Q<ģ,æ˛UŸÖĩ`ĖÃŧύešƒWÔ.q?TąŽ^Ö;ä)ģjJĖYō2iëe‡5n•Œõķ)ÎC Ք­}§†brĮJyH[ŊŦ&ÎÅĸ)ĪÕX“aXļ%ģcå<ŦyķíSÎīö3ŽÆ˛Ûĩî~ŗŽîœË/įnĢô˜¸Æ2įq­Įjâd9ãķVõÃʤ=ŪnŪēBCȋgžPœ=ÆJK‘s)š )•‘æÅsÔļĄ¨Iž‡•(:¤1TģŪDäŲĶ’f_OÉXąŪŅ&"—ĐĩŽSme;įĨd‘6w'–mĢ99Ö>ë2¤˛0w+ļi˜§PéiBrņžqËîÉŲ3DIjÕ~5!šxOœļŲÕSŧ'-cöėS2ãÃ䞊ׂŠjÚ¨KD˜Fׄ% îIÖ bq^‡Ōä.Ŋ‚KëąĖaË8Ėi î˛ ¸ĘÛŨąJĐX–1ēs^K€J!xîwđ°­É5áV\,ÔĶŽ•ÆŠ¨ĩŪv› ĮĩŒ\ģO°‰6Û˛ÔĮŨēĘq_Mˇķ‰K‰>‹?Vš‡wo튄ÂVĪR ŽE+RĻĮŨÅRōNr.î“—Ų †]KZfĪUsÁŝŨĔ ĪK žĢæYŠ›­ž‹åeb“įŠšĀ‘u5pÄĒYĮÂČĖvY3—čSiđ<ĩxN,Sô<5oģ‡Õ&lčTÜؚ t*ÉNŲzČäÆ>OlŲSI[q~ás‘ij&{{킈L–v„ĩĔ¨EZ6UüoNÕHŅøč^åɯJšąe3/ķ+t'æĶšÆmuįŗW:~bŨĜkgJ㋧+ë4LŠ„¯1UzVļ3ĨyŽÖnAÆ´‡Ļ5UfļāiÚbY{mKHKô§WHžB _SŽŅ›r˜bÃ¯BMXĸöP°Z%ķKÃĻ…)ÛéĜĻuōü4Ä<į-x~öčÆäʔÄļ.Ķ\eŲKĖS¨Ŋpnęd‚ÍŗŋŽ‚‹ō˜7w+ 0ZæÕrqŒâĖ“įđa[Kžkķ4Ĩ´0liÜ<3čPPuđ|0N/įŲ<'Œ%¯Ĩ0NŪĘcÉkķ2Ī cÉkKĩƒ5\ǜ›Ņ =1Å=õU#YĒ0—…qō"O‰“%Ŗ'÷híÅŋ8Îĩd—e׹ģ™O3åxšl¯įXÜ9o/–íú`*á`6›nĻŧŗ›Ņ\+— /${u͓JžˆÛæŅ‹cĖãėų{JÅ1Æ){ūžJĸ˜×:6åŅ’( ļŠžÃ§’(ÖqŽžÃ§Æ\‡•fí%„)dĪá_<)›„ĶãIÛäēFÚ ËÎqõôŧxŌ6mž_<)ÕæąG›9V͎ ’1Ą•ŲņÁV'“†ÚĨą@ķú$Ûû BoūŨ_Ëē …˙—hbɜsI˛Ëë˙ŦųBEņĶ=¯ī˙¯Ŋ÷˙„N‹×Ē÷Ģ´ˆ<ÜünũĨčÚö:/dZŸZ†éu)¨dš ČBåļJ¤Ųį‹âļß6@&6÷š°ØĩŌͰáËn•ŒeÕķ8L¯X¤dė‰+YsY§zŦĻŖĐÆeŨ\픏ˇ-šËŨeg{ĀrÆZc‰ųSŊ.ËāKÜ[ō~ĢãčŲ ČK=VĶËQČSŪY>ߋ’7 G ŗgðéQãöŲ)s0̎_!l{EĄĢšWsÉ­{øs֕Âļũą^ˇ”ku,lŽķ<,)×cũf9ķ~_Wo/ (+$uJŽ}–ÂW^å<ĩv[žX fˆyqĮ*ĒäÖäÚg›‡k×°tÜī$ļz(KdK&šÃ]=;ąŽŊĮzX ĶÎ‡ĢĄâ¯VfŋđŖį…;¯~1nœ˜öĢ×9{w¨=Åj¨h›sæÂÔĻqs—UĀRąPŦžŧ—ôrtõė]5cÜ\ķ†5Ŋ˜ŖŖg] Ų‚įĖ…ōܐˇŽ Kv%ņ9§ŸlŌ8u ŧ+pŌ——ˇŠĢžæ-‘qō–ČL6XJŪ‘æHŪ™ĻĮyKdšÜ†ŧÅõ oAĻ雑ŧkä-Č4Õ`É[܄Č[|,ä-Č4Ŋ5’ˇ ÃķdxŪĸvVy‹ÚYå-jg•ˇčÚUŪĸvVy 26— oŅŊPy‹¯y‹î…Ę[ŽŒä-ē_*oņĩ#oaÎv/ˇøÚ%oŅ-Uy‹n—Ę[t(ä-ę*oņé oŅ]GŪâŗAŪ‚›ūˇøĘ‘ˇ îÆ÷X{Öûņ}Nįņ]dœøt%ž‹Hģ%žCæ7+#ņOņ]dš›3ÄwŒeã â;dLTņ26O žCÆÎņëA|™&ˇ!žCÆÎņcY>øNíŖâ;Ũ ߥ§Ķ;ųßéēT|‡+ßų|ߊĒø™æžOâ;ˇâ;dšžw‰ī|Îˆī°Í[ˆī܆ˆī|ß%žSßé1Uņ. ņî„Šīt'T|§;øÎgƒøNŦâ;5˛Šī܂ā%ŽŒđz/áķ/áë/Á|šžGá%Ԇi‡uKՖn?ŦĖ2¤-Tc}7ë*įa×Í û­`^7 ¤° aƒ+ãŪįŗz{‘Ō8¤uŦÆ˛×U)-øŦ“ģŽųu_\eoDķ8”ƒ“Ü9īöËäŸũV0åĩËÎg÷.ēäÚyŨ?ŅH“wL͇§ú˜ZûlÛŪhŸŧĩĪcŌ\Ģą „Ķū1MĒdžVfōÜā3‡<øągŋ7\S |š ĮmHiöGJySôÎß<‡!'|ŦšyB\ŨØSū_ÃkāĶp CņųzŦ`eļaŒ“RvEŽn<Øī C0§éŠÜ{  đąQã<¤P;Ÿũâpœ6Œėm‘ÅMĢąš–Į°7Hāc/Øöxlę˛ķ‰ëĻø4—”iÆą>ëv— ä:Ā).°Küd‡–Ÿ˙t–LüTq‘iŠųāĐCšÔ48_¸€•9î•B3{pų›qēkÎŽ–x22Goä!ŊlŸ~R˛ãø :Ú&[3}8 į/áü2œŋp=ā/Ü5Ā_ Ãų uCÅ_¨}ÁŽņ{:gÅ_0–åā/\ø ]ģâ/\ø =_ŠŋXûP/W|†yųËš/ũõōžļO°čĖ@ØŌįŦ§Íž3ÅŊ¸{ŅsϏ=ā^ô˜)îEÕ(îE]܋zšâ^ô”)îE­î…-ŗ¤ā^|Æā^ÔŠ÷ēėԊ‹=āÔ=mž*f'2Ģtō_MSqŠãVĄHfßĖ ŊĖnexŌCĻĮßÔéöøččŗ%j i˛dĻ“Ĩ įŗĨÜCÜĐČí"ciĸĘ펌ävŦ˛ÉےÛ!Ãīž¸ävČ4o[HnįsFnˇ2|gë{;sķxö´æ ‰GÜŧ§¯ÉÎ@&ÔĪ2ŒE/@&—Ī‚B*Xŧ§ÍD…{Dæ~@|ĩĶčÕˆ{ĪØY@ôdŽ€åČĢq‚ŅĀÔß|Lt´5dBdš°*KÉĐĀĨdčĨš’i›8—ŖGd2!KÉ4Aé\j,Ëi%p9ö‘ĐĸvÍ>q GŸĪG_eÃũf>Îą>BõĒ[ĮēՆĘÅY`éÎāę1īhą‰ő"Å} DJÉ4OdDĘŅ#TGÉX‚(d源‘F†[]ČÉ#ûŪŅvš@…úĐ}ŋ—@;úšrP)~`AĨøÁ•j‚'ĩzÂŪek>_xÛ;ĮŦĢå$q >‘¸zÚlânélŗpKžˇĀ-ųˇäy ėĪŅ#ėįÜëņŧ…{=Į}å^ĪąÜëŠMkîėŽ{=ĨĮŪ[ÉŊž’ žÃāžī‰ŗŌĶÖÜ>Ę­ĄŠ Ík*Į­a#s§3öđå MmC'žyCŽ W+sÎĢŋQ›q읍Ŗ¯i.tĖ—ü û!đĢČ4=ŋfvĻđĢČ´O~…Œm˛~…ŒÅ”™Ũ!)üĘíüŠu5ä\đ+_;đkfwu ŋR= ŋBÆâāWŽķąYXÚPaAjC…ĄĮ6˜ Ō}WXĪX2Ö>Ā‚Ü>Ā‚|ŋ€Ö cą)Đ_ģ )ˆØL 4%2Ģm!šĸ[ƒĄlĩЧŨf5€ę=Ԁ <đ<đĄ¨c(đ@CēĄ ŌÄ š‚<™Ŗ)Čąŗ49k—Ļ gíŌäė—49ö‘Ļ ~Ĩ)ˆĮ'4ņĄĐÄ]ūh rF’Ļ %sģ{{ŽOį5°ˇf˛2öŦ˰ˇvnŊ>îrōž>zw~wŖd.ßŨĐĨÜēģšaD@ĘgŒØŅ×ôÅĸr+ĸBæ¤HŖ ko-wīY:ÚÎĘD ?c͎žÆšØÜšØ+ģWûēßp?aņŽļŗJ‘‚ķoœœŽ °A `ƒ) {ĄHČ|6â^);?`įžļS;ƒ‚ŧcįžNd`û3ˆ B5ũzûŧ"2‹~uŨė)ˇˆÍûÕŅÖÜ?‚&aOīß?šāy%НãyC†'s$]GæHēČ…ûGõ7īâŽ>ūY—’Ą7JĻyJâHJæ,KēėŽ÷n îh;ģCē¤ŋa:Zš IŠŽ %)*úŠą’9Šö"I>bįŽļS;K’|ËÎ-mėHŠĘ>ļZ$IŅ‘‘¤ČĪ’ĸ’ąˇ”’ļԜĪîí$á<ą_=m§7‡’ÎŪŲ¯ž–æĮ$)ō…¤ČĪ’bŗšZąsGÛéÍŧ$ɡėÜŅŌ~b~¤ZČ4 A*’ʆÍ줟I~tP‘lļ”ß`J…ō‰­čiŗRRīT1ûôšõ¨’Ũ:.g~nŲņOŽzÕlSęŠÜ÷Ĩžęė‰ÔSy8C=UÉØ:¨ÔSyF=U顏ŋ(ZOu–.õT‚QOmĖˎZęĢøcGÛēú3@õöĐĐÕrâæ¨ ?ãæ=}ÖÍQcæi5fîè1sßCųē? æü„?ô´qYT´ßō‡ž–3:ųCūĐŅ×øƒÔŨÕ>ÚgsĨîîøƒÔŨUÚŗČJŨũ†?Hūčh;RåËzZč]ß š+ā[ģ‚f+č"qwđ„™{ÚΎŨq3ņŽ‘;:NŽn;ž9r=}öČá“jĩ?Ís͟ŸTķėOĒ9ŅÅ'ÕŒâ“jĮĨä“ęë'ŸX?âRmg'pŋåT=-ô3pgKå3pž§ø œī)>į{ŠĪĀ›=ĨÖÂgáOėWOÛwú‘š’Ą™72w> wŠ\Æ[m;:-tZ™ ˇ—ęoiUƒ>ÚĒæ,…NČĐV5ČđV5ČđV5ČđV5%C[Ք mUS2´UÍąІXmUs֎æC[Õ¸U€ƒ mUsô ‡ųĐV5nCU"Ŗ6TÅ,襭j|ßU1‹ĪÅ,ČĐV5Į>( ņũBa2´UÍYģ† B[Õ Ã[ÕøV tƒĄh̚:í´U{J+Ô€Ē´ÂgŒŌ  ĨęĒ´BC•Vč†ĒŌ uUZĄJ•VøX(~`+hĢ_ģ*#PĒ2Ũ UF kWeęÍnj@ƒĒ"ú|> ä|> Ė4°(ĘėČeæveækeækŠåûúÉíúIĪ č'OŠ~ŌĄA¤./dŽ˛™¯ĩĒQdFĀŪrū]B#sėáoiUƒ>ÚĒŪĒĻd.ˇĒŅĨÜjUģaD@ĘgŒØŅו[ua]™ ĸr=€¨ k$UÕęqîŦ˛öėvûÎēÕvÚ@üĖÎuô5ŨØÔâ `ĶSûōQ€û‹÷´ļkÎŋqJģZšäRûØļ8ž "sÚ’đ„;ښ6+PėÅũ6ĢœĀGŌvŪ;íÉHÂā2’0Ŧȕ6Ģí‘~׎>§ÍjŖũŽííwEhß.÷ģ"Ô÷Öûõ6+ŅvŖÍŠYü^›•hqÚŦ¸ ‘Ž ÃÛŦ sŊÍę ;w´ŨhŗzĮÎ-N›ėÃÛŦ¸ Ō=* C†ˇYaÎ×ÛŦدžļmVoėWO‹ĶfEc”Jô\¨Ę”Ũ‹+mVOØšŖíF›Õ;vîhqÚŦDÆiŗ‚ i›=:ǚfˇôJ›Õ[ŅĶÆÛŦŗ¯ˇYõwëëmV˜oŗĸžZ ßÔi8Sĩ@Čđ6+‚U-zx›dx-/ĩ@‚U-КųJ›ÕūØŅvŖÍęĐĐÕrŊÍę7īésÚŦhSõQęÃĒ>Ja–Ēr=¨RVõQŽõŅËž§ęĨø^OÛiKĒąīø^OË™īĄÆûŒīuô5ž‡š1öąų%tŠķŊFÍ)–ūæÁu@ ųwč(;m Aú očjáenēRæĻû ĘÜvœž&é1zÂČmļ™Ką AĮ’•šcøš¸_*ėhâŽx:4ŗ‘šPŪWķH/ôŅ^g)RMĖÔČÕČ4ObJ5EÉ4ObÕČ´ŋWvÔ; ĶôEHeBe‹āR™P2´—ÃąT&Ô|lĩIjΜ…åĢųСü=™ƒåĢąh/ŸX>_Xžš-Ûr‚1+=ôk]%C#™û!ŗ3á´ÜĀ>•ŒÕ#ė“ûا3Ÿƒ}ĒcAqTC5ũ?T[Ęø!Ÿ1øˇ2ø÷TáojU– ãAüÍYšđ7%c~ƒüoø›ŌcJāoüpŋ)=–ģ sæ# KŲĐŦ \ˆ;*¸ ßvpngp>gpGpg>ÂUœĩ Wqæ#\ÅK؄3gÁũåä:“ËęŒtĀP%BįyÔEãŧ3”4ÎsßAãÚvŪv¤d.ˇŅĨÜj;r ļ#2ü×M”!šîva;X%í\÷d„í@†ŪÃBÆhÅvøēĀv0–%ž`;tΊíP/Pl2œíP;+ļ=ô—Ë”ŒíĖÛĄkWl2g7f`?=Īũr“ ´Ū‚[=zúšPp5îājÔŗWģ+w{Ââm§íW`†oDĢŽĘ/Šc+~IŖļâ— g—‹Â7°rGŲõ xĮČ]-ôįšyh%ĻQQQbz&%ĻQQQb%ĻQQQbî< Ä|é Ä—ŖĸĸČOxOGÛŠû€€ŋã>=-ô'ŕ}V+#4žîŠĸņÖĮøõhũvîiã?= öâv‹äë§(Ō%āy=īŲ÷dÜrˇ\āVd  ûCíXN{ĸĀŌõNīŽļ_Og  ˇ7ƒ‹›ßÕbí č ÎVF ķJûĝų~:CæŦPú‰ŊčhģŪÂøÖ^t´8—Ԇ î‹Lå÷!Ķ@yûvOnx˙ėEOÛ)šxc/zZŠBc‹"ĄîJ†_Čđ=ȇ~!™æÃYá|íĖ—Ī)ôŽŅ*ģũëp%õåq<ũ:Ā•ųL}žĖgękEÎŋĐķÄ×J_s}¤V-sV[:R-Ÿį•@ŲĶbx¤T=;›lŽä§eNPž$Ãū î&›Žļīg6Û%ŠÛŗæ‘ē[kļÉU˰ŸÆÔ26iPÉĐ÷´4čøš¤Ag풨´Œíö?RJkįķ^ûGŧ­§íûÉ÷!’ĀŪ:ą]-'1MŌâ3kīhûĩéhøL˛­ĖųwīŲ§§ålä#•?zúlh ũŧ‘ų„Î?˛ˇs4Ĩ–vã¸Hmí—éiģO¯ã4;ģJ0F8}čŨ•ŒÁecp`GF0—Aļ™öí!ÉցŊįĨ˛5ô\ĪÖáũw¸zÚÎŋ"”ėڛÁE'ęiir+rt`o™Š =W?)Ɔm§ĩŧcΖ&vWXûôpdÎ~V8ãö´Ōb ˜7lØĶâ`ĄËįTaŖ'ėĶŅv†Ē6ęÛįn6íčk˛)P,e;C[ Ã^ŪŌ'ŋųQqAv>WÆģŌŅöŨ´ĸ)|Ö ĒŖđŲ-ĄāwœŋŖÄ^_´ĐŖ`1ĮŨ#ˇklVOÛYˆ‘ģģˇŦÜĶÂnoDš|Æ<mgFîŠ0=}ÍĸĮũĨŽ ėgČė%÷—ÚâļSų¸ŋtđ‡Ü_:ŅLnoD*š|fw;Úl¤’›F'RÉMc+sį~nę1‚1Ÿ~tãʁā2B Ŧȕ"%ūæ™"åĖzA™YŸĸ"(aŨh™ĢO˜đõŪšĄķ6Ädf–ŠB@æŒ>RôVpúw´;AXú6ŧ[ėėčkŠ (ܚ ֚=2AũS‘ ȰÖ<%C[ķ´Öšįøš‚Îtí ÚBÆ;m­¯üđļžļĶb'€ô'ļĢåzņ‰ĩw´5ÅNĀq+sŪî÷ž}zZNGĐ˙Hčékč™ā};^¤¤G\á}š‚€÷š rzÂvæz;_>˜ í>āœ=m_(Ģ&ī|T´°ž1…ŠAE\FPjqdĩp ‘qĘĒ"ã”UĄįzYķ{ĸŦ*Ún”U{3¸čD=-NYUdœ˛*ô\/Ģ>aÎļeÕwlØŅâ”U­}zH2×ËĒذ§íFYõ ö´8eÕËįTĄš'ėĶŅvŖŦÚˇĪ×Ëǘ/ĢÂRŧŦ ^VÅÉįeU;Ÿ+eÕ'vĨŖÍ)ĢB†—U…/—UßqūŽ’ŗī!ģņĒžžĻ”wy§ëØčޞ/tŧĪĐ6mˇäĻéŧAŽ‘áIJP—ú›'|Qú؃/ŪRÅAf˛=Ņ‚â CŸĪÕ2ėet%Ķ AqJOķĖ끺”ĖfeDäŦ]dT)ˆČŅ#˜E­Ë>d#肏tĄÖÅ|qlˆüĪ÷ ™š¯ ™Ú™ŗäRžČuJEM’ëœųHŽScŲZ•ä:î?ČuJÆĸÉFŽŒd#%c˛‘läŦëH\ ;Ÿ;_ēÄ^>đsžëāįŽžƒŸĢŲØŸÍˇCĘáígr;äLGn‡œeÉíwfÜqgÆí3šRcŲGjäūFÉØĮAäū†Üßp;˗bÎÚå[.g]ō-—“äÁĮSåÁoÎĮƒ/žĖņā‹’ąß°Ëƒ/ZO훏Ÿžx{q<øĸ÷ÂūØŌņā‹7įãÁĪÎĮƒ/JÆ>˙+ž8><øâ,]|qĻ#ž8[úų⋺pyđÅqyđÅɀĮƒ/ŪdŽ_—_ŧ)ž8ņ@|ņ x<øĸõØ9ž8ąP|ņÆ:|qb<øâŦ]|qļK|qΟ<øâé9|qöT|ņæ|<øâÉžxs>|Ņzėã29 ë8úc-%/mĢ{FķRōŌVëcy-yíĪ~uëžĪ›|™+<)’曯=@Ŗôąh”}€FË\}€†/åÎ4wŒ6öŒ;úlĀPėŽ[ė2gÕ]°ŊŪZž\Ŗ‡ļĶú2¸ä3ÖėčŖß{Ö7Ŧ™IqĶë~ Žú„Å;ÚNëq`ÂoœœŽËŊ§a[cŸĻ{Ąø´Čœūøõvîi;ĩ3Øû;vîiažļ'ŋW€Œåî¨đŊ@ Ā‘‘€ČدjôœĒdØøjß°?#5;å Åã'ܧ§Í–ÖU….öĩ‰h.##Ÿ÷z2‚¸Œ`. 2MŲ:ŗ'•Mŗi¤pzđ$ģæ÷[zÚÎb…ĘŨŊ\E3=-´ PŲ‡=q§el92ôc_OFr(dx}׎ëJ–{`O{ÚNû ũŪØĶŽ–ŗ‘‘SŲÚ_ >Öūúī 7ŪĐ65?R&:ŗž"•CŠWŠlņ‘í¨Į¨lĮõ ÛŅhĸ˛dxÅÛFœ^ļãöAļÃŽŲL†lĮį,ŲC5ה’ėt.|6ûāęi;kįA}§[LĄŖŽŠŖ\OˇM•ë!söŒ”īŸ0eĢėô#Ü ÅcŸ°sGÛõNíˇėÜŅbũYąaęΊˇZžÂ[°aOÛ÷“xė6ėj9ãė`Įlä{Âwôņīø”Ėå^íˇ,ÔĶŌTĩ…Ã͈¨8|Aļܛ€A• ũZБŒ¨d&gfƒîČá˙kųĪ˙pļüendstream endobj 23 0 obj << /BaseFont /Consolas /CIDSystemInfo << /Ordering (Identity) /Registry (Adobe) /Supplement 0 >> /CIDToGIDMap /Identity /FontDescriptor 25 0 R /Subtype /CIDFontType2 /Type /Font /W [ 0 [ 549.8047 ] ] >> endobj 24 0 obj << /Filter /FlateDecode /Length 343 >> stream xœ]’Ëjƒ@†÷>Å,ÛEĐņ’ ˆ`4ŊPÛ0Î1ę(ŖYøöĪoč€Â7į?÷qŗ"/t; ÷ŨôuI“hZ­ ũÍÔ$.tmĩ#}ĄÚzZ‰˙uW ŽkËyœ¨+tĶ;q,„ûa­ãdfņ”ĒūBĪŽûf™V_ÅĶWVZ.oÃđCéIxN’EôR ¯UGÂeˇMĄŦŊæõy(>၄Ī,QMŨ+‡Ē&Sé+9ągO"âŗ=‰CZũŗËÜ.Mũ]–Vîyž—0ELĄĘ@& eĨ„2‚R@[Ļũ13О)HAPú'ĻTÂīōA9ųvĢ-­ļĘ3脘;Ļc ˜rD rĮÚwø7…ĮÔv,ķPkĸ˙3“D‘!Ú 0”ũo%_ L=¤Ū¯É~YÃō\î;ŽoÆØõō›âŊ.m5ŨŸŨĐ‹×ōũF7ĩaendstream endobj 25 0 obj << /Ascent 742.6758 /CapHeight 638.1836 /Descent -257.3242 /Flags 5 /FontBBox [ -20.9961 -205.0781 551.7578 849.6094 ] /FontFile2 26 0 R /FontName /Consolas /ItalicAngle 0 /StemV 128.9063 /Type /FontDescriptor >> endobj 26 0 obj << /Filter /FlateDecode /Length1 21600 /Length 12013 >> stream xœŨ| |EŪöŋēįž3Éä&¤Ãš&ÉB¸29—@$˜ƒDBIQPŒ(Ģx#ĢxÃ$ŧ@O\‘CŨõ ̍û ޞˆdō=Õ5 ˆîžßûûŪë÷MæéįéęęĒÕ˙_Õ՝Nˆ‘•–“LéÕķĢZē'\F$ũŒĒ^ÔĻüuô›—%n Ō‡ÔĩĖĪÖŊ=˜Č1ˇqIŨÕiK%rõ!ęŗŽžļĒæë×_ Ä!õH°Ž•ĘĄQõ¯ŸßvuöKŽl"6ØÕØ\]EÁ$JŪF$×Νēē%p>{Į"‘_iǚ_ģ˛÷ŖõDŧDĻÍ- j[úÖ Âņ ØYJÜvúĒöĒ ŊëŠā?‘ŲÄč…/b’8ŋšō­´Ūޙē+ īb×D’8ƒoõ§zNąŲēēŪÃŊįtWúĶ/|tjĘĮTEzĒ$˛‘‹j‰Bč /GWGå€ČĖŲBĶLšOoŅ~ʧfz–vЃô7Ę Å”ƒc9ô4R§!‡ŽR)g4_\aīaœũ¯?ŧ” Ÿ•´ûķi!ôlčgéFÔø,̀ž pKvP.T>%P §Į~UZÎŽųMĪūj—s÷%6üĶÛFßŅYv?[ÉLėYÖÉ\ôģiõ8ë;Ęa Ģ ;Ņ'­Ŧ›VöžcģĨ0Ģ` m¤c°īvXŊ–96 Ä=L"3ЕčˇfZ{˛Š›>Ĩd*§U°ė1¨NZŠ>XŒļ|A§Yˆëđ˙LEíwcģ’nĄČw˜—ˍEŧsNĘ'’ŋ é:ęeŌ>é-éĪŌ'ō‹ŠMq(ŅJ_ĨŸ2Lšą_To¯ZB ÎØpIžP%Réƒ|9Ę 5ëũIõ퐞=;{žęŲxîøŅkŽŽæiG—ytĖg+ũ‘׏>ûMŪÆÖ\´+Ķ­´…vŅ6z­ŨL %ĶŸčfjGÛîŖ0æ^ĸ—éÚM{čUzöŌjôŌ!ÄëaôŦíĶŖ…FŒ3ĸՊļR#˛íˆĮPôV…SERES õĄXzą‘žĸįčjA#i"ĘCyˆŠ*¤ŅˆĢ14–ÆŅxš@—ŅDšD“Š˜J¨”Ļ ×Ëčt?ŧô4Ŧŋļ#ˇŌķô mÂ|ĶA]t/õt}HīĶ>zƒŽĄč DæJzvŌzz‡ŪÅxēƒú"^ŠG?9Š?ây ¤A”˙§`4ĨÁ.JĮXË$7 †§ŗ)CwÃ÷C¨ņ31SŅq9ĸh]A•Ûs¨qSKuAõԀ8›GˆÁ¨‰ŽĸÔJmˆ™fZ„¸]ƒ8ZKwŅ:"Ț+fΚyųŒŠō˛ŠSJKŠ'OšxŲ„ņãÆŽ)]XŸįÉ5rÄđa9Cŗ‡dšŌRS&&ôwö‹‹tØmÁV‹Ųd4ču˛Ä(e´ŗ¨Rņ&Vzu‰ÎącSųžŗ U%Tz$ũ:WŠTŗ)ŋÎéAÎēKrzDNĪųœĖό Š)Ęh§âŨ_čT稌’rčÛ Š÷„Ē'ĒZ—¨îb'>g(Ŗ#ë /ĢTF{‹ÕˇŽ,DyVKŗ Ö’šB+¤Ę;ĐŲŌÁŽbǐŽÖ!‘)Wë•FWÕx‹KĘGÆÄĮW¨iT –å5xjYJˇ™nU:RvˇßÖeŖ9•É5ÎšĒ™å^š 'ĩËŖÛÛWyíÉŪAÎBī kžŒD“kŊ)ÎÂŅŪd' ›PzžæÕ'؜JûOã'Ž˙:ĨƟbH°ũD\ō&žī&×4Á6XˆöÅĮs[níōĐėx——”‹}…æÄt’Į•\á•*ų‘Ũڑ°2~dšväüé•ÎxîĒŅ•ūīĸúHīō9Jj z_ũ&ā‹ãŠWNŦœS]ĪšĒļŨYX(úmjš×SáŠōˇutGē ųĢ*҈Ū %å^—ŗÅëpæ‹ HP¸ϔ̧øOķ: ŧTYí?Ëë]ČíRFˇW yYΒōäî=Ú1X‰ŲĘĮZˇÃ^§$Žn/¯ŠķÆUÆÔ >ë”ō˜x¯§ŨWá,¯­ā^rÚŧƒŽĸēxĩFõ,´í’ÜZfŪrc‚I)—bä î-$(EØ8ķG†îRwšGķG(å,†´l¨ÅŸƒĢ_•ƒ9Ą`,?$ķS ÆÆÄWċĪŋ0)Æo“>Ákē¨,ÎÛ$ęų§Ļ‰ÜÜ AĘčÚ‹ üUĄzŋūŌ~ßN‰÷…ŋbœaâîĢ’0r‘&Ą5‰{1RņRąRîŦuV8CžârŪ6Ū×Ē'LqN(™QŽzÛ%Sĩ'Ž=˝ŧR°(9FķŠē?FŨ?ŋ;ö’Ãã´ÃJģÉ9aJ;/Ųé/ ´Ø8ŽęÖĄ!ƒ1.‹0ĩ9‹Ēœ¸ÂĩWuõ.ŸĶŪáņ´ˇŒŽŦÆËpŽĢiwN)ŖšVZž,æ^UM`Ļæ§Ļ`âÉīp˛Õ%ļzƌō6,.WO-ī”˜TP™_ŅŅĮĘw*˜ŲÕT‰§ōDžŖđ^R)vLjū˜ĸåęQš îWw1RĶLZŖę.I¤Ų´4 i:‘æQĶøŠŦG˙bŽ­Ôpß\[Qß^YÁG…Ïø2/sŽ"¯äÕÁ$C€×âŦÍ÷Zų<=—§įŠtO7"*X8Cįđ ŠŊŌ‰I ŅTN1LÄĄĖ‹Tēz{§–Įī9Q8› Ė(÷š“1ņëÆ#ߎJ$ņ.¯ŽâvPY9?ט0Žē1Ģˆ,ãŧf”`ö—€Eę9<qR5|Ēį/ĮŽwy…ˇ"™WZŪPĄÆ˛ÍKcÃāvQĻ>‘WäĒhqfĒãĀ’°Š“ļŅ”r‘ƒ]TV!:ÉËĢ8T]Š ˇuT=q.&RKŒHŠÅ|¨KŦUa‰ņ$Ū,9ÁhņšĶP ž\[ĶøxÔ'+*„ņęŪ*ÔmķZaQâE]é?ŊƒCã¸-øŽ‚Š<ë^LI•:¯Æ´ÂVK2â°70a\f~qž)ÎĄÚÉ&>AXũeėŠFŪōôģœ0ĩĢ÷ į’ø‹>Š)N~eāI1;ØTŅ~i‚÷ōäÔĶĨЁjr{ģ)đ÷Oũe <Ī×ÄgšøTŸhâ/šøŗ&>ÖÄGšøPG4qX‡4qPhâ€&Ū×ğ4ņž&ökâ]MŧŖ‰ˇ5ņ–&ŪÔÄ𨧉×5ąW¯iâUMėŅÄnMŧĸ‰—5ņ’&^ÔÄ šØĨ‰ščŌÄ효Ą‰íšØĻ‰­ščÔD‡&ŧšØĸ‰į4ņŦ&žŅÄ͚xJOjâ MlŌÄãšxL5ņ¨&6hâM<¤‰5ņ€&î×Ä}šX¯‰{5q&ū ‰uš¸[wib­&ÖhâNMÜĄ‰Û5ŅŽ‰[4ąZĢ4qŗ&Vjâ&Mܨ‰š¸^×ib™&ŽÕÄRM,ŅÄ՚XŦ‰EšhĶDĢ&hĸYMš˜¯‰FMĖĶĕšhĐDŊ&æjĸNĩš¨ŅDĩ&æhĸJ•š¸Bŗ51K35qš&*4QŽ‰éš˜Ļ‰2MLÕDŠ&J4QŦ‰Éš˜¤‰‰š¯‰qš(ŌDž&ō4áŅDŽ&Fjb¸&r41Tؚĸ‰,M ք[™šČĐDē&\šHÛĘE^ļt3ÕH+Ќ[&ŨˆŊ›°÷>ļŒ\Ø6[9n@ÚIuëÂv aģû-|+-ĮŲ×ãėbl—Ų°õ-€,-ëŦÉ+뒮ëĖëÛ)-•ŽíŦ‰ ÎK‘ŽĨ\ QŪĩô9ĀĪŊ%_!]ƒũk°…´j Ô÷ŌÕ8˛FŨ6Ģ[ßJ‹P˙bÔoÃV‘{J X^\ŗœ×$åÅ5¤Ö—ÍM­+ĢM­)ĢNSV•YYæĒĖ­”ŽČœ]7ã•RˌŖ3¤iŠeešeljꔲÜ)l÷öˆú-M-)+N\Ö2™š&ŗGÆ˛–ąl÷XÖ<–yÆ˛ĸÔŅe…ŠeųŠyež.ŠŠĶ\´Sj”æufÆažēßģ[ĒÛj(z˙Š&{¤ĢļfdáđU}â‹ē$šŗoŋ8ŦąÎ؁ R‰õ ō ętNĐ/}’Agũ,茠:-č'A?vƸ@ôƒ S‚N ú^Đw‚N:.č[A˙&čo‚žôĩ ¯ôĨ /ũĩ3z(¨[ĐQAŸ úLЧ‚>ôAôą }(čˆ Ã‚ :Ø5ô ‚Ūô'Aī Ú/č]Aīz[Đ[‚Ūô† }‚^´WĐk‚^´GĐnA¯zYĐK‚^ô‚ ]‚v ęęŒĖ=/h‡ í‚ļ Ú*¨SP‡ ¯ -‚6 zNĐŗ‚žô´ §=)č A›=.č1A´QĐŖ‚6zDĐÂô  Ũ/č>AëŨ+čA´NĐŨ‚î´VĐAw ēCĐí‚ntĢ öΈ1 [­´JĐ͂V ēIЍ‚n´BĐrA× ēNĐ2A× Z*čAK]-hą E‚ jÔ*h ĢĩjÔ$hž FAķ])¨APŊ š‚ęÕ ĒT-hŽ *A•‚Ž4[Đ,A3].h†  Aåáe é‚Ļ *4UĐAĨ‚J š,h’ ‰‚.4AĐxAã4FP‘ Ņ‚ Ę”'Č#(WĐ(A#4\Đ0A9‚†v†Íe "(KĐ`AîΰbPĻ  ‘˜.Č%(MPj§S:K”ÜšJ4¨3„ĪÉ ”((APANAũÅ R:íY 8A}ÅvÚ A}ÅŠ%(RP„ pAa‚‚B…˛ ˛ $(PP@gđUEYIQA^N,HÄ‘§Ėáz€sĀ/ĀYāgā đā4đđ#đwāāpøø8žū øđ đ5đp øøø+Đ >>>>ūüøøø8*;>īŪöīīooooû€×ŊĀkĀĢĀĀŗ~7ļ¯////ģ€@đ<°Ølļ@Gāœ8/°Ø << <X÷4ø)āIā `đ8đđG`#đ(°xxxxxč’V{ZããîhŽģXÜ ÜüXÜ ÜŦÖwwXÛãnnlŅŦ%zy´ÔĩčúVŋ„CÚ⠈ļļ…Ém ķ‡ ZũŸ6ž×ĻîōĖ­Éğ×įõJ2n.$€D5Ŧđ=Ā/ĀYāgā đā4đđ#đwāāpøø8žū øđ đ5đp øøø+Đ >>>>ūüøøø8€ĀûŸ€÷€ũĀģĀ;ĀÛĀ[Ā›ĀĀ>āu`/đđ*°Ø ŧŧ ŧŧŧėv]ĀķĀ`;° Ø t€Ø<< << << <l6€G€‡€€ûû€õĀŊĀ=Ā€uĀŨĀ]ĀZ` p'pp;ĐÜŦV¸ f+›ÜŗĀõĀuĀ2āÚ<ž] ,ޏŨdm@+°hš€ų@#0¸hꁚ@P ÔÕĀ  ¨Žfŗ€™Āå@PLĻeĀT (ЁÉĀ$`"0ų@ār‘Āp  dC€,`0ā2 piTķ?3r˙ŗ>˙Ķüŋ}ø[`dl”ŗ}—žUJuÔJítmĸÃĖÄÜđĢúNÔfÚCoĶIf`ąė˛ūļŌ˙ũGÃßÖęũÎw}ī/Ŋƒô?øŽųf"z ú{ōqqLŋ’|uŊ§}×û>î¤{Í7ŗ— uŊƒzOJ2i%č–RŌÎčëô+õOę ]ƒÔ>üī˜đ{Ÿ‰čƒ+ÔwWŽŠĖßlÁĸė]ĨžŊ˛ˆ–Đ5´”–Q-_G7Ѝt3­Æ~+RÄŅëiRWŅ-t+ŨFˇĶt'Rn@ŽBÎ[ũ)ˇ€īTķō2Vĸ~FûEįŦĨģá‘õtŨOĐõ}™ģiũŠ÷žORsū:ũÁ™˙aõ}§Gi#=?IOÃĪ"íBĘ3ô,mĄ¤oTS6Ķaüt“~Ąsô=BœXX‹F´Œd1kÔRŊÚK3ŅkMū7}ZU;ާåh!oÛ2ĩŽWûŒ÷°rŨäīÕj\čģTû×à n×Ũhˇ_ØūG5M´īˇ­ãG?ü÷Úŋņ|ž§ĐZ/uŌVÚF;čy´|3ÚŪ‰ŊíĐO õOų{ä9ņĸWDŪíjî'/:ļå7Gģh—˙m˛=Đ;Ąøv×ų7Ėöú÷ÅŪõMŗ×ÕˇˇøģZ‡éC¨7é:@¨īž}HĶ_ųģô9} ?| ŸŖ¯čú–Ž#ũ{:I§č4|tž:‡‘Ëũ” OEa 'Ā[9˙9#ųŋåsĪo“¤9ė]éLĪx~Ãøŋ[)ĄUŊ'}3é_%’ éĢūŦū{˛ęŗČĸ‘Ūë=Íž$ŗÁĄ ë=MĶiÎųbĪüX¨ë/ÁÉ S€oˆt3ũøH@ŋîœųßū¯ąČdž$2×?Y× ã€g‰ŖDAC‰‚uĀr"Ž"ļŊDöāĸ7‰BŅķĄģ‰ũ‰Â/Ü ß""˙?ĀM‘}€ļ˙" ŖP~ú4 žˆ.ŽūûˆŸb ˙cč3õŋë~:*Į¸Âõ[&#ÅQÍÜINv_gjp ˙Ũ“ÍfŠ6žŒeŖDĄXhšp“íöØtRāz›m`Ô}҆{åŧ`Ĩ‹Ån¨ģ‡PnĪg=īas"$Įu‚š>í>Ōm;ĩĪžãę~ũPwF:ŗĮÛU8‚$§Ņ9Ā™58Mr:ŗÜ™}%–ÆĶûĨIYƒGIē sãåō4Oɟ;VWcX°&é˛yį †ûj3|] JHH\DPPDœ>æė1}Ė/yē9ŋ<,}“Z–7āás7ĻŽuĮÔ¸Kæö|ëNđį Q"ƒøĖ"ķ7Íå3hˇúŌ *čˆJ|÷# am4X,ębyėq†¤Ā¨î„CäÆŧÄ/ hâ‰\ĩu9ĖväP7š’ƒ6žŽÆqããíņŠŨa0ö•eĩevwæ()T“Cä3ãVŊ˛Đ÷4[Äļ×.ČēblŌ7‘#ę&uuŽŦ3 9ЏuėŽī쇭šŊļf°>Æ÷ę•ë,é—ÕŗžcÜ}}sŖ‡”ôü˜YœŨ×7j\2ž‹E(œîÄÄøú6[ tpË-ψĐ/ĖyޝMÂdXÛ}hVˇjĻN5“4 ]lŦ§nŨŪežoX ËdŠ/ žrCã}+ŲËԃŗŸ]ˇdš ĻŧîÛrëîEYįˆŅ[č;/ęĩRF‡ÁÂksāŠĻ#`øÂlfú/¤<Ë1vĄĢP÷§{CrÔ^ŠGÕa~ČŪž(éëžw¤¸žn)KsoŪ=žQÜ?Œŋ§/īR븝™­:“Ą‹mōX­ë1sžPŠĪėšNŪkût/ZŸå/:+ž}ė;(ų˛Ôs?°TŨĘ{î9ē~=ĘũĘũåęų;?:ö„ĮLĖ åéüöÂÔũŨĒ•nØ÷qmģ4‘ĩLĩ)ũĨŽu[<ą&“Õb—e” uču‘ÆHÉcöwõ~ä‰ 6KFl2 Ö{`@žÅbeŒŦ¨ČívŲŨ.ũ‘3”Üö–3ŌåvGîÍˉâlķ ä”eŖė”Ȳ3Ô‘ęÖemŽļŽÜŗ´k¤.âIĪAoNöΏeËŲŅs†ũøļoįšĪá6ĮĻMė8ˇ}1Ú}ļRÎ6fŌ[Ŧ<ÜqŨķ‚ y˛Ey†F0/37ם“ŗĖu­+2—šē]Ũ™öœn“ívD ĩÛŨōÉo|76m:ÆæŨ¸ õzæûfëcΝķ–^ÄM3âScAAi<>_ÛlryĀš"Į Shˇ9Gčų@9õēíPˇ09QW5kÚ5“ē?yÁ²ĮËãKfĖÉŧüŽÚ![>͛="6d 'mÄĶV•ŒM™X?ōŪgĘ+æ%: ˆą,ŊvjĪeė­čô‚¤ØŦA‘ã&ķE(mčũQ>‹;ÕPJÄĖd{mAÛęą9LôQ=ÁÅæ)ũšôh‚û„ęfuˇÚëš~‰āgã!n5î0AÂbŖ=<œûV>;úú *v]žŲ4á™9Ķn(OéŒČœ4$žp\IšûĘôķ§dHĻe¯ß2Ž_‚~ŧoéŽÚ)coÜyÍä›fg…e”ŒđE9"+î†îũA~V>Ką4´3˜"øī }ƒē˜Ųcž>˜‡ül-ÆÕĮø|K,‹>+ÃØž#ŗN¨Ņ×é ŧ—‰_'Ũ™áa‰‰ĸgÃՎ}ļđÎËžķõŽŋyWË¤ÕŖōV-X}ĮvEá×č;ÅĶāŽ+S ŋÕc§~Ļ@}äY[ŠĩÄ2%î'}11H4ßg¤‡úmA÷9ũ1-ėąsë ķ‹VloNŸ•ēåAø§Ģ§Ũ8=ĨsۜüģK’ëŨw<Âē—ŋq˘€@ļéėŌ—¯Ŧ{c×Ō[ĩąa]ę -„}'¯üjíŲ†Úą5.lŨį KŠÛ§ŽétIũD0žļŠũ&.AjĮÂeĸ[ R†+tŧCõiv˜¯Ü|ø|ˆßįōIIęÉ}vcJyéeĖÖĘģįŒ\øtcÃÆæœí˛RP“Ÿ3ģ0I/%'d†ß˙¸1 Ø|§#ĒhåžkŽ|éΊųKž+-h.NM)n)ö“:­U˙ )ˇCoębž˜ Ģ5ĀHáaAúĶÖ`“Én ũ…¤ĶöŠ– po‡í9n\älûlGŪÃÎ^uĨū ã×:ĖBYņYļøĖĻ‹šĨú[ÖåÛá{‰°õ×ũrL÷Eėiīڞlé­ĩ›ØÚH_ĢXûĖöÍÔ9ЗÃé2ǤÛ_¤ ֎nČ:žĪ kŠœ1\îbžĀéũ3Ļʘ>=cņ{— i{Į“_e õ-*}ĮÔŋ(E6 Ą"Vd*2U ~hØä™ī )Î=ĐgšzųĪåMĀT:‹“=ĮvÂîļņæ jēqŨsš\vž0Ømëļņ‰?Üŋ LD! #ô}å ËÂ!Ųi˛ļáƄ#1× H’CÕŲ…ī ?Ę­}ârę×NĪmRBŖĮ`Ļ ×_îļd×ōkžiĘ,02)46˚3wmyBa4kė‘×Ũ4åĒŅ}kæúÎÆ'GZ˛”a“Ķ—dĮj,ĪvÎ2iÅLwŒŖOf\bĻd‘úyf*¸úō!F_‘3ŽÉĐ?9="¯1="Å=<‘į´˜î8g“×7#7~ä0Ŋ9<)9YŽK/į1)‰s˙á“ÄēgôÆa$š:‚­|ÃÛâ fQ6Ģū—°â )Ö*Ņ.Sæ¯x§}pĸ˜Žœ|ã“nx˜üÆæé%Αų“Ü›7Mž\šrĪcԊļÖPWņˆž:ũJß܇3 ’BžīBŒō5×Ĩ3SŪ‹ĖzpųÔ!‚Ė ?XÕę˧%Xņ ôX˜TĮ:č'y2ũYō‰\Õ­ûšŗmŨę%Ą)cĄū+Ĩ6ÎØi߇ėc–|î/lüČØĖ„°°„ĖX?Ë3ל{|íZ=…%fÄöÉL KĖ뛑ûnô͔ëa_‚ĩđEJ€}1¸/éxž,Ž${’uķvKđΆiálj 7Q ũ‰4ň‚™û>&ÚũĄ–՝5$ Ŗ‰]bĢ~.ąá-Ãb"7ŲĸCĖkn“‹×°Į5ģĩvŽ;0 €o-”ŋ“LėøVŗ™,/ŗĪpë%ÃҏėŗSĶY͓%‹QÖé§čüžÆđČõ{úõŖ˜ēÕŘęg\íuž÷ĨlŪĖ^ûĘ7HŠîųJŋō܃ė_†ļŽ>€:͔´SõĨŨīK‹ęK:,OQęEÎģØuģJwúÎ_ÖŽq:m Ā’Jc;b#ų+IҁŒSjŽ[<i.Ãé¤âĐĶũŠcĸc&G—jq‹š@L ,Įu*­:į ņöķ×[įÅŌ!B™oívšŅawôāR1GÚC”éņVšĸ3ī_252-˓ô’tĀ5Â9¤"¯gŠôrvabcđŒĸžĨōņWrŠ3"ŽlæãØ÷iO…1‡ļD`ĖIá|ĖE•š€ˆH‰"JŦ%ļ_ŒÅ<ŠøuOsęEOŗÍ~ŅāÛđ€1ĨíG6—OWFæOLß,ß8xt’ũųŽžĢ¤‹ŽƒOŊ.ĖėũNgAŨĄ4€úķ‡[[qč‹ĢŽÅ20ęįābį}é…8me|ņËßSaū‰OgÉŋļķĒÆgįjÜ9č˛ų“šĮÄēŦą3ķ-|í։7ė^ļpxÅî›fŨ:Ë5˛nÕøYˇŠĖ×Rž:ų,ėrP ŨhQLÜš–DRWaĻĀĀč[qĀds™ķ 01WLö¸aāĢæœ‹–T܉ŋˇĻ"ŸpĶļ†ô+26?`€eÁ¯ÖTéWēoß ™n~ķĻ|K€oĻūæ'įūfEõ _ģĀÖ=jöÃÚÅdák—€>ąvqšûG .ĩ”™‹•Cį×.b¸dí’ CŗũKąHõ¯]rīZáŽz˙ķ{Ëo˜žÜåž8‹—ĩ9wßĪē_Ŋ{šÍ>āl=[úáÛcoęēfęÍŗ2ųō%4ōUqŋŦ3ŠöevH!/°(†ĸÍęė&…–üĸ׌_ōbŧįÎ!Z€Ŋõˆžâšō5xå)ÅÎ1ĻĻ{åã;įUžũÆM‹Ŗ˛§ėY*ÆčŦ™^@ŊY4e'Ĩ°­ÛcúĮôˇĀƒÛˇÚ-CœģQ˙ŪŨž€¨ąũœļ§•bôŪcyÄēÅ*YƒÎĩĨ'ÜÚs$ų„˙‘f íAŸ;UGōëí€ —åķ+ŌžRD_Y~Ą˙¨ŌYs\uĪ”–ožļėęĐĖ†Ë [§Ļ˜Ŋáęák&•Ū=$¯";2,§Ž´âšËâYHÖÔŧ´žA!Ž QŅ…š1É’bîĸŲž¤ši9ļĄ§+&&eĐ ČčœĸrŅÖ\ŦŠßŌ§bäfuØų n1MÁĻHŖí–ĀÉôK„ãŒė_ē÷Ģ3Ū!ūXB]SŠ ęD~ˆˇgĢ'ü‹éˇ†_—÷Ō.ß×, ŦodkKú‚QAÁĪtHw1GĒī•ģ|Rãŧ`ŸGĐßoČĮ)œR;$â툃aI%Bĸ°É“ƒĪĸGK.šFēÅÄį$ĢĩģíuQŠģë74¤–—¤>đđæÍĘČL"枪KZĶsŨטAFJ÷ŖÎ|ū uš(ĮŦ70RLFY6[Ž‘™ÜÕûžĮ,M%ƒžWfĻ ÷õCg‰{Fˇ›ß5îĪä!Ļ>P`Îü}§ŗ˛|ŎčMųxOũÆMŌēsԁYR—¯ū&pg€^g0X‹Õj6é˜Ũn“u’lŗ…PP NЏP,/÷%; ôõDXmÁv‹Îh4›&Œ%+ Q­âw˛šüAČÅĪAĸ.<A¯„¨wßÚķģøQgūL„?Mā]ū]ׯÆßēíĶSC’īZöPjđ$\ĸėąēFß4lÚú•đdÁĘÛĨ-"vŠĐžAhŸ•v8M]Ŋßxú:’âd(ëĖ#îŊ'†É˛Ŧ+aŒ{÷¤ø2—ÛåļíËÄwBių6‰,rpŒ'@lĨĖ–ŧ*h¯mŲ*ĶŪ Ęsé™ÁŦãšTaÖ_š‹į1‘QäQ…[g)ũĻ$+E}ǰX/ÍĨ.žnūÕ úÚwĘ÷c7[éģũsfbō‡žUėzß v†ũčģŽŨāãˇË4ÜWŠÆT${Ą‘¤„˜ą.Šëę=ēëķ,ĩdO°%°—Ĩë˜aZ¨Ãé0wnNÎPņt g¨ß“‘{ŨŅ"ÔS#]Lt&\ë{:ëĒ›7TG'E…ŽMŸ2žPņ5īķš‡} Ī]wËžëGHŌÃ˛žOnÍ™nÖÁÎĮpŨŪ;ÍäōØd2š$=ŗŖüũd Ļn“Lg¸ŗNdåĀxÛģ7ķĶŊü)züi!‹—÷ŸûPĘîųP:ÛsPš´LúôŽÕ=.”ũīiéËĮ8v'9$i;VmŠ îbÉ^$ŧŊŗ`D¸¸×Đ.ĮęÚņ;w帔ØūąļxˇĶ™—;rÉöĢ÷KáɞAIƒS2“ÂŌ#cGĖ9åžE=‰Ũßû´ßŋpvčŖøŸ;ü%ŌžĨ˜‘“:¨XŦp˙ˇxÉõŌõĩ䎚•44NÉNŠŒLĘV.Ų—}—ėq^šBŒ­„]ØpØū"é%™‚)~ĩgę%āԊ“ûLŽNŲĪ9‚ķ6<:m¤ŗŸØéį™X Öņsô(":M˙c4‹q>᎑?m<ŸŐÅeGŨ!ũėsgĖßēŧÖwíŪŨž™ėIŋäÔ?,žë[áKûÆ7õe^Ļeģ¸ĖmÛŦFūĪh..“?­0$$ËÆØ|ļ?åĘ­Ģk_Ûí[Ļ?ā+ëųŠņ›ŋŸŊnîöØWėv-/ŗëŠ"ũ“Xž÷í4ęPfģĮŦ× 7> 2ŋCēœg˙í"_(;áÛÉÆøęŒųˇũ|D_˜‚rũåHzc{ÚcÖé>04ʰKĘáëîŦx9åŒöŊ€˛ęn3$Ũ†x܈2V  ūdĢ_ß.ļÚãIŒ Ї(ŦVŠū Ē1QŗJüzÂĩwߥŊâɖzmŅ~Ķ‚xUƒÕƟ"8ŒXŧ:ÜnyÅø=/†g_>:9yÜĖÁށ—¯m?ü›ƒ‡ŋîĢÛŪÅĘ3&fõš52ã˛Á'ĻŽēÂ}[÷ß~Œ ëņ=}ŒĪĮ`ÛëÂļm҉úžũxOŲCŦzÅz €ÛVÛ/ļmßŪS{müAV(_=ĢkWnV ę-ˇg'ÂT§ķ˜jÚLnÚ,˜6㎆ņ#ž>xøØũ“Ûģž>o™oŖ0ÍøąoÄšcĮÔš|uē:ßiBđn—%ų'ĩÙkŋčl]Ũ/÷ãpŨÕO{d¯´ ÷J2Eō§ÕßîĀ|¯güiŋzĢŌ-Ž7ŌĻØgûāÎ9™Īv’qũŽÁÍhmÜĄëĒ“/°ܨĘŦÁÆĸŖÂuRXD`L`L„Y×7Dg°wąņ;Lր`ÍЅL)FŗŌ7* öĨđĐā>ŅÖ¸—t&=ĶšBXˆ.gÛrm’Ņ–g,ÔgÔŗgšũOVÜnūL%3ķŧĀDw¤û͈œn{V7ęMŨ‘CØĩņ˛3TE–[…[ŽĐ˙˛;>4^Ö>OX—Đ ūk˙ƒGûŨ΍ß:į‡_:?đ=āė%'kö­¸Íž÷íā¸Áˇ‘ÍömäĪ–Y’Ü-GŠŋSŒôāNVĪôgŒōFŽGø$öĢXī>÷˜<“CēųnŸ˙7&>˙Ŧl‹j?[Éæ×:¨ŋÖã'Ų¯ HO÷ë@ļˆ<~Dil˙H:3ņˇ;ŋôk•ÆúĩDAįĩL™Ō4ŋF]ŌjŋÖSŠ´Ū¯Q—´×¯tN:ä×fRä÷ũÚJĶä“~@Iē"ŋ”ŽëZü:ˆfgNž:EŠŠ]а¨ļFŠknj+kjmŠ­n¨k¨­šôPFZzz:Sũ‰ŠyÚh‰j˙\ĩ-(a RĢ‘^§ļ…GįKÕŌëÔ˙Æm›wžŧŠę˙ãíŌr)H^š[kÕ~iTíŠUŊ¨ų¤YmGŗjIŖÚĒq8^­FŠč#ÍĪZ¯.Bz#úƒ[!ÎhQÛØĸöo×Īq[šũmesīÎUũ4÷7^hđƒV”Ū‚ŸFĩ˙¨P¨Bõ ΎPĩ_ĢĨí|Û#uίú@¤WĢoJ^ˆÄ…j­ÂÛZĢD¤ÎWķ‹r—œ¯ī×ũ3Å?BE_ŠXšĢ–ÍcˇV­ų‚ŊŠÚbŪ'ĸ'ŋbtp/ÎA.^{ŠßŌäÖt“ßî65e>Rø™uū>^ā/m‘ŋ—jÕŅØäīßڋZøëØāÖĢ­ŠōŸUí?RĨFāīyŽîüXmRkŋ V×ĒmhđĪ Ú¸įŖˇæŧ‡§û}Rt>Ē‡Šąų{ãåBä_'§Š˙tTüĢsš <ŨĨļuގüŋbŠųô÷ĘĻ˙-†Įendstream endobj 27 0 obj << /Contents 28 0 R /MediaBox [ 0 0 612 792 ] /Parent 29 0 R /Resources << /ExtGState << /G0 35 0 R >> /Font << /F0 36 0 R >> /ProcSets [ /PDF /Text /ImageB /ImageC /ImageI ] >> /Type /Page >> endobj 28 0 obj << /Filter /FlateDecode /Length 161 >> stream xœ•Í Â0„īûûmwķŗiA<ÚŗđÄ„ Ö÷7‰âADd 3ŗ„‘T 냧n‰°)`9ïļÁ—ėĶ4.ø’gĖ:ŒXÍ2A7Nwk{dō’§RŲÎ$Ø:žžÅÄYSā ôŽR>TÚDčv„ėZ đë;ƒč‚S6ʈėãļņgEū¯ôŸ•ŊęĘE>endstream endobj 29 0 obj << /Count 7 /Kids [ 33 0 R 1 0 R 10 0 R 14 0 R 18 0 R 20 0 R 27 0 R ] /Type /Pages >> endobj xref 0 30 0000000000 65535 f 0000065118 00000 n 0000065559 00000 n 0000109016 00000 n 0000109150 00000 n 0000116536 00000 n 0000142031 00000 n 0000142534 00000 n 0000142924 00000 n 0000143151 00000 n 0000162899 00000 n 0000163156 00000 n 0000202557 00000 n 0000217099 00000 n 0000246943 00000 n 0000247178 00000 n 0000329218 00000 n 0000330839 00000 n 0000418569 00000 n 0000418778 00000 n 0000427015 00000 n 0000427246 00000 n 0000427384 00000 n 0000436680 00000 n 0000436899 00000 n 0000437314 00000 n 0000437543 00000 n 0000449645 00000 n 0000449854 00000 n 0000450087 00000 n trailer << /Size 30 /ID [<3625d229fd65c2e4b180a4f7628da041><3625d229fd65c2e4b180a4f7628da041>] >> startxref 216 %%EOF trillian-1.6.1/docs/storage/000077500000000000000000000000001466362047600157475ustar00rootroot00000000000000trillian-1.6.1/docs/storage/StratumDepth2.png000066400000000000000000000524611466362047600211730ustar00rootroot00000000000000‰PNG  IHDRĀĐ_w´TøIDATxÚėŨ ŧUe™8ū­倠PP¤˜7TT•ŧĨ†ŠFiŖyÉKäũ†ibbš¨iR™™ŠC#Ū5™0/Œ:ZZÚĐÄü‡~9˛Æ™ņ7Ņ´~ûŨŦ-ëlÎeŸsÖÚgíŊŋßĪįųč9gŸ}yÎËģ×ŗ×ûŧĢP 3-زÁũž1´uđkƒ6Ûôß7´éŸo>đ?ļ<đ­á[ YļŅF|5Ü&ž-Ô]á{ę>°ūīˇ?úW\ø7˙ģäĄ[Ŗå¯,ŠVŧö@)^}ūžčž;¯‰ÎúÂĖUŖļŲę--ëŊ]ü3Âԋą--ëūrם>ūģ—úũ(ú̊gŸ\ø—~dԊuÖYį•â}Œ–Fōlōzë­û‡×}ųwÕž•qöiĮūĒX˙ļx_­Ō @_wŨu˙ԃ7ŋÕĶâˇg}aæoŠEđ“ËĄșūłõ_nœwî/{[ü†Xõ‡eŅØZYŧßŲR @ž\˛ŨĮĮ<ŸFņ[Ž~ōŨhƒ ú-—ZōbĀ:ëŦķīoŧtß[iĀ!FpğŠ÷?QŠȃ™5üŌ.~C|ųŦĸqĨwĪŊøožÉĸūŅŨ_‹Æ}ôC˙(ÅäÁŠ×_ŧīˇY>OôĄQÃ˙(ÅäÁ{īŊõl”EŧâĩĸaCˇøŋR @dRü†XųúÃQëÍWI1y°*\ˇ7‹xų+‹ĸmF ũ) Vüæį÷_YĀKē5Úaûž#ÅäÁ# ožø×YĀ×^yftāÔIŋbōāÔŊvßáŗ(€÷Ũ{įč”ĪqАŖ×_ŋåßĶŪ úÍåE›n2ā¯'tđ`) /wö‰¯ĨYŸyʧŖöŨí9Š O&„ŗĀo˙æÉUiíūŧÉÆũīņGøaŠ oŽúđčm^īmņ–Ro÷ņ˙uŋŊwūš”G-Åøįm?>æˇ=-~Ãõ„øä”hđæ˙Oņž—Rōbz1æ$žūX1žÛu—m_ ÅlwŠßwß\íŋĪÄŋ|päVĪīc›b´&îwa1&I7ĩ2Ą—$ž^ŒąˇPŒEm´Á˙ˇø‡ķߍϸŊīÎkÂYß˙˛å īVŸIŽÔßoŲĶí<.ôX¸ŅU‰¯cJ•ŋ;Ŗ+†l1č—_ü§Ÿ{mŲ=gyCÁûÎĘ%ŅĢĪß]pîė?Œ>ä­nđĪ…îáXQ(¯ŒŋUKŧĄČœÕ‹ûj‰ æ;‹ņz1Ū-F´îēëūŠøßW‹qs!ĨÍÉâ7œ)~ŗâįũũY˜]Q@žQhģܸ%ŸX*ũBEQŽ hS‹1˛ĸāŪā¯9š\:œ‘^”øēŋ‚ 1Œ/´Ũ0ęÔbė(-ī›YŒų‰¯CßķiČŋ֊‚7ôđΐ–ĒGŲp1@>„âltâë°ÄyŽ´¤&l –üaŦ‚ v’=ģáÚŧ Ĩ¤fn,´]B>ą ‡ 5É3ŽĄGõe)ɍģ Ģ——…MļZ¤ :•Ôʂeˇõōw[Tņ÷;JZ:ö\aõîÍÔˇp}å›+žļ!ĐÔn+Æá‰¯-ĄmL#‹qIÅ×SĨhdá2;g'žļŧš9… ĖNN|6ך"-@=›^Œ+_‡ŦėLĨI…ļK¤Ã¸™(-@ž…ūŨ_‡ŪĪ‘ŌB7…åŅÉK.Í*č úX(poK|= ĸp4„>ņ‘‰¯į|°Ô@ØŨ7šY•Ũ}Šĩp‰Ĩ‰¯įW| Đ#aĶĒÖÄ×3 úxɗY‰˙ÎÜ)%@5ÂrĶą‰¯ÃŲļáŌBÎ$W%„oJ @ģé{×īÔ:lHTü_!„ĸŠĸĨeŨr$Mäķ§Ī^°Į>“ĸ_ŊõĒh˛xęĨĮĸ{šëũ¯oŊëĻhÁwoŅņÂōgŖ=uīû_‡ąÎE_”›&‹l´˛°úzÔ@3˜6cęúš#^^ąŦMÁûũÅ߉.˜;Gn„(Æķo,_öĐû__|õųĨ›ÆŽ=öŪũgŎÂ3 @“øđØ1NEĸqâ—ŋ{%zpéĸ÷ŋ~ä™ĸcf}Jn„¨ō ņ3¯-y˙ëŲ§œŨpĮurĶ`qåüËVß 9€&ú7ÜhÃRĄä@¨q–5'Īøî°Ë'äEˆ” â寧ŧi…ÜÔw„9ÖYg?:"€& ˙ˇ1–5'?ĀøčøąmŌ…ŲÄÉ{ã'Œ+­˛HŽĀ'}Ā@Žč˙­ĪøųĘß˙˙ŨöœØf#!Dß}•üzđ–ƒÛ|/ųīVčú€ūßú‹#=<š˙Íyr!D}P·ËÍ%ĪWĖB0!ũŋõa—æĶÎ=ÅY$!(ÂætvØļÍŋką>` Cúķßēį–6;5Wöø !/ž}˙íŅôC§ĩ)ČôōëR¤˙7Ž;š<đ­ÜiVŅ|qí‚ĢÛŦüģģ›ôŊ ˙ˇī6ĘŲwÚ>m–>&wŽBˆĘ˜sé9ŅÕ7ÎmsMoKĻõUŌ˙[Û8ėčmVī\t‡ŧ!z'>ģ´lēüuø0SAŦč€ūßl#,-K›“}ŊN…YÅąŗiŗ’$Į6ĖĶ Äô˙Ļ7Üq]t˙ã?x˙ë[īēŠt6An„}a_ä‡n ž{ƒ?ú€ yé˙í]„Ü%—1‡‚7\ÖDn„yŧņ!GÔæë°É–Üč€Ļ ˙ˇûv`Möۅ˙˚åFQŊާœur›¯Ã*šŅ I˙o×.7’,xôÔŊŅeķ.–!DÃEčžøęķÛ|VĩčBŊö˙†MĨÂY‹Pŧ;:Újİh›Q[—.)tåüËzuĖp6üû‹ŋķū×?yåÉhÖÉĮ98B4]„ũ æs^›]ë“Ļ=‡Ø{˙Ŋĸ3Ī;-—„Ķ ĻŪúÃÁØģė :4:öŗ'G žw_tīËĸ‡žũEt׃?‰ŽYp{´×~ûF› ÚŦ´ûrĩKģÃYŨdO\8ŗ,\!ÚFøp0šįAX.ŨŨ÷Õķø'Šķxkq˙\tsiž4˙ā‘gĸëŋuWtØ13Kķøn{îÚ§—ŠĶ ¤žúÃsœuŌņŅā-ˇŒ.ŋî–čå}ˇĶx`éĪĸvŨ=Ún‡íÚŨ9|/yYpÂNÍBŅŊû$7ũ í!mXšĮO>Ž8o]qŨ‚.įņķNéváöa'ëŪŦėŅ ÔM˙o8hšzĐŅ.{LŽ–žöÛ.š’qÆy—E[ÚĻt@–,xÃRéäĨŠ„Bô>Â%–BÛHųë“NŸ]úē4ŧ4q=ģ=?ķúŋEŸ:nV´Õˆ­údY´>`hõŌ˙;eÚ~Ņ~~˛t6 ;Må¸äš›ĸ 7Ú(ēäĢ:@BˆÄáÄûN›í7ũĪã!.ēękQë°!5_­ŖD=ô˙ž7÷‚čcÛnß̃Ļį^úÕhėø•Ä” !Dí₹_ŽÆm;Ą×ķxˆĶæœíļįĚļîč€Pũŋ/{88hķŌÆ(Ŋ=h qȑ3Ŗ™ŗg: BˆîØŋŲ AŠÍãĄˆŪiˇ]KģũëĒVũŋŸ<ęČčķ_ĒčZ=\˙ˇ_ŋ~ҞåogrāČ>:~ŦT!„¨Ķy<ė* l×ēT×˙->ÍLšB<ēė¨uXĢT!„¨ãy|ČĐĄĩîiÖ õĻúCŦ×ŌRÚ°*Ģ3ĀیÚÚĒBÔņ<žõ¨mjúzô@LJūßrpXâ–ŁĶí?\íŧûNP…"ãāŦæņÛîy8Úyˇ]jūšô@Š‡ūß{í7%ēúĻogrāôÅķ/ŒŽ}ŒT!„Č0Â5×ŗšĮO?ī˛â<ūéšŋ&}ĀPgęĄ˙7ÄÜųWfrāŸØyĮčÛ÷ßîU!2ŋđ‘™ĖãÛīŧKŸĖãú€ ŽÔK˙oˆįßXmŧÉĻŅ“/ũ:ՃĻ{ũi4xËÁu‘!„¨įX=o’ú<ūƒGž)Îã[ôÉ<ŽęHŊô˙–ãø“NˆŽ>áäTœÂŌę æÎqp*„5ˆY'sÂįRĮ÷ÜwjŸÎãú€ NÔK˙o9^Xūl4xË-Ŗ[ī^œĘAĶ×Ũ}pĖ(g…ĸĻķøŠÍã—_wKôÁŅėĶy\0ԉzé˙MƝ‹Fm-ZōR¯šūíŖŅƛn=ōĖJ…ĸĻķøŅfƒõzŋãūĮKKĒûz× u žú“qõsŖcN<:ŧEĪĪå†oF ]ô•ķŒ !D_´´|îØbņēqįņ+ŋ~{éLō1'U*¨ûōĩč€:POũŋ?_ųâû˙˙ÔK•6™ā!C‡–z‚ĢŨPåĨ?‹:ėČhëQ[G×ŪüÕŌ†,D…ĸöņø˛‡ĸųˇ^Īã'ukŸ~ØQÅy|›čÁĨ‹ĸû˙Aé=Ą¯_>`Čšzé˙ũÉ+OFcƎė3§|ļ´;t¸DR8#đĐŗŋˆ^üÍ;ĨĨeËß.,]rÍMŅ>L6´Ytæy§ĩ)¨ËŅ×g„ĸ‘ãåËĸņÆ­ĩęhõ<~bi)s(l;›Į÷žzPiéô™įžÖ<^žËO9ëd}ĀĀÚōÜ˙ûŖ§î-,ĩw¸ŖKkĖ?7šrĀūҰ#ĸõZZĸâKŒúõëm5bxtȇD×.¸ēÍ}&#äáâĢĪw*„)G˛ā g};ŸĮ/ĻLÛ¯8oWÎã#ĸƒ˜Ņé<^.ĻoŊë&}Ā@[yī˙ —ÉEp_žuvā*„Ŋ‹“NŸ Ã>{üpi¤ZÍįú€ ĮōÖ˙ú¸Â'ûyšDG{Ëô„BtÉŊ:;[[ĢMÜŽš\ŪúÃ˛¸ßŊ!—Kö„BT´Ŗ=ú:ÂĨ’n¸ã:}ĀЌúē˙7˜;ėō‰>?;PMė;mŸŌ.Ŗn…ĸũ–‘äYÖŧ~€Újæsž>`hR̊ !„"ÕXîŽ*Æė: S‹q¸?%ĐÄÆsa=k)Æŧbô÷į$u,ln2ҟhb­Å¸ąįöąū´€ƒ¤Žĩ4`О™ņœŪ¨Ž*XŨôĀ%ÅŨ$¯udƒøeĄ}Ĩ™ÎNö'Ē=hܤ¯} ??Р™g7ékīawV¯ōh#,stõŌ …Õg…ęŨĀx~gõ™o›e@Ÿø˙ ÚÁõęļBķ´°tĮû=@Ķ ÅîĶqáKĮB/´ U€z2ąāÍŽ„:GJ4ļđÉ÷xič–á…æí…ęÃäbÜ) Ũ.—´H ą…>0מ횑Å8Y€Hļj„ŗŊ­RŌ+>č€zĀn–†T `ËĄĖ˙B˜˙šŌįOŸŊ`}&EŋzëU‘rLØaÛč‘g‹Œãį+_ŒöŪ¯čåË䪊ØhĀF+ Ģ{î0˙›˙{7Üq]4û”äĸ1˙›ķĸcg#æ€ôL›1õs.úĸ7†â™×–D?zęŪ6…™ŧôMA,Į{īūŗâ?ũ3Ė~˜˙Ģû˙Áû˙˙ËßŊ"'}4§ËŊų ×>ū;l3jëč…åĪʇų gB˙׆mčÕFXr{ĘY'ËE˙.ōĐųJ…uÖYįf@ķŋųŋãx|ŲCmVôˆ|D˛ø5vÍ˙ŨĻ˙Ģ÷qņÕį[r›ã¸õŽ›ĸ9—ž#úĀ0˙w+žuĪ-ál™\ä8Âō}ĢŽĖ˙Ũĸ˙ĢûÎø†ĨÎrQ?g lDĻ ķ5}ĻĖ#uö7ŗÚüĐ-úŋĒ‹Ÿŧōd›˙wÆˇ~ûĮÂ{Ëæôaūo/N;÷ķ{/í W^ ķ?@‡ôUáqęÁûËEDØĀæĖķN“ }`æķ˙ûZŅĶX}Ûō`ūčū¯Î¯;čƒÆf_­Ėüßės@ØäęÁĨ‹Ė‡ á:Íw.ēC.Ė˙kč˙ęü3|B*ûNÛ§Š‹`}`æ˙fÜ üģ÷gsœ vEķ?@úŋÚî|õsåĸÉûƒõaūoüpV°ų"œå?vö1raūš™ū¯ĩ—ÂēŪŖËi4Û°úĀĖ˙Í0ÎCß˙ĩ Ž6Ī5ųnŅ>đ7˙MŽŲûŋÂÄ?fėhˆ6g€›qšœ>0ķŖ<ÉųŪÎÎĸ˛Í驗ĶlūšI3ö…~ äõ];PtáY–Ëé3˙7ęœæ2ŅŲîß>1˙MĻûŋf|œŪ/QU„3Áßž˙v}`˜˙ëhsĢdAcķ#ŅK†+?čh`ÍŌ˙Îø6뛚H7n¸ãē†=[ ĖüßqØŅ3ĸoŨs‹ųJôčXĄY?7˙MŖYúÛڕķ/ķ/zŨV4ō%}`æ˙züwiãB‘Õ†ˆÍ´‚Āü4…Fî˙ ×w|ūĨŪÄEϟ˜7ÚŲ3}`æ˙zü€sęÁû›“Dꛧ͹ôœĻÚ Ķü4…Fë˙J.MŊ˙ņØŲYd'>ģá–Öë3˙×C„ž|›ŠZÆ÷§t\Ą ŽõU˙Wxŧpđ2ëäãŖwÛ%ÚzäČhĢÃKą×~ûDgžwFéZŧ=šžcø´ÖĩČë2ÍÕãū¸Õã~Ô6īûÉĨqfÆŊ>0ō>˙—Įūņ'PûߟķçLÛˇxĐ}yˇ‹ŲĶÎ=%˙^DsíŨūā5ãūøh§ŌœŸ÷ė×ŖqoūčĨZ÷…7ƒ‹¯>?ŧåŅö;īzîEŅm÷<=°ôgŅCĪū"úÁ#ĪDķo˙Atč1ŗĸƒ6vŨs÷â›ÍÂNĪö&wįuļWôe„MwŽ<öđvĮũsįT?î÷˜}wҎõQ÷ķû]uaqėomŋĶÄâØŋ¸ÍØŋëÁŸDWßôíhĪ)D› }áŲÎãárdWß8×\#rķfGĢJãūę VĪų]ŒûĨqnŽŽ_Ė˙@CĢe˙Wø¤~ĖØ1Ņ^ûî_:āų_ßí4^üÍ;Ņå×Ũ ŪbËčĀONo÷SŌđŊCŽ8țąČÍŅO^y˛ƒqŋ_7Įũâ¸?¸ĪÎč3˙÷~Î_\û)äW3öCq°ãŽ{DÛ}âŅ3¯ũ}ģgĻ.›wąšFä"Âū"ŗO9ĄƒcG{î;ĩ[ã~ÛOėũôÕ|ėYbūZ­úŋ’ĄÍmV:°īęÍ 2žyũßĸ#Ž]Z6ŪXÂį°á‰7`‘÷^ô?y@iÜ_1˙Ļ^Œû}˛ĖS˜ųŋwsūÂŌ™­žĖų§Ÿwy´õČQĨ:ô؇Âל"ęáúÁˇ~īĨ• =÷#F~0zč§ëČJ­úŋ\ē¨TÜq˙cŨ~CHÆW}=2´5ēōúË×:Ë&DŪâī–Ü–‘E ˙öŅÆũКy}`æ˙ŪĖųĄøŊãūĮ{<î/šæĻâ¸]|õ…vōuGd´ņ&›¤2îúiß~ČoūV-úŋÂY°­Fl]ųõÛ{U”ãŦ ¯ŒÆo7žÍNĪBäņėo÷_šņ–ĮũĮk>îõ™˙{2ö‡‘Ęœ>ü5z´3ĀĸNæüá)Žû1íļ˜˙zŠũŋa—Î釙JPŽũĻ\ē_oē"¯ÆįA‡–ō¸Ÿ}áÜSõ‘ëų˙ įœ^œķNmÜū‹įGģyEä:ÂÜ<ũ°ŖŌ÷{ėĄ mY÷˙žŧbYiésØā!ÍB Ü_¸_×yŒō¸_üôËŒûA5÷úĀĖ˙Ũûaésšs~ØnÛOėXēz€ųEäwÎĪbÜīTÚA]0@JjŅ˙{í‚ĢŖ}˜–jPŽfęr"—Æũ”Ļf2î§rDqÜ_ŠŒ\Î˙ķ\í=õ ÔĮũĸ%/•>TŌú"ō:įg7îõŲ¸7˙ §ũŋĶÖŖ̉+Š÷{ØŅŸôæ+raÜĪc&ã>ü{:ô¨Ãõ‘ËųڌC2›ķ'í=šÃkŽ Ņ—.͘Տß}ō”>÷æ ĄÔĸ˙7l”öōįr„kë…MŧųŠŧÅV#†Įũ+™ûqÛmĢŒ\Î˙aķĢŦæükÜ^úpÉ#ō7įĪlÜ_}͎ŖéŸ0r9˙g=ö[‡ 1ĮˆĻ›ķ[‡ļē0@oÕĸ˙7Äz--Ĩ ŗxSˍļ9Ō›¯Č]Ŧײ^Æã~”>0r9˙g;įŋm3jksŒČᜟņąÎ¨m\ ˇjŅ˙[î‹ K6ŗxS¸íž‡Ŗv›čÍWä°lĢlĮũŽĩŋ&Ē>0ķu=Ā[g6öo˙áâhįŨw2ĮˆÜEhKÉrÎßyˇ] ˇjŅ˙[š4÷Ų3ēö–īeōĻpօWFGĪ:ƛ¯Č]„ŗk×ŪrgfãūSĮĢŒ\Î˙ģīŊOfsūŲ]3ëSæ‘ģ;”g{Ŧ͎ãŪüÔŊZõ˙†¸øęķŖÃŽ™•É›Â.{LŽnŊk7_‘ģãūđOĪĖfÜOÚ;ēå{ßÔF.į˙ ¯ŧ<:4Ŗ9×=&įü›Ė1"wqŅUe6î'îągŸ{ķ?P÷jÕ˙âņeEƒˇÜ2õŨŸ|é×ŅÆ›lŊŧb™7_‘ģX=îˇČhÜoÚgã^˜ųŋËą˙ücŅæ[ Éfėoēą9_äwÎßĸąuĖ˙@]ĢU˙o9ĻL›{éWS}S˜9û”čø“fy㚍}‹ãūK—^•ō¸?5:ūsŗõ‘ëųŸH}Î?öŗ'EŗN>ÎÜ"rSØ?ũcĪūMnÆŊų¨kĩę˙-ĮžēˇtxÉ+˙’ÚŽˆáҰ$Į›ŽČkŦ÷[¤<î7-Žûŋ×FŽį˙E?^T: œŪØ%ÚxSsžČûœ_é,pŖë˜˙ēUËūßdœtúgŖŨ'OéõBX^ô‘q.›w‰7\‘û8éôŲÅqŋwJã~ÛčŌk.×F]Ė˙Ÿ=íoĸŨ&ī—ĘØ;îcÅ9˙bsЍƒ9˙¤â¸ß7Ĩq?>WãŪüÔ­Zö˙&#p}lۏEģîĩOßÂ5öö›>#úØĮ?=˙ÆRoļ"÷ązÜ4Úm¯Ŋ{5î÷=đ“ŅGĮįcÜë3˙W.]üЇJËö{3öĮ~l\´ī´}Ė'ĸ.bõ¸UjĶę͸˙ČĮÆįrܛ˙ēTëūßdüí“?Œ>ôáE3>õén_0ū™×˙­tyęASŖš×]j)œ¨›X3îîҏgö?hZnÆŊ>0ķĩ›}õ_‰>ąķNŅ!GÎėŅØ—–ŲöžúĮĮĖ%ĸn6Ã*û]vėņ¸ß}ō>š÷æ .Õē˙ˇ2ÂN†as QŖGGˇŨķPUoázĒC†ŽžuT͗n ‘î¸˙PtûŦrܯ8î‡ĮũŅš÷úĀĖ˙ŨûSĻíWûcŠsūÃUũ¯}ķ;ÅąßZ翝9_Ô÷œ_ũ¸ŋöÖÕĮ:y÷æ îôU˙ī#Ī<°Öū ž{C´Õˆ­ĸOėŧsôĨËž-ZōRé“Īđ&°ôĩßF÷>ą,ú—.ŒÆŽ;&úūâīŦuŋa9¨3Á"¯Ņų¸ßŠ8î¯,Žû×÷į^ûįrÜë3˙wOŊôXģ—lY=ö‡GÛīŧKi§ÜĘ9˙ž'žNûŌyĨ~ßöæü0îÃ}›[DŖķqŋúXįÜK¯ęāXįüē÷æ îôU˙oØčÛ÷ßŪndøū!G}pˍ(œŸf´ÉĻ›”Ū§ íå˜sé9ŅÕ7Îõæ+rģVwÆũÆu4îõ™˙;Š3Ī;-š˙ÍyöįûLĖųw9öØŋøęķÍ-"—Ņ“qŋqŽ{ķ?PWú˛˙WŅ8ĄĖü/„0˙ä^­ûŋ~žōŚîļëIä!šaÜë3˙÷åXŦåŋ1!Œ{ķ?P§jŨ˙&ę°ŧķ…åĪfūXaéĐԃ÷÷f,rQü6ø×fū¯Œ ;l[“^ÅĐ[Ë|#ōÍ8îÍ˙@Ũč‹ūßZ~ZéēĀĸĪ÷å¸×fūīĢąč °ČK4ë¸7˙uA˙—B˜ų_!Ė˙@S¨U˙īƒKEĮÎ>ĻĪŽģ7ëäãô‹šG3Ž{}`æ˙°Ô?ŒŊžúw7ũĐi.…'jÆŊų¨ĩė˙ KtjšŅVe„Ë(€E_,{nļq¯ĖüÆũ w\×gã>ôÁ›īE_Ė÷Í>îÍ˙@îõÕõ…úĀĖ˙Bķ?@îÔĸ˙ëÎEwD?zęŪÜLĖáRĨˆŦŖŲĮŊ>°æœ˙ÃÆ?y÷yûw(wÃ+ãŪüԉZô˙†å@áā;/o Ą3ôezĶÆŊ>0ķ˙]ŠxŸvî)š÷ ž{Cé9™“DÖ§qoūęĮĒbDB‘A,7Ś˙…æ€f16įΝĨũũ™0îĄ×ÔÁ¸íĪ„q@–“î#ÅžãįxAA Æ=¤a~1fæøųíXŒ;ũ™0îhf΂aÜC:Zâã€Ļ|CãđoŒ{€†zMžŽŗįŽUw•?Æ=tÛm…|/ų¯V@ŧ\Œūt÷¤ĨĩΞoØÄb’?Æ=t۔BũYjõgø¨ÂŒbUį¯ĄĨ^ÆŊq1S°úDNŒ{z"ô@Nh€7ˇpIƒūœ÷ĐĄ°”˛úĮ', ŸXÛÔBcî*čĶQŒ{hĢƒŽû°›¯ á0îjíĻī]ŋSë°!Qņ…čQ´´ŦûOu6ėŪrđ*;ŅĶØdāÆŋ̎1ŋņ&˙ÉßNô4† úŸŽo„ã !|ūôŲ öØgRô̎^ĸGąŅ€Vę¨'uˏ1'ķĸ7ąeë–,ÔWöŒíwžđ{;ŅĶųĄmūwÃM7ÜÁņp|ÔŊi3ĻžqÎE_Ŧ‹‰č™×–˜s{ėŊûĪ ĢwxŦ ß~üCõ2æû|ÆĄĮ|ō?ęi˝ŋūú7žuÁĢüíüīi3ëSŅ„ˇīøF4͏¯ˇã J;æĪ÷ôízķÆ}~cū7į…3Õ˘°ņ€åõ2æËqŌéŗŖoŨsKÏû–?[ú7ūķ•/æ~Ėųčč×ß÷Í4îëíø¨BčŲpŖ ›âāēžâ'¯×˙Ў1˙ÔK•’ũÛČΘß`à ūėøÆ¸oļq_GĮ7@5ôĮˆfë“Ņ˙+š°X˙¯hē>`Į7B0ĐŽz菹áŽëĸSÎ:šiΈ]ģāj}Ā)Ē—ū_ã>˙ãž^ú€ë­˙÷˛y7í°CŽ8(ē˙ņčn’ããž>ÆŊ>`h0õĐúC’ f-ęĨ'˛^údęĨ˙׸Ī˙¸¯—>āzë˙=ķŧĶĸ—W,kĘą˙ø˛‡rŊdˇžú€ë­˙׸Åņ =ũŋĸ ûdô˙ŠfëŽËū_ĄØņp|¤.īũ1a9ЃK™|Ÿę“ézč˙5îëkÜ×ApŨô˙ÖÃW̏sŅĨ6}Āy|cÜ×߸× "īũ1a K!Ŋ)Ŧ^ģÃ.ŸČũåōŪ'SũŋÆ}}ûŧ÷×K˙o8Ã˛Ûžû8~ôÔŊĨb@pcß÷õ7îõCƒ¨Įëã }ĀŊQO×˙õyīŽĮë˙Šüųŧ÷;žú€ĩäš?&¯Ÿū‰ēī“Éu˙¯q¯8‹1¯˙ˇž#üížŋø;ú€äøFÔ÷¸× u.ëū˜°Œ%\Â%<Æ˜ąŖŖ­F ‹ļĩu´ī´}§hnõ–:N=x˙Ü/õí˘~č´¨ŗOļC?QOrßč}2ĩč˙íiîÃxˇ1îësÜᏏ&ũŋíå>ÄŪûīUÚŲö‘gčôlâI§Ī6Æ;9č—ˆÉ"÷Ú\Ģū_ãžųÆŊ>`¨sYõĮ,øî ŅĮŒŠļĩMtŌ_ŒnūŪŊŅ}O>=üÜĢŅ÷ūqtÍÍߌöÚoŸhŗA›Eáņ}BÛŗKÄ´÷ũp ŊĐ/9dhktÜįNŠnžë‡ÅÜ?=ôėkŅ÷z*Îũ”ĖsŸ×>™,ûÛæūsŅ-ß[û‡Ëš_P›Ü÷Ÿ{ܯÉũmÅÜī›iîķÚœu˙o9÷­ĨÜļMîīytiôõoŨūécJš}Ží­rp°kŒ÷0÷ÃVįū–ģî‰îįûûëKš˙tœû]S_a’į>āŦû“š?ūsŗWįūĮĪ”ŽsîyôébîŋWĖũĖNsoܧ‘ûĪ”rߓ?-Î9ÅÜ?ōĶĒr¯šXÚũ1áātįŨvŠÆŽŨz÷ŅË˙ún§ąxé+ŅNģîmˇÃvīŋ 4ëõīŌX*4ëäãĸÁ[n]~Ũ-]æūRîwk“ûfč“Éĸ˙7™û+ŽģšŠÜ˙l­Ü÷iä~AUšßq×Ũ‹šŸú¸ĪkpVũŋmsS—šņ7ī”nnÎägŊ ĨQ#Ėmr?˙Æ*sŋ õÜįš8Ģūß5š͝C•šŋŲ¸OuÜsũ׋ųũ>ÉŊ>`¨ci÷Į„7špāŦ į–&ŽŪ’qÆy—”Î_õõ+JKŸMöÕĮˇîš%úÆÂë‹y›í˛Įähékŋífî/+å>íĨB9í“IŊ˙7ÜWŗ÷ØĢĮš7î{3îCî÷ėvîOš92ÕqŸĶ>āLú׌ûîįū™×˙-úÔņĮE[ØĒ´L1œÍ1ĻĢívØ6š´÷îŅŽ{ėŅŗÜ7Ģ”û4Æ~^û€ŗę˙-û]÷œTĖũĘnįūČãN0î{;î÷ÜŊ‡š?1Õq¯ęTšũ1a“‚°ÔäĻ;ukRJÆ%×|Ŗ´|ņ‰e›ėģšhĮ]wŽö=đ“ŨūāĄ2÷i_v'o}2Yô˙N™ļo´ßôŊČũMÅÜ5î{0îwÚuĮbîéuîĶ÷9ėΤ˙wßiSz•û}u^ņ}c`tßcwĶŨˆ}Ļî]ĖũÁŊËũÕ_‹Z‡ IeDû€ŗę˙ { ėĐôŪåūĒëJĮKÆ}wĮũäbîėUî/ŧj~4dXk*ã^0ÔŠ´úcÂ&áĖo5KžģŠs/ũj4vüG-íFœ7÷ŧčcÛnßĢ7…ŦrŸˇ>™´ûΟûåôr?îcÆ}7s?nÛ )å~\jšĪ[pũŋĖÛŽ÷šqڜ/—ú‚õÃw'÷ÛĨ“û/WęėmîķØœE˙īęÜo›JîŋđĨ RÉ}sû§”û SÉŊ>`¨SiõĮŒŸ0ŽôŠZo'ĨrĖøÔ§Ŗ™ŗ?mŌ¯ęLJŖƒ6zöŠäū#Cîg6lpšũŋჟƒĨ˜û™ÅÜk\W™ûÍŌÎũgŽkČ>ā´ûĶÎ}8 Ũiˇ‰ĨēíjrŋYĘšßĩךĪcpÚũŋkr˙Zzšßu7㞏röčmîõCJĢ?æęįFŸØyįԊßr¯ÆV#†G.]dōī">yÔŅįŋx~Ēš6bDjšĪYŸLĒũŋ‡uxŽsßČqč҇s^ēšžu*šĪYpęũŋ‡}hôųŗÎKuÎ˙ūCKĸ1cĮÛ]æū“Ņߜõåts˙āOzûŧõgŅ˙{ØŅ3Šš˙RĒšŋ+…Ü7C„ÜŸrö9äūÃú€ĄŲ¤ŅŪ\ÂN|á 4͉)DØĩoōū{™ü;ŨqûĮĨŗŋŨŨĨëÜßMŪorÃõ§Ų˙zFŗČũåĨÜīm|w™ûAå~ŸFëNĩ˙7ä~ŗ r"´dąco#ûėr?Ž×šĪSpÚũŋĢsŋYFšoÜ÷Qî?2îãŊÎŊ>`¨3iôĮ|ûūÛŖOėŧcę“Ry‰JØļ>íM™)ޏîĒhúaGį>÷yé“Iŗ˙wîüšu‘ûFŒ+į_^ĖũQå~ËTrŸ—>ā´ûÒÁé‡}*“9˙3§žÍšôcŧĶÜ™MîOųb¯sŸ§>ā´ûCî:üđLrâ)gsŽ1ŪiîË0÷sôC3IŖ?æČcæ\67“‰)ħO<ą´ņ7ö#ėDyå×oĪ$÷Įœ8;ĩÜįĨO&Íūß°evš˙œqßiî§e–ûŖO8Иû/7LpÚũŋáRkYå~ūí?p)°Nsŋļš?耆éNģ˙7äū+_ŋ-ŗÜ‡9Íī$÷7Ū’aîÔ Í"­ū˜1cGG÷=ųlfđõßēŗtÉoíGč}`éĪ2Ęũ]Šå>'}2Šö˙f™ûđĻ.1cŒˇa€,s.kÕ }ĀŠ÷˙f™û{ŸXVڅŪī(÷[e›ûqkˆ>ā,úŗĪũ8cŧÃÜË8÷ãõCŗHĢ?f“M7îöÅČģ‹Ÿ~)účøąŪ:ˆ~ũúE˖ŋIîÃNššīë>™´¯˙[Oš7îģ—û´Š°ô§~ũß,sv6ļ1^ŋšĪCp×˙Í:÷áUcŧ~s¯ęDZũ1ÅģĘŦø ņč˛×ŖÖaCŧ ôAū]öFĒšīë>™´¯˙›yî‡ļã}”û!)垯û€ŗ¸ūoöšjŒ×qîķĐœÅõŗĪũ0cŧŽs¯ęDZũ1a™Ņŗ¯ŋ™éāmFmíM ƒX¯Ĩ%•‹Âwt&,ÍÜ÷uŸLšũŋĩČũÖÆ}æ~›†¸pÚũŋõ”ûÆ÷ëe›û‘#Sķ}Ũœv˙omr?ƝãÜë†:fĖĮŒŠ-ų‡Ė āoŨûŖhįŨwō&ĐIjX“Eîo˙áâTsßĮ}2Šö˙fûÛîyظīĸ5ĶÜīļsjcžû€Sī˙­Mîw1Æ;éCÍ2÷;í61•1ߗ}ĀYô˙Ö&÷ģãô?ôėkŲå~×ŨeŸ 3iöĮrÄAŅųsŋ’YüÅķĪŽ}Œ7b¯ũöŽžéۙäūĖķ/L=÷}Õ'“v˙īęÜī“YîĪ8˙bãž“˜œeîĪģŦ˜ûO§ö\û°8õūßÕšŸœYîO/æ~fŠšo´Ø{˙Ŋĸ¯Ū´0ŖÜ_^ĖũąŠ<ĪžėÎĸ˙÷ũÜ/øVvš˙ĖņÆx'šŋfÁm™åūĶ'Îjˆ}N€.¤Ųķ­{nÉė:Ā!vØeĮŌĩ†Ŋ tp-ÚëŽĖäZ´!Âß5íÜ÷UŸLÚũŋY^xuîw2îûā:ĀĢsŋsĒšīĢ>ā,úŗžđö;O4îģēęaGd“ûŌË}_ögŅ˙›õu€ˇßiW㞏Žrŋ𾅠ąĪ Ѕ4ûcÂ2Ŗ°QŌÂŋ}4õ‰é‡.o98õĨLĪŋą4Úx“MŖ'_úuĒšŋįҟf’ûžę“Iģ˙7ËÜ˙ā‘Ÿ÷Uå~“ r˙L1÷[¤šûžęÎĸ˙ˇŪroÜ÷Mîû˛8‹ūß÷sŋiFšßbK㞋܇+Ž<ųŌō\į^0äXũ17Üq]4fėGRߤ ,1Ŋ`îo]ÄņŸûLtô '§œû}3É}õɤŪ˙û~îO:!ũÜīģŸq_EĖ:éøbîOJ5÷{îģęšīŖ>āLúßĪũÉĮeûŠÅÜŲØŽ"÷ĮœđŲ”s@t~ŠšīĢ>āŦúÛäūÄŲéæ~ĘŅysĪ3ļĢĘũ‰éįūŠ eŸ 3YõĮLŪ¯čČãŌ›œŽ˜˙Ō[>í:^XūléSĖ[ī^œNî¯[iîkŨ'“E˙o›Üo™fîo.æūƒÆ}Õšß"ÅÜß}pt6šīƒ>āLúŗĘũåĨÜȸīVîH-÷Ŗ2Č}_ôgu|Ķ6÷ƒSÎũhãž[š˙ģs?&õÜ놜ĘĒ?æåËĸvŲ!š9û”^OLa9uXîōČ3˜øĢŒī.úv4pĐæŅĸ%/õ*÷wÜ˙X1÷›dšûZ÷ÉdŅ˙›Œ;-L-÷gœûF‹Õš”Bî/--Í*÷ĩîÎĒ˙ˇmîīˆ6̃Ü7æ¸Īîûĸ8Ģã›ĩsŋYJšß´˜ûÅÆtˇs˙bns¯r*Ģū˜dŧûä}Ŗ%¯üK&ϝÜđÍŌ§|aĸ3áw/Žģõē^ žō뷗Î,dûZ÷ÉdŅ˙ģVîŋųĩēČ}#Æüo^›ûÜ×ē8ĢūßlrŋĨqßÃ1՛ŗđĨÜoąeéC¤Ŧž_­û€ŗ<ži?÷ô*÷áƒkcšor˙Eßiˆã  Y÷Į”7ÅúÜi'EC†‹ÎŊôĢҞåoW}1ōé‡m=jëčÁĨ‹LôŊ8rúRĢŨŦcuî?UŗÜ׸O&ŗūßöÎFö,÷GsŋqßëÜÍmîkÜœi˙o{geVįū¤ä~¤qßëܡö,÷#ŗÍ}­û€kq|“Nî.æ~T´Ø¸ī]ÜŽgš˙ɏåø¨FÖũ1ÉøŅS÷ESØŋTzĖŦčÚ[žWēˆ|yŖŦP‡ é’knŠöž:Ŋ´ŦåĖķN‹~žōE| Ŋ2'ūÍįJK|„>ņė8÷•–ŌyŪé5Í}­úd˛ė˙í0÷§|6ךoäq˙™Sfį6÷5ėδ˙ˇãÜĻęÜ,åū ã>ĩܟXZĘ ÛęrfMr_Ë>āZßô<÷›Gg|ų‹Æ}jš?Ą›š?Ģ&š× 9S‹ū˜ĘxęĨĮĸ‹Žē$š´÷äh؈Ņz--QņŠDũúõ‹ļ*~}đ3ĸk\]Z>mRO˙Ōsį_M9ā€vsHæžV}2Y÷˙vžûšÅÜO]+÷ÃâÜĪ3îkžûԜsø'ûdÜ×ǏũŋįūōŌ‡Ÿíûûy žjÜg”û+;ÍũĄÅÜ_SĶÜײ¸/ŽoÖ÷Ķö+Í1•š×°ŊĻÆšoēq_Ėũ°smMs¯rĻVũ1BäĨOĻũŋB䊸VũŋBäŠØņhļã  ĩî"}25ë˙"'}Ā5í˙"}ĀŽoDßÕč‹ū!ú˛OĻÖũŋBä ¸æũŋBôu°ãŅlĮ7@•ú˛?Fˆžč“éĢū_!úǏ/û…čĢ>`Į7ĸŲŽo€*éÍÖ'Ŗ˙W4[°ū_Ҍ}ĀŽo„>``-úcDöÉč˙ÍÖŦ˙W4]°ãĄh—ūŅl}2úEöë˙M×ėøFčÚĨ?F4[ŸŒū_Ņl}ĀúE3ö;žú€véÍÖ'Ŗ˙W4[°ū_Ҍ}ĀŽo„>` #̊ ‘ĶXnĖ cۘÆŧq/Œ{Č\ËGFœ1q§?˛ŨĮĮŧ5bø÷†oĩåĒŖļŲęŊ]v˙ÛÉ{ė°hë­ļ:(ÜVē¨ģÂwđ MĪŲdã ˙sˇ]ļũßš-yčÖhų+‹ĸ¯=PŠWŸŋ'ēīÎkĸsN?.Úvüčŋl<`Ãw7ÚhƒsÂԋą6ÜāŸØw×˙yų§ßĸ˙xąĒxîĮ ŖI'üįô{ŊxŖ47#;ˆ9|Ž­ņsËĘđø1ōb`üz[ę` nđ9ddÎÆ´krŋ~ø…7_ú—j ßʘ{Ņ)˙ŨŋŋˇâŠŅDÄĒbŧPŒé9yŽKâį••ņcô´@š™ōķš$~Ŋ#ë` -lđy$ęÅØ€šŋūú-\účí=.~Ë1oî˙Y,¤QŧĪ x`ÎpĪLjŗãĸfU“Āz/ƒ"PŦ€Ēô_oŊu˙õŽ˙ąˇÅo9Žøä”ßīķĻ&;°?*žÍŊ āš `0TW<ėšûö¯§Uü†x÷ÍĨŅÆ6zģxßã›ėĀ>Š‹ÃJ‹qA\¨Í.tÜ7z'Έo7§ƒü…ŌYņm.‰o?ŧĘxlaõëōīŦxėpŋíõp‡īOíĸũˇS¯3üwr;÷ž×Ķíŗâįšü~åRüŠņk-›äí’ī…ņĪ&r\ū;ŪŨ·"•¯q‰€<›ų‘1ÛŧœvņbÕ–EøĀúŋ/4ΆXáĀūšBû;AĪ(Æōø6G%~g^üŊ kv(gÍ^ ’ÖDņŧ*.¸ĘߞΎ‹Ŋ— mwŲßæšN ā™…5ËŗËgŸ‡ĮĪųø{Ŋ)€īŸCōLa¸Īâß]q_É"°\…pbÅs^UQx• Ŋä&Zg'ŠÁÎ āŅqū–'ŠËđ7™ĶNW.DÖėÚ<>Qp÷ī⃐ōßŖüģ#ãŋyåk/įmvÅī—?Dč_Q¯(ŦY0>ņ!IrÜL)´]Šß?qÉ‹;&ō‘,€ËŊîcĮįâīī˜Čeyãˇá‰ņô‚€<ģûÚšgū<‹8Ä´ũvûĮBÛ3˜õ^wīTŧÖpvīŊ¸8Š45ūŗ+ Ę%ŠĄāx$ž}(Hn.´ŋõ…BÛĨוđĶqąSš1Y8#ē8.ēzS_GĨSãߝÜI<;ūŪėv~˙Îøg­q1YY—=QE\ū0ĸŊ%Ī‹ãŸ OŧžU…ĩ/Y4ŋŠĮš9žMå2äɯ}tüõíÜG9'‡WĀ3;xŪ;V|e\Ė Ķ÷Učdœ”Įaå;Ēbl\ÕÁãNPg+^ņž7ŗ*€ŋ}ķĨaYėĸ*€WÖôE†‚¯ŧ7œmĢ<+8)ūŲâÂÚ;GŸ\Xs†1x5.ZĒ5 .6‹‘ˇģ(€ß‹ éÎôĻNPáÀįĒ(€ËãœvōT.€gÄÅzՕΨĸ0-О“+ Î…ö{šĢé5FŧŲÁĪŪMŧö™‰\Tžî k–<'ˇ˛yaĄũ^īŽž7ãã|^RXŗÔŧōūFv16žč$—o+€ČĢ÷Ū{ëŲ(Ģø—/Üû/q‘Ø(p{?Ũ[QŦ* œÎbIĸ`yēŠį0=Qt—ãåš%ŽĀÕė>ܛ¸5ÎÃ{‰įõfâšvV/Ž"Oå°Ŗį7ĢŠÂ´Ŗĸ°ĐÎ}÷ĻîėqV$^ûœ*^÷Â.ˇÚxraM?s9^ī¤îjl,īâ5*€ČgQ—Uņâ_^[ÎjŽl”\up`? °Ļŋ3Yœ•{1ĢYžŧĐļ3Š|f9œQ^ß.œąÜą°fIķĶUĀ‹Ûšī– āp¯ÆĪ-œžZXĶ:ģŠ¸\t ė"G3 /•ž]EaÎFŋĶÁĪĘŋT pggķW&^{ų1gT1>zS˙6oÆcqb"×KzX‡ņöļ€zŗ*lV•UüâŌ;\âĸbU\`•ûH[;)<ÃĪnL?å͐*{N‡Į÷–[—— Ol§ˆíj ôëqņÕŌAa3;ņ˙s:xĀãŸßØÎëŧąŠ¸|&´ŊÕōī‘q„ÛŨÖÎín+TߛÛŪĨŒĘK­Į§P—‹Čáäqay Īc~"oŊ)€/č¤Đ~ĩ‡pG}Îå>m0š´bų+‹VfU˙āÛ_ųYĄëŪĶF(€“ÅŪŊ‰ī•/}sTEÁē¨ĸ(™‘(îZÚ)Ú&'îŋ˛šĸ°fŽŽ ā9íˇáqžˆ –dųtâ9´$ŠĸŽ āÛyŨ…¸Đ|7ūŲôŠ<>Qņa@X:Ŋŧĸhö!ĘgÁ)Ŧ}‰ŸI…5K¯;+L'$^߀ŠâŽŧŖq!…8ŲûŨŌÎßg´ķzæÄĪĨüw™\åãÎhįīÜŪķžå×<'ž¯ņņīOčâūFVÜ.ųŧÆ÷{cüÅUü;€>1xuÖųãīūéŅ?Ĩ]üŪvã…˙VX}Ļ raū~{ī˛,Íâ÷•Kĸ6Üā÷…ĩ7l€>3pŨu×ũ˙ēīëoĻQü†]Ĩ÷Ü}ûPüŪ(ĩäÍ䖖õŪųÅ ÷ūWo‹ßCÜëŊ6Ú š.äĘQũûā˙č˙Ũ“âwÅkDļũȟ7´écŠ_ōnōúëˇüÛėãgüņÍåUŨī{éy'EÅÂ÷Ī#ļÚō") ^ ė×oũųũú}āŋ>bę˙|īö+JgwÃōæPđž÷ÖŗŅōWEáûĮu`´ÅāÍū˛Ũø‡k܎–:ęŅāõ×_īs[ Ûō§›ÚôOë­ˇî_‹ß‹>đõ˙wȖ›˙×Î;ŽûÕ~ûLŧē°úúĻTaáĸEO>ķŦ¯L=øˇŨ„? >âĪC†]ĩekë˙l5bë?Œüā‡ūa“M69ŊxĶÁ˛@Ũųūâяž5ëąÍūëö;í}áœ/Eˇ˙ā‡Ņâŋ:zø§Ī•➇.üĘUŅ~N˙Ÿõ×_˙úm°Á7Šŋ:@ö¨ _ēøâ™Ŗ>4úĪ{NŲˇTäžō/+ģŒ'_øĮč€CfŧÛŋ˙׋w1ŧÁSÔRŒ‹19Žz;û>¤YŒū9žaĩ&žníÁØęŋVĖm|ÚoēŲfŊâk×UUøVÆŲ\ôûõë÷VÁsŠņN1ĸŠxĸŖÛšũˊ".fÅĪyrÎsŊĸK_/‰ŋדã×:Ëŋnā}_øâ9Ÿ8hP´đŪŋíQņ›,‚ã3ÁvÖ풸˜z.|VW‡ãæbŦ*ƛ…ļgƒĪŽo?RœJ|w1žVŊråõ×i:ė/_šū†^ŋåØīĀéXũõ4PŠÂRÚ÷ŠņraõčJ§Æ…ÖíĖ āt āžPmí7ũ %Ķ=,•â7Äŗŋx#°ņÆŋ/ŪõøIŅȸēŗ“9lķ⯧cQü;áLđáˇ=Äsâ"ųŒÂÚˤ[ãĸ-üwz|ģ鉟ˇÄ÷yI3:(Ė ‰ßŋ ū{t§îßžü8SÛyœŠqT>Ö¤näˇ5ÎSøŨŲņcTĀSÛÉãđ8åĮœ\e|xüŊŅūõ@šāÚkˇ }ŋa‡į´ ā]õÕ?ގŪz5HšBAöNÕŽK m{„W$îįļø{īÆß˧ߋ ŋĘÂíæÄ}ŦŒ?‹¯Æß{3ū~ø˙—+ é‰įņv|ÛU‰ÂŧĢ×1)qß+ãû(/o­x­aiō#‰Û–ŸķUUäęđøõ¯Šķņ^|o:īžßļœĮōķ[”(ŌÛ+€ËgæoėäC qĖąˇėŊ˙ÔT‹ß/ũzEô|ā…ÆŲkV\¤…âiy\œUčx“Ģö–@_‘(žĘģ0 žU…5gM˅[ø^X^=ąSâŸŊ}S÷;5ū^˛`œßĮœÄ÷f&^Cgđ |;~ėĘ,i§Ø:ņZ‡Į9ZÕÅßŋ5~ŪÉŨÃĮ÷uQŋũ.”?0˜ŅA|Füõ|˙ō  íŧû¤íéŽĪ]ŞûLųY\t4ŠP ..Ŧ>ķ˜<ÃûtEAÚ^\>‹üjaí3Ŗ kÎ^& ˇ{+n7Ĩ°v¯qŲUņĪvŒī?–Īĩsģ;Ģ(€Ë=ÍŗÛųYšČœXQW.wŸSÅã”o3ĩƒ|tV—wßnŠ(܏Jänėāļáųžßnqü¸åûYÅë g™įÅtøũ°|zp;…n{gS'tãq&%rzsü;ŗ* ×ōĨŒ’Ļġ_ŋƐ“áUäąüÜ7@3‡ļ̀7ūë“/ücĒÅī]÷ā˙)¸ė y˛ë{>sôŦR-€‡oŊMč˙=Cvȍ)SĻ ŲlĐæšõŽģS)~į\vųoÖYg7 Îū7S:čÄM7Û띋~ŧ¤WÅīmw˙đ÷ëŽģîŸ Ģwä€ü™2mÚe›üמž ž|Ū×ūØŌŌōī…Žwū€žĩīÔiĮŪb‹UĄ'¸Úą˙ũĶŅŪûí˙īë¯ŋūŋÚ^ŸōëÃ;ė0x—Ũ÷øqØ:\"é+×ß=üĶįĸ—~ŊĸTđ.ûÕ¯KEī%_íą÷>īõë×īŨu[Z.*Ŧžėԗ‘ãÆĩîšĪ”kĮoģŨ?o>x‹˙ZwŊõūZüvÔŌŌō—MüãĐaÃ+~=ŗd ŋnúŪõ;ĩ˙ˇiŖĨeŨ2ÜįOŸŊ`}&EŋzëÕĻl´˛˜Š F@›6cęį\ôÅĻ.€÷Ø{÷ŸSq†ŅĐĀ>`€†§˙W0@SĐ˙Ģ )č˙Õ Đô˙ęhxúõ4ũŋšęn-ÆÅxĸá ô ÅXXŒ)Ŋ¸ĪIņ}Œ­ōöũĢ’ūßÜôīXŒwŠãõb,)ÆsÅx/ūŪÍ=ŧßYņīOŽâļ'ãU˙*€†¤˙77}Ā/ĮÅîÔŠī‡ŗÂOĮE댌 āPt¯đ¯h8úsĶÜŠwwđķąņĪ{RŒWƒ /sîĒŋÛŌÎ÷ûcd•Īgx1ø×ԔūßÜôŽ‹Ôį:(0ƒĪãé8*ąÃ+ ā“ãûâŋģcâ÷Âī„3ĐĢâ˙Ÿ—xœyq„ß{ˇ°Ļ'y|1‰§üŗ›Û)pûĮŋ˙v'ũŋšę^†a)ôėB×gTWÚ?[{I|?#+ āP¤..ÆôbœZŒ7ã‚ulâv¯ĮEjø˙I‰ĮYßöîøųĩÆŋ÷n|?gĮEņņã<]QČ?QXĶĮ<=ž˙åņī+‚€ėé˙ÍUp8kzoaÍŌ(..ÃÎ3 kŸînüHÅí&Äßŋ3ņŊö–@¯ˆoWšu¸ŋ÷ kī.=;žũŠņ×GÅ__RqģÖ¸Ø~ÁŋD Sús{=āŅŸ °úŦéģ‰b¸|æĩ§đÔvnû\ü]ĀoˇSŦ—Ī(Wj‰ ãōĪîŽp;ˇŊ­“Ÿ¤C˙oޝœ,&Ù×G kúf{Zlįļ ãŸĩvQWö—Īŋ›xÉÅņōøļ/ĮˇmīvåË>Mö/Ȍūß\õOŽ ×ÎvG~ēĸX­eŧ¤â{ãß[?^{QÎ×ëqĄ{I'1ŌŋH 3úsÕ|UĄãeĘe7V˛Ā7wPOė ¨~'ņuĩpy×ę{;xŽáąË=ˋãÛļWÜwvI&€ŪĶ˙›ģ>āã"1œ-ŪÎĪÃ÷یŖ\X–ûwû'n×ŋ°fĶĒĘøÆŠû —0 K•+7ÁZYE„ÍĢŪ+Ŧ}övrĄíæZ§Æ_ĪŠ¸]xËãûp]` úsŲ\žÎn8#{[\8žŽīÄÅęŒÄíËKÃÖpæøđ¸(~ģĐņeŽŠ_ÃQ…5—6Jîâ\Ū…z^âą:*€'Ö\3xf|ŋŗ k_^)å¯&?ûÉžæ ü‹2Ŗ˙7ˇ×—zŊĐöRH!^(Ŧ}ĸpÖôîŠÛŨ]XsÆĩ˛ž§åÛžZXûŧ“ k6Ļz狏ەĪ7lzUšÜē5.ŽW%ngŽ@Ļô˙æúzĀÁā¸8TčúAã‚ŗšK …eĮ o:Õ˙ŧ7žīđø9´Vņ\Ãã-Ŧ}]c€té˙­›ëĐúëōzĀt—ūßÜ÷ũŋuĶ @Oé˙Õ Đô˙ęh úõ4ũŋú€žū_}ĀMA˙¯>`€Ļ ˙W0đ˙Úˇƒ@‚ũ3ŇZBwŽÁ}—ũ¯ā{ú_0@‚ūW  ˙Õ$čuĀßĶ˙ę€ôŋ:`€ũ¯ A˙̍˜gËŽm¸<ĩį‘Tî/œ ÛIENDŽB`‚trillian-1.6.1/docs/storage/StratumDepth3.png000066400000000000000000001055361466362047600211760ustar00rootroot00000000000000‰PNG  IHDRĀĐ_w´€IDATxÚė xåšīŗ%„Bw 0Ü#×p‘€ˆ¨¨â5Šr‡F@@„ QQTTDtĮŖRĒĩ˛‹U䨡Úb=eˇ´ĩ[ÛZk­Ũ՞9ķŽĖ’•ÅēĖ˚Y뛙ßīyū&YŦ|y×;ßũ˙}YY`x‡öÅīļjųįâ6…˙G˙z6!_‘——sqķüŧŋßļüzíø[OkīŧMëŲŖķŸĪ:ëŦ'‰ø…‚ÜÜ&_ŧ°˙nMûË[_ëŗË øwúĪį"đ<]:ĩûîÅŽú*rđÖüäņ¯˛ŗ}Ŧŋ,—H€§iUĐâŗc?yB‹5õ-īņũe͈x–æÍ›+nSøĪxƒ_ŅÃ;oûŧQŖ9@´Āŗœ?¨ī֙Ķ.Ô €Å œŨčŗ,ļA€W9¯Ī9īƉΉĀĸŨ:Ô_^MÄĀ“ôčÖņ‡ܗt|í՗| ŋ|%OŌē°å?äŪßdāGî[û+ũåû‰xíÔņī'ŋũęcŸë¯=B¸Āŗādƒ_ŅÉ÷ŸĶôמ$\āë°Ŧë¯=E¸Ā×`ņ gą ~ËIŅ €Āŗ´*hņÕĮ'_N:žûĘ?ë/˙!OŌ­ségĮ~ōDŌđŌŗ~­ŋ|;OŌŋĸėä ûīN:žxÁ°Ÿë/_@ÄĀ“ŒÖ˙™õĢæ&į7Ëû“ūō2"ždЀō/š¨*áā÷ÕwiŲŲ>&ZāeŠō›å}ųÅG¯ĮĪųæ%é¯ÛL¨ĀË,(h™˙ûÔm9øũėÃÃZãÆŲ×_ˇˆP€—˜¤kIÄ×rrß4zÄĀĪc €¯›}ég5úžūē’ˆŗ[×pB *!W­Œđę*zMvĶĻ9>˙ÔŨ_E~_yaįWõ7ũįŨŖ^/ƒáüˆ¯_Íâ€,H3QŪĸŦúUßd oŌ¤ņ§oģų¯žxQ[šėšĪåk“˙V~gvÄ×§Œī8J䖿덝­0V×{ē4]ofŲßæ9ø••âŖ~žËGf˜5Č\“ÕpK˛jD–­Ė\GØ@ˆŠŦ†‡P­Ë:Ķ—ë%"ˇKËĒs]Äך ˆ‚CQԀwĨ1P 3tmŠE>)ā˛^˜›tÕ– …éĀ€Ā[DnķĒk;!1ÅÆŦzt˜2Äę"÷đ! Ž #žŽĖÂC 1b]î°7ĢĄZŧĶŲ„Ā=ŽD ÄØĻ›~dā[5žFXR#z+nBĸrwōŊQ_O!,‰‘SšĢ#ž_/ĢŧŪĸ‹Ž5Q_W:â%Ü>[‘U%øųL#¯Ÿ˜œû— ĀČõ:ŗ#ž–ŪJÂ(†g5Ü"=‰?P”Õp…ˇ‹Ž„"íŅ‘>o™ )', :rWläęžøw×°ĀÔŦ†‡Õfqø(‚liŧgˇ1pŲAPņõÖ¨¯\CŠĘđv!,&"=ä2ņ˛‡€SäF xåÎ×*Šäfä–û’Ŧú đ…­[}Ą˙WC!„˙Õ´iîŅũ€@R5Žjáˆ1ÃĩŸôBĶ;6i3įL'ČķzķÄëÚŗ¯ė˙úënĶ–ŽZDlR*,*ü8Ģūžl€`1čü‡éœĄt뙗žÔ"'^Ū=õqAžÔÖ^:zāë¯WoZąA™Ô¤K&üVī, Ŗ¤´í_"W+rk0xØ Ūc'Čâ×Ū?ôõ×sæ^­m{h ąAiՖûžķėėėįč@āÆŋÍ[ä˙?:Č É oäĘîÁמ#.Å‹Â_WO¯=ņü#Äš*™„i”Ũč3ēA(†ŽZ;nâ:ČÉJÖ+oŋøõ×Ō‰˙ŲīŪ!6YG>7åŊLņL!|Ā6é7¨â'+××Ō@ļ´iûú‡ũ<üĖŽĐVgbƒsŠļ 5øūy„Ā$Å%Å}ápdJ{ę )rĀËļf„ŌĢȝŦ—”ļm°:Œˇ™ÕÆīŽûGãÆŸĸ7AĄKëĸÂŌ @ņ$'×Ę 7rų5BH-Ʉfŀž6‡LI,+›4ū#Ũ!ƒÎ°jŌĨé ĢI‘+ŧrUŅÚÍ̉ B•LXEÖķrđQä[ĩ,hņ‰Ū%(ŖWž§oŋōŖ n‚-Ų.)ƒÜČՀ™sĻ„|Ē­÷oÖæ-›Ûā™g@l›8æCŊKPC¯|OQ›ÖŸãß æ–ˇČí‘r7/§Ę"LÕŪļ4t˜]äUel™Æ āGē—ã˙ ȁ9‘\ņūø—‰ Bč ]?NŸ˙žƒ3 Æ ā}*ö]Ų7Ļ(˜ËŌU‹BÛõd…‚Ģ>Ŧmm˙˙ˆ1ÃC÷ņ„U‰%"r§ ŽŠ‹ĶŽGˇ…„ĀeĘĘ{žšíM5ÉŨÄz1ĩ-›kí;–†ū_ūš}ÅÖėšYچ­k‰BČų{c/Ø`EXoX(ėI&&srr4&Ŗņ€īŅ–_¨:˜”C™dĀĢ7Č_¯4ČÉÄyÍōB~U:N uįŽMĄm‹á¯ŲވJ—ŊââË/jđĩÔGÄ&šܡ3ÔĻI[§Â0ø~¸´C;eũŋŌĄj”Ũ(t]Gä÷į.Ž už}e ;Nâɋ<ŠYNpeЋĘ´¤Î–z:ōëmm!6zãƒÃÚ3§†Ú˛^åeĘ €ņ€¯é͝÷Fi€Uí ´*lĨ•WôŽ9đ“΂œZ¤“XųyÉ`÷ĨŖčL"„”–x‡WoZŅāëûŋ'Đ1ë‘l{–];˛bŽĘ0øšî=Ī~Wî‚Tĩƒ ‚Čmu‘Ģ *ubmŨ–§:wí*ô ļ•Ŧ"ŧōuäŠŦ!äEIŲöˆĩ%ÕēMK=~TĘõn:'"˨R›†|KĶŧĻ˙P𓐍C ?“Aϊ'y6k–§ÍŸ{•öúËk3ŽēXkS\dzĨVNģ–­Ėá¯Ĩ“ČIĢ!ŋ%ƒāđײ]ZvúXüĘA‰7^?M;ōŖŨÚŦ“-Õģǎwō?A/ üFEįnŋRŊCy¨SôĪT;K:a-õN˜tĀ´ŋŧõĩîŨž*äņŠ5•Ž^ä!drpŒę+!äöÁPRŸ†ŋ^ģyuƒ¯ŖunyOm뷗4¨wøŪ*ũûež™@Ti,ĢĶšššŌM_QÖģėŽéŗ¯ôl‡ Ë8ZĨōUŠ-?ĢA',ŦņÕ#CW:ÉÁ]‘9ņ%ęØ!„wän ūZ&FÃ_ËĪÎîÖQûâŖ×΍w/œ0"Tī2ļŽŧüŧĪô2uĄˇžĄs×Î?“Žƒęšk2ÖaPō3•đ˛…¯[œN˜čŨ#O…ļäŨĩķہ?ü!„R‡Oŧ=v˜ļë{ĢbÖģīŊąO+Öë]/ŦĢ61vøëešMo üBvNNΗ‘^S%‡™ČÖáčīËa)ŌYPifĒŪqYˇjnĖNXXömāsC!d_2ÎÍmĸ}ręPÜzwtÕMåÃU¯ųέâ~”îø…ĘszõøJõÁėšYĄNAôA&˛ō+ߏôÎfR˛ē ×Yœx§.áøÖå5 îÆD!d_÷=ú]­ĸī9 ëŨĮv­ĶƌÅ0ø€ŊēNÆŅ ]Īëš’Ļ˛”ëęžæŋŗņˇš!7ąđ=Īíų]\ĒŪ!o^ŗ<­[ŽĄk3dļ_î’ĖŠŗ5:S’UŨž}z&넉ęöŪĨM˜<žŽ+B9 Ú•7k į^•°ŪũėÃÃĄĸUߝĨāÕ~ø€@5IeŠkŋŽŨQ:¨ëKãįs\.GĩņģĒŌü÷ī6ū>3œ0âģvüšW|¨RNéŧŸwHr×cØ˙Ĩ‚ä@–ËŽM:>ö“'´Ū}Ęč¸"„š4i´öėŪģ’ÖŊÇ ,Í0øcofŽB×ē>֕íb9fåPyŦ1n¸".ū_•fļ‹īWŽRņÄä &ŽŅŪy[ŌNØÉ÷ŸĶ:v*ĨãŠB¨ŋsĩCîKZ÷Ö.ģV[¸|žŌ‹XzŪøā°Re ^g+Áą^“kŦÜĘāu’ŽüīQnŧnZ֙ۜ‹t-1~‡üŧƒ‰r‡ßo†ņ˙Ņt1Ū7š’¨÷—ī9ÅøÛ"˙Î.ÆëŽ˙ŸoL„˙ŋÄøˇQŋ¯ƒņ7ÅúYĸXæz wĒúô+˙’Γs:ģGWS0wbŒBލ¤më¤g/ˆöíųŽVũ0büžņš’ˆīÉ ņ“Ŧˆí¤Æ×Ķb 8DŊN3žųQƒĐ°yrķ#ĘŠę*ˆZ­Ũįī=cŧ9ęũ>6¤YÆĒtôī›1(^cŧ>üŗÆāx{Öé-äZÄē$ĒL2āũ0F,•öŸŨŖÛŊ˛e—ÆŨ9eg72Õ cŒBÎn>uüûIëŪˇō„V1 /1à >Ë`sc֙[̌Ũq]īÉß4ž?6bUķ=ã{5Æ×EƒÍŨ6V€7ī7ÕdĘ{ÖF bí€?ŒđVfÕû}Õuŧāđ÷d‹ø^cP\QNųŲŊÆß—m”YŪķXÖéíä1b)+ҝ߯T5q:tjBu/”;arĐ `„JoŨ›ŦŪ ×Ŋí;R÷âŋ €Oe5<:rEōͨUËWA_ô@ĩƒņũ#Æ×a_ī’ŋˇÎøYYÔkĢL”7–y1Ā´;žõēđĒ÷ŒœčąbYž"cđúÃŋņoĻDÄáË%F,ë͛üĻyM˙á%˙¯Ÿ:aŸ|YkŨē€˜!„Pë^Y%.)mKĖđ€OĀ‘§@7žw"b57’xƒģȁ­Ŧ>uzKp4SŒŸŨdqŧÆxŦĸތX5ÆĘø‹ŦØ|}l ū“ €÷ÆųÛÖ¯‰ÔXãg[×~bŦ’w‰ĄcÆĪU¤z`åüŋ\…`!„Ō[÷ëiV€mJžlÚ4÷7tģ@Ĩpô dx īÁ¨Áa—ƒËČÕ.ī‹ĒˆAĸ•°lGž×´†W¨eĩz{VÃC¯Ŧ €ãyŽOFũ,ŪxsÔŋ›uĻg8–:ËÄë4“ĻKˇÎÍ[6—FŨđ—:š´väGģ#„PšĀ/>w¯Öšk'bfS999_dy €2`áų¨ÕĘ,ckÕ3zÜ!âßį&¯´8#+ĖSÁđŠŦĶ[ĩí €O%71^gŧÆø{bŠ,â=$x]•ŠIĶļ]ۓ{ęĸAwáhYaHÖ {ųšZEŋs‰B9 ą”ˆĩ$YŨģëŪĩڈ1əM >XvÖMŖë *€e°ûqŒŠŦ¸žˆķ~2`üĖø˙đZÃcŧ.|čÕT‹āŠYgúuŗŗÎŧĒ)ŪøTVlpQŒŋ=z ovÜoŒņûeb@+dŸĘŠŊ;|%“jäËũŋr§. ēŗ7qŒVˇ÷ޤ0š+x…Ŗ‰B9 ž}zjoūøŅ¤uīĒ[oŌf×Ė"f6uËēe➟Ž7¨<Ž˜XÉŨj|oNÔkgßĀøē"bĐ9Ā+1}Ÿdž )ü{Ē“”÷=c^õ}ņ$ņũΌÁenÔāY‹3^õ~÷f5<ŦJ­;¯šgŸDŦô†ŲõžaOsmÔëÂ^aÁš2dø`üŋ.Hļ•ßzKMŌNØĻĩķ´kn˜IĖBČ]8i´öôžī$­{¯¸r’ļiûzbfSĪž˛_kš×ôWtŊ@õpxp9HĖĪ:}7đ^cˇ7ëôu>‘ĢŠáŪ{Æŋßl `ŋŒ\VgöķJ4øŠÄn4Ū?ŧúšU{{Äī•×í7°īÅb¨.ęũöDũîcÆ÷_5ĶņĀYÆĒ÷gÆûn7^ķjŒUåüˆī?oŧîŖŦf)xg^‡N[ējš zâųG´~ũÎMÚ ;z°v÷ũ›‰B9 Uˇ/Ņ–ÎŸ•°Ū•+ęrrr´ŋķ21à gĢ1āLTu1^ķÈÁ­ ÜVƒÂ“Y§OeΏņī'ƒĘ“ÆĘ¨ ,ËcŧNČGŒaĸōŒ5‹áCĒÅXÎ6ĘđĘī¯4VZ# á¯+"Ę(ËMYgnKŽ4^#^ãŖŒ‡˛âßkWfü­'Œ÷=į}sŖbyÜXî bÂĩiũ›}§!wA?ûŨ;ZaëíÄ;uq;a_|ôē–›ĶD{åí‰B9 ĮęŌ†Ÿ_‘pŧg×:­Wyņ Ŗ@üŋ2PŖ!wG5ķįh æ^•đŦŪ}z+„rpōQ’{~ãÕŊãÆÕÖn^Mŧđ@˜:tdå?hÄŨ͚M+ĩüüŧ¸ĢĀc̆hcƏŌ8…!„œŅÖû7k#FÕƙ||vī]ZķųÚ#˙ļ‹xá€ ŅŽ}ģũ+××Ōˆģ¨—ŽĐæ.ē^2¨OČsŲ {r÷Zqq‘öÔžĐ^{˙ņB!‡ęŨ§_ÜĢĩlŲüŒĢčd2Ržŋlõbí/|Ā$ZļüŊĖŪŌ€;ģõŽzōxíØÉŖ ž˙™—iåįžôĘ@øîo/ mŅ{pßί“YĻĪž’X"„IŊyâõŸ7ÚÎsĮÖĩZįNĨĄĢæ¤ŪŨy÷ŠĐ׋–Ī;ŖŪŊsĮ&b‰|NIĶŧĻlvA?ŗëŒŽ˜|ŊūÎÕZii[9!<äû•×E˙[š9Ö÷B5ŦS#ëŲx ŪũĀw´ŽŨ:†ęŨ=ē†žŽ~ÍÁמã:$|ĀĻ?аēīņ{´Õ›V¸ōŪ —ĪĶ^8\GœB(Bŗkfšļj˙ ø€‡´m×ößÜ´M˛ō +n­&Ëö>⌠ē"īė•Ũ2n­,Oēt"õŽ=pŊ+P–æ-š˙Ņ­A[$¤t "Ÿ×ŧes‰?B(ƒßCúgÄ_ĖUÉÕopÅõŽÅzW *]Z´lūļŗ>_ˇ%nüÁĄ m­ÍôJėÜÅ5eB‹W.ø˛qãÆÛé^€ĒĖž0yü˙Đh›×Ī?ĸ-]ĩHŠ2Éjpŧƒ_BČëē~ūmßÁĮ•8p+Ö˙ŖĶ’Ī)ŋyū ēW $Å%Å˙kíæÕ4Ú$÷ôĒveÔÖû7쿃CĄLls–zMÕō=ķŌ“Úˆ1ÃųŦâL4nŌø˛đ€Š4ËoöÉKGĐh'Ņ3§zf…UfߙÔ@y}ĸQĩ6ąŦ(|Vø€Ā[”ĩiũ9ĩšmĪ^Ya•:ū`„×$^øząėã&ŽņlŲņ@¨™rÅEøã¸"žøáo‘ŋƒk<BĒKļ<{uUU2>`€$´)nũƒMÛ×ĶXĮšîÂņbmØē–ÃZBJ’æĖŊÚw×}ßŖíŠ{0>`PÜŧÜO99ø´f×ĖŌ^8\įû+›v<ēĪ!¤ÄDŖj :!üĘ!Yø€ņ€ZT””ļũ °†ĢĮNõõß(2ņ3ķy#„2ĄŠ}C'<å#dE0@æYpÅĖËūäŽØÁמĶfΙč-jr0×'!„ÜF^o¤ŋ]nY;Œņd˜V­[Ŋĸō‹éę”Ų§%˙ęM+č #„\=ØJė%ÄâôÄ+>`€ ФI“ŋņĘYņd pl=¸o'ąA9r[ä ˆCø^—¤ŗ7ÎëßįOz—Ŗš^dšAõ˙ʖ,ŽŠp Wy „R]õëU^F=kqk¸_uķŌŋlŌ¤Éfē]ijgΙ˙¯øÍ.ûÆ:[6:fĩˇ-eå!dĘ᤭ŊNiåúZmáōyžŸXmQĐâ§tģ Ŗ´*x-(WáČNŽūĄŗeũzéœ „Ÿī;6 Vcū}†8ŸŪdŠėėÆŲ÷ķö4¸õÚ ˇ$&~ŋ#!d~Pŗvķjbá°ozčČJ_ūmåįõū$ 0dĘN]:ūŲΉg_Ų¯ņ€/7%Ģ; €Bá5s×`‘ā„h|Āā V^]3ËWūß7>8ŦMēt"ą4nfõĄāM‚ųÚ¸tK&Ä[ EšˇhūĻ\wãˇÎ>ßô,ÆũÁī}v¤×vâ0>`Č$šâ˙=vō¨įÔMÛ×sčŠ"ÚöЖИX 䯉ŽęÉ㉅"žã€LQÕ휮ôC‡@V"tŠ!š.Ip!ųo՗8¨ą…X&ņXgÍ5s¯ū‹§ŦZËj„߯Œđēäđ1¯wÔ ĒĻĪžRķŖE†‰ |ĀPšå7û/{eņųzãSČBČ[“‹‘ õ'ĮMƒĀųâ˙õŌ ęÖû73˜ō?˜N5BęZI éO,<~*?>`€ØT—•÷üØK ģøJũr D¯ō`Œ:’:5ōĘ8l%Ū–Xƒŧ˛%ú盝đ:ēc.6Ū¸øzĨũŋŌ)“ÆÜK3ÚČZĮû‰į!exĀ$§  (r2Cu‰ŋŧ °āŨ1H Íō›Ŋë…k¤Œ^jБĩƒ[ØŌŽPúˇČzũ dŪ64gîÕJûÍ p.Ŋ2p›ņ˙Ē8°”áĨĢŅy ¨?˜í—š+Yí]¸|ąČj°ę–“szõøTī“TŅ-ˇ™Ōˇ_ŸßĢØĘ]žrr0—āuÔäʕČhBÎhõĻL.ąō:-Zĩ‰o|.ļ.\>īoĒ4€“.ēʁN Šŧ>IN¤%Ĩ.ŲUC‹TÜúŽŌBĶĻM?Pé”H|ž(Z;ŨĻmÚžžX dCr¸ūz”HwîØ¤Ä$#>`HE™ö˙JįLŽÃĄ‚Ŧ ˆ™$AČü•qœ°Ž’ €%Ođ@˜6đüŋË´‰k7Y‰wņ˛oLaŒPũˆ1Ú2Ų’”•É]aø€Āmî­]ģ4íū_9āHî}ĨŗR•lۓÃԈB§õĖKOd{WV&OĮ Ž’››{2̝ŌĀr )rBļŽ ]™D,PПƒyËæ äĘz|Āā6%ēĻ隭kŦŽlˇ~O“&MRZũŨõäũZ˙Áƒĩö;jį č÷ "dĪŽ™EgšŽ‡ŸŲ;W÷Õįji’\uú/įūÜ"ēõūÍĄ’Õ*§ˇã›ÍÕČ-Îō˙L*"7,'åŊãæ–[õj}ĀåēĒ醤—Šē>ĶĨEč˜1(všC† ú­ŨęæĨ7kEmÚjķ—ߎÕz[ģ}ËN­÷y}C×ÅjDU:iųSrĩ‹øƒSÉU'W1*ôÕÚw,åŗņŲl-Z6ŦŸĩeŨģV(vŽžwFŽJ~uîډA/Jû€øë\]f.WSđ¯ÉĀâÃ)]‡čФîēžĐu$bĀ+ĢĀ_ęÚīÂīÛŊzãŠĪí4N[îŋS+(l­ũāčÚą_öĩ^;ū{­˙āĄíxsæ^ eT˛ķāŽģ×%ÍU7v÷TĄĸ^EÔĢ ÕõėÎ2(­tšßUeô¯~(ũ"ĀéG*ŪX[KŒŸívđwÍ>då);RIģ’¸3ŋaŨ¸dYhU„ÎʤLåębgsUG ¯|0ö—dU?ÖVgY“:ēzōxOå*BvÔļ4=š:ëēĢÄŧŌå~W÷Ŧ†‡m1ČĀxw‚ŸũĐÁßĩwíæÕ–ũŋ˛Ē%[ŸŪúå' ŋģwíMŠ3ˆ÷ f:WûKRĮ:d-ü3ųŧŊšĢ9™Ģ[ČÕ ø€(8vŦRÎnœũ;wûÉÖ§™×ې°á=yđ5­Ŧŧ”Ņmz3¯­ÉhŽ2fė•\EHĩ\MŖ˜0€Âāƒũž˛æ-š˙ŅNƒ$3ērÕA˛ÆīĀë?Õ;ūč0 ŒIruŨ–{3šĢ €{%WR1WĶäf áđŪß/ĘrÖ\3ꂑŋ˛wLí}ß7Ųøu¤Ã€2x`‘ų\-u)WûĪzī†ģ×Úē˙7'''tá=5¤ēTČUĀū;ēs×NqOļ{čõ*ĸ^UÆĖ Í<`Tž]ĸžž¸Â‰_Ō¨QŖŋŧöū!Û[åĸ{:jČ ÛU3Ģ €ũĨŲ5ŗBy}ē\$ßâųG¨Wõǃšš0`€4SŽëK]¯fÕ_}$LŌõE–s'@W´,lųûTŋd ŸčĐ;˙ĨŌa@í¨e:WûK/=Z+¯č­ũø—Cß{pßN-¯YžV1 ¯ëšzøũßP¯"OÔĢNæjš}Ā €2Ā cĀ+•đĮƏE ˆSeÁ“ÆũŌíÆ• 䕎+ĀȊļ=´%4ā•üjUØ*ô_ŲmwW õ*ĸ^UĘĖ CČ`wŽŽZcØÉ­?uwîØôWˇŋGŸ}…ŽĘxG-Ų]•nįĒxC÷|œĪÃg’ÁîĻíëĩ…Ëįi÷=~Oč,¯į*BǿǸ€‹KŠ_MS˙Ģ*Ë!ģ¨Av*ū_Q§Ž´į˙gŌÆON‰ė}ŪytPÆDŽ"r!īįĒôYrrrž  v¨,jĶú7Š4DŖĮŌļ=ütŌÆOî ;ĄšʘęsuŋŠ\uÁXb†ČU„Läęö äjûŽĨŸŗ2 v¨|Ų…ŋHõÔ+Ö$münYw—vÕ5ߤÀ2zb/šŠ<“ĢËW“ĢČWš:ÃÁ\öÍ+žjÚ´éRēq`•ƒÛÚú×T!9ņtøčŅIŋę‹/×Öo]G‡eLä*ōgŽŽ'f(pšēõūÍéô€OČ>ëŦŗ>—;õRi„Ž<:õ艏6~EmŠĩg^z’ʘČUDŽ"ä\Å vŪĻ¤Í¯hˆĒ'×ÖmŲˇáĢ;ôļÖǰPûŲīŪĄÃ€2*r‘Ģ9ŸĢˇg Wņ€UÖ\6ũ’Ÿ;Ņ-[ŊX+)-Ûø-žuƒÖ§_-ÕÕf„œČÕvíÛ'ĖÕŪ}{ko|p˜xĄŒĒvíŌ¤šZ~^šöãw^&^(ŖēõŽåÉU|Ā`•C÷=qĪgN4B_{NëÖŖ›ļáģģÎhø^;ū{­¨¸Xģ~ū5Z*×-!äTŽvíŪ5aŽŪ°đ:r)‘Ģ]ÎîĸŨ‘ WåŪaĀČ š:ī[s¯Wņ€rÅ˙+Ū§ĸ'žDoäÚh^˙iƒÆīÆÅĩڀ!ũé$ eDŽ"rĄtåę-Žå*>`°BUÛvmOĻŌđŧp¸N›9gzƒī­Ũŧ:Ô.ģm“öōÛ'´ë,ÖZļŌ^:zāë×āWCé–lg–Ģ:âįę/bæĒü;V‚Q:%õŖŦęž{ę-Kš*yēīāãÄĨU˛9‘n6Wåßá€tŗņŠ™S?HĩŖ&ƒāčīīxt›Ö­GWM˙ZŀžÚŗ¯ė˙úgŌŠëÜĩ“æäĘ3BfrõžĮīą”Ģ!æmKĩMÛšbĨ7WWޝ=cĸ0YŽ>üĖŽPžC”N]?Îį%$ËU/]ĩ0¤#?ũĀgv;hŠ4V„…B!|.ōÅ˙šÅÎĘāĩŧĸ7ۘ‘'4éŌ‰Žma&į‘›ÚöƘ;jė蕷_$ĻČ5I? zU׎öÔ=”ō‰ûr\ĶĻšŸĶĩ€DTwčÔūD&Wpe ´tøčL ˇeqbā*ī#÷\SäæXNŌMõ}dbSļ˛Ķš%´ŠOŨ‰÷’­ĐĪŧôdĘīSÜļ„UF÷âąqúÕĶŽ[=iŅéä9s¯Ļ3#)]Ōü;‡ëĘ%ô`ƒ•ēŠŌøûēëÚMØÁ“2;›ß ļN}&Žß Â6ž™";$œzN° }ākĻƒāt#ŗŊ7~°€øߤsA&K* €åzęe3ˆO}ĒåX’Å𠐜CYõ[į3‰äé›Yä. Ģ5„<‚j‡ŖˆŸs x„ €ÁäāSü€k *[ķ–4€xÔeŠågõuŪ§Gž%ÃāKdËž`Ā ~@NzŪŦhĮ]&>9 "Š2ōUEÖdąS RDdŲOĀ#ägŠ/¤ŦJoäŖ ždļîC$šYęžl/ecR";Kí+\ēgąŌ§YbHõAúp>*ČĒ_I+" Žõ_|×pܛŕ!@§ŧIm–ˇļwá# 4=”b‘:’Å9Xh8¸RŧDw–™ƒĀKä Î^.“<6 ėĀGVÁ^@<ĩuoÍRßŗ ÎÂxEđō{˛¸ŗÔGļ<¯ķøßĀU3ÁAVQg{üoōsÄ䐇‘UYx}ŌEAnnîĮú5ä 5oŅüˇ¤-8ČF ŠĀb/™áņŋĄ$‹ r€ĖqĪcw**.ú’NšwÔļ]ÛOÉÜĶ4iÚä˛áUCŋúųGīi™ŌĪ~÷Ž6xØ íĩ÷i™,‡S7qŒöĖKOēöūŨztÕ.š:áB˛÷ô$NķÍ?Ĩ~ķ–ōšåũ:¨ ;vėØļôŧĨöÛ˙Ē@į†ųsvŒ3\™Žˇtē<ę‹A„čĮīŧėø{v9ģķ?ķZæ {ëérv§'—ŽZ䛜 ‚fÎ™ŽŽõŲû5SVø46_~‘öƇÉĮTTÜúYõž˙Ā1ųŠI+ĶŲwxpßN_õ DīžzKģėS´7Oŧž–ßĮÄ#€Á¸ ŖOÔŪļT™aŪ˛šŽŽ<ĨģqëQÖ=ô_'ßwúė+ĩЁ}ˇ’Ŋõ´).ú힃3aã!íxt›Öģo¯ßŊõäåįí`'uŊtô@h@‘Žß7ųō‹ÄzH/įČąÃŽ¤3g¯Ÿ?G{åí}—ŗ?ŗ+mŋKúՓĮ?N §sˇN_>ûĘ~:OŌÖû7k=zuŸė Q››ķ•Ŧh1aãŧ6l]Ģ­Ū´Âņ÷•ĻMs˙™ÅÁE!Z´øe:&qžöœoļéĮ[ŲNįķ(uq“œ&/1gģžŨųŗLM<"ûųÚo`ů¨q ĐŦŋķļá-Z6ĪxĨüÄķ„DeNԁmš×ôd°ū_?KļđģĩMļWy™6f¨KÉâŦ‚&Mšü#“82Ą!`rÛšē8ģqö_ƒ–°â˙Ík–§šŗōū/Ž D.IhĶöõŽįk~ķüRå@ ™}ÃĖ䰛LWüÛÚâë° "dĨ°ķdÂ˙+Ÿ'6Îli^5´Žš8Ŋūß HŦ=éXíĸ8]ū_Ya–mģAŲžŸŽv0ž‘c‡˙ríæÕt–ŌāžsĮ&|Ā. ˙¯üž =72čw:‡åP›szu˙0č9œ˙o'l$_Ķ1ĸ8Ũū_„Ā1Úĩ/ųJ<6ŧjNžĸ=xčĀ\ģæ˙•ÃX××ģ+××âvˆtøÉ[|ĀŽP9|đ¯Ü>q0VĮŲÉ Wˇ7áv†LŪ˙d?ģŦÚ:uˇõžē‡´Žgwų(¨9ėĻ˙WîÃ Ę ē™V|ĀnúĒWüd=qãž`|ĀXZū3—Ę;šz„÷,Đ>ā´øeåČĪ÷ĻĒ0 6|ĀųALâtŨ˙‹ęåV›$°[ū_yĪŅãG‘§’­ûnXođ@ šöĻĢ/lßą4­\ŗ† ũ}ßƒ8Ōå˙•·[^,T¯Ą#+ĩ~Cú]ÄI§ũŋ’Ģ™˜ØôĘdKŀžÚ›'^Įlˇũŋn|60@ˆ‹/Ÿ´÷Š™Sņ˜ÉŒmįŽ4ņĮZ)“NG¯ō2ÁQāčmMfb‰ø4ŠøeV]`;ūßúØŖžuā*ŗč‰‡Čŧ9gēĨ‰ |Ā8ú Ž8%[`RüļhŲ\ģņúiڑíÖž9c˛V\\s‹3ƒ_sąœk"–ø€ëąë˙•BŗfyÚüšW}ë6QąŪ°um īMMõĒ’pląuÕÅgÄ6Ū„P‡Ní˙´ļâ˙ĩ[˙,†đ`!ۅFlgڌm|ĀVũŋŅą#ļr.ˆ›§tûE˛hˇ{#bûēÛáÃj5&ŋđ@␭/ŠÎž[^ĻmũöMûË[_ëī­Ōz—÷ x阙īˆ%‹%>ā3°å˙ O4Hg!:ÖįFėRÁoĻîĮöú$Α¨ØŪŋŊalã=†¸(HIlÖ˙+ąm#ļ÷EĖ|ĩ&Y%—C ãÅv§[Ģ;—‚āļâ˙ÛÛW7ˆ­lŲįÔgëõmtl?<ņbč{fûvø€ P\6cĘå˛õ%•ĘWfuĪîÖQûâŖ×4lĸIFhˇŽ¯ÕÄ 8‰f×ĖŌžyũĖ„ą\šĄpvũŋCGUj‹įĪ:#Îĸ %Ö._•âW=¸o§ÖĢwOmIœØN4[ŲūnŸ˛›ƒ4‰cÖ˙;LĪÛĨqb;Ąz„V{ےˇ¯ē5¯ŠÄ~x‚ØVW´T'øŨlÕ˙;"AlĮ[Œ-j8a8``ߘą•Õā…KŽÃ Í“Æü› ŧRЀ̯Ķv}oUˆíŊ7öiÅÅ­Ųöl˛6:i,‹’Æ2h>`;ū_YÕíÚ5öDC8Ö­[ˇ"omhũ–Û´öíÛ&Œ­l{LÔq–ÎpyEīWT›ō˙JŪvëÖ1ĨØ"{ą}÷ČS–cëg°˙¯ÄöėąũO=o Zˇ6tûĢõļĒ flOŧS§é?{Īd;†ƒŪÉüŦāĻrĘhnNí“S‡b6lĸąUƒĩT=ÆAP(–šÉb9$i,ƒæļã˙•;Q׎š7ÎĸŅ&bbĮöö$ą4°OčÎßxīņė+ûĩâ’â?%‡Íú%ļë’Äv`’Øĸؚj*ļ}-ÅÖĪ>`+ūß˧OŅÖ'‰í‹ąE†8IlĮŦíxā;ø€"Čnš×ô˙ĨrՀŧPҧg†íą]ë´ąÜįg.–}Ī1ˑø€OcŲ˙+Ģē999ĄŲņDąŪŖĮš{(­_-c&ļk–_¯Í]\“đŊZ´øJ˙|;!‰ÍøÍÆvõōš¤ąEöb{ĢÅØúŲlÖ˙k6ļ+n!oíä­,@$Š­ôāĀ`âĨg”WôNŠū֊›C>“D ÛgÆĀvŌôÄ2H>`;ū_Ye(O2iŽu ōVŗÛ>&bûėŪģ´ /ž éĩJį”Ŋ$“8füŋVb;qōxōŅbŪö5Û bëW°˙¯ŲØÖYŒ-ĒíyIb+íXÉv 0‚QãF<Ÿę‘Nę$kÜF :‡F+=ą Š؎˙Wrū[K¯KgŅ0ōÖrlk—^›4ŽĮ~ōDh"Ņ{­ŨŧZĶz5Uą)˙¯Ävų2sąí$ļčĖØŽ4ÛrkąõŖ؊˙Wb{ĢÉØĘ)æäŖyŨ°@íˇæ$mՈÚî§Ėĩcø€Ā÷ôė}Χښī×ī\íЁû’VĀŌq[¸|VÉ,šSą Š؎˙wÜÄ1ÚŽ{ךË@™ŧĩÛMÄöäûĪi:•&|/šĘ§°uág~Īaŗū_‰ín‡b‹ę=ļīŧÍTl;ZŒ­}ĀVüŋã/t/ļAWĩÉØĘ ų[+Íĩcø€ĀīäŠ˙7ÕĢ2ÚļmÔÛ#Úŋį;Ą îi´Ōˀø€mŨ˙+3ܯ˜˜hí}t3yk1ļf&q¤ŗÛŲDgˇ¨¸čKũsîîį$6{˙īŲbËØšÜŒ­}ĀVî˙ín!ļ €­ÉllŸÖû %ąœā€@PuÁˆkånŪT+`ũ­´SĮŋoj{SÅĀž4ZNÅr@ßĀßl÷ūßFŲLM4ˆŪ4kd=ļfĀręq×îWøyĮėũŋŲĮŲ‹­Õ°ß|ĀVī˙u3ļä­šØJŸa Éū>`đ5į¨<8oŲ\GmfŌ¸učHã–ÎXúŨlĮ˙Žŗ b&Öŋxīy­=yëxlÍŌ6m_¯u?§Û>ފMųŨˆ-˛[;ƒ4?ų€­øĶ[ōÖųØâßŌĩG×?8qįžŲA›Ŧl–”ļĨŅJc,ũîļã˙ĩgҝÉ[WbûņɗĩĸÖIß_ԚˇĖ˙›_sØŦ˙×jl[›ˆ-˛[šŖŨNlũäļâ˙MGlÉ[s}†R í>`đ+ųâ˙uâzŗđņˇžfØÁXšY•ôšؖ˙×ęøŊ7ŸfØĨ] fW);wíô,ŸĸÆŦ˙×­Ø"ëąĩŗJé'°˙o:bKŪ:[|ĀāKÎ9ä&+[˜œ¨€_~n‡tdi´ŌK?û€íúÃ>Õ/˙tÔTŦ@ŪēÛ#?Úmz&ĢētZëĮIŗū_ˇb‹N{)ÍÆÖÎ Í/>`ĢūßtĖŧ5[+Œá_2°˛˙Ëvŧ“ą$[–dË]˛ XŽFqjĐíWšKŋú€íúÃūĻ÷ß|ÆÔøūä­ÕØūô­§MMâČjfW#ô<~ۇUąi˙o8ļĮŽ-:} ´ŲØV،­|ĀVũŋéŠmO6Ûķ*Îĩ\—ã_ŅĄS‡?ŲņNÆRŸ>=ĩ7ühŌ øļ[įjŗkfŅh9Ë5ˇŪd:–~õÛõ˙†īSŨ÷øSā[-ÄÕĮv˙w%ĢÜ]yá¤ŅĻW#š6ÍũũcĪöS[ņ˙†cûŒÉØNŧp4ųh1oëöš‹í„ ĮØú~đ[õ˙†īX~ÖåØUrĮ˛Ų؎ˇ[|Āā7Jdk‹īd,IGKî˜KVOģrRčDW­ÔcyĨ…XúÔlÛ˙+’ĶĪkošÁÔørōÖrl—ßR“4Ž›ÖÎĶŽģaĻé÷íQÖ]Ā•~Jb+ūßplW˜Œíĩb‹ęc{ĢÉØ^c3ļ~đ[õ˙†cģdá7ĩ]ß[•4ļ˙ZCŪZíšå×'Íۍzl¯ļ[|Āā+ú>o‘Ėv;U׎Z¨-?+aå+Įôįääh?~įe­Dą\í|,ũčNÅ˙+zâųG´ķ*z'í4ˇvc{nŌØŽ=XÛöĀwLŋ¯Ŧ—v(Ũä§I+ūßple‹¨™ØŪm!ļ¨>ļũLÄvŦ[Øų^÷Ûņ˙†c{îšŨĩw¯HÛ1UClĮ6ČyÛßDŪډ->`đûūxåúZĮ*`šJiØų +ß=ģÖiŊĘËh°œˆåë-ĮŌo>āTüŋ"éĀĩ*lĨũŸcĪ&Œõ#6btÕĮļ@;ņN]ܸ~ņŅëZnn“ĐGfßwĮŖÛ´ÚŋëŖĒؒ˙7ÛB3ąÍą[dÄļĩûąõ˛؎˙×JlsČ[åb‹|CÛvm?}ö•ũŽU‡ßyYËĪĪ Ũ3¯îŅŊŗļvķjŦ¤3Ž˙Ž5k–8–įôčĸ­ųö­–Ŋ<~ō§â˙ kæĩWiŖĢ*€Ī9§+yk3ļ˛âč@–víŠ-Ŋįą“G5Y1Õ?ū\?ä°U˙oX×ΟŖÍģņĒ„ąí]Ū“<´ĄŲ×ÍĐÆŽNœˇ%zŪĻbō˛؎˙7Ŧ=oĖŊ*IlÛjNYŗ‚¤Ģ¯×ķvÔā¸ą{ŨZ¯Ūöę|Āā›ÅŗÖE…˙t˛ō}éčmØ¨Ę¸›ĐĐŧyžöÚOŅX%‘ĖĐ1D[˜(–-ŦĮŌg>ā”üŋaíûūcZŗüŧ¸3įuOÜ%Ûŋ´7>8Ln:[ŲF:rĖđĐ ÖĘûöî{îßõĪŋĘIlÕ˙š-1ҊΨĒ!ÚĘ ĩäĄŨŧm?oeR§jÜHËyë°˙¯Hvœmšī;Ą[âÅv´Û1ãGiožx\´¨§_ܛ0oĪîÖQ[´bží|Å ž§Oŋ>ˇLētĸ +—‡´–-›Ÿqŋ\ž.ߗí‹4T™‹ĨŸ|ĀŠúŖ;fƒö y}É[g%ą<čĖØ>šû­¸¸H{÷Ô[ļV?Ûļk뇝 –ũŋnĮąŨPĢ ‰Û6ÄÖĢ>`ģū_Ņ ‡ëBį(Üē>vl÷:Û k•žˇ•.Ä0ø‚Ū}{Ŋî֖Π[׆.ą—ãöĨ–/:ë_/Z>JXúÅœĒ˙7ZWΜĒõîuv¨ƒŽu'ōÖĄØ^Ö ļw{IhčÁ};mŊŸüģ’Ō’ã>¨Š-ûŖ5Mb{ŽsąE‰c[č`lŊčļë˙Ö7f]Ļ•GÄvĢÃą ˛ĻGĮvĶRGb‹ŧŋėPXđšlYvĒÂŊBΙ{5Tm{hKHąb)—Ũßq‚ãęM+B§?Ņė„˙WVÃī!+ˇo^­ĩ+mûuŦ#O˔ŧe[^ ąŊs•VjÄļwŸžÚÃĪė˛[ŠG pž—sØŽ˙7úđ›uzŪ–”´ ÅöÜŪį4ˆ-˛&™BąĖÛō†y+WΤrX“}ĀŠøŖķv}DlĪ-/k[ų˛bLNZī7Hl7ĉ­\ågwG>`đ:eíڗ|ådå+ •Ų­5ŌÁ`‹S||í9Ķ ŋ 0Ŧ^Íã°#ū_‰ŗŲ—č°:ąí?¨ßzT{9‰íúã0éŌ xÕ¨Íjī{üž”âíE°]˙oįŽ,=ãr‚lģ%'í7„ˇĄã€ā~ËËÖ\ö)4ö{Ũė¤˙yS˛úV\R´Ã˓8Šø‘?ęb/ų€Sņ˙Ļr`R'_ņ€géŲëœ7eŒSwĪҰyO^÷;á˙%oŨS:b++D…E…ŋôpUœ˛˙y_^ō;å˙EŪ>`đ,Íō›ũŨî˜X[pGeéßČ6¨‹/ŋˆÆ$†ÆMŖ=ķŌ“–ūøyfΙ(pĒū_ŲâØŖŦģ-¯ģl5#W3[™|3|Ā^Ėa'üŋa?´Ä;l+‘mš 9¸Í–$ŽVÛFŲ‰ 1‚؎˙WōŌnC萓ÎÉMgû ŌvڝČĀ ^ĨĸsˇN_fēÂæZ™Ø?™U´L(X]q폨˙¯/ VŽÕ‰måđÁ“•T/&ą“ūßȆlSLå`Ļ ËŽĮ_&m‚r°˙¯ŧŪŽåJr™úÖų~C*;÷đ€'9§Ŧû2ƒGŖ—ĮĢ>`üŋ(ō¤ØÖm ôâ$ū_ä%p*ū_„ Ã3¸]ŪŧÚ%ÉUœ8ęė,n:˙ŊW}ĀŠúeņĶEŨ;‰4ą•UãÍįÁĒØ˙¯\=o%‡•3k;RĩĨ20ô‚8“ū_np§ß`w§>`đŲ999_:u•‹Üķiˇ•W=y< XD/×DØíDÉ ŖWyY |ĀŠúÅS–ŠgOüëÜŗĒNl›6Íũ=-Jŧ”ÃNųg×Ėjp˙zt'• JķWv‰2•Š|ž^đÛņ˙ĘAuŠž™ íôÜé7T čkĢŽĀ ^Ŗōœ˛˙PeÆ=^Į ĨGõ;â˙Mu ųŖNlGŽūšžĶŧ”ÄNúŨX‘Dé=õÜ >`;ū_šmâ‰į!}ØwĀ žAoĀî”*päU0ū_kÕšUëVyi˙/ō’˙/ žĨ}§ŌŸĨ˛=1Ōw&W?Đ8wō¨SĢA˛}ÔJ'Åk>āTüŋŲļīD'NŪ_šą•-–Íō›}ėĄĒ8e˙¯ŦĀÜšc“)Ģ ;m’ûČe ´y›Ęî•}Ā™ž˙WbëÄgDŋ!ö)ŨvļŠã¯+ū_'FĪHĒÛšÂžĢĄ#+é€é0<8ņ^r—Ē_ļ×|ĀŠøePĩvķjGâ,^UŽōR'ļÍ[æËuH]ŧÃNø_:zĀԀ@&<ú\üĒmmҜ˜– ‰Tęq•}Āvüŋrõ‘SąIŸƒž‚;ũŲĻ.^m|ĀāWĒĘ+z˙CĩNŗSrĄ@ø€3î˙Eję‚ ĮĘöŅŲ^Hâtø‘7ëbU}ĀVũŋRG;uÛ `›Ž];lwbļÁ>āL˙Å͆­kĩ–-ëŧ0‰“Ē˙—IC|Āé˙/ žĨmIņ/íls‰Ö€!ũ÷âŲK)ž]'ļ“Go{”íQ~ķ§â˙­ŊmŠļzĶ Į;\3ŖFleÛĶĻ9ö@Uœ’˙Wžk;[Aeģ"žõØWų9yĒ{ĒžL}Ā™ö˙Fo×%oī7ˆØNۊT'_üŋNt€œōôDŪ8zü¨Ā6drĮĄ`+0¯ø€Sņ˙JŪ:}}ŅÂåķØæ§Pl [~ϧI™Ę9ėÔũŋv|Ž €cīpreS&‡įĖŊÚW>`Ģū_ņīΜ3Ũ•ĪKną`ŌŅų~ƒÄTę|Āā7Ēûî÷?ĒžKG ° đ˙ĸ„štéDרœÄø‘×|ĀvüŋܕŽ ã”v*Ŋ?Õk‹dk×āΊøY1pO*ÅVNámŪĸų÷UžÄIÅ˙+ÛĖŪƒđ'˙/ žĨ°¨đ׊Ž:ČŋwęĒfŒë%[åŦ\YdÕ+heۚę>`ģū_É­eŨ];8H&…‚:1¤ZlĨiܤņį WÅ)ųe3•ˆl÷ģ¸†ŽģáyŦž<ŪĩûYSņĒĒäļę˙MG;.×zŲšˇ–~ƒš3^ŦÖįø€@ŲUņ˙Ē<ƒëĻgHe‰˙Ų-_žŦĖY9Kup*ū_ˇˇ€ŲņN!wb[\Rüį,Å “)˙o¤WđéÉšKŲ­ëũür°U˙¯ÜCīĝĘÉú n ƒŪo°3ą€”]u8dåßéô û€ņ˙"Sētú%Ņķe‰ŠIŒ˙yÍlÕ˙‹đ¨ąrÖļhoĒĢ+××j?~įe*{|ĀÁŽ˙WVxčŧš#Uc+ĢC͚5ũw'qėúewŒWØEn-úŠåø€“ƒ˙7¸ģ#ŦŦ0ã%iŲĒÅ‡Īž˛?eĪÛŲ˛ë֖4Õ$ņNĻŖŖ'ū¯û€íúeĐ žGˇã,[Íå^Æ mÃS1ļōl5Ęnô…ž6ŲĒíÄąë˙•ĻS>kéÜV ččô¤.‘ģĢŨDČÖPģƒG|ĀVũŋbWH×`Y<Ö騂Øo 7ĢWĐáÕ(iÚ4÷ŧPšËĘM&ũq~==×ĘīQÕŦĒ˙7,šŅmß[Pe'ļ:ĩ—ÁCĨJ9œi˙/ĘĖŠävŊš*ø€­øeBEüŋ銭x€ƒ¸+MÕ đ€jL5nätF‡}Āø‘%͘3]ÂZŠÔNūß íŠAęų€ņ˙"|ĀāIZˇ)ü7ŲžlˇR“•—Ų5ŗ¨āŪ–œîí‡Ō‘63€TŅl×˙+WEā[wī•cûāžZĶĻšGTšÄąã˙•ŋ#ÕûÛ]kᤝØK; ŧpvĻ}ĀøƒŨoíÖVn‘Ā JĄWHHåžā\EŌ8ĨûŨōpK]´Äŗ_”Ö;į']:Ņö•=™ô[ņ˙Ęķ™ĘŊĮv%gH|ƒˇéî7Čā×ęU^ø€@™ąC‹–Í=ĩũY|Āâ›bÆ7ŗ^•|ĀĒû#'odu‰R#ļ]Īîō‘ž>U*ä0ūßāJ&tŊčļâ˙•IöLė&°zZ1 Á`ÎøIãl€ƒ|Ņ<>`e|Āø‘-]7Î'zūlT!‰­úeûĄœÄĘįH]œ)0ū_„`P‚ŧŧÜOŊ¸Š+[ũ:ķŧr}mÆ|w2bön?U|ĀvũŋNŨ™jĮĢę÷Cãŧ[Ô>āü §ąrūßh¯_&o¤sž)žøŽíÖû™đ[ņ˙Š/5“íĩ|ŽŊĘË|ģ™ė7Ȥ‘•Ãˇđ@Ļ)k]TøW/VörōtO'Å|&^ņ˙FS9t9Ûszu˙POŖęŒNDZô˙Ļû„x¤ö¤Q&|ĀVüŋ2ÉNÎ"|Ā  7]2íâŋŲõūņtP¤œØ˛˙Wļxãvī *¯Åöæes˙¤įQF'rŦúe5+Ũ;wäseƒ8 ū_„^Ží’U‹dõėŪL$ą˙ī;6…NĀĪä*°ˇ‘úa–N0ū_”ęŗ„2B~ķüÃv͗™ÕuVâߑk0TjÄÆM“ôsΤØĒ˙W&mäĒŽ rgÛŧ—c+;I7iü‹LLâXõ˙fē^zĘowËĄbĒÜg/uĒl'UÕlÖ˙+[ĘUÜ­ “H^ŧúŅ ũéŽ? 0(KļŪPūŨoétgrEÄÉm›Ēũf':2åöĒ˙7ō°ąŠ}™R$ļr&‚žV%iNcOøũ.•ė21ŊzĶ e}ĀfũŋŠün€ũb#P­ß ũ+“cø€ Ũ īÜĩĶíTnĒ­TČ6,ļcĶėU˙/RSƒĪôK=­f§3‡ÍúeÛņėšY|N(ã>`+ū_„đ€JŦų×ŋųW;3Ķâ˙ĨâF ø€-ųį-›:@ˆĪËyų%ļ+Ößō=¯v§3‰Íú%ĪUšč“øũâöĶĄbéđã˙EN=Sø€ ­4k–wôÁ};mošĄ‚wVrí…\ĄbŲ&]:1´Z5°U˙¯4șž'1ŪĀÆŽßOĩΎb+GŖėF§Ō9‰ã%˙o¤žyéIĪįmxĢŧYĪbē¯d˛sF‡Û>`ŗū_9OeŸ­LāČā‹~ƒ;õŽØOĖØ đ@:É˙¯ŸŽ`™3÷jĪû€Uõc›-Wē}Ā^÷˙FzÄD"ąmÚ4įĪ’^iJcSū_Žz χ}íæÕĘų€ÍúĨė*NŠ…%“7~Xų÷Ã9.ø€ ]TwīyöGvDRuõW3îT –؊˙—] îÂâ§ŋgXÕĐzzÕ¤#‡ÍøĨ^:˛’S÷‘>`üŋ0x•7,¸î3Ģ•”xUžŅõĒd ę[ä$ûėĶė6í˙•rwîډ\sé ŋÅvíæÕ2x؛Ž$ļr˙¯ĒĢO˛zãÕÁšlãųņLˇ|Āøé7˜•œ aæj1|ōōšž“Ė͉Ō'9ŲÕî}Ėé’\eQ{ÛRe|ĀVũŋǝRJųÄ“Æ pæc+žÅFÎú(“8^õ˙FO‚xĩėr†ĘyÉĄgrœ*>`ŗū_¯Ž&ļ)•îĐõ[ŋA<Ęf&˜đ@:Č˙¯ˇ„Ž›8Fcf:ŗJ—Ø/ūßčlsU#ļzgė÷zš•ģœÆIũŋ2é¤ōABČũÉšŗV°Y˙¯—Î`W>`SĘ+z˙Îę,Мčč…-A4$Áđ›õ˙Ę!'ÜíŪÉĪ~íčęǞ\žKՌ˙WVxŧpȍ "¨ũīÆ˙‹đ€WŲē öĻŋX=Ōë',Ģ*Ų*æ•Õxéˆ'ģë5M>`Ķū_™¸ņúUĒĘĪąŨ´}ƒlŽs3‰Ŋî˙”lĶõZ.Čõ1~^]wÃėW˙¯×ĐôRŋAļ@'ŗOá×ÉÍÍ=nļÛõäZ˙Áƒ´ö;jį ¨ų9Töō 9Ô(kmĶļĘ{Î9LËoŪ\ëÛī<ép+^ŪGĩCiÍ[ļÔãÛ/n.¤ÃlÆ˙ûāžûBš[ęÜ|ÖJ;tđÄŗvžūŦ•†ŸĩíwxâYkŠm?SΚäņYgõŠ›“8ņüŋ sˇŸr÷~O=kėŨĨ—w°Öē¨HV5ĖÔ!=™ĖŨ!Ã+ĩBŊŦõíšųgÍi°˙oøY;ģjˇkģôg­o˙~FģVá™g­žßĐ×}˛’ŌR­{Īs’öÉđ€›”ˆ˙×ĖĘŲÍKįimەj‹oŨ Õz[ģ}ËN­÷y}´I—NTr6´Ua+í†EËŋ.ëš}ûi^2IŲŲÛV……zyWœŽ­^ŪI—\¨hywhŅå=¯"n.¸íNæ˙ŊyŲÍ1rˇ¯’š{úYk˙uy×)˙ŦžņŦMōĐŗvnß Są-hÕō”žn•.ĨqL˙¯Ŧ¤ž™ģį)œģŪzÖnZ:˙ëgm˙j7-[ĨĩīØAÉĀØš+íšųvÂIp2˙¯´gļkęæî˛›ŧõŦ-›į™gÍnŸ 0¸Å´~ƒ+N%Ģŧļܧ>āi­ũāčÚą_öĩ^;ūĄÖČĨüž-ˇEËÚļ‡÷G•õ÷ZŋA•Úú­ˇ+ˇ¸y )īĶg–wđåŧ։Ę+Ģ(ąĘëļ8‘˙wkÜܕōT.ž[îŋ+îŗ6@Ág­y€žĩ “/øŠžnĩnäp,˙¯l”‰ĻØš;HÁÜMđŦ QņYÛŗŧ“.›š4S.w[ÆÉ]ũYÛ`ōYsŌœČ˙k§Čôöp/=k[ôÉ(Ú'ÛnŖĀ nqī˛Õ‹?MVyÉ,î={ęT^a=õâ!­¨¸H™ĶjeÅä‚I“c–õɃ¯éemŖ”WææPy§x¨ŧķ´q^§ŧ? åBty]öĮõ˙Öįn̏šģĘŨäĪڏ”{ÖÆL¸ĐCš{“6ēú"ÛåÕ;˙­įÛA7’8Ú˙›4wãwø?õ˛+•ģĄg­zRĘΚ“>āDūßTŸĩôįnĸvâ'Ūj'{Öęۉ‰ r!ūŗ†\Ao˙īŗ¯ėOXy-YĩHĢžøō˜•WXâ•Qåá’Ō’PĨ¯ŦC†͞ĒŌąiÛŽ]ÂōVŽĨÔa2ÉË;âŒōēéNä˙•Õ‰ _–$wû)“ģKV-Ö˛ėY;Og­]˛g­ĘcĪÚČ„Īšt< pŽĶ“8ŅūßPîNš$a.œãY˔Ė=kę´Kn]’°]“vBVˆÕÉŨ$Īڈ*Sšā”8™˙7Õg-íš;åŌ$šÛ_ĄvBžĩЁč“á7蒗—ûŠ™†÷žŊĪ'Ŧlį.YĸÍ]\“ņŠVfŖe61QY7|w—VuÁ%†Pyۘ)o•2åmŨĻmÂōŪņŨ´ŅãGĨÍœČ˙+ o˛ÜŊqņ2%r×üŗļ˜g-ƒĪZ›âĸ_čiWåpŸá˙-)mĢįÂs‰Ÿĩm÷Į|ÖTÍŨ—¨ôŦĩKX^ɅQŒõXîŽN›8‘˙×LyīĐËĢJîšk'j=ÖN,U¨h“4wG%h'đ€ĶĖŽ>øWÉŽĢ—Nî[ŋü$av÷ƒ‹O#ã•íĘõĩڤˎLXVņ4oŅR‰íW+Ößĸ—÷&ĘÛB‰ō._wĢšōļ<ŗŧnų€ãų%we›X˛ÜŨēk¯š+å-0QŪģ|LĄgmšgrwÅúåŽé]=íÖ8™ÃŅūßúzˇMŌ\ˆ÷Ŧe"w͔×KΚJíÄĘP;áÜŗæ„8‘˙wųú•ļÛ us÷Ier7h}2|Āā4ģoŊcų§Éļ͸öƄ•WØĮŅĢŧ,㕭Tørē`˛ō<¸öāž/īø‹&˜*ī ķ‡*RŪIæĘ;ôü3Ęë’8Ž˙Wrwæĩ5ĻrˇŦŧ—Ûđf\;×DyĸÆŗvQĩÉgm˜"š;Ņ‘gíŪ=Û¯įŨ!'“8Ú˙+š0ëē뒖ĩūYĢĖx|ëŸĩ<ķŦÉŌsæšČŨĄ+¨2žģ“'8úŦ9áNä˙Ņ…ļۉL䮙g-”ģŊĪUĸ˜°>>`p”ė&Ųŋ9øÚsŽT^^˙ičęˆLWļåįõŅØ÷ũ¤åŊnūˇ´…Ëįgŧŧre™ō^?Š^Ūy™/oß Så­Y°čŒōēáNä˙•Ü]ˇå^Īänõäj ĪZ{rׯĪڒ„Īš\cø€ķšÄ‰ö˙†rwë÷L €k,ĖxŨP˙ŦíôŨŗVŸģ =“ģõåMž Šú€“ųåę SĪڂŊäî=&sˇŖg”ęôÉĘ-äî|Āā:ez…ō‡d•W˛Ļ*/U*[ŲĘ$wĖ%+ī; ÍĒgžŧÅĻËĢÂv&ņu™)ī]÷īŽY^§}‰üŋVrˇTŽM˛s|ûŦÉjąWr÷Îûö$}ÖJJKŽëéWíPŸá˙íQÖ]ÛõÔ ĻĀ[î(ãuƒĩvÂ;ĪZ¨¸h‚ˇž5“šŠ8ŲũŋæŸĩGČŨîz.đP;ŅÃĮíÄ|Āā:5#Į˙e˛Ę+''ĮTåĨJĮF˙ģθ/Ūö 9ũ×[å=Ī3å•ë…*ôuũ>āD÷˙ZÉ]:6֞ĩËŨ _=k—_5õ˜ū~ČáX÷˙šÍ…ú+Ob?kǿޗž5oļæžĩT|‰üŋVËKîēŲNø§O†œbīēģnûŗ™ĘK(đJã å5ĶiTiažŧí=TŪ÷õō–Æôō8čŽë˙õjîš-¯÷r×_ĪڃûîûPŋ#N$q´˙×J.ˆžä?c>kĒæ.ΚíD*>āDū_ëåõPîvčäŠÜõSŸ 08BŖFgũéĮīŧėXåuøũ߄NÜõJe+3’r•€—Ę[âĩō–ļéåqĘœČ˙kĨŦ‡Ūų¯Đ‰°^‰­”—gÍíÜM\^9­ôŦŗÎú\&aRÄ‰ö˙Z)ĢčÅ˙}<æŗĻjîx.wÛų˛°ëNæ˙uúYS%ļŌĮ)(lí™\đfŸŦ]Â|Å ŠRҞU‹žŊķŌlŖl!ōŌĖ~}yÛ{Ŧŧągöō'ō˙ú9wUÚÚäg­C§örԔ͸ ˙¯ÕpŨĄ˙­Ä*šŸ5˙ļv|ĀÉüŋ^k×,åŽ"+ĀAí“á€TY0nÂčNV^>ûЧ*[9DÂK›úōzgŧk߁¸r§|‰üŋá˛&ģ+QĩÜ5[^īåŽ˙žĩĢŽ™ū–ūž)åq,˙¯•\=kO=¯Ä˜gÍ{í„p2˙¯õōz)w;y*wũÖ'à ŠR÷īŨņ‰™ĘĢS×ÎÚs‡˙ĶTå%W5d瞕-?˛Í.YyåķGËxy ,”wčČķ(okSå]ˇe‡^Ūʸ^|Ā ũŋõšÛÉBîfū€1?kC=öŦU&}ŋĮū×#ŋĶķđŊT’8–˙WÔYĪŨįŋcĒã(×%™)¯:šëĩg­§žĩĄž5;>ādū_+í„ŲgÍMuļŌNô­ Č`Ÿ 0¤Bļx×Ūøā°ŠĘĢę‚*mÛÃO›ĒŧĒ.ŖÄõrš`˛ōŪ´lĩ6ãš ”ˇ§ŠōŪü­UÚĖ9Ķ3^Ūî={™*īŧo-[^'|ĀÉüŋĸŅãGiÛŪo*wG]0.ãą­ē`´ĮžĩžæŸĩ9™Öē—•™|Ön5ũŦ÷—؝ĉå˙=ģ{M €įÕ.ĪxŨ å5›ģŖ.ĢĀŗ6ÆÂŗ6ÚcĪÚUĻß×Ē،˙ˇž0ûŦ­T"wˇ?ŧĪ\îŽģ@Ü­˛đŦyŦO6gfŌ|Å vŠ,,*üĩŲĘkvÍ,mÁōĩI+¯[ÖŨ•´ōRiqéôŲÚĘõˇdŧŧŌ4SŪËĻĪŌË[›ųōę3åzÕU ˛Ē8™˙÷tîŽ6—ģ×|3ãą•ōÎ_~ģOŸĩZ=k3M—WĪãˇõtœf3cúÚ°pÅ­ĻĀS¯šžņøÖ?kkŧķŦ]˙M ĪÚ rwŒÉÜũĻå\°â6ã˙­o'ƙ+īU3”ČŨ…+V™Ę…ĢūõjEډāöÉđ€]j'NŠūĀlåõāžÚ°Ē1I+¯ _Ž­ßz{Æ+Ûš‹k´›–%oĖÚļ+Õžö\ÆË{ãĸšĄ™Īäåm§DyoX8ĪdyK–7Up2˙o8w‡´ŦՒģ[Ög<ļõĪÚXĪÚTmÃÖĩNXyĩ)֞yéɌWļÛÚĸ™01aYzæ%­¨¸MÆË.īč拸]“å-JęåIÁœÔ˙{:wķ<“ģϟĩb…žĩę =ôŦmuüYĢûŅūßęųxÂNĮķ˙ZÉŨ‡žy1éŗĻTîōŦĨĐŽMråYŗâ6ã˙ •÷Áģi'Ō— fډļz.ėãYËpŸ 0Ø!WüŋRZŠĀĒ'W‡ü$‰Ž¯—ƒ’ HŌ!ņ67oŅ"aã0cÎMÚ5sį(Ņ)7WŪšzy¯V¨ŧ-–wæĩ7hs’”7°˙īéܯ­ķHî†Ę{ŅĪZ+=k×x&wg\{cŌ܍VŖFg}$ģō­NâÄķ˙6ĖŨ ;Ž3¯Ģą\^×rW/īízÖÆûîY“vâ_-ŋˇY°Y˙¯ųvâFĨrwŨ–{æBJí„ûd˙j˛O†ėPÕ露—V+°ûŋGëŲģ`ŗū߆ĪÚXGŸ5ˇs÷ŽmxĻđWŸlĸĨ÷à Vyuד;˙h§“ĘT:0^˙iƒĘëÆÅß ÍîŠÔ0ČļŸöÛkߋēFDîÅīĖKG(YŪčĢ겧lyˇE]1´kßKåĩé6å˙=3wۜ‘ģ7,žEšÜũēŧmŧõŦm÷Üŗö”cĪÚKGŋJĪË­$q"˙oėÜ}?Ēŧ”Ŧâ=k7.ŽUôY{4öŗļd™ÂíÚ>ĮŸ53>`ŗūßdí„×r÷†EËCĢīJļ1ûdĩęæî#δø€Ā ųâ˙}÷Ô[ļ+ąĩ›W‡ˆeˇ}[{ųí_h×/XōHŠÖ…)ےUˇ…Ę*˜xbdۛjemXŪÛŖĘ{Ōå]ŧj}}yõÁ¤|mĨŧv|ĀVüŋ‰sw‰˛š{ēŧÅ_—ˇfÁ"ž5ĮËģÎČŨå–s÷ pv#—™ÄIæ˙ģ›BåŊŅÆŗ–ūÜõF;ûY[čgÍŲv"™؊˙7áŗļhšGrw“‘ģK=ģ듭nØNė´ŅNā+T—vh÷s'Nô˙…ū~ZŀžÚŗ¯ėW˛a=üĖ.­GY÷PYe+ážē‡”-kPËkÕlÅ˙ëåÜåYķVy{õîųĒū^5&Ķ8Š˙7Q.x!ž`Ģū_„ŌŅwĀ f8öøsüŠŠ$Ģ>āFÎú2{Œ‚2u°Y˙/B鮋ãų€­úJGžâ3|IЍfųÍ>äņ„ đ/˙ō/_ņÜ#Õ¸Iã˙Š•ŗguÖ?‰RM-Z6˙ˆœ§H×@]åēō „]ÃuuוM8@! tU*"āá:ļJW} P…j]oęŌ"ôĨŽ:cP`‡*ã}f›|ũ4ã" õŊē>3ū–Sējãŧv†ŽãÆëžĐĩßhôA]jtˆĘ{ųŦHa°ąÆxŸ.&^›­hŪw1žũ/Œŋå=å”x}hüũ:2PøaTnjF==6…ĪUŪcˇÉ×WĻP˙§krāT‚œÕŽ1ū&HS#ÚE§úŗ-ö-æ(ŖáFLĒ’ŧ.W×1 Ī/$`’Ņ˜|ŦkŖŅ¨ČĀ`¯Q)b˛3ŸĘxŖņZÕ8dÄ —:Ŗœëâ4Č?4Û5Æ@ęxŗŨǞԸĖN˙/ŸáMēߡûŲYË$ÉI'įžÖČį#Æß4#Îŋ‘8Ŋjŧ†pęt7ꙀØnäĻhŗņš|iÔÛn€ÃíB•ĸ1J–s80Úŗ›Œŧ–¸–“bia†ņųœ4>ŖŲF}˛ĮøldÂŦƒË⭊ö-”qHöŦe˙˙öîFŽí đ}~k{ŧ;/o_˛ylˆi0­K u…AĻX°‰LēE.¸ĘĢX* \a¨A–ęF.ZZˇ1ČGq~íV5ÔĄĻu͘ÔIu7pZLäH ¸dQœÄ/1ī9°/LįÆįhÎ^ß;3ûīyŧû}Ō‘Ŋ3wîŊsįĖĖųÍšįÜđ}ą°hãZhlnķEsn…đd~Iíût¤p{üa œZøņāz6˙ôŲ=áņĮTąžS áâFEČ=^ģÃ+€§z0ĮŖöŽ×ĐP+ž"ž3›ß‹./ŨŲ’× ÚęîÍĀã=€ģŠsWéY8›Ãm—TąWÄ­đ™Qv&ԁđڝYá܋m‹ôyÜKęqÕ{mk6˙L`ä¨ĶmîŸ õh{Åuã3X€‡ÃŪĄđøbcærō°=ŲÎöĐøÎÁ.YwūŸ‡ÔŖáßv§Ŧîaæ`ÉöĢ\Aˇv…}§BĮž’ƒ €›ĒXĪé†ŗÖŠĐi=Ũ^Ņ8 õ´€ˇ%õŽˆGÏ&ŗá˙#Évļ†úœ?vŦ:7‡÷ÂŅp_­MČß~ĀĪē?‹c&ÔÛĸƒáyĨ§ßÆ^ôÛÉ˙āĨ›ęĐh?^§Ą¤>>cHëVZīCŊ8^ĶžB>‘ü4ZØN>sęU-ÔÉcáŗžŨ)Ž;Â㏖lŋƒI›¨¨sÃmŪߓás}P5{EÚS÷õ…:|ĨĐļØŲEÛ" Ā#ĄYDÛb0ÔĶņB}ˇM„ĪÚĄ6Īao¨Ã‡Đļˆ?ĐÄļי6xyĪÎũŦû_úĢgŃ€/†0Oé‹=Ę}Iƒ;4•lg*›?.†Î}á !žēĮn>WōÅy-š?n˙|›āŨĮĻ,X4B@Îī(Yö|¸ĪiĐŊĨęÂlÖũ8´™ŠÆÜd6ŋĮ76ĘO% —8îmĸĐ8LËD˛‹…÷ÅÍęũíđŖLņGšÛ…忞‡OŨ/ĘĒĪøØYxfá9N„Æã¨ŧlÎ&Įē›`8QŅ€)ŧ&#IŖûvxÄúy=kõÔM–ÔĪt;'“Û/”ÔšŲäŗöDÉ{īRō™|§dûíîĸÎÅ%U<žĒwå5ŗˆļÅLm‹ņ¤m?cÚļ˜Îæ÷ĒH~`‰ëšIęéžÂ>å?ŨL>cī•lŋËYĢ{ĸM>>g‡˛…á ŊIŖúzh°ėiÚ€Ķ†\OgķO ۜĩÆļŒ$ 8ūöføō9–FL'áe8Y>†ž`Ķ/¯žđ…ŌÍŠWí~Ŋž“ÜW @Š“Y÷§ÃōĘ:–ÊPGGÛü0˛Đœ×ģą¤~ƞˆŊIšBÃHÖꁘ ût%ŧ„z{0 ąGb[x¤§z‡ŋgŗVoJœĖ­ęL…h{V>Æ= Og++/Ÿ‘Ŧ5.đvxížËĒĮK.4Į׹–|vĪ…:Zõyžng6Üŋ/Ôą´ÎÅĶĪútÉų’pŋ'ŧ_Žg ›…ŊĒÎÅ}kķ=5ޚ­¸ą’ļÅŪm‹…āš$¸Ö’ΏC%ŸĪÅíĖ…ŧ7|&f­aKS…ĪĶé°üöd[qž„=IÛâhōŅB´ Āeīg`™ėĘžq46ˆv/1_(YöF‘ž¤ŲÃŊ[ų>ŨĪî- ˇ_ Į1ŧeã8ã8ŪĄx˛MēUØ÷ž6_lģTąž´?4ˊŗ@ŸĪž(gĄøhEũŧŌĄÁpņÍÛឲ1¸ihg$”Í|3ŦgĄĸ›Æ—ŧü!ølÖęj$Aâ`Ą,4—ÕŖsáž-]āâgjģ:ĮŽgaŨUgÄ&Ō3[UįÚŠQøo[LU´-F—€‹?ÆÅĶĒowŲļ(~ÆO‡Ī˙âgīpØįXoãøå˛ŲĨ/…÷ėBÎú€ąĄ Îdķ'9´„\vų”“…/ v_RņZœ]y´¤ÄŪ°Ü餥S\.Î 9Ö!Ÿk€oöŊÖæ‹ÍĨ7z?lŒ‡×r6i¤í]B.›iv*4Ž:āë…Ûļ†u^ލ÷$X_MŪ‡Å{Ą7 ĀZ…YkÖãâ‹ §Jļŗ¯ Ûāâp+I.Öš8s~^‡ãŦĀ'K–‹ĄâÄ2āŖmÅø‘ļ-Nwh[,$—Í„~ĻđWÕļ¸[žįÂgoŲgįL˛o“IÛϏܙlá“Į Ā=&oĐÜ _ ›€GģøĀ¯ú’ē]L;•tíJģFĐ\V} ôŊäžŗYõiΧ3§@?núBc|ŽP˙€Û,Û)OU4ôە[Éã;-[uFÂÖ6!$6žN ĀTŪ‹z­€'ēxũÚā‘’:ۊ΍&oWŌ¸¯Ēsqß÷ĩųžzNUzäm‹8vwh‘¸]ÛbW‡ļÅLÅ{Ĩ›ļÅÔÛ0Ā#–7nvøā=RhL,4—Wœ8ĒÛ/Š€¯„/ƒĒ’Žs{›åÚĻ”—˛I°ŗųŊÃGŗęI°â$5U­§ ąŨL° ũ…ā‘Šú°˜ŧ3 ŸUuysRog;ŧ?Ēęc=kMW6ŽĀ+j{x / øU5 ˇ. ĮžŅƒ‹Āy¯Ų.ę\œ€ę@›å†–!ĮÛˇų>sVÎĘŸe;Ú,sĸPĮϞÖéōŠÃ ĀÅy7ēm[Ä /uŅļˆ“¸m]dÛBxÄâl™gģøpŪUUĄv´‹sĮ[_DŽ×ėĢøĸŒŋėĮIŽÆ*N‡ŗęSAĶ}ęĐPl÷<ķũŧǚõœX§ĩYæRáõOO{/[n¤Cc&¯¯Å™Åģ ƒYkbŦĸZ¨ë{ īΞēŊ?ÔÛvŗkīģcYûŪcxyÄņ†í&„+|æTÕšąŠ|ąÍëģsx2 euîp¨sąŽ”a°5ėÃB.%SUįâ,ī*Ū¯÷3?JŽ´8Įé6ËÄĄH;’ĪÃ˛ļÅéŠ\6Ą_ĮÛˇˆļÅløŒ.{ßMÚe×JOÛG˛îf4€‘žŦ5•˙ņ’†ņžđe’NšŋŒömŗxĻđe0Zō!~ϤaUõ%UuíĮũ…õnI’ÅküÅģh\)¯ø;”ÜvĢäyĖ:ĪēËŖ ņŌ\ žžŒã˜Û´>Îeķ/›g$/ ĀS…õÆ3ÖYü1§Ē§9öīĢøáéH!ô\,Ŧw[høßčplŌ^ēôxŨÎƝ,/ŋ8SōĨ’2v„×á~R÷—ËzÖ3\ Ās…2ōđy§đOŲiÄUx4yŋÔJęÜ­ÂgåŊlūųžŦ5Žx!ļĢsqĸÝ%īWbåř’ËÚ}ĄÎŪ/Ôšŗ%ÁrkÖēTW1Īâ‰īƒS%?Î vŅļˆÁļ8áB˜ß–ĩf‘Ž>'X`€G`kÖē~ãũđĄ>•Ú;…FÄöđáa?ž Ļ+p<%ķTøāŽ—6.itßÎZŊU_Rĩd[ĶáËõBXo1„Æ ˇ*ü?_ļ›1`į’†ŨņĘfܓl˙dŌ€ÎvY^9{ŗÖĩcũN[7 ui˛ėšNâlĻe8ööžH–)ևsICîd‡<œüXu)ÔĮŠdŊiƒëTōŪ;Ūw1đw:õ3žŋæB=>™ŧgv ˆz6œáÍŦ5'BüœŪWX>Ūw%ÔĢŲ$TđͰŽs!tÜ ucG!hĮYŅgBŊhwz<õt&…sungÖē&û…PWcŊ^čåcÚÕšÍáäÛ:“ĩfԞÍÖ;Įōļ-ŌĪØÛŲüŗvÚÂ˙/ĩ ĀŗĄžÅáF7 a÷XŌŽšĐĄmQ/´-N%ŸŨy*iŗĖ&õën؇ą'ā č—Â}l„ËĘĮdí _&ˇÂrãá‹l2ųB‹o đ×C9™=ÜķZKôéØÚĒQėĨģöõjx|Ųžî ëŧĘšŦû^†¸é°ËYųĖ“Åcr=ģēĒÕ͆CŨŧœĩzņ/…×ŧVš/%Ëíui2Š{cÉûāL¨syũ=–ÔĮŠđž-Û׹Đ(Œõ1ßΖ°ãáqˇBŊŪŅEcwr?Ē?{ö‡ĪĢĢáõžõcKEā;^ŗĢĄžô^“Ąäīũ! ĮēQj†÷ÆĨPWĮ uŊėũq!Ų‡Ķû:ÂÅtx\\dŊéTį6‡įv=yžÂīŖi[\L>O.‡ĪÃÁ6ßŖ7BŊ+i[ėJÚĮB°žZņ›˙}"|VžY`ÛâfxT}—ĮļÅĩPâ>-ÔXáųU‰īßĒŦy[šåxŗ\n–Í2Ķ,ˇšåbŗh–A‡€ĮŲŽ'Ÿ|âˇúûkŸūžī}ËÍ÷˙Ų?û?WßûâĖĮŪßøßøO/˙‡÷LüŲ7~Ã˙tÆõŸëë[÷öæō}“<ȞęīßøÂ/ž>öBãķŋÛhWn}ôbã›ŋéë_zíĐāĮ›vøx,ÂīÆŪ÷MßđÆĪÜŊ=Õč~ĶōĶ˙ęĮ_öėk>÷ęW×߸JM~Ē÷H›RëĄ}­…}ZŠĶĶë=øœķũzLęĐj>[b°ëÁÕRÂo,?øÜ[߲ķīL­ą†}_‚w x€`čėĩ¯ü¯o˙á/.GøÍËŊŲ˙ÕøŠÍ_öōWõëža5ėã2ãÅÂ}y@;Ķ,ûŗōq ųéÁųĨĨΆedk­…ĮŸ˜ Šu€ķņ™‡Âãōr°đ¸|ŋWę‰$¸Uā<øOÖ?ž{ēūŠđØS%Ûڛ<¯ŧ§}{ÅqŪ•,w8<‡np_8~gÂōĮ û˜…íN„ã5šl+īŊîö÷ÁđúM†Įoo€ˇ‡į—Ũ[QˇFÃcO$ûO%ߎi~ûŅėá30ōį=–,3ęÂ`aŸ'ÂūlIļs2Ŧŋ¨ŽÜī]0Ŋns} ˙ūB'ŊęTūãĪ˙Tcë×ŧáck,Įp—öīk–ûÍr/Ü#,3U)y☠÷]Ëô&įãŠg“€6”<ūZXĮíđ÷$Ė–ā<œÜ ëŧ7˜ė˙LĪŊ,ŸˇŨë‰ëŸK–;ß,w“åĻ“pv>Ü~+<>îk1$ËŨ ËŨÍZ=īđ`˛lÜÎŊ°C… Cz–ø5¯~ú¯˛åŸėčQāũŲüŲŸķžąŊI€ģZĩ÷C8H{r÷‡eOÂķũBpØ‚Įåđ÷é𸴇°/ŋôöbŽ…0s'›ß“ˇ? zK Ā;“┆ļŨáö %ëJCāą’ĀžīķÅ ;“ XKBíÕ.p|Ō°;?—lg< Ų;’e†ÛwØÎ•BđOuúÜw%Χ^˛#…\Ü÷øŖCZoúÂņn$ûīÂ~ž,Ü>’lgŧäuL‡5\ ĪqŦđcΜ @Īzũë^;õKĪo,wÎˏūđ÷7žâõĪūä* Āíf>Wē'ŗę1ÁWBpŠ… œ/wļdšc!čÄuŦd™=…ĐR Ā{KU”žŠŧØŧ-„­%-§˛œ/sŗäąÃ…`{&Ģ>…¸S.ü˜Úšøôų(,Wīb;›Ûŧ– ûƒjYō­ÂkŅ(9FņyŸ/Ü÷˙šđ÷XEh/֓‘’ ŨHög0Ëķm~d€č=¯zjā͎>zqEđûÎ˙쯗ŋîĩĶĢ,§ŗ@įaôN(e ūŠ$`K<%uGÖęīr_C¨ŪöįZ‡|,›ß#Xe)§@įúB aû|Jíp Œ×+ŽĶŊž˛°ĖlÅžßéLwˇų ÂĮÛŧđXVŪۚ…Ōįž‡ÜģĪ;žę>”l÷Ba}#YųXīĒũ¯'õæxR‹x˛CŨˆõĢlŧøA€žÕ×÷äÜũOũöŠāŧ”|,kī§_ˇĩ Ā;ÂßwÛŖø#ĘLÅūÅûÚĶņ?2¤ë^Jn÷ØņÂsĪëÆũõcs›ív€‡Âk1—ŧ>yŨŧ\€':ԍnžŖ @OZ‘đ›—Û7?Đčßô™U€są×/g;X ÷ēXwģāÁd×BˆÉĮˆæ§ŋÆq°NŽû^Öœ‡ŖúđĄŦ5–uw6˙4đN§@ĮĐuē‹ãt-ĢîžÍzĢø`É} Ī}&kõnw|Ÿ.!ĮMN†zS¯¨'Ũāø¸Ã0•uëÖ}1Ÿ°jĨz€ŸĒĖŽœ;—=<ö3ŽŪ]˛|ŧ\PFļfÕãFãWqŧįŒeŽfŨ>\*ī$!gŽÍsĢ ĀWÂ߃…ĮÅ1ŧÆß áǝ$ü_Lö;Ž.Ž5ŪŌE0ãV2x18>įvãcãsc€ˇ”,{&”åĀÅÉŲĸį€ãąŧT˛ÜY€žÕ ¨wf>öū ĀS˙ũ¯yæéß_#x(šâäPyPČ{ĶŲ°ĮÂíĶ…€Q6 ôũ“uĨAqG˛Ũ#¸ÂKqč€Nf:~uoöđĖžÅƒÜŽB⌧Ø^/9Žģ“įo;Ü–^é`áx^MÂv=ŲNˇŗ@)ŧnW ûŋ”GžŸû“Ûö$Įq$š­ęAúãÁĄ’Ŋ”œŸ…0›ÍŸiz[¸m18ũa&=FŖĄž ĀôϧŸĒ˙Ī÷NžcEđ;&~tnũú'ßŗFpkõôŸ ˙^XDŽ'?ÜHžã” @/;°oė͟_‰üuoüšĪdå§˙>Žļ‡@1Üašũaš‘BP9L~Šč$ §jážŗĄ,Y.ī=>Öu, ŅûŗÖ5Y‡Ã>l/Đ˙æīūę’ÂīôŸolذūÅėáKÕ@o¨Õ6ŧmđéúK‹í ūĨį7ę›>Ÿ™€ĮĀčSõģ?ōCß÷rˇcŨúčÅÆū}ßņ…ú@˙'ŗųי€ž6øėŗĪü|˙ĻÚ_žíûžķåŧgwæcīoĖ}ö#_ ŧ÷?õÛ_ ŊĪŋû'oũŽočßôŌāĢęĮŗōKû@ĪzæU¯ú‘¯yũG^ũĖĶ/<ųäēŋnŪÖXŋžīåg_u÷+ßđåWjĩõ?=|]SX¤-ÍrŧY.7ˍf™ åRŗk–­ŗO>ųäoÕ6múô[ž{īüÜ/<˙§~ũŧøßŧÚø•üzã§ßõîÛģŪôĻ¯_ŋūsO<ņćšË:dúö[Íü˙ƒ€ĩģšåVŗ4 å~ŗœĘôžƒōĄ|ųîŠ?ÖãáØŽ†ŋGÃßã \ĪTxžŦ[ŋū'žjË×|~)á7–ũ?0ūąæ*¯”ÂĮŲHŗÜk–üTīÃ!Üæ=Ũûšåjg§ ™k–IxYđö°Ī{``)ļlęī)ŸājŠá7Ž ŪüãÍõXEĮčxd{Kî˃ūÍxĶĶĄđ˛āÅ€€–zŊūۃ‡|n9Âo,įŪ÷ūΝëëûÃUt˜&C Ģß|0{pЍüū‘f™Ë_˙ߞ,›Ÿ~4ŦķløĄ Vķ’_†ęLö wy[r~:öаŽSáī2ۓ历€žŧ7l;ü‰ÂķȒį:RØÖņ°īŨ¨…ã7žëhI)9ŽĩpėΆĮN”ŧ>exkXö w?Ŧ-›ûî/tŌĢnĘkŸ}ö“Íõī\%Įé`d—ŗÎ“|í Ą+_ū^ø˙žpßžp[>nxēYŽ…ån†—ˇkÉzōō\¸īdøûvXîvøûda?ōņĮy¯ôŨ°ÜlØÎ.p-<×t;wÂßÉrŖYëôī|[ˇÂ6âsßÖa;C؃ëKĮ âņ˜ÎڏĖZŊîWÃūŨ ĩ Ā[Ãq˜É\Ã֜ßņ=o}išÃo^ūŅø~Ēšū#Ģä8õ…0ÃhÚōŪÎũYõ,ĐÅS ‡CHģUģģCpģVnųã/fzRw†P{FĪd­1Ö}!€Ļ§hoIÖ9˜„Ú‹ašN8†ėãÉmõäņ{ Á4ôéøÜņŦģSĀcĪúūB¨žß! ī)ßōËđÖäūao}Xc†ž}öÃīxįģ+€ßyöß7ú~mŽžĀĻC¸Œa8˙~ äÍđąpÛXÉēc€ŨUĀ#…åō@{'{ø”éZ×WÂß…õĨ!qŽCî ëēQrß`§S…`ZtīvØN-ŦëJ›ãQ€ĢŽåļl~¯s ĀÂ/Ŧuõ;—~czEđ¯~đC§žzÕ'VéĄËƒ`Ūûx2kĻœŦĄ68öž–]Ëvo¸īpÜî•,×ۛ()ŗ!tĻÛ*›‰ûz‡`ē-<öTÅũW“}‹ÁtĸdšNcwd÷2āNō‡ IDATGcđļäGˆüųœËŸīT8&ŗaŲ#Ūõ°F=Ų×7÷‘bEp>Ģt°WŅáĒWܞž‚|ŧMŽŊēeŠA˛lōĻ‘lū¸âĒŌi[S‚iģP[\÷RpģĮŽf¯ŧ3ũ{YĢ7>ēJöõf(÷˛‡{Õ€5bEÂo^>xõwõúS/Ŧ–ãÔ,ÚÜ?XxŌ{<ĩ Ā}Yk\p'q[ĩ’ûŽeKëž–­|đŪ.pT ÷įŊņwÃrےã˜ß6œŦcĘÛÖ uëÖ}1ŋnīJāüÔęfūķUr¨ō™ķņĒ#÷īĘž‰šj đž’Į—.ģ~í>ë%áø|&ãļö”õûŲâĮĮ/%ĮqËWKî;Ņ!ŽøAâ`ašâq<]øĄX+ú>•ŸĒŧøų÷ū—Æā3Ī\_%‡ę@Öē$Đx֚Yšíl–éĨuâ,Ė}ĄTÍŊ'+ŸzĻMĀģÍīŨ=Uĸq[ųiŋ› !š›^Đ@g­qĩé,Đû—!gYkļé#…îuĀq’¯ƒ%?{€g ?ÜΜ kĪ@ŊūáŸy÷{V$˙ŗū/æ6Öj?ŋŠ×ŅlūėĪiÉOŗ-ÎH<Ü'ˇÚ›ĩŽW›÷|ÆkāŪ,„įǜ‹Ŋ˜ņzžņ:ĀW Ąx_å÷“åfÃ6;Ķž$ė¯|"YnЏžÍ§{5›KpŊpėŌãpĸÃqܛ9Ö¤oŪķ]wW"˙ͯũÚ<î^eĮ+īMÍOŸÍ¯Ã;ūÍ{‡ËŽœĪ}$„ÃŅäöáp{|üsŲÃcu÷då§JG;CЛ xoÅr[Âö'ÃŋCaŨ{ē|žųzO…ĮįŊÁÛ ÷‡PēŊäąûēÜNļ÷‡cq&l3Žw¸°-ÉãjáØÅ×âD8.ŨĮ}a}C>`íÚ°aÃKēöûË~åŋŪØ¸iĶ Yųex╎~ũúĶûŪö܋Ë€ŋé›˙Ūį˛ÖiŋĐ7lØđŲ_øåķË~˙õĪūܲúû˙$Ķû @­mÚôâÅO-)üN^øoü”ęlū„NĐ;6Ôjo¨×_\lOđ;ŪųŽFŊžĪp<ęhĐëFû>ûũ˙øūĒۉą.ũÆtã;˙ūØ_6Ãs~‰m!‹Á§Ÿyæ=ĩM›ūōģĮūÁËyĪî~ķjã÷>1ķĨĀû‘âKĄwâgN6ží-ß>ˇŠŋ˙Ĩ§žĘ/ASsčx õ÷÷˙Ķ×mŪüŋŸ|aŨ“OūuķļF__ß˯zúéģ›ßđ† ü`ķļēCĀãk°Yn7Kc —›ĒĀę7öõ÷ëūüãŸēŅXĢečŲ×|ĻyļĢ ĢÛŠûŊ´–đ[˙á÷Üi‡ÃĒĀ*ļiĶĻ?ú•ËŋÜXËøÔ/žllظá×Ô€Õk°o}ßūđ“]Ķøˇ>6Õh‡ŋPV¯5?ū×8`€ĩa͏˙5` 0ū×8`€ĩĀø_ã€ÖãXŒ˙5`õ3ū×8`€ĩĀø_ã€ÖãXŒ˙íqĀ}Ír¨YϚ%ßöŊfšŅ,gšeËÖ{>°›Ŋ-€UĮøßžœĪ+ÍŌh–Û!´N6ËÕp[†w-rŨ3!Tw2Ō,7›eÜ;XmŒ˙íqĀĪ… {&„áÔhŗÜo–[+€GÃ>ĀĀĒcüoīŒ>Âį–÷īXb ë\D ĮĸĐëáö­%÷miŗ}€cüoīŒ>Ų!|Ž„€Y ˇY>ŋ}˛€§ŗŊËsáūš°L=,3nOËH˛ũ؃Ͱķ˙_M‚īéėAīt|ĖlÅ>íËœÚŨ¨Ø>ĀĘ1ūˇ§Æo 3†CˆiŗüBp §yŨžî a™üļáļŗaŊõd;w›årö`2­#á1—Â}yÎ{uG“ÛÂo~Ûĩfk–mÍr4<×ŧgēĪģXIÆ˙öŪ8āŲƒI¨Ō^ؙHˇ/1Ī…ā™š ËÆu–Ŧ3nįRáąģÃíÅŲĨķ0{={0‹uė­žJąˇ÷HÖę]X1Æ˙öîõ€ķŲžķS’ķŪŅôôâKĀe“`ÅĀ{´‹|°đØSáöm%ëÁvg8nų˙O–,7”ĩ&ūX1Æ˙öîõ€Sy/ęžŦuķØ"đdÉr#Yë”įNx´đØKŲÃc†‹eŸĩ&•*Ίœ˙}&Ü˙\!Ŧž(,{$ë~ŦËáö‘Â:v€w…ÛĪ—ėëÕ°ŪÍáļ[؃Y¤‡ ËĘæAXvÆ˙öŪ8ā<Æ ķ“ķSķ‰°Ng­ņŋ—“pœ÷žŪ AķxĒgBĐŧ[€ķK,͆p›÷üÆS˜'Ëm ˇŨ A|¸MNC{žŽąPŽ”ķŊa?gBčŨļ;žķ ˇ$°"Œ˙íŲqĀy<™ŪFHg÷ į§_K–ģn›ÎæĪē˙ÎuœU:ĪGJöátX&§y/oœ€kgɲųū Ŋ‘„÷˛õކũˆËŨzØ;XąeüoĪ_8—_3w$ëŽwt0ëž5­›W`‡˛îÆōÖ˛Ö)×+ĘøßĮįzĀ,ņŋįõ€Xã{~0ËĀøßĮk0‹dü¯qĀk‚ņŋÆŦ~Æ˙ °˙k0š`ü¯qĀk‚ņŋÆŦ~Æ˙ °˙k0š`ü¯qĀk‚ņŋÆŦ~Æ˙ °˙k0š`ü¯qĀk‚ņŋÆŦ~Æ˙ °&Ŧ[ˇî‹ÍĘʔūūOĒeåéå>ЗļÚÖ>Ĩ-Ú˙3ŋጠ‡“äœ33gũŧ_¯ß rr2įū“É+ßüį7XĢÕP­V5hPīhũúņ˙͍aƒ/6¨˙ErbBE||ŨEÚយ&@8ߙąąužėĐŽõų…s§ūųĐžuĒǞ\=ŊWüh‡ÚŗķYuĪäąß6nÔā-R“OjĪĖ´ÂIvlėuŸõęqãīūķŖíßĒŋœP5ÕĨ¯ŽŠ-kĢÄĻ˙Ņ4Ąņ[Ú×'0…€P7¸NëžZũ܏Ī×|Ũë› ęŪâ[˙^¯^܅N‹„°Î~ß߷†_s­Y5ī;-Šm¯S 5ņ×]wŨīÖŊ0˙œ•đkÔOžœ-+ÁŸĘv™Z@(Y4°÷3v„_Ŗō‡õûŖl—Š„Іręķ¯O–˙ŨÎ,WŒŽ[÷ú?ĮpQ,@ˆ¸ģWcgø5jd^˙/´í3ŀP°sÍĒyŋw"o^ŗøÛ† ëídŠAWˇnė˙ČéĘNā“íPÉI ŋa–AwŨu×ũãâG ĀgOīU M}Å,B#áWęܙŸĢĻ ŋaŠA÷ƒüāÛK_s$ËŠÕ)Í˙Ä,‚Ž^Ŋ¸ rǞøĐžuĒmVúo™e@Đ5nÔ°âÍ­O;€ŸYú ęÖĨũAf fN**ø‹xؐŪ*ģ]›‰L1 ´mܨū_ížô…ĒĒaƒú—´í7dŠ!ĄIã†W­xä;;đƒ3îR=nĘŪËėBIˇ„& ˙öåŲ÷lģúsŖ†õŋÕļۜŠ„”Æ Ŧ2 ×˙ŗ~åTꮝÛ}×ŗûĨĖ* Å6lX˙“Ų÷Mø§ŋáWî'|ûØÜĩo›ņĶ e ‚'ܑ÷„Y_Âī7*T^nßoŗÚ´<Ŧm'žŠ„ē† Äīo•–ōįCûÖy~å>Â)͚ū=ŖUķ7´¯e ádl||ÜųŽí3ŋrɏūzæÄ›ú*¯Ū¯ĪR§ŽîRO.¸īŌ ™­ū”Фáī´įį0e€p%ĢšC¯ģîēmõęŝ­sQûX]}ė_Ö¯÷ŋĩ€\Fđ xęׯ÷Gí_EQžTRŗÄ?pø'c{õíųį˙úâ”ĸ(_*ŗm›Ũ}Ã-BÂBũ†õWĪYøŽōš&Wũö]ÃQ ,4IhüÛ]ûˇč(ŸkÕú•ęÆŽ˙‹Ŗ@8H¨[ˇî??;_I Ŗ|ŽÃ§Šú ę_â0č˙Ĩčųč˙Ĩčš6Šĸ˙—˛ÚœŨŠ}G€PÖPú?=w‚ Gų]ĮĢŽ¨øø¸oĩī§X)Ą*ŋk.ßâ(Ģ•ŨŠÃŋZˇn=„C @HjذÁĒŲÎ ĀQ–̏d˛ęÕ¯×FŽ*!ŠibĶΎ–o"ĀQ–kõk/ĒvŲíū7G€PD˙/E0€¨ũŋŸ¯T¯î.SĶrrzŠŦŦtÕ:#M¯öíÚ¨áųÕˇ§Ģ-ÚsäšŅ0y玚yĘĶæiö#Á'ú€„¤`÷˙JH[ŧbžJMIR9ũēŠ% g¨CûÖŠĒĘruöô^ŊNŨĨŪÜú´šķĀdÕĩKÕ,%Y-X27Ē‚°ŧ×EÚ<ĨhķÔߋyē)ˆķD0€Ėūßũ‡÷ĒėŽmÕ¨Ôɏv(õ—^ՑßŦ ęŖ:vÎVí‹øđ+ķÔA›§Bįécmž† ü<Ņ ­˙WBwRR‚Ú˛vą×ÎŊJ= š§ĨĒçElø•yJÔæiķšRŋįiÉâĀÎ}ĀBQPúߎ(×Ãī‡ĘüuF=ūøLuËĀžy:´Ė“„ß æiņÂę–A›'ú€„”`ô˙ĘjszFšÚVö”åP'uéĢcę–ūŊÔŌUĨ~ežZĨ§Š×6,ąmžäĸYš'ú€„”`ô˙JāžĢ¨Ā–PgÔŅ_lUí;ļ¨,ķT4ŪŪy:ūÁkz/1}ĀĸMĀûOž=ϟŌ+W.ļ3ØIuîŌAíÚŋ="¯ĖSĶÄõkæŠk€æ‰>`Ądh ûŸYŊB.b{¨“zôĄ)jîâ9€ež š§?āx9Á28ģS‡ŋ:Øi¯ëH¨“:wæį*--5"°ĶķÔ2€ķtSŽ˙jÕĒÕH9AŒū_ŠØØ:úũhvrĘpfVzDā:ĪĶ œ'ú€U0úĨäe9׉`whß:uK˙^Ķėä<åänžčLAé˙•Ęͤ^ßŧĖ‘`ˇĸtļ*žwbDā!Ú`Á”ū_ŠĨĢJÕÄĸG‚]˙~ŨԖŨe€ežÆ/tdžr´yÚļ{C@ß}Ā‚"XũŋRG?¯PMš4RĒØęN~´CĨ¤$ŠĪÎWFD–yjėĐ<ĨĻ~žčÁę˙5jJÉd5cz‘­ÁŽpÄĩpé܈ŋFkķT2ÍŪyĨÍSé˛Įū^č Aë˙5ęxÕ•’’ŦŪÛģږPˇeíbÕŽ]fÄŦūšįŠ™6Oīî]cÛ<ĩoß&(ķD0€`Z˙¯šļ–oRII ęˉ7-…ē”é§TŋsxoD…_ķ<%Ú4O Ú<<ōŗ Ŋú€T0ûŨëų Oëũ¨ūŽo+{J˙úmZHŒÄđkÔĒõ+-­˜Ë<5׿éõŸm ęû @@ģ˙×Ķ gZZǚ5}ŧ×|ĒĒ,W“Š TVVēzģâ§~ÍķÔBŸ§"Ÿįémžö‡Ā<Ņ bĨ˙WzKC­×ĩdfą~*ŗ6Yą<{z¯ēôÕ1=Č]üâˆæĘ^^¨n5X?uúąųŗÔ§įNDEø5ĪĶ´™Sŧž§dmžæ-˜ĨN…Č<Éūrõ7äPā´žYí2˙ʡūyúųÅĒ pˆj‘Ļbcë(mĖZhĒĢ,÷Å}aí2Ė x…Ę<­xžT,đ`Žĸ˙—ĸ@T ˙—ĸ@4 ˙—ĸ@T ˙—ĸ@äŖ˙—ĸ@T ˙—ĸ@4 ˙—ĸ@T ˙—ĸ@äkڴɏé˙ĨBąœ7[ewĘ~ƒŖ€-Zώ<ējũJrĩk˙v•Ú"õ÷Ĩl÷÷縨ĢĪÎW}Ā ŠŦęÖ*ŖåE;ËĢģËÔ´û&Мœ^*++]ĩÎHĶĢ}ģ6jxū@5ķáéj‹öyn°BÕÖŨÔôûīžfŒ]ētP…c†Ģ'ŸY¨Ž~^Ôāˇũ§T‰kŒ7¸Æ˜acéĘ'‚6Fc?OÕösû9OÛĪŗąg?į é˙¯f͚q¨°Dú'ˇ%-\:WĨĻ$М~ŨԒ…3ÔĄ}ëTUeš:{z¯^§ŽîRon}ZÍy`˛âšĨ$ĢKæ,Ëë,ĒeŒ'?ÚĄÖ>?_Ũ‘§4¨¯Ļ”LV'Ī h°,]ö¨JMõbŒˇį|Œ2>Ųg)ÚžëīÅ~îjÃ~žŗđ!ÕžcûŸr´°ÄŽūßũ‡÷ĒėŽmÕ¨ôpĻūrÂĢ:ōŪ5pPÕąsļ:xlŸŖÁímŒ}ã…ĒĒøîŅĒSįęƒĘ÷—üLŨčĮ'ß5FŨ¨ÍĄĶc”ũÜA_ĄûųãßŦ† ö?Ķ ĀVûˇ–oRII jËÚÅ^"÷ZŧčÕ<-U9ՇŧÍâWũäÕBŸ“sįž*ŲŸ[1ĮŅ1Ę~NÔÆˇŲÂ~^˛ØŋũL0;Xę˙}ģĸ\–(ķ;ĩ`Á,uËĀžļŸmן×B°Ŧ;qĒņūŠŸęá×ę%¨ËJ°Ũc”9”đ[aÃ~^üøLŋö3}Ā,ąŌ˙ûéš*=#Mm+{Ęr(’ēôÕ1Õ¯/ĩtUŠmÁMƘaãåtčŪ;ŅÖp)clmã'ß5ZM˛qŒ2žVéięÕ KlÛĪrQ/_÷3}Ā,ąŌ˙;ûŅęŽĸ[BŅ÷ŊĸŋØĒ…œļļ…ˇ=vŋšdãŋšPĄjYĩkŒĖĩŒé6ŽQöķøņ…ļîįãŧĻ÷Ķ `üí˙•Slå”Xšō¯ÁHĒs—zØąÜdŒIŒQzt‡æ ´%\Ę“ãÆ5ĨjHŪ [Æ×41AũúäÛ÷sW÷3}ĀŦđģ˙÷™Õ+ÔčÂ!ļ‡"Š9MQsĪąŪžYŗ\Ũ:j°íã“SxSR’lšØÔ k—98ÆdËc”ũ\āĐ~ūņÃSÔüŌ9ôpž•ūßQãFXēęsMĩgįŗjäčá–ÃåØÛœãŒéEú}m­ŽņļÛōãôiÖĮX8n¤ZŋĻÔąũîgú€øĨ^ũzķ÷žģqqqęâG FgOīÕļÕpW׹1JhŊąŗõīÜĨoˇcįlKãsz?ˇöq?›ú€“9‚x+;ĩEę?ü FÚ×;Š¤ÎųšJKKĩ.c‹0cs‹ctz|-ũßĐCū•””TĖ! Ā+ÉɉŨ6aŦßÁ(6ļŽ~Ą%§VW33Ķ-‡K§ĮØ&3#äĮØÚâë8<ž˛|ßĪŌלÕ.ëmŽb^ÉČL¯XņŌŋƒ‘œĸ,§°:Œí[§néß˖`'ĮØˇ˙Íļô;5Æwĩ1öļ8FévräøžŸ÷ŧ˙†Jj–øŽb^iШÁ_ß˙ä€ßÁ(7oz}ķ2G‚ŅŠŌŲę‡÷N´.‡įtlŒË´1Nšj}Œ#㓋°ŪÖ`T8b€z\ 7VĮfÔŊ÷MRŗlc6Æ6Žąäžģmc~ū@ÛÆXŦíį’iEļŽo”6‡O.{Ėī1Ņ Ā+Vû:^uDĨ¤$Ģ÷öŽļ%mYģXĩk—iËĘĒyŒŠ)Iļqŗ6Ælã‰ßVÍSíãĻ5ĨļŽQæPV“ßŨģÆļũÜĄ}ËãŖ@m,÷˙škkų&•”” ÎœxĶR(úđ@™~ēōūÃ{m –FmĶÆ˜âc|ũgmc…6ÆÆŒQösĸMs(§|<ō3Ëcĸ@ėč˙u¯įׯÔû9ũ]ÁÜVö”ūõTí–FŊ´áiKĢŦúS’ô čÔהũÄŌˇjc”y§Æ¸JÛĪVVüeåũíŌžãĄ@ėę˙õ´B˜––Ē÷˛z{Á¤ĒĘrũ ČYYéę튟:,ÚągŖjéį3õ1–;>F ‡­üãŨÚÛdf8>FŲĪ-ôņų<‡7hsxĀÆũL0€ŲÕ˙[]¯č}3‹õS\%đȊßŲĶ{ÕĨ¯ŽéAčâGô0TöōBuë¨Áú)ŋÍŸĨ>=wÂņ`ų}ŋ­6ÆŗŧŖœŪ=gūė€ŽņmŒŗfũЧ1>Ā1Ę~ž6sŠ~:¸ˇûyū‚YęÔ˙ˇ},ôÖíZWSwkÕ9€c‰ ŌûĪņ1â [ûĢŊĩĪįęéįĢÂÂ!ĒuFšŠ­Ŗ´×Vņņuõåž˛/Ŧ]Ļ*Ī X¨ôtûĄg^XŦFU3Æ;ĩ1>¯ņdĮx\ãs/,ĒvŒwhc|nÍō Qn‘´BÛĪՌ¯Hß+ÚūŌÁņŅ XgåžZę-­âG/­Néũōây ĩZĨÕže‚§Yŗ¤Ųv÷˙RT0ë‰ķUfÛ6û9ēĪiÕÆCåhõą+/rx›]¯Ēxp€æ5Čj›õīĨ+Ÿ 8QSrĩë&M›ü‰Ŗ pđl Ÿ—Ū4éQ;C&[BbŸ¸…Eŗš&üĶõGG9‹/=ˇÆíëö›æxgŒįžęV撞Ākĩ‘ @`ĸ"­ nņ¯ĻM›–pˆ?u?sš„¸¯ĩē¨Õ†˜ËĢĸēžˇĶ-ŦžåzüC×ķ6¸žNo[×ķVē‚ŦvŊ–šÎ˜žÆØæYĪ;äaŦҌū_Š>`Ø€=­€æģ˜„ãæĻĮīŽ!\ēoīR Îß,ä"WĢLaŌÃ9~`OĢŲ 6āŽ˙{ĒĶ6×đŧš|‹Ō˙KŅ gpŒ+dJ@3÷ÜöšúQî$ˆžtũ˙Œ+@×´ín~`ŗ6ސx)æĘjsu8žšė)¤wv=wšø%×ãŊōžz¤á†ū_Š>`8€ãcŽô™“{*æÚŪ[cÕØ¸ząq!§œjļûĨiŪ`šjôS(5;ezFX]āöœĄÕ`9Ũ;ÁíšÆ õP×Į9>āąnáŲlë=ôrŊwãSî}Ō}]ãZΡ(ũŋ}Āp>›CŖ5ãŪŊscŽ\ŧŠ•+Čåģ‚Ũ7Ļ_āÚē>ž` ’ō9ãÖH¸CyŦÄūĒsĘĩÍÛ]¯sĨ/xƒ‘—bŽÜji¨kÜæņyē ô‡Ļ÷"Ûģäz,Æ4nyŪģŽ€PC–mœtmCŪcC×8gēûØô\ã‚Y2'ŲĻđ{ÆõÜÎ|‹Ō˙KŅ €Ā`aŦΚ{eŸr4ķUŖĢ<„WY9=įö< §žVgíĒa,Ų1WîlŽũ1W_ĢÄõ:Æį/š^ķPŒį‹`r۞|ÜÜíĩ›>Ÿ_CŽq}íģÆųą‡í.pĢŅĶ<–oOú)ú€`ŸVŽĒ‰Ŧ^ļ‰šöÔ`ú€€ČG˙/E0}Ā@T ˙—ĸ˜>` *Đ˙KQôŅ $ûˇ–oRŗĄ˙[ÛsdõÎŽ×]ũڋú6W‰¸€gŧ7÷Į?;_yÍcŸž;áČū åyĨˆpĄÚ˙+aIžjšØTũŧĸÆįÔ’}­Û&ŒÕˇig¨•2Ū›ņą„Ņ1wŒŌû_ÍĪ›ņp‰šX<Ū‘ũĘķJ0á:uŊqīÜÅsB6KåÎ#ÛP;Ūzõǰ+ķ&īÕ=Ëc2Ņ€?¨|OÕĢ_īoüT"T‹–ÍŋØĩ{H¯Ëŋ/nzŽėĀiæāĢĢezK ĀŨøÉDž„zõâŋķÔ*XÂYũõ=ž í)Ë{ybÅ|ÕąsļĒ[G˙:YAŪxī5¯!ĄēsˇNúķZϧ)Y ÷€Ož=ĻĻ?0UĩÎĖĐ?'Ī-.™|M?ëĢ!yƒTã&ômļËnĢœ7[y3ŋ˛úxį¤ÛUķ´Tũ5˛ÚeęīO^ÛxŽ\¨Ŧw˙›õבĪÉsåuzöéĄ?&Ī•S—å=˜ ČÍQ‡Oúūëį,|H˙zãt_™#ãĩäņģÖę˙ĘcÉ)Éú˙ÍG{fõ Õ­gW§ŋG9uŪĶÅĶļė.ĶĮ$Ī“1ĘČiÕá€'ßų]ŖFãGaZĩjq—„¤P "æp+6ÆÃŠĐž°Úũ9^äc÷ 8Éę¯0 §F¨•ÍVęíŠr=„šƒšŦ^ĘĮ|Í۔ÕRy\‚Ąąē*읈J`”0YĶ{•*ĄŅũTnc›2s–°l÷ÔSž§æÕfųã†oŦ|›Ģ{Ā4Ū›ûûq/ū`ã r:ĩûČû”ĪÉęģ1÷íII8—L0aBš˙ˇĻ+<}ĩr*´ûs¤īTĒĻ,˙§{ §ž°ŦK Ŧތ¯•SĢ%`Ë*˛ŽĨdžĢûCCuĄÖŸėū~ė ĀÆjšãKÆĒ›˙pĀô&”ûk ĀrĩdãThã´fsŸ°ôđz ›F/¯é5?ĮøYÁsīĨ•Ķ“%ËÅŖ¤GXžVV“ŨŸgÜęČjÆUĨåTe÷Ū] Įpp)Ûwŋ5’qĩiOĢĸæ+ŪĶm•äkåęĶN`÷Ūa÷l\\ĖĶiÚ2‡r%hé{–š•90_yÚ(š:uLŨ_™>` r„t˙omØ|*´ų9 eĩT™NųXŽėl<׸š˛ųĸXrÅhYU–gô皃šqehŲŽŦâĘ6å‚OÆĘŽlWŋöâ÷WJ–¯‘0(`p+_ož¯§ûĮ¸zlåkä5äB]Æ{‘í9€eģ1Ž[FÉ6Œ+KËäō˜Ė‹|Ÿ÷U–ŗdž¤Œ‹c™˙`ŦôĘkÉëJÉ ˛ņĮŠp Āô"ÔûŊ ĀÆŠĐîĪ‘[îĢFI5‡_cĩW¯Ėb\÷וĮ܃šœÚkŦ ›ˇé~ǝŒŲ¸’QFpŽíũĘJąšoXJ^ĶŧJow–90‚|Œé"ZÆ)Ī1ĻĶ™%ĀKP6OJúÍˇd2N#7ĪĢqĪâp Āô"ÔûÕ\ Kî}ĩæ’S™Ģ{ŽŦnĘĘĒ„:÷€æ¤eõVúu%¯ëž:.Ë)ÉŒ$hVˇMųzųŧdōĩî§\Ÿ—ąšŸ*-cņ4îīŘ/÷÷%+Ú2Vķ{’?$ČXÜ_KV„%ĐKÕfy•šqc åŗč"P¨÷˙R}ĀėĐđúē×.+qŦēûž‰ßÕoX!?2€0Õ:Ģõírģ EÕ\r wŗÔfĮųŠ„ŠŽ=:ŋé~%Šĸ<÷s__÷úh‡M,?9€0”Ņ:ũ|uWVĻ(ęęjĶļÍ_ĩÃĻ/?9€đĶPî˙[Ķ••)ŠēRÅĶ'Ņ „#ú)Š>` *„S˙¯Ü›VÆZS­xiIĐĮ)÷ļ•ąČxíÚϜĸ.ÛtŋĪ.+÷ôđR8õ˙J¸Õ†\c…Âjļ˧ŒÅÎ0.áWļiŪW3.Q‹ĮJéā…°ę˙5đÜÅsô•POuøôĄˆ žš,Û3ŋ?yÛ&Œ%Ō  6áÖ˙kāP8Í9ĐØS€éāĨpģ˙¯¯øŗķ•ęÁyŗUģėļú×5MlĒÆÜ1ęš>Zų#€lSN)Ž‹‹ĶŸ_S˙îŅĪ+TqÉdÕ2=MßnëĖ ũeyŊę°|NVŽ;wë¤ę7¨¯ęÄÖQYí2¯ú:}eQÛŽÔ‹›žĶĮ›œ’ŦV­_ŠoKÆ)㒒˙ËkČįå˙ëļŋĸ†ä Ōߟ§1įÎS“ĻN$ŧŌ D§pģ˙¯/XBeĪ>=ôį™ĢžX1_–>%Xî?ŧ÷Ē•T ĻJåkäķrĢ ŋ\åšŌģtUŠ*7R߆„Ėę°„SyLnje·TĮÎŲúcSgLš*ŒËöeœōy ŲnÍ=ĀÆEļäcyŽqÁ-Y –ĮÛw՘w|ũûSĮ ¯ôŅ(ėî˙k` ŠöÜKBĨņÜKæęĪ•U]ķ6ŪŽ(×Wyͧ~Į¸. ĩq×ÚīÃsuc°*Ī•Pj~\°<.AÔ=Ëcōąû ŦĖŊŦāĘ ˛9ËsÍ+ķ2OÁŠq;zĮ[¯z|Īü%°‡B4}Ā.ī˙k`YĄ••Q÷’•Xs” ë)āË*ŦlĮ8Zū/_īÍ$ŦšĢQTž§‡b95Ö=Ÿ<{LÁžhˇž]¯zm#Ëöjģ tŒ‡`÷ņIx–ų’hB+}Ā@TęvķMģŠ˙××S %TĘéÁž>'+Åæ0ãÃí“b\§TûsŦ]ûˇë¯-ĢÅōz›4ē&|Ø›Û y ĀŌķ,Ëj°|,Ą\>–žbBĢũ}Āqqqĩųį' Ânh—uÎ8å7°„K9UÚĶįŒĶŖũ ĀÕ]hĒē,ĢĐrsôĮ¤ˇWú%¸J˙°üßÎ,+Į1ĻĶ­eU\V€ÃéT÷pĒ7ļ˙F›īÁüDBWŧô˙ĘŠš‘€åJÎü<ön–Lūž_××,VN[ötq,š¸•ôģ`yÜĨîũÅÆ…Žė ĀRļåŊģV(šúŗƒ5möTé^ʏ DeĩËå)ÄER6Bî–ŨeW=.Ą_.<%ŅŖž`9ũY.(åŪŖ+Ģš˛gV¯¸&Wg6_yÚX­•m5OKĩ5Ëm“äswNē]˙WŽMXuĻä,ŠÄäÄ_ōSQŊúôØ9ũŠ€%\ĘiĐvĨ˙UVCeÅ×8YVeÍAŌÛ,}ŧZåÔeųŋlWúl%PËí‹äTc÷,ˇ2Šąę.Ī1n§dŧž`YŨ•Õcyķ­d ōŪ{ {ķžä]ōÚō‡÷{$›/.füqAįĒŌ—˙ âē0}Ā@( Įū__°q˙[9:Æu›#) †ŌėŪ×ëËąåęŋŦÍە@lŦđÖÔl”„Wų#„TŒiuؗlÜz)ÆÃ=~Ī™o USÉą=ÍCŒ‡+dc4ŽĸM0}Ā@¨ Ëū_cĩMB—¯c—•_Y–+#{ē”lĶ×ÕLŲŽŦËvŨO1–Īy§ôËķå´lãsîīIÆá)XĘJŗ<î>~ ΊåķîŲĶŠÚՕœîiä1÷mcŦé~Éôēpí˙Ĩŧ/ ϞjËŊéĸZ¸ö˙Rĩ—ŦpËƝü#Æt/`Š>` *…k˙/U{ŊĮŌ_ėŪįLŅ D›°í˙Ĩŧ+š1û—>` ęŅ˙KQôQū_Šĸˆ ô˙R}Ā@4ˆĨ˙—ĸč"^ûŽí‡uėœMPĄ(¯ÂŨ$ąÉ~ÚAÖ/§ĪĢÅ%“ *åP}zî„ŅܐŸ8@u¸ąũŲÕ¯ŊHPĄ(ĢK÷NÖˇ|~âÁŖ÷˙¯:BHĄ(kÖŖ÷W¯AŊgų‘ ũŋE0č˙Ĩ(ú€€¨čū߃ĮöŠ—¨š9Ē}ĮļĒUzšj™Ą†ĖUKW•Ē`ŸŠmŒo`ˆŽ/\*Øû9TŋĪč‚'`ũŋģžŽnîÛ]ĨĨĨĒŲ÷ßĨŪ)I:ēK=ŊW˙ā5ĩsķ25rĕ˜” æ,|H}vž2 ÁÄ<žf\;ží›—ĢųÁ_¸Tmķ¸Ãáyŧęûl†įīŗųƒļé‚$ũŋ0ū­d’JMIR›×.Vę/'jŦĒĘr5(§§ęŅĢĢ:|úãDÆwĪ}—ĮˇÅËņ ÔÆ×=@㠗ŌįąÄ÷y´k?_~ũÉ>Ŋ~ ŋĪč‚Ėéū_ %c†ĢĄƒû¨¯ĪĒ5”˜kEél•••ŽŪ9ŧ×ŅņŌÆ7lHoŸĮˇ\_ffēÚīāøÂ)üĘ~öce?gfY›Į`ŋ>}Ā@pē˙wDAŽēcė0uéĢc>…ŖĘ^^¨ZĻĨNJĘ÷ßȂ!ęŽ[‡ZŸœjûC㠗 ö<ęßg_ŋe÷#}Ā@ā9Ú˙ûÄŌšĒW÷ũ%F=˙“GT—.ÔÉŗĮl_éŌĮl_§Îö/\jąķØŲũŧČÆīŗÎ]ŗéĖÉūßwíSII ú…‡Ŧ„ŖĻÜ=ZũÛŊmßû˙ņļJļq|ÅÚø~hãøÂĨŪĶös˛ÍûšØ‡y|׿ũ(¯?%û‘>` ĀnÔw“SũŋEĮ¨ÅķĻÛJ¤žšPĄZg¤Šˇ+Ęmß]wŲ?ž Į.51Čķ8!ÄŋĪčBDįn~ķâĻįl˙å^ú(eUÎ׋ÕVreßaų-īŖ_žëČøä ךyƒĸ&üV84˛Ÿ‡z1~Ÿ ÍčøüõėŨũkí0,ä' 6øÖ‰ÛŋüäųÅjRQ­ĄDJz<å7V/Tôė ‹_JJrÔ\kĨƒûŲ›y|ÚŅ×Or|?>ōøƒßÅՋ{™ŸD€Ãēöč: Ģ]Ļ#ŋ؏3\m+{Ęö`"5sz‘Z°dŽĨņŨ:v˜cãģošõņ…KãÜ<ŪīÅ~ãāëΘîü~ÜĩģjÔ¸Ņoøi8lāМ˛‰ÅãųÅ^z8Ģ*Ë &{v>̆äZ_kĮˇ{ĮŗjØČܨĀNÎcųÎÚįŅéī3§÷ŖÜģØÕœĀO$ĀAŌ˙ģjũJG~ą¯Ģ.~qđ`"įF‹WŽvr|ŋÖÆįԕĩC­œŪĪĩÍc¨ŸųĐ<–ŸH€ƒœę˙•Ō6īH(‘:wæįĒEZjH¯šÅņ…K{#a?Ō 8ĖÉū_ŠØØ:ú…„œZ™k™˛ãûüäË㠗 ö~õī3ú€€ād˙¯Ņ›yöô^G‚ÉûûÖŠ>ũoļÜģęÔøžŊ^õļ8žp)'÷ķģÚ~Žmũ> Ė~¤p˜“ũŋRÃōĒ×7/s$˜<ĩø5ięDKãËËwn|‹Y_¸”“ķXęÅ<wđõŸ\¸ũH0ā 'ûĨ–:tVŠ>}ģĢ-ģË,īiĮwŗ 㠗Záā<ööb—‡ø÷}Ā@9Ũ˙+uôķ Õ¤I#uĄę€­Ąä??ÚĄšĨ$맍Z_‚ã;nĶøÂĨœÚĪ'ŧœG§^˙“īGú€‡8Ũ˙kÔŋ•LRŗĻŲLōķĒKæÚ2ž{ī“ņˇu|Ãl_¸Ô=Ėãpæņžû_?/Āû‘>`Ā!N÷˙uŧęˆJIIVīí]mK(Ų¸ĻTÉĘĩ]Ģr2žÔ”$ÛÆˇÎæņ…K]ŪĪöÍãzįŅîīŗ˛ íGú€8Ũ˙kŽ­å›TRR‚:sâMKĄäƒeĒq“Fj˙áŊļŽo›Mã{˙ŽŒOö͊—–¨ģÖ^Ũ{Ģ=öâĻįŽzL>–ĮOž=đl×~ū…ŸķhÛ÷™CûŅë>āø¸2~B6éŪģ{ŋ@ßŖöųõ+-­´žēa‰Ū)!Į‰ņŊ¸ái•šę˙ø6¯wn|˛Mmˇ]s;yŦezÚUÉĮōøûŸĘJ°ÕũŧÅâ~ļū}ö”Ŗßgĩ՞÷ßPõÔ˙o~J662wõmÆe…0--U͜>Ūë UU–Ģ EJûÛåΎoĪF}|ŗ|_ŅxgĮ'avöŖ3ô•]ķãō˜{Ē|,Ë)ÁÁ:ÚØĪžĪcĄ-ķxåõ‹BōûŦļŠ‹‹ģ¨ĻÍųIØ ĮÍ7}îĻŲ+:mæũĒŊwkc[ŲSęėéŊęŌWĮô rņ‹#z){yĄ]8D%&%¨įÍVŸž;°ņMwoR ãÛđŌB5JƗØņ…SO°7ûyƒCû9دoĨú čķGí0-â'`ƒ„Ļ ˙ Ö)˛æ[×,]UĒō rUzFšŠ­ŖŸēWW˙øÖ;GŠgV¯J/Ģ1žeÚøF\3ž8ũãąww|át‹$št°æąļīŗąw†Ū~œ˙Ôcôv¸ų–›ovīĨ(*tŠ>`Ā&Áę˙Ĩ(Š>`  ‚Ų˙KQ}Ā@Ā„B˙/EQôŽĸ˙—ĸčĸũŋE0č˙Ĩ(ú€€¨@˙/EŅ D<ú)*ŧj˙áŊĒ^ŊøßķĶ đũŋ~Õ Qƒŋj‡o~‚>čÕ¯į™ĨĢJ}ūüŗķ•jËî2U\2YõžĨĘČl­ZώŌ˙:r˜’m¯:ĩEæĮüo Kæ^Nk?yö1€ûüō1ņÃkމa#‡ÚzL 6đÚá[ĖO0ĀŠ-R˙!§TúōKū‚%sUrJ3ÕŊw5ëŅ…jÃŽŸĢŊŋTûŽüJmûĩâ•-jā°Ēibĸzäņ9އ@cL;gËrĩ×mdžä Rģöoŋæš“ĻNTŊûßėąÜŸûâĻįTĪ>=äļ3ú6 ĮTĩõJËXäĩ%Iø”¯ģĸ\˙œ|Ŋ|ėt“}m×ę%ã–åũm-ߤ˙?TVüe˙Čé÷˛īÂ=øÎ_ōc×1ŅWÍ|ô‰jމ|×1ņ°åcĸtåÂīęÆ×ŨÆO0ĀKCG 蚜’ėSīaģėvÚ/ōyęõũ‡ÕÉ˙ūĻÆ’ĐĢßÕĩGOõŅŠ ,%ØIđ|bÅ|5ûŅzđĢ[Gĸæį6nŌH´ŧÜËü< e˛M Ŧ˛ÍŠ3ĻčáZæëčįՎeãŽĩúx.‡”'ÔôĻ~˙9 ĄN÷[Ëë˸ģõėǝ8ĘdĖōÚ8e^B%\öW8÷ãļËn¯‡[_މ›zöP‡Oĸ”‚ą#^0š7aEÂ͓έ­õ—|÷z`Ū“*ŖM–Ú÷Ņ>Ûˆ\ Q‹Į_õø•īéaWV…ĮÛ§?÷ÁyŗkÜĻ` úäÜü+Y!Ŧím9…WÂīĻŨ|ūEߨEOŋĸR[¤Ų‚eeVB”qšąšōGįéŸ3>^ũڋúĮkÚĻ\yža÷S]%ŦIŽ-€Ęŋō\ųŋĨš°œ*-c’¯‘āúéš՞j+ÁZž'%˙7t ũsĪŅ_sÕú•úéÚ˛Z-!\ūP Ÿ—pîiތ’×–¯“Ĩd<ōZÆ{đTō|y=yŪWûecLŽdÜ2 ÕÆUž@/á°ē`_/ļdĖ›ŒKú—e 捠k>ũ{ĖŖôĮd^ū¨ ÁPúŽeėōc%ÛčK–•hcuYļŧ†{–šĮåķō< ŲÆJēfc%\žwÍûMŪģ|­<Ī—‹ēųSŗĨEöoĄpŠ7ũŋ>$ ʅ{ėúe˙ÄoŋV]{ÜŦ.ÜŅ I@’ fžjŗąĸ)AJ‚¤”„< \ō9cu3Æĩ˛*ĄSz‹eWž'Û3SĢX>–×p?u×čU6‚­H {îô2.ęež‚ĩq vM§,{€eÖ}ÕSæÉx-cŒōG÷m!ܸŊÖ-ûęķæ~ #ĀØʞN‰–īUy}™+Ų§ō<š—ų9˛?åkĢ;eڎē|L$:pLôōkõš>`Ā Ūô˙J¯fîˆÛ~Ņ7ĒüĐ'zˆ¨Ž×ÕjoĻ~ĨGÖü9YõÔŋ+áV•q;ž׊¯{ 1šŦ|Z žŠiŦūJ s/yž„đęNÁ5N¯v*{ Ą2&ãĩ$ÜÆ¸Vš=’.Ÿ3ūĀ"!ß=PÕüZhc\=Úîķ!.“ĪÉ)ãÆ3dūä5Œžâ@ôūŽ\ũ´’?ĘĄcĸŠĪĮ}Ā€ŧé˙-ŧĩ+ÜzSũõžŦ”'ãžŊîáˇļcs`‹qõ“z #J%|Y ĀÆ*fM%Ī|Ī\9ÛÜ,īUÆéD–íÔ€Ũ/đå^1Ž{ËŠÉ1ÕÜ{ؘ#›ī_\]ã’SÅeUÜü99ÅÚ͏íŦ‚[G;vLä ėķ1A0P oûåB?vžęiޝlŅļ]ÁDVo%0J@õõe#ˆũÂ5…\ u<­`c•TN–Y]™{…eŸIŋ­ŦxĘęŠŦ"›Ci đœ…éĪķtZļŒ+Æuĩœ2lūã‚7XB`uķážŌ+§;ËJŊŅßãaåŪÎJKOw똸Éę2åímÉčŧäíũ%PĢúŌ‘_öå”ĪÎ7uą%”Hā‰q­Vw+Y–Ķ…%@ēÎ8ĩ؏“qq'YŊtŽ„PķŊ…­ôËxZgfTŗ˛÷Ä÷÷ō• rÅx¸¸“{_n °ŦļĮTsOe™ëSŽ|§÷é~ēĩĒ=ŨĻJVĀ%øËū•,ķãŪëkjķ|Ø]Î'ôUm_ĮÔ¤i“?iī;›Ÿl€ŪŪ˙W„ŋčKŊsės•Úĸšå@"ÁHB YmŦéâE8%šWåqųZŲ†¨Œ`æ~ÚŽ.%¨Ų€Û‹2÷^Į˜.‚eÜČ=}ąæ@¨,á[æŌ}>eõWVĩeÆičÆéŌæmšŸg`™OûRūo\”L^Köš|\ Ė<>ãtkķ•ŋí.§ N¯Ī+ö…6Ž~˛xĶ˙+U'6VŋB­ŋėËi¤™­-cuTú~% y*#8áPB†\øJV|ee2Æuß[ķv 2Iā“€&aX™„M/Ūå€åĒČÆm–dE^^GB¯qUj#XXn-$ĢŨRŠ%ĐËķ˧ŗ*ĢĀ2V™{YY—ų4n1e^i7Ŧ&Ē;# ĻZņâ’ocëÆžÁO6ĀÍČąCoôĻ˙×čŪwäWŽü˛ŋa×ĪUī[úX$%”ÕTæJ xžŒp,Ę8Õøš`Ą…@šJŗqEf u5­2×VĒŨO=7îs+ÁQ^GBŽ<ĪũTn čÍĪ1N–÷hÜîIŪ‹|lÜ/Ø×’¯“¯÷4'2v÷[ÉUˇ%ÔJp3æĶĶ)Ėō>eūdåyōãĩÜÃļŧļlĮŧÜˇ)„pcNäõeūžo¯{€[e8xLėķëômŲ÷uëÖũŠŸn€›qãĮŽ”USo~ąÎ2H=ŗv›#ŋė?üøR5Ą¸( ˇŽĄ(ģĒ˙\įŽ‰…O^sZ7}Ā€†ô˙ĨqŋÛÚjáō…jÜÄbG~Ųī;`Į•BŠ åz|YŠcĮDŋü>&čųjÔ¸ąĨ͉)*uđč;*ŠYĒ3ĮD˙ ú€7㊠Ú˅“|ųÅzčˆ<õčâŸØúËūŨSg¨âé?$PQaYš#FØ~LLē÷žkúĢéŦ`úÍE’UāC•˙Įļ+ŨĘę¯ųÂTNUūīåú*°­ĮDëĮ}Ā€‰/ũŋæšöĀtuËāa–Ņ—ĶF;tęĸŧš1E…rŨ;û~í˜nË1‘ŨŠ“-Į}Ā€‰/ũŋæúė|ĨęŅģ—~ę˛ŋŋčËŊS‡ŽUˇ ¨°¯ËĮDo혘iņ˜sÍ­ąü-ú€úÍuŧęˆęҧ—sįŨú/îžüĸøĖīU˙ÁCUū¨|ũū­(*ęxÕĮzöû˜’ĢōGįŲvLĐ ؏ū_÷’+Ô9Leļm§6ėúšWŋč?ŗvĢJmŅBŋį¯Ŧšœ¨H*ũ˜‘įÛ1ąîō1!÷üĩû˜ ˆņŋ˙×S­~íEÕ2Ŋ•ęŪģ~5ÜōCŸč+ZōË}Åé˙Qoŧ{LÍzėqŊˇą]v;ĩã­W KTD×+ß}k8&8~LŒWđ{íp˙?ņÕüí˙­ŠrËî25æŽQ*Ģm–Ēß žŌ^Fŋš­ü‚/Ģ[_*Úú‚/ŖƒvL<ˇnĨôŋÅO<D-ĢũŋE…GÉ­”ęÄÖų†ŸzˆŪlC˙/EQáQIÍ˙ öŨøÉ€¨4xø€Jģú)Š íĸQ-Ģ]æßv|p@QQPô jÍ|djkšˇ ĸ(ú€€ˆ6ąxü˛š9ŠĸˆlCG ūĪ9 "P}Ā@d“ūß]ûˇ (Š>` ˛ũā?øVûGQ]U§Nßōā­ÎiĨ(ĘĮ:Ãá œŒíŲģû×˙õÅ)EQžTËô´˙§}˙tãâęÅŊüČã~G Ŗ|­ĸŪųmŊzõæp 7úÍŽũÛ t”ĪĩjũJ•Ō<åCŽ"á áúē×˙ãŗķ•:Ęį:|úŠ‹‹ģČa Đ˙KŅ ōŅ˙KŅ *4hÔā,ũŋ”Õ>āfŠÍŽp4eÍĨ“GYŠãUG”ô‘kßOąRBUQŋ}ūHˆŖŦVæ ­ŋŅžŸúrHIqņqeķŸzŒū_ĘrMžv×Ĩēuë.⍒ę7¨˙ß{ŪƒGYŽÕ¯Ŋ¨’R’ŽqTEô˙Rôˆ !Ķ˙{đØ>5ãá50ˇŋjßą­j•‘ĻZgf¨ašjéĒR=\2¯ĖĶ€ÜœīįI*7ozpŪlĩ˙đ^ú€Ā](ô˙î>øēęÕ§ģJKKUŗīŋKŊSū’:ut—:{z¯:ūÁkęõÍËTÁˆ*1)A=ļđAõŲųʨ ž—įЇ>Oŗ´yÚoš§“íP{v>Ģî)žU%iķ”3¨¯ÚZž‰>`0ŗ˙W‚ė”’É*%%Im^ģXŠŋœ¨ąĒ*ËÕāœžĒW¯ŽęđéCQ|¯ĖS˛Ú´Ļ´ÖyēôÕ1ĩE›ĪTm^Gđ•sú€„ĸ õ˙J¨9z¸:¸úúÜĄZCšV”ÎV7dĨ̃G~áwÄč<5ďyúæB…š~ĪĒUzZ@O‹Ļ@( Z˙o~AŽēũÖĄújĨ/ĄÎ¨˛—Ē–iŠęđ/ߍč<|dŽēmė0ŋįIjÍķķU ™ĢޚĶ ¤Ģ˙wáŌšĒg÷-…:ŠįōˆęÚĨƒĒ<{,"Ãī‚%sUæIęɅ3T˙A}Ö?M0€Œū_š‚ą\¤I.Üd5ÔIMš{´ēwڄˆŧŌsĸķ$!z`N/ũjÚôˆ6Aé˙ŊsâXĩxŪt[BŅįÚ&#Mí¯øiDāÛ&ŒUOĖ+ąmž¤äŠÚŲÛŌ ęåäöh˙ī•īéĢŋž^ĖŠļ’+įįŒ˜đ+ķ”č÷1mÖ=ŌüG€ ĒW/ū÷ŧ5ŽTá˜áj[ŲSŽģYĶĮë׊„,ˇ=ÚęĐ<íŲųŦĨí‡@ŧģÖDŽĄ“m‚ŠMƒF ūč`—ž‘ĻĒ*Ë vrkĨHĀ­œ§SGwŠ.]:ä}œ<{ĖčŽį,ч üC ƒ]\\]uņ‹#Ž; Œ뜟§8ĮæIŽ*Ąė@Ŋ—öÛũYû~Ė! (‚Ņ˙+ĨŊ´#ĄNęܙŸĢiŠ€ž§´Î}Ā‚*ũŋRąąuô V9ĩÜ&3#"°Ķ픕•°÷B0€` J˙¯Ņ,§ā:ėí[§úöŋ92z€Ķ§ūũ{ėŊĐ ˜‚Ō˙+•›7HŊžy™#ÁnYél5ięĈĀC´yÚžyš#ķ´B›§â{;OôŠ`õ˙÷ˇčĐũmûõíĻļė.‹˜û_čČ<õīøyĸ@PĢ˙WęčįĒq“FęBÕ[CŨɏv¨f)Éęŗķ•€y:īĀ<Ĩ¤$|žč Aë˙5jJÉd5cz‘­ÁnDūĩ`É܈ŋFkķ4}šŊķT8B›§ĨŸ'ú€CĐú:^uD_­}wīj[BŨĻ5Ĩę†v™ŗúë>OöŽąežļŦ]ŦÚqžčPÁė˙5×ÖōM*1)A9ņĻĨPWq L?U8X§tdžÔ¯,Î͇Ú<5 ō<Ņ  ‚Ų˙ë^ĢÖ¯T)V‚ˇ–=Ĩ¯JHŒÄđkž'++ÁÛ´yJ yĸ@ e7iÚäOĄļÂŲ"-Uī ööÂXU•åú•¤Ûdf¨ˇ+Ę#:üšįŠš6O÷ų8Ow‡Đ<}zî„ŅܐC€ĶJō ‡}ŠŊŽSgNŅOŅ•`++–gOīU—ž:Ļš‹_ŅÃ\ŲË ÕčÂ!úŠĶΛ­ĒhŋWÍ͌)ú)߯č+āÕÍ͘Qƒõyzh~hÍSį›:}­}æs(pTlŨØ7Vŧ¸äÛPžõÜ˙6¯ WĨg¤ŠØØ:JļŠ‹‹Ķ?žõÎQę™Õ+ô+ GSđ­nž†W3OãBxžfÍš_ú€Wr4p”<žz˙“QŠāŸĘŨ8Ąņ¯88)äúŠč+ú€BHö˙RŅWôpT¨÷˙RŅSôpũŋ}Āĸũŋ}Āĸũŋ}Ā"ũŋ}Āĸũŋ}Āĸũŋ}Āĸũŋ}Ā"_lŨØō•Ģ—Ķ˙K…\ũhîŦK×_ũ*ŽRļ¨[į›Ã§¸¨Ģ]ûˇĢ†Vq”°Cˇ¤f‰ lQĄXŸ¯4ú€8TXõŖQã ~ogh9xlŸšņp‰›ŖÚwlĢZĨ§ŠÖ™jØČ\ĩtUŠ:^u$čÁĘãĀÜū—Į˜Zcŧjŗ/ĪĄŒ17ozpŪlĩ˙đې™Ãī÷ŗ6>į›ĮØŊwˇ?jß§c9TX[7ö­įÖ­´Ĩ˙w÷Á×U¯>=TZZǚ}˙]ęō—ÔŠŖģÔŲĶ{Õņ^S;7/S#¨Ä¤õč‡ôÕŊ@‡6ãÍ}ģëc|`†į1ŽtqNÆ(ãëŠÍa m|3´9Üoßɏv¨=;ŸU÷ßĒ’´ņ ÔWŋUP0æ°gŸîúgĘúrĩcĖąaŒ/ø‘ôŋÄŅ Ā;ú%$N)™ŦRR’Ôæĩ‹•úˉĢǞ\ ĐSõęÕUĒ÷XÆxOÉ$•Ēq‹ˇcĖéŠzhŒ2žbm›Ĩ$̞5ĨĩŽīŌWĮô÷‘šš¤FkcŒ)Ú7ú2FmÎGY#}Āė`š˙WBŅČŅÃÕĐÁ}Ô×įÕŠĖĩĸtļē!+]pøt^cámŒCzû5Æ,mŒī88F_ūč<5hīsøÍ… ußÔÛUzFšŖ§EËGhc2Øŋ1NŋįũôhÆH0;Xî˙Í/ČUˇß:T_íķ%UöōBÕ2-U}ôËw o# †¨;lã•ī92>é;;f˜ßã“Zûü|ũ´n§Æ8\ã¸ąÖÆ¸FŖœ6íΊ:}Ā,ąÚ˙ģpé\Õŗû–B‘Ôķ?yDuíŌAUž=f{p[¤ą—McėŦņ¤Íc\°dŽęfÃø¤VicėÔŲ™1vˇiŒO.œĄúęëso5}Ā,ąŌ˙+W–‹ɅŦ†"Š)wV÷N›`kp{÷?ŪVÉ6qĘŊmŊО\lëˇ6OĒXãäŠöŅŽ9”=0§§~Ĩmú€ŠĨūß;'ŽU‹įMˇ-¸IŸh›Œ4ĩ¯ĸÜļđ6aâÛĮØZãÛ6ņļ cÕü—Ø6>cŒ6ņ‰yöŽQŽļŨą-}ĀÆīū_é3•Õ__/†T[ɃķōÚÜ*´1&;4ÆayÖĮ(s˜čĀø¤6¯)ÕīÅĘc”SŪeU—>`ŽŗŌ˙ģlUКTT`{(’Ķcåļ>v\Čiå ‹ŖÜîÉęåā;ĮÚ>ž+cLļeŒ˜CŠ?`ÎŗŌ˙+ˇÚVö”#ÁhÖôņęņĨs-ā1c†96ÆĶ‹ô CYēzöč<ĩyũGÆ'5}šõ1ĘmļnpfŒ{v>ĢFißGôpZ/+ũŋrĪŲĒĘrĮ‚‘ÜZÉjníđåÖEVÆ×2=M}~rcøí°>Fšo¯Ssxęč.ÕĨKû€“9„xkîmo=īo0Š‹ĢĢ.~qđ`$Ģ“ÁČSÅĮ;;Æ;g[_\\œcã“:s˛\u á1ĘUĨåb]žŽŠONī/ĩīß"a^‰=øĘĢ/ø}˙_mގsg~ŽZ¤ĨZĀNąšÅ1:9>=`†øe[ú1ž?õč?¯ŋūúõÅŧĘŋuę\÷ˇãUGüFąąuô -9ĩēÚ&3ÃrvzŒ­-ŽąŽ6žūņ? ˜Ÿ|üŋTģėļ!=‡YYé>iĪûo¨zõëũŽÃ€7úĻļHũŊ•`$=Ār ĢÁčĐžuĒo˙›-ā Įøž6ÆŪĮ(=Āŋ>õ–cxĪîWԀÜË=ĀNîįū9Ŋü=5ûĸö}ܜC@m,õ˙Jåæ R¯o^æH0ZV:[Mš:Ņrž?Đą1>ščËc”ûônڸ¹`ĩąÜ˙k”Ŧ"ËJčģ{WÛŠ6Ž)U7´Ë´eõ×8oļú˙íŨ|Tåīņ#Dˆ B„ƒB•ēTQQ”ˆAŖĸ‚Åēiá´´EËĩTŅĸÅ+VŦhAcŊ.ĩéB•*ŽYAEŠ[ėb7ŪŌ.w—Ūzˇ´ËíŌÛsį{xōää˟$3Édæķ~Ŋž—™3gÎyΙIä;Īķ;įįûŪĖxpsûøĶĮŲ ú¸fU×ô18‡ķįS­g]{ŠŋnÍ]‘ũSŋՍj_=ĢÚqĮszmõÆēīsõôIÁôķ/dčR RAAÁˏ<ų⇙HēuŽî;õŌÉūđCũ‚‚ž~l÷­ _qÍt˙Ū‡îņwíŨŅiĄ-^+ãôąúęŽíŖíßÅĶĸûWuųÅÁķ¯ŊĶĐåįđâ8įđĘNxŸŋōĩ˙Š:āĮøíā*TũoW†N-ŨMĶ*,,ÜΝ7פá'”ũ;Ą‰–kíØĸcÆ>ßåüа–^7g&˜–síü)ūOėķ]ï8€@AAÁÖĩO?ü!‰–kmé7žĸ:ā'ø- Ô˙Ō¨¨˙ĨQ /P˙KŖ@îŖū—F0€|@ũ/:`yú_uĀōõŋ4ę€ä>ęiÔČÔ˙Ō¨¨˙ĨQ /,¯™÷É}#Zž´‹ĻMÖđ<~õ€ęiÔS äęiÔS ä…nQ˙û×7øëęķwíŨ‘pg6/­ûÕöŌŊÍli:.ŗŽz?ŗíŧR äžnQ˙{Öygúąžú××^—paÇĻuŋÚ^玙-MĮĨsfīl|Ũ}üWŪ~éȲųˇũ›oû‚˙Āc÷ĨũũĖļķJ0ãēKũ¯ Āj &wŧÕΟ„[ûøĻ[æįẂí2k-ģgÕ]9€Šr[qĪ‚žģCũ¯ ĀŊ{÷‚SÔThpĮZž`|]đū,šŠztÅč˙Õ™ Ā‹ī¸9øīu5×ĻĒî}čŋō˛ŠÁķú¯GíCa¯ęĘiÁzÚž‚`TÖˇßķe˙Šë^sũ ˙ŠMßmĩŊm­FYĩΚĮ?ŋ¸ãš”Ž÷wˇúŸŋõ&˙üÉ‚×kęˇļî¯BĢĻ)/škq°ŽöŖs¤eú’@ÛĐ2õ5|Üz ļú¯=Įš­į´ũĢfUË.š69ؗûúGŸyØŋüęéÁëôŧļ¯ũ†EįFÛŅzúīæ×6feVP2āũØņŽåO{VÎũܧĶ°ÖØ3N ~~ä{ßN€ĩŽÂŸÖ]1*_ú¯+ģaMOËõzģ^É ŋ_˙ž-ļŠ`jˇĄmk]=¯Įچ{)V—„DkmëØ>ĮúĪžøtŌ Diß= zÁÕžVŨZ\;bĢuŽpܑãˇ_œqöéGę|õz-ŋsÅí‘5Ā Ŋöĩ:ŋÚ§BņÉc*‚e'ŽŲĸ^Xëkų #G ˇöœ¸įUûĶrÎƒļ­s ķ’¸ęŠKū=Ö߅üirL=~öĖķO~؝°~Öĸ —ēpSŧlâü/Îką­šŸĢ –ktÔŪGáRáĖZ­QWŠí2;"ęQ>…PmÃ^UY[ĄĶŨžFpĩŽ‚`ŧãÔļ*ŨĢ$+x+ŒęõvŲߘą§9ڟ ˛ ą6ŒžöNCpÎtŒņ.‚•ę臞xāHČvÃŽÖŅr;Rl÷Š~ģįÁî'đ}|ヂ‚‚üurKąî˙5e5Û°š ­@/+ Ģ…QíszŦ Ai[ái˚ęŦ jˇŠŠĒŠÆáū)”kšæŦĮZGá/<ÚĢđščVN6p*¤‡ŸĶ”c7ĐÛ žÚŦ€­åá)Ķ ĸîųioļĮæ~ų`›ÂģŨ‡ũ _AZį_#ÖŲ€ÚUΟ ˇt›úߨ5Ú Ā1õĖ(häTרr=¯õėĪQŗĶˆm-ĢÖĶ~ÃÍ3S€ĩŽBŸgŽZ­éÚ2üøŗkũd_8ØŠØáéŨ6ë9Āí>ÃWÅļŖÔá}…ŋ hoVxÕčvÔ9Đąj}…|‡ˇįÜr(k/.F0{ēMũoTŽš í<…./4Bĩ^ÔļŖBŖ ƒvô2Ēš[YŖąļūÖ3ĩ°šBœėJĖņnõdx*8ŲEÂÚ€=įjÜņšŽ¨lûõł­ÎÆĪuĀ@ŽéNõŋņpx*´đ4é™ĢGmOËm8ĶUœõsԔ^2ÛmĒ&׋3=9QĶHŦ^Ģ×iJĩqŧ[OŲiÃQ!YũķĖŧē2+Äk:u˛ãļĘŌŨi˜:` ˇtĢúßD؝ žbŗBZøBT6H*ˆęjÎz|÷Ę;#īuĢõėH§ ÕöĸNá~¨ļWŖ˛W‚VĀS°¯gësŖĻÛ eéų¨ eŠÖWĪŠfš+°ũō ę–Nē˜ čv¸{ul;•;[k€ŠrOˇĒ˙M€ŨŠĐ^čĘÂ60*°ŲŅ]ũ×8ę´L͍5˛i/ZĨ°Ģ ŪĻ.rĨeēB´ÅU`˛!܆QíCĖrky5-XÛŌ~}ų :bŊVÁÜ.Sŋėm™´ŋL`;šŽ{̝nÖqÛ`lkĄømÖņØ_{!0]K}ļĶ2Ûŋl ĀÔšĨ[Õ˙& Ānx ‡*]1YaŌŽäęŋzž5’šĻŨ†FˆuA'‡ī-lGqíĸĩ=5GģžÂĸŊ‘ļk×sÃ`ĸH¨5RĒpޟõ_÷ĩéšϭ>Úcŗsk˜ítfMÕvŋt°ëhØq×ún˙í=€ōŗ9S ¤EÁŦSį^:õÜcOûČûÃË*6¨ičã˙<ō„aŋ?Ĩâġ†•Z[¯„S2ĻģÕ˙Ú[…§(‡§BkÄ7|ËF5 WQ˙7ũX#ÁuÕzú¯‚œöĩM…EMEN´Mvęĩö ÉęTqŧĻĢFë^ÅzíĘīŦhU7Ŧ{k›vDØ ķQį*|,úYËÂÛT×ëí(ĩFƒxÕÜ}ég-S˙ÔĪxîRŋĩ=­§m먏Qį•:` 7‚oå”s8 ˙ÎëßuÛ|ŋášGüÆˇëũŊīl ÚŽWŸōž˙ËūĖS˙Öģw¯JJú¯‰ŊވSŌ­ÛÕ˙ŌhÔŨç|zųˆ!œ~ÉųAČõ˙ëͤmã ū'g]úÁqÅũ~ÛDg¤SˇĢ˙Ĩҍ˛ßø3Į\UÜŋoĶãߑRđ ˇûŋžČī[Ôį@ކā:Ī”’D´CąļË;\vQ}­1ũš”Ąí/5Û/oĮku~æĨš?åĻ?KģÉg(—åÃ1čĢæqî¯ 84Zâv،Ē˙û}Y˟ š“Nr†ÂīļÖļ+üē!xā€â_{š7ÚūÃ~Ĩ Zn[kĖķËĀ ­Ė@@"sŒrY=~ųÃÚđ!‡FKÜ|üūŋôėŲķūjI– ,ū¯'×.ëPøĩMĶĄ‡— ~*G˙a/ô•š|( Â6āL$0Į ‡•ĒŽ‘pCŖ%oēX—ęåŊė˜’d­“?Zžúú™—Ļ%üĒÜŋÕgŊuÎz“˞%ÎįĪļ=^Ë۝•:Ÿw{ŒÚî60€t[}Ķ—æī%ØĐhŠĩ+¯­ŪįQ Ä5dpÉgĒĢ.ø[:ïZĶīwøũûõŅ(pŽ\+Qå¸-Îō1&@í4ëHą ĩ~(äė4!b† —:ÄķšG˜g†öŪá\lÂâ~¯yt°Đ„ߨŽāJ'L9Û_m–/ŽØ– ĪļžÚŽ4Ž3ũŨgúīîg“ŗlŠ֒āMN œm ígŠķ…EĨYVâ„ČĒ$ûąīeUÄ{îģ=žÍÎ{WjBd“ķåmšķÚÅæ;ú}įs3ĘŧîT|[w] °ö˔1ĄÜd‚yĄi+#ždi0ëÕDŧß`éĶë7ŋļ1¸ĪlĸīūžŲÔõ%|ĪŪŽ4Ũ›7Ū=viÔ]ŠbtųëíŊęs˛V{CõŊÃŖhš€ĩ-^ËŅ_0G…ļUhBËļPđˆē€Ö6@d•ia‹C7€kÍãšĐëĸ4ęŧ¨ƒxĻ —áŠģ6¸Õ%ĀE&Ŧm‹Øī ŗî<'Ār‚Ē5/…<Æk=âé…ÎĪÂĐņ…Gé'¤°ŸņaŅ~Ąą/tėÛLøOŠ —ëCįqKœ~¯ˆķ…‚}–…Ūƒđëg‡>‡ë#>¯îūíš\qŒ{ ĀŌŠ[×˙^5Ģ:Ų?ü›n™ßåũTԗt†ņŗÎ;3ØĻ}Ŧ{8ß|ÛüģJ0ĐĨ×˙éžūlÛ×ĶīͧpsŽ`{h… ;"¨ô‹x–ī7Á"Üėč[`'´Ą?%fŸĩ&˜$ Āvtē$É6;2Ú KšN\mR˛<ÁY'|ŽĒCė€9oae)Ķ^ËŅîđvēš{|âúDûYhÖŠLpmXl2_@D}>öšĀėî7Ēkâ|ą‘čũŅąŽ5Ąwsčõ“B_ÄûlØ/ĸFÂ3q•oyŦ[×˙ÚŧøŽ›ũ{VŨŲ4Rš‹XaWĮok4XûĐ1RŠēŌŅG|xčw¯g$+XĮđģ9€Ëã„ÕH†¯ūė§ĐĘŊÔ¯Ē\bÂŦ[zĐkŽ7€ˇ¤J:€0w{­ëF“āšÎQƒs>ëãô=Y0]ę%ž2ļģŸxīGyöS‘ā9w[ÉZĸũĻ€‹L0ukÆm]rTŽIōŲHt.—€¤SˇŽ˙ĩ8Ļ9wv70uĀ@ÉHøUÛˇįyŋ°°÷īr<˙ŽÎNw™×Äkn­īØû/0SŖ†YÕôā1fųÂ$ØÖŸf(ÛlMégžp,öR›mGy—$8GvjõĄ8ũ+L!˜.ōâĖÚ×oJC^âÅÍ_î{‰ŗĪDŸtā-Îû0Ķ|Î "^Ÿj)`ÕŨī˙ÛÖŦiÂ÷>tŅ´ÉÁâĒ+§ų=ņ@Ģz]VũWŖŦZoîįjÖījJŦÖŊpęÁú×Õ\ë˙hk}ÂŦžŦüÎ ˙ō̧¯Qģžö睺¯U¸}ä{ßöĪ8>8fmGA×NīÖķö\訴ü•ˇ_ ūûč3ˇęīŽŊ;â>GŖ訞={üMŦĘÔp,˙[ā"¯y4ÍŊ@Õ6/ēfՆ;Õtf‚āąÅ„h[[ē:bUIđŌĄŦŅ î:†¨)Æ I°FzDėq)āQ^tŨŠg‚ī2į8ļ›ũ„KZRŠÍ­ôZ^PĖ5Åk9Ÿ#¸Ę‹?8<ŋĪ´‚8A˛& x”ä|q;đ¤įr@ētûû˙ļ%+ôqöéÁúcƞŧö„‘#‚Į Ąá‘TIķ×6|h \éųØšôΟqôČā9võCËÃ}ž{åÁē`ꀁLP đŪw6f$7<÷H,÷ʕ[!%ģ ’ Qī;×”5Ąŋ?•ĄPRl‚ŨžPXVčm2û&eŒ×|äxØ^zK¨5Ą ŗŨöQĄãjJ€MÜ)ā…NŠ ĀEĄ€ŨäĩQ´ŗW8ŽēØU,ͧŸĄ//všũMC.t‚miÄįÏĒáíŲã\•†\áE_@ĢÜkž(W[°=—ī‡ÎåįŗÖíī˙kpÍŧO!3ÜÜĢ$kĪÔ ģŖ°­Õō;WÜŪ"÷îŨûČë_Üņ\Ü>hÔWáÔŨ×kī4ø%ƒJ‚0€5 ­ĮŸŋõĻÛŌ(¯–k$Ø ĀjavûžVÔhmGËÂuĐzméĐÁÁņfŠŌmdų°Ÿ>]÷ĩŒā;ŋ<ī^ôU‹s1ģ븁ĪN?Ūm–×{Í÷°uˇUí,×zëŊæ{Ģ–zÍS íhéRŗŪ!'ÖÆ Āb§­6†úąÍkšmÖ9`öąÅlƒ—ÚhõO#ļ+Ė~všäN[^æ|Q`¯Č<Ęw“ Íęß.¯õČpĶ—mfŊFgŨdˇAŌ˙šcZo^oëbÆ m Ā64įqsŽw…ÂaĄ×|ßÜíĻ?öÂTî—! Āö {ģĨĨæ ™Ξˇ1ģįŌãsŒģ ĀŌĨÛß˙7ŲU íaŊcû돎Õj)Õsvƒ¤ĻG§2 ¨u+/›Úę9MoVØÖöÃXtÉ]‹ƒQéđm­į^ÜJĻ“]:*?ûâĶÁ2MáļË45:ŧŒF0NeC/œ}í´ŋe"W|¤\ábJŽœĒZP]IšÄŦSįĩũ›mBPƒ •ËâlgŒymƒiáõJL¸´Īۑá"ķķ<'€éqøBL“LđŗũXėĩžļ\eBhƒ 5ãí•8a=|.jL j0¯¯õšë“×8ûQ_—›ũ¯ Û2ŗÜncvÄ9*0Įi÷ĩŌ9īÕ)ŧåį0|īę8īu[öSaŽÛK•ŗŨđņ,pÎųfķžĨ°_ûžLHŌ˙"sní1¯3Ÿ…ĶĮENŸŖļį™sū¨ž¸Xíô{fœc€ļëîõŋn~jĶwƒpn ¨vš˛Y&mLåöIv]wTš-ÁŌHąF}oŋįËAßÂŖ°úמŦĻĐ¯Ņ^ûXŖÎZO÷P&ÄR dHIŸcų`ã i ŋģ^}ĘīÕëč˙ô(=mTvôŅG˙ąģTk€…Z[īÛۜėĘËáŦ‘_{f¤ÚŽ@kT6ŨX#ÍZn§hkZöØ3N#ĀĻ̏ §Ļkō'hiä CŸŧqîĩi ĀĪ=ũ˙zŅH¨&‚ū%_°ö{Íõ3"ŸW TmkļÁÚÖį†GwuEį¨)Đę‡×ΟĶĸžXÁ8Ũøwˇ5ĘÚ§mëioÆÕ4čIüIZ).î_ôß/m|(-áˇnõöū•Įč/h‡ēXhûUž`5MŽēđ“‚Ēĸ ™m Ā ŦēX–nO~Î^tK7€uÕč¨ē^ģīt`;­+Bk„YũĩSÃio3o¸ú7^ō‹—y鄲˛iÅũû6íyķû ŋÛ^XĢŠĪōZן$×ŖGß<˙Ú?~˜OXuēžšå‘Ŋ§¯Â¯Ŋ‘jqÛ€ŨŅ\ˇXÛЈ˛ ŽáŦ}†ow¤š\MOÖzē˛t{°j‰ĩ7čë^ĮzŽ_˙ž)]Üˎnëüē÷IVĩLˇQrG˜ĩLŖŲų€×Ŧ_­:āWøĢDsō‰Ÿ-XÜÔŪ‘ā'×.ķûĢRƒIœMĐåŊzõú}.„ļ`5…4ĪÔÜjVŖĄjēU8HĻ€5 Ŧ`ŊFŖŦeÖĪēø”vTÖũwĩ_…`MŋÖŊzõŗF‚uâļ`ęj˙žŠ+Öôk÷ęŌöšTīũĢ‘äđyˆĄļKt¯ä\nz˙Íũ€Šâ¨}âôAĮ÷gÕ§zaŦÆˇëũO\sɇũúöų­wøJÆí’õŋv”RA-|;ĄDMÁPŖĩ v žĒO‰Ö6Ã÷ÎMÖvmp…emWˇ@˛ŖĖöļGá~*8Ú~čBU6Ûc˛¯×ĪQĄUËÂĶu,ĒīUŗáÛļķ'OhĶŊ5u;|ėšQŨ¨e{ÜŠ˙ ´Ŋ~æĨÁČîŪw6úMŋßŪCŋ{=ŊkŧÍŋbú$ŋ¨Īą‡†-ų:_.€ŽĘ‰ú_ZęMĶ”5âĖŊО@ÉœĮßsÆĮOn,<đŋ{öėņˇØ2Õøūuđ :mĖčíŖN>ßkyĪR€öɕú_Zj#ä™Õtiāđh7:` —åLũ/-yŗWĸVsëœiÔų gęiɛjvU+ėŪk˜F0/¨˙Ĩҍrõŋ4uĀ@> ū—FŖČ Ô˙ŌhÔyú_:` ÷uÔQŋũ§7Lũ/–ĄöŨ<Ē:āüĩēVÅ1Įū–BŖeŽũ|ߛļ¸ˆ?9@י7îœ3~NHĄŅ2ÛN9boė÷­’?9@×YŋäîÅīPh´Ėļ>ķ āåüÉēõŋ4uĀ@> ū—FŖČ Ô˙ŌhÔyú_:` ÷uUũī‹;žķįqžūä ūG*FųeÇíŠøŸŋõ&ķkģ4¤´čßɇûwÂČūEĶ&ûw¯ŧĶßŲø:aŽœĮlüœQ tN¯˙}öŧũqgŸî:Ø_đŲOøĪũāA÷Īø{ßŲčīzõ)ŋ~ũ7ũ97\éXėŸ;qŧŋŽū1ŋķû÷qhŦ7Æúˇš~Ցūí|å ˙Šēåū%•ƒūŨ|Ûüųˇ ē ÎãsŸwŪįæķxž9ŸOûy ΞOô9ģ s?gÔ]ŖĶępæĖû¤?hP‰˙čę;}˙ŋŪLؚ~ŋï{øŽ`ũĒ+§e|¤Đí_] ũk|ģŪŋ`ÂūãNķ_{§ĐÛâ<^;ƒ÷¯ŗĪc‹÷1…ũësĻĪcg}ΨēN§Ô˙*”\rŲT˙ÂIgûö5$ %n;¸Ģ˙éŋŋ:˜ļšŠéĒęß´Ë.ö'ˇŖ÷Üy“â‰Ãũwņ”íl ŋ>¯˙c—ėß~ΆeđsF0Ѕ:Ģū÷âK'ûWU_Œļĩ%”¸íÁûŋė—œ‘ŅÖĘX˙ft kŧÍëÛÖˇ_Ęë<5Mįq[;ĪãÔās6ĨcŸŗ•™ûœQ tNŠ˙]r×b˙ôŸŌĄPbÛ_™Ô§ŗ^ôļģûg¤Ą÷}‘˙ą}ÔßĩwG^†ß¯Ü•žķxZ;Îcēö¯vûW¤ũsF0Đĩœ;ņœŸfú ŧęh(ąõš&Œ Žœũ›3û2˙SŸž./¯ôœîķXû™Y]ú9;/ŸŗxmÔGNjŒũVņ§Čŧú¯Ū{ûŋfōø3fUûKo—–PbÛOūy]p;tôīšëĒũ;n›ļžŠŽô„Cũm­ĪĢ|uÆÎãRÛđ9››ÖĪŲŽWžn›”Éķöé?õŋbŋ‡+ųS´P0õōËįžwÁ¤í=å”÷K‡=4xȐĻAĨĨ6|ÄīO=ú­ĄC‡Ū[¯„SRvÔQGũáÕŨ M™úĮũ+oŋŒĘĩõbDŠ4M5~fķ÷˛˛?|‡ņԉy~3y§VNėŌĪؘ4|Îĩ§Ÿ{ō/=zôø€ÃÁwüyį?Đ˙¸ã>øø™gú7Ūü%íĶ˙āoúįmūķ¯nÚ3Ī˙Ø˙Ęזû•Ķ/û[¯^Ŋ>0pāŖe císėŋe2iúč'f^šöPĸöĨ/Ėņßqs‡úˇüū;üë3Đ?MŸč_\Ŧ+Ē—åÁiԈ÷Ķũ fēū÷ŌË/öŸ\ģ,#Áä‡ëŋlŋ#ũ›žÁū-˜{­ŋäîÅy€Ģ2xoŒG]ÜĒĢö¯ĪŲ´ËĻvFp5’¯ūîô͝ęÛ¯_͞oŪ—Rđ ˇ[nŋÃ/ęÛ÷9‚ĮÅږXķCm—}/ņr/;ī1Ž>×eųš^júYnטĮ“ÚqŦ üv@vÉxũīđCũÆˇë3LvŋņŒęĮ>ÚĄūČ`˙œtKž|ĀÃ3|+“œĮŒÎÆ|”:` Cú˙ÂoŨ†´+üē!ø¸~ãåŪtčŠX;hÚJČÔVÄځXkōZ^H¯4Ö™ Gîxžkša0ž Ũ\ĻëÕz÷îíúŨë &ēÚ¯‚OGúWØģWÆú§@–éā”-­wĻĪc’/:˛ũsF0WaņqĮũņk÷?ĐĄđkÛe3ŽūpØđáĪäØ9Ēķâ@Ž1awŗŦÜŦONOîČą€ ‹dŧūWM˙ČD(QÛˇįyčĐÁYŨŋ!ė_wi]}ŗũsF0mHYŲˇ.ŊâĘŋĨ#üĒŊū‹wũã :d‚aŽh0a*žõŪáJũũĐčīL'hN ũ]Ņčø ōÆ9OéÔ¨ŗĻ“/ŽĩŲúĸ"°˜mĖô⏸—™õ–x‡k–Û€Įš>.5}. =_jޝĐé“öĨŅđ‚÷Q`Ö×ëæ™sĀĨįQ¯›bÎĪâ8ûŒ Āef[cøí€Î—ņú_ĩ‚‚žÁ…Œ2528räđŦî_ųČy€ģú<æÂûH0ōPQŸ>EÖžĶ€ÕTG›+ĘMđ=čĩž ´ ¨‰° •QrÚeÂq‘€›ŧÖ%ûĖsáQčqfÛËĖã5æq8čU§€KĖžˇE<7%Ök"Âģ ›ÉöSfÖYį|$ ĀëÍų* ŊNëUF`~÷DŧĐI:Ĩū×۟uÖĩ™š?ëšãĮú?ģļÃũËÔũcĮ˙x‡û×mîœÁû=Ÿ“ÂûŦũ_ׅû§hģ~ũûH÷ôgÛî_ķ¨ß§¨č…OkÃûŦ÷ą+÷O0Đf ŋj/l˙‰nöģ8GcMā[`ĘPā Ā đB¯eMkTļ͜W›íGĩ2g_{âėëũ$ÁÔ^Áē6ÁëwĨ1ΌxnA ØoÂđv¯šFģĐ郗zÍW–^į=tž‚ŖŽ:ę˙ŊągkSgũŖ~gãëÁ(Ū–Ĩ%”Ô=|‡Ōč‘i•S˙4Ę÷Ršú÷čę;ĶÚŋîŌŌ}Ûú>§ũsÖīã÷_X˙—Øīįģü™B>čŅŖĮ_uÁĒLîˁĶTę5×úÆ=œ WQØ^đ)ĒuC(đE`MņW›ĢŽÜkŽŨ`Ö- ­W–B0ĩS­×$xn}đ¯ųB[au^ō)ĐQ_HØÚįņNœs¤/y\ :Ũøžũúž×Ųáh]ũcū€Åūž7ŋߥP˛í…ĩÁT×ͯmL{˙ĻĄ[cũ뗁ūŠ=đØ}ū=ĢîjąėŅg–íÚģãȲžŧ!XöâŽįüŽxŸĻé}nĪyLįį,Sīc˛Öŗ įA Ĩ ¨øųWˇg$¯}úüÂcŽŲž#§ĘŪh“×úĘĪ eûŊ–e*wöTõĄ =ńU7đF`ąWWžZžÂkYˇ[b œ×'ģ ôNs<B¯ˇˇ'š’†l÷s0HĮz͡TŠ€7Į ˛›Ėš,Āž ÆL…€.°ø‚‹&ū¤+FW~gE0BØŪē'×. ^¯“‰ūŨëßāAÛ=‚š.Öŋã3ØŋaÇõDî˛ŗÎ;3Xöō[/YvĶ-ķƒeá°ÜYíūā}îØyėČûl?g]ĩ˙ŽļŠ1Õ(ÁLūT!× )+{ķë>”‘ŧ`ŅÍ˙Ī;|K›\Pd‚”[SÚā5_\IaĖŊTĄ vM&ĖÚ šŌŦŋۄÃz¯ų~žŖRĀ v{Ė6ļ˜mlķšīģë†:{Õé]fŊ]f?ÉĻ@{^ķU“›LëĖ1û&l{i Āļ÷˙]ī5_Ũ9ŲU Į™ķ{ĀŧĻÎkžŸđōPÂįŅŪf‰ŠĐЉ6ãÛwŋÛUĶd*† ÔjĻzÁĸÆˇëƒ+5—á˙hk}Æû74Ö?ÕˇĨ×uB˙jįĪņ¯šUŨbŲ͡}!XĻ‹@Ųe=ņ@°ėŠMßõģú}nëyüDšÎcķūŗķs–¨-Xô?Í?œÖ¯_ŋų—V_ņa&đČQŖ~ëĩŧOk.˜aÂVƒ Š ĄËŧčû+Ŧi*ōf¯åHj•YŪ`^¯úÕđ•įW˜åņÂøbŗ]mCŖžáÛ0šu“YOĶĘĖļkS8Ösl[Ėë×GŨ æ|DM)ŽKq?å&”6˜cšílˇ$Á~Ƙcj0Mį´:ĸ‹#ÎßķÚBū @æõŋ;_kęęZŅÚs‚ŠĖ ŲŨûÎFŋé÷;‚ rčw¯ad탡ųÕĶ'Sj?wË|˙įûŪė´ū}&Åū]ëŸĻÜŪԉũëN5ÁŸîÂķė~柺`˙_úl—ŋõ/ũuĀČ%Į{ė/íüiZÃī3Ī˙ØīÕĢ×ī=Ļ™÷ē¤ū7Ņ­kt˙ÖĘK'ûÃG õ zĶģz÷î<žęš*˙Ū‡îiQßÚéũģ˙ޏũģâšé]Úŋît‹¤Dīķ•~Ÿ“ī?ûŪGꀑ/†–xræ 5Mg>ũŦŗ˙¯˙>˛ tYũ/FŖˆPܧ¨čO|o}ZÂīWīŊīÃÂcŽųĩĮč/đē¸ū—FŖQ „ 0`jß~ũšę˙ŠĄCáˇnÃ4õųŋ=n3ŧ,Š˙ĨŅhÔaå'4¯xĀ€ĻöŽíūü>}úęĘĪ“8›@˛Ēū—FŖQ ¸ĘĘĘĻ7pāĄY5süT/Œĩ韎ųē’tQßž˙Į;|e^€õŋ4uĀ@ļ+>åcWß§¨č¯UW^Œė>˙ęv˙­÷öwĮ/ß BīŌ¯¯đ/œZé÷éSôįĄÃ†ŨëqKBũ/F0Đ]”ŒŸ8ņžS>ö௒ã˙ī={ū-ļĖ?účŖ˙:°äø?}ääSŪ8iôč›ŧÃ÷Uhú_:` /P˙KŖQ ä…%“+/|ƒ@AŖu¯vęߝō‹Øīīlū„ŠkxāŅoîiĪ?Ā˙å?Ūöëž_įßđ™Z˙ĖsÎõG”ô‡´ŠSOõ§NŸæßšâv˙wˇvJ øųž7ũĮŸ]ëßŗę.˙Įîķ_yûĨČõ´üåˇ^hÕâ­˙ĖæīŲæÎÆ×Sę‹úņÚ; ÁĪzúf~ô™‡3~.~´ĩŪ˙ü­7ų7Ũ2ß_|ĮÍÁzâ˙‡/o~^W˙XV„¸w<œ[ũˇ;„N}æõŪŪ0÷SąĪü9æ3?"øĖ5Úŋāâ)ūgŋ0?XGëf˛/_\ōš_Æ~ëø¤ĻPõŋ?ũՎĻ[—}Å/9~˙ņ3ĪņÜrģŋæ™įũ[æ?÷ú/‚öôæ×ü%ËŋåW^~ĩlŸ>~ÍÜü]{wd, <ōŊoû%ƒJüØ1i= zúķŋ8¯ÕēĮö9ļÅzļ >´Åz›_ÛčŸ<ĻĸÅ:z­Blĸž(dŽ=ã´ øĢ_NŊāČąkgwfÆÃ¯{ŒŖ+F˙ŊjVu6õŗBp6JÛũ7ۃī—īúŸąĪØņq?ķļėđī}øI˙†y ũžúą`Ũ%w-ÎX~ūÕüsė÷÷×üR3Š˙€ūmũŨüÚ&TE…?qĘ%AČŨõo“ļ—ŪzĪŋüšŲ~Ř“ãŽ˛vtąwīŪū‰ŖGŖĩv”÷ĸi“ƒpĨQh7ÔjŲų“'!Đm +v=V…U…j;bĢ×j ։F,5ōzû=_~žæúAļĪuFÖūŨPŠŅm‹ëk¯ šö¯Qapǟųū芏ø/ĒLų3¯öÄ_öĮŸ?1ø%S#ÜG÷:ú?c᝜?e@rK/ž~ŅöT˙ąũDũã~ņ€ūWī{8åāļ[îøē?xHiÚCđÜĪÕ!ʆ_wJtŋū}ƒPģLŖˇZ× ĨQM1j=MmÕr…Ėx¯U˜Ö:šfŦQWũŦÚYØîßîS­vūœ#Ąŧō˛ŠIŸ|¸é=…°B­ŅUsŦÂŗŧuîz ¸ę§Ļd+0j=m[ûĐz ņímļŖ˛ žA×~´OíGĩÅît^õAëé|Ēég-Ķsî…Į4\ÛP€×šÕzļnÛ Ā ĩžŠÉvĶũ"AMũŗÛĶz:V{~ŌqAĩaÇų_{ā;iųĖŋųĢÁ…âøŠ:_Ęõŋ7Ūr“_uÕŦ´ĩ×öü6ÔQŖ‰élļØ bŅSh˛ÁX#ÄzŦf/nĨĨ×ÕĖûä‘PĨ°æ™ kšĩ ĀvŠtøÂZĒöĖ4j=ļ#Ĩá}k¤XË5ęšh t{°úęNÛĩ}Đ(Ž ī:/á`Ē×h™žŗŖļö8ŨĢOë5z?ÜŦ/tŽnŨYĪžøtđžé=ĩŊˆŠčú2CË;úŲŌ9œ>cFÚ>ķjßûŅ+A-1uĀ@įKŠūW!¤xĀ€ājˇé Ē#žtņ… ŋ ¯žšōą;Újk‚ŨĀĻĐĨ Ļ€ĨđĻāhÃŽB˛ ’ų´ŖŗíŊ€5ʨ}*ėEŨĒÉíŋĻ+€†§Žkd8|œé Āá eéüšûŠ7}ŲŨ†ĻP':Îđģ4ę­Į ¸áumˆV?lV v§Fëü¤zĢĒDŸy}6n};­Ÿyĩž:ĻU­:uĀ@æĨT˙ģâĄ¯ûVNO{ДPŨ&&ŨÄRØŌ”Y…#MĮMĩæŌ^ôJ#Ŧ6Û@ŽwĩÛîhļ!NĩÆ wáæ™iĖá+^¯üΊ`:ļÖŅUŠ3€Ũ‘sÛÜ}iÔYŖę}íyԈŦũŲŽG]TĖ`…dģp ų`GįõE…~Ö9IĮŦ‚{ēĮŸrIeÚ?ķjŸš˙ÅØgíKԝ(åúßK¯¸ŦŨW}NÖfÍųt‡ĻGÕü*0zĄúŲT¯ö뙩Äv¤SĶŖŖÖUŨĒÂXG°Ũ§]ÕlĐÖ¨¤j[=so_P*,ÚÔLā¨Qnw_á+\ģÍ~‰ uėqFŨ{Ø>g°ŽŲÚxÍÖaëË]ôJŖôz=7:OšČšÎų]÷=˜‘ĪüĘĩOûS§OŖčD)×˙ĒV7Ũ͟Ũ0pŅ´)i ŋöŊî}ÃŖ§ ZQ÷dÕŊ~=§ĻTĶŊ}R8‡GfÛ€5Zé™Zãd¯ŗ5Áķŋ8¯Å¨´‚^W`;]9ęÂ`vt[Ŗžv:ˇ[§>ī6ë"ezÜÖ+9ëd'Õ˙ū!•ú_ĩžÁĢ25<"ā:úŨ›6Ų?NÜ\{Ÿ\…,{{#Ėđ¯4ĘhŸ×kė}pÃˇ,ęHVhŗŖĪÚží›~V}Ģ]O#ö8,m4‚ĒĮ'8įąŗ° ŦöâT:WöļFîļŲ)Ķ ŊöžĀǎõBW’VČ׈ągjí{ā†]{ŋ`[ûĢķaĪŊs{ß#m3ŖŸųō‘iĀ/ėøuĀč2eeC>‘Žûowį6¨tĐb§b,Ÿ˛O倒?K=° Ļmf" Ŧyæy˙ŦsĪėđ?<Ԓĩđ•ĸ55WAQ#°v¤1ĒŊņîÖ œi]*†Cr[›‚lTÍŦÂĸ¯öŖé¨Ģckßz.ŧŽBŊŽŅŽŦ*øēÛSO­×GŨNHËÃW}VØÔ9ÔšLv>5rĢ+nĢégíCÛ ŸWûiJtĸmęXí9Q Ž:ˇíų’"“Ÿų3Ī97#˙/,,ÜûũŽāO:ÛŠ§˛Qŋ§ų€¯ŧîŠ˙ŠŠ…|Č>˧_Yĩ-Õ˙Š_pņ˙žo?ž‘0°đÖĨ‘õ¸4ZW6MĨ˙ÆCfä3˙š[ŋęâSs2Ōī3ÎúøŽØī÷<ūÄĄŗ Vúģtßßēģ5•~ô)ęķŸ˛Īö‡Ÿ\õ‹T˙§~×ĘģüĒĢfe$ œ~ÖY G i´ŽhQž>ãnj|æ?~æ9~Ũ÷ë2Ōī/õ–=ąßīõü‰C'+>æ˜Âŋv¤ė šfąŨëč?ņq ģéūŋ?ß÷fSĒ˙S×āžũúû/Ŋõ^ZƒĀĶ›_ķKīįû?šhŲׂĪ|˙~™ųĖ?(cŸųzķĮĒū-æĐ™¨˙Ĩ€lÖĻú_Ûn˜;ĮŸ5g^ZÃĀċĻ5›üŖ‰–MWŋîSĩéũĖOšÄŋuŲmí7uĀčlÔ˙R @6kSũ¯mēPQÉ Aū#ë7Ĩ%|õž‡ũGŸČč/-kÛáĪ|Iė3ŋ1mŸų‘ŖFgü3O0:õŋԐÍÚT˙ëļ'ę÷‹ ôëŪęPxėŲũžũúĩē’0–mMW§Öm˜Ōķ™īī?˙ę2Ūgę€Ņɨ˙Ĩ€ŦÕæúßpģī‘û‚ÆöŽßũ­ĩAŨoÔũei´lŅŅgļCŸųãûß­_×)ũĨ‰ú_ę€ČfíĒ˙ rģ˙ŠĪÎ Ļuęųåzîõ_øoūę@đ˙ī`é7ží_X9Ũ/0Āŋņ–ũŸī{“ ŅēmMđßĪ˙û`ú~jŸųū‚›?ûĖŋÕé}Ĩ…ú_ę€ČfíŽ˙×ļŋģÍ_vßrō%—øC‡đ{øąũøŊ{÷Ž=ÜKõ}Ãßĩw˙0ĸåĖ-’toė)—TŸņ–ŸųūôĢføßøö}ūO÷ū¤ËúH0: õŋԐĩ:\˙KŖŅēGŖúßŦĢފĩ5ąÖ`ÚēX›kíũ‚#֖ÆZu[ū­Áo [¤Ĩū—FŖuF02úßŦŠVĀŨkšr(ÖvĮšĘ šeúš¤Û-7¯¯KaŨQąļ=Ö&ņ›ČiĢ˙ĨŅhŲߨFĻQ˙›5uĀ MPÕč¯;[kĢÚb;€kĖē`@ÖØõø÷×ėæ&4Z~´;WÜö‹Øī}=ú!Ô˙fOđ> ã<¯kJđ<{˙PQũī/öīĸū—F˓öę;/ĢøüųC&P˙›UuĀ›Mø,‹ķüīp-pĄVU#\Z¯Ô,¯ `Õ/ĩæņnŗ=ką ŲvēuCh?Ú˙~—™į4m[õÅ{ŊæŠÛšÆUļ1%ÖļÅZ“ŗ˙~ ‰T?øøüƒ„F˯vĖąĮüÆëü ņ P˙›UuĀĩ&*L.ō’×ū/5뗇–ÛĀģ4ô¸ÉXm[e;ÍōZûo īđl;Zŧ4´Ÿ}&ī4Ûđŧæšåõ&Ė.2ëŠnyŒĶ§™f˙ Ø Ė>7‡ú @++¯œuE˙ ĄŅōĢ=áŦ^į]ˆy„úßŦĢ^ę5ú&°*\ÎöZO}nk> ÕE&đZŽ*‡§@/uBŽ ĩZž,´ŧĖā-æqĄŲĮ¯õÕĨëÍņ–ķÛˆBũ/F0.Ô˙fįũ€ËĖ^›Ŋæ+@Û0<Ĩx]Äž™į*SĀ•áUˋ#ļģÁyŽÚüŧ(bŊ*ķ_đ"šß Ķh´~mtWkœ žGė8Ž›œįė•ŨúYŧ†8X¯uëx°tĄĒF'tĪöZ_\)^ļáp‹×ō~ēē%ĐAgģæįƒ^ˑT-ßė%ŸĘĢ/˜ĐįîguÄkë"Žs‚YVŅ÷ÕĄsˇĶėĢØécŖ×ōū˞×<2\qžf†Î…;ĒkC“yŸ œũl Ŋŋž×\ėŪ*jĻē5t E&LÚēØF€ö9Ö W…&˜ŲzV„îO€ ™õ֘`ĨP:ŪYĪŪ˛įŲv‘—øbLö–G{M˜\gļŪîxŗ?mWōZeú“ėXV­sN4’ŧ͋Ž.ķšīG\gŽķYVZw×|äfÛîč¯UiÎũ~ŗīMÎ9ßëxˇY×Ŗ=ĮáŅ^{Nw™}īōĸ§Š1¯?`Îīz¯yĻ@ŋ2ēģjŪļ™`iÚ¨ˆuKL(ÛiÖ[b‚q×rʰ}ŦQãÍ&p­Žjg›ˇÉJ۟xuēSL0ŗ}X§¯e&07˜ũ×{mģŠqĨ –ö‹đmĄÜũŦ4û°ĮYg›ķĖ—æx+ãŦ7ÎfĪíÎyŽúc™9ģÍk*âls†Ųį^ķžĖŽŗŪ(äw›íęŗP˝ @ü-ĸX@IENDŽB`‚trillian-1.6.1/docs/storage/commit_log/000077500000000000000000000000001466362047600201005ustar00rootroot00000000000000trillian-1.6.1/docs/storage/commit_log/commit_log_based_storage_design.md000066400000000000000000000316501466362047600267730ustar00rootroot00000000000000## Commit-Log based Trillian storage *Status: Draft* *Authors: al@google.com, drysdale@google.com, filippo@cloudflare.com* *Last Updated: 2017-05-12* ## Objective A design for an alternative Trillian storage layer which uses a distributed and immutable *commit log* as the source of truth for a Trillian Log's contents and sequence information, and one or more independent *"readonly"* databases built from the commit log to serve queries. This design allows for: * flexibility in scaling Trillian deployments, * easier recovery from corrupt/failed database deployments since in many cases operators can simply delete the failed DB instance and allow it to be rebuilt from the commit log, while the remaining instances continue to serve. Initially, this will be built using Apache Kafka for the commit log, with datacentre-local Apache HBase instances for the serving databases, since this is what Cloudflare has operational experience in running, but other distributed commit-log and database engines may be available - this model should also work with instance-local database implementations such as RocksDB etc. too. Having Trillian support a commit-log based storage system will also ensure Trillian doesn't inadvertently tie itself exclusively to strong globally consistent storage. ## Background Trillian currently supports two storage technologies, MySQL and Spanner, which provide strong global consistency. The design presented here requires: * A durable, ordered, and immutable commit log. * A "local" storage mechanism which can support the operations required by the Trillian {tree,log}_storage API. ## Design Overview ![overview diagram](commit_log_based_storage_design_overview.png "Overview") The `leaves` topic is the canonical source of truth for the ordering of leaves in a log. The `STHs` topic is a list of all STHs for a given log. Kafka topics are configured never to expire entries (this is a supported mode), and Kafka is known to scale to multiple terabytes within a single partition. HBase instances are assumed to be one-per-cluster, built from the contents of the Kafka topics, and, consequently, are essentially disposable. Queued leaves are sent by the Trillian frontends to the Kafka `Leaves` topic. Since Kafka topics are append-only and immutable, this effectively sequences the entries in the queue. The signer nodes track the leaves and STHs topics to bring their local database instances up-to-date. The current master signer will additionally incorporate new entries in the leaves topic into its tree, ensuring the Kafka offset number of each leaf matches its position in the Merkle tree, then generate a new STH which it publishes to the STH topic before updating its local database. Since the commit log forms the source of truth for the log entry ordering and committed STHs, everything else can be derived from that. This means that updates to the serving HBase DBs can be made to be idempotent, which means that the transactional requirements of Trillian's LogStorage APIs can be relaxed: writes to local storage can be buffered and flushed at `Commit` time, and the only constraint on the implementation is that the final new/updated STH must only be written to the local storage iff all other buffered writes have been successfully flushed. The addition of this style of storage implementation requires that Trillian does not guarantee the perfect deduplication of entries, even though it may be possible to do so with some storage implementations. i.e. personalities MUST present LeafIdentityHashes, and Trillian MAY deduplicate. ## Detailed Design #### Enqueuing leaves RPC calls to frontend `QueueLeaves` results in the leaves being individually added to the Kafka topic `Leaves`. They need to be added individually to allow the Kafka topic sequencing to be the definitive source of log sequence information. Log frontends may attempt to de-duplicate incoming leaves by consulting the local storage DB using the identity hash (and/or e.g. using a per-instance LRU cache), but this will always be a "best effort" affair, so the Trillian APIs must not assume that duplicates are impossible, even though in practice, when using other storage implementations, they may well be so currently. #### Master election Multiple sequencers may be running to provide resilience, if this is the case there must be a mechanism for choosing a single master instance among the running sequencers. The Trillian repo provides an etcd-backed implementation of this already. A sequencer must only participate/remain the master if its local database state is at least as new at the latest message in the Kafka `STHs` topic. The current master sequencer will create new STHs and publish them to the `STHs` topic, the remaining sequencers will run in a "mirror" mode to keep their local database state up-to-date with the master. #### Local DB storage This does not *need* to be transactional, because writes should be idempotent, but the implementation of the Trillian storage driver must buffer *all* writes and only attempt to apply them to the local storage when `Commit` is called. The write of an updated STH to local storage needs slightly special attention, in that it must be the last thing written by `Commit`, and must only be written if all other buffered writes succeeded. In the case of a partial commit failure, or crash of the signer, the next sequencing cycle should find that identical writes are re-attempted due to the signer process outlined below. #### Sequencing Assigning sequence numbers to queued leaves is implicitly performed by the addition of entries to the Kafka `Leaves` topic (this is termed *offset* in Kafka documentation). ##### Abstract Signer process ```golang func SignerRun() { // if any of the below operations fail, just bail and retry // read `dbSTH` (containing `treeRevision` and `sthOffset`) from local DB dbSTH.treeRevision, dbSTH.sthOffset = tx.LatestSTH() // Sanity check that the STH table has what we already know. ourSTH := kafka.Read("STHs/", dbSTH.sthOffset) if ourSTH == nil { klog.Errorf("should not happen - local DB has data ahead of STHs topic") return } if ourSTH.expectedOffset != dbSTH.sthOffset { klog.Errorf("should not happen - local DB committed to invalid STH from topic") return } if ourSTH.timestamp != dbSTH.timestamp || ourSTH.tree_size != dbSTH.tree_size { klog.Errorf("should not happen - local DB has different data than STHs topic") return } // Look to see if anyone else has already stored data just ahead of our STH. nextOffset := dbSTH.sthOffset nextSTH := nil for { nextOffset++ nextSTH = kafka.Read("STHs/", nextOffset) if nextSTH == nil { break } if nextSTH.expectedOffset != nextOffset { // Someone's been writing STHs when they weren't supposed to be, skip // this one until we find another which is in-sync. klog.Warning("skipping unexpected STH") continue } if nextSTH.timestamp < ourSTH.timestamp || nextSTH.tree_size < ourSTH.tree_size { klog.Fatal("should not happen - earlier STH with later offset") return } } if nextSTH == nil { // We're up-to-date with the STHs topic (as of a moment ago) ... if !IsMaster() { // ... but we're not allowed to create fresh STHs. return } // ... and we're the master. Move the STHs topic along to encompass any unincorporated leaves. offset := dbSTH.tree_size batch := kafka.Read("Leaves", offset, batchSize) for b := range batch { db.Put("//leaves/", b.contents) } root := UpdateMerkleTreeAndBufferNodes(batch, treeRevision+1) newSTH := STH{root, ...} newSTH.treeRevision = dbSTH.treeRevision + 1 newSTH.expectedOffset = nextOffset actualOffset := kafka.Append("STHs/", newSTH) if actualOffset != nextOffset { klog.Warning("someone else wrote an STH while we were master") tx.Abort() return } newSTH.sthOffset = actualOffset tx.BufferNewSTHForDB(newSTH) tx.Commit() // flush writes } else { // There is an STH one ahead of us that we're not caught up with yet. // Read the leaves between what we have in our DB, and that STH... leafRange := InclusiveExclusive(dbSTH.tree_size, nextSTH.tree_size) batch := kafka.Read("Leaves", leafRange) // ... and store them in our local DB for b := range batch { db.Put("/leaves/", b.contents) } newRoot := tx.UpdateMerkleTreeAndBufferNodes(batch, treeRevision+1) if newRoot != nextSTH.root { klog.Warning("calculated root hash != expected root hash, corrupt DB?") tx.Abort() return } tx.BufferNewSTHForDB(nextSTH) tx.Commit() // flush writes // We may still not be caught up, but that's for the next time around. } } ``` ##### Fit with storage interfaces LogStorage interfaces will need to be tweaked slightly, in particular: - `UpdateSequencedLeaves` should be pulled out of `LeafDequeuer` and moved into a `LeafSequencer` (or something) interface. - It would be nice to introduce a roll-up interface which describes the responsibilities of the "local DB" thing, so that we can compose `commit-queue+local` storage implementations using existing DB impls (or at least not tie this tightly to HBase). ###### TX ```golang type splitTX struct { treeID int64 ... dbTX *storage.LogTX // something something handwavy cqTX *storage.??? // something something handwavy dbSTH *trillian.SignedTreeHead nextSTH *trillian.SignedTreeHead // actually something which contains this plus some metadata treeRevision int64 sthOffset int64 } ``` ###### `Storage.Begin()` Starts a Trillian transaction, this will do: 1. the read of `currentSTH`, `treeRevision`, and `sthOffset` from the DB 1. verification of that against its corresponding entry in Kafka and return a `LogTX` struct containing these values as unexported fields. **The HBase LogTX struct will buffer all writes locally until `Commit` is called**, whereupon it'll attempt to action the writes as HBase `PUT` requests (presumably it can be smart about batching where appropriate). ```golang // Begin starts a Trillian transaction. // This will get the latest known STH from the "local" DB, and verify // that the corresponding STH in Kafka matches. func (ls *CQComboStorage) Begin() (LogTX, error) { // create db and cq "TX" objects tx := &splitTX{...} // read `dbSTH` (containing `treeRevision` and `sthOffset`) from local DB tx.dbSTH, tx.treeRevision, tx.stdOffset := dbTX.latestSTH() // Sanity check that the STH table has what we already know. ourSTH := cqTX.GetSTHAt(tx.sthOffset) if ourSTH == nil { return nil, fmt.Errorf("should not happen - local DB has data ahead of STHs topic") } if ourSTH.expectedOffset != dbSTH.sthOffset { return nil, fmt.Errorf("should not happen - local DB committed to invalid STH from topic") } if ourSTH.timestamp != dbSTH.timestamp || ourSTH.tree_size != dbSTH.tree_size { return nil, fmt.Errorf("should not happen - local DB has different data than STHs topic") } ... return tx, nil } ``` ###### `DequeueLeaves()` Calls to this method ignore `limit` and `cutoff` when there exist newer STHs in the Kafka queue (because we're following someone else's footsteps), and return the `batch` of leaves outlined above. *TODO(al): should this API be reworked?* ```golang func (tx *splitTX) DequeueLeaves() (..., error) { // Look to see if anyone else has already stored data just ahead of our STH. nextOffset := tx.sthOffset nextSTH := nil for { nextOffset++ tx.nextSTH = tx.cqTX.GetSTHAt(nextOffset) if nextSTH == nil { break } if nextSTH.expectedOffset != nextOffset { // Someone's been writing STHs when they weren't supposed to be, skip // this one until we find another which is in-sync. klog.Warning("skipping invalid STH") continue } if nextSTH.timestamp < ourSTH.timestamp || nextSTH.tree_size < ourSTH.tree_size { return nil, fmt.Errorf("should not happen - earlier STH with later offset") } } if nextSTH == nil { offset := tx.dbSTH.tree_size batch := tx.cqTX.ReadLeaves(offset, limit) return batch, nil } else { // There is an STH one ahead of us that we're not caught up with yet. for { nextOffset++ nextSTH = tx.cqTX.ReadSTH(nextOffset) if nextSTH.timestamp < dbSTH.timestamp || nextSTH.tree_size < dbSTH.tree_size { return nil, fmt.Errorf("should not happen - earlier STH with later offset") } } // Read the leaves between what we have in our DB, and that STH... leafRange := InclusiveExclusive(dbSTH.tree_size, nextSTH.tree_size) batch := tx.cqTX.ReadLeaves(leafRange) return nil, batch } } ``` ###### `UpdateSequencedLeaves()` This method should be moved out from `LeafDequeuer` and into a new interface `LeafWriter` implemented by dbTX. **TODO(al): keep writing!** trillian-1.6.1/docs/storage/commit_log/commit_log_based_storage_design_overview.png000066400000000000000000001226101466362047600311020ustar00rootroot00000000000000‰PNG  IHDRÚÄÆTR…bKGD˙˙˙ Ŋ§“ pHYs  šœtIMEá ¤Ļ0F IDATxÚėŨy\OŲ˙đWĶgÔ¤’De>ČhDLB"&[ÖLžˆŒ}Ëžīdûž%#Bû’Yĸ¨IQdĘôĄR*Zõģŋ?ŌEģ×ķņđŨĪš÷Ü{îí3Ū÷œ÷9QÅŖ‚ č=z„ĩĩõvAF‚0Ũēu—ííí#AđÁk͚5zöė͟ų3ŧvîÜŋb>NM@DDDDåœ2}A4ôŧįĖ™ķlŨēuĪRSS=Ųz ´‰ˆˆˆ¨|S!€ÕíÛˇ[ŗfMO777%6 QÉōööFBBBzŸ>}ŧ¸FW"Ē€‚ŒRPPˆZ/CĮ‰ˆˆˆ¨Lhذáq__ßö~÷Ũw ˛‰*ŽÆ|ĖĖĖ<ÛĩkgZ/=ÚDDDDTęA˜°´aƊĘĘĘlĸ¯G$€˙)((\c MDDDDôå$ß~û­ũ›7oZR'ƒôôô\Ûĩ´´ ĒĘ•ÃˆJHfßž}Ŋ;æ`7›ƒˆˆˆˆč3=zôH:jÔ¨§¯_ŋJ‹……… Ī?RŠTX°`œœüŲĮOLL–,Y"”7~~~‚ģģģ@TR’““…ÄÄċ‚ Tˆ7\ėŅ&"""ĸ'‚1€S¤ĨyíÛˇ‡ˇˇ7&Nœ ˙&7Įéͧ sssœ;wîŗz¸;uę„đđpDDD”›{#“ÉPˇn]Ė™3ķįĪįÃJ%-pđāÁŖ÷íÛ ŧŧ^ $"""ĸ’¤ŖĨĨu-33S["‘”™žĢ &@OOOnÛÚĩk1`ģģcá…XĩjUĄ›™YūV0ĘĖĖ,—įM†ąššš{PPЎĀĀĀ…åõ"ØŖMDDDD%F„ų‘‘‘ > jKKNvDDō:§””üøãHJJB\\œ\¯ļˇˇ7Ž_ŋŽđđphhhĀØØũúõCÎdn...prrB||Ļ/mii9×ú#""""ú€¤J•*Ą‰‰‰oËc Ŋk×.š`tŨēušIÎŪž}+Œ;V lÛļMîø9i}}}AGGGˆŠŠˇEEE :::‚ĒĒǏm˙ūũÁÆÆFș0.11Q066”••…ččh!11QĐŌŌôôô„ˆûēšš ‰Dppp ´sŽ—ķ2 ""BĐŅŅ”••Å <""B ,X°@Ü7'ЖH$Âūũû…>>>ÂÛˇo… ĒĒĒ‚———\yUUUÁÜܜĄ"}‘ 6DÛÚښķk”ˆˆˆˆč=‚ čŋ}û6Ž,ū#ž öŪŊ{åO777aäȑÂÛˇōī îŨģ—+@ũ0ĐNLLf˘!¸ššåĒ'§ˇ8..NÜWUU5×Ŧį—.]lll„{÷î‰A^3„ÛØØ‰Dއ€øķ‡Ŋæ9mđą@{ārûž>}Z Ŧ^Ŋ:Wũ3fĖŒéK”ˇŲČ9U/// €ã‰DĢĸ\Tŋ~ũĐ¯_?dff"<<?Fhh(ŧŊŊ?𝆆–/_ ;¯:<<Ož<¯¯¯¸JJ ´´´āīīccã\ŗ[ZZÂŌŌ°paö\Q‘‘‘pqq‘+—3ŠY`` ,,,zzzrŗĢ/“ÉđŠÜyšŸ¯]ģ {–öëOIIÜēu ÆÆÆüm /aœššzBYYywzzúÚDDDDôՒH$“ŧŧŧ~ĩ°°h\ž¯#<<{…Ą ˆäĖ™3ąwī^1˜ÔĐĐ'û___Œ7Nœ8 ŒsM–’’’kŌŗÅĮĮĻL™’o™'Ožčŧ 2Ķø‡AzLL ĀÉÉ)ß}rĘ} A:˜››WētéŌae~Z|ÚDDDDT,’““•%Iãō~įΟ´nŨ0|øp¸ššÁÁÁ666hŌ¤ tttŠĶ§OôX‘‘‘čÔŠTUUąiĶ&´nŨúúúPUU…ŊŊŊÔŋ{Q™L–g@œžžUUUą7úõë×Ĩ2ÃwNā}īŪ=4nܘ=UUUxxx˜0SPP¸VÖĪ÷Ū2""""*j‚ Ø)++Ī’HĘwŋNÎ2XŨģw‡T*¸ģģÃØØ;w•ttt!!!Ÿ<Ū‰'’’‚ `˘1rCÃCCåW1266†ŋŋŽžf___¨ŠŠaũúõâė+WŽäĒËÅÅ“&M*Öåœú/\¸ëŗ3gÎ`ܸqæ/Iffæ#ÚDDDDôUũCXYYųJttôîōtŌ2™ ‘‘‘âoooL:C† ĒĒ*V­Z%–UVVF||ŧ8l ,ב2dˆ`cc#(++ C† $‰`cc#–ÍYō €Đŋ!11QJĨ‚D"ēwī. 2D011444„^Ŋz ÄúŪž}+ôīß_\ŸÛÆÆFÜwĶĻMbîîî‚D"”••…îŨģ ;v$‰ ĄĄ!wîzzzš–{˙zŪ_ž+g]pÂéͧÅYĮß_ŋûũuŧsÖīØąŖĐŊ{wAUUUPVVÎs6tĸ"2@ƒ˛ú=¨0īđ™éoßhō DDDÅë…oūqú­g=ļUd‚ čx€r2‹‹ "##sm×ŅŅA“&M`ff–į~GÅéͧ}}}ôë×æææØž};’’’0}útŲšmßžÁÁÁ033È#õë×#88ĘĘĘ011Á°aД”´k×Nœ%Čn~ūüy„‡‡ŖAƒ6lXŽYŧÃÃÃáââ‚āā`H$cذaâ°vXŋ~=`„ rûz{{ãʕ+}ŗĖÚĶ\ ŋĩ6á]"""*fŽûcĨ]oļU`vļ•*UęÉĻ ĸâ6iŌ$Œ1"ĻAƒļem‚4æhQQP600čņÍ7ß0Č&ĸŅŗgOčdÛđęͧ€a ´‰ˆˆˆ¨"I ­QŪg'ĸōÃÂÂ"g‰9‰‹‹KĮÄÄD;AThQ… BOl "*iIIIPUU…††F{>ƒ j  T'` MDDDD_DAAÁũÖ­[ëØDTAļŠŠéûë͡iĶæ¸ąąąm""""*ˇŽ=zŖyķæzl "*ixđāŪO[qppĐ)BŠ­ĩÍ@›ˆˆˆˆžHīŪŊÛ07›ˆJK>ß?:ÎYZZŽA) #g MDDDDŸKšC‡ßčĖĻ ĸŌäīī˜˜˜\Áv¯^ŊæŲÚÚę0Đ&"""ĸōÂäɓ'(ŗ)ˆ¨4]ŋ~ááášļ;ļÆáǡ–ôlä ´‰ˆˆˆčs] Ėf ĸŌ6a˜››į÷qãÔÔÔĘĘĘŋ1Đ&"""ĸ2M €îl "*ßW,,,Æ(‘ %hŅįĐ_ˇn]?Zl "* <==áëë›įgĒĒĒ8w f%q.ŸÍgefÂ˙–/î!6&**•Q­zu˜˜™Á°QŽō§ŽHk륊ŠiĄë 삇î,;w…JåĘErņ~~ũ °îk›oš´ÔTxž?›ī5defâÂéSČĘĘüĸëôö¸ˆŋۀZzzhÕÖwnŨ77E-=ŊbyâãâpķĒwŽí•””QMK 5ĨĩPS*-Ņ_’Â<3Åõ|ŅG5zņâÅ 6•éééPRRúhü›™™yÄÁÁÁj׎]Ae*Đž„Ų“& âqxžŸˇĩėˆEĢū€†ĻϏmîdG@ÛĪ @Ŋ.^ÄļukÍ[´,˛@ęøĄƒ8é~䓁vRbâG¯aö¤ 8ę$@Ģzu¸žK$:!!!tX&mߛ3øČČȨTŽ CC¤ĨĻ!ė~āĒį%Lá€]Cņ+XOqÕĸ…rAöNˇÃĐÖÕ-ôqNēhëębĐđ€J•*•™ë<ę$ÔÔĢ`öŌeEz\ĢNˆxŽ6ļŸ,ëŧu Ö¯XÎo""ĸ2@„Æ(…ĩi‰ˆžÔ7tX)((¤-Ž: œŖ‘‘Ų“&ˆAöØ)Ķāu÷o89†CgĪÃũÂ%ąĮ5ĀĪ—ΟĢđ7Čyë¸îŲ%d×­Wø˙ßÄĮʼníÚ­WØ ģĄÃJåšFNtD`d#Ŗp'<̎n‡_;tiŠŠEZ_~##Î^쉺×nÂq֜O–ĩ:L,¯­ŖÃo""ĸâ§7mÚ´~l"*k6oیČČȂujŨēĩg÷îŨ[Įy¸Ëų¯CnˆŽô<cĮÉ}Žo`€ųN+ąjŅBč@EEå“ĮĖÉmö8{QO"ĄQUFM›ÁÆn`ž9ÁéØĩi#Žz^455…ÃØņPSW—;ŽûW\ŋâØčh(JĄUŊ:uíúŅ!â…qhŸ‹Øģúą Ûį˛'Î?†ŸDâMF´utabÖũ †JåĘđö¸ˆ3Į‰å¯_ņF|Üst°ę U5ĩ<ÛlķĢ÷ĐģßqhĩĪeOs;ˆØ˜hšē~:ėSš yR”HĐąKW\8ugĪ +3‘ÃĐȨĀõy{\Äå įQUSŊúõĮÚeK÷MM~FōĢ—b]ū~˜7ÅQŧžœĄāMMFī~ũąvŲøû‰åįMqDŨzú°5wnųâō…ķĮYsäŌÂî‡ĀŨÕa÷C‘‘ēõęĄ[ī>hĶÁRîZįMÉĒŪÁĒ3¤ĩëĀyÛ„ŨļŽ.:÷čQdĪ QĄņâÅ öfQ™ŖŖŖeeå•555­9bĈc§N˛UPP¸V*öõ+Ūâßs†6ČÄŦ%=_ ãĨĨĻbü0{øûŪ”Ûîī{nûöbķŪ?ķĖįvü}¸\ĪfPĀ]\õô„푪b€5oędšā5ĮUĪK š/— ü9.;‹•‹Č>ŋfĮŽ<ƒl÷ûądÖLšmáaa¸~Åׯxc—Ûa„†„ĀãėšĀ0ė~jJkÁÄ,÷„xk–-{Ņí†:ˆm´kĶFlZŊōŖu}ŽÄ„ņīßUV)T}Ą!!8é~ZÕĢãōÅ ˆz÷v)+3KL7€¨ČHDEF´eK455sįŗ_&ô‡ĮŲ3x&“‰ÛN灉YK؏-Öd÷Čį<Ž{vcͲ%ČĘĖ”kßķ§Nĸ‡-­^#w<ˆz‰ûAA⃜kŠûQŖųÍEDD”-p÷îŨál"*kúöí[ā˛={ö„ŽŽŽ¯>}úx;vĖ Āîĸ8ËēŽ¤¤T$3P¯^ŧP ˛;uí†u;wcÂô™PŠ\ų]>ņqqšö‹‰Æ˛upāä1¯7âq86Ŧtīœ ģO˙8tö<ļėÛ/kĪɅū\÷īaú¸1bđύ¨mŨ<_$ŦYē@v¯ûžã'°īø ´ng!žPˆŠŒ„‰™ė†:ČŊŦ9Ņ1Ī ÛuĪn1ČîÔĩ›øÂ )!;7Ž´0oƒ'ĪĀųČ11÷÷Ŋ™g[æ%ųÕ+<“Év?ˇŽ_ƒĶüšâ}ŌĐÔDí:zH~õĒĐõÅĮÅáE\ú‚ÎÖ=0tôŒœøßl†0rĸ#ę74Ėķŧė†:ČÍh?rĸ#zÚæßËpW ˛kJĨ˜ŗl9VlÚ"öƟt?‚};wäÚ/ĀĪVÖ=pāä,ūc8ĪĀąCų­EDD$OĘ& ĸōĖ qqq阘˜h'‚jQģĐŗ•UĢ^]îįg2ēšįÖnbÖ2ß^Ô¤„œx׃ØÂŧ VmŲ–}Ą~A]}}L> ɯ^áĐžŊ3yĒÜžSį-@×^Ŋ‹V¯Áũ{A ÃųS'1Īi%Œš6Ã)o<~ô-Ûļ‡0ˇngˇČŊH~õę‹,<,,;Ā–H•™‰äW¯°tÎ,lØí,WNĨreœõš{¨ßĐPœ ­Ŋ••8: 6&&f-QSZK  ŗíIbšãō… đēxA ˛6l?ĶĐÔęk7ņā^ūd­w÷¨[ī>đķ{wŸĸÄíãēg—x.š<{%¨ŠĢ˛ž¨ČČ\õœčˆA×ۖ3DÜĀĐPŧîŧíaboöą&}ô:ėŲŦĖL(J$Øėō§8â Mûč×­3ĸ"#ąoĮļ\įSˇžžØĶmhd„ķ'Oâúoą'žˆˆˆƒ“’’ęŧûj™ŠŖG"442™ Q2^ÄĮ#))‰wč hhh J•*¨Zĩ*4h333XZZĸqãÆeę<###qčĐ!LŸ>ŊĀû$%%åÜíø 4hôž}ûâ|öȝÚJJĘb`Ā|ŽĀ;ūbp§Ž]åß*tújęęH~õ Ašö561ÉЇ‡…!-5Īd2ԔJQKOpwŨ{wpWnØņ—RŠ\v;cÕĸ…ģ‚Ģž—páô)Xuˇ–55QŋĄ!|ŧ÷$]9öûšĩüb0[USîጌ L=Rœ,MQ"AŖŸŒ ĄŠ‰ûA_ž&šJåĘbūøŒ…‹aoÛ°|Ū´hÕZn"Ž +œā˛sģxúhjj*öúVN/úžÛŅŖ¯­\€øŠē vĢí†:ČÍxŽ­Ŗ“įK•Īšļ÷ÛĻ$ž@E%÷šÚßŊˇ-11AŽ+}ƤqDDD_ĄĨ:::Kû$ŌĶĶą}ûv,^˛í;vÂō ›P˙ŊT3"*:jęęPSWGMІFFčdŨ“ŨņĮÁ=ģáää„ÕĢWÃÎÎŽTĪS*•BZˆTg ÷ƒėė˜ÖÁAÛÁÁa$€ ŸĩÖvs´Û[u˙~Āyˇ\ lbÖRüSu~¨÷_ “(÷YZj*bcběųĄ¨'‘ųūŦĻŽŽ{v‹AöĖEKp#$T.7úKuėŌU|ÉĐÔԝ­{dŋ IH€Ķüšbš??ėŲēY™™°ĩûŧīū ÷ —Ч˙€Īǎn=}8yJJJČČČsŌāÆÕ+b]ũÉŗŽ‚Žíķ˓ķ'¯ ģ@õéč–ę/XNđëŗg˛(ņīĩëčņ[“ˆˆ¨AĐ*ÍsˆŒŒ„‰Š)N=‡Ũ‡Ž`ŽĶJŲDĨš –oۊõģö`Ũú čŌĨ âããËÕ5Hō­­āœĨĨå…^eĄĀvĮÎ]Ä!¸gĪ`ҌiHzo6ę°û!˜:zdŽe`ØH ĸŨ\œå&ÎÚĩiã{ÃĘģåÚ×õ]î-=ÁVNP­o`5uušÉm~ŗ{Ėķ†^&͚#ÖqūÔIø\ö<|p_,cmc#öæūû™šžCG†a#؏#ۃœáŅAwīŠåú ,֕“Od/‹VT V_FŠū˛´hŨ&ûELd$Îūu\܆ §NŠ/JJ˛—ˆˆ¨ĸĐĶĶs*ÍúŊŊŊaÖ˛%lÂ;vA¯W#*mÂöƒ‡P× Zļl‰ĐĐĐR9øøxL:ĩĐûųûû#æ]‡īûÁv¯^ŊæŲÚÚ:UĻĀCĮ%ŦÚ˛ öļ}üꎚÄ1ˇƒ¨)•"--M.čÎ ĻķŖ¤¤„‘ąjŅ<“ÉĐÛŌ­ÛYāßwK+Ųš×æ<ŲKtũÖŖ; qãŠˇ8DxčģôũžÛãĮĸ­Ĩ%<Ξŭk>âöäW¯Š,OG[WÃĮM—ēZ2{&Ü/\’ŽŧzŅBôî?áaapÛˇWÜūæÍ›B×g?j4Ü]˙D|\œæĪÅĄ3įĄo` ~>Ęd؈ ģr3eŋˆ‹Ës ˛ĪQú’ tŦœúKįÎ"6&=mlÅÉî>¤Rųŋ!ßöļ} ­Ŗ §›ķ,;høī8ķ×1$%$`ÖÄņ8ævjęę¸qÅP”H0uî~#}ÃŌĒÛĶĶvvvXļaš—āü/DT°˜qĤɐ֎ƒN:ÁÃà 4(ŅsPVV†™YáŋŽ_ŋŽæÍ›#{ĩ¯˙Œ;ﯨącˇhŠ  RĐã}S˜Ęõ pāäšaØĪd2š Û¨i3lÜŗ÷“kUÛ ĮŲsĄ¤¤„äW¯pūÔI1ČnkŲv;į9lyæĸ%†“îGÄžđĄŖÆˆÁ™ũ01ô8{s';Âß÷&˙ūßÚß÷‹ļwû×<‹ŽÆÚeKĐĒm;ąG>(ā.NŸ ˇ}{1pč0ąüsrƕ””0aFöÚÜáaa8~Ø í;ũ"Ū“ €ģ˜;ŲĮÄĀĄÃÄ6ŧW„=úEY_ˇŪŲ9îiŠŠ¸uÍGnD‡Ŧē÷āį—k ö_€lß@|áãī{^/ ##ÚēēØâō§¸ÔNDDÄĘŌ¨7<<ũúõÊM[d•aŨúÚ`Ԥɰļļ.ņaäĒĒĒ…ZK;Į„ `nnžßĮSSSO(++˙VĐã)Ls=.üÖÚ¤Đ'ōL&ÃŊĀD„gFuõõņ“qĶ<×ØÎ ˆĒU¯žĢW5ųÕ+Üē~ ‘CM]Æ&&šzßÉdb^­‰YK<“Éāuņ˛˛˛ĐĒm;šV {Ø˛ĪeO„‡…áûZR´io MMņr °vŲxœ=į#ĮJei´Cû\pÂũ0î%5m [ģ˙Ąk¯Ū|8¨B‘H$:z8”ŗ=ÚDDDDT(%™Ŗ]žŠWQGMi-š?Īd2ŧˆ‹Ëĩ]Ĩråë™L†Ä„ņįØčhšžâø¸8ÄFG‹?'&$ā™L†ŦŦŦŋîE3Ļaųŧ9HJH€ŨPŒœčˆž6ŒÄŦ‰ãąa…*V_’ŖŠS§z?‘ÄéĘŲŖMDDDD…R’9Úåú…D_[X÷ĩ•ÛfŦW ÕĒWĮ.ˇÃ…:ÖŲk7ĄĸĸRāōŽŗæ`äDGhp™ĸĸ†cnŅÂŧ ļėŨEÉáFōĢWpč˙+ölŨŒŽŊzCßĀ€ ‹/ÍŅ633ûœīÅļ͛7o~įΝ3{´‰ˆˆˆ¨˜Ŗ]<222đL&˙ësŲŗČķ›322p—Νŭë×äzÁ +3Īd2¤ĨϊķĨsgÅ!āŸō 8ģ\÷ŪŊå‚l ;Gwđī#7Ž^Éĩolt4|.{âÆÕ+Ŋîø¸8ø\ö„ŋīMdddČĩ¤ĨĻĘ]ÃûžÉdHzod@ޤ„ܸz>—=ņL&ËķķœļJKMËæuŦQ‘‘đö¸ˆ[ׯåy.9/n]ŋo‹ˆŠŒĖķķœķ ģŸËžâuRūž$G[UU}ûö-ô~'NüÆßß_šķ3{´‰ˆˆˆˆĘ€{wáĐ˙W8Ξ‹mëÖ -5jęęđžû7皎D[,ZŊĻ@ĮZŗl NēÁŲk7QSšũo÷ûąŪi9’_Ŋ’+ÛÃÆķVBQ"AlL 皎„ãėš¸ęy ūž7Årúذ{¯xŧŧTĢ^pÕĶ]{æļ-ģt…OK¨ŠĢ‹ÛŌRSąhÆ4œ?uRÜĻ(‘ĀÖn ĻΝ/wŒ +œā˛s;˛23Úēē4|V-Z€]n‡abÖžįĪbîdG,ūcMŽļcVf&V-^ˆ#ŽûÅc@gë˜į´RŌŋfŲøûŪĄŗ°púT1pV”H0jĸ#Ǝ“{0cÜšļSRRÂÔų `ķÛ@qÛļukáŧuŗ\āÜÖ˛#æ9­„Öģ%í\÷ėÆļuk0gŲr,™5S,ŗaˇ3aĘĻūļėŅ&"""ĸBbŽvņÚŧz%ZˇŗĀĖEK0rĸcŽ`õsø\öĒY3abÖî.áėĩ›8pō šššâ¤ûxy\”+ŋmŨ¨Tތ}ĮO⏧˙>áaaذbųGëiŅĒ5 Œāqö Ŧ-Ú`í˛%đšė)÷JJJrA6Ė?įOÄȉޏpķ6.ÜŧÁÃGĀÍe/V-^(–ÛˇsölŨŒž6ļđžû7n„„ĸm‡ŽXŗlÉgˇËĒÅ áæ˛}ú Ā)oxßũŽŗįÂãÜYĖ?VŽė‹¸8,™5Sį-Ā)olŲˇ5ĨRlZŊáaabā>rāÜŋ„9˖Ãûîßpŋp †FFX2kĻ|īÚ´ÛÖ­AĮŽŨpÜĶ Ūw˙ÆüĢāī{SGČũâdé 5ĻĪ„­Ũ@ū’|—ähÉū­tuuÛ2Đ&"""ĸBcŽvņ242ÂĒ-ÛĐoĐ`Ø V$Į ģ5ĨRĖwZ }ԔJahd$Îbž(æPSWĮÚí;aÔ´ęÖĶĮ¤YsPS*EPĀŨÖŖ(‘`ûūƒčĶ^ÄÅÁeĮvŒ:mŒaPīžpŨŗ[Ž7(ā.Žz^B˙ÁC0râ$hëęB[Wã§Ī@gë8âē_˛ŊoĮ6Ô­§ŲK–ACS*•+cöŌehjbúYm#ŽûŅÖ˛#f/]†ZzzĐĐÔÄ áŋcč¨Ņ¸ęyIîz3220aÆLôî×ĩôôĐĒm;Œščāŋ!ķ—ΟCxXFNt„ÍoĄĄŠ }ŦÚ˛JJJ¸qå ŌRSąsãz5m†Ĩk×Ŗn=}hhjĸwŋū9Ņ~~š†ÖÛ †ņĶgĀ~Ôh´é`É_’Oø’í/Ų˙͛7•~ûíˇ& ´‰ˆˆˆ¨Đ˜Ŗ]ŧLĖZų1ƎÃŲk7ĄĄŠ ģį5<, ūūy–ojbšĢ'ŊĻ´VęRSWĮ<§•đēû7VoŨģĄĐ70@PĀ]ŦZ´v=ē‰šÍ7Žd”FM›á™L&÷Į°˛23xĮa÷C‡ļ––šÎĢ[ī>ŸÕ&ˇo\CVf&šš˜æĒģn=}Ā__š}šˇŸ$ĢÆģÉæâŸgᔸŨ€\KĩiU¯Ž[aá?}îÜōEFFZ˜›įq͆€ëŪŪÅūLTd_’Ŗũ%ûwëÖ ĢW¯Ö˜ŖMDDDDTĻÔÖĶ+–ãú\öÄ1ˇƒ&Nŧ•߲b•””ž¸>•Ę•ŅąKW1č|&“aÕĸđēxV:ažĶJ<“EfMŸīqžEEĄš–ā;•Üį[ĢNĪ:ŋ§QŲ“Œ­_ąëķ˙áÄhJJĘy–ËéĨĪé}˙X{RböK†7`ᯠy–I~õōƒkÔã/Fųa`!m""""*kkkĢS§Ną!ʑ};w`ÍŌŨĨ§‡ŋXAßĀõBEĨ2Ŧ-ÚY=Ģ-DōĢ—yNÚVS*Åę-Û`ųssø^ķHŪõN¯Ø´E ĻsīWK ČķRĐĩÂ?œ­;§îŠķˆ=ÉĒönR˛‚Rz÷‚"##Cü{~†­Í‹¤^’googgįRŲÆ -č1Đ&"""ĸBaŽvųŗgëfhëęâЙķrŊØˇŽ_+ŌzÂî‡Āß÷&†ZyôĖ+J$(*Š=Ãzī†h+**æņ8Oūų•”” _?{ÍíČĮáyÔyŋ@įöáōYzõꉁú‡uĮFGã^`@‡Ëŋ˙R‡…ÁĐČHîŗŠŖGĸZõęrÃĘ?Ŧ7)!ˇn\‡ļŽ.Ú/PZ9Úđũ÷ß+͚5K9ÚDDDDT(ĖŅ.’_Ŋ‚šēē\•™ ×=ģoŠhmæžļŲËiM=2W`›•™‰]›6">.­Úe2ģt…ĸD‚=[7Ë­5•™‰ųS&cō¨H~õššhaŪ^/ČMܖ–šŠãnåęŠņ.H ų`íīSGČũÜĒm;¨ŠĢÃÍÅ9×ēŨĢ/ĔQ#õ$˛pÚ/ŋ\wËm ¸ ŗg ¤¤„Ļ&ĻĐĒ^n.{s­cžfŲL;÷ƒīņĄũĨ•Ŗ }ûöÅŌĨKĢŗG›ˆˆˆˆ¨‚ëđ‹<ΞÁėI`fnŽŒŒ œ9~L 0ccĸ‹¤ëžļē€#ŽĸWĮö0jÚĩęčáMFüũ ŖĻÍ0fōŲÃÉĮNžŠõ+–ÃŽGwô°ą…JåĘ8á~÷ƒ‚0tÔqb˛9K–ÁŪļüŠūƒ† ĒĻ&ޏū™ë܍MLĄ­Ģ 7—ŊH~õ ĩęčá^Ā]„? 'ƒ˛sČg.Z‚šSa×Ŗz÷­Õáqö,n]ķAgëųíΏQĶfčĶŽšDōĢWhki‰øįqpŨŗ ZÕĢÃ~äh(J$XôĮZŒ:ũēuF˙AC UŖ:ŽzzâĒį%´0oƒŽģđĄ-ßôhQá‚)æh6ŗ–ĐĘ'˙VM]&f-Q­z<÷Ë 8sē÷Ë6’;nŨzú01k)NjļpÕPSWĮå‹pæø1Ô­§+ë4üw8͟+öhWRRĘUW~uägöŌehmaGã^Ā]øųAII õ 0høØØ ”Ë_ļ5uõõá˛cvnڀŦĖLÔ30Āâ?ÖĀ睭XŽ–žö?‰ĩ˖ĀuĪ.@g랰ĩû–Ī›#–SRRÂŽƒ‡ąaĨŧ.^´ngį#Į°aÅrškëÚĢ7jččĀyë¸îŲ…´´4Ô­WSį-@˙AƒķmĪīŲ÷ĩū›ülžĶJ6Â1ˇƒX>o.ÔÔÕŅŠk7Œ˜0I ô[ĩmį#Gáŧu ÜöíERBô 0rĸ#ėGgV˙ž–4ĪzéãJ3G;<<NNNĻš~kmÂģADDTĖ\÷ĮJģŪ l *īęÖ­{îŸūé\u)(( 02ŠNų:uôæNvÄ.ˇÃ\̌3ÖĢAŠŊ—/ūũ%ûËd2xxxx0G›ˆˆˆˆ …9ÚDT–•fŽļT*…ŊŊ}*m""""""ĸĸŖÁ@›ˆˆˆˆ ÅÚÚڊ­@eEũ††9ŅąĐKqQÅeoo_jûĮĮĮcذaõ8 ×ŅϞİ ą!HTšëh+++ŖC‡JėŅ&"""ĸBaŽ6•eĨ™Ŗ­ĒĒ ;;ģ4ÚDDDDDDDEˆ6 s´‰¨,+Ím0`€m""""*æhQYVš9ÚīöOg MDDDD…Âm"*ËJ3GFŒ‘ÂYĮ‰ˆˆˆ¨Ü‰ŠŒÄŲ“'pįömD?•!!>ŠÉÉl˜2ϞšĒT­Š:?ԃĨUgt´ę MÍßã §NÂīö-D˲īq īq™¤*Ūg}tėÜ~ą*đ}ލhQĄX[[[:uĒTę~&“aÍōĨđ÷Ŋ‰–`ŪŖtkׁšF¨¨Ēņæ”1i)ÉHxūŅ˙ū‹ËŪŪXģl ~láŖFCĨrå|īņúËqûæ ´°ė„ÖÖŧĮåé>{zyáĨ‹a7ÔC~‘ī}.Nöööpvv.ĩũ  Å@›ˆˆˆˆ Ĩ´r´¯{{aæÄņč<ĀLžŽo+UâÍ(ãTTÕ ĸĒéõ`jŅ ΟÃmËôīŅ[œ] ­][ŽüĢW0sÂ8t`‡Õ“Ļō—ãû|hËF ėÕ›œ]PS*-Ņķ) 9Ú Ķ\ ŋĩ6áĶADDTĖ\÷ĮJģŪ l *īA°āUu)(( 02 WzˆÚ?ÖĪõYōË$„øß.ösøsíjxŸ:žÃGđ†RYČŅ.•@ûÕët ë¤Ī'€*ŧŨáåîY/oįĖķå9įužDTŧŪåhwfKPETߨ ũ ?o¯<íģ>Wņĸ"”žS)Öķ(Š|抈9ÚDDDDD ģÕīĘeD?yÍę5ФUkč7jœĢÜ˄¸qņKœLíaĐßX3m22^§ĄöõqįęœŪī‚úFÆēu“v1mo ×õkr OxūĄw0|öŧ\ö˄X1qd˙<†ļT U5øyyâô~4oĶ–¯ąhä0ŧ|ņĩŦŦŦ,x=÷Û0}ŨFÔūą>.>(áĮöėÄĪPߨ Ō_ŋÆú™ĶâU4ĢĄēŽ.Ž_8÷Ûā¸ō1tīoėXēĻí;ĀĪë˛|-ļŊŊ=œKm˙RËŅ&"""ĸō‹9ÚE+đÆu¸ī܆†M›aÂō•PQUÃÛ7oāļy<ށö÷RôlˇoŪ`˂9øļR%ĖŲ˛Ōę!++ 6Ŧ…ĮŅ#lČĸ ´Ûĩ‡ëú5š†û]ņ¡•*ĄY›vpßšMnŸ}kW#úI$ϝ߄FÍM@ Œīø\ėŸĮūPįÜ\ņōÅ ,vūŌę‰/P–…ĶŽû0zÁĖŲ˛;—-†ĪŲĶXsä/ąˇÍâƎG—ūvbāžbŌ8Ŧ™6+’ëŲŊ{S˙XĒÕĢ—ØÖe×Ņ&"""ĸr‡ëh­s÷ãÛJ•0záqčđˇ•*áˇņ“ ĨĢ‹ ‡Ũ˛ƒ<īËHxū6ŋ4EEEąÍ5Pߨ |=/Ęm÷ķōDŗļPūîģ\ûÔmĐ=ƒlPūî;˜uėH~ųĀKˆ)ŋ—ã]ߨ WŽƒįŧ¤Ĩ$Ãįėiĩh)WNŗF ?)ûķsgäöąúĩ?~jaéõōĖ7¯¨˜ŖMDDDDô•‹}€:?֗ë‰Ė ĸ›ļ2‡ĮŅ#ˆ•Éđ0čo ärĘ56ųŪ§N°1‹Đ‡ÃĮž?ĮàŋŅuĀĀ<Ëwˇ {YŽXYž?} Ų?qĮįŠ\9ŗŽpĮį ĻÛũŠF&?Ŗiks67ũä°î'áí›7øļR%\û  ~ûæø,}üSé` MDDDD…Âíĸ•ūú5ž­T)ĪĪTĢh➋ÁT•jÕr•ĢZŊ˛¨í†ßđ8Uĩ|âøčh¸nX‹Ā×ÄÃĩtuĄöîæha™ŨÃ}ö +o\CākbPüŋISōœ^Äd÷„ßņš’+xĪ‘’”$ėåķ\UtĖŅ&"""ĸr‡9ÚEëÛJ•ō]Ę)åevā¤YŊ†8\9>útëčɕ‹}*cC1Í5ĐČägqöqßKøš}‡<_ŠdeeaՔ‰x.‹BÁCņS 3ÔŦS*Ēj¸vî v,]”+ØnaŲ /^ Ä߁7ŽÁĪë2VOžˆuĮOå94=găŨGXŲöã úæhQšÃíĸUˇACüûč!ŌR’s}ö ā.”ŋûZēēâŒŌĄš‚ŧ‡AlČb`ÖąSö˛kī–âjņ.ßúC˙>zˆč'‘hĶĩ;zu€~ŖÆbžũ“wŗ‡ŋNIßŗ Į÷ėƒįVŋtÆčKĐą¯-ŌR’!ûįqžuÔūņG@ˆßí\ŸÅĘdØšl1o\įMCŲČŅf MDDDDTŒâž#>::ן„įĪÖ=‘ūú5v¯X&×ŗ}Úud˙B­Š<ŽÛæâ0äˇoŪĀmķÄĘd0ĩčĀF"*#h•CņĪ㑖šÆ†(&Y™Yxõ l "ĸvę(ôîß›VmaMeĘŲŋÎŖũ/íäFsü>nēˇëáã‡BIIŠLocãF8pĘđ4ęYš´o_÷ÃĄsŽrۆŠžŋôGFFF™kc"ĸŠ"66>Dhh(ęׯĪ!ĸr¯Ėįh?zކ ä‚lhŲĻ…P—¤øįņps9‚MĢļbߎũxõ OŖžņIĸ”FŨI IDATbwãŠ/~é.ŋŦ„ĸD-Z™âo˙ 6PØ÷מ<ˇĢĢĢ1'ūqûēžF=Cā lZĩÁ!ëg"*˙Š#G{Ũēu ˛‰¨H0Gģž¯]O"ĸ•™•+GōĪãō˙(ŽĒYUĢUÍ÷XjUÔĄ¤ôųŠ\žįŊ°eÍv ü+~ne‚Øčį˜1~BƒCá÷čŸh*V1ŅąyÎMđcC}<•ņeOQČĢĮúQh¸øũBų{…ã‡N đNž¯õ= ųËæŽDcãFø7â_ôü+Ī\†•[–ŖNŨÚl0ĸrîk[GûßGũī@īĨ¨ũc}ŽY]$ŋLBFÚëO–ĢR­ž­T i)ÉHKNAÕ5Jėū§ŋ~”¤$ņ¨`˜Ŗ]U5Ģĸ[īÎpč?ļûâĮúøą~že?5ŅR^9–qĪ_āöu?šmņq/ōÜīö?á|d'Ôßå€@×^ŅŠEW>ÍTj Je6LqŊÜx‹ŠŖgbŗ ķ?ŸšŪ¤gāIDļīßE‰"ą{Ë^ė=˛ŠE$&$ââéK>n(Œ¨œûZÖŅžwËûÖŽBŦL&xiVCŸaÃs­oú‰qq…Z;›JÛæđ9{ú“åfn܊†M›áÂáC8žg'Öų ZēēÅöĖ}#QDŖæŲ#wũŊ/cĮŌEâ9PÁ0Gģ€ė†@×^áíqn.‡ņ 8 U550vę(4lÜā‹Ž˙<ˇoøËm{õ*9Ξ?5iį­.č7ČVĘŽ(QÄQ7>ÍD푏"0uĖLŦŲžRœį>­U[3qô‘ŠĘwhŅĘTn4RF:—L#ĸōAöĪcŦ™æU5 ;z O#ūÁ؃ûáŧĘ ßVĒķ.Ũ Οc҈ač1x(írÆę×ūPQUË÷ķę:ē%rƒūÆĒÉđûėyâļ*šÕаi3TVUå*gĘÍō^U5ĢĸwŋžčŨ¯'āIÄŋp1-Wsa5llkÆæ§QΰiՖ\e'Ī™ˇ}‡ąxæ2ÄDĮBĸ¨ˆ†`øøaVJ%"¯ šŌRĶØĢ] Aö‡3ŧŅŦ­­­N:UĄ¯ņœÛdeeaĘëPˇAÃ˙ūíØ´L-Úcúoũāžs›h˙_V˛˛˛øp”Į@ÛļąõPF^ĪĪO-ĖđS 3Ū¤B˛ˇˇ‡ŗŗsŠí_.r´ƒCđcCũ\ÁEēĩŅ­W\ķēŽŽŊJfä’ĸDvCĀnčqÛMŸ[˜:z&6ėūZ5´øTSąŠSˇ6‡Á¸š‘ÜöGÂKėw€A6_GŽv|töü'ĩĖ=A[Íj°°îˆĐH~™„´äÜŊv@v>÷ĩsgđS 3TŅŦx™đ7.žGô“'PQUE#“ŸsO~Ū^¨ĸЉ*šÕpņˆĒÖ¨6]ēĄŠf5deeáŪ-_< ú/^ Šf5č7jŒ&­ZįĘū÷ŅCøy{áe ÔkÔ­~éŒĐ€ģbĐö~Pw×į*â˙Ųģûøšë˙ãŲ˜ÍČ1ļ1lYŽå:×W1ĨMųŽ$BĨTúĻTJHB’ I“Bh.’\å"r5Ú˛llfŒ™ŲŒ1m?ŋ?ÎŲigX_įœ=īˇÛšeŸķš|ŋ?įt^Ÿ÷ûõ~˙}õ*žū4m×ūē-ģbYZĘ9vmü…“ĮŽâėâÂũÍ[˜ģ~į¤÷nŲDLd˙—MõZĩiŲĨ%‰>ÂģwƖm€ÖŨ{p&!č?暟,ŨS–Ž™sOU¯U›ß~ū‰Ø¨Ã¸•¯@Ķví-Ū×öD9Ú7!tūˇt ęBģÎģāœO9Į~'lؚ­ß-Ú4#úp4ŋũē› >=ôM#ˇM›­ø9lCž@;33“Ũ;÷0nĸæxžöíŪĪ” ĶørÉ=8šŽâŖ]ŊVmØĪĸéōøŗ#  ũŸmūwԁŦ˙ۘJxøĀ~ŽĮĄruoÜĘW`Ī–Í|öΛæĀ*%)‰ĩ߆RˇÉėXŋÎ|^Žnnæ@;-åS_zøč#¸{zâęVŽ­aĢX1˙ ^> OoŨä7iĪ–Í|ņŪÛdggã]Ģ6Š)įXûm(Í;ufØøˇÍBŌRΙëŅĶۇ’ŽŽlXž”ĩ߆ōúŦO9~œ/žī×­DėũÖŨ{ũĮAsŽvN Ŋ}ŨLũ¯5INLdíˇĄÔkւ‘'åš§ĒT÷áėéDÎ'ÁĨ\9’Yĩp>!c_+0ƀ=QŽöMúđŨéÔō÷˓'ųûŽ=üžcOĀ÷v9sę KC—ķĐ#Ũō÷įSÎŗfÅ:>ú|ĒžiäļęÔ­ŸĪœGtTŒy@Ā“>áŅūh~į[ "<’OĻÎaö‚ė/fͧOpī;ž"Ōg(íģ´åŠgrōÄ)ƎĮˆ1ĪĐē}+~Ûļ›ßŸÉÄéo:@¤ˆˆü{Ŋ…ph÷.6­ümkWа1ĩëÕĮŋa#jÖ ĖĶ’Ü´]{|ëø3Ļī#t}ŧ?Ŋ? ķŧ?}ëŋxxUãÕéŗ(īáĀęĐ¯ų~ÎlΜÎāW_7ī'|įvZvíÎÃOũ‡Äøxę6nĘgīN áØQ^ņ‰šÅōĘåËĖ÷ ‡v˙FlÔa|ũˆ:L茏¨]¯>cĻLÃŲŕô´TĻŋō1‘yŌúâŊw9yė(ĪŊ=‘f;›Īuō Ŗ˜úō LũžXpŊgë&\ŨĘY|ĪŖĒĩëÕˇ$$đŲ;oâîY%OũŽ[ʡŸĖÄŗēų^øô­7H<Į˜)Ņ e+S}īāŖWÆ:s:#&L¤ē_-&΀‘ŖÍ) ųÅGaŪīQĩFMÆLžFy˛ŗŗYˇ$”īįĖfÉė™üįåWÍëÚũí‚æÉ_ϤŖ# ĮŽ2qÄ0–}ū™]ÚÖĀęí€@zõ â­ąī’q)ƒĒÕĒģ‡;ŗÎ(`\HKįų!cČ2å8ŧ6ú Ā8@ĪđŸ1¯ˇ~õž™÷-OöĖ3'RĩZ>™:‡Ũ;Ŗ?;p$Ÿ}ķ •ĒTĸkPĻŧ= iéxûV#áÄ)Ž^ÉdÔ+#4X’ÜvSgOâ­WŪ%;+›ĖĖL7kÄāáƒŦō|Ã÷bÚďķ,{˛ˇq´éÚū~ŒŸôēUoęųT˛˛ŗyq˜åŪŊûõē+e˜ôŸMJ&|ßAâ]OƟ$|ß!’ĪžŗŠ@{ˆ_™÷éW8Írbč¨Á¸ēš˛{įî_ŽāÁ¨čáÎKVážĐūƒúęC-bÊCŽļĢ[9ۚû%ŋ,_ÆļuĢ9´û7íūÍü^›‡zđČāĄæÖCK~^úŲŲŲ<;~‚9čüQû÷ŗmíj:ĖÜRi0øĪ˯RĒtis̞¯•Ēzåé\ĒtišwęLäŪßIKI1ūļ]ú#&L4ˇžģē•cØøˇÛ˙ą<ÁáĄŨŋŅá‘GÍA6[Fû ŽĪß{‡ũÛļæyĪŪ}ûÉĖBßkķPĪBíÍĢ~āīĢW Ôo÷ūÁėŲŧ‰_–/Ĩ÷ā§I<ĮáûéđČŖæ  AËVt~Ŧ/÷aǰõĻ{ę™×Į›i0čüá;ļŗ5lUžĨJ—6Ų9õ\¯ysvũ˛”¤¤<įmO”Ŗ}žzf `ėĸq)ƒķ)Џ{T(´¯Ŧ›+ –}qÃũvíŲ™Ž= ~Œ;œ‘l%÷ö­nž,33“ä¤s¸–uũŸb)Ēĩ|Y´b>ŌŌq*åhÕ-Ų ×+0×Ŋ5kŨž­Ûˇ˛ĒsÚąŲ<Đ]ƒÆõØux›ų;§OđŖ<ôH7̝]į ¤øúįšŦąŒEäß).ķh;ģ¸ŌkPŊ…x<ŽŖFąwûŨÂÚoC‰‰Œ`ÜŦ9…Ί|,ę0Žnåō Ļ–ãūæ-L-ŌQæāËĶÛ§@āŪĩo?ūžz•3 'H:y’„cGŲˇÍ˜~1-0N-æéíS pĒäå•gŲŅ?#ãāmÛ×­ÉŗnJR’y_Å)Đ~î퉔͕˙œ[šōå Ũ.'—údė1NÆËķžSigŌĶRI<Gė_QÔmō@}<ųâËE:ל´îۃŒ‹é|úÖxíūŊ[6”f\LĮÉŲr‹wÎtM92ķ¨ķKIJbŅô ßšŨ<*uN^un_Í,tĒ܃håĖ ž%l[ÂVY\?=-­XŨĪ5ī üWŖŽ§šré?īëŦ“bh],ÔoQ]šœAI'ËŨúsî‰ķĻ&€Å€â m+u˜‰#žáĄylč°ī;ģ¸ōČā§9´û7N?^č~J•v&=5Õâ{—.^4_å Ũ>;;›_~Äãqô4˜û›5§’—ŽnåØžnMž¯¤ŖgN&XÜOJRUŧŊ(WÁtŋ0iĒæûū9ģ¸âęv™ŲkÖ_wŊœž-<Āȸ˜^¤‘ŪK::‘y9Ãâ{éχ6÷Úiwp[sŠ@DDė]ff&ģwl';+K…!r uĩįëĢäåĀŽ?såōe‹ëœ;h Ļ ĪŅöõ -å‰Įã ŧĩ^ž5 Ũ>>ú ĮŽŌæĄžôü4~uÍ­– ĮŽæYˇNŊú¤$%™[Ŧs$3bPͯ‘{/pŧ˜ČæO~ßÜ%ZŽĪģVmŌĶR‰:\āŊuKB 9+—/SÕTĮG#˙(°Ū×ͧŌļeĄ÷™Ĩ{ĘR=Ūŋ˙†÷TqrWˇ0`€ģmąÛāúĢšsøpĩjŖŖuäšė=GÛŲŕvAs&!IŖ†į <˙žz•=[6ķ͌é”*]š–]ōö O6ām{Ԝ7ų}2.Ļ›—īŪ¸}ÛļRˇÉ×í˛œ“¯}611ĪōبÃlZųCže]úöÃ`0đÅûī˜s­Ī$$0{Âø<ëųÕ Ä×?€M+ rßķōŒ‹é,šū!ÛÖŽžîoö(ålɉ‰…žr×]n9Ŗv/šūažu"÷íáû9ŗ‰:LŠŌĨņõ z­ÚlZųCž$ņŅGØŗy# ZļÎS抚Œä—sO͟ü^žā|÷Æ „īÜNĶöō¤ WĘŅš –-ū†ßy‡+WūųŌ÷Qk9ŊkĒ!ąuÅ!Gû‰Ņ/råōeļ­]ÍÄĪPŌҎ 8Ÿ”Dvv6ŽnåxyÚ sPãîé‰[ų l[ģšmkW›§q ~~ K>™ÁØ~}¨]¯>)g“ˆ:ŒWš<ûæ„랃§ˇ Zļ&|įvŪ6OoR’’8r(œæē°míjswa¯5úß7™÷Á{ŧđh$'&R¯Y âķí÷Ųņ˜üâ(&??_˙ĘWôāČĄƒ¤§Ĩüü‹mŲŗ‰#žšîûŊ5OĶ•˙ĄENũŽéĶ›€†HOKåČĄƒ”÷đ`HŽŠÛž›đ.īÁø'ņoØ€¨ûpĢPāŅ/PŠĒƒīįĖæû9ŗ™ēdyc4lDđķcņ/õy„Úõęsöt"ņŅG¨^Ģ6Oqp5{Ĩm‘Û ĪiĶž#ŋŦ[ËO?ŽâÔÉž\ō=ž5īîTh‹wėeJpīĒ!=´ëg0úúxÚ?ܛƒ;wpæä ŌΝŖvŊøÕ ¤e—Žrk_Ÿõ)›V­ ķōeÜLŖUwíۏ€ ŲųķOœ:~Oo:<ō(-ģtË3(U×Į˜HËíųISØŧjG…“‘žŽ¯˙yųUÜ==ŠTÕ+Ī h-ģtÃŋA#ūļƒôÔTjRˇqSžjŨŒ’ŽNyø÷ŋ^Ėļuk‰:LFz:-ģvŖM÷Å*ČnÜĻ-î•o<šŋir˙†č=x(ÎŽ˙ÔSūú-īáÁ€‘ŖiÛŗWžûÃĶۇɋŋcëꉉ4ŽüūØ3ĪŌááŪæõĘ{x0nÖvoÜ@vv6ƒę~ĩč=xhžîrŽšmŨÎ$$ā]Ģ6ŨīO‹.ŨōŒ€_Ø=eé:äÖS -""vŠ’§'Áƒ‡ę>.r ‡y´søÕ įnāM­ëéícnĖ­z­Ú7 `sĻņ˛đwz´íSāŊ^ƒūÉ#ŨēúGĮņØĐgÍ]šáŸ)ģkČŅV -""""EbĘŅž#:vėH9WWvoÜ ‚ˇCë—.ĄiË֔uueߖM*;ĩé‡ĨtčĐ?ŋ;“ m‘øtÖLzôę…O*yyŠ@ėDLdëž åÛ°ĩ¤$Ÿå…ĄOãUĢ6•ĒĒŽí­ž×.e1KQ‹ļˆˆˆˆɝĖŅhŅĸīM|¯ŊĖ™„U€ˆ:Ė'ãĮņŪô™xUĢFŊ†5öfž6Vulgõž3§9u"„ņ‰&âĐ!Ν;G`` Mš4ÁßߟŽ;lE‘+N-Ú"""""& lܸ‘Ÿ~ú‰_~ų…ō*đ`ģöÔŽČ#ũPŗVmʔ)c×RĻLęP'  Ā{ŌŌ8Å_F˛{ī^ĻNJvv6:uĸ[ˇntėØQ]ĪEū ´EDDD¤Hė-G;++‹UĢVņņĮC§ÎiŲļcߜ@E ]ģíAY77š4kF“fÍĖËNž8Áö­[Xļ|9Ī>û,­[ˇfäȑ˜MlŽr´EDDDÄæØKŽvVVĶĻMãŗĪ>#āžû6j4­ÚļÃÁĄxūDŽZ­ũ>IŋO’™™ÉæŸ×ķūû“xá…xá…6l˜n~ą ĘŅ)F”Ŗ}Ë)GûnŧähĮÅÅҎo_||}yõÍ TōôTÅVVĮb˜đÚ8K˛hŅ"u)š‰æŅ‘b%""‚öíÛķĖs#ųhÎ\Ų7āSϯž_JÛhŅĸ§OŸVĄˆÜ€m)’   ›Nڝ6mCž}–nAŊT™E<øi:wíÆÜšsUbÕBBBîęö pW -""""Ebë9ÚK–,ĄĪ€`Uäŋđø“OņŲgŸŠ ÄĒYCŽļm)’cĮŽMļåķŋrå ëÂ~TEū a+~P×qązƒ ēĢÛ6ėĸm)vf~4Čˆ?TE°ã×­,ũvą Bä&(Đ‘"ąõm€ˇŪ˙€ÁO táWĒĐČĘĘbú”ÉŒ{ų%>šû… DŦžr´EDDDÄæØÃ<Ú ›ˇ`Á÷ËųqÅtoߖ˖’••ĨĘÍ%33“…ķž¤CËæüu˜¯–­ VĀ}*ązšG[DD¤Ņ<ÚˇœæŅž[oãķh—(Q‚đ¸æŋüž›īžZĀÁđüįéĄt ÂĮˇFą­ßȈ?XƒE‹hŨļ-ũĄvŽģO5Ž]ģĻ‚HáâT""""Rœ5|  hƑ?#ųiÕJ‚{ƒƒõĸcį.4lŌûũ؜™™ÉŪŨģXŋv-ë׎ĄL™2tíÄŧī—QÍĮG7ˆČŋpWž1VˆāBÆ•žØŊ’†{ø;û˙lęœ]K—$ũōß6sžN% dūm3į[ĸD ›k(åčĀ•ĢļĶŌŲŠ$4Н/ ‘Û(((¨kXX˜Ũ]WíûęRûžēŒ÷_ūú3’íáÍq¯r">žú Ō°Ię5h@ÃÆM¨čáaŗ×™ĪĄƒ9°wöí%ō?¸īūûiĶĄ#Ÿ/ūNÁĩØŧ,Xp×ļ0`€û] ´/d\aHg?Ũb÷æmˆąš{ŨÖÎYįĢsļtž"r{ŲCŽöÔš¯.uîĢːQĪ“~áá{÷p8âũ5¯ŧđuŠ””.¤Ĩ‘xę$Ig’ˆ‹=FÕjÕđöņĨjuoŧŧŊŠ˙@3^žđ•<=u‹]ŗ†y´h‹ˆˆˆˆQ5‹]Ŧ3339Įšäŗ$NäBÚŌ/\`˖-\ŧp‹ééœL8aÅ/33“ŗII7<^å*Up0¸Įā@eOOÜîŊײeņ¨ėIåjÕŠáīL÷Ē^T¨X‘ęŪ>ôS_änҧODDDDŠÄ^s´o'''üęÔÁ¯N†Č]b 9ښG[DDDDФ8äh‹ˆí˛†mÚ""""R$ĻmĢd 9Ú ´EDDDDDDn!ÚBÜø IDAT""""R$AAA]U "b­BBBîęöĘŅ‘"SŽļˆX3åh‹ˆˆˆˆÍQŽļˆX3åh‹ˆˆˆˆˆˆØÚ""""R$ĘŅkĻmą9ĘŅkĻmą9ĘŅkf 9ÚļPPķ?]HtT45jÕ`č¨Áē{¤Xē–Î¤ņ“9‹ÁÁ^}zŌP_Ģ>įãąņl\ˇO=3ĐęËxũę ,ųę{23¯0pČz¤›n>ši6ŅĸŨģ/FŽÁČą#øuĶv՚[ĪC›­ųn]( –}ÎÁ}Y˛pŠUžëáˆ(úufʄidggs8"ĘęËwYčŦYņ3æ}ÄⰅ|öÍ'lÛ´ŨjËXDänQŽļˆX3åhߤ{ËßKÕjU¨Z­Šî)ļļløoßęæÖU'''ÆŊû* ?_d•į[Ûŋ_¯œĪė…3lĻEø›yß2}îĘēšPÖ͕‰Ŋ͒…ß“•­›PDÄD9Ú"bÍŦ!GÛÁ^ ÷pDą1q¸WŦ@ũ&õprr*tŨķ)į‰<ø'™™WŠ[ī>*WŠtËÎãxl=ō,+ëæJƒÆõˆ´ēûÅā`Āā`°Š2žŊpFs68p÷pįlRō-ũn(ĸŖbˆŽŠĄ–ŋĩüũT "vĔŖ­ŧąJĘŅž_üŅąŧ1f5jųRŲŗá‘ŧõĘģŧ<ūE:vkŸgŨ“'N1vÄ8î-_ŽÚĩ0 |5wģwȓKēâģU|3ī[NŸ:CZžŧđÚH7kÄɧxmôœŒ?‰S)'z<Ō‘c‡›ƒ÷qŖĮXŋ.™™™Ėšō)íē´eø‹Ī8īõĢ7đųĖy´|°9NNNüžcīŽ{Wˇ˛|šdŽ>-ÂҘXũ ,ô'ú¯=˜š,õš9}ę įSÎ+Čž ûvīįŨq“hų`sŽĮÆSÖ͕ĒÕĒōËēM|4wŠ IDDDŠģ ´Ī§œg˰WøhîjÔō5/9v8ÃŽÄÕՅZ5ÍķŖzÁ˛Īķ´vd8!}†Ōą{ķîŪũĻ×c=yŦKž\2Įŧ~ÕjUX´b>O÷Îď&˜ˆgge3zČKŒ3Œmš™÷•Í䡧1˙Ķ… ‘÷)ÉĮ“fąüį%8—q6/;ÅûãõÃTū‘ûūČáz^ōŋɸ”Ác&0~Ōë*Œ›Đ¸Y#^÷žî?œŠs&Ņĩggķ{īŸÂÆŸ6xā)"ļ)((¨kXXXąģî>ÕhŌŧ_.ųŪâû{wũÆĶũ§WŸžŧķáG<Ũ˙qöîú­ā˙ŋ˖ĨaĶxzä(ę5l¤›Jä aÁ‚wm{ģ›GûëĪCyfô^}ë%~\ļē@ž§sgŽEĮæYčĪĢoŊ¤OŠČ] ˛‡ ÉĀ!hиž ¤'ČhßĨ-‡˙ˆRáˆØ åh]¯>}ķŧę5lĎ­[éû‡ėW‰ÜBĘŅžÅūäht,Û ™üHTLŌsĻÎÎ_w‘•mn-<Oī~ØžßS}Ú˙Ų<ī…Îûļ@ëtDx$҇Ŗ7úBĀ‹‰Í“ŗøŅÜ)ĖøāŽEĮâęæJM?_hÕ´ĀU)Ū˛ŗ˛ ä[Z&˙{mŠWŠÜK=/ ÷ÜŖ‚ą#ĘŅ.ēœîÜrZĀŋüd3į-P!‰Ü"ĘŅž ?îîOÕęU-ž?rėˆ<ö m:´bqØÂ<-ÛcGŒŗ¸}Y7WÔå÷{x USלįHTLÖ.§RNtz¨í:ˇ-ô\Ũ=*äųÛÛˇē91;+›#QŅŦ]ų›ŪĘÔO'éĶ"Ü[ūŪhĀ8.Aî”ųß$'%3zČKŧôÆķ4nĻŽ|""rû4iŪ÷ЉųĢ`ŸS ėÛũ'O$ā\Ļ uîģ&͚cpČûķũ¯?#Ųģké.āW§ÍZĩÆĩlŲû;ĮÎ_ˇr>%ŋ:uhĶĄãu ‘˙]ÚvhÅī;÷2ō&‚ŽĶ§Î`0< HĮ<€i?æVMYē‚ƒ/°Nëö-™ņÁ'7Ŋīđ}‡ĖģÁÁ@@ ?ūŒō§OŅ@LƒZņËēMíŋîâY ėÉŋ ˛‡ Éëīžĸ [Dä:ŠkŽö-˙˙ÎŲŗœ?ž;tĖŗ|ę;o:˙K TĒ\™S Ôk؈/–|oß|y ?.[Šs™2¸–-˙ÄD\˖eō'ŸŌōÁ|fNū€ųsfcppĀŊbEÎ$&RÉĶ“fÎĻaS=ŦûŖí[Ŧwŋ‡Ųˇ{?Û7ī°øzÆŸ˜˙v*åȅ éÖ ˙-kVŽ+ô5jų’ÍąčX~ ûŲâüū¸{¸ķÅŦųŪËĖĖä‹Yķ9yâ”yYlL,ķ?]X oûBZ:ņĻ‘{Ez¤ëÃ6œ”l^ļ,ôÔÕ=r 7â5{R ;lŲ2.eÜņsš6ņc6ū´0ö8aėģŽ0ļzä˰û~šSŠsŽöšŗg [žÔâkįÖ­…nˇw×oæ×Î_ˇōŨ× yvāœyîĨąæõÖŽ\Ačü/éü$;#ŖXģũ7vFFŅĢO_ØĪڕ+ØųëV~\ļ”g_ÃļƒŦ˙íw–­˙ƒƒ_Íŧŋ¯ŋøœųsfĶ7øIļė?h^ĪŲš Ŗ‡ü‡äŗguC‹ŨQŽöMXúĢ–­6˙œŊ›˙~éō´Ī^8ƒ7ÆL`Ūė¯hФ>`ŊûBZ:ΌbŪîŪō÷Ōš{žė=˜f-›’q)ƒ}ģ÷͞m z<Ō÷ĮOĄ}—ļ<˙ÚČį4hؓ<Ų{0‡ (47öí)ãųäÃ9<ÖĨŋyĘŽãąņDGÅĐwāŖyĻr08q)ƒĮ ĻYËĻ8—q&9)™đ}‡xõí—-æ;Jņã\ƙņ“Æ1lāHšĩlĘéÄ3\HKgæŧiVyžá‘L~ÛxnW¯d’pâ”ųŗ[ÛßĪęFōŪ˙û2.eđÖ+īxīXt,š5ŧãŸÅyŸ~ŃZĶą[{îûƒo~OÕęU ôgˆ_YúÍrÚthe5c9„Î˙–ĨßüĀ…´ ‹‰5§Ãlüi3ŗĻ|ʅ´ ”usÍ3}ĸˆØĻâœŖ{4†ņ/)ōvO÷ÜâōAĪ ÃˇfMķß—hÕļ/žū_sËĩs™2„ ÁË–r&1Ņx1Æą‡6mjîNîW§ī~øé.•Āü9ŗņĢS‡×Ū~'Īz˙}ī}žî˙8+ŋ[ÂĶ#GéĻģĸí›Đ'øQú?Z¤`äŖšSHNJ6âŨīŠžģ^?õĖ@ēôėL|l<#ĮŋŠŌ-Ú4cGÄæëŽcp0đük#:j0‡#ĸČÎĘæĄGē ¨O‚úô 33“ÃqõJĻ)¨z]ƒ\I›5bqØB"Â#)ëVļ@7rkØ .‹Vˎ™˛íŨīa‹ƒ ŪMĢ6-3įĐŽķƒ|ˇ.”€Ā:ĻfŠßø~Ģęâüj×!õüyj7ĀoĶĄ#m:täD\ģwngĮ–-üēi##=Éâ˙IŊ ü4Áƒ‡X<žŖF;¤mą9šGûÖ9“xĀ<%ג¯ŋĸšŸ~õužŠŧū?@vļ1÷zį¯[ŲûÛoŒ~õ5ĒųøPÍĮ‡>O díĘŧūÂhvīØAī~ũˆ;C/¯<ĮMŋp¯ŋ˜KÃĻā^ą­*BėŠ5ähßŖjšŗ2.]bũę0æĪ™““]ƒz™ß욝;wÆĨKĖūpĒ)07†vh˙~æĪ™MØōĨyÖÍEŧēŽeËŌžKWvlŨÂ/ëÖæYoæäøbÖLN%œPeˆÜjŅģ˛ã‡ī˜ÜۚOņšjé–9­"¸;4vŅ5đŠfqš““oMžŠ{Ŋôî7€Đų_Ō÷1ZļmĮåŒKüö#Õŧ}prrâr†qēÉāÁCXö#^}… k×RÍۇĮãøuã/4lڔ6í;đß÷&sä/^>ŒVmÛQÍĮ‡?âЁũ4kŨ†Ū÷WåˆŨQŽļˆˆČ-ÖęŅ~Ėž6Õ*ĪmņŽŊL î]Bĩ$ļŽ¸æh÷ęĶߚ…ĪøQĄbEzõéKÃĻüķÔļ]nÛŽeŨđõĢIģÎ]ÍA6˜×˙Ku6™0ĢäéÉķ¯ŽŖkĪ |6‡äŗIĻí˲`érBĖãЁėŨõî+2öÍ ô hîvî^ą"‹\Ã˛ĐoØĩc;öėĄ’§'oŧ?‰Ū÷ĪĶ=]Ä^XCŽv‰WBW\{ĸU“;ūCcHg?Ũb÷æmˆąš{ŨÖÎYįĢsļtžwú˙k ´Ĩ¸švíZ;`ŗ­ž‰%S—é́O5Ž]Sį‘ëˆSŽļˆˆˆˆˆˆČ-¤@[DDDDŠ$((¨ĢJADŦUHHČ]Ũ~Ā€î ´EDDD¤H4ļˆX3kČŅV -""""EbšG[DÄ*im;Ŗ@[DDDDŠD9Ú"b͔Ŗ-""""6G9Ú"b͔Ŗ-""""6G9Ú"b͔Ŗ-""""""bgh‹ˆˆˆH‘(G[DŦ™r´EDDDÄæ(G[DŦ™r´EDDDÄæ(G[DŦ™r´EDDDDDDėŒm)åh‹ˆ5SŽļˆˆˆˆØ[ĪŅvpp ;+Kų/¤_¸€›[9„X5åh‹ˆˆˆˆÍąõížAŊXģj…*ō_Xŧ`Cžĸ‚ĢĻm‘;ėųŅŖørÖ,ūú3R…Qģwl'lų2†>ũ´ Cäh‹ˆˆˆH‘ØzŽvģvíøüķšûx:“Ūø/?Ž\‰ŋŋŋ EŦšr´EDDDÄæØÃ<Ú]:wfįÎlZģ†ū=ēąvå åmį“™™Éw_/¤w§ö$ÆÅ˛į÷Ũ4lØP#VO9Ú""""bsėem_vlÛÆ§ŗfąũ—ŸéŅĻ% æ|ʉ¸¸b]ŋũÉėiSéŲĻ%ĮūŒdåŠŦ\ņîîîēųÅ&XCŽļƒĒADDDDŠŗöíÛĶž}{ÂÃÃųrŪ|F<L‰{ tęūm:t¤^Æė÷gsff&á{÷°åįõlūųg\\]čͧ;wėĀĪĪO7ˆČŋ @[DDDDŠ$((¨kXX˜Ũ]Wƒ ødÖL>™5“đđp~Xą‚ŪĀņãĮ ŦWŸē Pˇ^}4ÄŊbE›ŊÎS üųĮ!ū8p€ˆđDEFpŊúôęÄ;oüWÁĩØŧ,Xp×ļ0`€ģm){ČŅž™ ģAƒŧķöÛ¤ĻϞcĮöėŲÃúËyįĩWprrĸnŊúxyûāå]jŪ>øÔ¨I%OOĢš†qqœˆ?Ή¸8NÆ'áøqÂ÷īŖTŠR4nܘ–-[ōäã}hÖŦ...ēąÅnXCŽļm)SŽvˇârŊåʕŖGôčŅÃŧ,&&†ČČHbbbˆ‰‰aįÆ_ø+*ŠäsįđõõåŪō¨R͋˛eŨpvqĨŠ—e\]qquĨŠW5ķ~ ÃMį§Ė˙ÎÎĘâĖéDRΟ'ãâE’IK=Oú… œILälRqąĮ¨î퍟ŸĩkÕĸQ`]äaęׯ———nbąkĘŅąA~~~ģX_šr…˜˜Μ9CBBŠŠŠ¤ĻĻ}(œÔÔTΟ?O\ŽÁÖŽ\šÂ™3gnxŧĒUĢâ`ĘwppĀĢZ5*ēģSŽ\9ŧŧŧđ¯áƒ‹‹ ŪŪŪTŽ\???ķú"rįéĶ'""""Eb¯9ÚˇBŠRĨ $00đ_mŸœœĖũ÷ßΉ'(‹üK֐Ŗ­éŊDDDD¤HŠCŽöŨ˛jÕ*NŸ>ÍÖ­[U"˙’æŅ›c/ķh[ŖĪ>û €E‹Š0Dū%kČŅV -""""b’““‰ŠŠ`͚5deeŠPDlÔ]Iü(]˛$ķ6ĨôÅîJ”°š{ŨÉÁ`SįėpĪ=6už%°Ŋ{ĸ¤ÁļĘØŅ`ЗČmĻíÛcãÆŦ[ˇŽ6mÚđŲgŸą}ûö˙š ĢHqTlįŅîũ@}ÕžˆH1ˇ~u]{Š Dlr´o=z˜įŗ~ėąĮ¸xņĸ Eä_°†í¯„ޏöDĢ&Ē šcRSRx¸C;6íŨĄĒģxĮ^Ļ÷.Ą;@lŨĩk×Ú›UˇG‰%¸víš BÄvÅ)G[DDî¸Í~&-õ<[Ų ÂģŖ@[DDî¸e‹ŋāûĐoT"6(((¨ĢJADŦUHHČ]Ũ^ķh‹ˆČ—š’ÂņcąDG&[ŖęŠØåh‹ˆ5Ķ<Ú""RėėŪšƒYķŋ`Ü;9°w EÄÆhmąfÖ0ļƒĒADDî¤6í;ā\Ļ ē?DÆĨK*ą+jŅ‘;*'Č.ėoą~ĘŅkĻmą9ĘŅkĻmą9ĘŅkf 9Ú ´EDDDDDDn!Ú""""R$ĘŅkĻmą9ĘŅkĻmą9ĘŅkĻm;Ŗ@[DDDDŠD9Ú"b͔Ŗ-""""6G9Ú"b͔Ŗ-""""6G9Ú"b͔Ŗ-""""""bgh‹ˆˆˆH‘XCŽvtt4ãÆŋIëöđŠé‡Ģ›%J”°‹`7×âęæF5ÚuîŒO?%99Y šíŦ!GÛAÕ """"Eq7s´;Æč1/ąsû6šuėLŗ‡‚x¤ē7ŽåÜpvqUåX™Œ‹é¤$%‘Ī÷?Žæŋ¯ŊÆĐá#xwü¸¸¸¨€äļ°†í¯„ޏöDĢ&Ē šŖøT#<îDąēæÅ;ö2%¸w ÕžØēk׎ĩ6ßé㎠cĐSOŅm@0]ûö§¤ŖŖ*ÃÆ¤$%ąäĶ™œ9Į†uë¨YŗĻ EėQœēŽ‹ˆˆˆˆÕû~Å ž~z(/LžFĪā§dÛ¨ōŒ˜0‘–ŅŧU+Ž=ĒBģ¤@[DDDDŠäNįh˙ÉŗCŸaôû“ņ̍ °]ûöŖk˙`:wīÎŋU rKimą9w:GûŲ‘# ‚¯€ ß΂íJŪ>ŧ9qĸ Cn)ÍŖ-""""6įNÎŖŊmûŽÆĨũÃŊUđv¨˙ˆŅ|ŽŅČåĶ<Ú"""""×ņÕ7ßĐĻ{ ƒ Õ÷đ ~ËÖ,YēL…!vEļˆˆˆˆɝĖŅŪ˛y ZļRĄÛąÆmÚ˛ü‡TrË(G[DDDDlΝĖŅNLHĀĶÛG…nĮ<ĢW'áDŧ Bnåh‹ˆˆˆˆÍš“9ڗ324•—+īáÁéÄD„Ü2ĘŅ‘bÍŲŕ‹.¨ ÄŽ(Đ‘"šĶķhÛĒøč#>°_!r‡)G[DDDDl͞GÛV…ΜΤQÃU"w˜r´EDDDÄæÜÉm‘ĸRŽļˆˆˆˆˆˆˆqPˆˆˆˆHQu SAÜbûˇũĘžm[IN<…§ˇÛ´åūfÍ ŦwäĐAvūügđŽU‹Nö%>&šŗ§éÚˇŸ RŠŊ,Xp×ļ0`€ģm)åhßZŲŲŲLå%íūJ^^xV÷áĐîߨ´ōÚ=ĖāW_7¯ģ~éw„Îøˇōđõā÷-›Ø´ō<ŧĒ‘q1]ļ֑Ŗ­@[DDDDŠÄ”ŖŨM%qkü¸p‡v˙F΁ƒxüŲæā{ᇓŲļ _˙Ú?ܛ3 ,ųdĩëÕįåi3(Uē4ŲŲŲ,šū!›Vū€ģ§§ Såh‹ˆˆˆˆ{ŋ,_J%//:ĖŧĖ`0đä‹/ãėâʆåKØŧ枺ŗyęŗ)Uē´yŊāŅ/š˙ë @[DDDDŠDķhß:)II¤§ĨRŗîũ †<ī•tt¤vŊú$; ĀņčhJ•.MõZĩ Ŧįë Â1Ņ<Ú""""bs”Ŗ}ëü_v6+[îöíZŽéiŠü_vÎŽŽ×s1­'"šG[DDDDlæŅžuJ:9pæä ‹ī''žÂ`0āęV§ŌΤ$%ņ÷ÕĢÖ;Ÿ”¤Â1QŽļˆˆˆˆH1æVžå=<8rč ŲĻÖí͉:lî*^ˇéüą{WžõR’’ˆ:ŦÂą" ´EDDD¤H”Ŗ]4‡뎸Š>@΁ƒHIJbᇓͭÕĶųâŊwšrų2]ī@›îQŪÃS?āŨģČÎÎ&áØQ>?Ž@.RœYCŽļĻ÷‘"QŽvŅL5Üâō€†7kíCÂąŖlZųŋoŪDooŽåīĢWyxĐ`Zv1ΤæėâĘČw'ņÉøqL}éyķ~ˇi‹[ų *hkČŅ.ņJčŠkO´jĸÚ‘;ǁO5ÂãNĢk^ŧc/S‚{—P틭ģvíZ;`ķ8V‰%øzûn›,§?vī"-å\Ąī앝Āũ͚›˙Ž:ĖģwqædžŪŪ4nũ žŪ>ļûûęUŽüqķIITõ­¯cú>BŠŌÎŧ˙õb›,̧Z7ãÚĩkúp‰ŊˆS‹ļˆˆˆˆČm;ˆžžūםĻkįĪ?ąoÛVŒMŨÆMÍË3.Ļ“vî~ĒĐEŦ„r´EDDD¤H”Ŗ}w¸WödĪæM|=ũCÎ$$}„ã^áīĢWiĶŊ§ Iåh‹ˆˆˆˆ RŽöŨQģ^}zĘęo2ļ˙cæåÎ.Ž„Œ}­Č-č"öJ9Ú""RlujڈäŗgU"rC}줧Ĩ}čĶq+_ZõęSĒti›ž&åh‹QŽļˆˆÜŋėŲ_ėŽYƒĄ‰•Ē ¸ų–ģ›ŪËŋüކ&yšē•ŖQ›U"VL9Ú"""âŧ lö‹;¸ŽÖĀ /Uš™ °Ȳđž0ĮŌFĘŅkf 9Ú ´EDDЎÖĻ@k;Đh ,6ØA°ŧ üd $߯Uíf́%@T!åį‡ąU;åh‹ˆ5ŗ†mu)ž˙‚ōŸĢ0ļtž ŋĮq1ēWnÃ5¸ɅŧwŅôzØÄŨĸũZËõåW8]„}÷üĄ×YgĐÍŊđØąc“nÅáC’ž–Jԁœ9y‚’ŽNTŦėÉ}MšÚ|N´ˆ=4hĐ]Ũ~ذah‹ˆˆOĀT Ë-ŧ߯å ö–šū’+`ô7ũ{@ž ¯đ<ĐĶ„ē˜Ž3œŧ­§“€æ[Ņg›–ų`ėâ đ 0/ßšĩ6=pÉ`~haŊÍ1ļdßLPû_ ‰éœsō“‡[vÛC0v¯Î 's­ëŦ0•5Ļëžīē1+H0ũŨxŲT†7ē>?ŒŨūŊLûqöšÎaėuŽĩđ Đ÷e‡ņLhqüü¸pĢÎįīĢWķ,wvqĨßđįh˙pī<ˡŽūˇōhв•žaDŠ9u)žz`ė.žPČûYšÆdŒŨĘō­?7×ōÜAve ˆÚđO—ôqĻåår­;Îd_ÉÜĩVŠųÎë- ØĐļ7ŊZũ š?Ķš\ŋéaBhŽsVßZX?Õtîís]ßjĶŁÎĻ÷%oī€qĀ—[Ú;į*×Iχō]_ ×WÎô@b|ŽsžßTånpŊƒLåŧ÷ëĨš‚÷<ŠCŽöæU+XöÅgxÕ¨É “ĻōŅŌ•L]˛œgß|×rn,˜ú{ļü3Üáû™÷Á{\LKÕˇ‹Č]Ļmš[ú o°N) ˙βėåoŸƒą5uyžå쀝€üAZĀŌüMÍķ-ĖtŧáųđT͞§-ėÝw§.|a nķžĄĻköˡ<ÜÂCŠ8 rŽģ•…ķúđ^Že9ô‹Žo¨iũüåná!›¨ÛĻ珑ĘęˇXäho [EŠŌĨ7kÚ<ˆģ§'•ŧŧhŲĨ¯N˙ƒÁĀęoę›DÄ )G[DDDîޟŗÜœŧĶ:bšõŗqžåÍMÁY}ĶËŌqķnX˜B cwčÜÁėk[]'(˝Y!įķāāŠi äŒs´­MąŋéīS0ū1c>Ö IDAT…mˇ›Ö_•kŲtŒ­Úš\ŧą;÷ ›xđ‘ĀŋŠqāēpS}î3-ŋ҃…ÖĻ 7⇱Ĩ>âŖž–ŠSig‹šØîžžtzŦ/ˇōáûŲžn û~ŨĘŲÄDē>ŪgWķû{ˇlâÔņã”÷đ ^ŗæ4i׃Á`ŪįŠų_âß°ĶŌØļ ¯5č1đ)\Ũʑ–rŽmëÖ}„´sį¨äU ˙†hÖąsž}dggŗ{ãÂwnįJÆejׯO§Gû˛uõTŦė™gJ°´”sl û‘بÃÔ ¤ÃÃŊÍį,b˔Ŗ-"""wˍ~ ÃØ5ÅĶÛGߐ"ˇų˛"""bŸŽP°Å8wđė/°n‚qz,Klî€< cëęÍöŠ-,Îߝe ȋÚWˇ 0ŖkĖŨm{ŖéŧoäyĶšų–'›^–¤š”2Ŋ^6ČäģžÔ"\ŸŸéX§MĮŨnz-Ä8ē×ui,wqΝÆÜ <8 ęfאū#Fu˜ ˗˛aųRĒ×ĒM@ÃF4l\`ÔņŽ}ûQŨ¯“F gĀČŅ´îŪÃøTiŨÖŋ„Ļí;0bÂDsëķęĐ¯ų~Îl–ņũŸmŪOäŪßyjĖXÚöėEâņ8ÜĘWāũQ#(éčÄû‹ãVž_ŊʤQÃŲļv5ũŸ…Ģ[96¯ZÁžÍ›xh@°yŸéiŠL~ai)įĖĮøûęU>đ%ykî—æ :ņxG cö„ņL\°HߐbĶBBBX°`Á]Û^9Ú"""Å׌ŖUį×ã€\/ZXž?ø9Œ­ŖÉųÖ§)ØÍ͞vų–Õą°ß@ŒŖlīĘ÷` ÕÂö9ZķĪ(äšU.$¨tČ˙BŪAĖōoŸ;š°>ß:.ķŌ¯7āÚ^Ķ~Ū5=°¸b!Īy`Iģ|eúŧéa„Ĩķ-EŅĻų*Ė ģ˛Pr´Ë{xđî‚o0r4žūÄGaũ÷KøxÜXžëŅ…%ŗgråōåëî#'Ī{Č̝įéâŨ3ø)ŧjÔdĶĘČÎÎ6/¯äåE§GûPŌŅ‘ęĩj“MˇĮûķŸą¯™ƒl€’ŽŽÔmō™ÆsØļv5å=<čûėsæõ\ŨĘņԋy;zėßļ•”¤$24OËĩ§ˇ]īO|ôb"#ô )6M9Ú"""rˇĖ5oßōĪ X=MZ_ NgĩË$n4Įũsƒø§%6Ę´ŋmĀĮĀIĀÍ7§`÷įHŒĶŒÅ™֞üĶEŧ'Ɯ㜠tÆé˛–[ƒ)(ī‰ąÕ5į|:æ ŧ]0vĪ­y§ 5ícÆđK@USųø‘w´đŸLįû™)Xob žŖ0ļ"?åôíĻGˇ$ã¨ė_aėnn0į=Múö\ë61Ÿ—Š Á˜/ßĶTNY׊û(,įŗįö°éú,ļ°—y´K•.M÷ūÁtīLÆÅtĸ rīīüžykŋ åxt4¯~<ĢĐícŖãë`1īšn“ĻŦ˙~ I 'ĖoõZĩķŦc0ĖSˆ%;JŌɓœ=H|ôsWõÜĮjÜĻmž€ fŨĀëÄDFzî\ž÷ããˆû+ ŋ|ۉØåh‹ˆˆČŨ’e ¨0NcĩÂs•_Ä8ot[SPœ3đz vQža „ģōOįUXž×y;0Ņ_4mÁ?Ũ×Sķˆ-0Ž>ŪÖ´,ã ^š rš ×r=qųū+@Į´ŋœsÉmÆAĐZ›ĘpÆ.č>ĻWr!Į[e:¯Ŋ7€›šöŸûú‚(Ø%}€Šlr¯ģã\äWnP÷§MuVØ t.Ļ:é]œ? i)įpvq5įP;ģ¸Ō¨Íƒ4jķ }Ÿ}Ž_zžČŊŋšou7ĩ¸ŋ¯^-øæČ žSSRˁvŠŌÎÖûc÷.žž>•3 æíüęRÉËËÜ%<įŋÎŽúüĮOOK3>áÚû;÷X87wOĪ<­ė"ōī(Đ)ž*cÔĢ“)ĀëoZVcĢč4ōvŨÎ ‚ķÛRČūÚšœįœ`8@\ØČŲŠĀŧė/†Ī›mÉFĶëFž+$pģÎ6Š×)ĢüëÍŊ‰õrŽ5÷_Ö˙J̓Kö,Œ= -C{ĪŅŪųķO|öÎ[„Œ}ÍÜĸœ[ŠŌĨiô0Gä|RRĄû)Uē4_ŊjņŊœųļ+Vöŧn°?ë×p*íˍ‰Pģ^=sōå_ĖåČĄƒ¸•¯@IGG’OØGūîíŽnnŒ™<­@ ēˆŊPŽļˆˆˆÜ-îķoĢ÷mLÁm9Œ-ĪģTDvm6–ķ¸]0ö2ŊŪÆöžŖ]ķ>cˇé ˗(Ÿ:~€{ķFžg?uīįxô‘<ƒ‘q„đ;ˇãęVwĪÂíČŊ{¸rų2=ŸDĶvíķäiįt˙ûj& QāX{ˇl*pN{ļl.pŧíëÖđŅ+/™÷-bĢŦ!G[ļˆˆHņą9'ØēˆąĨv×o™û‡å–ų‹Ü¸Į@NŽļŨĒäåEģ ‡I8v”‰#ža˙ļ_ɸ˜Ā™„–1—uß~CõZĩņoĐ(ĪļĮŖž–ú˙íŨ\TeŪ˙ņ÷IRTė˝@h”ĻTfjZf?$ĐĖÛPI ‰ļ˛ōÖÍŌRYŗnËęҚéúŖ2ŗõGTū@tw‘,5 4]sŅdÍJ ×1G–1'Cqč|˙đfÖß ÃĖđzūw9s]‡ĪaæÁ‡ë|ŽKåååęųĐ (+Ķû¯Mpŋŋŧŧ\ ߞ&{Q‘âû=xÁëøMO&ÖģļūpČį‹hÛĻ“5ڎŸ–$õyl°~+/×;˙;NE{-*//W~^ŽŊûöiīmßõN…EFjՒEúvĶūŸöĶģôɌiÚũŨv5‹ŽæŋF6⋍+éäĘáĶĮŊŧ@Ģ›ÚjäġÜ5Đ×\Ŋ‚6ÔĒ%‹ĩjÉbũņŖ…jßõNĨŽĨÅīÎĐsI‰ŠžŽĨėŨĢŖĨŋĒkĪ^ę÷ÄSŧ†ØöuC‡ŽĘĪũRiƒúëĘđĢTb+ŌoååęŅ žX–ŠC%']oŨî=:*MŸĖ˜ĒąŠÉ’NÖgĮ÷{P_,Ët÷yyũúJ›2]“Į<§ÉŖGĒYt´4l¤Ÿ~ÜĨF!M4râ[į\ŧ @åXđWķá;; jØÂ ›õVj’A$āīx⁉ŲŲŲ/xåUÃĐGë7ÕÚĪZlĩĒ`ãŲ‹čhiŠÂ""Õú–[ΚZąÕĒoÖ¯UyyšîîÕÛũ¨wÅņĸŊ{õ_áWéæÛģœĩĒ÷úĪWčĒĢŖÕēŨ-§///׿ŧ/Ũ[n]s}kŨžĐÃ}<ĸÅ5§õõë/ũ¸m›œĮŽĒõÍ'û5°ßiûkK'jËĪûR{vü ßĘË}]KŨžĐŊ֒ėGīē]Ļiōá‚Gø@63Ú¨œē°v…fŅŅę90ų’Īí5(õ’ŸęŽ^ŊĪyŧ^Ŋzē=Ą‡nOčqÚņËĪxĪ‚ˇ§éDY™3VīžĮ}ŧĸģYtķĶß_ŋžēŪwŋēŪw?ŋĐ8ÔhĀīzļ?jŌD_ūí/ZöįŲ:ZúĢN”•éģ-ųúhęd÷ÖdžĘyė˜{5Ā|ĸFûō˂Ž,ܰš1ˇ€šeč2;Q*'čōËuĸŦŒDė"zĨ¤jĪŽ”•>OYéķÜĮ›EGkäġN[ąÜ×úwą‚5â&"°žģŪHI ! ¸TŪÜGģq“+TlŨ§čëZø nØPŖŪú“Š­VYvū eeēúÚëÔâúÖîÛ|Uņūũēŧ~n"<ÆöŅĻF•âÍíÁÁúnËfíKÔ,:Úīļįúnķ?ä@Ĩ˜Ļ')×[‰vd‹kôúü |:QVĻŅûÉqđ ĢŽ#XX >Ģėøq5jØP›rž hUæbuęr@Ā!Ņ@Ĩ$&&öôæx/LxU ŪžĻbĢ•āÂīļkåĸ5î%‚Ļ95Ú$V€_’ĸÖąmôæ„W´bÁĮēŗWoŨtk'E^ãķ[XÕe'ĘĘT´×ĸ‚ŋoІ•+tMĖĩšˇ$S×ļlEp°˜Ņ@Ĩ<đĀŗŗŗ_đĘ̧ĖhŸj͆õú,ëoښŸ/ہũ*;~œãŖ6j¤ˆĢŖÕåŽģÔĢO_ĩëĐņŦs˜Ņ†'ųĀ>ÚĨĖh Rŧ]Ŗ}.ˇßy—nŋķ.n€ŗPŖ ŋS[5Ú\Ėh#°6žDĸ €Jņö>ÚPėŖ ŋã 5Úp>ÔhĀīPŖ OŖF†m<‰D•B6_F6ü5Ú|5Úđ;ÔhÃ͍ŅF€ĄFO"Ņ@ĨPŖ Ā—QŖ ŋC6_F6ü5Úđ4j´`¨ŅĀ“H´P)ÔhđeÔhĀīPŖ Ā—QŖ ŋC6<mj´đ$mT 5Ú|5Úđ;ÔhđeÔhĀīPŖ OŖF†m<‰D•B6_F6ü5Ú|5Úđ;ÔhÃ͍ŅF€ąÔŲË2/éŧč1ęĐšķy_˙ûēĩ:Xōo%ö(IښŸ/ëOw{į÷ßi×ßëŽ{â.{I‰6ŽËSû[;ĢyLŒWæ5ŸĻcG\đœŠëŦø9.Ļâ|įFĸ €JILLė™í—×>~ô¨K:¯Ī€L´įŊ7S›ŋŪčNŦ˙šąH˗fēÛšĢWëũéS5gņ……‡Ë˛ģPãGŌëšęõD{ę¯ë€ÕzÁs*ŽŗâᏘŠķ_4xđ`͟?ŋÖ۟’’Fĸ €Jņįí9‹—œÖž?ë=mX›§Iīž§ĻaaîãM/’Dö8PēÜqÉãFE7×°įFŠõ 7ÖĘĪŽ7ߙyŪ×ÛÜxĶií´—'¨Í7^ōų€/ņ…mmTĘŋūõ¯I’î÷Įk?39ޘŊŊš}EEG_r?3חžhGkØsĪ×ÚĪ]ŋAƒJũc Í7Vę|Ā—<öØcĩúūĄC‡–’hįņō˜QŠīyŋũüŗĖ›Ŗ¨čæzūŗ”ˇzĩöė.ÔkSĻ^R?{vjūŦ÷””œâ~$ŊÜåŌĒOŗõÅg+dŲŊ[Žr—ŽmŲJŊ“TĪŨī?ë=IRĪÄ>zúT}˙í6ÕoĐ@÷Äw×ŖCūG7æF>†íŊP)uiíåK3ĩ(ũCŊ:6MŽŸÖēœ5jÔ¨ą6ŦÍģ¤Zæ KJ´|iæi ŊôüHŊøÜ=zT÷$$¨s—;ôũļjė3O+ãŖt÷yÖæé/‹ôhRîÚŠon§˛ãĮõūôŠJ{zŋĀ|amf´P)umíMëŋŌ+“&+)y~=|XMޏĸÚ}nÛúVf/× Į× ¯žî>^\T¤>qwë‹ĪV(ųŅ˙<žēĪbҐgGhøč4I'gßJyHÖæé€ÕzŅĮŪ=|XīOŸvÎ×ú xÖûįĪzīŧ˙H¸ļe+ ūũĶ|⺍ҀßņįíĒ WRō IōH’-\múŸįę†ļ7ŸvŧYd¤š†‡ë€õėŊÃûOr[/(H÷Äw×Öü|°îģÄDû܏šwęŌåŦ÷oX›wŪž:ušƒD>mĀĮÅ´lU#É{\û´ĪbŅĒOŗĩĪbŅO–=úvëÖsÎP7‹Œ<Ģ;ėĒKß^+*:ZŸ­ßxÉįĪYŧ„ÅЀj F•R—j´kJšËĨ—ĮŒRbÜŨûĖĶZœ>_Å6›z&öQč•Wžu~Ŋzõp‰¨Ņ€ßŠk5Ú5aÁüyZž4SJŅĶŖĶvĘžŨ˗.!@@5øB63Ú¨”˙ĢŅF5lZ˙•$éŠgFœ–dé€ÕJ€€j FūČBĒ'Ļe+mX›§ÜÕĢ”úē’NŽ,ūŌķ#}âúūžv­ŠöŸ?ánãŪĀŲH´P)W\qE÷Ljjüû§•ģzĨ&ŋ6AķgÍTŊ  i`ęīÔ,2R_Ž^åą­ÄĒbŪŦ™|ŊĪ€K´9ĸÆg,ôT•ŨnפI“4yōäĒ>ÖüųķĢüū”””0ƒ[€Jšošf?IĄ5=Ppp°ÖmûN 4¨‘ū÷ė.ÔÁ’ŨÜĄã9ĮØüõF5šâ ĩšņĻĶŽīüū;ũzø°{eîŠ~*ÚŦV°îS›oR“+ŽĐ¯‡kį÷ß)Ļe+÷ŖâĮ×W_æhŸÅĸf‘‘jߊŗĸĸŖU\T¤}{-jÕēB¯ŧR;ŋ˙NĮWģOO(JJdŲ]čã|ļmũF’Îz˙…âq1MÃÃu­‡VcßŗģPiŋĒwî䓅j+--UVV–RSSĢÜGzzzĩŸ={ļD•fšæ’bkzœæ×\Ŗf\tŸhø¯Í_oÔü™īhũēuÂÂbh¨ ›7iŅĸ…X÷íV´ßǘ˜€Bĸ €Ęē+//īo ŠÛlD<€ŲI ā999ZļlYĩúđÄ>Ú$Ú¨Ŧ6<䍁zõŧOÖæņļqŨZõŧī>hÚ´ŠŽžúęjõá‰}´ŠŅ@Ĩ™Ļ9RŌôšĮfŗŠeĢVúęŸÛU/ˆ sŊ¤D}ãî–ÃáP÷ƒmT‰ÕƒDDD¨]ģvÚŧék"€žú2G‰}û’d#āh Ōn¸á†n.—Ë+c=üđÃZļā‚€ū˛pyøap8ÕŽ¯–¨Ņ@-ŲącGņŅŖGōÆXCžzJ[6ëûmÛ|Yķųg2dĒwīŪ¤AƒUģj´PkL͜&é9oŒõazēfÍū@,Ę đ Üåԃ=âõáŧyÕNjD6Ē,Ë[=’šĒŌ_~ŅĒOŗ‰zøxΟÕĻuk’l,mTIPPPĘącĮzi,Ĩ§¨?ŊūĒ wî$ø~ėÖ+ķãtÍ~˙}‚)--U=<Ō—'j´ëqKPĻi65jTpÆ Ûzcŧ¨¨(ĩhŪ\Ŗž}F÷%öQŖÆš ~fĪîBxâq-[ēTíÚĩ# đ˜Ë.ģLíÛˇWDDDĩûúå—_Ôž}û*ŋ˙āÁƒ‡ŠŅ@u’íž’ūæÍ1'L˜ +WęÍwg),<œ›ā'öY,z~ȓúߗ^Ôc=F@ȨŅ@ĩäHrz;ŅîŨĢ—÷īĮcä~bËϝõdōŊ0ö$Ųđ8§Ķés×Dĸ €*3 cøÄ‰wy{Ü ¯ŧĸÉoŊĨ§MÕ+>åFø°ÅÎ׋#žQÆâÅzōÉ' <.++KC† ņH_vģ]iiiÕęƒmT×îE‹Ų7nÜĪÛˇmÛV÷ÆÅiÂK/jÍg+tŨõ­ŪŦwÄGlŪøwũáéaú÷Ģ–/_ŽŽ;ÔØwA||ŧęׯ_íž~ûí79rDmÛV}é j´PmĻiIúARĢÚßåriÁ‚û ēõļÛô`JĒ:tžMõ‚‚¸9^vüøqũcũWZōQēŠö[5eĘõíÛ—Ā Žąh ÚĻL™2zôčŅSjķœN§æÎĢŋkkAî˙ī˙Ö÷Ä)ėĒĢtUÔÕjŽ pŗ<äč‘#:XRĸƒ%˙VŅžŸ´.'G_­ÍS—.]ôøã+99YAüŗ5lÕĒUęÖ­›‚ƒƒI´P‚$e8p ,22˛“/\Íf͊+´fÍŲl6Y,Ųl6Ÿ\4É_…„„(""BŅŅҊ‰‰Q÷îŨÕģwo…††x…ËåԐ!C4kÖ,%Ú999r8ęßŋ•ûHII)%Ņ€G˜Ļ')—HđWr:ęŌĨK•û˜={ļDžLļ˙*Š‘P‡ą6<&¸iĶĻM‡‹P¨IIII˛X,>{}$ÚđįĪ?˙p8€G͜9S.WÍT§„„„¨gĪžÕîgčĐĄĨ$Úđ¸)SĻČĪĪ˙˜Hđ”ŌŌRŲl6ŋ¸VV@0M3HRļ¤û‰_Oâ•›[ũ SRR˜Ņ@Í0 ՗——’——ˇ—h¨*ĢÕZã+ŒkÚ´ié+..ÎɌ6jRû¨¨¨1û÷īī-)”p¨ŦŒŒ 9 :Ô_.y;‰6jœiš ’VJ "|‰ĶéTpp°'ģ\ÃŖã¨q†aä|ú駓wėØA0\”ËåRNNŽWÆĘĘĘԐ!C<Ō—Ũn× Aƒ"H´ā‰‰‰›˛˛˛– cĩZĩtéR¯Œ•œœėąúėāā`ÅÆÆîâŅqx•išŠ’æH &Đ3ĖhĀĢ ÃX?533ķŅPÁnˇ+--M.—ËkcŽZĩJN§ĶĶŨÚH´āušššņÅ=%m&$)44T]ētQPwÖLtš\ZŧxąGûĖÉÉŅ€bytĩÆ4Í›Í6×ét>C@€:Čbą(P>˙š0aƒ$Ú¨m ‰‰‰//_žüBÔ-?~ŧ˛ŗŗéĮēœD>Ą¤¤$ąŦŦ,ŖQŖF CCC Ā\.—×?—¤¤$M›6­&fŌí†a„SŖ ŸžûP‡ŽÕĀâD|Äøņã5wîÜZŊ†‰'z<Év8JJJ*—¤zÜføŠ˛˛˛]˖-ûäúë¯&)Čnˇë˛Ë.Ģՙ/ÕWZZĒúõëK’:vė¨ģîēĢV¯',,Ėã}ē\.•””,YŊzõrf´āSöŽ7n–ÃáPFF†f˘AP?fˇÛÕšsg÷ļ]ĩY2sæĖÛ>,$$DŖGž(I$Úđ9ožųfq~~ūĮÇרącŨĮg˘!›ÍF€—••%ģŨ.éäėņÖ­[kũɔŌŌԚūū(4 ŖDžjR=ž´ōĖN Ûž};‘|€Ëå’Ãápˇ-‹;Ņ–¤āāāZŋƐŊūúë5’đ—––ĒM›6—W´I´ā“ ÃpåååĨäååí­86räH÷ė‡CĪ>ûŦû|§Ķ)‹ÅBā/95ąž4i’,X S?Ģąąą>qVĢĩÆŋ‚ƒƒ•ššúĒûû‹_ø°öQQQcöīßß[Ō ; ôÆo(33SŌÉĩ-[ļ¨˙ū’j;!ŸĘjĩ*..N’”žžŽÍ›7ëwŪņųkĪČȐÃáĐĐĄCkr›a‘üĻĀo˜Ļ™`šæ ŗžũö[sΜ9îöʕ+Í~ũúšÛšššf\\mÚ´ĪĶîŪŊûiŸ§éͧ›8ÛącĮLĶ4˙|ęw3Úđ ŲŲŲlÕĒÕ8_y€īrš\Zģv­j|ŦŒŒ ŊōĘ++wîÜŲĢâ5Ú𠉉‰›˛˛˛– cĩZĩtéR¯Œ•œœėčÖ­ÛīN=ƌ6üŠišŠ’æH &|ĀÃ0ūtęf´āW ÃX?533ķŅPÁnˇ+--M.—ËkcfggÛ_}õÕšg¯Įí€ŋąX,6lø—¤¤¤[$Eõë×ב#GÔļm[¯Œįrš”’’ōMFFÆ|I§e÷<:ŋešfˆÍf›ët:Љ‰! @dąXTKŸ˙I Ã8k GĮ⎠Ã(ŒŒœ7bĈuD¨{ ôėŗĪÖÖđ/œ+ɖ˜Ņ@€())I,++ËhÔ¨QÃĐĐP0—ËĨ   Z?>>Ūž››ÛY’å\¯3Ŗ €€žûP‡Ž9NgĀ!ŲIDAT¨ņãĮkîÜšĩz Ũēurž$[bF&''ᚸøø’‚ívģBBBĖN`€?+--UHHˆ$Éáp¨–ŸZYlFʅN`F%!!aī¸qãf9eddhƌđcvģ];wvoÛU›IöÔŠS………i;mĸąĢW¯žŠGŋ;õāŒ3”œœŦˆˆ"ø°ŦŦ,Ũyį “$9ÎZ2å×_=ŌŠS§…ģvízZglįu&f´ˆ&õčŅã I+Ī|áÔŲ°íÛˇ)\.9wÛbąČnˇģÛžPūҤI“ĮvíÚõ?K˛%f´ĀrssC%ÄÅÅ]sæk‡CIIIĘÍ͕trÆĖfŗ‰ũ¸ī8ĩÖú7ŪPhh¨†îs×iĩZĩoßžé]ģv}žģHíŖĸĸ>1Mķy[ˇn5 ānīŲŗĮ\ētŠģ}âÄ @ÕüøãfnnŽģũᇚĪ<ķŒ_\ûÔŠS7HĘ×)p Ķ4LĶŦTĻüíˇßšsæĖqˇWŽ\iöë×ĪŨÎÍÍ5ãââhĶĻ}žv÷îŨOûúčĨ§§ŋ-Šáāt:_kذá6IËĒÛ‰6ę,Ķ4Cl6Û\§ĶųûguĶž={Ž^{íĩ†‘åŠ>ytu–aĨ‘‘‘ķFŒąŽhuO~~~q§N6{2ɖ˜Ņ$IĻiö”ôŽÃáhJ@€æršô‰¤4Ã0lžîŸm@’aĢîŊ÷ŪÎ-[ļ´š\.ĶđáÃ÷Ū}÷Ũo†ņģšH˛%f´€3›Ļ$itIIIZ“&MŗāĪJKKR iŒa[$9jr<mā‘•••ļqãF—¤‹ÅēeËõīß_’{;!‚TAaaĄŦVĢâââ$É:}úôŖYYY%ššš†QH„€:ā–[nš­]ģvãL͌3M3uŌ¤IÜzë­?šĻųąiššŸūšĩk׎ĮLĶÜcšæž•+WŅĻMÛÜjšæį .üŧM›6ģMĶ|Ō4ÍŪ/ŋürrķæÍ_ķ—Ī˙˙UØO¯ŲáIENDŽB`‚trillian-1.6.1/docs/storage/commit_log/main.go000066400000000000000000000113141466362047600213530ustar00rootroot00000000000000// 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. // The commit_log binary runs a simulation of the design for a commit-log // based signer, with a simulated Kafka-like interface and a simulated // master election package (which can be triggered to incorrectly report // multiple masters), and with the core algorithm in the signer code. // klog.Warning is used throughout for unexpected-but-recoverable situations, // whereas klog.Error is used for any situation that would indicate data // corruption. package main import ( "flag" "fmt" "math/rand" "sync" "time" "github.com/google/trillian/docs/storage/commit_log/signer" "github.com/google/trillian/docs/storage/commit_log/simelection" "github.com/google/trillian/docs/storage/commit_log/simkafka" "k8s.io/klog/v2" ) var ( runElections = flag.Bool("run_elections", false, "Whether to use mastership election; if false, signers run in parallel") signerCount = flag.Int("signer_count", 3, "Number of parallel signers to run") leafInterval = flag.Duration("leaf_interval", 500*time.Millisecond, "Period between added leaves") eventInterval = flag.Duration("event_interval", 1*time.Second, "Interval between events") masterChangePercent = flag.Int("master_change", 20, "Percent chance of a change of master") dualMasterPercent = flag.Int("dual_master", 8, "Percent chance of a dual master") leafTogglePercent = flag.Int("leaf_toggle", 10, "Percent chance of toggling leaf generation") ) var names = []string{"one", "two", "three", "four", "five", "six", "seven", "eight", "nine"} func signerName(i int) string { if i < len(names) { return names[i] } return fmt.Sprintf("signer%d", i) } func increment(s string) string { if len(s) == 0 { return "A" } offset := len(s) - 1 char := s[offset] var prefix string if len(s) > 1 { prefix = s[0:offset] } if char < 'Z' { char++ return string(append([]byte(prefix), char)) } return string(append([]byte(increment(prefix)), 'A')) } type lockedBool struct { mu sync.RWMutex val bool } func (ab *lockedBool) Get() bool { ab.mu.RLock() defer ab.mu.RUnlock() return ab.val } func (ab *lockedBool) Set(v bool) { ab.mu.Lock() defer ab.mu.Unlock() ab.val = v } func main() { flag.Parse() defer klog.Flush() epochMillis := time.Now().UnixNano() / int64(time.Millisecond) // Add leaves forever generateLeaves := lockedBool{val: true} go func() { nextLeaf := "A" for { time.Sleep(*leafInterval) if generateLeaves.Get() { simkafka.Append("Leaves/", nextLeaf) nextLeaf = increment(nextLeaf) } } }() // Run a few signers forever var election *simelection.Election if *runElections { election = &simelection.Election{} } else { // Mastership manipulations are irrelevant if no elections. *masterChangePercent = 0 *dualMasterPercent = 0 } signers := []*signer.Signer{} for ii := 0; ii < *signerCount; ii++ { signers = append(signers, signer.New(signerName(ii), election, epochMillis)) } for _, s := range signers { go func(s *signer.Signer) { for { time.Sleep(1 * time.Second) s.Run() } }(s) } for { choice := rand.Intn(100) switch { case choice < *masterChangePercent: which := rand.Intn(len(signers)) who := signers[which].Name klog.V(1).Infof("EVENT: Move mastership from %v to [%v]", election.Masters(), who) election.SetMaster(who) case choice < (*masterChangePercent + *dualMasterPercent): if len(election.Masters()) > 1 { // Already in dual-master mode break } which1 := rand.Intn(len(signers)) who1 := signers[which1].Name which2 := rand.Intn(len(signers)) who2 := signers[which2].Name masters := []string{who1, who2} klog.V(1).Infof("EVENT: Make multiple mastership, from %v to %v", election.Masters(), masters) election.SetMasters(masters) case choice < (*masterChangePercent + *dualMasterPercent + *leafTogglePercent): val := generateLeaves.Get() klog.V(1).Infof("EVENT: Toggle leaf generation from %v to %v", val, !val) generateLeaves.Set(!val) } time.Sleep(*eventInterval) // Show current status output := simkafka.Status() for _, s := range signers { output += s.String() } fmt.Printf("\n%s\n", output) } } trillian-1.6.1/docs/storage/commit_log/signer/000077500000000000000000000000001466362047600213675ustar00rootroot00000000000000trillian-1.6.1/docs/storage/commit_log/signer/fakedb.go000066400000000000000000000021771466362047600231410ustar00rootroot00000000000000// 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 signer // FakeDatabase simulates a local database type FakeDatabase struct { leaves []string sth STH } // Size returns the tree size of the local database func (f *FakeDatabase) Size() int { return len(f.leaves) } // AddLeaves simulates adding leaves to the local database. It returns // the STH in the database. func (f *FakeDatabase) AddLeaves(when int64, offset int, leaves []string) STH { f.leaves = append(f.leaves, leaves...) f.sth.TimeStamp = when f.sth.Offset = offset f.sth.TreeSize = f.Size() return f.sth } trillian-1.6.1/docs/storage/commit_log/signer/signer.go000066400000000000000000000167461466362047600232230ustar00rootroot00000000000000// 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 signer is a sample implementation of a commit-log based signer. package signer import ( "flag" "fmt" "sync" "time" "github.com/google/trillian/docs/storage/commit_log/simelection" "github.com/google/trillian/docs/storage/commit_log/simkafka" "k8s.io/klog/v2" ) var ( batchSize = flag.Int("batch_size", 5, "Maximum leaves to sign in one run") pessimizeInterval = flag.Duration("signer_pessimize", 10*time.Millisecond, "Pause interval in signing to induce inter-signer problems") ) // Signer is a simulated signer instance. type Signer struct { mu sync.RWMutex Name string election *simelection.Election epoch int64 dbSTHInfo STHInfo db FakeDatabase } // New creates a simulated signer that uses the provided election. func New(name string, election *simelection.Election, epoch int64) *Signer { return &Signer{ Name: name, election: election, epoch: epoch, dbSTHInfo: STHInfo{ treeRevision: -1, sthOffset: -1, }, } } func (s *Signer) String() string { s.mu.RLock() defer s.mu.RUnlock() prefix := " " if s.IsMaster() { prefix = "**" } return fmt.Sprintf("%s Signer %s up to STH{offset=%d, rev=%d} = %s\n", prefix, s.Name, s.dbSTHInfo.sthOffset, s.dbSTHInfo.treeRevision, s.dbSTHInfo.sth.String()) } // LatestSTHInfo returns the most recent STHInfo known about by the signer func (s *Signer) LatestSTHInfo() STHInfo { return s.dbSTHInfo } // StoreSTHInfo updates the STHInfo known about by the signer. func (s *Signer) StoreSTHInfo(info STHInfo) { s.dbSTHInfo = info } // IsMaster indicates if this signer is master. func (s *Signer) IsMaster() bool { if s.election == nil { return true } return s.election.IsMaster(s.Name) } // Run performs a single signing run. func (s *Signer) Run() { s.mu.Lock() defer s.mu.Unlock() // Read from local DB to see what STH we know about locally. dbSTHInfo := s.LatestSTHInfo() klog.V(2).Infof("%s: our DB has data upto STH at %d", s.Name, dbSTHInfo.sthOffset) // Sanity check that the STH table has what we already know. if dbSTHInfo.sth.TreeSize > 0 { ourSTH, err := sthFromString(simkafka.Read("STHs/", dbSTHInfo.sthOffset)) if err != nil { klog.Errorf("%s: got an error unpacking STH: %v", s.Name, err) } if ourSTH == nil { klog.Errorf("%s: local DB has data ahead of STHs topic!!", s.Name) return } if ourSTH.Offset != dbSTHInfo.sthOffset { klog.Errorf("%s: local DB recorded offset %d but that has inconsistent STH %s!!", s.Name, dbSTHInfo.sthOffset, ourSTH) return } if ourSTH.TimeStamp != dbSTHInfo.sth.TimeStamp || ourSTH.TreeSize != dbSTHInfo.sth.TreeSize { klog.Errorf("%s: local DB has different data than STHs topic!!", s.Name) return } klog.V(2).Infof("%s: our DB at %v, matches STH at that offset", s.Name, dbSTHInfo.sthOffset) } // Look to see if anyone else has already stored data just ahead of our STH. This will // normally be the next entry, but we need to ignore any entries that have inconsistent // offsets. nextOffset := dbSTHInfo.sthOffset var nextSTH *STH for { nextOffset++ sth, err := sthFromString(simkafka.Read("STHs/", nextOffset)) if err != nil { klog.Errorf("%s: got an error unpacking STH: %v", s.Name, err) } nextSTH = sth if nextSTH == nil { break } if nextSTH.Offset < nextOffset { // Found an entry in the STHs topic that didn't get stored at the offset its writer // expected it to be stored at, probably due to another master signer nipping in an // entry ahead of it (due to a bug in mastership election). // Kafka adjudicates the clash: whichever entry got the correct offset wins. klog.V(2).Infof("%s: ignoring inconsistent STH %s at offset %d", s.Name, nextSTH.String(), nextOffset) continue } if nextSTH.Offset > nextOffset { klog.Errorf("%s: STH %s is stored at offset %d, earlier than its writer expected!!", s.Name, nextSTH.String(), nextOffset) return } if nextSTH.TimeStamp < dbSTHInfo.sth.TimeStamp || nextSTH.TreeSize < dbSTHInfo.sth.TreeSize { klog.Errorf("%s: next STH %s has earlier timestamp than in local DB (%s)!!", s.Name, nextSTH.String(), dbSTHInfo.sth.String()) return } break } time.Sleep(*pessimizeInterval) if nextSTH == nil { // We're up-to-date with the STHs topic (as of a moment ago) ... if !s.IsMaster() { klog.V(2).Infof("%s: up-to-date with STHs but not master, so exit", s.Name) return } // ... and we're the master. Move the STHs topic along to encompass any unincorporated leaves. offset := dbSTHInfo.sth.TreeSize batch := simkafka.ReadMultiple("Leaves/", offset, *batchSize) klog.V(2).Infof("%s: nothing at next offset %d and we are master, so have read %d more leaves", s.Name, nextOffset, len(batch)) if len(batch) == 0 { klog.V(2).Infof("%s: nothing to do", s.Name) return } timestamp := (time.Now().UnixNano() / int64(time.Millisecond)) - s.epoch newSTHInfo := STHInfo{ sth: STH{ TreeSize: s.db.Size() + len(batch), TimeStamp: timestamp, Offset: nextOffset, // The offset we expect this STH to end up at in STH topic }, treeRevision: dbSTHInfo.treeRevision + 1, } newSTHInfo.sthOffset = simkafka.Append("STHs/", newSTHInfo.sth.String()) if newSTHInfo.sthOffset > nextOffset { // The STH didn't get stored at the offset we expected, presumably because someone else got there first klog.Warningf("%s: stored new STH %s at offset %d, which is unexpected; give up", s.Name, newSTHInfo.sth.String(), newSTHInfo.sthOffset) return } if newSTHInfo.sthOffset < nextOffset { klog.Errorf("%s: stored new STH %s at offset %d, which is earlier than expected!!", s.Name, newSTHInfo.sth.String(), newSTHInfo.sthOffset) return } klog.V(2).Infof("%s: stored new STH %s at expected offset, including %d new leaves", s.Name, newSTHInfo.sth.String(), len(batch)) // Now the STH topic is updated (correctly), do our local DB s.db.AddLeaves(timestamp, nextOffset, batch) s.StoreSTHInfo(newSTHInfo) } else { // There is an STH one ahead of us that we're not caught up with yet. // Read the leaves between what we have in our DB, and that STH... count := nextSTH.TreeSize - dbSTHInfo.sth.TreeSize klog.V(2).Infof("%s: our DB is %d leaves behind the next STH at %s, so update it", s.Name, count, nextSTH.String()) batch := simkafka.ReadMultiple("Leaves/", dbSTHInfo.sth.TreeSize, count) if len(batch) != count { klog.Errorf("%s: expected to read leaves [%d, %d) but only got %d!!", s.Name, dbSTHInfo.sth.TreeSize, dbSTHInfo.sth.TreeSize+count, len(batch)) return } // ... and store it in our local DB newSTHInfo := STHInfo{ sth: s.db.AddLeaves(nextSTH.TimeStamp, nextOffset, batch), treeRevision: dbSTHInfo.treeRevision + 1, sthOffset: nextOffset, } klog.V(2).Infof("%s: update our DB to %s", s.Name, newSTHInfo.sth.String()) s.StoreSTHInfo(newSTHInfo) // We may still not be caught up, but that's for the next time around. } } trillian-1.6.1/docs/storage/commit_log/signer/signer_test.go000066400000000000000000000020611466362047600242430ustar00rootroot00000000000000// 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 signer import ( "reflect" "testing" ) func TestSTH(t *testing.T) { sth := STH{TreeSize: 12, TimeStamp: 100} enc := sth.String() if got, want := enc, `{"sz":12,"tm":100,"off":0}`; got != want { t.Errorf("sth.String=%q; want %q", got, want) } dec, err := sthFromString(enc) if err != nil { t.Errorf("sthFromString()=%v, want: nil", err) } if !reflect.DeepEqual(dec, &sth) { t.Errorf("sthFromString(%q)=%+v; want %+v", enc, *dec, sth) } } trillian-1.6.1/docs/storage/commit_log/signer/types.go000066400000000000000000000024761466362047600230730ustar00rootroot00000000000000// 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 signer import "encoding/json" // STH is a signed tree head. type STH struct { TreeSize int `json:"sz"` TimeStamp int64 `json:"tm"` // Store the offset that the STH is supposed to appear at. Entries where this // does not match the actual offset should be ignored. Offset int `json:"off"` } func (s *STH) String() string { v, err := json.Marshal(*s) if err != nil { panic(err) } return string(v) } func sthFromString(s string) (*STH, error) { if s == "" { return nil, nil } var result STH err := json.Unmarshal([]byte(s), &result) return &result, err } // STHInfo holds information about an STH stored in the STH topic. type STHInfo struct { treeRevision int sthOffset int sth STH } trillian-1.6.1/docs/storage/commit_log/simelection/000077500000000000000000000000001466362047600224135ustar00rootroot00000000000000trillian-1.6.1/docs/storage/commit_log/simelection/election.go000066400000000000000000000030571466362047600245510ustar00rootroot00000000000000// 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 simelection simulates a master election. package simelection import "sync" // Election is a (flawed) simulated mastership election, which can // be made to report multiple masters at the same time. type Election struct { mu sync.RWMutex masters []string } // IsMaster indicates whether the given name is master. func (e *Election) IsMaster(who string) bool { e.mu.RLock() defer e.mu.RUnlock() for _, m := range e.masters { if m == who { return true } } return false } // Masters returns the current set of masters. There should be only one, but // bugs happen... func (e *Election) Masters() []string { e.mu.RLock() defer e.mu.RUnlock() return e.masters } // SetMaster sets a single master. func (e *Election) SetMaster(who string) { e.mu.Lock() defer e.mu.Unlock() e.masters = []string{who} } // SetMasters sets multiple masters. func (e *Election) SetMasters(who []string) { e.mu.Lock() defer e.mu.Unlock() e.masters = who } trillian-1.6.1/docs/storage/commit_log/simkafka/000077500000000000000000000000001466362047600216665ustar00rootroot00000000000000trillian-1.6.1/docs/storage/commit_log/simkafka/kafka.go000066400000000000000000000047121466362047600232760ustar00rootroot00000000000000// 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 simkafka is a toy simulation of a Kafka commit log. package simkafka import ( "fmt" "sync" ) type commitLog []string const showCount = 10 func (c commitLog) String() string { result := "" l := len(c) start := l - showCount if start < 0 { start = 0 } else if start > 0 { result += "... " } for i := start; i < l; i++ { result += fmt.Sprintf("| %d:%s ", i, c[i]) } result += "|" return result } var ( mu sync.RWMutex topics = make(map[string]commitLog) ) // Status reports the current status of the simulated Kafka instance func Status() string { mu.RLock() defer mu.RUnlock() result := "" for key, commit := range topics { result += fmt.Sprintf("%s: %s\n", key, commit) } return result } // Read returns a value for a topic at a specific offset. func Read(which string, offset int) string { mu.RLock() defer mu.RUnlock() topic, ok := topics[which] if !ok { return "" } if offset >= len(topic) { return "" } return topic[offset] } // ReadLast returns the latest value for a topic, and its offset func ReadLast(which string) (string, int) { mu.RLock() defer mu.RUnlock() topic, ok := topics[which] if !ok { return "", -1 } offset := len(topic) - 1 return topic[offset], offset } // ReadMultiple reads values for a topic, starting at the given offset, up to the // given maximum number of results. func ReadMultiple(which string, offset, max int) []string { mu.RLock() defer mu.RUnlock() topic, ok := topics[which] if !ok { return nil } if offset > len(topic) { return nil } if offset+max > len(topic) { max = len(topic) - offset } return topic[offset : offset+max] } // Append adds a value to the end of a topic, and returns the offset of the added value. func Append(which string, what string) int { mu.Lock() defer mu.Unlock() topics[which] = append(topics[which], what) return len(topics[which]) - 1 } trillian-1.6.1/docs/storage/commit_log/simkafka/kafka_test.go000066400000000000000000000051741466362047600243400ustar00rootroot00000000000000// 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 simkafka import ( "reflect" "testing" _ "k8s.io/klog/v2" ) func TestReadEmpty(t *testing.T) { topic := "test" offset := 0 if got := Read(topic, offset); got != "" { t.Errorf("Read(%q, %d)=%q; want ''", topic, offset, got) } offset = 2 if got := Read(topic, offset); got != "" { t.Errorf("Read(%q, %d)=%q; want ''", topic, offset, got) } if got := ReadMultiple(topic, offset, 1); got != nil { t.Errorf("ReadMultiple(%q, %d, 1)=%q; want ''", topic, offset, got) } if got, gotOffset := ReadLast(topic); got != "" || gotOffset != -1 { t.Errorf("ReadLast(%q, %d)=%q,%d; want '',-1", topic, offset, got, gotOffset) } } func TestRead(t *testing.T) { topic := "test2" want := "abc" if got := Append(topic, want); got != 0 { t.Errorf("Append(%q, %q)=%d; want 0", topic, want, got) } offset := 0 if got := Read(topic, offset); got != want { t.Errorf("Read(%q, %d)=%q; want %q", topic, offset, got, want) } if got := ReadMultiple(topic, offset, 1); !reflect.DeepEqual(got, []string{want}) { t.Errorf("Read(%q, %d)=%q; want [%q]", topic, offset, got, want) } if got := ReadMultiple(topic, offset, 10); !reflect.DeepEqual(got, []string{want}) { t.Errorf("Read(%q, %d)=%q; want [%q]", topic, offset, got, want) } offset = 2 if got := Read(topic, offset); got != "" { t.Errorf("Read(%q, %d)=%v; want ''", topic, offset, got) } if got := ReadMultiple(topic, offset, 1); got != nil { t.Errorf("ReadMultiple(%q, %d, 1)=%v; want ''", topic, offset, got) } } func TestAppend(t *testing.T) { topic := "test3" values := []string{"ab", "cd", "ef", "gh"} for offset, value := range values { if got := Append(topic, value); got != offset { t.Errorf("Append(%q, %q)=%d; want %d", topic, value, got, offset) } if got, gotOffset := ReadLast("test3"); got != value || gotOffset != offset { t.Errorf("ReadLast('test3')=%q,%d; want %q,%d", got, gotOffset, value, offset) } } want := "| 0:ab | 1:cd | 2:ef | 3:gh |" if got := topics["test3"].String(); got != want { t.Errorf("topic.String()=%q; want %q", got, want) } } trillian-1.6.1/docs/storage/database-diagram.pdf000066400000000000000000001057661466362047600216270ustar00rootroot00000000000000%PDF-1.5 %ĩíŽû 3 0 obj << /Length 4 0 R /Filter /FlateDecode >> stream xœÍ]Y“Įq~ī_ŅģÛĒûđ›()$Ke’°ŠĄ Ay$‚2ũëųeUuMwĪŅģ m2ĖäTge]Y_^ÍˇÃ§ãÛA{5ZmōhCÖSiŧ39æIͧī_˙1žôČ˙~˙õøĢįjüúŨĨGžÔÆú=öŅŗÁÚÉZgŊũ;Ķ)û)YŖŦfnãŗ‡áW_ŨŠ;E>ûjøâæëÛģxķęö.ÜŧiŸžŋŊsææ9˙áöÎ ņ%ųë ūôĶíææķ[­nˇƒ57¯ÛķˇwZËķŋšŊ3ifnū Âũ­u}_Ä×Ũhú>Ü8~lâ~øûj_ņ)Ü~ųėÃš´qYĶÄĐü¤čėåãFķ-ĸÖÁęßÕí@ ´ôFŸîđ<ž›ų;¤‰üŊÉ.ĸ˙K=Ū$0Âw+Œ!ģž\ÎŲ;ČNŖHEvˆ„9‚˜øŧwˇ$ßĢԟŊųą|exøų›[ë‡6 oã͟ą?ņ´RúæĶ[šüÛØ=ķ›XūÅ'žˆĄŒ”y(Búânäûoą˜1įn–Ū3‡íĶš†2õīe¤Ģ2KŽ_āp´ü<]‹ŲJŅtŗÕÉķc›ŸnGŅlÎR¤Ë~a–dÛvoázāG~]4´^đĐų'ŦæídzĀtĸ3ˆķ–Áy)ää/öÖÆ)ũƒŸĮ’`ČøtG"ŅOĄ­ė<ņžío7ŗČŸaœ¯ —2QeĘâ?˄=gB;fŽˆØhļ,×WôĀ?ëĄ_žbŅ^4/ųFĒÛf1!ˇōØPōŧÕÔąMüšKOVšÄö„lôų\ũíģîwĪŽtėgŋi_ú'Ž˙ÔHÛrüĮđŗôŖ_Zߎĸ{īø/kbƒNƏ&…-Žsų` +sĮĘüķņĶY˙kOšßĐ~g•=FEÜLb]nĒŪĪ.+CŨ&“ŗü!:_Ÿu¸!čy^,?ī=Ęãđ…AZ}"™3ũ}š/ô|_Đąp^6Éķē|Ļ(‚ĒcĄnÛ4ž9­DÆ}įö‹m´k÷œÉô´OøŧēīžāUfE|¨Š{ŪčŌ—ģņŽŦœ‰Ŧhrœļĸža&žŋÕNžÅ&úWæü˛riũ&ž¯"=‰›Ë+ë…í9Ÿ#?eo>Á2ײ…đ—ÛBõ=õßņ'tÔĮˇÃJčšŋĻ?ŅßöęũõV<ë!ŅŌ.Čpū0¯ë;ūūúÂČp'÷#û+ëĶ_3˙Īæģĸî¯ųģüôŸÜj1đaš[Ž<иPûÁöęĮmUžWí#ō^ķ„ɰbXô]!F땜Ę~Ȃ 6@hčč´ly­“V˛méĸ?˧(k6 d Ō æļHΒųĀĸĢ•čĒbšp…äÃiuĒ–Ëŧ=­G7ƒz¨},|Ær*ä§ ?œŊ VķŽ}t}ß`ŋ˜ Z0Ė”īÚAũ†›ŽūWĀ+9u3˛U.GíŗíļųęŪZ\g'Û•ęZÛjÁ“UkwJ›Uŧh´“„KyŠ9˛_`˛ÎŖæ;"›0ڔĻhéģv!ΚؿQĢĸI#TõÆNZĮaLÖsK­Mœ´ō,›Ÿ\rÜÄMΐöĩŲNÎņcŗp¤n/HéyŒ™}!¤ģ]b)Ŋ˸ŦH#“õ?Ū…U´'J ´ĻHėD)xû~N4‘iKć~ē_oõ÷I{Zļ¨5ÚF+˛UR 2^+RŸÔo2“Š4¤œ'K *á0ŒšÔë¤t˜Ņ]6)g6Pē:ô$M }ã2ĐÄxŒqîĢ<­MDaŗÕ°oÁččMæM[ē+ CšĨHRÚN§)‡Ės¯T å`ĄŠˇ”=ŅØČŧclĸŊ¤ũ€ŲāwrAr9ÚP‚žéĘVü|ĻÎy/ėŲ}Jņ.Fī Áig'ŸYt;Åä1C?g Gcō˜F“ÁxhøVËxļXžŪaKéäČYL'>ŋ‹3î4-@d‹4ģ5rŲēŖ6j¸ų;˙ īĀ‹ÖđŋXūš^W=6CĖl{ZeäČĨ‚ŧŠ}ŽĀ™Ų2ö˛´Áų¯b9 ‚3;@fî EęüČPėāæį Ũŧ…Î~?›NođņĐĻë%ž<›Muūœ–3xĨilMUé2^WīîņŌ!ްįÉp:æ-đ~ČjYCAŦū€5ŗ]J°rÃnLîg°ˆįūŽ-âËüI D}¸uØũeÅõ ’+F"Qû%&¯6°ģų=d1Ô2(ސ?o¨|"°Vv×f\ęgém5oĸ@‚Âäe„gáØ Xš˙j6˙õæ*‡as$'ņĒāøGC5͇ŗPãāXmr]ŨŽOĮ°ĩ‰Wã¸÷UUjŠ&¨š´S¤—ąŸĄŲšéë_žTM0xhén™<ŨĖtŦ zRA×ņh¸‹˜2`•‰‘ËŽģÔd‚Ŧ–´o΁ûãP§¤ĢTÛ=‘ žE+í@Ē7‡¨íņ~8،Ҍ •Ųëe‡'/ŲŌŽ°“ĮDā̎­|SNžyœ™¯ÎT:/„‘HˇZ凎Q,R76•Pē9t$›púƆfІ0õܗPđT•§DÚĘĸÉ.ÔF6ģ瞄­s6<L JLŗ 9­T‰Æœ}”ŖŗÁîôô›Šsˇå#HFīlôt'ŽÃzš8 m’†Đą@Ü${°¸;<؆ąâvf šäĀ`‰ËšŠ§Å osE+Ģkûčę V~ĶĸEsŦsaē"ęō[šH1Gqbŋh-ēģ1MY]Ą`ėč|œR°Uŋ°ãhkäEŋdëFG&#+4ž­¤ }÷dy靘ˇž#]ČæSŅ.vN– Ķ.܊f´×.Ž(Ķ^ģ,šŅu0ēX™Ŋ^vx^ģđ4Đá¨ÚőF]N3]Ä8úíÄWB¯]æFQTũĖĻJ7‡žDˇ ÔĻ]œsS°Ŋv)”^ģTRž°čG˛_ģ8‚ēN%Ø.EģđŅsŽņĸI,QÅĻnĸÔp4õ_9–´–¯%Ë&žwwŧaŽžūĩĄ“ĸ3ya‚‹Ĩ_Ĩ(,^JüæŽÔĢ—5éĨ¸:X[5^ŠĒ$ˆÆëŪ‘Y÷Åș%ūˑPNC0ö?Z_Ņí-˙āáđf°qFĄē&Ãք\ލBFé)e=ŒŗRØÆđ$ƜZâ¤X)’ ™Î_Ô ´Œ™“H žQöë{Æ)ēM ģ[ŌŋŪW—5ĖTÁ q8ŅČC|6ƒļ Â-:< ㈕ƒI—āĩI4#HcaøÄ.Ž1h†O/ŽA&b™;mĸcīøĖĨ|/f YvÎĻÆ‚ N§`ÆĪÂĄĻļiâV6‹!ítô¸"TÂwdĻÛՊíĖޤ#‰l‹ÍHŨ "’ŒM‹S?“pd17ĢqÅņļëŦlpÆVÁĻyœ§Ŋ0öŨŋ:“AāFqtûŗøą1?œ#ĸŅŨŠI™°œšũÔ9Õ­9žŅRzžˆ0ΖWތčԉ/ą+NĄČ07RpøČĄBc,h¯ķĢ‚ųd$G„ĶÅhÛh:đ†–ÆGÆŧĻŽ>ĐaŌ´]Œuĸ“L9x^0Ū‘Ŗ!Ļ!Ōī1˛û5mĮ;UWĢ{ˆ}ܤøāĘ šĻÎ(V&ÕEĢīį0fZsķ0zaŪhÂGĀgZ\Sˆ6&^.›I;eˇÛŠÔplÂĄ J°$YE1ú°nĻâĩ9ЃÖÍČfCŌz1}‹ZöyN ‰ŲŌĸecD ’åø‚;ô>ģĮH{ĮĘ!cßĐBiĖ}Ŗ(žĀ™M%”Ž=‰n Ÿ—Û˧ĐõU‡NžÚfDaŗÕۈ#m#2K%Bȡ1gÚl™Á?퓈UĶtUŖ‚v85EÄ1ˆcL%BˇÁowđ!6ŅÜr{CŽnWÍÎ)>Í9:|D´Ô* /ŊD4WÜ hgd@f˛Ú´ãŠåųã‘ørĒ8ē·Š•ĮސŖ”0Āæ°*ų˙_ļį:TIvRŅSŒhbqĒīą;ÅĖŦ‡X!ƒũ–§Q-PéŠû#í3?˙\Ģ-ÃâjPfŽpŧŧ.ˇ(ÛYōŖ$š]ļĻ„}Šs ņ“,Mö!ggō3ä8ČyŧôŦhÕR˛]‡…pčĨjšä3§åwęĢTŪ§•ķŦF8÷wl™\šll=œËĐÚËŊč§°ĄAF[p+ ߟĄQ;ģæ]IsNÂ˙Ô|+2bŽ<Ķĩ Wf¸ÛN|؝ø­F' ča—}ÎBNåƒ<9CÙ‡>€ ‰Ãí=÷JĪų-¨w.yU„ÎîÃ]…ęĸŗĢ3Ēčė'Ô3d­Fcœ)ĸ \•™#Ņ´ÜpÄ3ßrå]ŌRš"`JF‚üÉX˙ĨÚļs9W™}î*H{Ô@žF<ŪRāÅ%v §™VcēĻĪ>ŅPŲ,…\ +ųÁëŽĪ$scjCmMƟiÜā$đl5QŠ9Læ OÛ k!JI.Ķŋd'#/F Ŧ$0I >ĸėɏŌڊV÷- !‰iģS8čqWk)jhĀpÄMŪ8Kâŗ€ĸ…AĢĒI‹O‰Ÿu>ļĒ…ËķU Gâ—Ēŧü+§ÕšĒ…ŦĒ)hEÃÄXčA*,PĄdX‹Đ‘6Ę[ŠBZ^KĐUÔcÅ5\âÄE Ė ¯I"”†<RD´ē<ØfÎĘ+…Øš%ģ’ˇŗNŽD]¨Q_œK’Ķ…pMŋ[@¤kgiwϊdN‹)%P °;D/āÁx5‡>¯@V ÅK" Äö ä¨į úCŽĄˆŲhÔ%*žpRĄ¨<Ūú Į™Ķ—*eV ę¨Ģ×sĒ„ŌŨĄ#‘obz>VŪ;ŅuW(‡^¨ÖĒ >ŗZŽoŸS‚ßd–ĩ˜Ÿ˛U3Šß´øŲR)+pS‚—MŪÔu/EąPÉÎ#­ÕÂzKvģÔ°iÆäuûĖրBz’IÜ"b_k ŗ5¨„ŲĒDI8“K*ÎĢjŧ—ŒöVŗ[W,·){é„-T,RĪüŽBƒ(x#ģW›{‡åĸøČĶvœö5lW ¸˛28OštÉú˛P/—•õå^5đįtxœĨ'rąôÎE,NJYúģ6oaP´`IŨ:WŨV'äu…ĮAĖĮT˜n,*†ÉŽđœ5<­’`ۂûŲLÖۊûy_į}ilŋtĸØÉĄwoųzׯķŧ”ŗ×I9Á] råsxlb}Ĩ1ۓ–øŋĩ”°ŸĘ{Ɗ]VUR"vâŨäģ_2ļ2ņŋē˜k­æOÕĮa]ųßš”ŠŽo1/.ūå;(ÆOæŊKųÛ˛*Q§n^û÷Ømœ“aĪAŲ–­:š†.éÉUI7ŊĶÕÎĘ[Ĩ Į9bs,%&8č‰ĻamjÍŲIŽy‘ÁÄī€iāSJí&´1ød¸ÎJw™­]S^ĸ‹ĸ<ÚHötUëy+ļ@æ ĩY5›?ŖÂŸ­gW1įĮK ãkáæAŪ‘_ĻģŅ–2ßõĨÆ`Ũՙ"nėd.K_#åúohĸNj ‰Đ0@dĄHp—4ˆ,-ĨaËuŦĨôwčiė!uzčXy2§bĩĮJ9trÍ­Ēč3§ÅøvKœŧ7tI°Ä†,)ĨŦptpŦ„ŖÔĩâ5#¸Āöô+YpÛ_pā˛DÚuS %āQsĶĪŗ—XI4ļÆJøgĸ…Vq¨nÍpŋwÁ’Ë ŊøĩēÕĘؚƒģŸ˙ ēĸĖ; endstream endobj 4 0 obj 6704 endobj 2 0 obj << /ExtGState << /a0 << /CA 1 /ca 1 >> >> /Font << /f-0-0 5 0 R /f-1-0 6 0 R >> >> endobj 7 0 obj << /Type /Page /Parent 1 0 R /MediaBox [ 0 0 3992 3279 ] /Contents 3 0 R /Group << /Type /Group /S /Transparency /I true /CS /DeviceRGB >> /Resources 2 0 R >> endobj 8 0 obj << /Length 9 0 R /Filter /FlateDecode /Length1 2389 /Length2 15841 /Length3 545 >> stream xœœĩeT]Ũ˛Ž‹ģKp˜Xpww‚ģģMÜŨŨ!¸ģģ;wîŽÁ‚\žĩÎ^ÉēûÜ?ZcΧĒ÷ǎúxû€œXA™NČÄÎ(ngëLĮDĪČ ŗ°1rqR6´•ĄSšš>‚ėpää*ÎÖĀ˙•…#W::YØŲr˙Ī:G ĄķG@ÔĐųcšŠš @ČÅ `0ąrŗ°qŗq˜9ūgĄ#7ĀÔŅÎŽ\ÄÎŪÃŅÂĖܙđŸ¯JU%u*Ú?&...€‘Į˙dĸ@' 3[Āį˙*đuZÛŲÛmy"akk c€™ĩ‡ŊšĀĐÄhōO 5Ck @ÜÂÚÂŪŪÎ@)BõßU>„2Ņ}üaƒ#|Ú?FúׯKÜÎŅ ø¯Y9˜Y”æÎÎöÜ Ļ)ĶRôNĻôļ@g†šäbļ&"v6˙¨q‚ƒûgŗ¨…#Đøcx†˙÷i[ŲÚšŲzũ¯°Š…­É?Ĩ&.ö Ēļ.@IŅ/ØÛŲL ­€¯,ܟ´ĐĀÆČĖČÅČ :€îÆæ ˙tWņ°ū+ÉôOØĐÖÄĮë?5|,Lp^N†Ž@€ŗŖ ĐĮëīÄĀÄÂØ`4ŗ°…ûSũ# 4ũ7Ë:;Z¸´é?ŽĀøĪīžé~xĀÄÎÖÚãĪr9C €AÄĐÂŅî_žd¤cü“ļsxŅ1q°č˜9ŲLŒĖĖ.6€Īüß~ā -ūŧŋJJښÚ¸ū=ÅĮņũŸI Ž˙ö5€ō_Sūģ>ƒœŗ…1đÃ2˙1Ļ#ã‡'?>˜ū¯†ũ¯ülû˙ĪŠ˙Ôų§Ō‹ū/…â.ÖÖ˙:KĘ{ đa&'€ ā;Y:ūīõ†6Ö˙—˙kĨ:đßwķ˙Ģ’¤ŗáĮ BļfÖ˙sĐ 'q w ‰‚…ŗąų˙1ëŋĒļ&@Gk [ ‚“Å?ī Ķ˙;Šbnale trúđķŋr@[“˙nË fklgbak`fc::zĀ1~ؐ™ āÅø¸>@÷ŨŊ­ķĮ€Ŋ‹ŗāãŽÂũķØY˜ Nö†Īô#ü¯+#€ÁŪĐhk 4uūeú?Ņ?Ũ˙„Ų æĪ hû'Äūąčhagō'Ä `đ:Úũ pėl˙ôü˜ŽÁŲíOūã$œÍ­øĐijįâø'Āō°pũkëĮ$î?ü!ÍéÃe”}đŋäŗql-ūōĄÔØÎÚîĪ&ö2BčŖ„đúØ.ō‡>öŠūĄÅūC}ųCÃHüĄI$˙ĐĮRčŖģôúč.ķ‡>ēËūĄîrčŖģüˆķãxūЇÅ?ôĄEé}hQūCZTūЇÕ?ôĄEí}ôĶüq}t0r44ļ:˙—‰¸Xū˙oq}”vųĮôNÆvŽž×Į„†čcBŖ?ôŅŅø?ÄÄø1ĸÉ_øĄøūãžŋđC‡Ų_ø1Ļų_ø!Æâ/üÔō/üĐdõ~ˆ˛ū ?TŲüAĻUļá‡*ģŋđC•ũ_øĄĘá/üPåøūcįŋđC•ķ_øĄĘå/üPåú~¨rûƒĖĒÜ˙ÂUá‡*ĪáŊhŒ]?nŋķŋū_|ŧ…ū‡M->Ūv@ ;ĐîWĐHęųū]iJÜ2ĪoiŠ6…;ĖČ×*ŧ^˛e [8´ÖÍé-Í}Ü&…ė7Xˇ íG˛ŗž'ėA}t…41Ņ û{ūąˆÅiJhŖŊßļ~Tf’t—ą;ôŠCˆ\œtC™?U*tk8/˜Ü<‘á7R™×Đ.!}’ø~¯_^¯ĶÄJėāXÜĻúY‡XV:Y’c/Šĩ=Œ“‹w'ëė­ģÔAūčÆxļÁ;fÔį9ŪÕģrP&а€!õI­ŽČ1ķ(•Uĩôgã°ƒ }Ž3y4Mõ‚`~KæČŊʅeĘLĪLŖž]SÆn¨k/'Č;ŋņđ Á‹IãĮ܄ŠčéĪ |Ģë“ÆŒ|!ącâ ņã„ĩĖãĐ˙Øę[!ö_kūāŦįmĩ4>';,*úÅ|á„5EÆA)ŖķuQļ4ą>†á3ĒK Ōĩuß}Ÿz(p$5ÃëŽö6qOSëîCgTõ¸&ņØÁäĒ đŪ>QUŨ3%ÄV5ŽōÉ4#Í=*ÎMX—ÍÔ_IŽîaėX×Quâ 6@Rq>vÎčƒōåĖ^ŒÁoiJđĪĖ~Abŧ„s;˙­Ąd^.5ÜíCyč $Gņ†€ü­ĮēS€éˆŅM—\dˇÕŋoųŠ ū‐ĐācŅđE4ō“wQÉČū𛆮ĢsĄâjWņF'Vĸ¸&)ŠØą¨MRĐÍx”6v‡ƒú- E#ÔŖé°tëŗ!yš_ĖžeģŽšŨŽ—f{mĨvs…øũqüÆuŠô;ĻŸnČtô莁)iŊ ëûQIŸû‚Úķô-ō‚ĩ4ËãėŲ xëÔĪwg,Y: ļĩ…U‚Ø/•ÖG2Tá\‚Ęŗ›iŠîÎMj™I|¨Ĩ÷¯ĢËŋØ:”°]ĩÎx=×öąļ#&˜Yë‹nōęíÂ/>ĪŖXS[÷E^Ė×)mãŌ—Nļ Ŗâv/šU´•ņÂĶÉ×D˙Ū ãņËé?І„ĐęBeiסĮÖ=MŊÁVŅsoĀ@¤AvíIŨŠÍK•`bŋFĪqņ7>JĒ Āf™û‰’S!ßėŗzČiVʎđ|žéŽ’Ņĩ‘UđYžå #ƐĨlŖī0’î¯|Od…áļGsŗ‚ÔH9ˆčÆÚxŽvšÔÎ2 Vr÷¸íK*ū†m‘Õ}w*‡‘Œ„[lųm>ęÖ('RiVŧČķ&@ą|/eļyŲ<2¯ąžøwb—’{6É ķ’)Îė6ŠeF\Rė‚Č™IZŖĻ:ˇŖˆ´‚ķ;Æcĸų–„’ĸ’zÅ ũ\˛&­âS‰ŗIč\´%:f§ŽŪL¤bB˙ĢWŅæ”s zÆŪҁį`*áFx!@í=Īqz@öö¨ ĐŠcĻ\ÃŧŋŗÚXYĸ˙™EKũö†’⧤b°(žUŸƒ:wBš N˜ęO‚q÷ŖīЌoš 2Ũq<—ũĀ5"ļĶNĢ;út˙ËD\!Z)ž1ÁĪņTBö^ūŽb#—Æ„ę(ffēųP™Ā_ī!OŨv#Šp„éX7Ôqƒ—T;ėë*ądčNˇØ,ŗO‰ĸMļ2ũĪ)_Ž­x_šŪÂMø…}ũl2AĨh2m#ˆ4ŨËÍąß`} BėƒĶÆN™¸„z=iz̈́G†Ėû´]ŦbI#$‰ŦL"¨TBa“^“6”5–'ėč;UŒ=¸=M&šįķkŧŠ]ËvŋŦJ Õl*<Õ`â“|¸ļ%ÁƒŽčŲfš„ŧly7dÆa Vīģg‡öRp’ĒÆ´°tņ_ ;rVÆĸ\uļåî¸Y%íĪ0el{k5cęĢúyāD´Xß̏ZŪj ‘ēcĮåPJqĩ5C"nu#vÄΞaęĨUǧCˈyøZ¯Ŗ2ĶâDw|ņUūÅAWÃy}yžčFt<0{äo8IF/.ˆNØķ^Œ_Â|Z‚Ęįe4Ú'¸f˙ÚļŦ,ŨŖB I-üDĀ™aÔĘNd˙[‘üŠ‚ųzˆåŖ`M” H‹‚„ËÉ#lhFόŅÎÜĪĨŨ!jÔU_$ŠĀ÷[2#ė­đFĩˇ?Ŗ’āŖ3k°‘Æ^JŌKÁtž8Ŗ$ N \,Å˙ #‘q7ՄDŲ™œ„ŪÅ~¤ķ`XÔ<8D=3ëhUģƒ›žTCÖŊâëiĻîi­8f `n=sęE°yK°ˆđ&h\ôģV1˜´ÜdJdcįâé…įnŅjWHŅrĄ‰įy}');Ūö:üÆÍ”Đ]–īNõ\Luh9œŖE^âኹ(Ļ'-EņĢ§Đ¤AÕ4ĢÍņüûÅø{iĨ^ûrŠ{Âö•˙šŖönúNŗÚ ŗe sxāj ŨËXÆüßT ŧ”aŗ§ž$™ōÃˆŌŽŽŊöS'¤„ŽJúDÉ5å7ãN_—8đIßëüÕE‚J"w]ŒŗĐŽ yaâd‚âØãĪü¤‡‘d-Ķ8"Ô@‹8˙ĩčŪaÖĮcq›# ´xÎ9đƝuĐšÆŦŖ§æ#†ēĄV~đôņ,<†Ëŧi‚xøu"ôqĪ^+>šōÛįt‹‡—‡kĨË]É9BžXJZ_¨{ŲâÛą%ĸn„ÍŌ^(jIĩuˇ6?”BlÄ?€Ø*Rr{\žę¸NøK,ãJ1ĻvRˆđ ōwÆPte=ņÂZ¨Ņ)›28ߞ/Ú; Î35{ø}čđ3â _Če?ĢŠęv"DŗĨiCķ‹‘,^NNę'Õ"ãAÉá„Aę¤%‰L8cŊš =ėˊ{Üķ;;†ņׯévĄˇm=ī3ģŊŨäæNĪ7MÚ2û ­2—™hÖŖ×tr­ÆÁ4BĢ[ÖÆZ;­sĮÕ,’›Ŗ"9ÔuâIˆ)Ķԁ@߲æõUK ߟXî(X]>ë™ÄĖkcúv/†Ún,˰‚4#Ī0hŗ‘ •F~ņ÷…â+7āė´ †…Í>A!ÖÆ“ Īô}ʞ2QũŅ!ķá÷(Š×c pę<•”{ëŸüŽaį…XAĸg+eNã{e—ÎH•͍Ü'p ĄÜBÁęJ„įĶŊ+Ō¨ixƒŸ:Spcųu[[(0&~Vē,jAĖdŨŨ~jEÁ|ZƒīÕčŧGmįJě蜝_ĨAžĖŅũėVŋnĸƒQcÜ:8pPVQJ‹Ũęp)'žŲ˛Œ!uáūiųĶ—ôVD/“bøÔ,öOšđaøGđI’3 •ę@­.‰`žrcÍ%īŊÔ[˛øŲ‰`ĀōÄ͆iKdUÎėČYƒÍž~˛ąÄBę@oHLÃüī˛ĨО<Ž Mg¤”…"BbĀT[#ĶSo9Æē`‰fq3!ÎKŲüi9āD%dĄ5W¸‡ŲÄA"Ķ„Æ {AKßÅīFw!IŒ‚Ö¯MkųūEMpŠ įæŋ˜Zõ‰a_žČ&šN-xŖaāēįœ\Å ŸLąf˙žã×lèˇŖlŲ$s÷ˇm8Y6 Ö.9!_]NęŨVŲAĸË}-ÖÆH¤€P‚WKúšõŸ5˜Xõq;ūä3Ąī–&Đ)u2¸cÅ fQ9Œ9–šz9'îQ°´dl…'š†j÷"1;> Š„ü~§JŠ0?pŗrŨB6Ŋx¨Ãœäæ—ĩŸēëŦΖhÚõJ}0¯ģĢWõá]ēĢhGË× ?$˛em¨´xŽ ¸Îp؁L%ŊŲԌ ™´ %K/­ ¸`†Á/aÆ´ķG>Õ8¨Đ™c'ī5’EXČÃ"ujĢAbB„~~oOé(ĐÖÚäX›]”,aĘyW‡ĩVöÎ8õøēęžđ™ŒQĘüŠŒbŊˇjEës 2ĘžD!†øMŸČ#1Ė›5dĒæ›ÅÕÅŌaД¤˛Ū'g+›wpŠ-ŽVßŗĐį͌ĄŅķ!ĒgŦ/@„_ģÖŽŸkú5°NÅû*iā<ŗŧ ÃÖq2ļ]’úSË97—Ģ5u,„UŠÍPīE›ÛXĘiˇO!ŒZ\MCØ˛úkPeÃĨI‘7B—üËëáDX–žļœ %?™CqQĒ7Ģ(‡1$|Qé§p^îX`ņj,{ôWYz€äi ôWČĒ5fą6÷֞įrą‡éVéČG2ZåŋD`ūi^AžøeŠv‚œŒīq<š4ĻD0đŗĒvŒS‘OĢ76Øņ{byëá…83jŠí" B¤˛qjdØCT6 Éâq-įėnGWD]ōYŽū“Œ2L2`‘ŠĀBi5NJoä[Ę|ĒŽŲë~ÍũkĮ|´S62^“†||Ļ´v$TER'ˇQE-čwŋ9š¸M‰­ŧWáƒÉ“ēAf.ãŊđĩ6^Ô°>rˇ5|S0&ü§RūOŲĻâ=FßÍ×8UMB`.ÚŊī åf10Ŗ<ŲBęÅąžŒø<$Œû&SjĒyÛŨ?a6Ģŧ+4EËĻWŖˇŽį/xƊĀA/äĒĄĨ” ģUˇ÷ãGÉäĪ÷đ˜aBũ$ˆ Ÿ÷íĢceîīŦ`qÕP˙Rw†E Í/gpÂ.>‚Ķ)G1ÔüļČ f'‚šÔ{˛nƒ€õėŌåéīl¤ņĶQņĶ€›ũžáXįĪ÷ģúpo4ũ[YÎ+Ōp[ėû=(.{¨ŅÁPČ]äIr)‡Ū¨’Mâč/æoˆ1oô b˛‹ôwŒūųL\¨ĶüpĪę­˛Ū5ˇĸČ7wŖßv&ȰķØ}6°Ū›ü]-[ÖYđVįܲN‡ėŠŅxŦ~9 ›Ļ Š“Ņy3Üãģn œ[‹œŪ,…yļ˜ī…=ˇŋö:Ā\ĪIŅļ‰åC?Uj}ļč’DŠXė#_ę]â@ŋŸ÷#°„ę'Šûáál‡nā 'Öēė]ė(Š/ˇŠq›0N)ä3z„ŽR_>2é¯|’NDšjėŨpsœ­ĮčDJ/Ÿ‡†?ÆÉåc\™āžEo4‰™9`Ũ?).Ôkaöˆ ũĖ‚\<¯_ķG•|{¨š¸°ŋˇ?" xÍßÎųɀ ÅØX¯Ķüšåt|Ͱ' *§í âcüHŽWÔ5Wr–Į„´ÍėSœŠûHųˇ„Loã ķs(KĸvYJđŒ…ŨŠ›ƒP÷ęÂũ‘%æQKûtqmŧ­´oWņoyąĢ+ąV¯CĘk&˛eo‡”ŠxĨ"WļE­­Õ ÆSģî4)!0ŸĻ¤¯ąąÖAķĸže< CŽ•ōX,ęĒ}ëĐČCēDÅ6•ãičLŦļî9šz 0ß Õ\ W,ŽsUQŨ“Õ-îSˤÃm<ĪÆZļČøádmž]KÂŖû`F+–gÚ{ŪÃFGƒŊHnŧ—îp/;ãļ~ŖãĮ° čĻ&KōĀ 4Œ•Ŧȍ“­ æ^÷ĪZp:5QHÁzíųŒ(HքBÂ`zg¸Lîą¯˛Ŗ"¤úųÅrģ?ZŪ™š™ič™cdôsšŽBēEĖZčuAĮÕ;œœŗĻ¯öāiĀŽdsUū^īSUü+9RûĘL*Ū`Κļ5ƊB €éeô– Œ[= VRāļ*×ņž R…É +hë.eƒ u)ĖU:Œ;,ŽZŠÛ*Ė3‚]!;?l$eˍЕ9y ƒå~L&OˈGŪZĘ÷Š€Ō×įŊˇėŽ bõÄ“Ž VKGZN=ŽÉy×áŠ+xČ%tD‘ŗA-,ÎkŖXapû„č>LŪ:<’äí]īúlYu#Xa-Ģ´~Ė(34DÛŧŨ$ z˛¯|+ՁVßzá3ŗgJ´qáË.ËxߝõHčíÄÅ ĩ˛ēnVã vBdĶrĄ'¸kSÔu힊 GĐ\ĨE˛÷ ČXRĢZ”fCä™ēĸÖŨîĩÃYī([!‰åoō9¯”KīZ¯ķK”Ĩ`Ė#D{—ĸöĶŌQ¤–ÃΑzS§Uæ÷traÎyŽFđ¤súTąļ—Û{ R–@ßÄãĘË0€“‚xz—~yôX{,ų\Ģ$oLr":ëöŽnۊG 6>ŦĒr˛;8„ŲhYņáÜexB€WķƒôÂЕ.T˛đJ Xö_•ĐōI鰙dŨĪ˙é? Úå$>‡ÅĘütÔ]zLö5¤RĸŲõŌžúģLĀĪÅŲĩ&ŊVŅŠ„SŌ&.A'Ŧpč:*ō’"n—áđN-÷ÕE :¨š°,ß"›‰ōZ,b(€ŧ .[ôJŅʘNŠŠ"ŽqËÜû6% 7äb9+Ēų])†7ŋDÁÉä§Üø I'ėŅĀ=ąijd@ g™ĒÁđî™×m‹zĻ—f7ž2B"ˇ‘‘ĢFĢĒ6P똋:¸…YģĢVŧŧđˏã§GØã˛ÚûP5Lĸ gŪUYV^ljH2}Ôˇŧ#0ūoąĶ͕“ŧŧxi`ĒĄéD=ĮPĄĪ¤1 ã™īKx¯Cm?*bˇR^3&č’Ŧ,ŠQ9Æ “uNßjOá&"Ô°aÎéå;ĻZ¯r3zj/jū„hö3ŋöXĪOüˆ)ö8NĶ÷&ŋgeU…ZÚgéyöķžyâéEâœSā=“`ÃŲ¯?œpSė…ŸËįåÔcŋÔũmöĢ&Î/æzGĄ“ėuŠ$đGĒŋ~îę•€ŌGēĢĐNō‰EIa˛ôUHļēW‹íëŠļ™Žq†ŲŅlüĻ\NüĻ ‘}=ÉOÜV›éRu ­šu—eˆĻ&dH3 Πm žĸu2‚B“Ū.āęü9%YŨ|îõĨ䇲Vļ˙īŠ=DsBėĻZGû{¤ŋŽj)áOqŽSŠf/GBŒÎ˛páäœŌ‹xo8ų Íēķq`pûšT `6˛ai߯;õßÚbhšˇ4xw/B4‚ÂŦĒbĐúĖå¸a“ŅܑāįVŌ>œøÅĸĄ ŖĪb˜eĐ&h=•Ģ`žtdË>Ā æ-ÄfûÉë"Ŗt§x´ŅāúĢĖ€|ŒIZ)ˆ{¤ˇÍ÷ļ–čG\ĸ}­āČČ3´~a…ę*üĄ(Ŧˆģī3Š OšŠÁR`l§DĪzß´.¯éŋ™HÄcĸézŊF"$/%ÔĮŌ‚*tC"ŗ^(Eg")ōÛžŊ—/?._æĐi™¨}ĸ%\—ĀĩGH§]+*sŸtey3ŲRiÚžĶ.ĖŪŲQ5ĨdC›ČũāoŌą?~)YČgjõųÄ+œåŅöܕ^ãVŒč:3NwGH2=ėsqæb?âŪ§FĻÜAO,„I46᝝Ô+u ŊkŊūvGŅˆÃ§ö02‡%†Ôģ´IA ‘÷z@Ųö"EM=C"†ŽyučKHz V[œ MČf:lŠÔR"6’īlđž °ĩJö-?!ī‹3-}ĩ΋į~ĖWĩ¸/›X$°ēƒˆŸ)" ßą´ŽJĒįhd_°ņŋJ(ņz9Y­ (č}ëŒx0S&`Čä0,{7TM˜ąČˇ?f­t´eÁôŒąyg-RĄ›qh˙Vá+r@Ą ˙Îôķģŗá1‘ä*yÅŗąCVÖ¯tÖĘ2õOiČ-ö€- #•Si‹!Ų%"Â'ö–0¨9ĨØŌHÁŒEģc4š/…ŧâ ŖP™bÛÅwk^įÚܝ÷Ŧėž žę3&ÁJÎc¤”Ņ/4߃C`qĨŋË|ļ}BŨ8ÔŅķ+j1’ÛČ7ˇnæmOalŪųeX´kxÛÄF’%EF^8ˆvב{Ãģ]¨˛,!”ÚK ÷ĮũëÖuģoN`tķÖ…Ī¤š˜QlT"`‘ŒrHW7ęA¯Z;ĘåB<Ī…îŠ;C>ĩŨ°GN`¤ąčN7˜ˇūÜĒũJõ:§m‡š÷Ā8Ā‚ī÷aŸNSŧžnéķŽŅ”]Ģ`šaÆ0TZSK’Éāöė—ķĪą•ÅĪbeĶâšÅųņŒEĢ9eM~PįN|):ĄÅ°hå­p1ė/´°ŌKŠūŖABûēR¯ßë÷üyŋ÷ũ Ü/ļK_×ÝSžLžĪ-]Véuė˙•ëÔ=œŅ‘š‚^Œī“ņÃâ<Ø^ũ›€Ž8ÉČÜ:O,ƒnm7Įߐ\ŧ#*7ˆŗD´= ô CũÛžZĻņ*ãjHSīUüëÄnÅåčŋ&öŌ ÄCd›ÁH(o‚„¸#iõܛûōã•´ę M…ęHo7ÎīeI0ÆĐzŒ˛ŅoīzūšĒrĩw¯c~[ (Æ"¸ļŨ_ž0+ĻĻà ¯‰Ĩ4TœÍáČDB&Œą„ļŊĶī˜qÄV‡’$=žz.@Oßŋa™etD3o“lĨ [aõųÁG9ŦĶ;ÃwéVÜüŒ°š’[æŽdáąÁ~¯J".J´*\ģz°&ŞjļOsčŖö~ãÛŠł÷q÷jT Áãk‡ŦŒ×Íį?ŒÃP}ļ‡ī@~=ŨV­ŨĐeYV ^LxjzHēTˇb.€÷[j?6ŗ‡Bj)˛}$ÖēFgĨƒŖ-|ąĻŽ>N†Ķ}$wŸT¨ÔˁŨ”Oim˙žîš!uĄĶ¨F™×æfÚ*(Jgkzü ĘŌž%`h'^@3$ŸíU|œĶAúX)w˛R<'÷ĀÎģĸô$ß!Ē3h=ôФ!ø7M¨WtDĐ/ÄXvķ.í˙S ô…ÕdBIFŧŽ´Ŗ8˙‹8|Fs­ˆíŲÂĀL3b¯I.@ | ]PžëĀ! 0LVhxäüæ BÚŗp>ô۔ēãÔFlFŨÎZ7)ÕĄøNŧĄËäãˆũ[€Ōiß§š1,Κ2ą‰Î4E4ū3I—œ˜ ˜ŦW#ģusÖ7õg6ęŽo(0+ŲĩtwNP¤fC”ē•Ûih=›(/qHō 8QĮ7Øis"čŸgĒÎZ *æÕ^gŋ6L­|æĖē֏ Eˇ¤-ÅČRą8Íë)F}IįbāĻŽûÍVĘŽgLeËŦGË"ÆęmH"|Āe´œF˛ oqŲŲٛ Ŋ}Ā={%\3TJΞŅĨܸj‹T“°¨ ŌĒ~Ōk{įRPØėĮĀ  ŽMÎ˙>ōj:ø­ŠÛŊ¸nTpūÅGÜB’[ –Žh‰Š-šßėZpib ŅruŠHiĢ3û5ΝöTœ´ĩ´ø…ØV";ÄQ‹ī“íĢ$î ˇŦŪ‘'}3QŌÉ΁RĢ"W™~­)fX›ĸŒ¨f%äq]BáĐ+5{GyĨu€ŦŨŗQŠēŨX[Ė4žŋĪJõÁ›Õęžę,›Nu K›-$ˆĻįT_DŸQëEMVA›CŒiAN‘âāEĐ\{âK˛ģ%F[ŲY=m æØŠ­áëŖíjO‹'ĸ›qrD[ã>6ÜåŒŊmĐIßj`9CŸ͍f+'NĀāö×=xŗuĖcŧ‰Č[¯†ō‡Gã›/­Bž•§ûKr–ũ$‡ĒĮ;Šå//’ŲYz—Ž ::õĨ‹ųĢįŦÕĄĄĒ6­iē ‘ûīÂȑö9æ1 ˜ Čđž1;\sˆBCgoOi,ÕDŊ͘~)=Ü0áĻsđãéęŊdBJķž%”ŗöÕDŊ<Šbd3EÉ?ēr_ŗāÚweúnPÅׁ1kUO** ¸ķ6uë Ö¸ ]žÄĨ œÚ…ø__ģQe°A0"HîŪ×ęb%ĩž0ĀqCRÚU0_-/YŒ$dģkāš-`ôŨ‚+Üŗĩ‚†rXšÛōû=ŪÔ}P=7Záč:‹øąfpš“„ŦŦËĀ~ã’ú Okū­œížˆ´ˇ[=ZEM­ˆˇ[÷ßtĻ­>•ĩės+ûT{ę[.‹1Ŧ!ˇaÁ÷x md-“b°ē)4?ōđ‰žsøĒæČËĢÕ^ÜÕFņ5? –­Dm,°Œ{âjãņ¤žl|š×ũĻ2lRŖĖ‡œL(^…dĶĖØå;”Á…Ü4Š'0ę>!đ˙ĸĸF‡éé—7 ŠÕ܁š{v|ä:t’ .ö*šnæeŠÂÁöŧ)†â9~‰*eû:)ņ‹*ôŨŽM^JŌĻB˙>ęô-Kš™"|#N3iķ&Ó ´~‚šī°  Œõ—L…OŲĢ͎ę×ŨÆØ´Ŗ`{ŦčĸõË0ęG0đ 4+¨Ë„oĪëL:(mAåy[Ņ?ŋFæ°ØSi¨‘žƒđQ *Ą†—%ÃĮoč(jŲīįÅĨ۝_E0†Ļū,iQ^E°‹žČ@äÉ?؛1ę°į (ĢA4ÅņɨbÆS^õ?ŠFcíGķæĶWOÅđŖÅĪ2ŅĻd?>ĢŽÍ6WÛuŒÚYƒÄŅYŒNŨëÂ–ĶŠ´‚gé1““Aƒ¤üÔ$ûÍ×Ģq ‘‰ë’ë|Ô)’ĸŋXQÔîgÅÕ°Q*á!RQÆūh;°˙+wˆ8éũ2YOŊÖ lR1Ŧãü#0\ŒäŽ™ÁÄ'ųP–ŖČƍČ[]¨[—‘ScGL¸¸vV‘ūŒnžvŊŽúްĪW@'c<=§G0ŗŊ–}üb Ntäxg#Áųx…¨”ƒ¯ëŲn~ žh9`oԌ*gūT´)žÄ/VT#¤ĸ¨(a'Ōf~F˙œ¨¤×VļL’ .@x ÆÍ™nL|1›NĶöP8MOåä™ÖėmŦØ^Ą=gŠ ¯ÆŦ@Vēä ņ…Yø6¸IēŠ3V> TŊ|Õ¤,užŽE”´Ĩüķš~ŌĘĻHėø^!=Øčšd΍påvĒS4;—ĶŸQ3ĸ{E$bķ隓: œ†‚į ßēöŊĨˇR÷7ũ;2ËŖcnōâ*ÖH( ŋ΄°ķæ˙’=˛s((‹Ŧ•žÁŒtĪģŠč…y›šAĄūu ›TKæ‰dŒ:[5‚x23˛qŪeĢSWvĸNߙŌŒTvXĀĘbœß]Õ€įcr^ĩžß“l OW,’|OyÅ `JΞšø&ų@§'ˆ?ü{ˇ>BF é@`ĨęSV¨ŧT– ÉL롒>IŨA-_Âh˙ļ†DJųétĻLÅvԌP;¯cb–30ĸRwQĻÍō„¤>ÅvĘ g’~Ōö÷8r;™<ˆÃޝÂļx’Đ;ᕲšÜ2y¤0”éÚ*„æc*Vˆkt&ĸÛbņgō^C„WËĮÎû­ų÷nߊn”Ą’Ũ'*(ČÂlđŗsœģ|žÕĀ€G?˛šŖŨÖôŽÎߊBŗlÁã ÷ĮíÁ¯Ž=_Ō'Õöė>”á˜&ütX',ŖÚCāŋ UeKĮ{%~ ĸr´0°—wÅ%eŸœ"mįė‚)ŧ怤ƒĻ´Íg Ą1ÃēčG—CJí.ˇĨr×ģYTÔķ<˛(_?.ēĘo–0.9ģÂĘ›g)~펾˜#–”´YW|1re˛đĘĪjšAíN]1íMܕŸÉ°‹Ä[løĒŗ4×BĮZÁ5ŗJäŽ ’}Éé@û*Ö靂!ŒQ9W|~Õ|NũöÔwLž –ĒkŌâü ē˙ōč[|d~°KĀ@›hdûƒę”Cũ>æŲfķAÅIūŒ~(ĸ‚‚ĘwĮŸØ>ė ŊŤs7˙[^ÁĆ,ë]YyĒ[.cDzŽ16YhíUu<ŋĄÅŦƒæbŧ7CĒ ¤ûš‹!å댎Ų:9Ē/OBNMŅzü”BcäŊÅĘTnt¤äo]qâ’9s`´i`už°~íBŨ-´„úĮJ{Í{ûRá~ @& %\;EÃÔ+×đŊ` üÄp¯ž˙ü-?d=á.9<îJČ –…iÅ\ 5¯˙3FJ…8ĖC‚ôŗĐ5)57Ąy”ŋ5…Á÷tęwėP~،Lö€VЎßįõązO7Z)œ ¤Č-^ˆOŪ7ghĘnC]ß9—,•ŌMéÆî֖5ûļz ô¨ęĄ ØoĶBF=tk}@>Ũ ą&RĄåãĒNeÆĄHø1Ą´u2Gâtã˛iŸÅ¤ŋ!;ĨT?¤&°˜ÔˆxJ{f/ä4­+BäË!—-oc„BÎ `kSÃrÔîĨq×˜Yi}ŋÜ/ØæĀ‹­—YP:P÷–ë9Vŧ‹l9‹+Š8ĖËiÔ0VĻ˙!>™]{9~ķÕUđ%¨q›d wˆŒ ×­vÖÎ_Ų…uRĒ anAļckTˇ„pķCČvŨē˛"‹”[Û̈́kÎöNŨäķHBVHũĀy1FPÛûđē™Â"¨å§i™{z ŽÜūĉm—ā‰9šfÃ({–kįmŽg{)EËäĨŨk´Q3˙ž ũėŧGIX*,%”t6Ž5Sļ[æ(įû Įl_tÉ=Ą:ņö_žĪSfb”3TAkU×1„ė¯7=Ü_oHõx•ˇ1'ą"E“Ä”ã,Q’íΚQģßļE1vĸ’đ)„ō۔Š'Ø´•~ˇOŌÆyŒâīŸZ Čŧ¯FŠõ.(ŒĢ"ußķĐ5˙0ĮXt•áėą19é×a߅!Vž õíIčęĪ%‰Ž]AMži7ąŊ•ę9ī(įg÷´@Jízš°ßŌpz4Ęģč¸BŠžĪSKŌūV$#ōxŸ'GVáb( 4ĻųĶĢÉrSÍ}pÂm.ŅeŌĩņŠžˇFåÛ 0lõJLÚP^@_ք5č`ļâAUorl°ōū >W?‰>”&žGFåų^ɖk Mīäĸ“›ļ\HOéļāõPđIĀ–]snÄ]b÷û¯i~ũ,  Jåā•TĢšĘ# CkĶrą…W‡ønŨœĶ™sÜʏĨãķ‰õ/ũ=ÂĒfâŅėcČžØmڔ*öâNFQß˜Ķ—÷õŦ&ԗˇNޤĄáéēÚ6 ŲåŦ1b‡@UžO5§ėgmßRX¤iŲYā_Åäր´’ŠvāŖ˛åžˆHns{Ĩ,ĄĶë5Z–ÅäBúžp1vM™¸(­ē‡÷Šb§T2ęŅ’ņ9ąķ41KëŌÛÉp01–įiģ~ZÕLæ[í\?\ewgų‘=)ĩĮĩA…n,´rk/aė4ŧŗyeŅáā;æqģKé6! éKœBįŦ0ģfĒ‰žT%Üw^<á€N r ráĨz•ķÍ [9R+Å+Ô­ÖĘĸĸå0á2kÖ!'sšU+U5VĸĒĐ*uōŠÕačWL2š–ü G>ŧÚ̀J+NtØĄųF/+ÉØ<˄ҍFk6ŨņŒ’‘ûØčČ^˰üøĘŊN­Նfzû šŸõĨĻ•`“đ×g—´ M ÍęĪŲbÄ÷ÍčՍĢc6Ŗ„Õ[ɛpŠZLny͙äžU"^yhĄŧ›Í“FÎėė‰ŸŪĮ=,*p•`œúa€X$Ûšģy4õęĪJĻ)—œC¨}ëĒhĩ.E+ˆ2ģ9dģ§Œkņŧ)dđlϧ +ÛŗQ‘ŋžgø\Š.mĒĪ˙ÖXäI­Ŗ4‡’#¯zzQ;Ö"â›SM&;~3@Ä †¸ĐwFíōëûŦ|xyņ3n‰üEĒüÖNØ4ņÜ:NaÚX…Ƅ‹Ŧœą"¤y9m„…Âč¸8‡į ėSžpĒĄHsÃŲû´1ãđøЇ}Žį¤>TĄĀģyôU‡™Câe>դކ™ė¤ČģˆŠŗ#×a(4=Ķĩ}™ę ä†säuęŧrię‹ĐôbßģAleįė3Ž2ÔüĘO^_ž~‹ š  å~*„aü…./Ą8CI5='sú€û=´öŠØ]Iž(Ł"0HeīĀ;Tû!ĨTũl֙Ģ%`ņJ/[ßOƒŗE${Aaõ*S¤(–¯ŊlĮŸÕßF^ëąmGēĀĻ‚xMîôEDŋ¤­¨ŊFļĢ-’‚I™u‡ã•{/Ԛ2ę,ō&sŽGO(äÁsŋČ'Áúîļ$ÔO ×ĘLmÖmQlŠkJÅN7'=Ōa7bōÍ}ÃVƒŸ+œ2ũf oEF;ĨÆ´ŅžXÔīéŒ/‚#æĩЁ…Áđ!šŽúHģJ’ f s<4Üŗ4ÚϤ8%+°ƒų#ĸŖ5!4 žZŒŅå¸-ޤįöœÄĖÔÜ3§ \[\î•Đ4Äg‘å,Z RpO?…ŠzĢöULEôw[t;`—‡™=œQˇjæ`;Āũ“j, ņ^G‚EīŊnÅctKĸ"álv„Ū•Ā` †gģņĄN*Ø"wŽU…“žčnMÁ"Ã^Í\n/ĨŲ÷HŦo:ÖĀWī¯ÎŒãŊŨáĄwĄ.$xI5UÕTÖzđéã~ÍŗŊÖXū"‡ÂøŲāŠQƒĐŦĪ7ž¨ØeŸ„ŨļÍY™–s¤92áLģh 0’´@šÚŲ%ŦínßĶ|”']Ļl?į{ęīũĩ— ’4ŊLƒD8ONæ[@i:Sáš!ĻŨ1šÔDŽ7ŦŒã<­ĒL ĄˇtœėŧņBLėWL(âčûyåԛûžáR¸˙~5ÅÜÕv€ŸkšĒŦ›øŒ¤˙7BØ'9vãéĖųœŒŽ^­Ņēį4 õĒŌ/Ø4˛ĄÚ}I> Š=6čx ųAâS+Ā<žéÚ“y.IŖ—Át5†UOé“ķ¯û$ja{fāôxÎĐJbfĪ]tÖ+œ¨,DĖM“•īĖ}oÖíždŦörʼn\ c–0Iģ‹Ę‘CEôÚ^Ûũū^1Úo­OĐāŋé‘´™Čt›ā…ŨAæUn!ÉØļž¨ÔĢׄQjÜl>FX‚žīKXO;ęÚ(–dŧų‘ÛkÍQ‡wįGč;úûúŽzŧ=ĸŦøĢ°%8ēdæĨŠXÄAˆ‰F—nŽâ3a§œ}X_°ļč‘Îğ2͞ߐë„OvÁq<\xe‡`"ÎŦØN5†L'ƒĒgĒ“#z‰°ģÉ+wÖNUČËlŧåįšĐæëÖuųø“í#įö$ĮÃ6:;äŠíHčžīejP2vuą˜ņveŖ ÍL=ŲĘŧ9Ŗ\ZĐĩ˜ũĒ ͆Á÷™[XŦxëūË7uéö}˜ Å’ 4Į˛>Ņí—~5$•:GČǍęščąš88)YžãׁŧĶĐ/~UŖv2ų+žī>a*;ˆ×gē­ēîs…lč‹<5:Æ(Z͉ô~>ėĮŊ™âŪn/.sŊŨˇĻi<#ež\ļ\ā˛~ZA^IJ˜Ÿ:ŒīŊ§š%8kĻVŪ¤ŧŧ[K~_Â@č(x‹ŽŲB¨ ę¸áė7Ē$öQ‡L)Fb[ŋ¯Č´GRōÂC›ŨEW놐ĐíJīŽč°8Vp„›;1%ŗéJ5˙Ÿ-Ŋžļļx}(ī¯@†ãÜí–ō;\’/ģÎŌ*"ŊŌšīÚôķé§Yõ.M/ŲįÜĒĶĒ"ˇš9Wņīkļƒ+Ėn^{íÛáˇb§ûąė§—=VθxƒsĨĸ÷æÔ {Ō_ŧĢkĒ1ĢîÛÕȞpJaĩŨŊeU Ėoƒ?˜bÜšōėĻîābŸÆUG}ŋÎöpŋÚÕôŪ4ZåŖN%Ŗ‡ÂÎNļü–ŦÍ)SžĢŧ<īrR͊p-#ÁŗYĢös=~ÍpĒD;p-ËĮ“'į‰=Ū'ē+ Ų<ų§ūzé§j4ģ›L— ˙TXeą@3ã6ŖF€ôÚUĩíÉ9“{ _ÕŊ‘Vļˇ_×ÖvEûÕûŨßĩ•÷÷Ö51Ø?š']’ķo}ŽXTœéԄ{Ķ…-+8ėâ+>&xžnyÆûwˇl°Ķ@¯‰~Ŧ̞ė„^ŦtÚ2ãž´\đœĨrŨū˸'îá<ī"é+į)û{ų™ĶŒnÕw]Ōō{í ģ%=tVІ\|ŠÆ“ V­ÅX…ĻÔLx~eĮē™'eĢMß÷ˇ,× ˙Ŧŋ\Ė€BĀ5j°0 9'5ą¨$?7ą(›Ģē(ĩ¸$ŋt0W”2 endstream endobj 9 0 obj 17215 endobj 10 0 obj << /Length 11 0 R /Filter /FlateDecode >> stream xœ]”Ënã0 E÷ū -;‹ÂŽDI-´›,æÉĖø!§ÛPœEū~ty‹0‹D×4E’ ë—ãëqž6S˙ĖKJ›§yČéēÜrŸL—ÎĶ\íŦĻ~ûxŌ˙ūŌŽU].Ÿî×-]Žķ¸TûŊŠ•—×-ßÍÃ×aéŌ—ĘS˙ČCĘĶ|6^N4nëúž.iŪLSfHc ÷­]ŋˇ—djŊüxĘûiģ?–k˙<~ß×dŦ>īˆÔ/CēŽmŸr;ŸSĩošƒŲãĄJķđßģyĨûˇ6Wû‹kĶ”ŖhOíĄu*:ZÕå(öí;؅v:@ÛFu9ŠwîÆgú?íęrę ú‰ú z Šú|"™Ŗ2÷ôé‹vds`säqĘÃZ,jqds`s: ÎČ8#ėdv`ļd°`pĖë×ĩÔ-4™˜9rRGÕĖ5ûĐa|A|O~~a]ĸuq.sÖ%¨KČ)Ú[úDõ!›€-2fDĖ@{€Ũ’Ų‚Ų3Ž×ų’3€Ķ‘́ĶR[]G˙šü^gDŅēØsAĪ=™Ŋ2ŗĪ‚>{2{0{æõČ+d0{Æô“ū˙Hū~!€'ržķúˆÖČŧyũü…3Ė4˛?ũņŒS,ŅĮļ`°÷Ÿ{Úßr.+ĒŨMlå4§ĪīĮēŦ¸Ĩŋŋ„÷ endstream endobj 11 0 obj 524 endobj 12 0 obj << /Type /FontDescriptor /FontName /LLGQIS+NimbusSanL-Regu /Flags 4 /FontBBox [ -174 -285 1022 953 ] /ItalicAngle 0 /Ascent 953 /Descent -285 /CapHeight 953 /StemV 80 /StemH 80 /FontFile 8 0 R >> endobj 5 0 obj << /Type /Font /Subtype /Type1 /BaseFont /LLGQIS+NimbusSanL-Regu /FirstChar 32 /LastChar 122 /FontDescriptor 12 0 R /Encoding /WinAnsiEncoding /Widths [ 278 0 0 0 0 0 0 0 333 333 0 0 0 333 278 0 556 556 556 556 556 556 556 556 556 556 278 0 0 0 0 0 0 667 667 722 722 667 0 778 722 278 500 667 556 833 722 778 667 778 722 667 611 722 667 0 0 667 0 278 0 278 0 556 0 556 556 500 556 556 278 556 556 222 222 500 222 833 556 556 556 556 333 500 278 556 500 722 500 500 500 ] /ToUnicode 10 0 R >> endobj 13 0 obj << /Length 14 0 R /Filter /FlateDecode /Length1 1862 /Length2 6717 /Length3 545 >> stream xœíšgT“Ûļ†i"„ŪEÚH/ Ŋ÷Ū;ˆ!@HBA@iŌĢ(MĨISz•ŽtĨH/‚ôrãŪįlŨ÷ė_÷ūģã&c$yÖ\ķsÍņŽīW¸ØÍ„TœŽ0M$#$" ‘ áŽŪh3„ž*ŌŨIãā`’ ..s8Æö;@\–0ŽDČū{¯ æ€Á.¨;`°)æŽŪ€Šˇ ˆ"â˛b’˛âR€("õīH”,āŒB:‚¸Ôžū(¸‹+Føë'ĀkajÅ' økEDFFpô˙wP‡Ąá.€ûoØU˜;Ō͆ĀČjØeww8pq÷÷tENN0§Ÿ–î07@î÷ôDúŧj|WÁ6*"„ũqZ0 …=Ō‰?‡Ļ‰DšĀū8 DJT āuÅ`váĒМTîŌ„Mĸ \¸J?øÜtéŧh­~ē™wøšOæOŨ‡`j˛Ŋ¤ŌŅėBHd§ĸĐGb“Ņ&ãî—fß°¯=6ō‹ĮGå“NÖjK-Žņs-ģ‘5Ļ;¯ĩ ĶßŗîĻ[yŒË\X†JČđzןŒL]'~zV Ļĸ–•ÄėâéÚČÃûƒJ^^ūØōũEGpōyU@w…ŗü{üĪËNE65‰]DōB˙œ#ũxeŧˇzĢVĄß§m­ TPZœúšBēÚŦÍĸ˛dNQ=Ųĩģâ~ē ‹üß\‹lž,[nV÷mckė{íK“LãEA°ķÂøöÎ×0´Ē&dÛaS–‘ĶЃa:+čåUMęY†…×ÃGÁއK>='ہV‹bÎ1ę 5÷TâûęZéeŽĀÔŊ_Æą?˛å$ØIãˆh}Ú´?ƒ?iąũ„ŦāėŽßXÅļ§âŖą÷‹k§@¸ŠÂõ•ŋJhŦŒ†×9ëĢ9…éQģų§åĀY5IVœØPĸņ)™G €ü’§úĻ!×=5‹,"<<pR—´ÖRŌ >BŖ*ē]@æœAŖĒâWCę#ëöŦXâãđJ÷cŪ>¤V~”ēž~N ğx\pĒ!˜ušˇíãØĶ—įžįY7­Ū<”Ŧcœct¤lĪī3םÉREœķš6ž—‹ŪĒō[y°o(sMĖšĒSÄôôĶ'r•€ĻŗUđíAC2œ/Ž[åF(Đ#9"o#rGĻÃ'†O´īĖĸØû¯Sw´>C×e §¯\iÕi•žŗ˛ßëúegdŋüøKDĘÛ 4ĸsãēË=“íwÅʏ8„SčBrÖĻâYvÔ{uQŨ•Âũǚ{ģ‘÷äXé´ķŧį>ŋ.=ßąÛđ€Í|ų‡CđLÉ ­éúƒ9quĮEČÆį|ž!ŌŽŧø¯Nŧđ˙ļîJr Ä2QęR„LēDĩ”Ŗ^HFJžæâhÆšmįõÃÄ~nĐ8Hč‘¯Æ2ølņr:í5šúė/ŦŸÉ7ĩë'ÖlëVARīŽ“X{îŠyĘJeŗ™ŒM}”Jâ]bg„p°ô[|ᔷ:2â˜îež¤”OiĨvŋ*0 Ôî˜hĢB‡ÛīúŪSôXKLFŦ2%vŠ0=ëžarōQx뤪H×ŧîë=žIYĢŪ°]Ž%(›o‰VxČ-‘CŅwōv]ØUĐ—´´S,ėyĀüpÆäzŌÂÔ)áĩ÷đîŪŗWQLE0É;‡Ģ{l‹”čJKAôėG´ctBÅ÷ÆkŧÄė1oŋŲđ¤~ršĘŠôrÔ䆑EE­Tčā Z¯kG=u–æ­'†zŨˇn…Yfŧ‘/Ŋ8՚N9Q*DDŊ專FđŨAM{Ų“Ą8žWą>åF:HŊËĖČÍ,W;Ōq˙B=$’Û_tŋúp'éDņĐ´+Š´^e:Cs˙ŲŌâ>fŋœMsĪ&f{@tãē%ˆm\gÜOØIąOŖ-š-ų’×ÉėÚŪ€Ŋ´iY†Ģ‰ÖįCPŌ]|3fpbVdŽQžšW-)l=ÖĒ<ä­wĮŨ$Ų0&´Á“pÔ`īúHēĪN3ƃ÷BiQz‘ø(Ôč†p…_>˙ˇŲöšīûŒ!ēÕķ|“nĄSÔIĪžĄĻV{ƒˇÃę Ŋƒ´ ¯ÆšGël{äĩŊIīL¨Ū°t-“ŽŠr0-ņ•Đ~áYPėšāqׄCļšbũāū§|CqZy×FŧIfEm[NĒä…}.ÂJ+ĄÛf#†öSŊ8—u_iYÃŌduΆ 2fS§äĀâ>ę#/īq4øíNĨyQōQų(Z‰ré{MØĄ ŧäš{Чj^L^¨Íá4Ô~5âķím_?#XĘÆę'Ģ"`ä`4j FZæHƒ¨ÃŲõ!Ļ”šÜŠI5—ÂĶ„Đôvũ' /Zåšb> ÕŲ{‡ĩi‹dˆ†wīĢJ}ˇP>`9>Ję‰×ČŌɛí,˙œ…ßņL:#–ˆ•¤(‘réâûĩ ,jūkÉãŦ"CĘÖØÛmbGΑæJsŠkåFė–ā<Š˜Neūáč6Ãrf~B˛äČÆŠaÅöÁ•267šd…Ģ)1ëŸÔĸ{‚G@".×WM;U_Æî9ÁˆLÜ†Ķ újĩČÄY^N}¯ƒ5{ũžėU{H[ˆ˙M?ģÄāEĪ>ÆŊèkŸ1z\cgĶ™i…GO,WOo‚‹î|k¯G;Åô÷åĸ&ž¸¨`B]Ęĩ öE‰zˇtŅįįxæ_ašÍHķ/K‡ÎA)†ĘíîqŠnPÖuœŊ÷]đ0š™¤Ž.ĪŦu?ŋāÁˇgÛ ~šĖu¤tŲˇ™į¸đŲŅ;zŅ“süÁžÔœž%ių*ᝯ& õė×Ĩ‰nzÎŅX?뎲âœÛG Pö.áĢ…"Œ7Zláąõ&ģ 1UGia雸kZnåŠéšpōˇė‚đ`81ÁãeÃĶÔ÷nܒ1§&Ÿ&ŋœvÄPKD;Åq†ķgƒ\7ŋļÖN“Æ!áqãEsĶqÃ6K5 &˛ 3ūĸ÷\iĸŠŽL$°/ņĖãMØŅSMäûņ}(žžÖË|RŨķy§bBh`„¤MooĐ,+V˜ũ Ņ÷ķ– ą“ļƒfgF•´vÅõū.ŅŅ čhČÆ Yâ“L“î“ØbįÍųy•bÉŧ5ķŧūAKøLU˛ūGĮ•W'‚)įpō" 6 üā kā_eįÉhÎ7x,ŧ˙üf’ĸÔ>—æ=@-ĩå”bįjņÍ}~ĻĨ•mčÁÄĖŗõø ‚ÁVH,˜xß'Kž¯ôfՑ\kĢšôŖ^ įū„<9‘VY]ĐL§šiČj:Ė>7|Ú1eRt78ã@>%čĶNYjbÕlĸˇLMÁD}áÃk Gpƒ?^|9ãâÖę‘ô$hķfxúÛę ‚^Ή'ÎŦ+í1ņ¯`PŲ×ņĪÂD>_Յš`įéĪî‰ xXŧļI‘[9$0TqlkŊ¯Ž˛ÜÎ ėØŪ´žÅčŲ2°Į♠Ũę3˛ĶĻ}jøiáJĐû}–ZÃČA-{ŪgËģ%Å8Öė wŠéIs†nOÍʏëY—yô˛63Ĩī ÷p—Ž´“5IŒÉôåvˆą]Ę>[_Ķ ģĻZģëq_Ķ@fšŊŋŋĶ'ÛGžŨ 4úVŧ× šęg3é“ ­ÉøRßĘŅĩ.â ;ngâōz“¤Ž{ąí$ŌÚãqâr†}&7ĖR|Eöūu 'ô†ƒ™Ė‹/6ˇÄ lK‹Y0š]ëßɧ„ž°˛eįö¸s*āĪ{'mdÕÜeoLf,ø[ŊŽĐŦįéžây¤æ/ųÉ]T̝Ié­ŧrŨÂÜ~ĸ¨[jēžÎÚį™$Ō‚yÖĘĀc|šŒö;_ߜnĮL Ŋ-Ėe‹ €ī„Mœ“Í5á‡ną¯ ‰>§Šø_ŌY~_GÃũņ97îác6ŋ[›œˇvĸ%‰C4hīJ÷}āÖaĄ‹"w8}VdŽž šMtĢ &~QÜĘŋ“Ū4d§Ąžü&ŊvKü4N“Ą/¤ygŒ´Šmç!d¯Í[rŠŋS+æ<°EaÔ<ÃôŖÜ?F8JÚėE”=ŅNÁP¸ū§ãɡ‰-*ŊI:cb†ŋžz˙šũū#žiŽ!ę–ôOįÆ&#ō8 +¤ŪKĮž~Š;ę—múæũœĐg]d+ÁŗâĀÉÍ:˙Ô)}Ņ@Š™‚Ägy Sb&kE.ƒ?7š:Kˇ‘H)›°ˆ•)SŲÉ!4ųSCiÕԌšP§IBû‹Ž‹Â˜ĩ(r͘´ZÄ rŠ<úģüžņyÁø6n8Ô´’TŗM;¯ĢMãš}Y­J]|īđn~ߟĖknôÔrJ’+Ú;= XÕ+ĄßĻo~ 8pƒįŌûo/R‘ŨšÂ ?87' wŦ–öW+D<ŋČ >eUä­¨gĖ;î‘7XiF_3bÎŅXs Ŗ˜b;īĸõhā¯Á ¤]†ķ”âŦĨ$~Ē_¯FģIfúŪ;­¯F‡*Ę|rė—āŦSeĨō˛ŅËú´é8‚Ŗ—#ļWמ–@áŽS…3ÁČtŖĖģēg§–¸Ķū4ΖbOËvxp‚°€*ū7›)‡äū~Z8Ō]Qė´'nōö•JÖTĐ-'ĸ f!Uc‡ā-ÜLČŪĐŌōā1ëm‚FQļS™¨ĢĀ΂—âÄB 2Âé}žé7hÆtęĨûËÔ@æĪ_vŒ.ačČĘÚIãH2™6Eŧ‹áĐ ˆC;˙Ž)‡ÎwŌCmīȍn_ŊĨčŅéŒâ&iÁ˛ÖdŨzÈĩĶĮxūøÔ%BŊWėe, 7h*šŊÆÚTv}‹ŗĮ NˇŅ_z'ĪUz÷í(ÁëüT<āäUq‹Ēdįה RbšPܘĻΞ‡áfÕē(é‘ī8Žņõ´Ždļ;N3¸ũ€ļæ¯HÕ)ķ°4ĩT›´ģõëņFOŒ4ŋ֑ĮËw2C“´ø™„Mãå-S›NŒĨËĶâ—4+įĻ›f9Šģ7Ί „›wŽŠÜøqž'‡ųŲ3ž{ũŖAŲĐÂ*ÃØ€ ė oAÜf ĒÎ74 sdܖåŧeÕy¨žģËjô^…Ėū‡JG:ųʃHcUĪė’ 6ĢÇĸtæ¨H}˛RJ}“ŋĖŲ+ų+‘âļ<^ÉÔžĪô<ēŋ?éā+…œ¨ÅjÕ9g۝¸_ĐŽ|Z–Žø×Šû>o,AŨϜIĘŧ‡¯l/ "ˆí[ičutø2›c?ØhŨđč øŠšM-?ÅËhU'ŧøFPéu<°Vqk§CķšjriĸĒéÕĢŪÎÚ8ĸv×’ÄÄųü>Ĩ:o§‚ŧ<‹4CŠHĄâB@čũúí¤2‘ųī-ô8§´^!Vģá ;ÃŌh“xB‰žžÖÃũv?Å0ž™ĩŠ~ķ1éž;ŠXĩ… :5 J"¤^ŊÚ|ąëōJÖU>ųC”C„ōÉXQ§ŋíáF,HŲČûHîŸ}ÍiöaxŪĨŽ:Žģ§ŧú;ŒÅ”†ŸĩČŨÍ—zī§{dANĶđDģB5÷Õũp‡åmKVwĩšŒ Īß ņ˛á‘ãŽ8/ŨĪđ)đÉÄX•ÃX9Aįt¸ŖŊæŗžēĶ}ī@čøeĪãÃĸKß>ų1Qa([0=) ÍéqmV贄,ب?;˙ŅVĮΡԆqŨŠ Í`8àt]OžØŖvėæ ^¯5ÍĒ`ķ€žĄ=ifŗíüēYí3nŊčŅōœēîĘŦJ°Äˇo1Sī|–Í+ĒŪ¤ŋ_@ÜØē9ųJw…ž=[’ēMāē2¯VR1Yß-ĒDĻā]UwúT +FxD`sŦ+îjoĨ˜ųõŅMŧLōđ”xE)_kĶŲŖëS}ųņ”sß-âŋĄ{$\+UIN“‚Z/2›l ķ„Ũ*[Ži˜,Cüā‘pųēø™mœxîĻ›\ē12܉%AfĒ9åú¤ŠšŌÖ :Ģ+ǎˇOajÍ.*Ũ ŒÛ‹ķzMžôŌÛŨ¯uOĩļ ‹9’ŽŨ ĐĄ…Ĩđ…ƒlņĨī#m1rŌbWČäfũ)gŅ‚ä¯ëÛI‡Ã”üXķEönîVΓŖšŲ_÷ OuĶŅzŋÄ|•}q?ÆÕģĄ4¯ņ)Ę\FĻW nЕ1$tí#ŗŸ™ļĘJRvKÔo)œ^ŖŖāŖ(]TڈŋŸ˛J/§Ü{zŊ°hødĄĄL¤åĸfŽęŌåģF÷Ї§+ĻÜŖŲ=:ŨeU´<ČŧâÚY -ׄēvDN"×tqßjŠXŪŪŪęæÁÉ_ŋ+K,ĶĐvnē>ąŽ—} å ˇ÷ôV}PË1%ØW‡ŅŪV TĶ5™ü͌įHzpą_NsT—ķˇY›+KMÖXÃQŌ‚Ž'7IÎ2æW&I3Ÿt ĻI3ōŠî’ęŅŧ)ĸā{BŨū ä.7@e•œũ¨ú/@eĨ,>ß+tâJW1ûu‰öąDKũŠ÷’ vĩ—äi-ŪBÕˇpoUĩ ņE”XXĮg-ŲmĒB¤jÂdę:bˇŖØėęאâfŨíČũύ3”ô)ā–÷8r`pŠ(c|\ÚØsßŧ!âfŒ]™#´ĸ÷~°;>Ũ+¸RØÄ]mô’†]ydįîTļÆEēŽ“ÜzíéJÔ;VížĐįY™ÔMŦø_ÚGî]”MļŽÆˆģb–š=hƒäōøŠ%%釧øŌėhŪÚ.‹é‘ņ _úpÛW&ZņHË„­57ĘÛ¸ˆ īaËU‹Z7*Ģl0–EJ8įŗÁÚVķû/:h)h_)Ŗpæŗž0bpĶįŖŨ9’É ;RK˜L+™š ík–°{&Ė'c t—¤%qĒ”ŌÚKuØŪÎ[™ˇ­JĪ\kHÄiô M]d=ŠčķŊ‰ ĨLÍļ,ŒpjŪpšCą0’Ū8øėL6mŸ.ÍŌa'ŧÖÉ~圯'ævˆjWĒšđ@gųŖŅaÛĢ5¸ƒáO†:Äâ]ã‡ēÜâ‚GĖ Ķ>5õ”ôD5z2D*$Tcnį“m•eėesIĪh‚øŨę rŖ‡•öĮÍyŗÕėœģ‘–Kņõ†åȌ%Ũ\ [*RĶö€ÅJûŦnZ3Õ1H‡Ÿ›œŨyÃŦŖÎŅtOâŖN B՞>m䓷,mAÂÍģ¯î­V“}_0<Ũ– Ö>ˏT›ŖāfØúũ¸0f¤Ē]āPQSėĮˆ“—0…ė%°‡Å+¯ŪûÖ3ڍ\VІ,ĄkvëĮ—ĘŌ ŪMȍ˟…MōœÕĸˆ„ũú/‡ŨĘ1-cËF(2׆ßĩŊQĒA‡ÎqšžHņÅ vČĪũbü&ãmŸá^ÆKŨZ¸}3]5åũ…E›ĪģäŽļ‘Ÿã(ë˜'ķ5īfīųĘŪ– …Sˆ>\ūaŪ|[3—ˆmL3ĸrÔĀE 'ČĻPÅlˆÆ2úŖRų“‡MâįņūįôŗŖīeĻdÆŽĩßî×´N8pš“ž÷zØ8ézÁ›ZŊŪ=BŲ(Ō§vTՀSārEאôyG;ÄhsžŨŠ3œ”ošoüĄ;Öü 9‘0–Œr¸´ĢhJÉá~ڂ†s‰R)īÚyöĐ=ūž™fŌ+€.ÝŧĩëZ¸ĒĨHbž1vL=q‘DĨ†2ŨPIä­ä×čĘŠÂÅ>ū>čz×_ëĨpž$šOŪĪWØī OēˇÃG~WÁ÷¯AŠ=ō×ëuDųôÃTEw:>žÍ&ØŊĖ ÖBĄ“B%ƄIø"/4 p„zI{žbڝ˙°=íe˜÷leåš ^uuÄȉúČ|œŒÛG–BxÅ}ē kĮ7ųŸŲĮ•†ŽR¨Éú#GôÂZQi‰ö‘n.‹?´ŌDk-Æô'äa.MĢÜĖtœIz‡ü4&ČAo%ō.$W¯J_‚ÍpæzNúÛØ\ęn{p@;É"Op-ŋÎMhđœĮąG~ą•d1ÖĀū¤ŖũyņlŖK:^ūŌE[¨`6%ĖEÄÚdH'ĖĨâe¨õ5ÃÕæ­<Áē†ŠLŲT÷ Â×Ú[ËŽ+nô ŲąXūŅ5Õ˛˛“7æ^ÍÛîâĸšФK=îÁfũūZ‡ųô1åĄų‹ây!n((8íũåwJw–qÁ~ōūjDßsEĘsÛܜÅŽrrxķ7ydž–į}ąPÕ1ˆq›‹¯RC::¤kqš>§Ãĩ!TÚpc‹› ˙Ëč˙ūO@Ũa( ŌÃå DÁĐ$ęįŋ*@ ˙ŠQɓ endstream endobj 14 0 obj 7819 endobj 15 0 obj << /Length 16 0 R /Filter /FlateDecode >> stream xœ]’Ënƒ0E÷ū /ĶE$~4ŠTĨ}¨i?ė!E*9d‘ŋ¯Į7JĨ.€ëë3ã<ÅĄyn°Čâ=NîH‹ė‡ā#§Kt$;: ATéˇÜVųíÆvE >^Ī Mč'QײøH›į%^åęÉO=)eņ=Å!œäęëp„uŧĖķYŠũ^zęSē—v~mG’E^7>íËuÂūˆĪëLr“×Jr“§ķÜ:Šm8‘¨Ër/ëžß ūßŪV!¤ëŨwE­\BË2}DmtÖé“t]ą6Đ&iĨĀ̤-´e­Ąuö7đ7ĖīĀī8“yäÔœĶ Ã5XÔ`šõˆØGöˇđˇĖÃ7ėkøš}‹šmŽ™Āk0†_eßÃ÷ŦQŗÉ5ƒWųŦ~Ī>xÅŧ-qVÉ z4ÜŖęĀtĖŽėkäלßĸwËŊkôĢķ?ˇČcķÅŨnˆ¯gí>îc‹<yx†@÷™§™Ŗōķ 'ž˛ endstream endobj 16 0 obj 378 endobj 17 0 obj << /Type /FontDescriptor /FontName /LNCJWR+NimbusSanL-BoldItal /Flags 4 /FontBBox [ -177 -309 1199 953 ] /ItalicAngle 0 /Ascent 953 /Descent -309 /CapHeight 953 /StemV 80 /StemH 80 /FontFile 13 0 R >> endobj 6 0 obj << /Type /Font /Subtype /Type1 /BaseFont /LNCJWR+NimbusSanL-BoldItal /FirstChar 32 /LastChar 121 /FontDescriptor 17 0 R /Encoding /WinAnsiEncoding /Widths [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 722 722 0 0 0 722 278 0 722 611 833 722 0 0 0 722 667 611 722 667 0 0 0 0 0 0 0 0 0 0 556 611 556 611 556 333 611 611 278 0 0 278 889 611 611 611 611 389 556 333 611 556 0 0 556 ] /ToUnicode 15 0 R >> endobj 1 0 obj << /Type /Pages /Kids [ 7 0 R ] /Count 1 >> endobj 18 0 obj << /Creator (cairo 1.13.1 (http://cairographics.org)) /Producer (cairo 1.13.1 (http://cairographics.org)) >> endobj 19 0 obj << /Type /Catalog /Pages 1 0 R >> endobj xref 0 20 0000000000 65535 f 0000035097 00000 n 0000006819 00000 n 0000000015 00000 n 0000006796 00000 n 0000025398 00000 n 0000034629 00000 n 0000006947 00000 n 0000007163 00000 n 0000024506 00000 n 0000024530 00000 n 0000025133 00000 n 0000025156 00000 n 0000025930 00000 n 0000033878 00000 n 0000033902 00000 n 0000034359 00000 n 0000034382 00000 n 0000035162 00000 n 0000035290 00000 n trailer << /Size 20 /Root 19 0 R /Info 18 0 R >> startxref 35343 %%EOF trillian-1.6.1/docs/storage/storage.md000066400000000000000000000326321466362047600177430ustar00rootroot00000000000000# Storage Design Notes ## Author: Martin Smith ## Tree Node Storage The node level of storage provides a fairly abstract tree model that is used to implement verifiable logs and maps. Most users will not need this level but it is important to know the concepts involved. The API for this is defined in `storage/tree_storage.go`. Related protos are in the `storage/storagepb` package. The model provides a versioned view of the tree. Each transaction that modifies the tree results in a new revision. Revision numbers increase monotonically as the tree is mutated. ### NodeIDs Nodes in storage are uniquely identified by a `NodeID`. This combines a tree path with a revision number. The path is effectively used as a bitwise subtree prefix in the tree. In our subtree storage optimization the path prefix identifies the subtree and the remaining path is the path to the node within that subtree. The same `NodeID` objects are used by both logs and maps but they are interpreted differently. There are API functions that create them for each case. Mixing node ID types in API calls will give incorrect results. ### Subtree Stratification As an optimization, the tree is not stored as a set of raw nodes but at as a collection of subtrees. Currently, subtrees must be be a multiple of 8 levels deep (referred to as `strataDepth` in the code) so it's not allowed to have e.g. a 7 level depth but 8 or 16 is fine. Only the bottom level nodes (the "leaves") of each subtree are physically stored. Intermediate subtree nodes are rehashed from the "leaves" when the subtree is loaded into memory. See `storage/cache/subtree_cache.go` for more details. Note some caveats to the above paragraph. If depth multiples other than 8 are used this might require changes to the way node ID prefix and suffixes are packed and unpacked from byte slices. There are additional assumptions that all log subtrees are the same depth, though these would be easier to remove. For maps the internal nodes are always cleared when stored and then rebuilt from the subtree "leaf" nodes when the subtree is reloaded. Logs use a modified strategy because it is possible for internal nodes to depend on more than one subtree. For logs the internal nodes are cleared on storage if the subtree is fully populated. This prevents the loss of internal nodes that depend on other subtrees as the tree is growing in levels. This storage arrangement was chosen because we have predictable access patterns to our data and do not require classes of tree modification like re-parenting a subtree. It would probably not be suitable for a general purpose tree. Subtrees are keyed by the `NodeID` of the root (effectively a path prefix) and contain the intermediate nodes for that subtree, identified by their suffix. These are actually stored in a proto map where the key is the suffix path. Subtrees are versioned to support the access model for nodes described above. Node addresses within the model are distinct because the path to a subtree must be unique. When node updates are applied they will affect one or more subtrees and caching is used to increase efficiency. After all updates have been done in-memory the cache is flushed to storage so each affected subtree is only written once. All writes for a transaction will be at the same revision number. Subtrees are helpful for reads because it is likely that many of the nodes traversed in Merkle paths for proofs are part of the same subtree. The number of subtrees involved in a path through a large tree from the leaves to the root is also bounded. For writes the subtree update batches what would be many smaller writes into one larger but manageable one. We gain space efficiency by not storing intermediate nodes (except as noted above for logs and partially full subtrees). This is a big saving, especially for log storage. It avoids storing entire tree levels, which get very large as the tree grows. This adds up to an approx 50% space saving. This is magnified further as we store many versions of the tree. For the map case things aren't quite as good because multiple subtree revisions need to be stored with the same prefix but only one "leaf" node differing. The efficiency of this needs to be determined for large maps. #### Subtree Diagrams This diagram shows a tree as it might actually be stored by our code using subtrees of depth 8. Each subtree does not include its "root" node, though this counts as part of the depth. There are additional subtrees below and to the right of the child subtree shown, they can't easily be shown in the diagram. Obviously, there could be less than 256 "leaf" nodes in the subtrees if they are not yet fully populated. A node always belongs to exactly one subtree, there is no overlap. ![strata depth 8 tree](StratumDepth8.png "Stratum Depth 8") As it's hard to visualize the structure at scale with stratum depth 8, some examples of smaller depths might make things clearer. Though these are not supported by the current implementation the diagrams are much simpler. This diagram shows a tree with stratum depth 2. It is a somewhat special case as all the levels are stored. Note that the root node is never stored and is always recalculated. ![strata depth 2 tree diagram](StratumDepth2.png "Stratum Depth 2") This diagram shows a tree with stratum depth 3. Note that only the bottom level of each subtree is stored and how the binary path is used as a subtree prefix to identify subtrees. ![strata depth 3 tree diagram](StratumDepth3.png "Stratum Depth 3") ### Consistency and Other Requirements Storage implementations must provide strongly consistent updates to the tree data. Some users may see an earlier view than others if updates have not been fully propagated yet but they must not see partial updates or inconsistent views. It is not a requirement that the underlying storage is relational. Our initial implementation uses an RDBMS and has this [database schema diagram](database-diagram.pdf). ## Log Storage ### The Log Tree The node tree built for a log is a representation of a Merkle Tree, which starts out empty and grows as leaves are added. A Merkle Tree of a specific size is a fixed and well-defined shape. Leaves are never removed and a completely populated left subtree of the tree structure is never further mutated. The personality layer is responsible for deciding whether to accept duplicate leaves as it controls the leaf identity hash value. For example it could add a timestamp to the data it hashes so that duplicate leaf data always has a different leaf identity hash. The log stores two hashes per leaf, a raw SHA256 hash of the leaf data used for deduplication (by the personality layer) and the Merkle Leaf Hash of the data, which becomes part of the tree. ### Log NodeIDs / Tree Coordinates Log nodes are notionally addressed using a three dimensional coordinate tuple (level in tree, index in level, revision number). Level zero is always the leaf level and additional intermediate levels are added above this as the tree grows. Such growth does not affect nodes written at a previous revision. Levels are only created when they are required. The level of a node is always the level in the overall tree. The `NodeID` coordinates are independent of any subtree storage optimization. Index is the horizontal position of the node in the level, with zero being the leftmost node in each level. For example in a tree of size two the leaves are (level 0, index 0) and (level 0, index 1) and the root is (level 1, index 0). The storage implementation must be able to provide access to nodes using this coordinate scheme but it is not required to store them this way. The current implementation compacts subtrees for increased write efficiency so nodes are not distinct database entities. This is hidden by the node API. ### Log Startup When log storage is intialized and its tree is not empty the existing state is loaded into a `compact_merkle_tree`. This can be done efficiently and only requires a few node accesses to restore the tree state by reading intermediate hashes at each tree level. As a crosscheck the root hash of the compact tree is compared against the current log root. If it does not match then the log is corrupt and cannot be used. ### Writing Leaves and Sequencing In the current RDBMS storage implementation log clients queue new leaves to the log, and a `LeafData` record is created. Further writes to the Merkle Tree are coordinated by the sequencer, which adds leaves to the tree. The sequencer is responsible for ordering the leaves and creating the `SequencedLeafData` row linking the leaf and its sequence number. Queued submissions that have not been sequenced are not accessible via the log APIs. When leaves are added to the tree they are processed by a `merkle/compact_merkle_tree`, this causes a batched set of tree node updates to be applied. Each update is given its own revision number. The result is that a number of tree snapshots are directly available in storage. This contrasts with [previous implementations](https://github.com/google/certificate-transparency/blob/master/cpp/merkletree/) for Certificate Transparency where the tree is in RAM and only the most recent snapshot is directly available. Note that we may batch log updates so we don't necessarily have all intermediate tree snapshots directly available from storage. As an optimization intermediate nodes with only a left child are not stored. There is more detail on how this affects access to tree paths in the file `merkle/merkle_paths.go`. This differs from the [Certificate Transparency C++](https://github.com/google/certificate-transparency/blob/master/cpp/merkletree/merkle_tree.h) in-memory tree implementation. In summary the code must handle cases where there is no right sibling of the rightmost node in a level. Each batched write also produces an internal tree head, linking that write to the revision number. ### Reading Log Nodes When nodes are accessed the typical pattern is to request the newest version of a node with a specified level and index that is not greater than a revision number that represents the tree at a specific size of interest. If there are multiple tree snapshots at the same tree size it does not matter which one is picked for this as they include the same set of leaves. The Log API provides support for determining the correct version to request when reading nodes. #### Reading Leaves API requests for leaf data involve a straightforward query by leaf data hash, leaf Merkle hash or leaf index followed by formatting and marshaling the data to be returned to the client. #### Serving Proofs API requests for proofs involve more work but both inclusion and consistency proofs follow the same pattern. Path calculations in the tree need to be based on a particular tree revision. It is possible to use any tree revision that corresponds to a tree at least as large as the tree that the proof is for. We currently use the most recent tree revision number for all proof node calculation / fetches. This is useful as we already have it available from when the transaction was initialized. There is no guarantee that we have the exact tree size snapshot available for any particular request so we're already prepared to pay the cost of some hash recomputation, as described further below. In practice the impact of this should be minor, and will be amortized across all requests. The proof path is also limited to the portion of the tree that existed at the time of the requested tree, not the version we use to compute it. An example may help here. Suppose we want an inclusion proof for index 10 from the tree as it was at size 50. We use the latest tree revision, which corresponds to a size of 250,000,000. The path calculation cannot reference or recompute an internal node that did not exist at tree size 50 so the huge current tree size is irrelevant to serving this proof. The tree path for the proof is calculated for a tree size using an algorithm based on the reference implementation of RFC 6962. The output of this is an ordered slice of `NodeIDs` that must be fetched from storage and a set of flags indicating required hash recomputations. After a successful read the hashes are extracted from the nodes, rehashed if necessary and returned to the client. Recomputation is needed because we don't necessarily store snapshots on disk for every tree size. To serve proofs at a version intermediate between two stored versions it can be necessary to recompute hashes on the rightmost path. This requires extra nodes to be fetched but is bounded by the depth of the tree so this never becomes unmanageable. Consider the state of the tree as it grows from size 7 to size 8 as shown in the following diagrams: ![Merkle tree size 7 diagram](tree_7.png "Merkle Tree Size 7") ![Merkle tree size 8 diagram](tree_8.png "Merkle Tree Size 8") Assume that only the size 8 tree is stored. When the tree of size eight is queried for an inclusion proof of leaf 'e' to the older root at size 7 the proof cannot be directly constructed from the node hashes as they are represented in storage at the later point. The value of node 'z' differs from the prior state, which got overwritten when the internal node ‘t’ was added at size 8. This hash value 'z' at size 7 is needed to construct the proof so it must be recalculated. The value of 's' is unchanged so hashing 's' together with 'g' correctly recreates the hash of the internal node 'z'. The example is a simple case but there may be several levels of nodes affected depending on the size of the tree and therefore the shape of the right hand path at that size. trillian-1.6.1/docs/storage/tree_7.png000066400000000000000000000404761466362047600176550ustar00rootroot00000000000000‰PNG  IHDRHˆŦžîĐ IDATxœíŨ{@åū?đ7, (((ÆM.‹`О( Ū8ŪĀ[iu Kŋį-ˎ…'Íō”÷Ō4-éXš)ÕņFŠ™e‘÷K`ĘM.Š&Č ¸\]q%øũņøÛ8 ËėîĖėĖđŧūŌŨŲ™GgŪķ™ĪėΌUSS(Š’K€2—N8u Đj-3ŒĐP„„ (•đđ°Ė(=+Zą%-%˖ÁÃ~~đņģ;\\,0Œē:TUAŖAn.._Fx8/†ŊŊFB4ØRĨĶaųr¤Ĩ!.žž–M3 ƒ¤$œ;‡%KaéŅ´W4ؒTXˆŲŗ1p ĸŖĄPXz4-ŅhÁƒąrĨĨ‡Ō.Ņ`KÃ`Âŧø"‚‚,=ƒ+Wâ•W0qĸĨ‡ŌūX[z”ŅÖ­ƒŋŋØS @ĄĀėŲX´ČbįķÚ3l‰),ÄĄC˜>ŨŌã`ĮÅąąX¸ĐŌãhh°%fölL›[[Kƒĩ#PQ”KŖĄÁ–­Z­›S(0~MMM•ßĻĻĻ˗s.^Ėtpčú—ŋL˛ĩĩĶŋĨ?"ČÎN-(ČņđP:ŪښVņĸëFōrrN74Ô74Ô/\øüŨģˇkkkĸŖû^ŧ˜Ų|Žö­ˇĻ|ôŅëÖũkíÚšO<áUWw_?Áũû÷Ū~;f÷î vvö55Úyķžš{÷ŽūŨŒŒã:9~õÕĒ#Gö8::}ųåōwß}A¸!eöäˆ`üøŠú‰ķķĶ}}û88t%MJÚ¸wīĻŪŊØÚÚé?5sæ{üĖŖÚDƒ-m99Š ĶĐ<ĨŠŠ?VÍęmj}}Ũˆ“õ‘KO? 4tTķų,Z´ÅÕĩį/ŋėĖĖ<‘™yBĢŊ1}ú|ōVFÆqoīō×ë×5—/įL™ōŠūŗjõś7+rsO[[+ÜŊ[íææŨŖ‡˜nūĐūГgŌ–‘qÍRÚĐPôhRXب=<.]:W[[séR€ŪŊƒõIO?æââîãĶûęÕßkkkOŸūĨĄĄū­ˇ>=t¨rëÖTŖG÷’‰īŨģ›ŸŸöįqûáÃģĻņã§2 “ ´´ā§ŸJ~ūšôĢģve{{÷ūčŖīôę”EĐ`KÛCß`geēsįÖØąĪ755­_?_Ą°éÜš oīŪd‚7Ę.\H jeeĩ~ũ|ß}÷ÅoLÜĩ+€BĄ6`Āđž=ũČô䈠ų7ØĮįââ>`@įŋČÎN0gÎ*rXŪÔÔ´lŲ?fÍZJÂ-Ž[ÂH9 ųķaEE„…ÎĘ:Õ§Oˆũ_ū2ÉÚZQUU ēúæÂ…Ī3LƒRŲˇēúf‡;wvŦ­­éßptôĢd׎;÷ĢūbŌ`‡†ūųcÕ+W.„~H˜<ųī‚‚†ˇŽŲĶąŖ]DÄüÛ)Ãh-a%ĖȑOé_5晝žZĩmÛZ­öúʕ‰üüßyįķĪ?7#ãø… i/ŋŧ$#ãxJĘOšš§gÍZ āšįūY\üû×_¯vpčzíڕëׯ}úi˛¯o2Ãk׎xyøø<Ž_Dtôėß~;øæ›O?õÔ?ēwwÕŋ^_˙Įēu˙ZŋūGaūí”aôžgRƒ—^úŸ _ģVäéŠlūÅUmmFSäį×ĪÆæĪŊöŨģwĘ˯úøĄŅ`KŒBøxlŨúāŒ”Č%%!<œ>šĪč×]’”˜ˆ/ŋŨs6›Ķ?sķ‡hšļlŠ"OÆį7ÉŖ6éS˛-ˆ[ÂôČŽŽ†Ÿœ-œđšTV"3“>Ûōh°%/;"#j5ŧŧāfĄģĨ§@d$ Ā3ĪĐBma4ØōÁ0¨Ŧ4ũöŨ'Z%'›¸1¸ģÃۛūļLDh°ŠŦŦčÆ ôë.Š’!lŠ’!lŠ’!lŠ’!lŠ’!lŠ’!lŠ’!lŠ’!lŠ’!lŠ’!lŠ’!lŠ’!lŠ’!lŠ’!lŠ’!lŠ’!lŠ’!lŠ’!lŠ’!lŠ’!lŠ’!lŠ’!lŠ’!lŠ’!lŠ’!lŠ’!lŠ’!lŠ’!lŠ’!lŠ’!lŠ’!lŠ’!lŠ’!lŠ’!lŠ’!lŠ’!lŠ’!lŠ’!lŠ’!lŠ’!lŠ’!lŠ’!lŠ’!lŠ’!lŠ’!lŠ’!lŠ’!lŠ’!lŠ’!lŠ’!K€˛€˛2¨Õ¨Šyøõ_~yøxzÂŪ^˜qQœąjjj˛ô(!”•ᇐ•…Ŧ,x{ÃßJeŠ­…ZŗgáėŒĐP<ų$† ƒB!Čp)ķĐ`ËÃ`ĪŦ]‹ØXôî ØÚ7‡Ē*”—ãØ1čtˆ‡‡?Ĩ¸Cƒ-sZ-Ūy:fĖ€ŖŖšsKKÃÖ­Xą'r18Š74ØrĻÕbėXÄÆbôhÎæYSƒõë1l,ālžįh°e‹a‹1cÎũœ.IJeˆˆāxÎWč×]˛ĩgėíšO5…qqXļ :÷3§8Aƒ-OeeXģ3fđ5OO ˆåËųš?e&lyÚļ ąąœ-3 :‡CĢåq”Éh°å)#AAü.BĄĀāÁ()áw)”ih°eˆa VÃŲ™÷)•¸x‘÷ĨP& Á–ĄĘJx{ ņ1//dgķžĘ4Ø2”› !äęŠ#G„Xe,lyjķwāœāõäelŠ’!zŲf{w˙ūŊ_= ĶÕūD÷îŽú×oßžÕĩk7 Œ2 vģĻV_|ũõ %ētéöÍ7gŊŧü466Ο˙× ÚÚÚYzŒ”)čĄxûUUUūōË#UĒaīŧŗņ­ˇūãåå˙ūû/7668~|_hč(šjéĸģũŠ_đęĢËŖŖ_%2å•W_ŧt)ĢoßĐ;>^ģvŸe‡G™ƒģjjj˛ĩĩ{öŲYúWll:üã Īž=\[{' @Õ­Ûce&z(ŪNYYYŊ÷Ū—ÖÖ˙ŗ¨TCĶĶíŨģięÔ8K Œâ 6õ'GG§’’‚›7+}}ûXz,”Yh°Š˙ĄÕ^Ÿ8ņEK‚2 6õ§úú?Œ÷ŧĨB™‹[žjkMųTyyņãtpčĘrz†1e)”h°eČŨWޘōÁÜÜĶũûf?ŊV+ЏŌ)cŅ`ːˇ7ŌĶMų`fæIŖ‚]T„°0SDņ[†Č-}‚O›˛˛Œ ļZā`Ŗ—B €[žĸĸPYiÜGŽ_×tčĐŅŨŨ‡ũG ĄRˇJ4Øō4r$~úɸ8;÷Xš2ŅĘƊåô jjāęÚö””đh°åiÜ8čtHK3â#:tėŨ›í5à >K–Đgô‰ ļ<)ˆĮÖ­ĻtÚl$%!<œ> Dŧh°eËÃķį#!ûo› qî/æxļ‡h°å,&žžX¸ 73d|û-ļlÁGÁŪž›yR| å“ŋ”,[†mVKŦŅ >ááXŧ˜ĻZėh°Û˗ãđa „^ŊāåW×ļī1Ę0ĐjQTĩ¸{K–ĐžZh°åĻĄĄáâŋA-=āGĢEI ōķ‘ŖGYÍMŠDXT*ÃÕĩ…‚o`q”Ņ`ËJCCÃĉ>|X˜%æååŠTĒ”””áÇ ŗDŠ zōL>ôŠNNNlĄAAA)))ŠŠŠ‚-”j ļL4Oĩ ˇ˛>|8ÍļØĐ`ˁSMĐl‹ ļäY<ÕÍļ¨Đ`K›HRMĐl‹ *ÕÍļHĐ`K•SMĐl‹ ļ$‰6ÕÍļÅŅ`KČSMĐl[ ļÄH"ÕÍļŅ`K‰„RMĐl[ ļdH.ÕÍļEĐ`KƒDSMĐl [$j‚f[`4Øb'ƒT4ÛBĸÁ5Ų¤š Ų ļxÉ,ÕÍļ0h°EJ–Š&hļ@ƒ-F2N5AŗÍ7lŅ‘}Ē šm^Ņ`‹K;I5AŗÍliWŠ&hļyBƒ-í0ÕÍ6h°EĄŨϚ Ųæ ļåĩķT4ÛÜĸÁļ0šj=šmŅ`[MõChļšBƒm14Õ-ĸŲæ ļeĐT@ŗm>l  ŠnÍļ™h°…FSÍÍļ9h°ESmšm“Ņ` ‡ĻÚ4ÛĻĄÁMĩÉhļM@ƒ-šj3Ņl‹›w4՜ Ų6 6ŋhĒ9DŗÍ 6hĒ9GŗÍ 6_hĒyBŗÍ 6/hĒyEŗŨ&lîŅT €fÛ0lŽŅT †fÛl.ŅT Œfģ54؜ĄŠļšíŅ`sƒĻڂhļEƒÍšj‹ŖŲ~ ļšhĒE‚fģ9lŗĐT‹ Íļ ļéhĒEˆf› Á6MĩhŅlƒÛ44Õ"GŗmÕÔÔdé1đNĢEI ōķ‘ŖG۞>$!! ‚R –§ŲšsįsĪ=GS-fŠŠŠŊ{÷îŅŖĮŖoétĐhPP€“'‘™ ­ļY)• ƒJ…ā`¸ēBĄāeĀ’y° 1{6´Z „^Ŋāå''V,*Bn. P]ųķ#uIąQV†šsQT„Ā@ôę…>}ŒØ$Ôj ´Ī=‡yķDŊIČ6Ø ƒuëpčĻMCPéķŠŠABt:ÄĮˇZŊ)I`ė؃ĩk1s&ÂÃMŸO]ļmCa!6n„ŋ?wãã”<ƒM ĩŋ?ĻO‡­-3LKÃÖ­˜?ąąĖžV‹Yŗ`o3āčČÁ ķō°};ƍié–a°u:<õ^|ŅŦBũ¨šŦXeËÁål)0 bcŅŋ?&MârļuuØ´ Oy>>ŧ/ÅŲjĩčz*ĒE/BŠä})ļļpr×)4Y;3îîŧ/EĄ€ˇ7*+y_ežėlxy ą €¨ÕB,ˆ%Y[Ģ…‹‹ ruEnŽ ĸĖtäÛ_ŒšIĨ­[B,ˆ%Y›ĸ(‚›ĸd¨=^uXVvõæÍ //'§?ÜoßžURrš[7WOOūOļPâSQQrãF §ŋüũƒėí; ?3ĩĮ`_šrū_˙šüÔS3/ŪĒņ›oV˙÷ŋk>üp7 vû´gĪįß|ŗšüŲĘĘ@SSŖĩĩbßž)níņP|øđ'ŧŧ’“wÜēuŧŌĐĐđķĪÛēus5ęˎ˛”×^ûāĖ™ú3gęĪžmHKk˜0áoŧąFŠŠFû ļĩĩõÔŠqõõuß}ˇ™ŧröėáĒĒōgžyɯσeĮFYŠBĄ°ąąąąąQ({÷~‘œŧ}˘)/žø/KËDí1Ø&Mú›ƒC×īŋ˙’a?ūø5`õ×ŋžléqQ–wáBúÚĩq>>/^œ`eeeéᘨģS'‡gžyšĸĸäĖ™CwîhOœø>"âIwwūļF‰[uõÍˇßŽļąąYŗf¯ƒCKĮtí4؞~Ž••õņãûJlhø#:úUKˆ˛°ÆÆÆÅ‹§WT”ŧ÷Ū–^ŊúYz8figÅ wwŸČČ)))nÜиšų :ÁŌ#ĸ,lëÖ÷OŸNŽ}cÂ„Š–‹šÚo°L:÷ȑ=UUå¯ŊöB„÷­ĸtæĖá/žXÚŊģې!ãΜ9ÔØØH^ âč(ȝR9%ˇ`3ŒwYPІúũ÷sO?=ĶØņwá>Å!ggļSnذhēyŗbîÜ'›ŋūÕWŋ iķãĩĩ°S_+Ģ`+•F_RW§=úŲîŨ]ZĐåËB\ H™/, ååŦ6‰íÛÍēČ^­ÆØąæĖ€cbÚɘ-, EEFLŸ““zåĘycO›ÕÕáömzqi1ÅÅB,čėYx{ ą –dėā`V×ÄŪŋīōåœúú?6mZܯ_xhč(Ŗ–RU…ĐPĶH - }Ŋijjcû ĢCq• ›7ˇ=Y}ũ/ŧ0ĀÚZag×iķæÆūáōe„„˜8BJ`žž¸pÁ¸3/&¨ŦDTķ7Ŧ*6yZZZZ“98tŨŋŋhķæû÷_éĶĮ¸ŒÖÔ 1“'›>HJHöö;II<.‚a°y3÷7-7“ÜžRV†§žÂÚĩ|ĩŽé“¨Ö'Ãŧū:_ˇ¯ũö[(XĩŠ—™›LV€‡æĪGB/3OKƒN‡˜^fNņÄŪK– >ž—ûOj48w‹s?g3É-Øbb`ooŋåxE’įōÅĮ‹ņ l”a?6<8ËōŸ~Š>‚Ŋ=—ŗå„ÜÅ ˗#- qq€Ņ'éĘWĪĐÕĪ-) įÎaÉ‘>¤QžÁ&RR°lDd$œMŠ´55¸zÛˇãĨ—Ckĩ䕕!...xæĶ7‰ĘJlیđp,^,ÆZMČ9Øt:üį?HO‡Z //°úÅXm-Ôjœ= QQxé%ņ>ߜ2Ã`ëV> ĩŪŪđ÷gģI\š‚ôtˆŒÄäÉ"-Ôz26ņķĪ?{zzõ蔓ƒ††ļ§ˇą‡\\j×Ŧųw||<˙¤„–———ųBn.ęë۞ŪÚ={ÂÛK—Æ­[ˇÎÆFô?i’ģúúz777ĩZmÂgU*UJJ ×#ĸ,Īä5;uęÔ;vp>ÎÉđŦøCvīŪ=zôh___>ûųįŸŋöÚk\ˆ˛°ÔÔTÇ7áŗ+Wޜ7o^›?‹’y°æÍ›ˇråJĶ>NÖ=Ų(Ųxíĩ×>˙üsĶ>ëëë;zôčŨģws;$ÎÉ<Øæ”k‚m™1§\’(Úrļ™åš E[fĖ)ׄ$Šļœƒm~š&hŅ– ķË5!ūĸ-Û`sRŽ Z´eÃürMˆŋhË6Ø\•k‚māĒ\"/Úō 6‡åš E[¸*ׄȋļ<ƒÍmš&hŅ–4nË5!æĸ-Ã`s^Ž Z´%ÛrMˆšhË0Ø|”k‚m‰âŖ\ĸ-Úr 6O嚠E[ĸø(ׄh‹ļ܂Í_š&hŅ–ūĘ5!Îĸ-Ģ`ķZŽ Z´%‡ŋrMˆŗhË*Ø|—k‚m áģ\",Úō ļ嚠E[Bø.ׄ‹ļ|‚-Lš&hŅ–aĘ5!ļĸ-“` VŽ Z´%A˜rMˆ­hË$ØB–k‚m‘˛\ĸ*ÚrļĀ嚠E[ä„,ׄ¨Šļ‚-|š&hŅ-áË5!žĸ-ų`[¤\´h‹–đåšOŅ–|°-UŽ Z´EČRåšIŅ–v°-XŽ Z´EČRåšIŅ–v°-[Ž Z´EŞåšCŅ–p°-^Ž Z´EŞåšCŅ–p°ÅPŽ Z´EB 嚰xŅ–j°ERŽ Z´EB 嚰xŅ–j°ÅSŽ Z´-N<嚰lŅ–d°EUŽ Z´-N<嚰lŅ–d°ÅVŽ Z´-Hl嚰`Ņ–^°EXŽ Z´-Hl嚰`Ņ–^°ÅYŽ Z´-Bœåš°TŅ–X°E[Ž Z´-Bœåš°TŅ–X°Å\Ž Z´&ærMX¤hK)Ø"/×-Úsš&,R´Ĩlņ—k‚mÁˆŋ\ÂmÉ[嚠E[0â/ׄđE[2Á–Jš&hŅ€TĘ5!pŅ–F°%TŽ Z´ •rM\´Ĩli•k‚m^IĢ\Bm [r嚠E›WŌ*ׄE[Á–bš&hŅæ‰Ë5!XŅ{°%ZŽ Z´y"ÅrMV´Ålé–k‚mÎIˇ\ÂmQ[Ō嚠E›sŌ-ׄ0E[ÔÁ–zš&hŅæÔË5!@Ņo°ePŽ Z´9$õrMP´Åly”k‚mNČŖ\|m‘[6嚠E›ō(×ßEÛĒŠŠ‰§Y7Į0¨Ŧd;ą‡RSS?ûėŗ;wō9(AĨĻĻ.]ēôđáÃ:´ZVqv†Ŋ=ĪÃ˛¨˛2ļSzx //oÚ´i999|ŽHPW¯^:thiiŠ į3į1Ø ƒ3g’‚Œ ¨Õđö†Ģk۟ē|ˇo#4!!2ÁÁ<NPeeČÉÁɓČĖ„V‹Ā@tîÜÆGjk‘Ÿgg„…aÄ ggAÆĘŗÔTœ?Ŧ,dfÂÉ m¤ ÕÕ AH‚‚0d ū*ˆ^xaŌ¤I/ŧđįsæ+؅…˜=ūūPŠāįgg#VF]ĒĒ ŅāāA¸šaÕ* oĶ ƒ={°v-&O†ÜŨáâbÄĮĢĒP^Žâbüø#VŦĀĉŧ ”Z-.DEƏ‡§'\\`kËöŗÍ7‰>Į*ūŠ6÷ÁflŲ‚={0m‚‚ĖÕŠSHLÄúõˆˆāh|*+C\ėí1c͚UU6n”đn.9‹!6#F˜[oĶŌ°u+æĪGLŒJ7OE›ã`3 ĻN…^}Ոũąa âã1~<,āf†ÂČÎÆK/aæL„‡s3Cũn.1ūūÜĖSŗgŖŧŗgw´b@M `oĪ?—|ļy*ڊĨK—r8ģĩkQW‡×^‡ƒėŌcÆā‹/ĐĢŧŊ9›-¯t:˘ С/gķ´ļ†R __ŦZ…_„ĩHŋĐxXr2NœĀŋ˙ Îæik‹!C’‚úzôīĪŲl-ÂÉÉ)++Ģžž>ČĖãÛ˙ÅeÅ.,Ä̝bųrÎjus ūķüđƒ4N/\†ÁķĪķ2ķ-[ TJãøEĢÅØąøāÎjus55˜??ü ų~›ĸÍŲnŸa0{6ĻMã%Õ<=1p –/įeæÜJIAZĸŖųš˙ôé8t……|͟C "6–—TptÄĖ™ˆ‹Ãđ2Áđņ6gÁ>tūūæž-3,:ééF|ųi)˖aÖ,{?[[L›†5køš?W QQ#x\Dx8ęëqæ ‹į?Dã,اNađ`ŽfÖ2…!!ČÍåw)f"ŋ?áûäVīŪČĘâwæ+(ŧŸÜ?įĪķģp^´9 vFÜŨššYĢ”Jˆü§G úõã})ļļprûÁËɓđņá})žžØĮąÁmŅæ&ؤLņÔJ5įį‡Œ Ū—bŽ‚øų ą €¨ÕB,Čd™™Bėë]\™ÉûRĀmŅæ&ØÂ”)ÎÎPĢE}˛D˜2@ĨB^ž 2`ûzIŧ°ÄaŅæ&؅…čԉ“9ĩ4lė¯'ÃQĻxzĸ¸Xˆ™FĢå÷Ljsl~p. m‰üʁzO_+r¨KK@‚¸*ÚÜ_/ÆÆŨ쎺˛NŨēu=,lT§NŽ?ũô_?ŋ~ÇKųS;÷ëõ뚨¨˜ĻĻÆŒŒã† géAY@CCÃɓû/\Hkjjrw÷1ŧ,d| IDATb˛››D~fČ)}Ņ6ķ×ãvNÎé ĻÜŧYag׊ĄĄaĈÉĮŽí4éoí3ØÛˇ¯;yr׎Ũ–/ŸyũúĩČČčvėû÷ī͙3>/īLHČHkkëüj͚9II}}ûXzh°råĘĄC‡>÷ÜsæüMč`WWߜ;wŌũû÷–.ũf„îŪ­ž=;Jā1ˆĐÜš“ÃÃ#?ūxŋŋŋPŠ˜œ8ņ}vvĘŧyë§NPWw˙đáo==ųvA|8)ÚB÷ØÛˇ¯ĢŠŅΜųî¤I˙gccãääōÜs˙x "Ôŋ˙āõëôébcĶÁŌcą++kwīŪ&ĩĩĩ›4éo:t´č ,iåʕsįÎ5§Ķ:ØįΟ0a‹úWúö x "4mڛÖRš\‹ÆMpuõúâ‹%ŅŅ}×­ûWjj˛0wė-__ß7n\ģvÍä9Ŋ1UV–xė1Oũ+doŨÎųúrwy§9::mŨšō×ŋÎē˙ŪŽ]ëã➈‹{ō?ę,=. :TžĘĘŽ ŧ\‘ëØQô_^ņĖÍÍûŨwŋ8p 8)éԘ1SNŸNūņĮ¯-=( :Ø!!#|˙ũũ+åå"ū™%ˆÚÚũąˇ¯īã˙÷o¸víŠE%mBûųįį89šėØņņļmk¯_ל9sčũ÷_x ”ØėÛˇyæĖˆÃ‡w_ģvE­ž˜˜ø)€>/ø”;ĄƒíāĐõ‹/Nxz*ããßz≞~øÚ„ Üß{ĩ=¨}zįÛ)û÷ÜąŖŨĸEĶžyÆ?&&đäÉ^ziQDē|ŽNæ¸ų[ĨÂ_°¸W¯~ûö_îÜŲŅÅÅ=3ķdbb<ËĪ’­YˎÂqqAyš?Ŧ[÷Ŋi Ōh æĮ9;qĘ€›6mhhĐhŠjkīøø<ŪšŗwuŊ|Y7Ė7ÛÕĨĨF\teeeåëûxVVVF-¨Ē !!FOH*•@×f俊ú>~ööpvFU•ąąąņņéfTĒëępûļ$oÉĖ+n‚­P@Šdûäsh4bvp0˛ŗ…XĐåËP*…XÉÂÂP^ÎûRJKÉûR$‡ŗ;, EE\ÍŦUššÂ] hc^LCʔ˜[#FqđRZŠx_ŠäpėáÃņÛoĻ|0$dDjĒîŊ÷žlsĘē:\žŒĀ@S–"rđ’ŸĪīRrr*úėāÔ)~÷q ƒ”ąoÁY°‡…N‡´4ãG`mmkkĮæ7ŌÛļaüx tSĢVaûvĪZ×Ô`ëV,YÂ×üšâīđp$%ņ¸ˆS§āæ&“'7r‹ŗ`+ˆĮÖ­¨Šáj–˙#/……˜7—™sËßãÆaÛ6žæŸ€ųķÅ~N,^Œsį Ņđ2ķĒ*$&bÕ*^f.u\~íáų푐ĀũŅWM ļoĮƍ’yPĶŧy(,äåždiiĐéÃũœų`o%KĪũ&Á0ظ+VHāÎ"8ūJL :uÂʕÆ}ĪaX^Ū11RzBącžũ–ŗmšađíˇøî;ÄĮKf "ãĮcáB.ëļFƒ… (íį ķŠ—įcsõĖÔē:lۆÂBlÜ(ĨTëétXžiiX°ĀÜûu’Gކ‡cņbIū#%˖aā@DG›ĩI0 ’“‘’‚%K$ųdeöŦŦŦÔjĩ¯Š?BâëÁ÷ú§œhÜßZ-ŠŠ V#+ ãÆaŪ<)¨GĨ¤`î\ •ʸž“§Ŋ_žŒėlTVJ~SÖīæBChÄ& Ē åå(.Fv6\]ņÉ'’ÜģE¤Á& QP€“'‘™ ­MM ŦŦZū+ÃÔ*(• ƒJ…!CdŌAétČĘÂųķČĘzpwûĻφÖūšŋ‰ˆŪŊe˛)—•!'ššČČxđĀ˙úˇœ†# Éc7ˆ:ØÍét8t(õë¯7|öŲŽ'đô´*(¸ĢTv–t}fŖŦ ˙üįÔŋ˙}ΠAÃ}wßžYYgWŦˆ—Äyosčt¸råFPĐcMËaTTđW_íWŠ|åąS3Š™Áîf†ööČÉ99ŧĩí5**Ǟ2Ûßŋ…m]f<<đũ÷‰ÛˇoéÜš…wŸ|rØûīĪÛē•í…1Ōeo’’ôŠS§ļļIÄÆNšråôāÁž‚KŊlsīŪŊ#GŽlíŨ§Ÿ~účŅŖBŽĮRŽ^ŊĒRŠ:ˇkĀ×סĸĸâÆĘ"<8iŌ¤ÖŪŒŒ}zʔ)&˜:ujzzē`ãą Ũģw6Ŧĩw °kWˍe˜pÁÎÎΎŠŠ2pôž}ûæææÖÖÖ 6$K9pā@¤Á+’&MštđāAÁÆc)7nܨ¨¨0ĐFvîÜYĨR]ŊzU¸1ɅpÁ>zôčĶO?m`›¨¨¨la.z´¨]ģv 0xEŌ°aÃ8|ēhĨ§§O:Õđ4SĻL9}ú´0ã‘á‚m¸Á&ÚC›m¸Á&ÚI›m¸Á&h›m‚ŨfƒM´‡6ģÍ›hmļá› mļi v› 6ŅÚė6lBömv› 6AÛlĶė6lĸ=´Ųm6؄ėÛl6 6AÛll6 6!ī6›MƒMČžÍfĶ`´Í6ÁfŲ`ōnŗY6؄ŧÛl6 6AÛll– 6!ī6›eƒMȸÍfŲ`´Í6ÁfŲ`ōnŗY6؄ŒÛlö 6AÛlc lö 6!×6›}ƒMȸÍfß`´Í6īÁ6ĒÁ&äÚfÕ`rmŗŲ7ØmŗÅ{°j° šļŲF5؄,ÛlŖl‚ļŲÆâ=ØF5؄\ÛlŖlB–mļą 6AÛlŖđlclB~mļą 6!Ë6ÛØ› mļQø ļ 6!ŋ6ۄ›_›mlƒMĐ6Û(üۄ›_›mBƒMČŦÍ6ĄÁ&h›m~ƒmBƒMȝÍ6ĄÁ&dÖf›Ö`´Ífß`›Ö`rjŗMk° ™ĩŲĻ5ØmŗŲã1Ø&7؄œÚl“lBNmļi 6AÛlöx ļÉ 6!§6Û䛐M›mrƒMĐ6›=ƒmrƒMČŠÍ6šÁ&dĶf›Ķ`´Íf‰Į`›Ķ`ōhŗÍi° Ų´Ųæ4ØmŗYâ+Øf6؄<Úl3lBmļ9 6AÛl–ø ļ™ 6!6ÛĖ›A›mfƒMĐ6›%ž‚mfƒMČŖÍ6ŗÁ&dĐf›ß`´Ífƒ¯`›ß`RoŗÍo° ´Ųæ7ØmŗŲā%؜4؄ÔÛlNlBęmļų 6AÛl6x 6' 6!õ6›“›t›ÍIƒMĐ6› ^‚ÍIƒMHŊÍæ¤Á&$ŨfsÕ`´Ín/ÁæĒÁ&¤ÛfsÕ`’nŗšj° Úfˇ‰û`sØ`Ōmŗ9l° éļŲ\5ØmŗÛÄ}°9l° éļŲ6؄DÛll‚ļŲmâ>Ø6؄tÛllBĸm6ˇ 6AÛlø6ˇ 6!Å6›Û›h›ÍmƒMĐ6Û0ŽƒÍyƒMHąÍæŧÁ&¤ØfsÛ`´Í6Œã`sŪ`Rlŗ9o° Éĩ؜7Ømŗ ã8؜7؄ÛlÎlBrm6 6AÛl86 6!­6››\›ÍGƒMĐ6Û.ƒÍSƒMHĢÍæŠÁ&¤ÕfķŅ`´Í6€Ë`ķÔ`Ōjŗyj° ĩŲ<5Ømŗ ā2Ø<5؄´ÚlžlBBm6 6AÛėÖplūlB*m6 6!Ą6›ŋ› mvk8 6¯ 6!•6›×›J›Í_ƒMĐ6ģ5œ›×›J›ÍkƒMHĸÍæĩÁ&h›Ũ΂ÍkƒMHĨÍæĩÁ&$ŅfķŨ`´ÍngÁæģÁ&ÄßfķŨ`’hŗųn° Úfˇˆ›` Đ`âoŗh° ņˇŲ|7Ømŗ[dÕÔÔdāmL%*ss § LV_[_|ĸØ˙ITô†ˇ3œ9eĘÔPį!¯¨ĄčŌîKũ^čg`b'8E B Ĩ<8ƒÚ”ä#?š&+M)uėéčäë4#ā O{Øs5th Pp §Ē¯Vpōu20}0‚ûĸ/ˇĢƒlšČÍAN5Ē Lyaį…~/ôķO‚8_d“8ķWqõÂÎ ŊŸîŨĄs‡Ö&v‚“ Ē`ģÂU‡Ã0SķúĐ[-ūŖØ¯ĐVƒŒä$¨Ąî‡~~đķ;Ü ĪĢÕĨ(Ŋ‚+éHč8ęŗY—Ąl)–f!Ë NPAå O[ØūTŠÔPĸ°%!Y€ūđ7y :č>ŧ{°Ā ę…^^đr‚Ą8(Gy1Š‹Ptœáü&ۜˆ‰&ŗ{ļbĢÚ@öB/öĢC õYœƒ˜ų˜oÎfėđjoxûÃ_ Ĩü ¤uhr‘[€‚jT‡"t –˜“p-´ĢąúŽtE×ŪčmÔ&Q€‚R”*ĄŒC\"Lƒųš¯PcķÕ|…ž7ZËW ÁÖBģ +P13ÜāfÚĻPƒš“8™‚”%XbÂ"ų—¯ÅÚ9˜ĶŊÛ\s-ĒCŨe\ŪŽíã0næ™đIAĘ2,ˆOā G8š0hāˇUXeBå,CŲ?F#ÚžĻĄ5?ãįs8ˇMØÍ1`ÖaŨ!z/+Ą4m“¨C]rļbë|ĖAŒ 3IFō",ŠEė0 3m“`ĄŪŒÍá_ŒÅLą§…vfqĩB?ÂGĐ™ڇƒ­˙ŋæ´h ‰Gŧą˙‰úMyf˜'Ŋ:ÔmÃļBĩMë [ŽåiH‹CœÉ˙ûz ˜S8•ˆÄXaTéNDâZŦ‰™á7s ōgÂnŽ…ŗ1ÛūĶ1Ũ´85Wƒš$č ÛŒÍėwsúb3ŗ]ābæ0IH:‡sĻUs|qĩB5Đ|ŠO[\Ą˙lR``ų˙wzä?ņîlÂ&6Ķë { O=‹g9ų—ëå!o6Æa–ĶĢxĩ ēD#šÃ–Ŧ Uk°f–ąÜ˜’‘üž˜‹šæīŨôęP÷9>÷‚×*Ŧb3}ʞÂSs0'A\Āq?ˆƒŋā6˙Ŋ ˜XÄöG˙‰˜Čáę ŠØ„MætjFIFōGøčmŧÍažZ[Ąž×Aˇ ËâĮáR( ˆFt*’‘ĖfúåX>šM5€ Å"v!˛™8ɨā6Õ\⇏eXσŽÍ‰ĩĐ.ÂĸؘÍaĒØÂö ŧ‘†´¤´91&q31“ÛTŅūđ_‡ul&ۃ=ö°Ÿ„IÜŽOxNÃ´Ų˜Í€ápļ­!+”ÛTŖõúg°IĸĖ?ė|”ŠŲ˜Ŋ‹´Đž2)iH‹F4įc0#Øė_ô‰âãôŠ'<bār,osʅX‹Xn7BËũ Iį;Yb:ĻÂĄBžŦ ekąvfđ1† ąßŋ˜Iāú ØŲČæ/Q\⋨wđށitĐ-ÅŌ8Äņô…„~˙bxƒæo҈>‹ŗ†÷/‰Hŧ‡{#0‚§1°Ųŋđš(ļ°}/ĪÆl̃ķ7üm&fr{ØŌËũ‹™Č1 +ôA°‘8ĶyũŠo4Fg"Ķ@ŅÎBųâ—ŋ1¸Āe ÆœĀ‰Ö&ĐA— ūV¯ā•“8i`šŊØËy#đhDÆaGĄ§p*ąü% €?üŨá~—[› •Žpä鐁°…íL8‚#ü-Ā>뛁BŽĐÁÎDf›_Ŗ™o• ¤ĩw퐧‚Šī1(Ą4đ  4ƒ1˜īß0¸ÂÕĀ–Dž’qƒ¯cP@á īJTļ6A6˛ŊāÅë¨ ēˆ‹­Ŋ›ƒÎl!( Yŧ." Yü (ŧāĨ_ĄÖtĐiĄå{ÁzĄW>ō[{7 Y,Ëõū„ũÉ;XŠ{”ü2ŅÚģČPBiڜŲ#e°ĩƒ—JTzÃÛ´Ëé_N'mJĒ­auœ?ü ėãŽāˆ+\MƒQŧā•V¯ęÉEŽ Ģãîíģ;×īüā•ļŦØR^\ŪæôÎpÎB§ĐĘPæ'ķŋ)lSrCūl @M ų^*ÚZ‹ė÷jŸŧųÉgī~fÚœáŦ†ēĩĩ(L™‚Áƒ—\äš\ĻönÚûáėoßŧÍfb%”úíā!d§Ãëq8aøā%mū¸í!׊ŽEF˙÷Ŗ˙Ūžyû×ŋūžũ{›iķāÅLj¨ĀĶĖ›k~4j …ŊĐK€ģÂõ(Zž6Ģ e]ŅU€Ŋš øTĸ˛Å6Á‘ŅÍ÷ôB¯k¸ŒāGßĘAŽG üā×Z¨tĐ Æ`Æ ?xiņ÷j¨ũ­Ūš9k˜f_Á>‡. _ Ąį ×\ärûkv=azL~đۂ-äĪzėĮđ˜ 6\úŖŋcĐ=…YŅš-žeøĘ Á´6<>81nTKÂ0LÖÉŦUI5+++sgļb ļ,5Ôäŧ<›ĸ,ĸNWw˙Ū}kŨĒaôŒšššÎŸ=ūėųëšëŪŪcŸĢß; ¯0¯đŌšKļvļCÆ qtâŊ!|Ôũ{÷3OfæÉŗļļVöUŽ|zdGێB ąą1?#˙RÖ%7oˇđHŋjSņåâßūV]U­Ē:~¨đĨRŖÖ”ŠËÜ­ž›w&@ā @…BDi˛tíĘĩŦSYöí‡ŽǰQ\HģPQR6&ĖÍˈ/JŒöÂįŲsD¨ôđõØ÷åžÍË6o<˛Ņˇ¯ąķ1SíÚ7žxãbæE++Ģ[•ˇ\<\6Ų¨ė+Dkǧž¨^Ŋ@¯öíãÛØØxíĘ5įÎ{/íuč*ОîūŊûīM{īÄží::÷p`Šbĩįķ=ëæŽs÷uīîÚ}ˊ-QŅQėú@āPíüdįîĪv8öüˈ™NTŸčäĐIČ1˜oĮ';>]đŠ2PY]U}˙Ūũ;8š8) ‡ŽFÛč흟ß×gŋŪsaOüOņ[~ŨrŖėÆĘWW;ķÕhkÂŖÂ–÷ëšû÷î;39tu˜öæ4kkk++̧ūņԘ)c r 2ގú5įvŽßyũÚõ9Îņ |đ}Œ›ˇÛ[Ÿž%Ø!čí[ˇwļÛÍÛíŊ-īuvė `Āđ}Cyŋ;ÕŖūķΜz8Í];×ÚڐąC†Mvāz2Ŗúņš0>v<;{ģQOĒ(ЏrūŠ ŗ2ënÁõÔwrėÔÔØtëú-_^ž*`)ddČąŊĮ~?÷û 1ƒ„Ybæ‰LOXėFšõuõĄŖBíėí,5u÷ë.e]ęÚŊë;Īũy!@IA Ķ Ä%S2CöŒ ķāŋŽüĄCĮVoųd€ŅÁÖÕęönڛšœz)ëRļƄEōt˜ˇŽßl‰•Ĩ•k§UUV ‡gK €¸}ķv#ĶčŠôņį}<D č`kĘæØÎ}~ėÖļ&}ž´pĶÂ;ˇîJ<Ô{@oĶN`ė˛Ģeŗ#g×Ũ¯›öæ´×?|ŨÍÛí_“˙u!í‚ æVia)ŸĮ}[ĸ2PYü{qyqš—ŋŋT{”§Ÿ'ū˙ūłzxôppr°w°Ÿ>ēeG"ļöļ …â§m?ûVįķ¸Ī‡ģ?$eÜXÆ}&yG˛ĻHķö†ˇ§Í›ØíąnÔũaÂRÍ×ĐĐĐPßđāĪõ ‡ŋ=Ŧ°Q„Ž lcž`Âūæ/V•WéjÛž‰'zõīåčėøÛÁßîŪšK^aæfÅMa–Žgee9%2ķDfQ~QķׯkŽ <8”xČŠ‡S˛&y厕 Š Û3ļ{x›6+ã‚MJâoäT•W}šü˂œĶlĻûĩ÷—ūcéĩĸkEųEī<˙ΕķWžũųžŊ„ûIŲÄ'0ô›ÕßlyKQ~Qîoš[Ūßō×ŪÕi„@'‡N3ߝY}ŖzÁ”…į o”ŨXõęĒâ߅û‘“ŪëĢ_īæÚí'ŪHMN­ŽĒÎĪČ_ķúš¸'ㄉԚyģU•UmYąĨ(ŋčܯįö'ėĪNÉŽģ_gÂŦŠ_ĢũëčŋŽŽz.ęČî#'÷ŸāéįÉ~SރĄ!^Ã5–ķ`×ÉÎÆÆ&&0ĻžŽŪĻƒÍ´ųĶæŦšÃōŗF-¨5VVVī˙øŗw?ûzÕכmāčėøâ›/ēû˛ēúĩĩÖ­ėUÛŧˇąŪ‹ožXS]ķÍęobƒb­ÖS^Ō{@īËŲ­^Ū˧îNÛ3ˇŋ˙ōûs'Ímjlā¯ōyņËėį`āF— QŨ Ü>điĢ/b{xôŸØüEw_÷Ĩ_/ey4Ē˙iŊ øīÄN6S(~ûaųšōÛ7oģzš’SVėUĄ*-ĪŽÅ(fšOTŸ xwķģÅŋ÷ėÕĶŽÛ3à ˜bˇvAbĸĒQÍō"ŗ;ĖũhîœUsŽ^ŧjī`īæíÆū'Wpe,ÆļøV0‚á›ģ XYYÍ^1ûoo˙­¤ ¤§_OcĪ䥨ĩŠ=ėĶ‘ū^b9+7—õ?Žŋw÷^iaЧԓũHjPƒf›ãC”PuAq'‡NMĻ|åY‰JūŽĶBĐ~ėos…ŪŅŪ™÷Ė<Õ0Õĸ-‹\Ü]šššn]ŋ•{:÷Ŗ7>zëŲˇU˛éĐÆąr”G!ŠüŲ€'< \&ũ(w÷>!}ŒM5 4!iņ-d-5Ã;øųŗO5-´îŒ=JQjÔlllüƒü=•žFũĐ*éŪhš}RAeԝz:9tę3° įįÕPˇļ5“¤‘Ô5’Įæ' §10`âŋK Ŧąq;ļˇyk˛mØ6ãøže"YĄĮqœÍÄvėüûû‡ķíãËō*ā*T%"ąųÃ@ūüÆe1ŸÃ9 xų6 ķ1ŋÍ[ĪLÄD7¸ņT0Ų$ €3œųÛŋ<ēZ¤€"ņ[ą•‚É&Q¯û—$$…#ŧͧųÃÆmÃ6>Ɛ‡ŧBÎÃ<>fۜŠÍØĖSÁ`ĀŦÁšõXßüpøĪ`ÛÃ~ –Ä#žÛe3`ā€ēİ™žLΏžōĮ&QŲŋ$!‰Ûl× f5V¯Ā 6÷ņŌLnŗÍ€ų _ąIší_8ßŨ§!­ÍCŊy˜WˆÂāør1 4Ûą}#6 ķ=šž`ŗBK—.Õ˙ÅŪáą÷đžŧáŨÚ/(ØĢBÕ'ø@<â;ÕÅąö°ƒ1̰ę*ŽöC?ķŽ?P‡ē¯ņõIœü_ŗA$"ůû°¯úąšaiH[Õīá=öOÛė‹žõ¨_ŠĨŽpåä9 h>ĀŊĐë}ŧĪrå’ûõŋ÷oâf_ô5“¨AÍ&l*@Áøĸēąųˆ5Ŧ'cō>ėKAJ?ô3˙Ž— ˜“8š;–bЁsōœ#÷*f…ļņ|l“_‡ēĶ8mƒc ũ™§aZ MÛ§2`ō‘oÎķąÍ 3€*TíĀtņˆ7á>˜ú‡ ŋˆMž÷{ęžĮ÷&?8V˙PáY˜er;ʀÉDĻÉĪĮÖ?2}&f†"Ôä2kæãĘÍ'Ė m!ØD2’?ÆĮZhØ Ŋú O›?uŦCš\ä  ÕQˆzo›ķWˆÂ…X¨†Ú ^PBÉæ.Ķå(ĪG~ JQĒ„rV™sjD íjŦ>‚#Np @€ *OxļōjT—ĸT õYœqĀũ‰íIDATu†ķLĖ4íQī„~›0ƒ•PzÁ‹ũ긌˷q{,ƚų¨÷¤,Ã2-´ũĐĪ~>đaķô˜"ŠĄ.Da JüāˇëÍšËoʖaY&2Ŋáíö›D1НāJ>ōáü&Ū4ĄØpH€Új° th PĀō„–|‚¤„’Ã[43`*Q™ƒœ\䲚5¯œTP#ØŽļOe(SC‡<–w“ Fp_ôõ†7‡eA m J.âbk7úˆ/|ûŖ?ˇĢÃØM NÁVAÅáę ›D.rsÃōnÍ#0‚<Μ]įø[Ą˙|¯ŪS_PāˇIENDŽB`‚trillian-1.6.1/docs/storage/tree_8.png000066400000000000000000000450001466362047600176420ustar00rootroot00000000000000‰PNG  IHDR\ŠĪnI IDATxœíy`LW˙ÆŸÉ k!›l2ą„ "‚RK-AUQoߡhKûŠ mŊ%vZKÛtAՖ ĸę}ĩ!A…ĐŠ$dB,‰„™‘IDL"3™ß§ŋi‚Lfš÷Îܛķų‹šgÎųŪĖ=ĪųžįŪ{ŽH¯×ƒBĄPū‰­ X‹FĨ …ūÔŠšsįD%%6 #<\ÛĢ—(8X//›„@aÍxÍéĶúåËk;VöęYÖĩkEĮ€J/¯G܇ĄŅ8(•ŽŠëN§RÚ¤Ĩˇ -Z$vrâ> PQā+ VŽÔĨĨÕ|ũEŽTĒąu8ŖÕâķXŸ¤cmcb Ų:ŠŲPQā% ŪyG7bØŊšQ7%v9T(œŪ™&^ĩĘ6ĶŠÅPQā:FŽ‰Yt}ā€ûļŽÅZ-ĻŊüú?›Eķ>AEŦ[WSĒ.]ĩ"Īց4ŒJÕtÜË!IIpqąu(“ĄŠĪP(”Tŗč?ųļÄ$ŧŧ-ˆ.øč#­Ą˜ž1{vMĖĸ<'§[b*ã_šĢ.ytú4MHy>ĄVŖ¤ÄÁέ„ĮHđú?nŸ9ÃŖØĨsMЇ‚DŒ,yėô´ė_ž¸{WŨĄƒûâE3oT§Ķ}ņeÜ͛EwīĒ˙ŗđŽ]Ė­Aō`Į4Sā 4SāWŽčd!û°Ēę‘FSĩe끜œ6ÕjuzŊūøīįvū”āčØÄ‚Úˇ¤PHtÔXā Tø„\ŽīÜųácĐķų1 ĘFŖŽŽMįŊ7ĩuëŪ^íŧ-¨A"4°˛¨ˆņĐ(Ŧ@EO9"iߎúÉĪO$§ô,+ĸāáÃĘŗg/Ô[$˛đ‰ƒĀ‡r9ŗAQ؂Š‚øũDš‡{[ŠÔ‡Ĩú˙øCū¨Z;hP/–ę§ØÔhä=dååĄjõũßNWV>1ŧ¯ŸŸįcÅôzũŲŗŗ.*FŽčįįįŠ×ëöõz}fæĩôŒËmZˇ;öŲfÍ ‡ ™ČéĶ2å9ū^ũčˆ"LčīĘ{ΜÉŦÖjĢĩډ“–•=(/¯čÚ-2=ũrí2šņĖ™ģŽĻĻæŊč sß[īã7Ϟ˛ĘPāáÃĘ ?øęëŊNNÍÔĨå/Ŋ}˙ūߎæī'ŌZĩlžæãīöÅuvnĩ|厝Mųˆģ3¤p ÍxÆīŪUKÚܤ‰D¯×/üĪWßn=ĐģwWR@ŖŠ:ė­ęj화íNNÍ&MŒpi;¤}{WGĮφJV¯ŲŽĶÕlũv1€3g2?øđ‹sជ PQĄIMŊT­Õūķõž{Ž€‚‚Û ?úęķĪĸ=<Ülp–ĄĸĀ{~?‘æâÜęāO›4‘(,,ŽzTŨ˛esCOÖ~.-;íėNNÍ8:6iŌD2dp×0~˙1??ĪĘĘĒfÍĩZŨķcؓ"™ČԘ7‰"ČÍŊ%HmáAEßa|Bäp—Öä“sį.īFū[TtīãOvH;„†v!ŸüņGÖŖjíĐĄaĩë 8đË g×!ũûËūųú ?|ŋÜ $™<)ÂPø\Zv—.ūmÚ´4|’”ôįכö‰ÅM›4™=͐¤Pø~sútĻV§ĢŨÃK8-či(Põ¨ú…ąƒ üøīį Üģv=[ˇ,îāíūĶîÃ'’ĶO$§ßŊĢž=úũDš—§[P/ų¯Ry'3ķÚ[oŽ7|÷ĉ´~ü5~ī'‰äæÍÛCž{ëøŅÍO:ž@F~s"9 ĩzxuĩ6~˙ą!CÂŧŧڝ?Ĩŧŧ"ãü=zt2|åøīį<=Ü:uōģzõFyyEMMÍáÃgĒĢĩ_Ä.(*L<}r›—§ÛūŸ‘Â<đöŽrrĒ1Ģ•Ēin^ŗŸââē(&`åJũ¨Q"FÂŖØ!T„ŒZ#° ē`âĢw™ĢS2ûŨNĄŊ, .ĩ0Ąĸ Xt:L™ĸûį? FE”0[ŗV‹įĮÉbb$Đ|A€Pą,ņņzWׇŒ+‰_‘ŗ|yFÃxŨÛC3aĸRaÜ8$δŪG¨õ|ÔemW­ĸw#„a˛nÎˇƒ’A+áI´ZôĐ;) ..ė5Bątú LŌĶņėĀ2V›H0rDiAĢPlĸĶAĄˇo˙ˆí†zČî_šB3MĄAEA€A*­”°˙{įÎårëÍP¸…Š‚‘ËzŸƒ†ü|ĢŽĄ¯Ī * ¤Á÷Ŋ[BEBĄÔæ~‡+ũzĒĸB3fôw÷ļ†ĪKJĘ\]ÛØ00Š­ ĸШš|9oԘÜŧ ĀÕĨõŲ?ž—J}ÔÔÔŧ<~ū‘„¯š5s´uŒŽĄĶ‡ÆKaaņāĄ3Ÿé/Ûôõ‡_Æ.J}fžĩ˛Ļρŋܛ*Bã„f —÷?ˆ]žėíˇßŠ$˙}ëÍņÃFŧ‘qĨwīŽŸ~žëĀūõļ b+¨(4Rôz}ŗfŽoÎ|ÅđI“&’…ü+éčŲû÷+d!AíÛģÚ0<Š ĄĶ‡FŠH$úvË"‡:@˙ū˛ãŋŸÛŧeԜÉļ Œbs¨(PūÆŲšUNNAQŅŊ.]üm ÅfPQ ÔáÎõ”×FÛ: Š-Ąĸ@ų›GĒL|u¤­ĄØ* ¤ŦĖ’ĩOōķ {õęÜĻMKËkéSÎB„Š‚ņô„<ËԎ]›3Čû†w7Ŋü;MĨR* BƒŠ‚ņõőDKn(&'§÷ík†(ČŗZöîM×nTYM­6û!”ä“fe /ļ Ąk4 * Â$"B›_`ŪCĘJåĻM›øųyšū•´ŒÖ2™™‘Qė* ÂäŲgļlõ6ë+íÚšÄũ´Z$2u: P8ŠÕMÜŨÍŽbßPQ&#F8””4?|Ä gĄiĶ&=zt2ą°V‹wæÅÄĐũf]â]°°ēõŨôAĀĐLA°xyaÁũG1šāBf‹¤cm-ĸŠ L¨(™ČH‘‹KķįĮÉ 'F*Ôją~ƒĪ ƒÖŽupbĻJŠŨA§Âįôiũōå5#†Ũ›u͚uß §wæ……9,Z$ϊ `¨(4 4Ŧ\Š;rD1˛Dō sį‡~žU z Z-îÜi*Ījyņb‹´ôÖęŌ&11t§iÁCEAhhĩÚ˗/‡„„}ēS§NíÚĩ{ōFĨ EÍŠS5įÎ9””40:JĨÚŪŊE!!2™ČŨb1;Û…ŗfA­FŸ> „œMúbn.ärää ´ķįc_• ŅŅēœqxxy¯å}úÜo߮ڔ/ĘŗZ^ŧØ"-Ŋĩâzŗ‰kæÍsę%!XQĐé°a1u*BB,¯§ŧÛˇCŖAllŊY…čtˆ×¯['ZšÅl%Ī,+/gËËäA‰…Bą*  ĨÂąLMGĨēqīŪmŠŗķߓ˛˛’‚‚kŽŽîŪŪ6Œb+ nĢTwŸü<$DÚĸEãz‚ĩ1ŠÂõëß{ī…qãۈ‰Ųføđûī?ųá‡ĩŧ—ŠBãdãĻ}ŸŦûžüÛA$PŖ×‹rŽ`ķŅûŖ1N ã㔐°Ģ¤äųDĢÕūöÛŽŽîC†ŧdÛØ(ļbÕĘŲՕVWūŠ­:Ģ}”úÚäQÖ~2§ą)§(888LžU]]õķĪ[Č'gĪ&žôŌ ‰¤‰mcŖØ ąX,‘H$‰X,ūfËū?%Œåš÷æNąu\6 1Š€ąc_oŲ˛Í/ŋ|K^dųß˙vĸ—_žië¸(ļįÜšKQs×wîäˇ}kŒH$¨wĸM¤‘ŠBķæ-_ziæíÛū™x˙žúĉ_|ŪĶ“ũĮ!)öÍŊ{Ĩ‘¯~ ‘Höī[ÛēuK[‡cŠ(˜8ņ]‘Čá÷ß$&Æiĩ"#ßļuDSSS3íõ˜‚›ˇˇ~ģ¨[7ößĸąWãŨ‚§§ß°aãSRŨŊĢôđđëß”­#ĸؘ•Ģļ%>3įŨI“'5ꋡņЀɓį=睏¸pöėUb­ŗG1“¤¤?—.ûÆÃŊmÄČ~‰‰ÖÔԐĪûõ qvneÛØ8Fhĸ Ķ™ąÂŠLÖ?8¸ĪÕĢį_|qēš ĩj\× _qu­1ąäžŌˇ‹î=?nníĪ˙HųŽ_ŋ†Wû,+ˇqˆ+)(Q0û¨Ē*ÍĐĄ¯´mënVC׎!€>âÄúôŅįæ5ķōzÔ`ɴԝÖ4”)o=q’@’MAaaČ5g%žĖĖĶׯ_4×bŦĒBY™ ę0Ī>ëpųr JLröõå .”(ôčSŪi¯Ŧ|xíZfuõŖÍ›cēu īŨ{ˆY­ŖwoˤpT*:Ą Û­¨Õ..lˇÃ‚š>ČdØ˛ĨábÕՏ^{­§ƒƒ¸Yŗæ[ļœ0÷•k×ja„ŽņöFęšZ-XŨô3ŋĀ1"B+˜Ū$¨Lėü™šÚ@ą–-Û<˜ģeˉƒ¯wéb^˙./G\^xÁō )\â䄈Ũįą>ė5ĄÕ⃅cÆÄP€đ6ƒQŠ0nÖ¯gëî@l,ƌĄ›Įņ /Ŋ¤ûú‹ĢRІú×ođQ—ĩ]ĩJ8ĸ ¨L€—æĪĮöíŦTžš &°R9…%œœãđΜ - ĢĨ)NIĮÚ.Z$E€đDĀ„ prž= /¸Lö˜Ĩ{ŌķDNjįŧ׉8‚LĄP8Íūw§ĩkļ EA,ÆįŸC,ÆÂ…P*¨°ŧąą8~Û2°Q1ožÃāÁ-GŒęqøˆĢõĩiĩXŋÁį9—,÷č!g– ÍS¨MJ –-C¯^6 ..–Œđåå¸q;wbÆ L˜@sŪŖRaŪ<]`Į˛wf)ÛˇdÁ- ĩZ’_āøÁ°0‡E‹ÄËB žüįÎ!/>> 2éIĊ äåáėY>3f@*e;R GčtØžŊæØ1ŊB!–J+ÃBīwī^ŅāˇĘĘÄōŦ–G]DDhĮŒ ´Á€ĀEđÛoŋy{û´k’™ SÜ&‰^^psĢXģö?ąąąėHášŦŦŦ ˛† {M.`Š)ōôûúbéŌ¨ 6HX}ėÁæč…Nuuĩ‡‡G^^žߕÉd)))LGDą=˙˛“'OŪĩkãņØ4cīŪŊC‡õ÷÷ˇāģ7nœ={6ĶQlĖéͧ 0Ā‚īŽ^Ŋ:::ZËÆíMģAāĸ ÕjŖŖŖW¯^mŲ×ÉuCŽ!Š`˜={öƍ-ûŽŋŋ˙ĐĄC÷îŨËlHv…ĀEÁš4@“aMš@|˛ dQ°2M ĐdA`X“&Ÿ,YŦO4Y Ö§ a' ‚FŌMƒõiAØÉ‚`EŠ4@“ĀTš@p˛ LQ`0M ĐdA0•&œ,S˜M4Yā5ĖĻ Ą& ÆĶMx ŗiA¨É‚E4@“žÂFš@d˛ 4Q`)M Đd§°‘&™,MØK4YāėĨ á% ‚VĶMx{iAxɂ Dí4@“Ávš@X˛ Qā M ĐdG°&–,G¸I4Yāܤ !% ÎŌMx7iAHɂ@DË4@“;‡Ë4 ˜dAĸĀqš@ É‚Ãeš@L˛ Qā>M ĐdÁná>M #YāŊ(Ø$M ĐdÁná>M #YāŊ(Ø*M ĐdÁąUš@@˛ĀoQ°aš@ É‚bĢ4 €dßĸ`Û4@“ģÂļiīɏEÁæi& v…mĶߓ‹‚=¤ š,Ø ö&x,đUė$M ĐdÁN°‡4Āëd¯ĸ`?i& 6Į~Ō“^Š‚]Ĩ š,ØûIüMx) ö–&h˛`Cė-M đ4YāŸ(Øaš@ É‚ ąˇ4ĀĶdĸ`Ÿi& 6Á>Ķ“ž‰‚ŨĻ š,ØûL|Lx& öœ&h˛Ā1öœ&x—,đIėĻ š,°Ķ/’{žĻ š,°Ķ/’{ūĻ š,0Ķ‚ũ' v- ŧN4Y`ūĻ ûOėZøž&h˛Ā |Ovž,د( M ĐdAøž&ė&Lā_Ę Ņ :ēF]RĩaÂ,-xZU+WûKsŪąÃY‰dXt:LžŒfÍđöÛfŒÆQ*‹ˆŧ˙>3rŌ{dN IDATÅ ˜1ͧ#<œ™ Š”™:šaÖ,bÖ,ķ˛$#”—cûv89aãF>é‚J…ũK7bØŊšQ7% šy)§[ĪĢ0@ÄLŒ‹ÂÚĩČËÌ V :.IJeŧÉ4Œ‡˙ŪŪ ×œ•…]ģpø0o:CBžų˙ųķĮÆbĖLšÄpĩ,ĄĶaĘŨ?˙Q0*ĸ„ŲšUĒĻã^IJb,…dō–¤BÄDL›Æ`•!#* ˖AŖažr6XžŊz1¯BB •bÃækfĩ‹cÖ,V$ė7°~Ŋˇ0lK|ŧŪÕõ!ãŠĀËëŅ‚č‚>Ō1U!cĸ ĶaÖ,LĘØŦá1ŧŊŅĢ–/gĨrfIIAj*"#ŲĒÚ4$&BĄ`Ģ~Y¸“&16kxŒV­0}:ĸĸ cŦ;°…J…uëDĢ–į˛T˙øWîĒK>ĖLÖΘ($&B*ĩÖY4Nd$ΝãÁȰlŪ|“ÅôŪŅS§bíZļęg …ˇocĐ ›Gu5ūü“Å&áë¯ĩ+—įš¸°õüĸD‚ ë‹1c+0& 'Oĸo_Ļ*{:b1BC!—ŗÛŠ•QØ6;uBFģMXONzõbŨûˆˆĀŋė6a=GŽHú†ßgĩ /¯GmÛj2…´4xz2UYŊ 3“õVŦAŠDˇnŦˇâčgg{Oš’“áįĮz+ŪŪöŽäiEöŌaŊīįå1P3ĸ@†G–ĻŽĩéØiiŦˇb 99訑‹†‚‚ĀČĀéé\ŒnnHOgŊk((ĀČĨ4ôėžėlüfD›á€‹ ōōėÚXâfx “!+‹‹†,ƒŗqÂū“Ļ+Wô=dėÎŌ@Íųķ xĖˆ‚BæÍŠŠČÕôw̏G§ãbxāíü|.˛ ĩš]ך6Ļŧ@aCäōš6mĖĮ.^Tt ˙Õ×{Ėm¨ys]j*EŲéĢĶ”aéÖ/ƒ´nmëøIeåŖĢ×ō‹‹š˜q<Ž^~ŒĘ22N–”Ü ŌŧyĢ_ũĄcĮnđųuK9ūԝ;ĘáÃ'čõ5iiŋč×o¤­ƒ˛Z­69ųāĨKŠzŊŪĶĶoĐ <<|m” øķĪŦËWōÜēuįĪ?ŗøųyzz˛? Ģ… D!3ķĖûīŋwīvŗfÍĩZí A/?žėØ×§(ėÜš!9ų`›6ŽË—OŋsįÖ°a‘P*+žûnDV֟ĄĄƒū÷ŋīÖŽ}7>ū˛ŋ[‡Æ5M'ol˙îāŽī˙`Ãú÷ĸæLæ2ŽEĄ´ôŪÜšc++.]úũ¨Q¯=xP:kÖpŽc°CæÎ}!<|اŸ”JššˆÛ'NüráBJtôį“'G¨ĒĒLJÚãíÍÉ];Cû(õĉ´ĄÃߎYíĒ*cĶ&ėڅ={ë:öėÁĪ?#6–7â`ā@DD`áB&ķĨ "8Ŗyĩ.ĪĒUâ}û=>Z Ņ0Ķī´ZėŲÛnƛ]ÖŽu`đI æˇĸsûŽWUáĮĄP`Ķ&>)‚˗#5īŋoíēÆdëíđpÄÄØõƒ:õ‘’‚eËĐĢ"#­ē$t:$$ %K–đfˇáÚčtøô͚¤¤š˜EyXĩÄŗJÕ4zÔÅĩéĒUbfĶ%VD€Z… qû6zõ‚Ÿ<=Mí:Ôjäæ"/9ŅŅ|Ÿ$%sįĸOČdđö†››ŠkŽVUĄ¸׎áÂņĩ0HdīŪ6ã’P\ŒÂBäįã¸ģãŗĪxЌ Ėž]# |0xPiįÎũ|ĢLÜ*FŖqP*םNĨ´9’čērĨ~Ô(Æv 7Ā–( ää 9ééPĢĄ×kˆDO S§Ģ‹[@Xd2ôëĮ›Ŗq4ddāâEddüĩs‰^¯­īīPûč°ačŲÁÁčԉßŨĀ€J…ĖLČåHKûk3# Ã!„…aĐ ņ2g|/âʝ\Ž?rD‚Z×=å˙:ŽíÕK, eë’`WjŖŅ 1ņôŽ_}ũõî§đöåä<hÁëŧĀT*ŧķÎäūķŨ>}|øn^Ēár=ã§ `đāÁû÷īį,ræĖ™ņãĮ)0yōäsįÎq Ųģwī3ĪÚ Œ ŪŲ Œ >Ú ŒĪŦ Œ™ ‹K§ÍG[qCĀ;[ CĀ;[™5X2–N|ŗØ0ŧŗØ0ü˛XšYƒ…!“IQ`ī´Á7[%CĀ/[ CĀ/[Ĩ™5Ų!“IQ`õ´ųe+°d(xd+°d(øe+°4ŗ&0;d2) Ŧž6ŋl– lö löfÖ`zČdRX=mđĮV`ĪP đČV`ĪP đÅV`uf χLÆDíĶlV _lö _lVgÖ‡LÆDƒĶ拭ĀĒĄ@ā…­ĀĒĄ@ā‹­Āę˚ĀāÉ˜(ppÚ|ąX5ŧ°Ø6ŧ°؞YƒŅ!“1Qāā´Á[mCĀ [mC`˙ļ3k0:d2# ܜ6ø`+p`(ėßV`ÛP Øŋ­ĀÁ˚ĀԐ)ŌëõFë +BQ&2O⤑bÕÕų'ōĨĪKe#Øž.pą>8*¨ō—…Ŧ\mî•ŊWēŊÖÍHag8ÄĀxÁ‹ÁÔP  Ųrȍģ™rŗU‡VÎū΃0(AŪđv‚S1h QB™ƒœ“8YzŖ€ŗŋŗ‘ō=ĐŖ+ē2ûsKBy&2KQj¤äĨŸ.u{­›üBÂøĪA.‰‹¸x7.ũtŠĶ‹š´hR_ag8Ë ëîpCĖT äįHCZ&2+ŊQúčūŖö˛öl\†*‡\uGuīÚ=Ÿ>FĘûÃŋúŋ$ę…$lĮö<äuCˇŽčč?Ox¯Ĩ7qķ:ޟÃ9‘ˆŒB”5ᝂj)–f ÃÎA’Aæ oG8˙V.r퐧€ĸĄ}īK!ĩ8 4_ā‹}Ø ú"Đ>Î0Öĸ0ųšČŊ„K.p™‡yŖ1ÚâtĐíÞmØĻ†:Á4ũįČCŪYœ0æcž5]â.ŦÂĒ<äųÂW i:ĸŖņ¯TĄJ Ĩōä”ĸ´7z/ÁkÔA õ'øä(ŽļA›NčdÖ%‘ƒœ›¸€€(D Ä@‹cĐAˇ[㯆ē/ú ĀôKâ:Žg#Û.oāWņĒ5?G Rb›‡<ø!ČŦŸãŽ•Ą,‘oá­'Õá)ĸ †z!ŪÆí7đ†<,‹ģåÉHNAĘ,ąā Ũ`=Öŋ‹w;ĄSƒŋúSŠBÕ5\ۉ#12ŅœH R–aY/ôƒ1­Đʂ(ĄÜŽíđXƒ5ŒØ*¨ˆļF"ŌŪ–ÅPŽōßđÛyœß„MH¤ē ؐˆÄ™˜€Ë.‰*Te"sļÍĮü ˜`A% HXŒÅ“0éčS€‚úŽf!KÛ1 ĀČĶGJ(ûĸ/ĢŊ€;܍\…äļ™ŋ9žĸܤˇb¤ŅĮŖ8ęw b0 ø\@ŊëË!ˇāįxPöā§ĪZõÖĒ­+ļæ6XŪ.Ȩ/i*@Aô17 F°ʧ"ã„)7ÎîŨ‹ßŸvĸŪ+Ü82Ȳå@ e0‚-ĢÅ,Œ_Č0Q˜>›÷Ų×}mY .pÉC^}W7Ã#Œ&MrČ-÷oŪ˙ņŦËR8õ=ŸËÍđˆ†’& †Į[šˇ"ƒ#X÷CŲŊ˛S‡N]ŊpĩÁ¯Oš˛‘ˆ@ŗb°Œ@æ įŠ‡ŠPäSÆ UžęãY'ė´pČô†w2$PpsÚîp?†§ŋ°Ą‚Ē Ú°:e"ˆ!öƒ_Šžú$ŲQŠĄlĮ ˇpĢzŦtĶĢÚ*Ė~Į[¯×_<{ņâŲ‹w”w|ƒ|GLaPeîQd)ޜŋâØĖąßČ~­œYŸ?IåÃĘôäôŦ?ŗē ~qpSĮĻ\PSS“–}%㊇¯Gø0oŨ5Hūĩü?ŽüQZ\*ë/ëҟ‘!Ú,”yJUž ƒŌYfî,ŗ{#éIn]ŋ•q2ʅS˙ˆūb‰øRęĨÛˇÃž ķđa÷vŌchĢĩŲi؊,EĮneũefĨŲĸ°pâÂŖûŽxų{øöĀ–e[6ŨäßÅßÜzŦ¤â~Ŝ1s.§_‰D%E%n^n›Žn čĘEŠe īrŪû‘īįeįųwņ¯ŠŠšuũ–K;—ũWöˇlÑJV>Ŧ\4u҉'š6kęŌ΀­É}÷m˜ģÁĶßŗ­{Û­+ļžj÷*Ž;äOŸũ´÷ëŊ.žŊ8}āt'JO4o؜Ëv}ļë‹÷ŋ(-.­|XŲ¤ig7gąXܲMK.EA™§|­×kåĨåę;jmĩvĖ´1Ëhø!Zf_CģuÜqvĮžKûbŨzjë]ÕŨÕo¯6ˇë)W—‡?RxäHᑘí1ÅĒâ˙Lū‰Ž#h*4īŒ|§\]ž÷ŌŪøËņ?_ũųŋš˙>ax‹Öė.ÄV›M‹78pbÔkŖ’î$ũZđëĒŨĢJ‹māGœ;~î“w>˙öøũWöo=ĩõĢ#_?z&ëu|ą š,@÷žŨSĩŠŠÚTŽáâŲ‹ŸE6mÁ´¸Ė¸_ŋxøz86sܝš;N7äĨ!\F’v<í ūņÛÍߎŨ;Ø=đˇ3å.ŒŗEáÍ%ov˙Ë”v—úųž?užōaĨšõXIË6-§Î›ęāā ‰ÆũkÜsãŸËÉĖIûŨÂÛŗđĶį?ŨšuįŨßíü×=3__,ā,m.+)Ûûõ^_E[ĩhÕ@Ī=ģöf}Eŧ'ųōÃ/Û9Ī]?—ä¨ũFô{fÔ3‡žˇëuŲāä˙NBˆIš95ōâÛˇ¯_ŧÎ}$^^Ī˙ãy‘HÔĸU‹ž#ú¸vĄŪĮFŸÄĒuãĒU7oÕ\_Ŗ/šSâåĪäb[æ:8ôøūãWĪ_íķĪ™H?‘`āËđą’yNuUuī!Ŋ›95ŗU Ē*ĢŽd\iĶļ͇¯ūũbKANNÛđ̇ƒhĸN÷׉“4iZī"qėĐ5Ā08ĩvm  Ē˛Ęô¯›- š ÍūÍûO'œž’qĨ\]nî×Y‚ˍKî”pÖbŅÍ"œŲORŦ*ĐÎģ­ ”Ũ+ĢŅÕxx÷ø÷Íüž{6q´Ag°-#&ŽØļj[üÆø…›Ū/šŸ—ØŠg'îí6ŽNV=īcž(¨n¨f ›UUY5uŪÔüo_÷^xīRę%k"`„›Š›ü:ûqÖb@p@ūÕüÂüB)O@>‰wGoüŋ6ؐv^íZ:ˇtjé4m>[/đņG'GąXüëŋ˙ų¸ĻBã×ŲīãŊ›eûÛ æEœ°+A™ĢüāĢĻFO  vmīú¨ęK‘GĢÕjĢĩũģZ›´'I,÷ܛŗž{å9ˇŦũaqaąĻĸáT!°{`+—VųãÁũäNwīö=nZ7 ‰†–~"=7;ˇöįw”w8ŽÄæ$Æ%:ˇsNPĸ ¯š ¯IDAT&ŦŪŊzûéí;ĶvúųÚ:(K0OČP|čûC9ōœâÂâo—›“ųô§ĩŲϞĸréŋ–ŪĘŊ•›ûáį_ŧ>ņß;r÷¨âč)Ŗûę˙ũ'ßo]š57;Wū‡|ëĘ­/wzY™ûôwZ§yËæĶ?š^zˇôũņī+.*îĒîŽy{MūÕ|nZ¯Íŋ?ųˇĢģëœ1sN'œ.-.ÍNË^ûīĩQĪGq‰mņđõ(Vo]ą57;÷üŠķˇŧrÁŦÉŧđ×ôáLŌõĄ/ūęđŖ{&LÔ#ČģŖˇéŨ  Æū@ˇpËÄz4kŪL"‘LžP]U-i"™:ęģkŪ5ņģf5T"‘č̓Ÿ~ũŅ×;ÖėØŧx3€V.­ĻĖ›âéoŌč¨p¨G‘M¨vĘŧ)åĨåßōũ¤Ib‡ņoīÔŗ“Y>3#8ˇuŪ™žsåĖ•sĮÎÕ×čHeŌ™13M¯ÁČĸž:čØ~“ŊAJQjĘĢzíŧÚˆ‹ĢũĄ§ŋįŌKMÉa?Ūnbĩōĸ H¯×+ XŽådŅS(Ė/,ģWæîãNė=ĶQBų+~ũß<5šžčģÍēĒUį_ÍīØĄYsSxtŗ1û,Î>ĩĄņa/ô2ë%E­V{ãō §–NžĻ?Žŗ[߯ÛO}!ę0ĮqĶWõxøāaANA‡ŽĖu=S‘z÷?ĀORC=#žúKäĻâĻw€ˇé‘”Ŗ|>æ×÷2ûL˜ŧÔŋ ĢžÂWO}!j7v+Ąlđ5šûęûcũÆJC¤‹ˇ.vķtĶëõ%wJägäëæŦkŌ´IâíDI“üģßņ{txę Ž (V`ÅĖ1ũŒ,C å!rā o#Ë<‰§Ÿg—Đ.æ*i2ĄO=$†8 .âøMš6‘†HMWj¨ė\Đ=oâĻY1H$iˆÔ;ĀÛŦøÎáœ/ž>á”AÖāʅĩiŪ˛y—^],¸’‡ŧúVĩ!ī&–ÃŧģKÍ[6īÜŗŗY‘Ąh8†×w4 ašČ­ī(Sč +@A}‹G#ø:~ÖāúÅëË|~`@׀VέZģ´öīė?î_ãŒpŋä~YIÃ/ŗį!¯+žū˜‰7ŧ/ ;ŸôPNprK1ŠŲnRy?ôĢīč0 ãā ČEnÂę;Ú]ķĮv ¤ŗÕ÷R°;Ü P`ĘÃV’ƒœ§Ļ*„afd]&Ϗ‰›F^L–AÆÁĪa|œđ…/ŲņĖ8ŨÂģuė°÷ĢŊ)ŋĻ \SĄųīw˙Mų-%|xx[÷ļ Öpg;ĄĶS9Á)ÜôĐ„ü5­íŪ…hx+š†kõ BÂÁ`dxā ßŗ8Ëv‡,BŅ0 Ģī(Išnã6Ģ1č ģ‰›FÖVę…^æ&M ‡ŧžá@ô¨oŨÉB–‘qÂÄ¤ŠŠcĶīūøŽ˙¨ūLø ŗūC\†<ÛōŲOį}úʛ¯|zđĶc(Gš \Œx+Ü$M9Č @Ā_ĸ0 “~ďŦv†T¤ēÂÕȚĄ=ŽãŦĘa1Šãø Н€ \Âf|7]+ŅAˇģÄ`<Æ_ÆĶzâ?#Œ88ƒ0(qæÎ ĖB e! ë¸Ãũd!‹ŊĒPu‡ĮÃØFá31sö4X•K;—%ۗ+>ļ;s÷úŸ×Į_ŽOŧ8īĶyĻLo÷`ņĩÔ#xļ{¨ \ŧāõ—(ôDĪp„ŗw!–Ŗ|ļÅ"ÖH'8­ĀŠMØÄŌ™ë Û„M+°Âøvk°&qėiĶIœô€‡ņÍã&`‚šT¤˛ƒĘķ8ƒ#eŧā5ķˇc;K1蠋Eė,1ōsˆ!Ū„M;ąĶø}+kø?ŽÄHãÖō ĖP@aĸ65kŪLÚ]64Ėŋ‹ŋ‰īŅg!KÅ Ė0R†Ëú÷]ąĜĮųú–Ž´’íØ>ķÜKs4F{ƒĨÚ”ŪĀ.ėiS1Šã×ā–mbˆcģ ÛØ¨MéVĩ)ņáop=)¤#1ōGüČF ¤7F#Úx1VĩŠ U;ąs65xߍŗúˇ(8Ái –Ä"–ŲARŨ!Ō@3L)OjÆ3Æ,d™Ō D›ĪÖĘQū >YĻŦ;h¨™Õtßá;Sz#jiãb*RLU D#ZÅ!0üÚĨJ{#ū_›6b#ŗē@ļöl0U!pÖCÅK—.5ö…o{´_„E-ŅŌžõ=]c:Å(ū ŸˆEls˜ôrģœžÃsk°æntC7‰uīq¨BÕėHFōė0}§™av §ā@7t31rã¤"õ|˛‹Lßuē+ēVŖz)–ēݑ=r”PŽÂĒ@ŽÄJ\˛ËJŦŧ‡{]ŅÕúKĸå›ą99ßāW¸šō8ŧ€ā@ RēĄ›õĢûę KFō.ėZŠĨĻ/ĘÚũr‘ģ[=āÁČâ÷YȊEė3x&Ņ&ūašéĄoEZÛ~ŋ7,žĢPug,Ø|P{Ûī`[öL›ēld[°ųē$,ÆâI˜ô žąøZ,Fņ.ėŌ@‹Ø'POĸ‚* QNpš‚)?ÆS…Ē_đ‹š›¯Đ@ŗËS‘ú&Ū´xųytéHˇ`ķuÃ×÷aßzŦŸŽéŊŅÛâĮ•PnĮöĮ6_7ŗ0K éDL´x ür”īÁ›°É‚ŋ'Û=ô)ĸ@H@§øT u0‚Ø]|üļ UJ(åį §ĨÃ1ü|`ÁßŨ€Š…X˜‡<ø!(ĻėPˆÂldį į&n ` ÖXŗËęOđÉQu†s‚dyÃģA(EéMÜĖCŪYœuËtLˇ 0ô}Ņ7>đ1ũᏆke(1ˆiĐG0B R–a™ęnčÖũāgĘŽbšČÍCžŠtDĮĪņšĘh@Õ2,KGē/|Ĩš~Iä#˙:Žg#Û.ķ0Ī‚Ę€ē­Øú-žĐ}Ø Lŧ$Žã:yęa&fÎĀ kžā~Ŧ‡šūs4ØCë‚%”9Č1Ņüķƒ_B`Í˙:čŠP”‰L9äĻ,î gd=ĐÃî >6¯‚*yYČʇI/õ@Žčę _kdņ1ÔP ā2.ס‰ËcøÃŋ;ē3ûs˜{I8ÚzČ cđį —„ōLdš¸"ū ";•Z#‹A~ŽldŲhĢ62ȂĖė%ÁFm@(Jcƒ+@P(Vų?DĻV™Ėq*IENDŽB`‚trillian-1.6.1/docs/vds/000077500000000000000000000000001466362047600150775ustar00rootroot00000000000000trillian-1.6.1/docs/vds/VLDM1.png000066400000000000000000001164471466362047600164450ustar00rootroot00000000000000‰PNG  IHDRē´‘[4€IDATx^ėŨ ŧUS˙Įņį1ĪS“Ĩ$™‡ ‰ŠˆˆQzD†'C†H‘' RDŠLĘ\‘&%%J“2ĨIĨ”üūžĢ˙:íģÎŊšÃ9÷ž{Îįũz­WŊö™öŲģöw¯a˙ËH#˙ Pœti…  H+]@Z!čŌ A¤%JØŋūõ/ …BĄdPŅŋũA¤đ2Kôß~ūi‡  ™‡  ŌA2A¤5‚.d‚.Hk]Č<]Öēyē ­t ķt@Z#č@æ!耴FĐ€ĖCĐi  ™‡  ŌA2A¤5‚.d‚.Hk]Č<]Ö t˙üķĪpQŌ-\¸Đ,X`›6m ĢŌŌoŋũæžo´h@At@ZËOĐŨ¸qŖ]zéĨöī˙Û~øá,u  Í›7ˇSO=Ֆ-[fͧOˇSN9Ånģíļ,ëå×6Ûlã>ķĪ?˙V%ÕđáÃŨ÷xüņĮŨãëŽģÎ=ž;wn°fbõë×Ī}ß°”-[ļH.4H]ÖōtŸ|ōI÷ŧã?ŪÆŽVÛ+¯ŧâę}ôQ÷øŧķÎŗíˇßŪ~üņĮ`ÍŧS€VĀ\ąbEX•T'Ÿ|˛•,YŌ–.]jķæÍsû˛Ë. WK8tõŊ[ĩj+wß}w¸*äA¤ĩüŨvíÚšį}ųå—aUĖ9įœc|°ûûâŋm÷ŨwˇŽ]ģk_}õ•ûžĪ<ķŒ{ŦÖé%JJ̞ē¯ŊöZXųFĐi-?A÷Ļ›nrĪ›1cFXŖq¤ģíļ›}üņĮîąZw+WŽlũõW–õ^}õU;î¸ã\7hŊæá‡n5˛gŸ}6Ëz×\sxāļ˙ūûģ2bĈ,õžÖķë„ĨOŸ>YÖ9r¤ÕŽ]ÛļŨv[÷ŪĮsŒ=ņÄqŸņ†np-Ǟ~ũz+UĒ”=ũôĶY։ZŊzĩ]xá…vÁØĘ•+Ãę<ņAWŸáŽģî˛öíÛÛ[oŊŽyBĐi-YAWž{î9ûđÃŨß5žôúë¯ˇß˙=V¯ Š×Ųyį­N:ŽKōŽģîę–]|ņÅąõD-Ä ĢjIUũë¯ŋžĨŪ;˙üķ]}ŊŨúĨK—vUú÷ī[īąĮsËrO<ņD÷Ūģ랋[ϰĨ–h?WãŽožųæ¸05a„Ø{Ž3&ŦÜÆč6kÖ,c&äxúw$ö÷Čr€´=ŲÉ ĩÔÖ¯_ß=oūüųaužtëÖÍŊN§N\€ŊÕØß=zkoĻÉ ôœœ‚îUW]å²&Ėúõ×_íčŖvë+d{+ŧãŽ;Z™2e\ˇdī—_~q­ēj]ūėŗĪbËķC-ԃ Új ΍ɓ'Ûa‡fwÜq‡7΍ÖwÚZĢ2l A¤ĩŧŨŊöÚË=G˛ ÔJę_OE­ĩ§Ÿ~ēë>œSkå?]uÖëjŌ¨#<Ō­Ģ託^zÉ-׸á°{ŗo1žįž{˛<'•,Z´Č}Fuņ€ü 耴–× ;pā@7Övģíļs- õÃ?¸ÖÛK.šÄjÖŦé§>SØ}Øû§ +š$Jc}ĩžē‡† âę˛Ã ë‹Z™S™Z¤5žōƒ  (r_/Ym÷Ŋ1ךöųÜjwŸÖåÂĮĻZįásmÜÜåáf@’ä5čʍ7Ūčžíö›j‘ŦUĢ–›āÉĶßĢV­ęnG”]Ģî?ŨŸ~úÉ=ôPˇŽ&nōÎ>ûl{ꊧÜß,X⯿j=ĩü†T˙Į„‹ķD]°}wėüÚ°aƒģ°~Æ÷ßß}ŋ˙üį?Y–{ß|ķŨ{īŊöĀdģ €  (2›ūúËĩĐ÷šl^lŖf.ˇ/¯NëōūWËėņŋŋkË'§ŲOMˇKˇL\„äČOĐÍídT˙DˇŌë”/_Ūĩčęū°žģq5bëŠ+sØĩX“Léq•*UløđáąuũøUŗõĪŠTŠ’[Ö¤I“ØzˇŪzĢ[ļĮ{¸Ų‘õŪ 4°Ŋ÷ŪÛ-× ĮųĨqĖ'ŧĶN;Ų×_V᚟ÔJŸQŸ]Ÿą^ŊzļÃ;¸ŨĪ?˙<|ŠŖ{ėęyē Ų!čŠĖcŖØUŋ´ ķWÅÂL(>ūÎÎ}t˛-ú•°›LEt;vėhgœq† ēz=Í|ܲeË,÷¨UĢfôÖBŅĸ°ŊŨŽnŽãK›6mbëi’¨ŪŊ{ģŲ™ũ{̍[öå—_nķæÍ‹­›WjöAˇ ­Ūš)ú ƒŠ}žmļŲÆĩ‚ûŲŦŗŖÉŧÔ<]ˤ'‚. H¨ģr“^Slüŧ•q0“ĘãŖY‡Wŋ 7(?A÷Î;ītĪĶ­o ÚÅ×SW_DŨ†¨°ŠģŗŪ{͚5aUž%ĸërÔēuëÜgü§Yœ5Ķ´ēeĢë2䄠 (‡Īą>ŖÆŋL+cįŦ°F=§„› ”Ÿ ;}útwī[=W­˛HšQēTŠRnöiČ AP$÷šbŖg-‹ ~™VĻū]Në61Ü\%‹ņãĮęgũčŖŦ}ûöÖĻM{ꊧlŨēuá*YŦ\š2öų~ũõ×°:FŋĶgŸ}æÖ{ã7Ü}Š7mÚŽ–g7n´áÇĮ^7'ē5Ø /ŧ`¯žúĒMž<ŲÖ¯_Ž‚ @Đ ‚î–BĐMŽD]°Ÿp öī˙ÛæĖ™c_~ųĨmŋũöVŗfÍ,ëtĐAîŊUFŽ™Ĩ.ؚ4i{īG}4ŦÎփ>čÖ衇ÜãC=ÔvŪyg›7o^°fb5nÜØŊī˜1c\Üc=l˙ũ÷ F +gžyfė{ųrōÉ'ÛÚĩkŗŦuÁÄÖũņĮÃjûíˇßėėŗĪļO>ų$Ŧ*ũæ{îšg–ĪĒ}&§€ĻPŧÍ6ÛÄÖũá‡ÂUFÛļE‹qÛRŋųŌĨKÃÕcn¸á†ØēSĻL Ģ=˙¨ŖŽŠ{mŊ_A)¸ú×;ōČ#Ãjûã?ėœsΉ{ī5j„Ģ&\Æ Ũī§û<˙ôĶOî߄ƒ>Ø}& ũöąŋG–TŨ-… ›\Ņ“D™6mš;‘ŊõÖ[Ũã;î¸ÃŊZŽŧĪ?˙ܚ5kæ–vН?žŨ|ķÍîŊstÕWĨJ;âˆ#\Ģ™BŸžûĀ„Ģ&Üwß}gģīžģ5oŪÜ=îͧ{oĩ„FuéŌÅ-oÔ¨‘kĨûâ‹/būŪ{īÍ˛Ž§VÔhāÉ.čĒåQu÷ÜsOXU ëģ랋uíÚÕĩ@ęĪ˙üį?îˇÉÉ|`'t’û<É ēĪ<ķŒ{Úĩkģ`ĻĐ+¯ŧŌ-ģęĒĢÂÕ­ŗŨvÛÅļeNAWĮ„ę/ŊôR÷Ŋĩ˙Šõ}čĐĄáĒyĻ‹/žøĸÛ_˛ ētī}Úi§ŲĶO?m}ûöĩ›nē)WĮ@A,Z´ČļŨv[×2.ú­uQãã?ÖDaŌžû{d9I•ˆ ûŲüev˙ŖOØeWļĩ˙Ūv 9!n]•AÃFŲ wtļ‹Z^e×´ë`=ŧhSž]‘eĪŦ´žOŋl˙šîfkqÕõvg—6jęü,ëø Ģį>Đûi÷ž×ŨzˇŊđÎ'qī™—BĐMŽčÉN"ŠËgŠRĨ\‹ē™*(ū÷ŋ˙ͲÎ#<âŪ_AW-Ŗę*:kÖŦ,ëD­ZĩĘ­÷ÕW_…ÕY,X°Ā­§–Pĩ* F)(ęŊu’¯€§us 'īŋ˙žkš0a‚{ŦĐyØa‡ũcˇāDéÕĢ—í¸ãŽŽ5P­Įŧkéēøâ‹­dɒYZoĩŨ0N9å”Čš›i{¨…Rõ E9]u…U]ÛļmŨ6UÉŽ‹ņŠ+\Ũ… ŨcũîãÆŗ™3gknžĐ ×ŦS§Nė5õüė(čëˇŅE}æ–-[ft˙üķOwņDë*čëâDø›{ę6Ũ×Ô}[Ÿa͚5îąē~ī´ĶNYŪC¯Wēti;đĀcËĸN=õT÷šŽ>účƒŽ~?ßęŽũ7§m)Ņ}ũŸē×ĢׄÖĶ뉎ģ0čĒ[ĩŽ?Ŋ÷°aÃbÛ=;:^t܌1–-[æžĢuųå—pÕ\šûîģmß}÷ĩå˗ģũW9ZˇnކBFЉ‚]΃=ÂvŲuW+WĄĸmŋũļÍß'´]{=[OôÔ37wc+ŋ_%;öÄÚļ×>%ÜãŖŽ­ ģúķøZuŨøę‡i‡}Ŧí°ãNîĩŸ~udėõt÷Ü{;ü˜ãl§wqīĢõ:vë÷9s[ēɕŦ ĢĀUšre{ųå—ŨcßU5:ÎŅ]øęO_4h%°éīž%/ZŽ;î¸¸ĀŖ“ņ3Î8#Ëz - ŠŅną>čVŦX1K—X„pĖđE]äZŨD'ëę˛Ŧ—…3…ÎŊ÷ŪÛžũöÛ°:ĪŽ=öØX˛ZkõĸÁTÛ5 ‹~øĄûNęžŌkŠîškމuĶ ƒîˇÍ}Š[ˇnlŊĮ<ļ\­š/Ŋô’•-[6ļŦUĢVąuuÁ#|-_úõë[O]ĻuA!Z¯ũéCqūîj™Ūoŋũ˛ŦĢ}MÛ?:ūTaM­´ŅõL`õw}vĩÖëFØEYaSõ'žxb–åĸÖX=˙ÜsĪõ^ƒŽ.üDß7ZN?ũôØz ™zp P—ß(…e‘Â]ÔØõAסPgWʗ/īBŧ§ßR-ž^ĮMõęÕcÕ=ģcĮŽ.đë¸ÍލŽC‡îõôÚzĩ4‹Âŗļˇ4Š–~ĪØß#ËHĒ‚]ũVŋaûtænŲ[ŸÎp!tßōûÅÖģëÁ^nŊ›:t‰-SËmãf›Oz2Ü-{¤˙÷8’õz{ėš— Ä~™‚ŽÖĢsZûäĢīŨ˛“žą’Ĩ÷uī=uŅoqŸ57… ›\Ņ“DS+\t ĢZŖcK}ĐUQģīžûŦ~ũú›÷ˇŽ]cë)Ä) h|Ą‚Äũ÷ßo—\r‰[/ėJĒ–"-?īŧķÜëŨu×]ąõŪŊ{ĮÖķA×íŗuę¸u}°RˇŌ(…tQpzûíˇŗÔ‡ÔR¨P¤ÖŌhwí‚P+¨ē„{ [{Ģ.ĮûėŗûaҟūŲAÕkŌ¤œ‚ŽÆ%klĨęœX}éßŋl=ou;VĢßĻģíļ›Ũxã.kšZ\EŨŊ}Ģl… ˛ŧĻ^GŽū´ŽBŸ~,Oö¯īƒŽBé^{í垏Zĩîĩ×^ßUÔ*é/¨¨KˇÖĶžŗÃ;¸eēxĄŽĩá…ŅXs]ŅzacũĒS ÔžžSĐUˇk}G˙:—]vYė{kĸ+O¯§qÕŅ}Ũwņ×E OcÔrĢ‹4Ú_õ}´ž.HúßËSˇjũ>ęz¯:ŊļīΝ;ĮÖķ]ÛķÎ;ītŋÕĒUcÛ\ tÜčs„á6,Ņãōĩ×^‹ũ]Ũ˜ĩoĸčé7ũ=˛€¤*hĐŨnģícaĶ—s.Ø|?aöĪlëļˇÚäo—gY¯ßķo¸õ:˙¯ŋ{ėņŊô˲ŪsoŽąĄīOŒ=VĐÕIÖSæfY¯ŲåmÜķÎΚ-•NÛ\|‰ž˜iÜ`´N'…žZĸuŅā¤đ­‹ŽĩÔ ^´Ž[ˇnąēÛoŋ=KŨÃ?ĢģíļÛ˛Ô)Äyę ­ëŅŖGŦΏõ%:^NãįĸuęÂęE'žQŅØMO'üŅ:ÅķÔ2­S)*>čF7ĩJjYØ5W4ŽQ!K-DŲuËõXŋ՛ožiß|ķk=Rˆ™;wnl=tĸ-ZåʕŗjÕĒÅį×âŋcŨx ›B°‚ĻļĪŗĪ>VÛW\‘eŋČ)čĘĉ]Ž§ĸßBëjrŖhKļšžÂŠÖ;ëŦŗ˛,÷ôųũ>Ŋ0ĸ ¯@Š:HÕEZ­Ûj5Ô1 .ˇjU+¯ÆÃúßVĮŋžį[=uãÕr…ņėLš4)Öâ=Ū=uˍžnNA×ķ“‹ųnŌ9Q÷ë螎[5ļÖĶEŊŽZ`Ŗ|K}ØuYôU—SĐTÖö Đ‚J•*šįŠ{4Ō‡~ĶØß#ËHĒ‚]u—7ŋbsĀ3}Q–å ÄĪŧūuęū¸ĩŧúF;ø°Í3‚vz¸¯ĢųŲl×zĢeV?ÔZ]{“=ņâ[qŲw]ß÷Ē6wŲ{wÂ×quš)´č&Wôd§°EĮčz .ZĻ™~=ĸÂíB+š]7VuŽŲŒŽŅR –Z¤Š+#ĩâĒ…ōõ×_Ģ]ĢŠZՒŠđljĒ|÷Wũ= Ĩy ē ×˙tëŸ ēū–BkČĪmyUØķ-ŗžhF_…XOŨĪÃįyÚ_˛ ēj‰Ô„YڞO<ņDXíē¨+d+„*TkûŠ5Vīsũõ×gÛâūOAW]īÕà ú]|ŠW¯^l=ĶÖŌđ÷ŌļÕoŸ× Ģ úí4D ä/¸ų Ģ HW_}u–ÖøhŅE%õŪ@jĶoû{d9IUĐ ›ŨŦËaĐUĀ=ãœķŨØ]-ßa‡í#ŽąĶÎ>Ī=öAWåÍOĻ[ã‹[ÆÆđĒčī]zˆ­“ĶŦËŨÔ=Ų)lš ēûč— <ØŨ+VáF'æaĐUWaĩLĒģîc=æZĶ5žUĪWØõŌ-č*đøV>uiÕ=gŗãģ§fW蠘נĢ×ø'˙tÕ WõŲu÷“>Eģ.kŌ&uÁV°×íŸ4Cr™2e\ W˨ømŖũ'ĘĪ–]ĩëų “Ų}Ņû†ÛĐ…_ß;ꟂŽļ‰ęÕe\­ņÚ×ÕōŽ×‹ŨZĩjšpŪÛW¯Ģī“× +ē ¤qĐá­Ģü-¨|Đõa~k%ŧR~§Øß#ËHĒÂēg6jęNžŽŊų.{ũÃ)ąõzÚ|/Æ{z,î5Tžûc7›ōž{ííBō{fēåŨâ)z˛S˜Pü‰w´{­ēüj™&$MŽŖ âīšŠq—>ˆh’…ŅIŋnk¤ąh7\ÍJĢuŋī– ē?ĢeįĢ–I˙úõŲ<×Ž~WÍĘíĮy‡cyÕĘ­uüļVĪ˙ŧh°ôŨ°ũwŅļÖwĶ÷Öūë÷K áĐë(Ôû°Ģß֏V‹ļŸ%Y=ô>~ÖeMĘæß;:Y—˙Ūúāß˙É'ŸŒMÖæƒn¸eW˛kÍ΋víÚšũ#ŧ0ÄŅoû{d9IUAWŗ&kåp=ŨbHëilŽßŪšģ›IŲíõåæŽ›ĮŧõáM÷˜ []W=4ŪÖß8ģĸā튕\-ēZŽ ēĀ]ˇ°Æč*|ëŊuĄĄ œ3ũĻąŋG–T…t+TĒlģîļ›ë–ė×ŅŊrwū˙“ũ[îyĐ-ĶŊxõX“JéŪŧZöéĖė˜Nrˇúđ‹nAˇxŠžė&åSKĄZtBŽ ĒÉĀüX?…҉¯“&ËQĐPWf™T@ÕzĒķ-PšYYĄYŗ.~øánb)…ŊŸ§NŊ§O¨xĩĒi–Y˙Ū]ēt‰˙˜[ Í —š18ŧ%L2hģ…ã$ŖEcGŗŖ‹ęŌë×S—áėî=ĢX]lĐļÔŊw›6męZWũLÔj‘ßSEŸéⅧßHŋU¸žŠē•‡ˇœyå•WÜo­đĨąŖę ßEëë÷Ķ-Ôē¯ī§ĸɚtû"Ũ2H]ÖŗģG­Zŋ5Ų•ZtÕ}]A^÷DŽ]ÍÜ~>_´Ŋ˛Ŗ‹"§ë×ĶgõÛG^}õU7ķqøzšÁØßŸYôyÔË@ûēēŅëûë8đĪÕ÷ô=ÔJŦ‹6ęJŦuÕǝ[4iÖi­ĢI÷ˆđŗSĢīŠņļQ –šČMˇîRV뷎‘ ēšHLī§ũÉCЉÂē=ŧčî¯ĢąšÕ9ÜJ”,íē#wčú¨ë’ÜđüKÜzŸ/\eg7ŲÜĘ `Ŧ‰ŽvÜig×"Ü­ī Øët‹§ĸ ē@Ēƒ.˛ōŨĄuĢŦĸėÕs zĄ‰GЉ‚ŨŪënũ.WË퀗ßĩ)ߎˆ-{gü×ÖžķÃŽģr÷ūĪš–Z-×ķÃ×ĐØÜ[;usëv{ėũŎYę_1Ū÷žoûĘŊī¤yŋÆÕåĻt“‹ ‹LĨŽÛj!U÷eM:ĨیŠ5>“Šĩø˙û_Ŧ•\÷Vˇr] ˆŪ†+™Ž9æ˜,ˇKCrtEĸ A7Ũ A7šēČTûėŗÛ˙ŖEŨš3™&íō~ųĸņÅŅąÎÉĻIŅō;lšGĐ ‚î–BĐM.‚.2ÕōåËŨŊz5öTE!›' ķÛÄĪzôCĐ ‚î–BĐM.‚.d‚. Htˇ‚nrt ķtEâ”'ØÔEŋŅžL+ÚuÜrë $A2AP$šôšbŖgmžgm&mƒķzN7ˆ  ™‡  (÷Ŋ1Įĩų~ˇ™\z°Đî~}v¸y@]Č<]@‘øzÉj;ŋ÷?oe\øË”ĸīŪ¸įˇ-<‰ēüņ‡ 4ČŨŖTf͚åë–!‰đŸ˙üĮZĩjeŋũö[X…<˜?žû]4ŗŽŧ˙ūûîąfÜMŨ2æŲgŸĩÁƒÛĻM›lîÜšî=?ūøãp՘؉'žhp€ĩlŲŌ.\ŽâŧöÚkvꊧÚūûīouëÖĩ—^z)\%Īž˙ū{ˇ¯ŨxãaUmܸŅîžûn÷ú*:nBē§í3ĪtŌI'Ų /ŧŽ’gãĮwûî×_í2Ä=^ŋ~}–õ&Mšd7ß|ŗ}ņÅY–§‚. Č<6j]3čK›0U\L÷ĸī|åĀéÖûƒáfA‚%:čĘå—_î^Wˇnųõ×_­TŠRî¤YáĻ ļŲf÷څyĸœ+VŦ°=öØÃz÷îVĨŨ/Uŋ‘.ėžûîV­Zĩ¸õDSŌī÷öÛoģ÷­PĄ‚•)SÆŦP=Üē;밃•.]Úũ]ë*€F=öØcŽnÛmˇuû™ßG â̝žr¯ŗīžû†UĻũwįwv¯¯˛víÚ,õ:VÜ}}´äuß˙å—_ÜvŅs÷ÜsO+Y˛¤ûû^{íwKĨ‹/ž8ļÍõœvÚÉ=V8 é€ęô=ü=‰xW­ZŽŖ÷×zķæÍ Ģ’âĮŒí;úˇÛn;÷÷‚ėũõ—UŠRÅmGûO>ųÄŊæe—]ŽjˇŨv[l͜93Ŧ.Rú\ąŋG–t›ūūĪôąQ ]Ëî“gŖg- „éVFÍ\nũ>\l{Ovß]ÛÉ=ŲIüé$°yķæîąZņô>īžûn°fŪųûžfŒR‚ƒžkÛļmÃĒ”ĄûĮęÄ[­„Ō¯_?z?üđÃ`ÍÄS*Wޜ5jÔČ=~ũõ×Ũözå•W˛ŦˇfÍÄöÛoŋX°íŌĨ‹[ˇuëÖąõÔ2Ēõ€ŋũö[ˇL-Å 7ģíļ[‚ģ§öĩDõFéĸČ駟îžStõžZ~öŲgÛėŲŗŨw9r¤ŊņÆYÖË ļԂė[fû÷īī–ųcÔ;÷Üs­Yŗfn}ÆN8Á­mí_ŧxąmŋũövÔQGšuôēj­Õz;wŽ­;vŦÛĻëÖ­ Ģ’B­ØúLū“ļöí˙˙ÔúœũzMĩļËĨ—^ę‚~v ´>ųä“nũ{īŊ7Ŧ.R]@‘S×Ũûߘk>öšģÕN2Ë ˇŋi7ĀƟtąí^áÛûĀė„;ۊ[/YEßQߕîʅ'AWԍoĮwŒLžvÚiÖ¸qã`­Í]vt¨ĸ.Ī:ÉOÕĸ×ôek]lÕ÷āƒZģví\×W…˙ũũļÖU^ßŖW¯^n›wíÚÕ>ũôS=z´ûŧQz߁ēõ´žēëģGƒŽžŖnä7Ũt“[Žå?Ã|åõĸô9Õō­×ԅɐöÛ:uę¸õŖvŲeוųŸ\yå•îŗhzڇĩLI<'zÍ#Ž8"ļLôšŖÛŌ—°åWĮŽ u•Ö÷yāâ~}ŋčķ6ĩŽZhũ…ލZĩjŲŽģîšåˇĐņĢĪŽ×đÔ%~Á‚ŲŋŸz\pkm×kęß7ĩz?õÔSY։Ō1îËŠ€  H[:QԉKŸ>}Üø%uSS+œNu¯ ĩJŨrË-áS‘F’tE'ƒūP­lęĢ“aO'‰‡~¸û a‰ļډZaĸõ §Ųyøá‡c]W}Qāö×~ŽqŽūąZũÂu4ĸ*WޜeĩpŦŸīš]QˤhœĨ_ÖĸE ˇLá'ēŽųÚVŅīŌ¤I+[ļlėąZaßzë-ˇŽZĒ˛Û–úėͧOßü˙ŸÆŧú1Ô {ęZžĩÖ-ß5SŸ%ėęš_j9TāĩĒģˇēš{Û¨÷ÔÅ OÛŖ|ųōnyôBÃÁė–Z;ÕUU Ôb§îĨ #ē Ž¸~›ø@¤ßÄ/‹ļĀ˙ôĶOqÛQûov^~ųåX÷Ū°(Ŧ{ ;jɎÖkō]y}ĐÕØĪđuĸåķĪ?ŊĻZ^ÃzĩbūßĘļčzjEVÔö×įŦTŠR– äeWôQ 4ˆ[GEĮŒh?‰.׏čcĩ0û}ĘĢYŗĻ;ĻŖ|îšįˇžÆ>ËņĮ÷žaŅą'ŋ˙ūģûwĖēĻ1ááE(‚.IļråJ{įwŦcĮŽŽĩI'ø:1žúęĢŨ MxŌ!K–,ą%JÄĩŽ!}DOvMÁ$Ú:†)߲ŌĻMĻt’ß}÷šįC į'ŅØR='ģ ëŦÂЋ/žč&€ŅI˛ÆnjųųįŸī”NØũÉŽNĘīēë.×rôüķĪģ@¤V (uķTK­Æ•ę¤U­Y:~°ĸôÚ9ĩčú–)ĩ@ĒĨÖņßQÛI“Ųč¤\ËŖĮ[´uOEN-w:– 4‘“ēįxāŽ^Įŗē†ĒUQ!EßO/څ7ú;č=0ŗŖÖ.M`¤–:ß \P ŅîēágĐįÕŋ=*jÉÖú]tQl;DģYkß /nhŠļ€j}M°¤:tõ›øß+ėjŽíĢß­^ŊzŽ>§ Ģíĸ°ĒßOŋŋžįĮÖę7uíՅ1í Āڇ9䐨įõÛB "ŗkŅÕsĸãÜĩotčĐÁMÄĨũRŊ´Oj×ŋ÷9Ņ…­ŖîļŲõ`ß"§(…Z+úūęZŽī¨Āį×÷cpõy}_tlhđ˙õ@ĐqŠ ú>ꠀ­ >¨ĒĮÂa‡枯íūøãĮŽ]áBëxÚ>ZĻc\ôûø _úŽĸ×đ“‚åTĸß?züčߡčÅģėčâ„Ū¯}ûöaU‘ŌgŠũ=˛€”§+ÎęÖuÕUWš]…V÷@ĶŌ ÄÖN„ĸŽŋūú”û‰=Ų)lšWī¯VGīSLuųT˜ē fG'ĨzNvAW=Tᆧ^ ZŽ€č)øhYØcAáC-oQ:‘UØÕŒĪ 6tjbĩ„†ÔŌ¨× ƒS”ZĩNæÕēĻåá…%ĩÚjškvŨ‡ĩŊT¯.ČŅ@ĄĸYfU•â@cģ}€õ“ų–Yí'žZæ´LÁMaÖOļvĮÖ-vqũ§ßˏ;Í)čú |æ™gē'õoޝZz}(õ6ÔÛ J-ĮjÉV]8F×˙æ[›ÔL¯¯÷Ņ÷×~Šīë[õŗëÆ+ūB’. (LæDCˆõš ÅÚ×ĩßFŠ›ļŪ+ģßgk“Mũ÷ŋ˙uë„AW ÔĶ@ŋ“˙>šÜJëF/°h[k™~”z.hQö4ÖXa<ģĪčƒn˛é"Ž.Œi˙Ô ĖŠBÛ ö÷ČrRŠZ;ÔĒĄąXúĪ[˙Ą*<¨5@3’Ēe+ŋ3ŪęjŊNŠÂÖ8¤‡čÉNaSkĸzč$ڟ0̍E6 ĢŪւîÉ'ŸėNfÃ}]-EzNvAW-zQē(eWãķ|kiX ;čæzÔ]3ülaIĩ‰prK­–ęæŦĢ1›gœq† j~ܤū}ŌãęÕĢĮ&PŌEŧ#<Ō}īh@MFĐÕ8[/ĩÖFˇˇÆ ûđęƒ]vãēëׯīęōtÕâퟛ] ƒŽÆŋúŲĐÕÅ>ģĪ’uOÖķ´ũCē€rÖYgš‹¨úŋF­ūęžŧĩIž ēęęė/ĘdW˛ ēŲõ.ĐØs…ã(ũßĨĸĩéb˜?^4I—¨õ\‘´]˛+:ūŖ˙vä•ļ…Ö [1Ĩ }žØß#Ë(R:šĶ„.úú¸ãŽs'ęö¨ŽŠēĘ^y/(ėĨÚø"$Fôd§°Š%RŨ4EŨcÕõXŨ™Õû@'†ŲŲZĐõˇF CōÖZt˙)čú×Ôąæģy*ܨ•¨ A7ŧ‰o‘ÎkĐõŠëhØĸëKt"ĄâĘĪŪmąķcZoŋũöȚ›[.ĩ<Ú}9ŅAWáQīŖ}WAU›ŠÛ­;ë÷­N:šĮúĸÔ× ŧ]ëĨsÎ9Įũļz- 5QĀŋŖn/Tģvmˇ\˙_¨%9/ôúzî 7ÜVeáĮŸĢĮÃÖdtũ6R÷tõZиj]ĖđŨœŗ ē ĒŅąą~Ŧģ&‹Ę‰Ž-aĩRûąë:Ãp–°įG^hŋĐįŌū—Jô™b, Đč?|hh–NtkĖ’N°uBŖP “údßĒA“€¨U7ģ+č(Ūĸ';…MŨ,5‰NÚŋūúk&ÔB¤ ŠåžeV'ŧ>°ų1ēęRī—ųũR­ĒS‹°ÆėŠkĻf'öct}ĐU˟‚‘–5mÚ46ąŒÖ×}]5öĪĪ^ĢV&ĩjlŸēS+€čd\ËČ5!U´õĘO˜Ŗ@1cÆ ÷Ŋæ5>X3ßęÄ\­WębŠņ” Z:†|×J?ɕZ&î˛¯ŖĒ¤Ī­įkh‚Æęŗ*䩗‡žcAn‘Ŗ_ÔåōŽ;š7YôīŪāÁƒm„ Ž›˛n…¤mŽnÉŅ€äo•¤îæęÆĢmŽ@ŠŲ™ÕE5ęü…G]´ĐėÕž[Ģ•ŋ‘‚–ßΞk˛&čĶãčīíotėąĮēũBÛ\˙Vk[iš†cŦßFûĩöo­§îÍÚ'´žŠ&mĶĖΚI87ŋš¯W˜ÖëiŦļZ-ũÄXúŋA-™Ņ0­˙7tĄČŋž‚ŠÂĄ§ž? ÎúŒÚ>ÚÎēå'kōĨDŨųõ\=GCa|ĀVOĸđBM(ģ Ģũ_ËÔ*ŦīŖß\]˛}7c}ntUôoˆē8ĢEYû‡Ž]sžŽi}n…Íž}ûÆ&3ĶXāÂÂdTü?ũgŽIo4y†&5Qk­ēáé?}øeײPt5ZŨL‘^ĸ';…-§ŲNÕ Tcb=ß—SQ̚§î•:áÖûÉg|ĐUh‰Öûązęūę—)P‰&ļ ģĨĒDßC]ŖŖü-c¨“ķ°>Z|Ģ]vEĀßUtĢĄpV__˛4YR~ųû†Ē„ˇ{IÖđ{h r4ÄxšA8üôlĸôšú÷4|]_ĀDŗR‡uŅâo…ŧ°ÎuwU`ķ,Ŗ3?ûâ/¨(€küp_ĸŋšBĸ‹-ŅũR˙‡č"H¸N´zčĄąĪ¨ŅĪ-ēĐm=Õ˙Qá:ęė/lMvAWÛŌWŽ–č÷Ņq+>čjū‰čŒęęĘ­‹ QūB…/ē(ĸãĒ0tK-:ē2Ž{ę¤C­Dú[Wœ5‹fx¯Áĸĸ“6XųqpHŅ“ÂĻ.ŸjĶ>jaÕ1 {hF‚h2uÕ÷-QŅĸŽĸūV&žZäÔŌĨ.ŨūߔkōQk”Šž¯ ąüŊV5ŽĄ.(EĮ0ęžŧ:IU8ÕÉļBĨ°ž¯ûņ†­œzŦVšëŽģÎĩ<+ŧ„ŗÛúKuU˚ZÕ*§×TkŸhėŊZÔÂīŦ’Ũ¤Tú^ږjíĶí™Ôš­ÖŦ‚eP]]äŌ…püs2é7Ō~Ą÷UkũÖnáĸ €šėI­ÎjÍĖ邠•ž‹ŋĮ˛ēĄk{ĒØ÷ Đ~Š–Ûp›g÷{Ģ봂Œö ũŪz]ĩˆf7FUûĩî÷Ēũ\ŋŋߎ^Wŋŗ~oĩžįö7×īĒũ\ûĨB­ļ‘ŋĪŦēöë˙ígáëD‹ŽŸ(uyÖVŊžēk›f7i•öMĨßG—Âá[“Swpõ†Đą īŖŽĖjÁÖ âúœÚüėÆ>čę3hŸĐvõ­Ŋ!MĒĨĪ§ĪŠ|uã.lē8¤Īëg~N]@Béęú˜1cÜɉZ}t@M‚Ą1Bj}P÷Ē­Ė5<ëéŖ(ƒnaŅ ģž§ŋw,€ĸĄ°Ē ēj/PåV4čĻ: yđ=Eĸ]ŋSAP [¨˙ÜÔ˛¤q\ę6§nqÃĨ+îy¤¨ŠõCãŧr{k"¤žt ēē—ŦēQĒpĀî;ęĪüžXČĩļ*˜ęXԄXē¸ĢãQ=ōJ!ŲßU@¯Ą×Ԍܩü˙‘z‘hž×Nĩ‹Ø]@ŽŠ;›&Ņ¤QšuS“ĄčVš¤CŨÕ4qIvŨ؊u+‹Ž‰Dņ–ŽAWĶø1›š|FˇU)h÷]y§qҚ@QĮĸÆĢuS˙æ§5VÃf4+ē^ËŊ^qģ`œ*ē€iLfÚėĐĄƒģ˛ė'Ōø#ĪĶxŋt¤qUjÕM•ąÃ(˜t 瀭#čb4›ŖnĄqLJrˆ›!ōŒ3ΰ{īŊ×ÍJĒ[CdŠ-ZÄ&öAņFĐ€ĖCĐ€ ĨIŖ4ÛŖfkÔ +Ō?<ŨĪO“Fe2Í\Ēn،y,ūēyē!t[ŨfD“FÕ¨QÃuCŽUĢ–ŨrË-îö ē§%˛jŪŧšuëÖ-\Œb†  ™‡  iH“F龀ēWāE]d*T°2eĘX“&MÜ==| ŦiĶĻÖŖGp1Š ‚.d‚.¤˜E‹Ų‹/žh7ÜpƒsĖ1ŽōI'dˇŨv› >Ü~ųå—đ)H2]L(Wޜ­[ˇ.ŦB1@Đ€ĖCĐ€"´aÃûėŗĪėŅGu­†å˗wŨdĪ?˙|ׂ8qâDûã?§Ąh"¯^Ŋz…‹Q t ķt -[ļĖŪxã ģũöÛ­N:ąIŖÚļmkĪ?˙ŧ}ûíˇáS"ĻNęf¯fļę⇠ ™‡  I¤‰Œž|ōIkÕĒ•tĐAļįž{ÚYge÷ŨwŸ}đÁļzõęđ)Ha5˛ž}û†‹‘âJ”(áNx( …’9E˙ö{](…ÖŅŖGÛũ÷ßogŸ}ļ›4ĒZĩjvųå—ÛOųd×U¤‚.€Bŗ~ũzw/Ō˙ũīÖ¤I+SόU¨PÁ.ēč"ëŲŗ§Mž<™ŲlQlŒ9Ō=ôPûë¯ŋÂ*PÄē’æįŸļaÆŲ-ˇÜbĩjÕr­ĩ5jÔ°oŧŅ^zé%[ŧxqø XŠYŗĻ :4\ ŠA@ÂL›6ÍüqkŅĸ…pĀļ÷Ū{Ûšįžk<đ€}ôŅGļvíÚđ)@ąöîģīÚGAĢ.)†  _4i”ēnŪ{īŊvÆg¸IŖ9äkŨēĩ=õÔS6sæĖđ)@Z:ūøãíĩ×^ €"DА+ķæÍŗgŸ}ÖŽŊöZ;ōČ#]7äSN9Å:tč`oŊõ–­Xą"| ´˙k"5ZuH]q4iÔ¸qãė‘GąÆ[éŌĨ­bŊÖŦY3ëÕĢ—}ūųįLDhėųđáÃÃÅ ˆtØO?ũd¯ŋūēŨ|ķÍvâ‰'ēÖÚc=ÖMĨ‰vžûîģđ)"rv@j čFŨ+5iTßž}í˛Ë.ŗ*UǏIŖ5jd>ø 3†IŖ€<ŌqĨîËęÆ ŠAHsĢV­˛#FØ=÷ÜcõëסŨwßŨŨûķĘ+¯´ÚŦYŗÂ§ČMHĨ‰Š@Ņ#čifîÜš6xđ`쿚kb“FzęŠv×]wŲÛoŋͤQ@’¨UWˇŌ-‡@Ņ"čÅØēuëėĶO?ĩîŨģģIŖJ•*e•*U˛‹/žØM5uęTûķĪ?çHi¯Yŗf¸2‚.PŒüøãŽ{dģvíÜÉ´Zk;î8ģéĻ›ė•W^ąīŋ˙>| €B¤V] Đ=Ļ@Ņ!č)jĶĻMöÅ_Øc=f—^zŠUŽ\ŲöŲg;īŧķŦ[ˇnöÉ'ŸØīŋ˙> @{ņÅíä“O€BDĐRÄʕ+ŨØžģīžÛN?ũt7iÔa‡fW]u•=ķĖ3öÍ7߄O‚t‘Ēzõę6zôč° ‚.PDfĪžmƒ ˛Ģ¯žÚ?üpÛmˇŨė´ĶNŗŽ;Ú;īŧã‚/€âiȐ!VˇnŨp1($] hŌ¨ącĮÚC=äē—,YŌößģä’KŦOŸ>nŌ(ĩHšŽZĩjîžÔ đt‘QĻ÷›uxuļ5é5Åjw_he˙úWÛîąō']l7ĀN¸ũ͸u]ę>0ŪÎī=Å:ŧō-\ĘX^ °é6_õęÕ €B@ĐEFØô×_ÖũŊo­QĪÉÖgÔ"5sš}ąxuÚ—Ņŗ–šī{ÎŖ“ma(T7n´ĒUĢēŪ pt‘yož]ņôt›0U\Ė„ōĘÅÖjĀ4û+Ü0’jāVŋ~ũp1H2‚.Ōیī~ŗķzNąņķVÆĀL*—=1Í>ŗ<Ü<’H­ēē5ØøņãÃ*D]¤Ŋģ^ûÆz°0.øeZé;z‘u|mv¸y$Ų€ŦAƒábD]¤ŊķzNvcUÃā—iEã’59€ÂĩaÃĢTŠ’Mš4)ŦIBĐEÚ;åÁ 6uŅoqÁ/ĶĘį sÛ@áëßŋŋ5lØ0\ ’„ ‹´§[턡/S‹ļ€Â÷ĮXŊmōäÉaH‚.ŌAwK!čE§oßžÖ¨QŖp1H‚.ŌAwK!čEgũúõVĄB›:ujXŒ ‹´GĐŨRē@ŅęÕĢ—5iŌ$\ Œ ‹´—ß Ģ ŦŽi×Áz x1Ž.ŲĨmûNöĐãƒã–´tĸĩnŨ:+WޜMŸ>=Ŧ DĐEÚËoĐũ|á*û×ŋūeį6Ŋ4Ž.Ųe§wąĶÎjˇŧ …  Ŋ=zXĶĻMÃÅ ēH{Ũ-…  Ŋĩk×ZŲ˛emƌaH‚.ŌAwK!芥{÷îÖŦYŗp1H‚.Ō^"‚nûÎÛžå÷sK•)k×ŨŌŅÕG×īûÜ0;öÄÚļÎ;šõvŲuWVßøxZ–õēözĘ:ôˇÎŋ˙ũo;äˆcėŪOgYĮŨģģõąōûUrë–(YÚŽŧū6›ōíŠ¸ĪšÛBĐRÚ5kŦL™26sæĖ° $Ai¯ AWuī%ŨÄTw=ĐĶŽ¨qŧ[~Ë=ÆÖ}ôŠ—l›mļąŖ;Ņ…ĶûzôˇæW\gÛnˇUŦ|€›ØJëõzæ÷ÜĶÎ>Ī­Ķéážvø1Įšez ˙z ēzß=÷ÚۅێŨz[š'ģõ˛ÃΚÛBĐRGˇnŨŦyķæáb]¤Ŋ‚]…Ρ>[ūéĖm×Ũvŗcޝ[ϰēO‰R6aöĪY^Ŗée­ŨkøįĢuxûíwČŌ*;öë%Võ CėŋˇŨ[Ļ÷ÜnģííĩŅ“cË&Í]ęˇZƒŖī‘—BĐRĮęÕĢ­téŌ6{öė° Ai¯ A÷Ä:§ÅÕŠģņū˜eŲč/žÍōXĄˇŲåmÜkŧōÁ$ˇŦuÛ[ŨãĻ—^aĪŋũqÜëúĸ Ģ–ãpųqĩ긎ĶáōÜ‚.Zēvíj-Z´€"č"í4č6<˙â¸:ĩā,ˆŧ5ÆZ´šÁj×;ķī\ÍueÖķU†ž?Ņ­3æËÅvØQ5bË÷Ú§„{ũ>ƒ^ÍōZ 秜Ņ0î}kÖŽg%J•‰[žÛBĐRËĒUĢŦTŠR6wîܰ Ai¯ A7ģY—à{ÝŨē ĄšDĒÍMwX΁Cc-¸>čúōÔĐ÷ėōkn´>,z5Ļ××į4ë2AH?÷ŨwŸĩjÕ*\ € ‹´—ė ;fú"7žöđŖĩIķ~ͲŪĨ­˙ģ9莜āülļŊ<"ëįygü×VŠJU7q•ÆájAČ+WŽt­ēķįĪĢ@>t‘ö’t5ūVë5ž¸e–u4ɔŋ%Ņ ī|â–ŠÛ˛&”Ō„VŅuëÖ?ۅe” ē@féÔŠ“ĩnŨ:\ ō‰ ‹´—ė ;ųÛå.Đęūšˇvęf^~×:uÜMVĨPĒ×Đ=vĩnˇžƒÜã#kœ`]zpcs/ģ˛­ģŸŽ&Žō¯OĐ2ËōåË­dɒļ`Á‚° äAi/ŋAW÷ž-WĄĸ›`*ŦĢ×ā\;öÄÚąĮjÕ=愓bPŠ+ōíģÛOs¯ŊuP—žOf›Ģ|íÍwšĀė׊\õ wkĸđ}5qÕĄGֈ[žÛBĐRWĮŽ­M›6áb]¤ŊüŨt,] u-[ļĖJ”(a‹/Ģ@t‘öē[ AHmwŪy§]{íĩábG]¤=‚î–BĐRÛŌĨK]Ģî÷ßV€< č"ítˇ‚.úÚˇoo×_}¸äAiī”'¸‰ĨÂЗiEÛ@Û@jûųįŸ]Ģî’%KÂ*K]¤ŊķzNļŅŗ–ÅŋL+ÚÚRß-ˇÜb7ŨtS¸äAi¯ãkŗ­÷ ã‚_Ļmƒģ_Ÿn)čĮt­ēúäAioÆ÷ŋŲy=§Øøy+ãÂ_Ļ}wmƒ¯ž_n)J-ējŲyGĐEFč1â[ģâéé6aūǏ˜îEßYßũґ߆›@ Ķ]ĩęjĖ.Č‚.2ÂĻŋūú;ė.°F='[ŸQ‹lÔĖåq0ŨŠžŖžĢžķŖ#¸m xŅė˚…ä AEŨ˜ī~mŽß{ŠģÕNŌK—qvĀ9ílŸƒjY•ŗo°“ī˙4~$}G}Wē+ŗî§ĢV]Ũ_äAH’yķæYíÚĩ]ųčŖŦ^Ŋzv '،3ÂU G×^{­Ũyįáb°] Áūúë/ëÕĢ—•*UĘũŠĮŪ€ŦtéŌÖŠS'Û°aCäYŊŋģVŨe˖…U ] ĸ­¸ú{v4ÁLãÆí°Ãŗ‰'†Õ§M›6ÖącĮp1ČAH€­ĩâædčĐĄVŽ\9k׎­]ģ6Ŧ€˜ Xɒ%mųōåaČA( Ü´âæD][ļliUĒTąQŖF…ÕĶēuk7ėü3‚.OųiÅÍɈ#ŦRĨJîDvŊa5ØüųķŨŋ7+WŽ Ģ@€  äCAZqs˛zõjkÛļ­•/_Ū† V€ĩjÕĘîģīžp1tūøã°@›>}ē•+WÎÖ­[VŅēČXÅĩ7'oŊõ–íˇß~vÍ5רĒUĢÂjiĒI“&îB؂ ‹Œ“­¸9QĀUĐUāUđūĻNj*T°õëׇUd,‚.2JēĩâæD]˜Õ•Y]šÕĩ@zkÔ¨‘õíÛ7\ @Æ"č"#¤s+nN49•&ŠŌdUš´ @úšŽNž˙SĢrö î˜ ë’Y8ŽP¨įƒf. 8 čĐĻŋū˛ĮF-´ķ{OągÍZn_,^ļeęßåŨ/ĩN¯Īļ&į™K¸')ĪWÚĮžķ}˜ĮÕ{3~ĩ{‡Íq˙–p\!-_žÜJ–,i ,ĢH9ŨzlÔģzЗ6aūǏ“×t/ĪOXb {|ÆI9ŽĪßĮÕUĪdîquÎŖŸŲŦ8ސz:vėhmÚ´ rē n•MzMąņķVƝŦfJŅIųy='ĶŨ ŖãĒ1ĮÕßۀ㠊G÷ž.Qĸ„-^ŧ8Ŧ Ĩt  ķđ9ÖgÔĸ¸“ÔL+^ųÆú^n _î6Ûz°0n?Ë´r×Ģߨ- 7PäîŧķNģöÚkÃŤ‚n¨ÕiôŦeq'¨™VŪũrŠ]Ô÷ķpķųĸW›ĢfO 7Pä–.]ęZuŋ˙ūû° €”AĐ-€Sœ`Sũw‚šiåķ…ŋšm$B]Ž+Wt\Ęq…Õž}{ģūúëÃŤ ‚nčļ áÉiĻm 8ŽļŽ+¤ĒŸūŲĩę.Y˛$Ŧ %t €ō-…r$ ĮՖÂq…TvË-ˇØM7Ũ. %t €ō-…r$ ĮՖÂq…Töã?ēV]ũ @Ē!č'ä[ 'äHŽĢ-…ã ŠN-ējŲ Õt €ō-…r$ ĮՖÂq…T§1ējÕ՘]R Aˇ rB~Ę ­Õĩ7Å-OvipŪ…vqĢĢã–´pBŽDÉīqõá ėØkÛŨŨúÄÕ%ŗŒ›õ“{ßöŽĢ+há¸Bq Ų—5 3Š„ [ų=!WŲkŸVë”úq˓]ĘīWɎ:ļfÜō‚Nȑ(ų=ŽŪ›8Ëūõ¯Ų˙Ŋ%Ž.™å“¯žwīË$d*ŨOW­ēēŋ.Š‚ [ų=!W!čŲËīqEĐŠÎĩ×^kwŪyg¸€"CĐ-€üžĢDƒîč/žĩ/ŋko~2=n=_>_¸Ę†ŽœāÖ{áOlʡ+âÖQųtæöĖëØĶ¯Ž´ą_/‰ĢŨ1Ķš×öŅÔ¸õōZ8!Gĸä÷¸ ƒî[ŸÎpû÷¨ĪįÅ­ëˤšKíųˇ?vëŊ:ęŗ¸z_>šļĐ­ķėŲÄ9ŋdŠ ƒîģžvëŽülvÜëäĩp\Ą¸XŧxąkÕ]ļlYX@‘ č@~OČUt?éģ ųėß˙ūˇ;QV9˛Æ q'æ7wėj{îĩwl=îŌs@–õūsŨÍļÃ;ÆÖŲf›mėė&ÍlÂėŸcë(č~ĖqÖüŠël›mˇ­{đáGÛ{fÆ}ÎÜNȑ(ų=Ž|Đ=ŋy+ĢYģ^–ãEcĶ?›ŋ,ļŽūŽ`=^Tt| 6*ļžŽ3ĪŊ Ë1ēÎ;ŲU7ŪnSũæÖņAˇáų—XŨĶĪĘōz§žyN–ã/¯…ã ÅI›6mŦcĮŽábŠAˇō{BŽĸ Ģáj‡n=Ÿ~ŲĩĒĒ%JËNũz÷?úÄ˙ŸD_ėZœŪ˙ĩõ8Ôö-ŋŸ;IWK“ÖëŪ˙9ˇ^ëļˇē–ŦˇĮ}傯–]uCûØëéD^Ë*W=Čé?ƏųÂŽģõnˇLd…Ÿ3ˇ…r$J~+tUNŽwĻ=ũĘúūDĢß°‰[Ļ F~ŨK[˙×-ģöæģ\O /ē?n;íŧ‹íˇ•Øz:žtA¨ķ˙úģcOĮéég7vĪ}ä‰įŨ:>čĒ_ĢŽ=ņŌÛöĘ“ėœ šģe×´ë÷Ys[8ŽPœ,X°ĀJ–,i˗/Ģ(tŨČī šŠ‚îŽ;íėē-G—W=čbũãmnpaxōˇËŗŦwË=ē“huQÖã–Wßčŋ?yN–õÔr{ī#ũbtˇŨn; ĸ멕w=÷ʲ,/…r$J~+t÷? Z–ŽũãŋųŲļß~‡,r4l@5| ĩĘę54“˛Ģ×EÉŌûÆZoU4$@=1üš{ėƒŽŽÛIķ~­§Vã]wÛŨ…ßđ}r[8ŽPÜ´nŨÚ:uę. Đt ŋ'ä* ē —áru}Üm÷=â–ûōņŒī\¸=ĢņEîäú‰ßrËė3Đ=Žr`uģņÎû]KVø\Ũǎ\];ÕÕ9\žÛ 9%ŋĮ•ēļ¸2ŽŽLŲōî@ár_FMī‚ĢƯë5Æ|šØ-×-Āôø˜Nrˇ-RĢnøÜ-]—/ŽĢSĪ —įļp\Ą¸™?ž•*UĘVŽ\VP¨ēßr7UŨĶã–ĢÕI­@ūą&ĄęØ­ˇs|­,ãt÷.Q2KĐUQÉ]wÛ-ļNŠ2e]‹ŽīŪŦĸ ĢqĀáûĒģ´Æ!†Ës[8!Gĸä÷¸ōAW]öÃ:ĩļÖ¨yrėąZyÕeŋúĄGÚÎģė➧.ĘūĶDmZOãk]xŠköĮÕūh7ÜŅ9ÖzëƒnŗËÛÄŊ¯.<|ØQqËs[8ŽPĩjÕĘîģīžp1…Š [ų=!WÉéöBaĐՄ9:‰Vk”ÆöôĒk}ēëÁ^ny˙ŪĖō|Í"Ģ–)AŦXųˇÎA‡ëz™Ķí…ēHų=ŽÂY—Ŗ%tu,č‘z0œqÎųv{įîöäK3°Ļ—^‘%čúĸ0Ģ^/néē2ks›^ĢĶãėn/DĐE&š;wŽkÕ]ĩjUX@Ą!č@~OČUrtÕę´ŨvÛgL¯ŧū6wrŨīų7ÜãžĪ sá7\OŗžjŊˇÆ~ét‘ęō{\å6č>;üÊ&ąRŨ‡_,pnî÷l–ut1Iã€ũqJĐâĩhŅÂēví. Đt ŋ'ä*š ē:Vø ģkœā>%Jš“ë^Īŧ▩{ĨZ¨^xwl–uëœÖĀuģô÷Ô%č"Õå÷¸ĘmĐÕ=nŗëjüÔĐ÷ÜDmĒķ÷ĀÕDp:Ö|đUQ—å •*ģ°ĢĮ] ŪėŲŗ­téŌļzõę° €BAĐ-€üžĢä&čǜvV#w]ģŪ™n Ž&ÚŅdUš VËīėŌí§ÛížĮžŽŽÉ%—ģ[ ŠģŗÖšēŨą×#č"Õå÷¸ĘmĐÕlČ Š“Ģ D:Ž´˙ëÖBštJ¯1xØhˇnŸA¯ēđĢÉŦ4@“SxđaîĸŌ˙ž|Á­Cв×ŧysëÖ­[¸€BAĐ-€üžĢ褸æģˆ[ŪžķÃYf8į7iŽnQrDãŨĖŽjyR÷ÉÆÍZØí÷=[W÷ĪÕɸŽftÖíStŪčëë6DmÛwŠ{_MxĨ“ūpyn 'äH”üWš)Yû°î)Öé6]š@Ę?Ö8wMÔvôq'ēņēĩŧĘ^˙pŠģ§Ž^C÷Ēöë>ûÆGn™B°zWčīĪŊ9&Vī'ŦęŌs@Üûę>ŧŅ My-W(ÎfΜieʔą5kքU$Aˇō{BžŽ…r$ ĮՖÂq…âŽYŗfÖŊ{÷p1IGĐ-NȡNȑ(W[ ĮŠģ3fXŲ˛emíÚĩaIEĐ-NȡNȑ(W[ ĮŌAĶĻM­Gáb’Š [œo)œ#Q8ŽļŽ+¤ƒéͧ[šrålŨēuaICĐ-€Sœ`SũwršiEÛ îßÛHíKW›+ũ¤ƒ&MšX¯^ŊÂÅ$ Aˇ÷šlŖg-‹;AÍ´ĸmp^ĪÉáæōEûĮÕæãĒq¯)áæŠĨŠS§Z… lũúõaIAĐ-€NÃf[īƝ fZŅ6¸ûõŲáæōåŽ×žá¸Zŧų¸ēwøœpķÅVŖFŦoßžáb’‚ [_/YíZ\ÆĪ[w’š)EßũŧžSÜļaÆ÷ĢŨ>•éĮ•ūmá¸B:™˙Û¤›† Z˙ūũÃÅ$Aˇ€6ũõ—ëbx^¯ÉÖgÔ"5sy܉kē}G}W}į^ī/pÛH$íS=˙Ūˇ4^7ĶŽ+ũīķ÷ŋ)WHG“&M˛J•*Ų† Â*Š › ębØyø\ģ Īįî– …UŽ¸ĒŸísP-;ōę'âę’Uô;ŸCˇJ$öą{‡Í)ÔãęČ̟tĮÔWöĢKfŅwŧīīC8ސî4h` PŨblܸqVētiëŪŊģ•)SÆÍj ˙žüōKÛwß}Ũ1Ĩck˘1á* hüøņVšreÛ¸qcX@Ât‹Š‰'ēņ>øĀ=>|¸•-[ÖfΜŦ 7æĪŸīn2tčP÷X!—° $Gũúõmāáb† [ iæJĩāž÷Ū{Y–2ÄöÛo?ûö[&‡ōâ‡~°*UĒØ“O>™e9aHŽącĮZÕĒUiÕ$ Aˇ˜Q÷du­|ûíˇÃ*į‰'žp'ėK–, Ģdcųōåvøá‡[ˇnŨÂ*‡° $GŊzõlđāÁáb‚ [ŒL›6ͅÜ7Ūx#ŦĘBã >ø`[ētiX b͚5VŗfMkßž}X•aH<OÕĒUŗ?˙ü3Ŧ ĀēÅČ3ÜÜ×^{-ŦĘÖ=÷ÜcĮsŒ­\š2Ŧđˇ?ū¯Ŋ퀎ĸ8û°ą$&&1ą!EE{Ecą`%vQQP#ŦDƒ‰‘bĨÚP‚…¨($‚@bb%ŠŸ%Ŗ`TĸĖwŸ!s˛wNŊ§îÜũ?ķ{÷ž™={ÎŲwvfūĶö̝LĮŽM׎]ũ¤ŒHė Q~<đ@ģėF!„(7ēĀS-Z´0ãĮ÷“rŌĢW/ŗīžûšĪ?˙ÜO"Ņ|ķÍ7ĻsįÎÖøŋP$v…(/Ī=÷œŲvÛmu !„… ĄsŪxã ͞eK3zôh?)/ĢV­2įž{ŽŨŨōË/ŋô“…H,Œâ2šË¨nc‘Øĸŧėˇß~fėØą~´BQē1fŅĸEvåR6렗üä“O6:uŌ:(!ęa=.ërYŸ[,ģB”iĶĻ™íˇßŪvÎ !„åBB7ĻđLĪÖ­[›‘#GúIfåʕæčŖ6]ētŅô0‘hØY™–ŲišT$v…(t>šgX !„å@B7†,]ēÔląÅöQAåâ‹/ž°rčŪŊģŸ$D"āš˛ģL8ĐOĒ .´Ŗ^SĻLņ“„–°Ąģ,× ‰]!Ї ŠØ˜J!„( ŨÁĻPģîēĢ0`€ŸTuæĖ™cš5kfĻOŸî' ÜW<'ˇšË˛!ą+Dq0ĒK'0B!ŠEBˇ|ōÉ'f=ö0}ûöõ“jÆ /ŧ`åŗfÍō“„‚+VØĮgÅ龒Øĸ8XvĀũ,„B‹„n•ųôĶOÍ^{íez÷îí'՜ŠS§ÚiĖķįĪ÷“„ˆ5_}õ•éØąŖéÚĩ̟Ts$v…h<Œę˛E57“BŅ´Đ­"˗/7ûėŗéŲŗ§ŸyäĶĸE ķÆoøIBĒožųÆtîÜŲ˙Į‰]!ĪØącÍ~ûíįG !„!Ą[%˜Vš˙ūû›=zøIąãžûî3­[ˇ6K—.õ“„ˆŒâ2šË¨nœ‘ØĸqĐqĩíļۚįž{ÎOB!ō"Ą[>˙üssĐA™nŨēŲéX!pįwšļmۚ÷Ū{ĪO"6°—u|t$…€ÄŽãĄ‡2x -„BäEBˇÂüûß˙6‡z¨9ûėŗc;­27Ũt“Ųa‡ėc„ˆėŦĖËė´ģBÎ×_mÚĩk§ûE!DŖ‘Đ­ _~ųĨ9üđÃÍ駟œČuôīßßėšįžæŗĪ>ķ“„¨<#—gåōĖÜ‘Øĸpîŋ˙~sđÁûŅB!DN$t+ë<ōHsꊧÚ鐚đ Í`G§…¨5ųä?)ņ˚5Ë6Č_xá?Iˆŧpoą.7ÉK˛!ą+D:ˇß~ģũ!„BdBBˇ 0ĀėēëŽv7X‘™éͧ›f͚iGLŅ(ØY™–uoeGbWˆ†° dķæÍÍ… ũ$!„BBˇPhvÚi'ûX‘›)SĻØiĖj|ˆBāš<+—gæŠÜHė ҐÁƒ›“O>ŲB!$t aĐ Afûíˇ7~øĄŸ$˛0nÜ8ģ+æâŋũ$!RL˜0Áæ“%K–øI" ģBü–:0‹čõ×_÷“„B$ Ũ<0Ĩ˛}ûöæũ÷ß÷“DFŽiÚ´icŪ~ûm?I3mÚ4;ōŋ`Á?IäAbWˆ˙A=}Úi§ųŅB!Ž„nnģí6ĶŽ];MŠ,!C†˜mļŲÆ|đÁ~’H03gδBŋĸ8$v…X ųã^āQwB!„CB7 <Ŗ¯mÛļfŲ˛e~’h$×]wŲyįĩҐ°0‚ËH.#ēĸ4$v…XÍ 7Ü`N?ũt?Z!D‚‘ĐÍĀĐĄCíæ8šr[>úôécCĪģH.ŦÅeM.ksEyØÂØįÚoŧņÆfŅĸE~’Bˆ„"Ąë1lØ0ŗÅ[˜ĨK—úIĸDēwīn>ø`ķÅ_øI"°€$vYåEbWˆÕŗ‡Î:ë,?Z!DB‘ĐĀæI­[ˇÖ°â›ož1]ēt1G}´YšrĨŸ,š0L[į9šl#*ƒÄŽH:˙ú×ŋė¨Žęp!„ Ąû_îŋ˙~ŗŲf›iÚS…ųúë¯M§NėsžĸéÃã?˜ļŪˇo_?I”‰]‘txæũšįžëG !„H ēõŒ=Ú´lŲR;6V‰/ŋüŌvØaļ1˛jÕ*?Y4!žúę+ĶącGĶĩkW?IT‰]‘d˜=˛ŅF™ˇŪzËOB‘0/tĮoZ´hĄ‡ÍW™Ī?˙ÜėģīžĻW¯^~’h"0bßšsgkŊ¯.ģ"É\uÕUæŧķÎķŖ…B$ŒD ŨG}Ô4oŪÜ,\¸ĐOU€õTģíļ›šúęĢũ$Ņ`—Ņ\FuEõ‘ØIåã?6n¸Ąžœ „ 'ąB÷‰'ž°Īō|íĩ×ü$QEūņ˜öíۛÁƒûI"`XËē\ÖįŠÚ!ą+’J˙ūũM=üh!„ "‘Bwʔ)VäΝ;×O5āŨwßĩáŅN"|ØY™–Y+'jÄŽH"tĸ2ĒģlŲ2?I!DBHœĐ:uĒiÖŦ™™3gŽŸ$jțožiwŊ~衇ü$<#—N ž™+âƒÄŽH"Ė,éŲŗ§-„"!$Jč>ķĖ3ļą7kÖ,?IÄ6cÍôĉũ$&L0­ZĩŌ3,cŠÄŽH|đÕe֐Bˆä‘Ą;}útÛČ{ųå—ũ$#˜NΈ;"ĻM›f—,X°ĀO1BbW$Ë.ģLģû !DB)X莘5Ë,9å3ŋU+ķĮ5ÖÎ.Ўáâãl 6ßÜ,éÜŲ|RÄ'ūzķÔSƒôרzģ8C|ėmÍ5Í­ļ2Ëúõ3_ō‰ī’ŧ,ųeŗäÄÍü–-ĶĪsÃ_øÍĩũ×_īôéS”ŋV˜i–œtRpūēg°ī¯w.ŋŧ(‰Ōĩ|šļFāųŊČōI!DBwÕ×_›w.ŊÔĖoŪÜ,ģâ ķĪI“ĖŠ?ūQVûäąĮĖ˛ĢŽ2Ún;ķįw4_üųĪž{Ō°ūēė23ŋE ŗŦų̚6gŽųxėXŗ¸sg{ũél(|ö÷=ĖüfÍtUĶĸūĒ/ߖ×ÕųŽÉūzûĸ‹ĖüM7•ŋĒiQÕ_ûå3føŽĀ–O\ ü^mķ˧™3}×!„ČC^ĄûNīŪæ/:˜Īž>Ŋ –UÍŪŊæķÚÆ›/ūúWßE  Sâ/{í%ÕØŪŋã3oà ͊W^ņ]”Æßģw7¯īžģ|VCŗūÚ`ƒ‚:'hôË_ĩ5į/FEeųûųį+ŋרRųŊ€ōI!Ä˙Č)tiô1Méŗ?ü!­ā•Uߖõíkuęäģ)ūZĐĒ•üŖq˛ E‹œĶÎ>{á;R"ŸÕŪđ#'šüŨŠüŗūĒ÷E.‰ŌølÆ å÷˜X!å“Bˆ†äēovéb–]yeZ+Ģ}úĖ3æĩM6ņŨ”Ŋ{õÕiī“ÕƜzĒŊ‡˛ąøØcíš^˙}˛ÚksúĢsgų+Fæü!*ÃâãŽS~‘-Ļ|ęßßw“Bˆ,äē Zļ4ŸLžœVØĘjds昚ëŽëģ)ŖšŸL™’ū>YÍėã Ė­ˇö]•‚ušēĮâcoļmëģ)Ŗ[ōW| -ØrKßMĸL(ŋĮËlų´ÕVž›„Bd!§Đ}uíĩÍōW^I+leĩ3vcˆü?[>{ļyuu|WĨxu­ĩäŗ™ü–åķ—( å÷x™ōģB4ŽėĒŠD•_ĐĘjkš„ŽüO“ĪÂ2ų+,Ëå/QĘīņ3åw!„(œœ%Ļ*šøYŽJNūЧÉga™ü–åō—( å÷ø™ōģBNÎS•\ü,W%'ÅĶäŗ°Lū ËrųK”†ō{üLų]! 'g‰ŠJ.~–Ģ’“ŋâiōYX&…ešü%JCų=~Ļü.„…“ŗÄ,ļ’ûë“Oša×\cūxņE{^=:-­ÜöĝwZa Ÿx"-së­æ‘!CŌâ+mnŋŨŒũÕ¯ŌâKĩJøläõכ§îž;-žÜFžĐŊģõ×ô{īMKĮø#Žģ.-žŌöˈ6ßúņĨZ%üõčwØ|íĮ—ÛūT_æ^{ŅEÖ_ãëķŗŸŽÍ3Æ^7î{?­’6gÂûšËĻOOK+ÅrųK”Fąųũ›n˛eŊ_n{eüxķË=l~ĪV×ērbųœ9ii•´?ÜwŸũÜÖÕĨĨ•bĘīBQ89KĖb+š‡ũkŗFũ{ŧųæ´´rZˇO´Ÿƒ}ë[ß2īŊđBÚ1ΜiĶĪ:öØ´´JÛ:k¯mNčØ1-žËUÉë/Į\Ŗ+ēvMK+§ éß?å/,[įÃV›onöÜa‡´øJÛ.ÛnkļŨrË´øR­>[ī{ß3??č ´ør Įĩ×Z+å¯ßdyĻö1lžˇîēiņ•ļ.GmŋY~Z)V íĩĶNfËÍ6K‹/§ũeĘķÃī?å¯ķO>9í Q@zļŽĻJŲ —\b?ˇŽ^hûiĨX.‰Ō(6ŋ7ßxcsP‡iņå´xĀ|{uRųũÆ^ŊŌŽÁ~ņķŸÛt:Æũ´J÷Ÿ›ŠžS~BˆÂÉYb[ÉUKčūč‡?´Âäũ#ēų­ZBwßŨv3߯hŒøûiQ“ĐÍoÕēŽ#‰?-jēų­B×u$ šâŠ´´¨IčŠB)6ŋWCč^vÖY6?1[ÂO‹š„ŽB$—œ%fą•\ĩ„îZkŽiN9ōČ´ø¨I迎j ŨÛĩŗæĮû&Ą›ßĒ!tšˇŅÍ7åOB7ŋUCč^×ŗ§Ŋœ0!--jēĸPŠÍīÕē];wļųéƒ3ŌŌĸ&Ą+„É%g‰Yl%—Mč22„đkˇÅVT\ü‹_˜ŋ?ûlÚûg<ø 9ŗS'ŗkûöĻu‹f˙Ũw7/¸Āüãå—múÄßūÖV^LWĻņČ˙ŖŗŦ‹ ]ŪwÄ~ûŲsvÜgŸŒë1_}øa;’ĩĮöÛÛãöŪyg+˙īųį÷ėȑöˇđų;lŊĩ9ĩ^đŊŖĮ8ĄËÆŖ<ОīŊ÷6÷ŨxcÚįjš*šbũ•MčÎ{ôQë~ßv[me˙Ÿ˙ØcßßŗKۘį7rí.9ũtķÖĶOÛtÖæá#Fā7X}û˙āË/O;3'tYĘõã5ŖÁŋî×/Mt-ūũīÍågŸmĶųėŨļÛÎt?é$;3zß›ī%˙!Îü‘'t 'q„i[˙šø˙æK/-Š‘T Ÿeē\oŽ;÷ÍÖ­[›ã;,ãôđwĻOˇëniˆrÍvŪf{āoŌÉëø¨M˖fÍ5×´˙÷:㌴ķ8sB—‘zÎÃ5ÜŊŪ*wĪ:cöÅõ_lũÉOĖõŸíî}8åŋ…Ž‘6­ZŲûöŽĢ¯n jĐũÛSOŲF/y”ßÎHO)ëO+á¯LB—:ëi˛Ë.öZül˙ũ͘ÁƒĶŪKÆũrøžûÚs´¯ŋžÜŅ‘vō<ŋëqĖOj}ÆûüsaNčrÍ/=ķLŗĶ; (÷Ü=ëė_ŗg›ĄŋüĨ9ō€lžr÷eiô¸ę?ëš /4vÜŅæ)ō~Žúß ]ōd˙nŨlžÃ˙įŧY4ujÚ÷,ÔrųK”Fąų=“ĐĨÜésÎ9ļl LīTŸ2í3@ųC^ĸŽ$/‘7š×YK:ųšüM~$?Q~˙ũķ8sB—r‚<žĢœ ŋRŪĶ> ÜáXęÖÛFCŧō[ČÃÜģĪLŠh=á„î‰Í§žjļoÛÖÖ1ԕ~{ĸ1Ļü.„…“ŗÄ,ļ’Ë$tīŧę*ÛhĻ‘FAÁđŲlĶMÍõPôŊŒ"ŅXĻÖīÜsm#‘ķwčĄö˜b„nËM61ë~ûÛæØC1žvš­ˆ‰č–[RĮ"^ŋûīØ> {cîš§=nŋz!=Žīˆ¨â˜ŪõĮļ¨?˙węĪ˙¤ŽCčō9|.ĸäĸúĪŨŧys{žģLûŽ…XŽJŽXeēS‡ 3ë}÷ģöû#8đߝŠĮŅJËē@ŽŖ§(á|øaRŒĐŨđG?˛âé°ú׍sqNÎīŽCX5ÛpC{N¤á_Fúņ÷‡/ŊdŖÃgĶpĄqCžĸÃųĸ›(ŅY˙?°"ōāŊö˛Ÿ‹ā8~›˙= ĩJøĖēŒÎáƒŦˇžmĖņųî\‹QƒĨŽc# ō7~ėz öš1rËZ7ŽŅŌgž)Jč’×ņ÷*× ņÆu#͇ĸŅĘũpÆ1ĮØĪæ/ųŒûŽ!ĮqĪnĶĻųqũ÷Ą3ŒãčČā|äCw>'tņ5÷"ŸK§qøĪ˙ž…Z%üå ]?dŽ u~#€īŪˇž|tĮq˙PöPۜüŗŸŲãđų”8Ö)r\1B—ë†pĨ'tų\Ę=>ņNŸãĪBMų]! 'g‰Yl%į ]*9AT.Ņ<ū¸­Dh˜š8*šV͚Ĩ­ģŨg×]mÃî“YŗRqT’4vũĪšēTTŅ]cŠ0‰Ŗ'ÖÅŅ ¤ōōG5hŒqŽ%ĶĻŲ×Tœ4Öi¸ģc‹ˆ „”‹ãüÄ=ųģßĨâ87•8B ú…ZŽJŽXųB—kŒh|FGÜéÅf”“†YcԐßéOƒtĶĘf›Šcô1éžo]ŪŨQ—F0߉Fƒ‹ŖqCcÚ5öš†üäģî˛¯Ų¤„׎Ą‚Ņ ÚtŖ4Äųnw[ßžŠ8ō+ŋaũŒÆX%|æ ]ZÄE§­ŌpŖa…|ûšįlÜUįŸoŖ?šÍh9ņėæėâV`˙ŗ}s÷ĸ4ĪũŽÜ.ģ4h9ÎīäĄŗ‰xFRxÍn­ŧfGuw #ųnäĶå='ti8GĪįD[žĩāŲŦūō….^Žŋģ8 gâ]‡ŗNø-ŒŽF{zøp-oÜÔe72ŸÍÜũAŪŠZ×0įÜŧfä5“_9?ņtRđšŅX^3ŗ"z~Ą|w™NčŌyáŧøĮ"š1–Ë_ĸ4ŠÍīžĐĨŖ„6@tÖ÷1eeŒË#ˇ^v™Í ÷{ŗžč$žuč.ÎÕ1Ņz8“9Ą‹hÎĸs†x7û‡ō†×ŋęͧÁû]yÄ,^ģûâos>:eéėsyÛŨO”ŖŅ™(ŧ&>߃lĻü.„…“ŗÄ,ļ’ķ…. X^GŸ3KX7…ˆŅ?ĻûĮŅģË9ĸë]#tũiTŖzL'r¯_{ėąŒëĮæ¤ķÚmŌC…čO}Аiŧ~ŧ›ŪįĮbš*šbũå ]"¯3ē:_>˙ũö5ō…&Æ{9Î5šąÆŨM6Ø -žsĮŽÖįŽąBJĻŠšL įŗŨ(ŋÛ¤áwbDīÆH§?MųėãŽŗīĪåë\V ŸE….0ˆŖLëН/™öËkFl3=†ƒ)„ŨȨąB×ŋļ,7 ŪM9dę"ų!ÚY…qĪsœ­gJ.¯iˆ’7ũĪs愎ßÉâō^ĻŧQˆUÂ_žĐeT”rĀ?_ōŨeå5åŋÃ_‡ˆ9.:‚ÔXĄë—Į<Žˆxˇ´‚ÎFfoøk éüA´ ^xM' zÜ?/=ôPÚį9sBwÜmˇ5ˆwyŊ§Ëå/QÅæ÷¨Đ%‘?2íWA=‚īoųo'yiZ}ųä—ÁÔ1GwqētŦGãŠŋŖårĖÖōg „9ÎÍęĄķ–×t.ē6A&sB—ĮyEãéč#žØĮ/)ŋ !Dáä,1‹­ä|ĄëD*ë.™Z5iŅ5zŒÄ0„0at†‘<ŽÁh,ģã#t3Įô#FŖq44ipQŠ!$ÕtŸ=÷‘Gė1T˜ô܇˜eZ%zŋQNšk FßÍoōã ą\•\ąūō…ŽëU§#Ā÷—›B<<2åŸ0úĘôV~/Ķ!Ũ5‹ŠĒÆ]|ãĮģK´D#ŠŠšLiet™Ž ÷ŲnZ:bꘋįÜt^øržŖˇūį2rĖûüĄV ŸE….‚…īĮh§ī/î9ŌĸSiŌ Ā5 ķĀMqæ8î;w\c„nĻãnęŨ۞3ÚĐC$Q> ļ8?žpa:#ĮБáF[0|r^Ŋ|a愎˙œJ7rídiŒUÂ_QĄK‡ßąëû Ŗ\‹Î4!ŋŗ\ƒg…3Ĩ’Î3ĻrrĻ ģã+tũFēšōŸ‰Œe-1kÜųĖā¸č´t7*‹ąŒߐĮĸžqB×ßËĀEGëcšü%JŖØüēN¤˛LÉĪënYR4SgĶéB}ÄúXĘeęRŽ#ēã+tũŠüÔķÄ#Ŧ]3˜YĀĖĻD3ŊšŲ[Į, wßËåwÚĖ^āŽÜ:ĄëīCâ:b5ƒA!*OÎŗØJÎēŦä5 [*Læ69ĸ—•J CœĐGx! 8GąB7Ķh—/tL4 9/"uf4ÎuæNčbŦc¤aÆt%Ļ"“ÎČttDĖmFå.|œ….Ķ$yÍæ7žŸœšMD¨Ŧ™žČņ\KĻĄ#úé¤ ŽXĄ›i×e_čŌ@ŪčĮ?^ŨØhÕƊ#ņüâĸë¯ipã_Ä0kQIĮĸĪÍļër܅.͐ų~4}?9sŒžēŽ#žL_Eđ:RŦĐÍ´ë˛/t_Ÿ<9ĩŽŽŽ"ÄÜ S't‘ˇ¸÷˜ŽîüÅTl7"œm×å¸ ]:ōø~Ü/žŸœš=d7ĨžuëŦ)d´×Mŗ,Ečú#ážĐeeqŦ—fi÷ –|ēŖŋ|7įcŒû‰ŅgŌŗíē,Ą_ŠÍīQĄëfh°>ÖĪįÎÜ:]:‘)Ë9žę :1oīÛׯ•"tũQb_čŌÉMEP”5ˆ[ĘNâĸB›r×]vļÛwc‰•Îļ버ŽBTœ%fą•œ/tiĖōšFļltJS„YbJŸ[SčĖ­k‰Æ—Sč"\iŦķÚß” ŽsD§TG+/DbŅÅÔW×đUčŪsíĩöuĻŨĄŖūBpĐ @ˆ¸„œŅ á•ē4¨­ŅuĀĶtŖųķ4ŽŨ†TŽÁĒĐe6ß/牑3|Ucö#LŅ‹ĮZQÎ]Ÿ\nĄKĮ÷Ŧŋ.•õ¨Ņü‡1 ]OĮz6:•8ÎM} UčŌĻSŒŽ˙8,zšeūēf: ˆŽO.ˇĐuĮ ēøâžāûŨ[¸čˆbžīÆqŋėŅÃÆIč†Gąų=*tŠđo´SŅeyT¨ŌņF`ŌĐĄ ŽCŒrvcvqåē§˙ˇCÛ¯÷ܚt:o]3‰ĸįŖŖžō˜ã\§ĸ„ŽBԞœ%fą•œ/tŠ´üŠŖĘÔ É4îØÅ“ã˜ĸ=ŽJ…ƒH{3˛°œB—iŦĮN Ņc n*ŽÛĖˆŠžô:û' ?*i',Bē4RiˆĶPņįãÄ⒠Døß˙ŧĮMIŖ×ÛŗSčrųŸ üãx”inˇaĻ !Øĸŗ0F9Î ąP….Ķí!e€ÛiڙîZ°ŠĶĪũķšuČn­VnĄËd:ƒüãčį63r3 ü5ļ”'Äģ†d¨BŖ  ÃÁ=Žé|wˇÉ#D”)ŅMŖ0ˇžéÄ.ŽÜB×Íĸq›đ9cŖ âYVÂkėŧöר2†xvĨįĩ„nx›ßũͨ˜ŪKå—Ąî^užg–IĻē‘ Ņ8Îm…•[č2âĖ=é×yŽĮgĶ€æ÷ŲŽņä:P¸î._1­™Š îHĒĐÅxÖ)ߑįūŌpCd0åŸû†‘oפņÉ}CcŒëĀŊHƒĖŨ_nC(ŦÜB÷ÄÃˇ¯áãŗdˆ\– P60zÉqŒŅāä{ĶäXîI|ƒPwkŪBē”-t&‘ץFt"0éā7ē`Ũ}ČŦîSĘ-כ÷3JîÎYnĄËč¯š§ø\>Ÿf­/˜ĀqtŽ0͔Į|ąk7ūb3e'"ŨmV'ĄÅæw_čēN*Ä$÷2õ8ū&3õ×=eÎbŽ#¯(Įč(qkŌŖ;€—[čē´GølFiétw˙č0å8f|QūŌąčę=f.ņ¸0Ö¯ģ¤%t…ĸöä,1‹­ä|Ą‹1b˨‘kPcT ˆĢhC•† ŊÔ1õ ΘÁƒW7†"k`Ë)t1Ļ#tŨgĶØĻ—gĘōډž/ĸĪUžĸaĀ”Kwž….ŋ‘Ūsˇũ\īčîÈzáŖĮ°a#äQņ‚•[č"ž%tŸMŖšQ(Äų,*v4ÄŨąģfŌHqĮ„,t16s·vÆtßčsĒ74ČĸĮ°V–ių\FŨąåēĖÆpĪ×u†Xbęĩ{l•{/÷ĸÛ¨ÎųĮíö…,t1'ŪŖŋŅßš˜ō˙DĄÜâˇq-Æn´ˇÜBá:'œņ}ņ >§üsåâÕĪW” ”ÛîüēáQl~÷….ÆL€čzVŒ9Ņe/ė¤ĪŌĨč1<ŽŠŲTä'fSšcË-t™ņCįLôŗŠ‡x,!%:ŽŦĄŪ=Īm‰čÆVēBQ{r–˜ÅVršŒŪ7Âz]DgÄSą!XüU+m4ų\žŖ˙˜ߨ`9Áå?÷ˇR–Ģ’Ģ„ŋXãIī;b$ÛodĞÆ5 ØbÁSŠŅPB°ÎÚO‹FĻaō׀WŌĒí3Ä;‚ĐŸĒ5ϧ3úîžq[M[üûßÛ?7ŌLÆ÷¤ĄÉ{ü´JYĩũÅoã7úS„ŖÆNÍ\ŗčŌj÷ Ÿíī]Éø~ü7"] Ëå/Q•ČīîžÎu˙“×ČsÕ,§‘‡ųl˙qi™ aÎo)äŪ(—)ŋ !Dáä,1+QÉÉJŗ\•œüO“ĪÂ2ų+,Ëå/QĘīņ3åw!„(œœ%Ļ*šøYŽJNūЧÉga™ü–åō—( å÷ø™ōģBNÎS•\ü,W%'ÅĶäŗ°Lū ËrųK”†ō{üLų]! 'g‰ųęÚk›åŪ˛Úžxu­ĩ|7ĨŋâgÖgõ~Éū”ĪâcōWX–Ī_ĸ4”ßãeĘīBŅ8r Ũ-[šOŧį<Ęjgøb~ŗfž›R,hÕJūŠ™áü’ ü)ŸÅĮŦŋęËŊlĖßtSų+FfũÕĸ…ī&Q&”ßãeųĘ'!„ É)tß<í4ŗ,ō8YmmYŋ~fq§Nž›RŧŲĨ‹y÷ĒĢŌŪ'Ģáü’ ü‰_ũ÷Éjc”w”{ŲX|üņōWŒ _,9ųdßMĸL,>öXå÷YžōI!DCr Ũŗf™ų-Z˜ĪŠ|~¨Ŧ|†č]˙ėŲg}7Ĩ°ūjŲRūЉYŸÕûŋdãŗįž[íWųŦæfũU_Ūåō×ō3䯘˜õWķæ9ũ%J#Uī(ŋ×Ü )Ÿ„B4$§Đ…wzõ2¯īą‡ų,Ī3Je•3Žũëģīn–ž{Žīž4ŪšôRķ—ä¯×?ŧĶģˇīĸ4đ+ū•ĪjgöĢ/į(īōņ÷îŨ寛ķ×Û={úîefiˇnĘī5ļTųtÉ%ž{„Bä ¯Đ]õõ׿íú•^]ĻÍüsŌ¤´BXVjáoŊâIDATãZsÍYĮIccÕʕž{ŌĀ_ˆ+F:ä¯ę[Ęgõ×?ā|ŦúĪŦņŗ|V]KųĢž|Ŗœ+Č_õĮüũüķå¯XՋÜBü%JCåSíŦA~ŋøbåw!„h$y…ŽcE]Yrâ‰v*&Ûۇfįeˆ‹ģ1MiŅqĮŲ)K…éMKN:IūǞqŊšîÅL/ÃĪøŋûįģí¯úrō­ą|öâ‹vÍŽüU=Ã_‹;w6ËgÎôŨ!* ŖŠ‹U>UÕlųTŸßW(ŋ !DQ,tCg5ķS›ōWXČ_a!‰$Ąü.„É$1Ĩŋ*簐ŋÂBū ųK$ åw!„H&‰)ũUŅ……üōWXČ_"I(ŋ !D2IL鯊.,䯰ŋÂBūIBų]!’IbJUta!……üō—HĘīB‘LSúĢĸ ų+,䯰ŋD’P~Bˆd’˜Ō_]XČ_a!……ü%’„ōģB$“Ä”ūĒčÂBū ų+,ä/‘$”ß…"™$ĻôWEōWXČ_a!‰$Ąü.„É$1Ĩŋ*簐ŋÂBū ųK$ åw!„H&‰)ũUŅ……üōWXČ_"I(ŋ !D2IL鯊.,䯰ŋÂBūIBų]!’IbJUta!……üō—HĘīB‘LSúĢĸ ų+,䯰ŋD’P~Bˆd’˜Ō_]XČ_a!……ü%’„ōģB$“Ä”ūĒčÂBū ų+,ä/‘$”ß…"™$ĻôWEōWXČ_a!‰$Ąü.„É$1Ĩŋ*簐ŋÂBū ųK$ åw!„H&‰)ũUŅ……üōWXČ_"I(ŋ !D2IL鯊.,䯰ŋÂBūIBų]!’IbJUta!……üō—HĘīB‘LSúĢĸ ų+,䯰ŋD’P~Bˆd’˜Ō_]XČ_a!……ü%’„ōģB$“Ä”ūĒčÂBū ų+,ä/‘$”ß…"™$ĻôWEōWXČ_a!‰$Ąü.„É$1Ĩŋ*簐ŋÂBū ųK$ åw!„H&‰)ũUŅ……üōWXČ_"I(ŋ !D2IL鯊.,䯰ŋÂBūIBų]!’IbJUta!……üō—HĘīB‘LSúĢĸ ų+,䯰ŋD’P~Bˆd’˜Ō_]XČ_a!……ü%’„ōģB$“Ä”ūĒčÂBū ų+,ä/‘$”ß…"™$ĻôWEōWXČ_a!‰$Ąü.„É$1Ĩŋ*簐ŋÂBū ųK$ åw!„H&‰)ũUŅ……üōWXČ_"I(ŋ !D2IL鯊.,䯰ŋÂBūIBų]!’IbJUta!……üō—HĘīB‘LSúĢĸ ų+,䯰ŋD’P~Bˆd’˜Ō_]XČ_a!……ü%’„ōģB$“Ä”ūĒčÂBū ų+,ä/‘$”ß…"™$ĻôWEōWXČ_a!‰$Ąü.„É$1Ĩŋ*簐ŋÂBū ųK$ åw!„H&‰)ũUŅ……üōWXČ_"I(ŋ !D2IL鯊.,䯰ŋÂBūIBų]!’IŪŌÉG1w×Ũhޘ|–š`\§`mĪÎÛĻŅdŽ?Ö ˜|ŽöŌMæŊOßņŨ”â͏ūjîŠģ9x…n…ú뭏Ū0Ãën1ũ'ŸvYõ ]9ųs÷K7æõ׈ē[ƒ÷Wčå!ÖsÂņæš'{˜Ņ¯ 5­xßw•(*Ÿâa…–OB!’Uč~ŗę3fŪīLßIg˜1‹†›WL7ķjfŦxŪLøÛŊĻīÄ3Ė˙}úvšŋÆÍ&Å(ā¯q™Õ_æ 7ũ&)Å$ā¯1žÕ_Ė)Å(Ô}ų˛yūĶgÍŖ¯ßgúM<Ķ|ŧâÃ>ÅC~xŪå÷ʧŅģĮô™xzZų$„"3Y…."÷ú闘Ų+gĨ¸ ĩ žõ šqZoŗĒ>8ÆĪģGūŠixø­ûÍ Ķz5đČAĶ{É_1 ŪēĪ švI=:o”šqúĨōWLÃï2cæ MųK”:7Lī­üÃ@ųtũ´‹”OB!2“Qč2]™žÜŲ+ëŌ Y…x„ëžîiž;Įú‹éeWL:KūŠq¸æé øĢ˙¤ŗå¯‡O_`ŧûJĘ_WN:GūŠq`dwā“į§ę0Q<*Ÿâ(Ÿ^{w–ī:!„…î°ē›ÍčE÷¤Ž ņ 㖌4ŖęnŗūQ7XūŠyģd„õā7ĻúĮ(Ä'ā/ĘA¸ˇîv3aŅŊiĮ(Ä'ĖųúsÉÃ'Ļę0Q<ä÷ą‹F¤]c…øʧģënō]'„Â#ŖĐe4—õ ~áĒŸĀš)6;Fså¯xüÅf"Āh‰üī€ŋ؀͕ŋâæÖ‡ Į›ĒÃDņ¨|Š |bÃI!„šÉ(t/ŧ™ģjnZáĒŸ0wÕĢÖOōWAū +Č_áv§ĨŖü˙@ųtáøã|× !„đČ(ti0øĢBü‚kØÉ_aų+Ŧ …$t˃ō{Aų]!ō#ĄpPC<Ŧ …䯰‚ūåAų=Œ ü.„ų‘Đ 8¨!Vŋ ōWXA ˙ō üFP~BˆüHčÔ+Č_aų+Ŧ †yP~#(ŋ !D~$tjˆ‡䯰‚üVPÃŋ<(ŋ‡”ß…"?ē5Äà ōWXAū +¨á_”ßÃĘīB‘ŸŠ ŨûgŪoŽ8õˆ´ør…Wŋ~ÕėqĐYíĒaWĨŊ§Raŋ#÷3ūųŅ´øJ†¸6Ä{ũ1ŗĪû¤Å‡ö:t/3iҤ´ør„júkęß§Ú{āÕo^MK+WxūŖįĶî9g#ž‘v<áôKO7ŋzäWiņ„ß>ų[sō…'§Å×*TĶ_ŲÂĐŠCÍVÛoeZmÕĘŧôŲKiéå Gv9ŌÜûŌŊiņ!5üËC-ķ;áΧîLåų——ŋœ–^Ž@ۄ6ŠJP~BˆüTTčŪ1ų͞MË´ørņ×Ũw]ĘÖßp}sėšĮĻ^ų㘴÷T*Ŧ÷ƒõĖu¤ÅW2ÄĄ!ž)<0ëķŨõž›BX{ĩÍØšcĶâËĒé/:ÖXc Û䧕+<ũîĶö3.ēáĸ÷!öä[OĻOčuk/ķģ§—O8b Ųé';ĨÅ×*TĶ_ŲBûŨڛ{œhī)?­œaŗļ›™!O I‹)¨á_j™ß Ûė˛íđĒtž§mBŏ%(ŋ !D~Ę.t_ųę+øžøÛ…î”7§˜ánńmzáŸ/˜gßļÁqŒ^<û^ø|aĶÍ75׌ēĻAņēĪëR¯3wō’Éö;ų߁0ņ‰6mú‡ĶĶŌ˙Ëãöˇ"&|ĄËįŒ|q¤5cTj$fÎĘ9VDGظ߯!HĒÕîƒįV_Ÿ ūĀ#_Ų į=“ĐÅœƒkŸļlZC˙|úRĘü΍áÔˇ§š1¯æī´˜ũålû9/~ōĸšīåûėõ&ū˙ųŖypöƒļ÷žüéŋ‘xž3žYčōũGĪm&˟Qčâ?|đĐ+ĨŽ~ûũ;ŋopžŲ_ĖN‹ËœĐÍ6“ĪĀŋäF yoņĢ;fÖŋgŲ´§–>•Qč>ū×Įíwž°`B*î˙øƒĩčqø<Ķ=ZJ¨´ŋō|ĐŧussõđĢü^wM2ũ^Ž-Ŗéã_Ÿ––+8ĄKy‡?¸—ücųĶ#ösÉ[.ßR~ų–éũ•jø—‡Zåwe}ŗVÍlY)Īû÷=ÁåyĘ=?-WpB×åųLuÃà ļŸK]īâ(g)¯ČãÔ}Ņú̚Aų]!ōSVĄKĨ@%E, §]öŨ%%tŠDüųĻÅ-ėÔÆšodļßs{[YÜūøíæĮ˙Ø wŽcÎ:Ɯqųvš#īņĪđ??“ĐŨrģ-Í­nMŊî?´ŋŲũĀŨí˙ˆ€ŸŸųsŗáĻšw°ßáâ›/ļi|—Ÿtü‰iĶžMûÁ~`ú écĶPu:ČlÜbcŗãŪ;ÚßņíuŋēL™ūáhgģÛ™õ~¸žY♚åžŖ[›nļiQSLĢŅ?§˙9ö;īųĶ=íõé>°ģG4mˇĮvĻՖ­ė5āúÜ2ū›æ Ũ3ûœi¯×cũ Ö7G~TJ€ĩn×ÚÜöØmŠcûŪŅ×t8¤ƒũ˙˛Û/3ģ°›íDXsÍ5íĩōķ3:VčX ßmąíöøA˛ $âČ/;īŗŗŊÖãæŗį'īíûŗ}mžŨq¯­ČZs­5ƒēĪüß3fëˇ6[lŗ…ũËŊē']p’Íįø€{Ct" ×Z{­<ēĐ|ÜÁV4û×Ų،ÍČ+ty?÷äÖ;mmũņË{~i}‹I§qÉ,Œ:ė`ŋųČ ]¯ø~ķ­7_oęã~œģjŽđģÖĮŅĪ—ūęŌqĨ†Jú̐phįCÍwžûëĪ ŽŋĀv P†âĮŨößÍŪS—ÜrIęxFŌŋŋū÷í5D “œ08äøCŌ|ˆ1ÂN:÷÷÷#ŖČ›´Ü$%ÔíwooËR|Áįv<ŠŖMģáĄRįbĒ)Į'Ü=VÍ †y¨U~'OŠKÉķĖADîÔū˙ËķõuAīÁŊSĮ_{īĩ6?Ú˛ŊžŦŲuŋ]mį(i?=ö§iųsī§Ėá}”™äyęÚ0¤ŅšK\4Ī~Ęá6ķSf˛<‡ríÜ+ĪM}Ÿjåw!„ČOY…. Ķŗúže˙G(pô)ĄËhĸŅõôĶP^÷{ëÚŪR„ã›l`~3å76m抙ļBŖŌaÄÉ-Āčųõ?ŋąB—˙iœ1ÄkÖ5ōš4Đ]#Ü ž' Oū§ĄÍyŨHíõ÷_ow]Ä,•2#šî3yß į`˙Gpü봟ĨŌX×u@×ÔëÆ„J7Äé%GĀŌ{Íëɋ'ÛOŽ?ŋéIuN ™4Ä6P?QĄ‹ßšŽn*+ oÕŽĶ ŸĐ]įÛëXąJƒ‡Īōķ3ōB? ¸y!ĸXęÎĪ9Y˙Å˙—˙úr+€ÉoŧFˆņū…n§ŗ;™ÃN<,ÕarĘE§¤„.Ŗ{6Û0%z¸ŽtP0RČk:,œ?øŋq¯ú×ŲŸã„. J:Ϝšķr‘Žã3éėŠ ]>įŌÛV‹SDN'tŽ\=ēë~÷;įb4‘ûČųICä“÷2Í8(%TŌ_…Ę'î-ūīvU7ë+Wî DšĖ^Ā'Ü+÷LŋĮĻq­iˆ}ÆŅö5×Č÷!ÆlŌš'ŅŽŧãžÁüčdp߉ĪÅūõĻLoģC[û=ŖņÕ jø—‡ZæwbÔĩ‘”ánÆ(äyęZf‘įŠ›IŖŧÚįđ}lÄëlyŪÕ÷´Mč°vyžK¯.V(ķ?b8ēĪŸKž§ĶĄË˙į_sžM͈ŽBė˛ ]ÄŊ›ˆ—ię2Sīšv—ßûū÷ė4ÄͰr=όÄŅ ‹ž¯ĐXĄKEFÅ][ČņŒžPI"Ö)bß۝ƒ†ŊŲ$0ĘÄHJtę2>ĻËō}-<ęGŲxįœ—Š›+˙3éŪטP醸ŲũÎ6‡ŸŧÚ'.¸éĀ|oÃ!„Ë 5ēģwļëĻŖĮq}]Ã9ŸĐĨ‘}oŽ€ĐEˆ;ņÍ(% >Īų—s‡pÂ˙ß´zŸ@ƒ‡FTˆB—Ųî^"0Õ.:ĸK`”Ÿõûm?;zŅī7ũl<÷„ģߘy@§“ķsŽā„.ëéhô9c†éäõo}ë[VÄē÷8ĄËŪË}âŌ2M]æ8f@0Ķ‚Ņv7…ÎĸĶ.9ÍūOŖ”NĩčûĘ*é¯BCTč2Â4āî Ō<æ@+č¤ņgšpŨčdŠÆe ]'.LįÄwŅ °(§8ã;|ãŌČ/ø–2œōĐ?5‚ūåĄ–ųēäiōv4{ŊÛ/ģŲgfDĶhs0ķ(—-Đ6‰ÎŽĸˤ ã:> ÜœķŧĢĪŗyžN_'tŖĶ™k”ß…"?eēCŋ—áį„.=Š4ĻYbäĄH#Ė5ΙB‰Č aÅΡWÜy…Gœp^ߘë‡lB×MŠ%ĐČwBŠŠŽFa7šÉĻ3%ķ”ž§ØF >׌01• Áũ~'ŋ—F›Į02ĖÔAD‚Ę ]͞„ŒZqLô< •nˆ3 PõãiÔâƒčēISOi|D….B™ŠËŅãVn´ĄûĢG˙ˇ /ŖŦQĄIbDŲĪÎX?ˆĐešĩ;Ū‰=üæû˜Ņ{DՕw]™:ž€` QčrŊŖģæ2zËoGč2‚A‡1äEf]0ģ ]Fõåzu>ŋŗųEī_Øxf%ø×ŲŲķ?_ĐÔe΍sB—(úõÄ_§„.y‹īČTs:–˜ÆČtAˇÁÜŨĪÜmË~÷SļœK •ôWĄ!*tų?ÚiG /#ø™‚ėî(žũÕןëęûcié”qŒ ģ÷2rE:å9įáŗ™Ā,:ŽH‹Žˇgä˜{š-ŅīPÍ †y¨e~'D….˙GëK_(ŖčüÚû°Ŋ¤‘‡™)Æ˙Ôī~~ĮÜ *Wgģ÷’×I§CķßSyūēÕy>*tũ ÕĘīB‘Ÿ˛ ]FŅwŅQž›ĮۜēĮw;Ū6ÄÜĮ3ĸE@#pЍhHWęÔe+*”zÍ´:'t™ęé?΄F% ~F/hxģxF’hhS1{Îą  ;7ŠŠƒL§rĶ} L+eО{ÍT@Ä>$rĶ<‹ •nˆĶ õˆ_FĒYcmtãO:.č! ]DŖNŅs0­Ö=v 1C>qi4ŧŗ ŨBĻ.G….ë<dķ%‡¨sSiņ‰$×BŨŒŠ|NĮ‰{ÍoĻ1ÆũFƒuhŅ‘ ϘF§+3×ãÚV<ēŒ ē\ŒĐåūÆ7Ņ tr8ĄËũáfxë|–›ŽĖįŗ•Žō]ĻdJ •ôWĄ!*tY'Oƒ;šNcžû„Î<:ĸ”…ø˙ŗMãŒN]v|î%Ö%’˜ÎIˆKsŖņt<ņšNKAĻ‚ĒÔđ/ĩĖĐĨŽĻ“+šÎž”]äWęühžgVu ˙gËķŅŠË7û_ŨCۅÎ@ĘĘĄhÛĀu˛ō~ ]!„‡˛ ]ŊŦL¯C(2"Æ1Nč"(ņcĒ" ŖžŒč ~xpęũŒô!TÜZØÆ†LB—MuKWFh :Ą;ėŲaVT#Øh¤1Ɗ`et1GÂÆ #FŒŽPy:!Į”[*;D<ŋĄK‚˜9&ŋs˛É†ûNô͈dT:ēmcCĨâŒØđ;P|gĻM˛f—kÅôEĮüfŽ˜×ˆŖ¨ĐeM%ŋAÂTbŽ#n-!‚†éáø‡ÍĻØ'›ĐÍ|ĄK`ŗ1FäG„›č¸Qtvˤs†õ¨üŽÅ! ]:ˆ~øƒÆëåĐe÷y—NŽüÎč´m–píü鯚B)B—˙éė@ŧŅ)ĔiîM't™ @ž`Ú3y×|g|ėÎÅtBž3' ?š<Ą’ū*4D….rĘ fÁp/ҁAÃ?ЉÁtôS/>ÕĻ1O~(tƒ.î]ž¤ķNĮĶ/[Ŋļ{N:ø,:čđ{(PfRN"0X+éŦ]ģËÔđ/ĩĖĐEĖŌ‘Åķ¤Éה]ÔA”etL2‡ļi” ”st˜ųįĖh›°_umÚ.nöŗĘ˜ ävާ,"ĪsŦ„ŽB„CY….ŖdŒāŅģĪnÃ4°ŨČSáh Q‰ą +‡)D4Üû”ˆÄč(oc"&翆€Xcē&ŸËôM*Îč(ĶĸB¤3™ ÕĨąŖ##e¤1-1āŌíŨv×míoeŊŋĶ5ønĒ#ŋˆ]w\`Qt”ˇ˜P†8„!׀]/é Åîŧ4ŽIŖ!ėFŲi D7ō á˨ĮŅŲ}^į¤E“lCÚú§ū=7Žž1ĩyėŧí§l1‹pŽÆ!ė8S¤Ũ÷D8št„7y1‚Í45ž“îr„Jû QÃīÄ'LÉfŠ=-\ĻÎķûI#2m™\÷^Žc”>:ʛ/Đāã3ĸŗĸÁäwTā 7͘M\čđâ{q"€Ũ( Ŗ†ˆ`ŌX‡Gž`#ˇč4F:GĸŖŧå•öW!ÆvtJ1eųÕŨ/ŅŨYOČæ:¤Ņ€§ü-tŊ,e%î^ ÁīÖēs?ģ2€û—5ØLUĻcŠzÃú‹ŋŒĘ_ņ ø˙Dũ…˙ä¯xüBųG9čüŨŽüĪ€_ÍũÛGJÕaĸxT>Å;øå“Bˆėdē0vŪŨæúé—ÔĒŗŌ Z…Úü1hz/ķđŧ ü5~Ū=6^ūŠWĀÜGø'Ę#ķFĘ_1 Î_cæũށŋ7ĘÜ8ũRų+fÁųëÁyw6đ—( ʧĻ÷V~Ypų}ôŧģ|— !„Č@VĄûÍĒolcžrĻYž¸bzZĄĢPŊĀõĮôä"rņī/Ĕüāü…?ÆÍ–Ņ_øĘ_ĩQQîeōų+!ę/DŽī/Q*Ÿâĸų‘Ģü.„…‘Uč:˜3Ŧî&sÅäŗėvö˛ÚX˙Ég›áuˇ¤Ļŋfã͏ūjîŠģYūĒąqũņūČūįø×?‡Ŧz†ŋ(įōMÄ_#ęn•ŋjløëîēÍĸūėģH”å÷x˜Ëī‹?zŨw‘BˆäēB!„B!DHHč !„B!„hRHč !„B!„hRHč !„B!„hRHč !„B!„hRHč !„B!„hRü?ņ]ÖûZuˇõIENDŽB`‚trillian-1.6.1/docs/vds/VLDM2.png000066400000000000000000001541311466362047600164360ustar00rootroot00000000000000‰PNG  IHDR›Î?Œū€IDATx^ė ŧUĶ˙ūĻL ¨P"B¨i@%2$sČëžŗ÷ÎŊįvž÷ëĩ^ĩ÷Z{ŪįîĩŸũ|>ë˙ŒB!„B!„…Ä˙ų3„B!„B!„Č/›„B!„B!DĄ!ąI!„B!„B›„B!„B!DĄ!ąI!„B!„B›„B!„B!DĄ!ąI!„B!„B›„B!„B!DĄ!ąI!„B+å˗7˙÷˙§ĸĸĸĸĸĸRB Īî($6 !„Bˆb…NĢB!JqĪîčZ!„B!ŌL\‡U!„™EÜŗ;ēV!„Bˆ4×aB!Df÷ėŽŽB!„"ÍÄuX…B‘YÄ=ģŖk…B!„H3qV!„BdqĪîčZ!„B!ŌL\‡U!„™EÜŗ;ēV!„Bˆ4×aB!Df÷ėŽŽB!„"ÍÄuX…B‘YÄ=ģŖk…B!„H3qV!„BdqĪîčZ!„B!ŌL\‡U!„™EÜŗ;ēV!„Bˆ4×aB!Df÷ėŽŽB!„"ÍÄuX…B‘YÄ=ģŖk…B!„H3qV!„BdqĪîčZ!„B!ŌL\‡5|ųå—fųōåæ×_õĢŌΡß~kˇŊaÃŋĒDÃņŧ÷Ū{æ7Ū0_}õ•_1Ŧ^Ŋڞ˙ß~ûͯ˛Ŧ\šŌÖ˙ī˙ķĢrĮÎō›6mōĢ,?ūøŖ­_ˇn_eÖ¯_oëÖŦYãW !DF÷ėŽŽB!„"ÍÄuX“ņįŸšK.šÄlĩÕVĻOŸ> uũõ—iĶĻiÔ¨‘}ĄŸ3gŽiذĄéØącBģzõęŲmSƌ“P—n.¸ā‚`ۏ>ú¨_”—_~ŲĮOk͚5ķ-6!:13•ØÄŊÉö—Â|öŲgæâ‹/ÎZąiãÆV°<î¸ãŦØųôĶOÛs1xđ`ŋŠĸ‰{vG× !„B‘fâ:ŦÉĀĨ'œzęŠæĀ´˙ĮũSētiĶģwī„6"Å!6Áë¯ŋžkąiúôé ĸÍĩ×^kvÛm7ŗvíZ¯ez8ķĖ3Ížûîk_ū +[ļŦéŅŖGB›{īŊ×îã‹/žĖ›;wŽwÎ9į„Z– nŊõÖØ{,ŠĮ{,RlŠ7OļŠMC‡5ĨJ•˛ĸ ÚQ¸÷„™CÜŗ;ēV!„Bˆ4×aMF‡ėrŗgĪöĢVŦXavÜqG3qâD;ũĀ˜jÕĒ%ŧ´:ą ˇÎūûīo˙ŋįž{šm€°ŊîŨ웪>:ĢRĨŠ ŖÎo‹đâBąvØa͏qcĶŦYŗ›˜Øa‡™-ļØÂTŦXŅÜ~ûí9ÖyÅW˜“O>ŲūŸ@åʕ3/ŧđBB›0ˆPD„ūōË/~už!ŋĶØącíôC=dĪSx?™Įņ ˛8>ūøc;īŌK/ĩĶ_|ņ…9餓ŦŖ‡2~üx;˙íˇßæ2ĩlŲ2;‡›Ãeذa怰bD˖-Íwß}l‹usŧ8Ģß;ōČ#öŁāčÖIIuØtŪyį™ĒUĢÚ˙ãÔÂUįÃqâTcģ\Ž)×Ė›XgxÛŽøë ‹M?˙üŗ=V×öėŗĪ6ŋ˙ūģyōÉ'ÖŅž}{ģ,!Žáų\āž4h=Ŧ{ûíˇ7 4°÷bf˜¯ŋūÚ\vŲefįwļmqÔ]xá…ĻyķæV@t qŦ8ÚpģsĖ1æúë¯ļũøã›wß}× “Q÷œƒëÖšsgûB`Ywx›BˆĖ îŲ]+„B!Dš‰ë°bđī„‘̝ž:A|qb“{ĄæšB… v:úEî§íļÛÎBīh‡ØB;ß-ÕĩkW;ŸõЎöIĖ2dHĐΉM”vÚÉļEĀJļÎ.]ēXņ pqüQŒ=:Xw~9>Ī?˙ŧMü äÅēîēëlȓᥥҁiDĘ9sfp~wŲeN„q˜ŋëŽģZqq¤~ũúĻRĨJv>"˙"ŪRČ˙{öėi—G,ÁšÆŧC=ԞKÄ1Ļɏ†\I{īŊˇŊŪÔßsĪ= õ'6QöÚk¯`\K'’Á;īŧc‘-ˇÜŌÔ­[×uÔQö˜Ũ˛aąéÜsĪĩÛvÅ›ī ‹MĢV­ îÄÄîaîÜmĖįØĪ8ã ģ,!Ŗî\Pī„Ž“yî^c_Ũ~Ūwß}ÁļšÜŊ›ļNˆĨ8!–ü]î×ĒUË^+΃kĮų%÷ŌņĮĖKUØāšęöOž<Ų<øāƒÁž !2~ģQD× !„B‘fâ:Ŧ>¸lpX°ÜįŸîWį '6ņĸîF Ã!Ã<-a~øáķĮŖ”ņ/"@ĶĻMÚąoĖ5jTĐ˜—f\U~øaĐΉM¸AœC‡‘ÜS, b9n|ĮLē!iģ/$ Ž„Gt›1cFŌķ†ā€`ƒˆ†$Ņn]¸hC`ܸqÁHnLŒ€į Ą„åÂÎ"$uqbŽ,œD@2qæŨxãvšų•+Wļēpĸņ)SϏ¨0:ˇžTb9Éå˙8ü‘ëp°!ø †š}ä_Ä"ļHę ”‘õpß9ÁąA qԁPHģģīž;˜wÜq‡?pā@;¨Åt8—זsÁ|Žđģâ÷UÂĸĨĸdĀī<ŠčZ!„B!ŌL\‡Õį ËrČ!Îã’,gķČųä@ŧáåÛšrÂå„NÚâ‚sĸPp…āhņ™§ĘŲ´Į{˜ƒ:(a^I`äȑöxpėôáĮ%ÉöŘīÄ7—„ܸ‰M­[ˇļ×Ágɒ%ļž0/Âŧ¨CķÉ­Øv†á2b#ĸĶN| ĶŠS'[WąÉÄĐdĮ„]Ō†\GārEą˙aU ;Ž $|4,4÷×Đ˙m!lá DD.—-ĖUW]e×īÄ&Äá>ø ˛p^…% ~įQD× !„B‘fâ:Ŧ>äáE™ü8ß~û­_'r+69ၰ°ķĪ?ߊ&ˆJžØ8<ƒbT3^ōqíŧúęĢA›ÍMlrŽ\\Žõë×ې.„Á0Ÿ~úŠ=85pđpŽ-Z”ĐœØäBæ|ČëC=IĶ}}:ōų†ØÄ}Ã4áb>÷ßŋ­+ˆØ„KˆBŽĒgŸ}6ĄWmpÎąN%ÜqÉ~œ+B2q%áã7ÄvČ˙ä \ąN:˙-”ļÉ:|ŸdNlšĮtT! OQ˛āˇEt­B!„i&ŽÃšŒ[nšÅ.‡hQr+6áâ ŋN8ŒiÚ´i9Ä&ÂÅČUsÛmˇķ€Đ<ŋmēÅ&„žĸ OÂõÂņ„C =$œ‹K>¸jhHÄŋ]t‘ßÄ'6qŊpģqSäč"_Ë&ËûSb‚‚M5Î5IŊbh[ą ą’ûŒããūņCÛî÷@¨˙úN+=ÜJäĢ ķå—_Úũ‡mwÜq6$‘íúĮĖ Ļ'žxĸŨVX@]ŧxq{ˉMäĶ"ažŸܕž}ûšŠS§ëČĪ=÷œ]ĄĢBˆĸßyŅĩB!„B¤™¸k2r› < œ'$Šf=<„r‘„ķp‡8G Μ8¸™p51M ą CˆÂeâæ#°Đ–ÕxŅg#…„KP[Šŧ7ˆ¸@xųįeŸÄĘá\Gyaá…v˙zČ-U œāNbģ­Zĩ˛#ÚcL&öâÆ9ĨžcF¨ƒčæ’h#b¸ÄڈvásƒØ@›mˇŨֆœqŪĢW¯\—pQa(œ œ¤ÛLã–sų‡HĒí’ãBđ"išK”[‹p1čØącp!nÎįŊSa0•H”Ll"ų¸1ą yÜúXˇŸ ÷Ą;„Ž… ‡åqs~(¸ ˜wųå—mŲ>×7yĩ\[FL¤-#2Â'Ÿ|bˇ…†đÄyw‰É)NlJ7iˆqÜ{ÜSBˆĸßyŅĩB!„B¤™¸k2 ClÂmáÄ „†#FØ|4ŧŒģųgžyĻm‹3‰ÖîEA…:„Ú1âÜO?ũdķę4nÜØŠ!´qí40œ@ō /ËRc›p˜¸y8LœH’WxéF,`–-[æW§ ÜZˆ9aa$<Ō™bíœS( #Ø!šķ.Œ|撯;ŽdänÛ8éÂáå`4ŊũöÛ/Įú\4hm‡ĢÉÍC`"¯Ž)'TQޏâ ۖkŽ;QÉmá‰P5×ļ[ˇnÁ>„I&6!ˆš}$4Ņĩ Ÿ ŽË‡°Mę|!ĘAâzîK—Ā۝#\Qá×WXPĨ f!"†ÅK\lávüޏ˙ųQ‰MŒpČöR9ã„éß]ŅĩB!„B¤™¸k2ēvíj—2dH0 WQ@Ø‚TĒdÍ>8Ĩ(Å!l$u.ŪÜ{Šāâ\ÂÅU˜‚ÛvIŦ‹ŽuÜq‡A ã>FŦ*JĖÍ9­Gģ¸ļkÖŦ1ß˙ŊũŋKX/ôéĶĮ:ŦŠëw&Dļ÷ėŽŽB!„"ÍÄuX“AHĶvÛmg—%áą(9~…Sˆ\I\?’„‡samÎ ō¸Ø­Ī…l•0“æÍ›ͰaÃlh*ŽŠĸĘÖ¨QŖ9Ē„é'îŲ]+„B!Dš‰ë°Ļ‚°&“ÂIĄEf3räČ ä*\Î=÷\ŋéfIåʕs{IK—,Y’ãxŨ$œ˛¨¸ëŽģŦ‹OQ´Ä=ģŖk…B!„H3qVąų°iĶ&›ÃÉ‘,[C’Đģc~⁠<šbqC($Ŗ)ēcâ˙_ũĩßLą÷ėŽŽB!„"ÍÄuX…B‘YÄ=ģŖk…B!„H3qV!„BdqĪîčZ!„B!ŌL\‡U!„™EÜŗ;ēV!„Bˆ4×aB!Df÷ėŽŽB!„"ÍÄuX…B‘YÄ=ģŖk…B!„H3qÖtÂhZãĮ7O=õ”-/žøĸߤİpáBķėŗĪÚãXˇn_Ā¤I“JüņFņķĪ?›‰'š_~ųůJŋūúĢ=§O?ũ´_UâøđÃÍ|P&L˜`~ûí7ŋŠ"K‰{vG× !„B‘fâ:ŦyŠN:f‹-ļ0‹/6ŗgĪ6[oŊĩ9öØcũĻæŨwßĩÛwĨ|ųō~“CéŌĨƒã˜?ž_0uęÔ ]Ŋũęsá…šáÇûŗspųå—Û}xõÕW­8´ĶN;™J•*™Ÿ~úÉoš'V¯^mĒV­j×Ũ¯_ŋ„:ĄũöÛĪlģíļæûīŋˇB#÷Égœ‘Đ.?|ûíˇv›Ük%˛eË&ü.\š˙ūûũĻvu={ö´Ķ‡v˜=ŋ‹-ōZ !6'øŨG]+„B!Dš‰ë°æ‡YŗfŲ—ū[oŊÕNwčĐÁngŪŧy ípj<˙üķ֑‚@Q’ÅĻ÷ßߜtŌIąb͟ūi^zé%ŗ×^{稴víZģũ6mÚøU Ŧ_ŋۊc§œrŠ6l˜]îå—_öZæÛnģÍŽëÆo4kÖŦņĢÍØącmũ}÷Ũg§/ēč"{¯|õÕW^ËŧąiĶ&ĶŖG@t)É 6íŗĪ>ãĪ•~ø!ĄŨß˙m8āsāš˙ũīfōäÉöÜr„›7qĪîčZ!„B!ŌL\‡5ŋÜ|ķÍf×]wĩ/ÁˆI7Üpƒß,āˆ#ŽČ•Ø´råJVôÍ7ßøUŧ„Ī˜1ÃļK&xäÖÅ:?ũôͤ!b,NlZąbEä~ÖĒU+ObĶīŋ˙n×K8bQ2#Ø~ëÖ­ũĒ}ôQŗãŽ;šå˗ÛéŖŽ:Ɯ|ōɉōÉÅ_l÷!J<ēā‚ LõęÕíuÂáTĄBĶĢW/ŋ™§!y8Â6nÜčW§ļGČã'Ÿ|’ôz§ Ä&ŽIÜ_8ø'€sø¤p;!6âžŨŅĩB!„B¤™¸k~áE÷aZ0räHŗķÎ;§|Ž›ĻOŸnj׎m÷וƍÛ\Ia>ūøcŗīžûmļÚj+SˇnŨ`ēAƒ ís"S˖-ļXÃņĩk×.hįÄĻ5j펞új^&,6…‹(„˛{îš'˜Įša[nz›mļ17Ũt“ō-Z´HØŋpÁ%æČ# BÜÎvØaŗlŲ˛„6azʔ)c:č  EnÄ&Î'× ž|ōIŗ÷Ū{'´á>šâŠ+ėą†Ī BĖÃ?´C„ĢRĨJÂņN™2%´Ļ˙@ĀņĪ+8‡K×_Ŋ)UĒTPĪ9B@Muü;wļíēvíęW厑c"ŧ°aÆæŦŗÎ Ĩ0įŸžiÛļ­ũ?yÂØG(!ÄæoĸˆŽB!„"ÍÄuX QX zá…ĖüjņQb͜9sė‹4ÂŽ„Î>ûlŗå–[ZË9tČ…(Â|ruīŪŨ +N0@,éßŋâĘc@Đ9ôĐCíōõëסëėØąŖG˜wĖ1ĮmØ„Ā„ģˆũDTbĮƉMß}÷Í•DD!ÂĘžūúk›Ī¨I“&Ážī˛Ë.V`ęŌĨK fq.ƒ6'žxĸŖ ‘‚r '˜nŨē…ļllčĸģ8¯FPīķÚk¯Ųõ"°Ååtʍ؈'¸Ô‡ÉÕÃ8ą čŽ;î°!™5kÖ´ķ8a¨CxtšĸĸÄ&BöÜšŠ\š˛mŋŨvÛY÷°/͚5ŗķ OCDēũöÛÍūûīoįq_%ŖUĢVļžû˛ ?Ë]w'ļ!šú åß|ķÍāz ŊōĘ+ õBˆÍū.D]+„B!Dš‰ë°QbĶŠ§žjЇaä1ö˙’K.ąĶ8ˆ˜~üņĮƒ6äHBpažŸ°:7āēaŲĢŽē*a>ų‘ĀxNl2dH0įķ¨ ƒØ„xãDĢsĪ=×&ęķŲgŸŲ:œ.aįîįÖ"?–#ˇatųaæĖ™640 rQá*C ņ%¯8§×ôwŪą‚"Ân'Bے¨Æ2ŠÄ&\@.Q=b×oûíˇOÛÆŒc×Ņ´iĶįbÎqĮgīÚsįķÃõ¸ŠR9ŸōIŋīēë.ķå—_ÚiŽq'—BÄ=ģŖk…B!„H3qÖĸ"Jl"™5âJ2pŗP—ĄVž{ 'O~Å&ÂäX6Ųč^¸s,XL‡s69–,Y’TŦrŽ' Ž™d"…›p ųöEŨc=ĖK§Øao¸oØ>ŽŦ‚‚3Š‘ëÜ9ĸāø!īW*!+Nlr¤Ą‰Â˙ÃÜ}÷Ũ ÛLVy䑄eŠ'¤2ēŖBđ÷ ŠčZ!„B!ŌL\‡ĩ¨ˆ›!Ã1ãį=ÂyÂ2ˆ5€Ëį‰ŸÜ ŸąéĘ+¯´Ë†EĨTäUl"'ĄyÔķī?ū˜ĐƉMÉr%]vŲeļî­ˇŪ æ§Øäv⸸6lđĢķN.œ=¸…}DdĒS§Ž=>r%#7b.&ÜLM¸›€Đ9Ûbl Į\˛ōŪ{ī틊N:Ųũrá~Bˆė&îŲ]+„"k™˙ÕFsį‹Í™ƒfšúwOŪlKƒŪ“ÍyÎ6C?Xi6ūš<‹"ŊÄuXĶ báQōņ¸üK”°{åŌK/ĩûyŨu× ÆI@ŪĻM;Ÿœ=ā„‚SN9Å&L†iĶĻŲŅȟą‰<9,Kî°„@ĢˆQÜØn*ˇŸ~øaЎí;Ä…@!Ä År„w҆\L„Åšctb…$⸙)sy‘Ņ-œpü=Ė'‘ƒđ.ŽáÎ;īLUōËÂŲKEns6ُČ=áBɀ‘ëwß}÷PKcV­Zeī’zŗí#FØiġ0ˆDäg"oᑴ!$įÜ Aƒll8´ČŸå‡Ë‘;ŒĐÍdy̏7șÄ>ö‹ēđČ{o„["’tD>î!ōUĩoßŪ¯B” âžŨŅĩB!˛ŽŋūéĐŗÜ´8Ũ<8nĨˇ`­™Ŋjãf[fũSŪ™ķŊé<|‘i9`ēŲ„EK\‡5] ` 81Å/ä3r%D—›Ü5ŧxãLaĘĻM›l;„ōę0ŸŧA.´+ų›pS5oŪÜ.HÁļ)äQbBûGŽŸđļHĒ¸žĮHx.éŗŅ7k‡ Âą8ą‰ķÄļÃëB|J–CéđÖa?W˜fķë6zųå—í:Øoß5æSXbIÎYįŝswŪp99H¨>/~>|xĐ6œxÛ/UŽŪŊ{ķm#Hšy$c÷qŖ">„ĶO?ŨއpPļģįž{Ûíͧß<Ī D˛ŽQŖFųUBˆŋã(ĸk…BdF/3—=1ĮLYē>‡0ŗš—į§|eNy`šYđĩ'!Š’¸k:!?ŽĸäJ‡’!#(! .ņ^¯^=N⠄›ąŪÃ;ĖæüaøxF3ã8ķ#6ûķąČ Œr‡;药hjnßIbíÂĀČųäæ6ĖŦ_ŋŪ:´˜vî#Bđ\;öœØÄčkl‡m 80rxûÕĢW[†cg?IŽŪĢW/;Â]~!,'ëōΎOa‰M:œD\N B Îĸ°C‹Ųü{'|–.]´%$ŌoãĘsĪ=´ÄļíŽ7"įõ™gžIš¤{Ā€ļ]~ī1÷9#ņš{­Zĩjæ´ĶN3¯ŋūēß4ΰnDŦFųUBˆFÜŗ;ēV!DV1oõFĶrĀ 3yÉēBLļ'N Šĸčˆë°n \pœC‡õĢ2š°ØTR@€cŸ;vėXā2Q¸Œ9Ō:ÅČ­%„(ŲÄ=ģŖk…Bd]^[d]‘C€ÉļŌųÕEfČ˙:„é'ŽÃZÁŌŖG[j!#ė.œ˙'Ķ!ą¸K8~ôŅGÛcÉMΤâ'‘ ŧ÷Ū{ũjQŒđ[Āé'„(ųÄ=ģŖk…Bd§ ˜nŪ_øcņ%ÛĘģsŋ7­Éü*!6â:Ŧ%‘råĘŲãrĨRĨJ%.G a„ác ÖU 6įÛåŌ™ÁėŲŗ N)„ČâžŨŅĩB!˛Š}ϘY+7ä_˛­Ė\ąÁ4ę“zØj!Dá×a-‰p›ŧF”™3g&äbÄ:—‡'ސ—Š Ŗå„ģcp%Ų(hB!˛¸gwt­BˆŦĸūŨ“s/ŲZ8Bˆĸ!ŽÃēšņÄO˜š5kæ–’•SN9Å&īB!2‰¸gwt­BˆŦBbĶEb“EG\‡U!„™EÜŗ;ēV!DV!ąéŋ"ąIˆĸ#ŽÃ*„BˆĖ"îŲ]+„"̐Øô_‘Ø$DŅ×aB!Df÷ėŽŽB‘UHlú¯Hlĸčˆë° !„"ŗˆ{vG× !„Č*ō+6Ŋūū ûĀšūļ9ęŌYۚ4Įn÷ę›ģä¨+h‘Ø$DŅ×a"Ė™3Įް÷Ã?˜ß˙Ũū˙Ã?LhŗpáBĶŖG[Ū~ûí„ētķõ×_›ž={Úm?ķĖ3~uRūūûo3aÂ[ø?ëā¸,Xā7-TæÍ›gˇķŨwߙ?ūøÃūŌ¤I~3˟ūi† bš6mjŽ=öXsÁ˜YŗfųÍ`DwŌ},>˖-3÷Ũw_°ũ^xÁŦ_ŋŪođÚk¯m/^ėW*ũõ—yüņĮMŗfÍėšlĶĻ™>}ēß, 6ûĮuKĮøØcm~øaŗbÅ ŋYžųõ×_Mßž}í:īšįŋÚÂčĪ?˙|°íDîkaąjÕ*{īēëÆšdšs&r÷ėŽŽB‘UHlú¯Hlĸčˆë° ‘^}õU{īŨ|ķÍvēE‹vzöėŲA^t™GšōĘ+ƒųE“Ûö‰'žčW'e˘1ļ}Ûļmí4BÎ[la_˜Ķɛožiˇ{íĩ×ÚéV­ZŲéO>ų$Ąâ#,ēãÚqĮíŋĨJ•2ãĮOhæÆo –yũõ×ũjËá‡nnšåvx˙ũ÷ÍvÛmlە›nēÉoĀH“ŽŨ{īŊįWˆ‰gœqFŽsšÍ6ۘQŖFųÍ:vė,ķŌK/ųÕDÃjÕĒå8îƒ>Øošg>ũôĶ`}ģ랋_m~ûí7S§NÛŪa‡ė1§“æÍ››­ˇŪڊŸĩœË#<Ō ¤"'\—(ĸk…Bd›ū+›„(:â:ŦB¤ „ōåË[ˇÅʕ+MéŌĨÍÕW_Ô#ŽāĐá-ją Ø'ļ[ąéŦŗÎ2+V´n- BÉ%—\â7K åʕ3?˙üŗųę̝LŲ˛eÍe—]–Đ×ĮÃKũ?ūhįáÛvÛmÍa‡–ĐÖņŲgŸY‰ŠÄĻ-ˇÜ2×į)ˇœtŌIfĢ­ļ2O?ũ´™6mš7nœ6l˜YētŠß4L—.]ėžĻSlâŧą ö‘k lk~āz­˙åķĪ?ˇÂž;—ŠÄ&GÔˇoßŪ7b%NŖTnĩŧōÍ7ߘC9$ŠØäDāsĪ=׊•¸ô^~ųeķÎ;īøM \[\k'>÷ęÕËNΘ1Ãk)qĪîčZ!„YEaˆMīMY`nę|—š°Ũ ĻKīfŌgĢs´ŸúÅwĻīCOš+ÛßnZ_z•m˙ʘŠ9ڍ›ĩÔÜ~×ļÍUí;›AO 73WŦęÃb͍O™ö]ūŨ.ËLœ÷eŽõåĨHlĸčˆë° ‘.–/_n!Ī=÷œž˙ūû­H˛iĶĻ  ‚›xé&¤jāvŲdāÜĀ•„+Ē˙ū‘a_'N´/ö}úô1oŊõ–™?žYŗfMB'6ņŌK8۟;wnBøöÛo­ÅÜŅŽB… æûīŋ÷Zūۚ<špžw_~ųĨ)SόcāĄ‡˛į6r6|øpŗĮ{ä8w„€ņbŸĖšŌ¸qc{œķĖ›páp]ppáBá˙Žp>ÃL™2%¨Û¸qŖ=7<ōˆŨW'Øį…6ûîģ¯ŲyįÖéD˛0"„†jF¸÷Q2ą‰eޏ7Ø.×1YØaˆI„Q&ˇzõj{,lŸ{†03î—Ę•+›/žø"aŲFŲmÎčsōÉ''œK_lbŊË9įœcëŸxâ‰ā¸—,Y’Ŗ-÷!‘ĪāÁƒ­(œkļå ‹äZ…Å&„Ũ?ūØēÆØ6bÛv˛:DČ{īŊ×Ü}÷ŨæŨwß BF)ūo(7tëÖÍT­ZÕūöŲūå`ņĪîčZ!„YEAÅĻēõ˜íļßÁ”¯°ĢЏ[%;¯Ō{YŅČĩETĸnë­ˇ1r¸9ø°#˙é—˛ÄŽ}í^xg’)]ĻŦŲątisxcL•Ē˙ÚškÕ>Ú|˛äÛÆ‰Muęoļß!qģģUÚ̝ą8ĮžæļHlĸčˆë° ‘Núõëg6@¸ áX/žøbPīÄ&„ūu‡Čˆ#‚vpúé§'´Ąā¸4hPB;œTMš4ÉŅ–‚;)L˛mŗN¯0lã„NĻkÕĒežzęŠP‹œėšįžv}œƒÂ ŧŧ°uÔQV\‰qgĶūûīīWYqŠũ;õÔSÍŖ>j˙ī‹Má°<ŋ `š|;cĮŽM¨ģūúë­°čĻwŨu×@,AÜķ×åĘŲgŸl!-ŲuŦRĨŠũ7,6ņœ_~[ î!îŖ5j$Ôs¯áĀsĶŠŽ áf{íĩ—_eÅ)–e‘øŋ/69GS˛˛ĶN;%´e~ÄEÎs˜>úČėžûî í8ī”°ØäMÉ ÷{X4íÜšŗí7úët˙ĮíÕŽ]ģëņKŨēuƒuîŗĪ>A^67Ž‘L¤†sEt­BˆŦĸ bĶ–˙tęî04˜ßĄ{_;ŋŨM‚y‡Yה)[Îŧ1av0WRųŠģ™Ũ+W æ׸ŠŽ&Ė]Ėģņö^v}=l§ØD'¤Į}í:õú÷‹bÛkoIØĪŧ‰MBqV!Ō ĸH8ņ1ĄdÎ&JëÖ­ÍčŅŖ­€BČΗ0Ėŋá†ŦČƒÃ‚°ąJ•*Ų—đpŪÚą>Ä)DÖyûíˇÛp1Ü'aÜļqøJäÜAŦ“5ĮÚĩkmqD…z9X'Bë, p&…]K¸Žĸ’+“‘‡į8į*Ė/ŋübŨ%Q$lN%6‘čW₁K*M ‹|$ŧÆEåÎ)Ž1DFÂ˙€ũÂm†HÂyvëCŒAĐq8q§×A§AƒÁúÃbųޏgp?qo–vôŅGÛvLדcāX¸ÛÃ Ä˛nė÷¨įũeY_D"÷*×§]*ą‰õr.ęÕĢgë )å¸īŧķÎ"ŽĢ:Ø{‡ũĮÅúYÖÁųD¤â˛ÚqšæŦ?,6!ėāörâ!atîŧ#D9ž}öY[ON)Î!ŋr“šķÃ˙ÉģFŋĨ† &-ˆĸŊ{÷ÖūŊāÚŠ'ĸá|G]+„"Ģ(¨ØtÂɧ%˟žl­u0tĘvzæōu6Ô펏åXGĶĶβM7ã ęũŲ˃yS>_cyî`ž›œxrÂēØNŠmˇ3šžšc;š-UO¸,č¸PčT9ēvíšPîŦ𲎠´NĘIÁfī G@¸ŽÎžƒg¸ģŋƒdŦá::qB>Âut˛—_~yB {ō{„ëÂŖ ]xá… utæįw^B9ΒīJøÅæĖ3ĪL¨{ã7‚ē–-[&ԅGƒōŋf‡;õŒrŽ w’ũ/Ö$€uР׎ā¨_ŋ~B_kĮsLBŨÔŠSƒ:žœ†ëÂ!GqDB]811ųKÂuᰙđ‹%Ļãį+šcŋũöK¨ wŽũd°á—oį~p…aá:rq8Ü …+á/Ķá¯ô!2'6ņ÷( ^ėÃáJˆ-#GŽ´ ­ų[Ęßčƒ:Č.ŋnŨē a?ĖĢ^ŊēũûO(xŠF4C;\Jáí 0?üû.i v‡ûhčĐĄ~ĩéŪŊģ=ÆN:ŲéTb“ƒk‘›œMˇŪzĢ]ˇ¸” ūķw,ŒTČzėÂá„Ā!\Q~.ņ|áXÉm…8†ÆĩæØY\Rxr%…áo'á|Ô% ãã9†kŠ~T¸ā ŸÂ˛.Q*ąÉAĸuęũ{1 ĸ)÷"÷:ûĖ>"–9Mc=áū æą¯Ér6!8ąŒīt f!Ęú#ũšį~Ēc…į;ŠčZ!„YEAÅĻ+nŧ-G.&_ ĸŒüø3ķĐ͝™ÛîėgNo}‘Ųi—_<]N&æ3MX^ũÆMÍm=ī3o87aNlēôš›sŦįōLŊ†'嘟Û"g“EG\‡Uˆâ$œŗ)ŒŧqAÎ_č—°Ø„át ˇŲ{īŊ͇~˜ĐŽųžˆâ>BDåƒĘd× Hfv­88WÛoŋŊ=FF&C¨A˜c!gŅĸEū"y›<∛\ˆN%ō Q›ØgįdrÁåâ‹/ļ ā bžī"‚f͚Ų:_lzōÉ'­Ŗ÷.(\zî>Ģ]ģļ=—p€æøÂN-GœØÄ’‡Å•°Øä>&ų÷>°|~Ä&>pđáće,įÄ&rHųn&ŋöč…Ųį;ŠčZ!„YEAÅĻdŖŅųbĶÃG›}ö˙ĪyQļÜN6dn˙˙:5 Āû>ü”Š}L}ŗUhš#Ž:Öŧ;ež­Nb“%‡¸ĢÅInÅ&3MčÎGÂÉx‘waUánęYp+áZÄ҉ƒ–ŧ3„B…CîXÖQJĒØÄqšĐ$/'!t23Üsß/¸ĄÂÎRGQ‹M„¸ąį ƒ€D›8nÜĖ8ŠHDŽ Hؙģ‡Ht 8‚™&/Tøāq‚‘›˙tĮ‚hCžĄdāŠōĪĄ+ŦĶ…đ…‰›Zĩjeë¯ēę*ۆ{ķ‰›ÚļmkÛų#ē.ˆ+?bŽ`„5?”°;–sbĮy/YņCVEŪāF]+„"ĢHˇØDøÛŽĨ˘Ũ÷ØĶôüŦ?{YЎ<Ö›\ųhÁ׿ūĄ/˜&§ūû…ėØFMė|‰MBlÄuX…(.ošÂ=Š…îđbíB]y)&ŒĘ „‘īeæĖ™æļÛnŗ9˜Īˆw.ĮĄWŧ0ōÄ(_ˆ+„íâŪÁíâ„)—Ô™ĐY'ް$@f>ų†œ+&? rōWTųiČyÅ~NFH:š­(¸Pš/ãĮ7]t‘]–Đ,v†ÄØäxBā[ĩj•yŽĐ.ŽŖŸâ‹ķ‡dđŦÛæēĐ> 렎$Ņ€n?q3š|^8ŲȝÅu$LŒs¸lŲ2+*q ŲĄõˆ"„H2}ÜqĮYĄŒëÍuw(BŅ€ã"Š:ķjÖŦi÷˙Š+ŽHpĀ9ąÉļÅvÂį—O*—œDˆbø\ēsä\Tė_˛stČ!‡X7!ã7÷îĨ—^j¯kŊͨQŖŦÆŋŦ‡öl7 áŨhxü6Ču…čĘ5zå•W‚ã"ģ;Ļđˆ{Ü'Ôãx›4i’ gėŌĨKp~Š2ŒŽđA܈>ø _•pžŖˆŽB‘U¤[lB`ĸŨ­wŪ›ĐŠĘ^{Û:ōœ/„w>9Č}ä։“Čߞ+ķįĪOXâĄß†‚k-<âā\b0ļI¨%.'ŽŖ[†í‚ ÷n"ÚrŪ K%T0Šß´iĶ‚\˜˜›Ū|ķÍû.Ʉ;_ƒ°ÛĸΑ˛IØŪ5×\c…cbįm| íq@áŌĸ-ŋBđƒļ8Špį­Yŗ&Į6] ˙.,# įŋŖÚķ“LˆL/ÛëׯŸ_•5Ä=ģŖk…Bdé›f­Ü`Nhūīčb$gÄšvÜŅrxmĶĻí5vūã¯ŧŦsˇJ{ØyüKč˙'ˇĶØ‹m‰MBlÄuX…"ÛņÅ&‘ˆ-×O°Ÿ.p VŠR%Šđ™-Ä=ģŖk…Bdų›Ļ|žÆ<öōģæŊ) rÔáLzuė'Á4‚ĶÃĪŽ0×tčfnčt§úŌ;v>Ž&ģŽŠ Ö{ßāįĖUí;Ûļ,7cŲOA=Ž(–q ÃÃåŠãĖ+Ŗ§ä˜ŸÛ"ąIˆĸ#ŽÃ*„Ų 9Ãķ#Q9+›4i’Ôé“MāÚÂÁæF•#ːsCōpnE‰˙Ÿxâ vV÷ėŽŽB‘UäWlÚ‹Ä&!ŠŽ¸ĢBd+ŒLĮßHWČMDø[6Cˆ!šŽÜ9!Į ú鯨Ã,ūŲ]+„"̐Øô_‘Ø$DŅ×aB!Df÷ėŽŽB‘UHlú¯Hlĸčˆë° !„"ŗˆ{vG× !„Č*$6ũW$6 QtÄuX…B‘YÄ=ģŖk…Bd úLą ŧ}á%Û į á?įBQ4ÄuX…B‘YÄ=ģŖk…Bd-L7ī/ü1‡ø’m…spÆĀūéB¤‰¸ĢB!2‹¸gwt­BˆŦĸÛ럛AcWä_˛­<<~ĨéųæūéB¤‰¸ĢB!2‹¸gwt­BˆŦbūWÍéf˜ÉKÖå`˛ĨpėgšiĪ…ĸhˆë° ‘†nzôčaæÍ›gū÷ŋ˙™^Ŋz™ģîēËüõ×_~Ķ<Ķ­[7ͰaC3räHŋ*_L™2Åîë¨QŖėôāÁƒíô7ß|ãĩ,<~˙ũw{>8/ūų§ųôĶOí6GŒá7ĩlÚ´É\zéĨfÛmˇĩŋécŽ9ÆĖœ9Ķof~ũõWĶž}{SļlYÛnˇŨv3}ûö5˙ũˇß4OL›6͞ķ+¯ŧŌ¯*0_}õ•iÖŦ™]˙™gžéW[žøâ ĶŽ];ۆröŲg›gžyÆo–+FmęÖ­kĪĪ–[ni4h`fĪží73K–,ąûŗãŽ;Úļ;í´“šõÖ[íĩķYž|š9餓lģmļŲÆ.u˙PĮq4oŪܯJ+4ģîēĢŨĪ=÷ÜĶ<ųä“~“<Ķŋ{īŽ[ˇÎžūŸlŊ]ģv5-[ļLzߊœÄ=ģŖk…BdƒÆ.7—›cĻ,]ŸCˆŲÜ Į|ÕSsÍCã–û§E‘Fâ:ŦB¤^(ˇÚj+sŪyįŲé[nšÅŪ‹īŊ÷ž×2īđ"Īē{ė1ŋ*_4jÔČė°ÃfŲ˛eöe™ũnÚ´ŠßŦĐšíļÛėqŧũöÛæ—_~1ûėŗ‡’‰įŸžm{ĀX‘b‹-ļ0åʕŗûœŦëâ*VŦh§īŧķ΄vy†õÔŽ]Û¯*0IÕĢW7ĨJ•2ģīžģ_mžūúkŗË.ģØísÜüKa^^;vŦŊžB'žxĸ9účŖ!‰kī@ åZPwØa‡™SN9ÅTŠRÅN_{íĩ˙­đūøãSŖF [W¯^=s衇Ú˙×ŦYĶŽ'\7Ú /ŧđ‚Ũ&÷Â÷Ķų푔u¸ßųÍ7ßl§“ũÎO?ũt[WĄBķĶO?ųÕƒsEt­BˆŦã¯ŋ˙6Ž[aN8Ũ<ōū*3nÁÚĸĖæVÆ˙sŒC'~iÎ4Ã<ôĪąs„EG\‡UˆtÃ'Î÷ßo9UĢVĩ/œ™ÄįŸnŒ{îšĮNwîÜŲ OK—.õZ>?˙üŗŠV­šiŅĸ…v‚Î̝žšĐ" :ÔļŊâŠ+‚vß~û­=\;N°âÜ#.äG˜)j9䐤b“;ÖŪŊ{ķpC!Bå\L[oŊĩ™>}z0Ī­ņ΁ÛŦuëÖ B Ž1DĨíļÛÎÖ;^~ųeģ|‡‚ymÛļĩķžxâ‰`^qÃųEÜúōË/í4÷~éŌĨ­Ã)ŋŽÃkŽšÆėŧķÎöŪãü”/_ۜ{îš~ŗÜMœ—qãÆųUÂ#îŲ]+„"k!ŒŦכ‹ÍYÎ4õīžœļrT§ˇÍmz›=ŽmmĘėy°Ųyŋ:fĮJû›Ũkˇ0ûļ¸ŲÔēúqslr,W˜…cäX:'Dņ×a"]lܸŅėĩ×^æžûîŗĶ„ŊņĸPeذaĻUĢVæ„N0W]u•yüņĮ­# !y„踒*ä ‹ēüōË­@ƒÛ§bËöãDœ(L#Đ vôéĶĮ[ÛĐæĄ‡2/Ŋô’_•/Ø/œ6NĀ!‚ã&Ė[oŊeĮ„@9Ø×ʕ+ķpŅŽpģ0Gq„uō° át<ōHpŨyxú駃y“&M –Å™s˙ũ÷'œwˇL2^ũusá…ÚķÎ~ā>{đÁũfæ“O>ą!Žõę̝6~øaąé‡~0wß}ˇuhq\mÚ´ öaȐ!ĄĩũōÆuEäÁŠvÖYgŲûĮėׯ_Žëģxņbģ~ö; σ:Č:°Âၒå9&b%ķÂakÖŦąĄ“ásIá}æÎkîŊ÷Ūā\rã€s˛æîgÎŽ-Bųũ\ũõ9BÕ¸o“9ö¸FėįŦYŗėôgŸ}f>øāƒ”e„ æˇß~ŗmqG!ÖÂą¨đAū°=„UMÜŗ;ēV!„(DčPŅšĻL§„¯Ĩ|É$>ž/ļtą-Θ1Ãv4éĖĶŨ~ûímGûâ‹/ļŲ?ūØv „›qV!Ō /Č<‡¸E^yå•`šgâ÷Š_p]„ÁEŽįš•ŒįŸۊZūúÂeá…ļ-šĻNj˙OHБG™4ŒÍëČ­Ŗ0ā™ũâ‹/Ú˙ãŲ{īŊ¯p°ŊN:ķpĨ z0߉wœË<ĐēÉxā+ÜxãļÍi§fÛ|÷ŨwVtpĮ€7—›vš0Špa™dĐ¯đÛēâÄ40`€ujųm˜›8'~WĪÖ¯_´ual~!ĮR;į†ļôŸ’huÉ%—˜Ã?Üļã˙a˜˙ÆoķÜž#ļ:Čcäī…œP>Üû~;Ę AƒlŊ )áĐBW¸ÆáãáŪ ŋ‡ į@0#Döˆ‚ÜÉŽ‹_î¸ãģ<ũJ„0'č!ô!G!ą)÷pžĸˆŽB! ,ÖŨģwˇą÷eʔ1|°ĩÔķ…xŅĸEū"IĄSHEGãēëŽ3uęÔąr ĐæKáĉ͆ üE…%€¸ĢÅ Ī*îQ^ZMpßâÄĮ‡ÁQ0Dōq–I&6áĒ@8Áqķ‰jQĩjÕ˛ËāL)ˆHÄú'ĸDŠÂ„p;ōÜāAdāpîåAȁÃÅfÂĮŧråJ›[ˆ:'!r!P1/,6΄+™0\7>>tҏļä:Bø@ČöAƒJ\2€‹‡\PŦÛŖãØp@Q‡#É9lV¯^ĐŽ$ī\ī+VØięzĸÂ9—GÁ3Õõ¤oåÎ%ûv0ûˆāÃ5ĄîÍ7ߴ׊öô§ÂĐß ;…čk%›Æo¯ĩs !.ŌĪã㠃ûÚ%‚g˙qļqƌcöØcģ¯|`tTļäīâÚ¸ü^įŽBXķWá‚3lÕĒUÁ:ķŠģĮĸ\‰â_8OQD× !„yKöŗĪ>kíæ|‰ĸsBįû6Ą …™l‘/}sæĖą_án¸áûå—íņåX|,õt„°q !2›¸ĢÅ /ü ܧŧŦūÄK-ލ°& ĸ퓉Mäȡ΅î9b> ĄK"œįdrŠK‹g7Î,æ! ájBĖ`ÚĪŖCž(懅Ŗ÷ßßÎķÅ&G˛e¸dÛ8ŗpŗá*BT@(qęE›p¨)U‚p>ząĖ;īŧãWp¯pŧˆ1l‚ëLšŦŽ=öXģ^BĪĸŨ|ÜãŖÎ2„3BÃŽ*@Ā _ļ‹Ä>DÁuJ&6qÎøxˆĐÅņ Æ!\‘ø= b.¨ü1a>ũBöƒsâ@€c]áũt÷˙{Iˆ_ˆ`¸īzöėéW‹\—(ĸk…Bˆ`Ÿį+_€Î8ã ;" Ŗ œsÎ9Ö~ÎÃ:*gB:Ā&ÍW4¯›nēÉÔ¯_ß~eÛoŋũėËšpZ­]ģÖ_TQŒÄuX…(n‚ČUÄK|øEįE2ĸÄ&žEÔá ƒ+Š—Ü’*6į‰QũWãyËČt.‡y8öpvú/8o ĩ“ G›¸.íÚĩŗũ‚đu<î¸ãŦ; š7ožr,—ą —ŽM^Tœŗ(™Ø„8ˇīžûÚzō@Ĩ1.ŨēuŗËš°Į0„;ōAÄø¸‹THHE2ą‰tĖc;8ô8ú[L'›ÂĄzÄ*Úû!|˜Ä!Æ|íė/ípÄ! 9á7Uá>ōķe兏>úČŽƒ¤âÉry‰˙ā|G]+„BüøÂFŦĮŽm§ŒN!¸ŠHBZËrēY°`ít1Ü-6n×1B$ŖÃJį$ŸBˆâ!ŽÃ*Dq‡Äį,A” Ô¨YŗföŪ%ÔČ'JlâYšŦÎåĐ)Éb“ƒM¸^8>ū8\7˛žˆųķįĪæ9áȉ@€ ‰yų›_;õÔSƒ06BŌØžÛöŖ>jįģŧN$FŗlŲ2î—ą ÁˆzڅCá•Ī›œÅČh~ņ8l—Åų >ēšÃ¸DōÉÄĻã?Ū Ŗáį¸Ö5.™ØÄvÁÜy$æwIÕ”RÁ(z8ĩp­ŗ,ĸy—pͧ*'Ÿ|rū˜\]8Ųy*č;2#‰ÕŗÎSŅĩB!˛B|âËRõęÕmĮ‡¯}$™$<-ĘÎ] oITå‡|R|Á™…­Û4bōC!ŌO\‡UˆâÄ …ŽÄāŒf†ãÁå$"| x'lœį&._ępÂ0đBž@l I6õ¸nÉaȐö WīļSēø°ÂČyÉD—t@ˆŸ:TãXSäE"Ԋgnßž}ÍSO=eĪ/‚ BEØ}Ųe—Ųu†čd.LQÁ…ž‘l‚ķĖ:¨g„4ĻÉųË/ŋí¨ŖOÃ>réįā‚fžsà $"*•/_Ūî#ƒ“ ĸЎũį:sŊ!‘Õ8NꚎí2Ę`8É<יzŽ•ųäâƒķmø‡Ģ‹cr‰ãÉiÄē\AD!ĶÁŊČ=Čē8”.]ēX‡ŽĨpÎ(Ōp} §ĢTŠ’Ũ†?*`2’‰MÜŖˆ@l›ãĄoč ŗ}ŽĶ…ņ9ą‰BŽ3ŽËã„āä(čÕĢ—MƒĀčqŒ’Gîäēt’—áîŪ‰K:žšÂąG]+„"+@8B@ĸŗ€ „°DgŒÎ /„§l€¯–¯ŊöšíØŌŠŖŖÉLž”2˛ _Ŗũ„ŸBˆ‚×aĸ8áĨ><:š+ˆ¸58œü6áÂ‹Šƒ‡Nq…į "GAÅ&D-ˇN>ŦĶĻMKqŒãĀ1”lā’§;ąÁD"dap%ģĐ3WÂĶ8P€ūJ¸M¸ äÚ./ŲĩÄeUãc”sš‚āâBĮ(„¤Ņ_đ×å š+„Å!…ëÎ\čŅQÅ_O¸ :¸Îáú\Ąīu!.܇7î˛Ü¸Ļ’‰M„Ņ!…×Éž Äšéɓ'ÛļˆM\ŖÛoŋŨ†UōģaÄ9D?á9)Âëä7Ã}P”äVlBLã>wŽĢl„ķEt­BˆÍBŪ°Mķ•‹/kt"ãËBæÄŋpŽøĒÉW:D'žĘŅŲeôžlōeÔYō…ų#ŽÃ*DqC˜ xvâ Aįr0°šdÅ9lŧ "€°>¯p!ņ˛NRč‚Āh_„75ƝJ+|˜rį&ÎĖG.ž­´'OǜDôGp$=ũôĶvD8Îį2üÜÅ9åŸkWüđ&Ļq$ņėfÛĪ=÷\ʑ˙Ę\;„Ä\cŦ×]{ÎĩŋMW|8ĶmŦcĮ ÆHn´u‰æ9ūzÂō˜į`Ÿ¸wœŗ‰‡ū}æ@ Ĩ ‰šũ}‹‚€§|Ø÷{`\~#ė§Ą›H Ÿ8>wŊ‹Zdrānį™ÄšŒÂ…trMŗ•¸gwt­Bˆ–t’u“´[?Ąb|9"__ų’˜Ē“'’C'“0;F#"ėŽ„ątϚ4ib;:nXe!D&D&@ˆ(!y„ æ—ŧˆMō ÕÄÕæžįCn/„¸Ü¸Ã6WâžŨŅĩB!JŒâÁ&\7ÄÅãZÂĘÍhŒŌ†u_>|}åŧŠˆWĩjUÛYiܸą™‡¯nčg!D"qV!6Gx^„ÃŽ(„@Uč›>„ú!Žˆ/$-Ī+|ĖŦXąbÂēH,žÉ\wŨuv‘.ŽÖ­[ųÚ˛•¸gwt­BˆŒ‡átI.Jō҃>ØÆ˙“đš¤˜Ä›ģĸčáĢa äE ĪÃ͉Ŗ#̡̯÷ŗ%–QÄuX…Ø!\(<ŠŖļ­\šŌo&D‘A.&>’š{’ܝ$LĪˆĻ8žÃ÷ø5×\ã7%˜¸gwt­BˆŒ‚8üI“&Yņ‚đ-ŦÉÕĒU3įŸžy衇l‡ ›íŧ%œg$%%a$Ë,]ē´MĀI-r0j y „Čâ:ŦB!„Č,âžŨŅĩB!Šr1:.˜ŖŽ:ʆÄņ/ĶĖO–œT”}ēZˆ’D\‡U!„™EÜŗ;ēV!DĄÁ(pŒ‡+…Ņá‰#Y"ŖÆ1Šš|„Č+K3fĖ0?ü°i׎9âˆ#ŦÅ=ÆžSŒƒP%DĻ×aB!Df÷ėŽŽB‘/mâŸ}‚mŪxã~uJp*ã,uËōą ‘?]#Åâj Ÿŋ¤H„‘[mĩ•m3tčPŋÚÜ˙ũ6ŧ?ŧŽķÎ;ĪüöÛo~Ķ\ƒˇI“&æ´ĶNŗ˙īŊ÷ė=ĮčŦ>ôĄ¸w^ũuŋJ‘!đw!ŠčZ!„I™7ožu\rÉ%Ļzõęf§v2͛77wŨu—?~ŧŲ´i“ŋˆ/?sįÎ5O?ũ´}™B(-]ē´Ų˙ũÍYgeúõëgƌcÖŽ]ë/*DĄ×aÍĪ?˙ŧ]īƒ>h§›6mjŇīžûÎk™wxAĻÄ %šaūüųf‹-ļ°yØņ‡iÄßtÃsk›mļ1ß~û­Yąb…ũPrꊧúÍō ^>ē8Á ąé÷ߡŖl:Á)î~üņĮæôĶO7 4°ís#6ũđÃöú1ŋđˇī„N°ÛĖ­Ø„PXĒT)ë$eŲú5jÔ°h:pbÜĄ‡ܗ”˛eËÚųŠîwūŪ;É›ČũĮü=öØÃÜvÛmĻW¯^Ļ^Ŋzv^AÛįž{ÎŽƒü‚Āõa?W¯^íĩ4vģô­¸'•f@ˆĖ$îŲ]+„ GHItĖéüĐyDhBpBxbsâŗĪ>3/ŧđ‚ißž}đŌÂčˆgžyĻũÚ˙îģīڗ9! ‹¸k~áe–đQ@ĀÍwīŊ÷z­Š„ „~S„Ā"Ėđ|) pRņ2Īīpŗāv)l7îšįžˆMūžäFlrpnr+6ü-Ė‹Ø„pO{ ĸbŲ˛ev€ÖFŽåÃŽ$à ąŒx›LlÂuÄ(¸ˆÎ˙Î;īœršáøã7uëÖ5ũõ—Y˛d‰ŲrË-m^ÁTtčĐÁî.<ĄŨM7Ũ”āÁu“ ܀ Qž|yëtaÄČŽ]ģËŨqĮļ/õŧđ?ķĖ3vúÅ_4*Tˆ|Îŧ˙ūûæä“Oļ.‘ÂņmŸ}öąį÷ ûęö΁ŠwīŪĻf͚VŒâˇĪų!*,HĮāÂt8ā+Zķ7#JlęßŋpnhKˆ—OœØ„CŒ­đõĄJéā˜ĮųkŲ˛ĨŨGŽוķÎūōœw„Å&ÂŅØļ[/Įå@điÖŦ™]žö„'ģv„s"ū8ŗÃ9Ę}ČŊ¸xŲe—%Ŋ戁{íĩ—Ũ×<ІAsîĸūŪžúęĢv?zôčáW™7ZĮŋ ÜĢÉÄĻd ø áØÚm¤*<\߂ Ŧ‹Î][úTuęÔąÂS*p7ą„Á !2¸gwt­Blæ`ņ§CÂd:„$ņ&ŋIŊÉaC’o:¸Bˆœāyíĩ×ė /ZŧXWĒTÉž€vīŪŨŧųæ›6wŠqÄuX .Üx€hrŅE%ŒÎH¸/Ņ[oŊĩũĀ€Ø‚Ā>ūųA;‡QŪz뭄z@ą"ĨŋmJÕĒUŽ›ķĮüŨwßŨŠQN Bė ŗhŅ"û7Õí÷"ŋ[o˛{ĪqöŲgÛ6<>„ÁQĮßh'JĨ›øŊpČAÅo„sIž%øâ‹/l¸§ŧ~q!s'N´â–ƒ‘xįĖ™L'Cb“™ ŋĪ(ĸk…b3ƒ¸ž*ŌÉĄ“G§’NũgŸ}VŖ­Q@VŽ\iFŒaētébsŋDá˙ĖŖŽ6B„‰ë°Ļ—׋û¸€HŠ›„¤ŲÉ TĒ~BŽŠÃ‘ÂË:āPAt`~ø…;?āÖBÔácHQA("‚YØq„ŗ WŽ" ¤A‚0s÷Į-u÷ŨwÂC2ą‰äčü‹ŗÁ(qbS2p5…Å&@LD [¸pĄ=ÖIŅŅ;™Fč'6ŽéŽI˛ÃNĨ0 Ā@Ü]Š@ÔÃÅDōtî ūE˜C¸ R‹K‹u…… ÎŽ2æã&MB×ʅ†YŧxąÍ[Æąp qbÎ;\Zîú!v!Ŧ9pŧ"ÖĻ*ĶĻM‹t.ŁÎí>ūøãÁoI‘9đûŒ"ēV!J8t‡ fķg€¯žtųĸ7zôhÛÉB¤^Üq9ņģÃõ„û‰/ö¸Ąør;JBov×aM'¸?œķˆ~œ&­Zĩ˛/⊈›p¨ ēøĪ—G}Ô.SPąŠ8 F8p€]{íĩ Î×&†æpŖŗ%›(¸‚E‹ĸ0Å&„-`€ÖéBâŸgÚ—›\!„-ŠÜˆM&L0G}tÂz]AÜ„ ö;ėžrā(B K•74Ŧ‹6Dęnžųf+9'āĸ˜3‚Ž?„*'Šqū|)\>účŖČõÆáÄ@ ų›„™ŋÍ(ĸk…ĸAį ›;I- Qā‹+~ °qĶiņķt!Šōđež"ō>‘†¯æäk!/ųĄøj.˛ƒ¸kēÁņŅ­[7+d0r!uėbh2ĸÄĻc=ÖæĻņ‡ˆwÉŖKĸØ„ļ!0†`Ėą÷gęÔŠļž<ĖsĶaȑD]2ą‰Đ<ūÅ vöø§ØÄ‡*DDr”TĉM,ËŊ¨‰›ŠcĻŋâÂ在„ķ‰i\Ly…4,ë˙ũtį/UÉMRķvíÚŲļ„Z˛~Ɋ 2?¸‘ö¸ŽEéäBä~ŸQD× !DCŌItIFJH˙2Í|—”RQ2āK=šukâEą˜*./#äņō'6?â:Ŧé¤M›66d)ėĀXˇnÍ“C¸U˛QbŽę§sÚtä‘GÚų…!69Qĸ(@ø@œ!”)ˍQŖėņ¸‘ķܰöáT€ĮåRJ&61(‡ËŲ„ķ8ÕčtÅ)6‘ŗéå—_ļ÷[ĒŧZqb÷õ.īŪSŒųîērĪ!豟~Ū;ęH(Ÿ,9=įŽ}ÄIæÃũí'đvš¨Â Įš_}gÛuÎ(„&Ö÷Č#äXg¸†ę'Ī .ŋÔôéĶũ*!DĀī3ŠčZ!„ČčäāLÂĄ„S‰—PœK|ÄɄŖ)U!DɅQŊƌc_ĐŨN„ņ,—@Ü"ŧ0&DÉ!ŽÃšNá‹í>GÎBĨ8™Î{ÃŊÆK<Éą…‘ĘFH7nœm‡8@ž2ę ƒ"ų6ƒO0M)¨ØÄ3õäĮõ’…ŒíáėiÚ´Šu0]zéĨA#Äa@ "i9ķÜŽ{8Š5ĸ ‚2nFēˆ@BÂv~×Lģ[~û ¸9Įn{,Į4šĄŽģî:ooņÅ&ōÆąœkmÛļ Ä&Ža“Nlâē=üđÃ6üžiūրËĮÅšāŪ 4p}qlˆCÔsũŲGū^…Å7—đwĸ$"O8y|íÚĩ !‡yœ?Ž5Ŗ’€í2?ŲõG§ŽÆ(8߄4s˙ŌžséFl„æÍ›Ûũ"?b"Å%ŊįˇQT(A¸™ ŋĪ(ĸk…ĸ˜ā‹9•Čņ—NžŠŌéŖŖE&—ŧS‘}ā:!#FōÂE[^Œp6"ā.øôĶO ”+D-qÖt‚`Áė¸čÜK?b/ãádöũû÷ˇBĒ‚ŖÖ3&/ˇl§šņ,3nÜ8 hâ:ŦB!„Č,âžŨŅĩBˆŦ†/û$Ī%„„)rĖ›¤Ū|Õ'ņnQN"„™Îįž{δoßŪüņĻlŲ˛6šmĢV­l’b†˙ņĮũÅDˆ¸ĢB!2‹¸gwt­"Ģāküȑ#m͆ Z×Ŗ7‘ÄÜK—.õB‘’ãŊå–[LãÆmˆqÕĒU­XOØ1kåũ¸ĢB!2‹¸gwt­bŗfŅĸEfذavD&ō”)SƜtŌIĻ{÷îfôčŅĘE"„…ČâŋÍđáÃmØq“&MĖ.ģėb*WŽlZļliŋõÖ[æë¯ŋöË â:ŦB!„Č,âžŨŅĩBˆÍ†_~ųÅLš4ɆtđbÃKNĩjÕĖųįŸo‡'îß˙í/&„"ŦXąÂŧūúëÖQzĘ)§˜ *˜]wŨÕ´hŅÂtíÚՌ1ÂæĘÛÜ‰ë° !„"ŗˆ{vG× !J,|íĩ×l‘ŖŽ:ʆÄņ/ĶĖĪÖ¯įB‘éŦ^ŊÚ¸pĮwXŅi÷Ũw7å˗7'Ÿ|˛éÔŠ“ũžlŲ2ąM\‡U!„™EÜŗ;ēVQ"Ā‘„3 ‡N%K8—p0ádÂŅ„ŗI!DÉäÛoŋĩav={ö´Û°aįwļĄĪ:t0¯žúĒųâ‹/üÅJ qV!„BdqĪîčZ!DFB.%r*‘[‰ r-‘s‰ÜKä`"“BˆÍ›~øÁŧķÎ;ĻwīŪ6ņøŪ{īmʕ+g5jdnžųfķâ‹/š ø‹e$qV!„BdqĪîčZ!DFĀ(pŒĮ¨pŒGHŖÅ‘ネEN!„Xģv­ũqīŊ÷šŗÎ:Ëėˇß~öƒDũúõÍM7ŨdŸ%ķæÍ˸}qV!„BdqĪîčZ!D‘ķŋ˙ũĪL™2Å<đĀöKõnģífÃ%Î9į3`Ā3cÆ ķĮø‹ !„IYˇn?~ŧš˙ūûÍšįžkjÔ¨a?ZsĖ1æ†n0O>ų¤™3gŽųķĪ?ũE‹Œ¸ĢB!2‹¸gwt­"í|˙ũ÷6,CawÜqöāˆ#ް//ŊôRVŒB$„ĸhŲ°aƒ™8qĸéßŋŋÍõWŗfMŗũöۛ:uę˜ëŽģÎ<öØc6āīŋ˙î/šH€N§UEEEEEEĨdžŨQHlĸˆ!|aȐ!æ’K.1ÕĢW7;í´“iŪŧššëŽģė—įM›6ų‹!„i‡įĪĮlh.žøbs衇ZŠ W]u•yä‘GŦģöˇß~ķB!„H@b“i„Ž;B‚ÂB‚“B‘WæĩŅôzsąiõāLS˙îÉi+ĮöøĀÔēúqŗo‹›Íîĩ[˜+íoö>Š]Žvé* zO6mm†~°ŌlüU!äB!DIAb“…!o„žĮ—`Bâ#DŽP9Bæ„Bˆüō×ß›‡Æ­0§œny•ˇ`­™Ŋjãf[fũSŪ™ķŊé<|‘i9`ēųlõ˙”!„"‘Ø$D>!I7á$í&y7IŧIæMRo’{“ä›dßB!DaņĐ¸åæŠ'įš)K×įf6÷ōü”¯Ė)L3ķ%8 !„Ä&!rÉO?ũdFŽi:wîl6lh]K‡v˜šúęĢíPŌK—.õB! BįÎ8ÃL^˛.‡“-Áé´ĶR'„Bd8›„HÁĸE‹Ė°aÃĖW\a>ø`SĻLsŌI'™îŨģ›ŅŖG›õë×û‹!„iŖĮ_˜Į­Č!Ād[šũÕEæá΃B!2‰MBüÃ/ŋüb&Mšdîšį͞eKŗË.ģ˜jÕĒŲá zč!;üķß˙í/&„Bäizá9ėl+īÎũۜķđL˙ô!„"ƒØ$2Š9_n0]†ūO‡zFŽiŌYö>éJSf΃ÍĮļ6ļémŽęôvŽ6…Y]į܇g™Ac—› B‘ ö™bf­ÜC|Éļ2sÅ{.„B‘šHlŖëô{o™ÍÃđ⏕Y3ēĄ§õŸn>]А^ø­ŨîKī}œŖŽ Eg!„ŠČīķ’‚Ø„Č͏Y 3næ;oⓝš­ļŪÚÔ­× hwSįģlģoīeŨHĖ7ki Rüø3;¯KīvēßāgƒexėEŗå–[šVm. æ!6ŅŽÁ‰'›ą3Ûy=ũšŲzëmlōp?s[ôŧB!2‰MĸØÉoįyÄŗlöڎwä¨KgyûŖyvģWļŋ=G]A‹:ĪB!R‘ßį%ą §Ō”Ī×$Ė?â¨c­ģÉM#"qŪÅ9F‰íÖ÷AûėōŌH;}éÕíí´˙Á‡9BëÜ4bĶvÛī`?ԄÛÕ=ļá?ûS&a^^Šž—B!Df#ąI;ųíY”cˆMÖŦ•c~ŗ–g[W•??ˇEĪK!„"ŗ‘Ø$Šüvž%6 !„Č&ōûŧ¤¤îÄæ§›RÛnL´āsÖųmÍ;îhŸu–=ø°#Ä&Ę §†š˜\!ø‹ī}´I5yĄČßäĪĪmŅķR!„Čl$6‰b'ŋg'61RN§^÷›ęb*WŲËvjéûí~æu͍éŠļ e˙kšË¯ī˜#¤ W˙!v´ŠRĩšíŋ2fjPīÄĻv7u2īîoj|˜mËWßūŋ”cģy)ę< !„HE~Ÿ—”ÜŠMäVÂątáכĄ/Ŋc۟ŊÜÎīÚw}öátō×AN§ģ>nŨJHå+îfˆS'ąI!„ČN$6‰b'ŋg'61…Qr.šú&ÛÉĨŖŒÅßĩÅŪO[žĀ^wkwsũm=lžæŅáuíēôhįŅųFHēėēĻ|…]íŪw§ĖˇmœØÄ6Ésqūe×ÚÜwĢdļØb Û9÷÷5ˇEg!„ŠČīķ’’ą WĪąz OĘŅŽMÛkėŗäŪLßzįŊæĖ6—ähGrpÚŊ9ņS;-ąI!„ČN$6‰b'ŋį°Ø4fúÁüŪũĐÎ?ë‚Ëė4ŖéTØuwŗoõÍôekÖQŗÖ‘fûvĻm‡a›Ãm­Ęí´ŗéqßŖvډMĖ{oę î'æŸŪúĸ„åķRÔyB‘Šü>/)š›>YüŊ)Uj[ŗWĩ}ÍÔ/ž Úđ…ÜL<ãîú‚wņU7Úi‚ģv,O¸]Ųr;Ų˙3Ob“B‘HlÅN~;ĪNl ąė RÃ&§ĶŖ§}nŪūpnBD¨N>ÍŽÃ ī|lŖ&f›mJŲQw>œ˙UŽõRœØÔōœ rÔŅÁfūüÜuž…B¤"ŋĪKJnÄ&Ę7ŪfŸq´į9JrožŠ¸‡™;˜vį}i?â00öÚĮÔ7씝hŸĄ÷<ōt°>‰MB!Dv"ąI;ųí<;ąéĒösÔ!úÔoÜ4aŪËŖ&›oīeNmÕÆ:˜šå)3–ũdÛ<÷öëXb`ō0]sË˙cīNā­žö˙ß{š?×Ŋ\4iRQTR‘DW…DT’P”(%Ō ˆŠ"ĨA*Ĩ[”4š4Ф$IҍyÔ¨I‘øüĪ{]ßķß­S9į´Ī>{x=õ¨ŗÖÚÃŲû{öwíĪw­ĪzÚ&Íũß:• ؤ|OūãjÉ]ųŠ•SÔ§ļ0xœHzĪ—*ŨŧuÜ|KƒGOļį{>ĻNKåę6|ØÍÔÕNtĘÉôõ†ũIũŲĀ‘ã“ûiöRįWÚ]÷?dˇßu¯=Ūáy›øųwĮÜW#’—Ū…–!c§¸‰~}j įKĸÁ&dēôžOļlRî%õU€éŠrŦnƒ&nĢD¨ĄÁ&]­ÕĖϊ•Ģ%¤tĨVfĩŸl7:‚M€Œ’Ūķe<ΗD7‚MČté<§6ØäRǐôsC"(Ē 6igēOoHnWŊv˜SŽ í^§:‚M€ĖŪķe<ΗD7‚MČté<§6ؤĨę×úš—Žé3cájû×Ygģ6%BÕ6Í˙8ķŸ.ī„ v Sõ‚M€ĖŪķe<ΗD7‚MČté<§6ؤü§Ÿūw˛¯€ËWĄŸ_ė7Ô ŧØūų¯šû˜ūÕ÷ޝrTčįÚ÷6rģĐ 7Ũîy°™Ģ ’ĸld†ôž/ãąpž ēlBĻKīā9ĩÁ&mÍ|^ÖlŽŋJļ9­UĮnnĮũŦ§ę7õ.—č4ØâYE ÃyĸCōŽu›™ĄRˇy.QˇîH´ĸ×@¯ˆ^›éŌlJkŅāTģĘ}4oYŠ6ŋ(W“úĒ,\ˇ7E{F‚M€šŊĪûxųîįŽD+z nëŊĀy@!؄LŠ`S,‚M€yîƒUöęŒõ)ΉVúN_oß_éŋ< ŠlBĻ#Øô˙ Á&‰,ŨrĀjöųĘæŽŽÜŒÛh+úŨoOz ôZ€čE° ™Ž`Ķ˙/›'ĶoÆ:kûė)በŊĻM›f^xĄ›õ‹įŖīŋ˙ŪSu!§OŸ>.ą8ˆ›S–.]ęv¯9˙üķíÅ_ŒĘYDĘõĐCšĨ­Zĩ˛­[ˇú]Č ,)¤@“Nąnɒ%nų_ž|ųlȐ!äI FlBLĐŦĄ:uę¸+œŨģw‰iõ[ļlqų'˛dÉb?ü°Û!€ŒĸĨrZ2§ĨsąpžL åRžÄ… Û¨QŖĸ&Á98>‚Mˆj‹/ļ;īŧĶ™zöėéļJŽ5;wît;íhy]ũúõŨė,ÂEIŋ5ûGIĀ”‰gڜŖ\šrVĒT)›8qĸß ĸÁ&DĨE‹Y͚5-WŽ\ÖĢW/;|ø°ß%æ(yš–ū)pĻßmÁ‚~RMŗ{č.ftîÜŲŽ9âw‰[ãĮˇ’%KÚÕW_mŗfÍō›@&#؄¨˛páBˇŗ›–(!čĪ?˙ėw‰y œŊúęĢ–?~ĢRĨ ƒd@š­ZĩĘ*VŦč‚-˖-훂‚moŋũļ]|ņÅvã7ڗ_~éw™„`ĸ‚fųT¯^ŨōæÍkũúõ‹Ë “OW ‡jEŠq_&L˜āwāJŨ­[77›ŠoßžöÛoŋų]Ž^“AƒŲ\āf÷Ũw~a›Š”[âæ›ovģĖôīß?!ˇ6Ö…ącĮÚe—]ærP(Á+_>Íū-]ē´ŨtĶMļaÃŋ9áéB•–Ūkšēr$jwX96!SĖ;×ĒUĢf °×_=!ƒLĮ3yōdĢPĄ‚Ûmį7ŪH¨ü€ã;tčĩiĶÆÎ?˙|1b„ß ΁ŦK—.nöW“&MÜî° ˛6!ĸ>ûė3—Wá /´ÁƒL9Ųŗgģ`œ–(w•žhōú*TČęÕĢįv7EęíŪŊÛÚĩkgY˛dą'Ÿ|’×€"؄ˆøôĶO­råĘV°`A2dˆË¯€?÷õ×_۝wŪéŽfwíÚÕöîŨëwÄ!}Ū7nÜØ-3Ÿ8qĸߌ4øá‡ėŅGĩlŲ˛ŲŗĪ>ëv‡‹`2”ŽČVĒTÉí3lØ0‚Lé´|ųrģ˙ūûŨ@š}ûöļcĮŋ Nŧ˙ūûnÌf͚Ųūũûũf¤Ķúõë­aƖ#GëŪŊ;ŗ†Č@›!f˘áļdÖNkoŊõ–=zÔī‚tXˇnûōqŪyįŲc=f›6mōģbÔļmÛŦV­ZV´hQ›3gŽßŒ0Ņœ:uęXîÜšŨæ$,é ü6!ŦĻM›æ\+VĖŪ~ûm‚LDKZˇnmYŗfĩFŲĒUĢü.€ĸ%æšqĶĄC6͈-U×Î~Ę#ųæ›o˛,aD° a1eĘ+_žŧ]rÉ%öÎ;ī0`‹={öX§NÜ”ēuëÚâŋũ.€(ļfÍ—ͰL™2|†gm^ĸŲØÃŧûîģ~3H‚M8%“&M˛˛eËZ‰%lôčŅöûīŋû]´ž={ZŽ\šėÖ[oĩšsįú]QD3õš={v÷/33Ÿ.œ]qÅŽč˙ ũ6!]&L˜`W^yĨ]zéĨî* AĻčđķĪ?ہíĸ‹.˛ë¯ŋŪ-kDÍ`ŌL&ÍhŌĖ&Dk4ËIŗ4ë ¤Á&¤É|`—_~š]vŲe6nÜ8ŋQBģū1Š/îžĐhg#‚š”‹I9™´ôY9šŊ”@yœ”ĪIy”ß ¤Á&¤ŠK 0)Ф€bƒL 4)ā¤Ā“P D"KģËi—9í6§]į´SvŦĶÎuÚÁN;Ų€?G° '¤@…Ļ’kМ–Ėiéb—–Ôii–ØiŠ–Ü2Öūũû­Yŗf–7o^üGl:tčuīŪŨÍJkذĄ­_ŋŪīBlB 2)Ųˇ’~+ųˇ’€#~(y¸’ˆ+™¸’Ō*š8 ü&NœhųōåŗÆÛŪŊ{ũfÄ }ûöŲŗĪ>kŲ˛eŗG}Ô~øáŋ 0‚MĄüīŧķŽKŠYž|yvb‰sJP[ˇn]w•ļS§NļgĪŋ vîÜiõęÕŗB… ŲŦYŗüfÄŊĮO>ų¤eɒÅÚĩkgģwīöģĐ6ÁmˇüöÛo[ąbÅŦB… ė`–`V­Ze5˛ŦYŗZëÖ­šJ §@šņÎ?˙|kĶĻ[z…øļeËkŌ¤‰eĪžŨētébđ쐐6%0™Ūzë-+R¤ˆÛŪwƌ~$M›6Ųc=fįwžË/˛nŨ:ŋ ā6lØāv-+]ē´-\¸ĐoFœ[ŊzĩÕ¯_ßræĖiŊzõ"/" álJ@ڍlذavņÅ[ĨJ•˜âcėØąÃÚˇoīōQÜ˙ũėŧ'Ą%č}ûöu3[ēuëÆŽŸ îģīžŗš5kÚ\`ƒ âx$,‚M Dž!C†XÁ‚­råĘö駟ú]€dJfÛĩkWˇäÎ;ī´¯ŋūÚī mŲ˛evõÕWģŲÁZ’ žüōKģņÆŨ…=Ĩ*Đæ+$‚M āȑ#6xđ`ģđ ŨĀįŗĪ>ķģ'¤œ#}úôqWiĢUĢfŗgĪöģ@BŅyĩsįÎn6́ $ā„4{\ɒ%KÚøņãũfâÁĻ8öË/ŋØë¯ŋn pAmy¤—ž\ŊņÆV¸pa—H~ōäÉ~ˆ{_|ņ…•(QÂjÔ¨a›7oö›ãš8qĸ•*UĘʕ+g3gÎô›ˆ;›â‚Lũû÷ˇ|ųōŲÍ7ßėÆ@¸(?ÉčŅŖŨ ų˛Ë.ŗącĮē:ˆg´–-[ZîÜšŨg Vš7jÔ(wŅFé ŸâÁĻ8ĸOúõëgyķæĩęÕĢۂ ü.@XM˜0Á-Ў†C‡uŗŸ ŪL›6Í-E×Ļ ģwīö›4 rhęĸ fČ-Y˛Äī@Ė#ØdRN]mŊíļÛØr§œUĒTąüųķÛ̝žj‡öģ@ĖQ`I&špÂI3Ņ5~˙3§ÕĢWĪž˙ū{ŋ 1‹`S Ķú^ŊzYŽ\šÜ6ģ‹-ōģĨŲt:5p~ņÅmßž}~ˆ Z*§‹8Z:§%t@FŅņõ /¸„ķ7ļM›6ų]ˆ9›bĐO?ũd={öt_čĩ%ũâŋũ.@ĻZētŠÕ¯_ß œŸyæÛšs§ßĸ’’~ki“’€“S‘ôã?Zûöí-kÖŦöøãÛŽ;ü.Ä ‚M1DWžēwīî‚LuęÔąoŋũÖīD•ĩk×ÚÃ?lY˛dqį-[ļø] *(yķ]ŧsįÎä CĻŲž}ģĩhŅtÁfīŪŊ~ĸÁĻpāŽ$éüķΎēuëēY#@,ŲēuĢĩjÕĘ œzč![Ŋzĩß2ÍĒUĢŦbŊnÃe˖ųÍ@ĻØ°aƒ=øāƒ–#GëÖ­››Ų@Ŧ ØÅöīßo]ģvuƒŒ{îšĮ–/_îwbŠ’í>ûėŗn怒Ą2;@fŌŽ`ú¯Ī¤ž}ûÚoŋũæw2Ũʕ+ŨÅFåčÔŽÃJ,@´#Ø…”TšK—..ČtīŊ÷ڊ+ü.@LĶlŊ—_~Ų- ÕŠķįĪ÷ģ@†ŌέĨK—ļ›nēÉÍ ĸrtV¯^Ũ (`C‡ĩŖGú]ˆ›ĸˆCvęÔÉ]aÕV˚ÖÄŗŸūŲ^{í57pŽ\š˛Íœ9ĶīaučĐ!kĶĻ[š>bÄŋˆzsįÎĩJ•*YŅĸEm˘1~3Q`SØŗguėØŅ™xāōŲ áh)˛ožiŊŗ˛eËڇ~čw€S6kÖ,+T¨[ÆË.™ˆuͧOˇĢŽēĘÍЛ|ØzôčáΙÚt†T €H#ؔ~øá{â‰'\ŠE‹ļeËŋ €4Ząb…5lØĐå:k׎ æ€ŦYŗÆílYĻLˇM<č´tT;뜩Yõ[ˇnõģ!6e _|ŅZļléĻō/-CmŪŧšû;éŪŊģõėŲ“ō…ļiĶÆ^zé%ŋ € A° aC° aC° aC° aC° aC° aC° aC° aC° aC° aC° aC° aC°)ĖvîÜiÆ ŗŲŗgûMÂčĶO?ukģvíō›$˜ŅŖGۈ#üjxįwläȑ~5†`S˜}ņÅö—ŋüÅęׯī7ŖģīžÛũ­-X°Āo`rįÎm˙ú×ŋüjȞ=ģ{îš~5†`S˜mŪŧŲž{î97nœß ŒŪ}÷]÷ˇļeËŋ @‚éŲŗ§Ŋđ ~5€?tīŪŨ^|ņEŋ€ C° aC°)ĖÖŦYc 4°×_ŨoF pkëÖ­ķ›$˜–-[Z“&MüjhŪŧš5mÚÔ¯ Ãl 3r6‘AÎ&r6'GÎ&@¤l ŗũû÷ÛŦYŗlŲ˛e~€0ŌߘūÖô7 ąÍ;—]`“øüķĪíŗĪ>ķĢČ0›6›ÂLŗ-*UĒÄŽ8@ëÜšŗû[[ąb…ß ÁÜyįV­Z5ŋĀnŋũvĢ^Ŋē_ @†!Øfäl"ƒœMälNŽœM€H#Øf?˙üŗÛkįΝ~€0Úąc‡û[ûå—_ü& fĶĻMļaÃŋĀ6nÜČß ĸ6NŋūúĢmÛļͯÁĻ0[´h‘(PĀZļlé7Ŗ-Z¸ŋĩ%K–øMĀ)SIŸįC† qĮšr•+WÎ.¸āˇ|3(gžyĻåʕËũ?[ļlî˜ŧîēëŦQŖFÖĢW/›:ujÔĖtũíˇßléŌĨöŪ{īŲķĪ?o͚5ŗ XÕĒU]ūŗ‹.ēČ.ŋür÷˙ ÔĢWĪZĩjeũû÷ˇ)SĻØ÷ßīî'ڔ-[Ö.šäŋĀôˇ}éĨ—úÕd‚MaF° ˆ ‚M§ŖGēmÁ;tč`eʔqA$}1ģīžûŦwīŪ6vėX›;wîI—ĄėÚĩË-íüä“OlĀ€îŊá†\ž”’%KړO>iĶĻMŗÃ‡û7ÍģwīļwŪyĮ6lhĨJ•˛3Î8Ê+f5kÖ´§žzĘ^{í56l˜ ˆÍš5ËÖŽ]kß|ķûPFm/ŋü˛=üđÃVĨJ+X° ģŸ *¸×JŋĪÁƒũ‡Ž8‚MĀÉlDÁ&@š9sĻÛĐAĄ+Ž¸Âž~úi›3gŽ:tČīšnŋ˙ūģ͟?ßētéb+V´ŦYŗēā͗_~éw=eڝącĮŽnÖŋ˙ũoĢQŖ† |éBH¸‚\zmôēéq4‹ëėŗĪļk¯ŊÖh?ūøŖß ˆ` ĄhÆOˇnŨÜ,Ë.ģĖúõëŅŧK[ļląŽ]ģÚÅ_ėf<1ÂÍŦJ/mLĄLZö–3gNkĶĻ›]uäČŋk†PkōäÉVˇn]´Ģ]ģļM˜0á”~'Ä6‚M€°Pn"-#Ķr4-¯Ō­ĐĸĨhZšĨŲ7 ¸DÚž}ûėšįžsš•|đA[°`ß%âf˘áf;-ZÔFŽ™Ļ|Hû÷īˇgŸ}ÖmiŽŧKĘÅŠĶ‰č5l¤š‚_|ņ…Kũøã[õęÕ]Páoû[r‚l-ŠēņÆ]ōéĐRĢV-7ûFũsįÎí’jëßjÕĒŲc=æîSy‘~ųå˙aO‰–ąiöŌųįŸīž‡rE-K쿚kÜōˇīžûÎo>†Ūå[Ō,ĻûīŋßÖŦYãw‰ ,ęXPž¨QŖFš÷‰`ā„4+EAåįšūúëíŦŗÎ˛ŌĨK[“&MŦG6~üx[ž|yē—LmܸŅ-ÁŌÎmĘctå•Wē@˙ųĪ܎iŸūš“4ŅL+¸ČŅķŒvƒ r3•4ëxŗ”´#œ–ß)@+Éņ§OŸnå˗wīvŗ@ü#Ø8†GšiŖ€’fiw6í<öŅGšĨ[M1qâDˇ{[‰%,_ž|ÖŽ];[ŧxąßõ¤”ĮH›ž={ÆÔR.-1Ԍ1Í Úž}ģĢĶl/Í$˟?ŋ ŪĚ`v™Ū>}ú0Ë Îl8ģvír‰ŗķäÉãļ’īŪŊģ›”Ų´ŦŦ}ûön‰^ŠRĨløđáöë¯ŋúŨŽņŌK/šūļ$-Z)Ŗ|L .)“f“ibŦīöĻ%ĘQĨ]ė‚@âÁ&Hp h ÛyįįgGķōŦŠS§Zåʕ] éÕW_u;Ąųš7oîv™ËŒ$äáöôĶOÛgœá’nĮ Í2ëÔŠ“Û påʕ~3âÁ&HP{÷îĩÖ­[ģÄŪĘ´cĮŋKÔúōË/­f͚næĪ|\ßĩkWģęĒĢ"˛Ü/ŖiF“’€Ī›7ĪoŠ C‡ĩ\šrš¤đˆ/›  8Đåcz衇bz9Ķ'Ÿ|âv;ģõÖ[Ũm >mŨēÕīs>ũôS÷ū¤5OUŦQÂķ9r¸ßņƒ`$–nžųf+Wޜ}ûíˇ~sLŌŽmJ ~æ™gÚĸE‹üæ˜ŗmÛ67ãįã?ö›â’’Ņk×ęÕĢũ&Ä(‚M ´›œ’+П%؎5Ę9õĖ3ĪøÕ1IÁ@-kL$¯ŋūē›ĄĻĨˆ}› Ž=j+VŦ°I“&Ų°aÃŦgĪž.`Đ´iSkĐ UŠRÅ*UĒtLŠS§ŽkSyāŦsįÎöÖ[oŲgŸ}f›6mōâ”ôīßßōæÍkŗgĪö›bžf6)šy<,Ÿ0`€Ë9oÁĀÔxüņĮ­zõę~5bÁ&H#%Ō?~ŧ )Iuá…í/ų‹[ TĩjUŪTŦXŅÍC|¨[ˇnÂåŦ"Aģ?jŠĒfæŅ6e€üŅ ,čfč m,o+$Í$;vŦŨzë­vîšįÚ}÷Ũgīŋ˙žũôĶO~רĄįüđC˟?ŠDŧ´RĨJYßž}Š98āvw˚5Ģ5oŪÜļmÛæw‰ší¤ÎtjܸąíŲŗĮīD%Ü˙Į?ūᖅ*˙ģīžk[ˇnõģ8ÍXjÚ´ŠÛŠĶ—4ë^; –/_ŪÍđ œ6…‰ļĒ֗š/ŋüŌortE)WŽ\6cÆ ŋ @)o…r5hĐĀ6mÚä7Į-% W9}ŊũöÛ~3u.ŧđB1b„ŊôŌKvÛmˇšˆ—_~š[žž`Áŋ;€?(ˆ¤™7Üpƒģ¸ōg4+ŋPĄBn÷S…`SčD^ŧxq{ã7üĻc|úé§.9ã÷ßī7Č`[ļląjÕĒš/ĢsįÎõ›†ž¤—.]ÚĒV­jk׎õ›¨qņÅsžÔLĪ>ûĖÚĩkį–)–ų˃€DĻ1é7Ūč–ĮũüķĪ~ķ )q¸Æ¨sæĖņ›H‚Ma úC=äW×ë¯ŋî’2ÆûVä@4=z´åČ‘Ãžūy;zô¨ßœp”SNKëT€hĨeŽģvíōĢ“}ûíˇnļ^öėŲ­J•*$GÂĶßËUW]寤éIü=}útwŽÔ9€SE°éizšråŌ´ÛÜŖ>jÕĢWO×@@ę)¨ŌĸE 7 bá…~3€(ÖšsįT'5{cÔ¨QnÆŪe—]fãÆs›s‰dãÆVŦX1kßžŊߔ&Ú(#_ž|\Œœ2‚M§@W€rįÎm›7oö›NJ_€ĩŽžmÛļ~€0Ņ^í´ŖĀ.3 Ä ŲMšŲĄĀS"/—EbŅnŖÚ ĻwīŪ~SēhŲšˇ<ōŗéF°)”#Bkە‡)=vīŪm $Q/ÖrÕ§žzŠ@ŌL'mШQ#wžâÕüųķŨxTÉôÃIšŸ”įđ–[nqģ*V›ŌA[‰+ÉđŠ^AúîģīÜÚøí` íV¯^ívązå•Wü& d˙ūũ.§Sž˙üsŋ)ėôˇS @w‘€Ô"ؔFũúõŗRĨJŲĄC‡üĻtĶIŧlŲ˛nÆ€ôųņĮ­D‰$5‚Ņ*T°:uę¸Ų@,ëßŋŋ[&Ē#eäȑ.¸Ĩā-ŠA°) æĖ™ãN´k׎õ›NŲŨwßm÷Ū{¯_ ´c•ōJhÉ vŊũöÛíļÛnK͞@4Ņ._|ą­[ˇÎoĘpŗgĪvãā7ß|Ķo ‚MФenēŠ4eĘŋ),4SęĘ+¯dV;vt;Īąk€“ŅŦĻÚĩkģā43œKtQĨyķæ.ŌöíÛũæˆYąb…[ŽūÜsĪųMƒ`S*9rÄŽšæ{á…üϰŌZ heTĸG i{såcŲąc‡ß)((}ķÍ7ÛO<á7QIãP̀×E•}ûöųͧ`—Ō?(‡ŠžĮC°)š6mj5k֌HbŅyķæš)Ę˗/÷›x4#°H‘"öūûīûMpBĘņV¨P!{įwü& Ēŗ˛eËúÕ@D 4ČōäÉc‹/ö›bÖĪ?˙ė’īW¨PÁ-}$‚MP~¤Â… ÛđáÃũύҠAw•ˆiÉHd]ētqģ5@¸höÅYgåž ‘¤eá ´5kÖøM1OãUå?ÕøzõęÕ~3 Îl˛˙ kÔ¨a-Z´đ›ĸŠÁW_}ĩû˛ $ĒoŧŅ&L˜āWĀ)šōĘ+mŪŧy~5!4öԎÃĨJ•˛mÛļųÍqeđāÁ.éųÜšsũ&@#ؔDy´ļü×_õ›ĸŽ$ųōås[ˉFIûĪ>ûlÛŗgß§¤^Ŋz6räHŋ;7ëׯo×^{­›YŸĻL™âRU(7 1$|°iŌ¤IvÁØ?üā7E­… ēö’%Kü& Ž­_ŋŪũŊ@¸5mڔ-ۑá”{ķ–[nq3ę:ä7Į5å\Ô9ŧgĪž~ %t°éûīŋw;Äâ´ųQŖFš$É;wîô›€¸5}útģá†üj8eĘ-Ķ­[7ŋÍĘŊæšk\ÎŖGúÍ aĶĻMvéĨ—ÚŖ>š°¯$Š„ 6$L°IKæ´t.žˇ[ÕR:-ŠĶŌ: Mš4ÉĒUĢæWĀ)ųī˙kõęÕķ́tŅ’9-Ķ:¤ž–ŌiI–Öb_›” \IÁ㝒…+i¸’‡ņfÕĒUV¨P!ŋN‰ō5=Ú¯ŌLIĀ• \ŗåvJŽ‹ÃJŽ$â€Ø•Áρēífa{Õ#GŽØu×]g:t𛀘˛cĮ{÷ŨwŨĀŊI“&n›d]ņ<ķĖ3™Ŋ l´Ä>[ļlqąi2Īīŋ˙nO>ų¤onŨēÕoFhŧ^ŖF wŪßŋŋß ˆqlš7ož[žHW˜vîÜ龔5Ęoĸ–”ãĮˇĮ{Ė ÖĪ9įģũöÛŨîs °iĶĻŲęÕĢŨ2ēqãÆų7€tyîšįŦYŗf~5jGĩ Ø5×\c{öėņ›‘zM}ôQģôŌKIÜ1*ރMšŠ{Á¸å͛×%ė˙üķĪũ.ŠōĖ3ĪđĀ)QĀZ3*uaH+]Č+UĒ”ĩlŲ2áĮš‘0wî\˕+— <ØoDЏ 6 >Ü .ėŽ8%: €ęÔŠãr ™iúôé.÷‚Øk™ÜŠPōp-ˇ€ôPā[ųu”Fyŋúę+ŋ pBkÖŦą‚ Z׎]ũ&d -wÕøūЧž"Ā1 î‚M‹-rSmŋûî;ŋ)aiŲQéŌĨí•W^ņ›€ §YuJžĢų|ā7@ÄuīŪŨ*WŽėž°N˜0Á͘øöÛoũn@ ‹/ļDš¸ 6)į‹va3fŒß”đ´ĩsîÜšmęÔŠ~atōōË/wŗëöíÛį7@Ä͚5ËræĖiëׯOŽĶ¸A„UĢV…ôŽ5{öl7N9„y>ėÆĘ͚Ú|€Č‹›`“ĻÄW­ZÕÚ´iã7áÚõKƒ¤•+WúM@ØMž<ŲÍ2|íĩ×ü&Č˗/wĻO>ųÄorųäŠ)bô›?~ŧ;§Íœ9ĶoB&ĐŦDų‹-jk׎õ›Q n‚MZŋ­)ņGõ›bȐ!îÄL>+d$íŖĀæüųķũ&Č›7oļ‹.ēČåu<‘† ZŖFüj$¸aÆšĨ–äöŠ> p3÷o@ô‰‹`͏qãŦ@ļsįNŋ ĮĄSĒUĢæfƒá6cÆ wõ÷‹/žđ› S,]ēÔōįĪoŊ{÷ö›ŽqāĀ—€øŊ÷Ū훐 zôčáÆ˜Ė ^“&Mrãv–€čķÁ&M‰× †ĢMЧŲ_7ŪxŖĩjÕĘoNɗ_~éūĩdĸ>´tnäȑ~ĶqiˇLõß´i“ß„Ķļm[+^ŧ¸›‡čöõ×_[Ūŧy­OŸ>~ “Ät°I ‡ĩ$LĶ›‘6{öėą‹/žØŪ|ķMŋ HízȌŅBV^xá׌˴ĐíjÕĒåW#AčØyđÁ­|ųō$ Ž!6l°%JØc=Æė}ˆ1lRbš5kZĶĻMũ&¤’f…)¯Îŧyķü& ͚7on÷ŨwŸ_ §]åʕ+gUĒTI×Ŧ”C‡šŨé´Í=‹v:ĶøRéH{”“T9\õę" ķÄl°IW¯šæ;räˆß„4Đ:wM;NĪ`|üņĮ–/_>Īˆ Ũģw?å0ĩG_X‘84cžRĨJv÷Ũw3žŒazīîŋ˙~+[ļŦmßžŨoDHL›´Ķ•$[ˇnõ›”_yå•îJ.×^{­;Ö¯€˜Ĩ.k,Z´ČoBRPâōË/wŗtY‚ž{î9ˇåŠ+ü&@Ä\°iíÚĩné ˆÃëŪ{īuWō€´úæ›oÜ.OĘqņ¤oßžœĀēuë\ËΝ;ûMˆqĘMĒī ŗgĪö›,Ļ‚MšySĒT)ëׯŸß„S¤+¸šnÜĩkWŋ 8)%Q}ņÅũꄧ/-ēĒZžūy3fŒíÜšĶīV üéņ>üđÃä:ŊGĄģq 4č˜ĪRõÕmâ!h8uęTw\ÖĢWΆî7GœžÃË/ŋėW#čoõŧķÎŗ_~ųÅoBœøöÛoŨ ļūũûûMˆ3gÎt§ÔîH ˜ 6ŨsĪ=n 62†–%^pÁ6~üxŋ 8.};įœsl׎]~SÂ;ũôĶí/ųËqËŋ˙ũo7nœ“°ŅûĸĮQĀ% /ĖÕĢWOūYKg‹+–üŗúę6ąūĨúķĪ?ˇŋũíovæ™gē™ ¯žúĒß%┤Z_t›*VŦh}ô‘_8 Ī ũmŽ=ÚoBœųîģīŦ@\T€Š™`SīŪŊŨZzÍĀAÆųōË/Ũ6Ņ:)fá…nļ!RR°éŌK/uË3B‹žÔdĪžŨūųĪfXâRå6l˜û"øŗ`“úę6ąžĢ¤S§N.höÉ'ŸøM™†`SlëŅŖ‡=ōČ#~5bœ6HŅxgúôé~â”.ĒęģDãÆí×_õ›aÁĻO?ũÔræĖiëׯ÷›Ū~ûm+X° íŪŊÛoŽņß˙ū×4hāWÃūl*Sό_íhļ"Ę%)lŠmÚ´q¯mFōԃ`SlûūûīŨ2+ď#F¸qåüųķũ&ÄšƒÚ-ˇÜbÕĒUŗøÍ€0Šú`ĶæÍ›-wîÜ\yаļmÛÚ 7ÜĀ•œ”víŅŦC¤t˛`“ē ˆøų#&Nœh×_ŊuÖYŽũÜsĪuˇ7nܘÜGÁ=mÍ}ĸĸ´ūnõí4øŗ`“úę6ĄķÛļmsW€ŗfÍꞏžpˇjÕę˜ú?üān7mÚ47ĢH}ÔˇPĄB6pāĀä~˛cĮ{ā,K–,Ž0Zž§Įų3ĘŲץC—Œ>xm´Ŧ:ôĩŅgVž|ų\û5×\ã‚<'ĸãļN:ļtéRĢZĩĒũã˙påÖ[oĩ5kÖĶWK •ƒKAø`dŨēumõęÕĮô“ĄC‡Úe—]æ–ōiɆrc/ؤ\@š-Ŗ™ēOũ^Ī<ķLŠeŒĘ?Ĩßåī˙ģũõ¯u÷=`Ā€cú ãé˜Ũŗg_¤ŋ}ũŊ-_žÜoB‚PnB}ūęķt˖-~3 Lĸ:ؤAwšråėĨ—^ō›Á´”F_L}ôQŋ HĻ/ÜŖFōĢa'6)€~ÅWXŽ\šl˙ūũÉõīŧķŽ &\wŨu.@Ą%m Ä(hĄū×^{-EâqŊ XhŲž‚EáČŲ¤å Üüë_˙˛öíÛģįĶŦY3÷{éų(ø#šq—”ķ­cĮŽ.'†-Ē˙āƒ’CŸįúŌŽdŲē?ūī˙ūĪŨßīŋ˙žÜΧįTĄB÷Z4jÔČŨöŲgŸuųÂÄŲ°aƒë§`—3zÜÖ­[ģ„ė'ĸßE+-iŧãŽ;ė•W^q<ŊE‹M^N¨/% Fé>ĩk§[÷ĢßC%ô ĢîCũ|Ļ^Į´ęƒ/Á,4ˇB)¯Ūß`öÔO?ũdgŸ}ļ]uÕUĮĖ›3gŽ{CƒMO<ņ„ĢĶIĄ‚×6ājĻ“~ũb|äČ÷z=öØcÉuČx5jÔ8fwGÄp5ĻÔß'Z ”ō(’ģ 2FÔ›tˇxņâŦ§ÎdĘUĄ/‘Ę›øô…Į§ ˆ‚ū2ˇ`)VéŌĨ "(@áH4{HËÎÔ?˜ŊJ}yR˛ņ¯žú*š>Á&Íø9ŪĖ,}iĶĀ<˜m›B+ åfšŠ#ú,×q ē)˜:ĢëĪ( ŖY@?˙üŗßd7ŪxŖ[ū<ī´›VŦXqLŊ–ūŠ~ÖŦYîįÚĩkÛi§æ‚īž;īŧĶĩíŨģ×íVĻÛ)`äĶëlŌÍBķéwĐīr×]wšŸĩ\N÷Š™kķæÍ;éė/d,Íō† ‘vúÜP@ZŸŒ)q<ē( ąŽōPÂ'*ƒMÚMķ•+WúMČ3fĖpK~HĐ_ģvíNēT)‘hŧõÖ[.ˆ $ĨĄ4SįÉ'Ÿtyƒl×}¨ŸŠv˛ Ĩ ĪmˇŨ斖Ŋ˙ūûĮ´j°I_ČôåD:-õĶ2 ‚MO=õ”×Ë\PJKĐZ¨esę¯ßMKŪēuëvÂY[Â… [ɒ%ũj§e˖îū‚Χ´›´¤-”–´Š>˜u¤×IÁĄãŅl'õ]ŧxą[Ū¨˙o<ŊŽA°I3“Ô/ŧųE¯‹ˆĸ`Ŗž Į€^sÍt{īŊ÷ÁIƒ¨ 6;(qU!:é “érEÍ ŅR)e)ũY°I ‡•ˇGÁ% WāIA˙ĩ ‚@ĄÁĻ^Ŋzšē&Mšü˙Ž!N5Ø$ځNƒnŸūūĩÜ@Á2Im°I÷Ģ鹘m9Ę=6|øpŋQJEŠI‘#ø3:'kŲĨžø3_iUÁ&%M Ũ‰ŅE_ ¯ŊöÚ‰r‘ØôÅ÷xK‡ŨɂMJHĒ …ūžDųx<Đ,P 6(°Ŗ6MņÍ&ĶŌ9YBQ‡ G°IKŋô8ZÖJų–Ô/X6—Ú`SĶčÅ_<Ļ‚)'ē}@[•Ģ–á…ZētŠ 4…Ξ w°I9<ôŗ?ƒlíÚĩ.WVSbwŊÆzĪCßåŊĶŌÁĐ`S„<Č ˜0a‚Ģ×sēt[Í Ėv#Ø9š‰§€"ĸŸ>´Ų9ļ^úŌĒU+ˇIŽ?̐zQlŌļßJœËU„čĻ¯´¤dėØą~”ļ{g×ȔN” \ƒW ÔļpáB×Wŗf4ÛEuÚAAˆ7ß|Ķ.žøbW¯ū H)¸ ‡fA)ˇ“ŋüJE줅#ؤ„äŲ˛esy‚hQđEŗN•k¨hŅĸÉ žSlRFKĮ”[šotÚéMËã”ü;ĻŽ4k&˜KĘĻ]ƒ´¤IœsĪ=÷˜ü~á6i6•rKé5üņĮmÚ´i.qˇžĖęyk&R Li&ÖøņãŨNÚV[ŗ˙BƒMJ ŧßĘyĻŧxĘ]Ĩ~zÍ4{IÄUĀOŗČ4ĢFĪŠK—..åįûBÆRPũ‹/žđĢeô9ŠŊÆ”ĀŠŌōhí"ģ`Áŋ  QlęŪŊ{ōV͉ā`Ō umũúļ$é ËWI_NbЌL*-ŽSõ%éKÛw… Ûæöíí(AͰQ wîÜ õ÷›Ę3ä'VŅR9DüĢĨJš“Gˇ×•y¤t;åMSn ˙ūüĸŲ1 č˙zœ€ÚŲ. $äZŪP_Ũ&tКō(ÕŦYĶ˜ôœÎ9į7Ģ&t)œ–ęvūĖŅRąûîģ/ųgõÕîn ŌčūĀQNŋûŸŅc>ōČ#î9čļgœq†ģ/?ד‚6z>šv2 œŠŸŋ;Õģīžëę?˙üķä:5ëLK õØ ö(¯Ö˛eËBnų?ú’Ģ Ąúé÷Tî-Ü Ĩ™Q Ō*x¨ž Âé=ŅL¨PJūŽ@[p\(ø§÷ -ģųáÔh×Gõo"pã“zõlIŪŧ)ĪŖQ^K*¯§>ĒK‚ŒMÜquĪ=1w\Ŋō—˙W~}ÔWIįĸx?ŽDˇ¨ 6%ŠßĩM­ZŲ’Iyb DŦü Y"9rØá+üˇé /ā]t‘›…ņ¤uëÖnųbŧÛøčŖļėĘ+ŸdbųĄOû&[6;čåʋe[´ā¸ĘäĸãjQÖŦqu\ˆ ›"HSˆ5}x˙ŦY)N”Č]š]]ŗĻ˙6!žJzM•—fõęÕ~€ϤîuëÖĩ)SĻ$äZŪ:wî\ŋ:Žøüs[œ3'ã“(( höu<,}:8ož›ŅÄq•ųEĮ•f8ÅÃq vlŠ ­Ußōėŗ)N”Ė)ûĻOˇoräđß&œ%N.]ē´Û™ @|ĐÆúÛ.[ļŦåΟßm (Kú”‹.WŌ—åíü/Vßq‡mnÛ6Åy’’9EËũ•k'Ö­Š]›ã*ŠŠ–ÔÅÃq vlŠ ÍjúqŌ¤ū”L* Ø×˙ø‡˙6á) ĩvãō/ˆ}JX_¯^=—,ŊcĮŽqtēįž{ŦC‡~uÜŅŦĻ'NLyž¤dJŲ=fŒKë4̉ã*zĘîŅŖ]Ōpˆ‚M´đôĶíĀ—_Ļøđ§d^ųę¯õß&œ"-ŗiÚ´Š›ņg;‚ˆMëׯˇ† ēĨŗŊ{÷ŽË™?Ú1RģnܸŅoŠ; O;ņI•ķįÛÂŋ˙Ũ›bĮUt•x9ŽÄ‚M¤­Hũ~JæŊ'Čš PŧxqˇÅ;€ø´bÅ ĢRĨŠ•*U*îō5kÖĖÎã“č+ņ0>ḊžĮ€ØÁ'NqŌžÂI7c)ĪKŽ9lā~€82fĖ˓'ĩoßŪŽ9â7ĮÉŗeËf[ˇnõ›âã“č+ņ0>ḊžĮ€ØÁ'NqŌžÂI7ã­\šŌ-ŠĢZĩĒmÚ´Éo'vėØa5jÔ°2eĘÄüßē>¯ēwīîWĮ-Æ'ŅWâa|Âq}%Ž+ąƒOœâ¤}…“nd=zÔēuëæ’ ˇmÛÖ})Ÿzôčáf9Åę˛ēĄC‡ē]5ã1Չ0>‰žãŽĢč+ņp\ˆ|âDP,œtˇĪžmŗ† ŗĪß~Ûv%}QđÛã­pŌŦ͛7[ķæÍ-K–,ÖĒU+ûá‡ü.âĀ”)SÜÚɓ'ûMQmųōå–={v[ētŠß×ĸ}|ōã_Øŧ‘#íãĄCmËŦY)ÚãąÄÃø$ڏ+•¯ÆŒąiƒÛúéĶS´Åc‰‡ã @ėā'‚Ō{Ōũô­ˇė/IˇíŅēuŠļp–w{÷ļyĻ{,•/GNŅGåĖ3ΰÛ+WNQŸŅåėũËĒ_w]ŠúS)œt3ĮļmÛŦ\šrvÎ9įØwŪiīžûŽ>|Øī †ÍŸ?ßræĖiīŋ˙žß•öíÛg%J”°!C†øMq/Ŋã“3ūī˙ėŽoLQÎōí‡ÚIĮQ06éĶž}Š>*×_u•åĖ–-E}F—*W_mYĪ=7EũŠ–xŸ¤÷¸ēŦhQ+vŅE)ęÃYļ}úŠ]~É%ÉĮÕÃwŨ•ĸJĶģīví›>ū8E[F–ĮîŊ×=îÚiĶR´J‰‡ã @ėā'‚Ō{ŌT°ŠT‘"öĪüÃ^î9ķĘ+îJĸßG…`NÕķĪ?īļLŸ5k–[˛ōŸ˙üĮÎ;īę7%„ôŽO"ljR§ŽĩkÔČÆŋöš­š<9E‚MŅ'ŊĮU$‚MŊŸzĘW÷T¯n“ °cƤčŖB° ŌOœJīI7RÁĻY˛Ø eËύ÷ Á&œŠ§’x… ˛īŋ˙ū˜zÍvz-é‹D͚5ŨlˆŧyķZ¤//Ŋô’1Â>ųä[ˇn]BåQâūvpZąb…ß~ûí7讞ôĨSųåQzĮ'‘6Õ¸áw!˝÷ ÁĻč“Ūã*Á&/5ļūnüømĄ…`¤Ÿ8”Ū“î‰‚Mšyôv÷îÖūĄ‡\™ĐŋŠÛĒė˙ōKû°_?{æ‘G\ŋ.-Z؜¤/īAûˇ|āf3õĪÚĨ… ģ˙+/‚?A ‚M?Ėžmũž~ÚŨįKOJqŠ-œt#KÁ¤K/ŊÔļoßî7Ĩ°fÍ>|¸ĩiĶÆęÕĢg×%Ŋ÷ °ĶN;-yÚ{PžK:fD¯W_}Պ/nô›2Õīŋ˙n>ø ]ũõöĶO?ųÍ #Ŋã“›Ļ”<æøīķΧ8Ÿ庤θ{Ėõ{úá‡íƒ¤ãäĀ‚ŽMc G4ëZKüõ˙ҝŧ’â>‚›4æųōËî>5ö8QJ€ųŖFYˇĮOwŧ×§íKē­ßOc˜āwҘĮĪí›ôŧGõė™ü¸_ŧķNŠûJK‰‡ņIzĢ›ôšvnŪÜŊƯvč`›OŌ¸öå'Ÿ<á˜rpįÎvĶĩ×ēņC÷V­lP§N)î#(ĄÁĻ)IĮupŸĘķä÷UY:~ŧŊ’4n úéøßųųį)úéØ×x\}ôtģĐöĐ`“ĢC“&ޝžƒ_i)ņp\ˆ|âDPzOēĮ 6éJLҤąę‹\xĄȝÛũ˙?åËێ9s’ûé$U˛paז?ŠNŪ˙HęįļIlõQĀJm§ũíoîęĄū¯+>ūķŠ‚MZįŽ å=˙|w˙>ëŦc‚X:1_U˛¤kSŸâ… šû×ĪÔ­›ÜoãĖ™îw8ũ´Ķ\°ëâüų]Ÿ‹.¸ā˜@’KƒNũŽ ŒĢúOŪ|3ÅķLMá¤9Ÿ' ļ4ciíÚĩ~€8§ ÎÍ7ßlM’ž0E å‰ Ų‰h’ôŽOü`“Æ7WŦčÎÍšsä°_lK[Ė—Ī‚~{įΎ;ĢTqũ4ĢZãƒsÎ>ÛũŦ ‘FË&Lpãtúŋ;ūsŠn—åœsėÚ+ް˙ûûß]+4ļĐącr?…îŋí6÷XŲÎ;Ī=öy˙ūˇûšėĨ—Úžyķ’ûiV•ęõü5–ŌīĢņ‡‚iÁũé9iüS)éņƒĮũûé§ģįÜ÷ųĨRSâa|’ŪãĘ6éŊ‚>z¯ÔŽ÷Bī÷ô7Ū8æļÛęĩ?7ПŪ[ŊĮēÆ–ArųBIī§Ūŗ`|zaŪŧ)žCP‚ĮÕąđ×ŋūÕŊŋĢîņûî;ϝa:æt,ëąul̟n:CŠÍ$˙\‘4žūWŌ1Ĩۅ§A°)8ƒŋũ˙ҤĪ-˙yĻļÄÃq vđ‰Aé=éúÁ&ttŅÉlƐ!Éũt%OœFwŪ™\§ĩčhéĘ\PˇaÆ ģ¤`A7(Ō,Ą ^Wæ4Hôß/ÁIV'ģ`Į:͜ŌÉũ–Û+ ¤ŗŽôu:Ņ_Yŧ¸ë\|Ēqc×oîȑÉũ”ŦÜ?Ą*¨¤:%q WëėõûŨx’čÉ 'ŨČĐš .¸ĀíP 1i÷IœŖ!ĶÖ­[­lŲ˛nŲîĄC‡ü愓Ūņ‰lz¨Nw>Mâ­\8š˛gw'‘T§Y):ŸwlÚ4šŸ‚<ĩĢVuõŊūzrŊf4+Āā?ļ_lŌmõīē?žØëœžđ3T÷f׎) ~5L:TŒY4ŽŅΚĨôSĀL‚:›Ô¯â•W&–Oœč~gĶŽ7[*5%Æ'é=Žü`S¯ļmŨkÜĸ~ũä|ĸkĻNuũôŪKÜfū÷ŋŽßŨ7ßėŪSÕé}×8Ķ/[˙đ9ŅŦü Á&]ũfÜ8W§ąŦÆá:փ‹ĸš)§ąmë¯?&įiז-Ũí5ÛM?¯˜4ÉŨŽqíÚÉ}4CKãr/Ámƒ`“‚d_ŋûŽĢÛúÉ'n ­ÛëķŸkjJ<WbŸ8”Ū“ŽlR€E?kęšßˇū­ˇē ’vŲĐĪ:ąjšŽß/¸ĒēV=-Á&]a AŅŦ$ÍF ~ÖtsM‹÷o¯įŖĮ‚KÜq‡;AûÉp N°* 6é*˙¸ôå˕+Åã¤ĻpŌŒģ“k;vôĢ$˜qI_Ö´œNy’2Ë… ]N¸æÍ›gęķˆ&韄›4îĐΎVĒ”ĸŸÆ:īËũĩN“‚YDAס¯ë§eNA]Z‚MĮûޘî3)ˆÔ,éąˇĪž}Lŋ P$´´J?빇öĶ’ĻYÆ%˙›BgnŠč1Tŋō Í˙ŦÄÃø$ŊĮ•lŌØRA— `”ā=Ķ24ũŦ4 H) ÚOĮ„úĩjĐ š.­ÁĻ÷“ŽÍĐú Ļ1š~ūüíˇ]€(HE"õkTĢ–ûYËįôŗŽĐ~Z sA,6iÞĐ~Áߒ–œ†Ö§ļÄÃq vđ‰Aé=éúÁ&]ŅĪ ,kƒĸ>j â­ĸĢ>šÕ?é ŋxæÉãú-9)Ļ%ØT­B…õו)ã‚A~ŊŽi@ ›… p=;é÷RģNÔ 6)HĻå ĸ}uœ]AlĒ\Ž\Šz-Ėž%KŠúÔNēoņâŖ;wî„_ĻāJ—.ívĄĖ o'}!˚5kĻ=~´Jīø$4ؤ\2:ˇk,āM‚YCĪ5kvĖí5ģZcÍ6Ōō§`éũĀgŸM`“f†øõA"h?'Žōčč žōųč9^sų原rũ¨]ÁŠ`yRčųilåĪTR°I9ĨüĮUŽŨvÉûī§hKM‰‡ņIzĢĐ`“fÁëuÔ˛4˙¸ .œŪuĶMĮÜ^ŗƒ”ßI3ū•+4+€ôIk°ÉßqØ3ä”ë+´^Á"å {§G{žE 7ã_ũta5hŌ`h9Ÿ‚N d3öƒ›üãVŠ/Tēj -%Ž+ąƒOœJīI×6)`ŖŸķäČáf¯h{`õU0I?õWQž&å$té 6o7:?ؤ+I„éJŖKÁ$M˙ tA°IEËü4ĐĀ5xŽX„ö9Ņnt›ĸÛ­ˇŪjũúõķĢ$¨ącĮZá…]§HŲ˛e‹Ũ~ûíV4éKėĸE‹ü愗ŪņIh°I_ėuîVŽL” ؤäßZæ¯qnŖq‚ÎųAŌæô›ŽˇlRéÁ¤/ūAūJ}ų׌,ũ?6ŠhŒTī–[’ķû¨œŸ5ĢKúô9ŅntšŲíŗŌRâa|’Ūã*4ؤ™BzHô§ Ü[Ŗ†ëĢ@ ‚Hʁŧ_ē¸ĒcT˙?•`“ŋ]°36i֕ÉšĮT4ëžNĩjî˙A°IE4ĨšĐņôU.°`ŠĘ‰vŖI° @ á'‚Ō{ŌõƒMÁÕ˛ĐdÜĮ+ēzĸŠĮ:)Bč’4å*Đ}dT°I']-ĢS_ Ü4S)XūĖĖ $EW95^AåbR@-˜6M°)ö|ûíˇ–/_>ûå—_ü& JK׊+f}ô‘ß”!lįwžĩhŅ‚üL'ŪņIh°Itn ֜¨ÔŊųf`Ō˛&-Šv Ķō:ŨGF›”+G?+ˆ qT°SX03+X’Z4žŌėpåūQâiÍÆ–Íl:ąôWĄÁ&åfŌë¨qĄßĪ/ fǝr6iFŊ‚šĒ×{ĨúŒ 6Ëę4>ÖRË …ō¤Ē^ŗûüûVŅ ,Ĩ—6úÛĢ—Ģ'Ø DüGS×ĩMųŪŊ{“ë&OžėęV­Z•\ˇ`ÁW7gΜäēX—Ū“Žl r/’ĻėŪvà .G€p꧄Ú~?-GS[h*œÁ&å‚Ōũ늠ßOˇU[°ƒœN¨øũ‚DĄÁ ž`Sėyá…ė‰'žđĢ$¸ūũû[Ũēuũę°Ząb…]ũõV˛dIûėŗĪüf„Hīø$4ؤsĩvÔŌ9Ųī§q‹–ßO8ĐũŦegJ°ė÷ .ĻŊöĖ3Éuá6)q÷ņv{)é\Ĩ~ŨÜũŦ´Úa.E9Ô/ČĨC°éÄŌ{\ų9›´cœf0ųŒ‚˛pėX7ž}ã_š=¯ÛüĨŽÁŌŗĐMgÂl r†ÁË hĨúiDũŦ\KšÉ?/dS%ÅW?-ûĶĪ›Äƒˆâh‹a}HŽ_ŋ>šîҤVÕMš4)šn`Ō€Du/ŊôRr]ŦKīI×6é$Ē)ß:Ą†&,Ô`NĶÂ5€Ķl -c͉O'ß`-¸fi ĨûSQBÃāöá 6ig :5XvŧĶsŌ6Āē"¨Įv›iōĮî5Ą Au%K‹Đ„ã›bĪÕIĒ™3gúÕÜēuëėėŗĪļÇûM§LAĻ{îšĮ˛eËf=“žũúë¯~xŌ;>ņwŖĶr&ßĩ|0›YÁ-'Ō,kÛU§ŸĩÜ(4‘ŗwĢOčxG%ÜÁ&-×Ķø)4¤™(A~Ļ`‡ŧ 'Æ(AđB Í\ŌÎŋÁs'Øtbé=Žü`S°Ŗ[ƒÛoOĪjƐŌBhLŠÄāĒ R7?Ģh搎7Ũ>tˇæp›jUŠâ~ÖqôYôŪ{./“ęuUu 2é9*ā:>'Ø=š`€xņOåJĐļĮ?˙üsrf4Šn÷îŨÉuƝ ē7&×Åēôžtũ`“ŠÖ°'0x‚ŠŽÖ…&ŖÔšpÕkĢŪk¯¸ÂåRĐTŨā$:U=œÁ&•`€§\zl ÕŽ¤ŽĒĻÚoœ9Ķ-šSŽ8jĮÍô|4 :¸?‚Mąe׎]néʑ#Gü&py›f$}a — ČtVŌ9§]ģvļgĪŋ N Ŋã?ؤeKA2f;tÁHžtūM¤<ô…\Ŋ‚>åJ•rũÔG„(x  BĐ7ÜÁ&-SRލ ‡Ĩ6-ŅķĐcĢ>ø}fXëņ56ŅxF}tá,¸‚M'–ŪãĘ6)pŠ@‘^O$uŧč=SĐ¯įģĒh—@ĩëØŌNÅ%“>cÔG‰Ä5žÔûô w°iū¨Qn‰ĨR@”.VĖ]øÕck6•ÆëĄŋŽ œô\u\éøŅ})'kЇ`€xĀ'NĨ÷¤ĢĢ7¯?÷œ›.Z¯Ũ6”ß ؕCĶ„ƒŧĄE3ˆ”´P}t’Ō•9]Ō}*AĐOWņ‚+*'+šŽĖJ -šŦ]]Bët˙ĘҤĮ։YĢ+„zėĐûĐīĸ NĀęĢvíbz_JČl1Z4EY÷í×§ĻpŌÍ8s“ŪkÍl€ãiŪŧšuN:Ÿœ åš:uĒÕŠSĮí2מ}{Ûž}ģß "ŊãÍHVŽ#ŋ~úo¸ü9:ŸkĢvcü>Ę娏úhɚ.<Š^ã%úéŧ¯ā”{ŋhFÉ[ŨēĨ¨× n)ļ˙‘ŋGE ͖Ņc+p´î/ô‡ø÷ĄqŒr捝.’iÖxhû„¤q“ÆOūãj‹×_†—Úã“ôWZĸxŧ`Švy ŽŊūNm*ßô‘ËģĨ>ēHĖ@Ķq¤]‘ƒwĘ×Ĩ÷Į_öæĨ|P?Cë•.BõJ[Ô)0¤e–zėŸxÂõQŊūüûXtükéĻú*†~ˇĐûW^SŨÆßĨnŲ„ ŽŪ?S[âá¸;øÄ‰ ôžt)W8éfœ?üĐnģí6ŋœQŖFY͚5ũęTŅ,&Í^ʓ'•(QÂúöíkû÷ī÷ģ!•ŸD_‰‡ņ ĮUô•x8ŽÄ>q"ˆ“nôNēG;@5jÔȝį“O>ąË.ģ˝>!-Ãų嗭\šrnSãÆãj‘ĖÄø$úJ<ŒO8Žĸ¯ÄÃq vđ‰AœtŖ¯pŌÍ8Ŋ{÷ļ–-[úÕā,]ēÔ%ņ>‘•+WڀŦvíÚ.¸”/_>{衇Üļä‚ /Æ'ŅWâa|Âq}%Ž+ąƒOœâ¤}…“nÆ;vŦÕĒU˯gįΝöĪūĶ–-[f|đuīŪŨ|đA+_žŧ .i‰œr1õéĶĮϐqŸD_‰‡ņ ĮUô•x8ŽÄ>q"háé§Û?Rĸ $Ŋ ˙ö7˙mB˜|™ôú–)SƯGŗ“Î<ķL+P €UĒTÉxāëŅŖ‡›šôÃ?øŨ‘žvã“(*z/4fŒuWŅUâå¸;6EВŧyíĮ]+(™[ö͜i‹Î=×›&ÚędKd$ļ͛7[Ū¤ķ"2ßâœ9íĮtînE ŅXQcÆXˇ8W.ŽĢ(*z/–äÉãŋMa6EĐÚ{îą­:Ĩøđ§dNŲŪŋŋ­¸új˙mB)RÄžJz­Ā÷Å_XŲ˛eũjd‚ÕwŪi›ÛļMqž¤dNŅXQcÆXˇĻNŽĢ(*[ž~ú˙ĩwāUUŲĮgÆF—Ļô*RT@ĀŅA°QÄ2ęP‚€ø¤H‚‚2("H—OņI(RВ´!JĊA‡Ū¤¯ĮÚķî5wßÉល“ä˙[ßų†ėsœÃæėÅē{ī“+ú€œƒbS9—X§–-+‡VŦ8¤ĩl)ģĮŒą˙˜B¯ŧōŠŧđ v3Čŧyķäá‡ ŽŦYcfĄJH+9Â{hލšĸæŒ9ūØXĒũʇūčlšÜĐ¯ä›Âl{T”|ß°ĄZš2h āßņۄ fĐ=}čũG„úúë¯Ĩ|ųōröėYû€-Ûûô1…Ž__{MöÅĮ îûĪŨol7/.Gí?¸@7 ×7Ķ@z52›ÃL~ifĸüŌŋŋė‹‹ C9Ü94Ü1hÉ 5GÔ?‹ÜÂôĢsy—éWĐ¯ÂxčŊŪņęĢš˛_Č(6eÆúSģv’Zޜy iN;žÎ -'Š*ČÖŽ]åÄÖ­ö \˛råJķļŠcĮŽŲ§äQ{÷î•Â… ËņãĮíSČfūü¤lŲ 1ÔëGŽÍMÎå‚zĪsķ'úUøŧĐ¯xÅ&8rŲetdŪã?.ƒ ˛›äQͧO—ļmÛÚÍĀ%!7čWā OO8ÂĀ‹ŦØļm›/^\’““íSō f͚™‚Jä&pũ œáé Gx‘U ,2eĘČĪ?˙lŸ‡ŦZĩJ*UĒ$§N˛O—„Ün _€3<=á/œ;vŦÜtĶMrđāAû€<ĸiĶĻ2yōdģ¸dä&pũ œáé GxáT¯^Ŋäoû›ėÚĩË> —[ąb…TŠR…YMpš Ü@ŋgxzÂ^\ŠÁƒKųōåeÆ ö)šÔ‘#G¤Zĩj2oŪ<ûä&pũ œáé GxqŠæÎ+%J”˜˜û€\¨S§NŌšsgģr¸~Îđô„# ŧ…õë×K5äÁ”-[﨧äŗf͒oŧQŽ=jŸB†Ün _€3<=á/Båäɓ2|øp)VŦ˜ŧūúërüøqû9˜•u#Ëfá6r¸~Îđô„# ŧĩ­[ˇĘ#<"%K–”ĘîŨģíKä0ŠŠŠrũõ×˂ ėS@ȑ›Ā ô+p†§'aā…[6mÚ$Ī>ûŦ)RÄėīĸo¯:sæŒ}ûæ›o¤TŠRlް!7čWā OO8ÂĀ ˇíŨģ×,¯ĢW¯žYb!ķįĪ—Ã‡ۗ:ĻûD?^š7o.Æ ŗOaĄKG‡ b7įhētŽtéŌf¯& \ČMāú8ÃĶŽ0đ"œļoß.&Lf͚É5×\c6Ö%wZŒZ¸pĄ™AąmÛ6ûÛčŪP7n”™3gJTT”T¯^]Žģî:SÄŌ7ãék؁됔”dútnmöhúđÃíS€ĢČMāú8ÃĶŽ0đ"ģœ={Vžūúk™={ļ<˙üķrß}÷I͚5Ĩ\šrĻ_.\X*T¨ā? (`Ú¯ēę*ŠRĨŠ<öØc2bÄųâ‹/ė d‹qãÆ™~™Ķ8qBēuëfŪ0ųũ÷ßÛ§ב›Ā ô+p†§'aā…W˙üsû ۑ›Ā ô+p†§'aāEn {Ė”*UŠ7Ķ![ôíÛWēté"&LúõëKŲ˛eåå—_öĖūMŠS§J˖-Í[ŸyæŲ´i“}āä&pũ œáé Gx‘[čž9%J”0˙āÂå?˙ų-ZTļoßîoKMM•ČČHŗŲļ.SĶˇĩ†ķín?ūøŖ)ÂŪwß}R¨P!iÛļ­|đÁaũoœ"7čWā OO8ÂĀ‹ÜD˙]§Nyâ‰'ōô?ĒGŽ)iiiv3\Đšsg‰ŠŠ˛›ũRRRdĈŌŦY3)X° ŲDüšįž“‰'ĘÚĩkÍŦŖKĩsįNYļl™ :TZˇn-Ŋ3ŗĢôīÁėŲŗåȑ#öˇžFn7Đ¯Āžžp„šn|üôĶOKõęÕķė˛ēˇß~Ûlú|æĖûBh͚5RĻL9tč}*CÚ7ĩ(4fĖŗėN—Ü(P@J—.-7–V­ZIDD„Ųø^÷UŌˇÄ;Öü:ũĄ×Ü}÷ŨRĨJšâŠ+Ė *-bõéĶGæĪŸĪ†ųČņČMāú8ÃĶŽ0đ"ˇŠ‰‰1¯sīŲŗgĻ‹š…nPŨ¤IS˜€;tæÜ 7Ü sįÎĩOeŲÖ­[%!!AâââLIgBųŠJ:kĘ.6EGG›ĸ•Î^;qâ„ũ』Ün _€3<=á/rŗ}ûöI׎]͒"ņ‘—ėŪŊ[*UĒdŠŊ:˜ž ôČMāú8ÃĶŽ0đ"/Đ×ģרQCš6m*ĢW¯ļOįZúÆ1]bĩpáBû.Á[oŊ%ĩjՒŖGÚ§„š Ü@ŋgxzÂ^ä§N2oįĒXąĸÜ{īŊyf?§/ŋüRJ–,)K—.ĩOÁ ˜=–téw›Ā ô+p†§'aāE^sōäIy÷ŨwÍŌēx@-Z”k7ŌÖŊ›/^,÷ÜsäΟ_–/_n_‚,Ђ]‰%dŨēuö)!Dn7Đ¯Āžžp„y•nŦŦ-7hĐ@*T¨ Æ “]ģvŲ—åH[ļl‘W^yEʕ+'õęՓņãĮK||ŧ)”,Y˛Äž™đ駟šûˇjÕ*û€#7čWā OO8ÂĀ ˆŦ_ŋ^ēuë&EБ֭[›Mĩ÷îŨk_æi‰‰‰ōōË/KíÚĩMQ$22RRSSŽŅũĒtIžŠ™7}útsßÖŦYcŸār¸~Îđô„# ŧĀ:d >ú¨*THš4i"cĮŽ5¯˜÷šÍ›7ˌ3䊧ž2›€×ŦYSú÷īofŪčōšķŅTųōåeđāÁö)Xô>ę 1}Ģߡß~kŸār¸~Îđô„# ŧ@Æ~˙ũwķˇÎ;›åhŋ—6mÚČȑ#ÍÛíô|¸üöÛo’ ŖF’‡zČ˞)Sό)Š7Î,›ËŠ;wĘmˇŨ&mÛļ•ũû÷Û§qÎîŨģÍFōwŨu—š˙‡Ün _€3<=á/9ŋüō‹Ė™3Gz÷îmöyēōĘ+ĨjÕĒĻ ŅŖGyķÍ7ÍžH7n4şØ?"Cē9š^Ÿœœ,˖-3Kø†.:t0ŋOá…ĨXąbŌ°aCķ{ĮÆÆĘļmÛė“eēQē.ĩĶYNėCč“O>1ÅŧČéͧíĶ\Fn7Đ¯Āžžp„pF‹D›6m2oŗ{ë­ˇ¤W¯^r˙ũ÷Ë-ˇÜb6×"‘ūũŌåxúuFGÁ‚åōË/7ŋŽ[ˇŽÜ}÷Ũ!}ûö•É“'›}˜ÜŪ;Jgo•*UJzöėi–æe{öė‘víڙesŧšČ>ä&pũ œáé Gxw)76GåʕÍĩųķį7ĪNJ+š7äuéŌÅąt/ŠŨģwÛŋ=€ŽÜn _€3<==l˞M29q´ô{JžŨÚSGũGn jËÎŖ{L¸čų(uš;yÄž•r8ĩeËsčFã fļŌ”)SĖ1kÖ,ĶχÎdŌëŽáY„‹r–ėĖM4yuŅ˙ȂÔéä!™¤}fJâųW\į ûéĨ#;ûÕÅō_^FąÉƒÎœ=#1)“¤_\G™™öžŦ<˛\Rˆ Æúsąl˙'2nŨsß6īũŪž­ Ä4g‰My/Īį,ž<äuÃäĨ¸N˛eīö­Â˙Ķ>3/e˛)LÎI›’gûL(‚ü€—Qlō Ų)īʐåŊ%éäÚ A…¸xÄũ+}>zō܀û}k@é‡cä,˙ëyáŖv˛•‚S†´Đ4jy?ųâdRĐŊ#œų/¯ĄØä1›÷|g>™H:™4ˆ™pûÆu`J1.!g9hÁIg8‘‡ŌĨsēlî Š“Žų//ĄØä1‡ÉŒ´IAƒ‘õx{Ũ`™›:ŲžÅ ČY.“֍4{8á҉cäŖ´A÷Š]hū;/5Úžõv›ž ßđŠMà Ú`°Āä,ō@ô™đũ€PlōáĐƒ-î gšx‡ĸĪ„'čwŧ€b“Įd× \ĄZŠYŋfP{N[ܑ]9K(ĸxŠâŌ iƒ öPyH 7úLĮ:Ęe—]vÁ#jtTĐ÷Ĩw—žkŽÛķ™97ī›yAįœũ€Plō7áĖ„›túp™JeäÚ×ĘMˇŨdĶ"ŋČĖä™ūët0mxC3¨—ŽXZęŪUW .`žžãŪ;dũŲõæ:Ŧĩ­Mį6rų嗛_—,S’bš„Û9‹æåĒ–“úMęû÷Ūé÷v?snā˙”+ŽŧBŽÉÉE )(ųë_üį}ņÎâwĖ5zíun”|ōIz5¤Đĩ…(6e7úLVŠMŨ‡t7ųmëN­ũ}J?8ÍhĪĻáŗ‡Ë_¯øĢ\uÍURũÖęæÜŨ@.˙ķåÅĻŋ˙PŽ+wųšÚĮôZíoéûëĘũ+åĘ̝4š˛ũߤûIéĩZŗĪ9 ú/ Øä1n ™ -6é@w˙?ī7Ÿøi›~úR¸XasÎW$Šzãŋâ‹ã^ôī—'ŋ”{š'` ÷›´Đ4āŨæĶƋ­—w#lp‡Û9‹›ūô§?™âĐė”ŲōūĘ÷͌ëéIĶÍ?ø˙~ßß͌lŊVs—–[šŧCg_kÛęÃĢ͇fúáØŋˇūÛ´éĖëۛßnrŠMáįFŸņ›t–’βô3Œ´Ø¤}JûÄÔ5SeƗ3Lß°‹MKw.•Ģķ]-7Ürƒ,ÛŊĖ´é ¤Z j™ëŌ›´ĒEĖôŗ–t&“~ČĒũĪ×ĻßŖŠĨģ–úÛ4ĒŪ\UĒ×­ĐvŠAŋā›<ƍA83ĄĨü…ō˚#kÚ}ŸļLZ>É|ũæ‚7ÍTb{†’NÖë|+úŠMáHä. ļ¸ÃíœE‹MšKh! }ûíČđí‰GÍ,Ļæ57_ˆaž茡×éō;-J…#G! äFŸšØÛčtÛßĩZlŌļWŪ{%āgØÅĻ~oõ˰īiŅ3}ąIgôë2;]ž™ū:V­Ėĩžqu–~ũ›/ø¯‰ŲcÚô÷ŗŋ˙R‚~Ā (6yŒƒpfB‹MúIŸŨޟę ¨kĪ͎ëū :SI?1ŌĩéÕjW3׍ŒiÎûŠMšØ?3œÁ` €;ÜÎY|Å&]‚”žŊl•˛f ŋũÆ1=Š^WÔĖZŅëÚ÷iožß7Ģ)}hŪCą)üÜč3žb“î5ú^Â{AGú}ž|ÅĻô[DhØÅĻÚ?`f@­=ļ6ā: ]˛iīŲ¤ņÉöOd§Ėė˙Gē="EK5?S‹ z^?¨Õåv:SĪ÷=í"ۙĨz ˙Iúy—ô;^@ąÉcÜ„3štų> Lē]JšŦ_ëb]2§Ķ×ĩ]÷GĐĩéw=xW†ÅĻg_{6čg†3lœë˛eË9|ø°ŋíØącĻmīŪŊūļS§N™ļ;wúÛ¸ĪíœÅWl˛gS*ZHŽ¸ę ŗ×dF‡.KJ˙ũvąJã–;nĄØ” Üč3YŨŗI¯ĩ7“ˇ‹MZ62û.Ų߯ĄÅÎôÅĻik§™åvúũzčJũ×÷AŦ¯Ø¤Ņu@WĶöáwĘēSëLq´iÛĻAŋĮĨũ€Plō7áĖ„›îhŧiaôĒh3(úĻ÷6jÕČšt°ÖˇføĀáŗ†›ëtĘē~í/6 ĻØĀ™aƙįȤI“üm .4m={öôˇiĄIۚ4iâoā>ˇs–ķ›tæR՛ū[PēPč›uõûã7žaLŖJ­*›˛}ƍb“o œîûe˙ }‘Ž¯Ø¤…LŨlžØõÅdØĖa˛xĮb˙uēĒ]lŌüXgLuØÍĖ‚ŌķoÆŊđķCô;^öb“ūã!""BöėŲão›>}ēiÛ¸qŖŋ-!!Á´-Z´Čߖ¸1g&´Ø¤ƒĨ=]Xg4é@8sŨLķēW-4ÕiX'čû}Ŋ´ú5Å&—jƌŌ¸qc‰÷ˇ­YŗÆ´;ÖßĻ3š´-22ŌßĀ}nį,į+6ĩøG ŗįŌŸ´'ũžd6 īÜŋŗųzlüXķũ˙𸝀ët¯'™Mą)üÜč3n›t&ũzôŧ˙îEę‹ųßÎ7ížb“¯=7⹀ët/§J5*™sē)}úsˇ6ēÕĖ„úG÷˜Ĩv:Ã)ũųPũ€„ŊØÔ¨Q#ķāŨēuĢŋ­GĻ-}aiâĉĻmĈūļŧĀA83á{Ũƒ”¤ãIĻM^}ÍĢžeÃw~šŖo¨ĶˇløÚt Öéėúũú*bmŖØ@îævÎržb“žEL?üēéļ›ü3I´Đä{Ŋo6ļž-ˇrÍĘ&oņíŅŖ˙đ÷-ũ§Ø~nô7ŠM:Ŗéē˛×Éõå¯7[Jh›n%QûīĩŠMē„Nŋžķ;MĶ6í‹˙ėũOĶŽ‡Ŋ‘ũ čAĻ]?ämÕ>ā\¨‚~Ā Â^lJII1ŗ–Ž?îoûá‡L[ú=8vėØaÚļmÛæoË Ü„3ZlĒ{W])WĩœYkޝkՁđÆ:7š×ŋúŽĶAXßŖE(MōŠ—*nöNĐ7kč´āĮ{üôÃ×kĄ LĨ2æœ^§oĢĶŊ&ĩEą)üÜč3n›4tVŋ.ĶÂĻöíoÚgJ”.°gS‹Į[˜īÕĸæÍˇß,ų æ3ųŗnū­íē\.ũīĨ…Ŧ|ō™sąc΅*čwŧ ėÅ&\˜ƒpfBß:§{0é¨ œžŅåųod8ĩW¯‹eŽŅ× ûĻëÛé|Ÿę§:úŒŪÎ`°Ānį,Z(Ō\Ân÷…~ϝ°×|Dķ’Yëg]ŖĄ3ļõ&zŨiCĖך¯čkįíkCä!Üč3ē'—ö“U—Ģeš—ęĩöļ+ö­0íö,$ŨoiČô!Ļ3ĘäÅZÔŌåté¯Ķ‚’~ĀĒĮÄ%Í2:ÍĪ— ×jPKĒ×­ÔĒ ßđŠMãÆ œ—ƒÁwŗ\<ČCŅgRĖŦ*] đŌø—‚Î…*čwŧ€b“Į0‡6lp9ËŃ<$P^î3o/zÛlV_õæĒfԌŪtĒ ßđŠM“—a7‚Áwŗ\<ČCåå>ãÛÃI÷k˙Éø ķĄ ú/ Øä1yyv#lp9ËŃ<$P^î3ēOTljŦ¯S7ƒ~Ā (6yL÷˜‡ĖĻ‚ö Ad=ô>öˆikßbä,ō`z?6œŨt¯ˆĐũ€WPlō˜~qåķŖŸ DÖCīã‹qö-!@ÎráX{tĩô{ĘžmyšŪ/Ž&Ũ+"tAū Ā+(6yĖģ‰ÃeFÚ¤ ƒČzĖL{OŪOmßb“Gŗ\ ¤Í”čÄ1ömËĶô~ÄĨÍ ēWDč‚ü€WPlō˜-{6™O#’NōŠĪĨ„Ūŋ—â:™û Bœåü‘|ōKׅ<Äĸ÷CīËú“ë‚îqéAū ĀK(6yМ”˙•×—Gž0Ö "ÄÅCīÛĐåQ2/e˛}k@‘ŗĮē“_Ęčå/’‡œ‡ŪŊ?ÉœBúwpØō>ô;žAąÉƒÎœ=#sSŪ7ŸƤM–•G– (Dpč}ŠM‹6Ÿčč@Ģ÷¸‡œåX}d…Y:§û‘‡œŸŪŊ?zŸtIŪ7û^™ũ;7'm ų/ĪĄØäa:vJâ3ŨX_aĘqáCī“Ū/Ļ^ä,ä!YEŸ ÍAŋāU›2›2›2›2›2›2›2˙É[2ė`qIENDŽB`‚trillian-1.6.1/docs/vds/VLDM3.png000066400000000000000000000634311466362047600164410ustar00rootroot00000000000000‰PNG  IHDRŗVz†ˇfāIDATx^ė ÜLÕĮÛh߯%‘"Šlɚ­(Ų"K’ˆ(k˛d+˛Ļ"K„,ŲC˛/Y_ОīB›%K;ž˙û;ūgÜ93īxߙšķŪ™ûûÎįųŧīŊįĖŨΙ{~÷9Ī9÷*!„B!$JšĘ\A!„BH´@1K!„BĸŠYB!„ĩPĖB!„¨…b–B!„D-ŗ„B!$jĄ˜%„B!Q Å,!„B‰Z(f !„BHÔâ%fĶĻM+W]uFŖŅ\d¸÷BH´â%fqS#„â.xī'„D3ŗ„ârxī'„D3ŗ„ârxī'„D3ŗ„ârxī'„D3ŗ„ârxī'„D3ŗ„ârxī'„D3ŗ„ârxī'„D3ŗ„ârxī'„D3ŗ„ârxī'„D3ŗ„ârxī'„D3ŗ„ârxī'„D3ŗ„ârxī'„D3ŗ„ârxī'„D3aŗŋūú̜9sÆ\m+ß}÷,[ļLíÛ-\ŧxQķĐĄC•ÅĮĮ›Y!$Y„rī'„”&,bļsįÎęģE‹5“äíˇß–l؞ɴiĶäĪ?˙”"EŠHîÜšå?ū0ŗ&›˛eËĒũ~ņÅf’­lÚ´ISũúõÕr—.]ÔōŒ3Œœáåȑ#R @uÎV{ų嗕Č%„`öŪO!N d1ģsįNõŊú¨‘ë2ŋũö›8p@ūũ÷_3ÉvŽ´īŗgĪĘ­ˇŪĒŧą`ėØą’*U*ųūûīœáįäɓ˛xņb¯uk׎U×ŋmÛļ^ë !$Šsī'„§61;pā@3ɋråĘIģvíÔ˙{öė‘n¸AļlŲâ•gɒ%ōĐCyēĪ‘§PĄBR§N¯|={öTŨúÚ°ėx­ųŦīĻ•å˗ĢŊī›ožY7n,?ũô“Wž‘#GĘ}÷Ũ'ũõ—Z.VŦ˜tęÔÉ+IõęÕ%WŽ\˛aÃ3)$Ν;'M›6UĮûÁ˜É„’$‚š÷BˆSˆ˜˜ŨŋŋōĐj&OžėņԂŖG*ņzĶM7I5”ØĖŸ?ŋÚv\\œ'øčŖTØÂ#<ĸŌÛ´i㕎4hJĪ—/ŸĘ˜^-V[´háÉ7gΚöÚk•‡õ駟VûÆw"ŪX Îwũúõže„: Ô 1 zoŧņư ÎÕĢW{ļ Ãu G 2!ĝsī'„§’˜Å€$íüä“OĖädAUØNųōåå›ožQ]ęqĸ¯Ŋöš‘û?üđC@1‹Ag×\slŨēU Î *¨üđ”ę0Äšf͚U é5kÖx}ÛEūž}ûz­O.˜u1­˙ũ÷Ÿ™;vėPƒß‹ŒãÃņ#4‚B‚!š÷~Bq!‰Ųš5kĒīĀĢęôX.\'Ÿ|Ōãm„eʔI^zé%9|ø°™]q%1 ~ųå%d§‹ŧ8f̍„´î͟A`;ņūũûĢcŦZĩĒ™L!I"š÷~Bq!‰YŒæ¯]ģļúŪ§Ÿ~j&'‹ķįĪËĪ?˙,‹-’=z¨i¯ ,¨ļWćš$EĖĸûņēČ÷ÜsĪŠũX9tčJģãŽ;T(‚?kŪŧš×wœB!ŌĨKgŽ&„$‘Ü{?!„8‰Ä,ĀQøŪ•bf¯D¯^Ŋäúë¯W]ōVŋŠíc€–É•Ä,0Ä(ōÔ­[×#d_yå—́ľ͙Š€iŋķ 'x­F*+WŽ”cĮŽy­ÃÅ-ˇÜ"yķæõZ¯Á6\C fĶĄ„b%˜{? žmGĪJĪY{¤úĐMRĒ÷š˜ļšÄKˇģeõnļ?Ä>BŗIv%ZļlŠļƒWĖ Š—+`bCOœ8ĄōAÔbĻŦ÷g¯žúĒg›­[ˇöIז>}zO>Ėĸ€ycą^Ãā2 FÃ:ħ Btlk(qÅH†pˆîâŋĢcÄ °ÛÆœŗū€ĮéVņN!Vp ösáâEų`ņAŠ6dŖ [rHo?)ß>Ķöõ'äÃ%‡ĨÁˆī¤ņ¨īäĀo‰˜&$X#f—.]ĒÂ*VŦ¨^B€mâoŊzõÔL ~Â@.3@ÛđáÃ=yß{ī=ŸtmÕĒUķäđĀVŽ\Ų#”1 ŗĀ‹Ę˛ˆkÅôb˸VĄ0eĘ%d1Ī-Ž"ņŧ˜!1ā‘žîēëÔu#„sī'ÉįƒÅ¤éØ­˛vßiŅįšü°T´Q§ %á%d1ģjÕ*õŊgžy†#ęÕAđb!„$F0÷~’<ZđėxYŗ÷w‘į&︐tžvyZNBÂAČbŨčxqž‹[Ä9ÄĮĮ+ķ?ūh&Bˆ‡`îũ$yôœŊ[>HrϏs›­Ú}JžŲWГØ'd1 0õÕĉeŨēufIAđ"Š™3gšĢ !ċ`īũ$éĀ+ģtĮ qį6ۜ`ŋK­@ÂKXÄ,!„č…÷~û)Ķw­l>tÆGÜšŅëíũ‚"BB…b–B\īũöƒiĒLQįVÃĩ $œPĖBˆËáŊß~(f/Å, 7ŗ„ârxīˇŠŲËF1K Å,!„¸Ūûí‡bö˛QĖ’pC1K!.‡÷~û EĖ.ũö€Ė_ģÍgŊŨf×~)fI¸ą]Ėbz(ŧLáīŋ˙–ūųGũäȝ}úH͚5ÕÜŽ4eŪđ†ō˙ėŗĪˤˆ€ķÅy¯\šŌL˛_ũU՟ŗgĪǎÔáq'vÜû‰7ĄˆŲ'ž~VR_ƒĪzģíÉĒĩäÚëŽķYĒQĖ’pcģ˜íׯŸÚ.^w‹,<üđÒ*U*ųé§ŸÛ $Tõqā5ɑæÛoŋ•k¯ŊVíŋAƒf˛[e˖5W Ę%[ļlęuŅ?˙üŗĒˇØ?^'M܉ŋß' /ŗ—b–„ÛÅ,<ˆyķæ•x@-¯]ģV‰‹ŪŊ{{ōœ:uJFŽŠöi1 ņ3wî\ĩYŧ睉…'b uęÔō /˜ŲÂōįĪ/÷ßŋōĻmܸQ Ą=z˜YÁĮė%*M´ˆlßžŊ,[ļĖc[ļl1ŗ&™áÇĢmĸ<ā}üúë¯åöÛo—[oŊUNžŋuK‹Ų1cƘI~ųæ›oTyãø ėPæžĢV­ĒŧäÖúСo_ĩŸącĮzÖáÜôą[ 'ūøå—_dęÔŠę:ųÛúúš¯Ö!(°3gÎxĨĄN⸴ õ'fQ&O>ų¤JGŨÕÛ2÷“ĒUĢ&%J”Puį…‡ą>úČĖF\„ŋß' /áŗöÎŊÉ3ĩęÉŗu_Ÿ|î“öņ¤9RīĨWU>XûīÉō­‡ŊōÄī?%Ŋ"5žoŦō4nŅV&/øÆ+ŗØo—žCTžjuʀ}ö™Ŗ˜%á&"b4oŪÜãŊ„ĐȘ1Ŗ,Y˛Ä“ŽÅlš4iÔ_m2dÍ›7{ōˆNkD“k Á ¯“™b"PŖÅ,ĩīZĩjÉŪŊ{ŊōA`Z=˜0å›ožŲŗŦãbŅ2Ä9j –‘§I“&žu­Zĩōڞ6kŲ<$ŧņÆJđYķ!|bëÖ­*îUІķŌ lôú9rxÖCŒgΜYnŧņFuėH7Å,B#ĖãŗēĘ×.ŊķÎ;*/ŧÕØ'â„Áûīŋ/Ŋķz°$îõ‰ØK¨böēëRIž|åļÛīÂÅJɝiĶŠrkôJO>ŧaŦ|ĨĒj}Î>>^*VŦ¨Ōđ€ĄŊĄ8x¯Z‚0“ÄÄ,<úēãáF iUx$ŧ‡ZČâ˜F­k!lë Āģuëæ‰ąÅąc= ŧ˜qqqj5&n<Đ4jÔČc8wSĖjŅ›+W.÷ú† ä͔)“:į}ûöI™2eT>¤mßž]#bĢņ ƒsC=ÖŪ| pė¨Oøßŗđžžųæ›ĒūčmëcÅ÷u=„H×ū 冸[€ō@¨u#//‰ üũ>Ix UĖĸŒJ?ū”ŦŲų‹Z‡ŋ÷åĘ#ŠRĨöŦđņ•īå×;y}˙õ.—îī]û SËŖĻ.PËđļę<ßėøY˛fĪ!2föŦƒ˜Ež’å*ĒtŦ[ģëÉõ`>%„Wo?æsŦI1ŠYn"&f¯„ŗŠ0 æĮÔņĸũû÷Wĸ!œā‘ŗōÉ'Ÿ¨mB  6L…% <Ū(„>h´˜…ĩ†ĀKˆõū ’C8à ’ÂŽ]ģÔqk1́ëĩ×]ōúü Ęđŋöŧâšb× ^;xoûŒëwũõ×Ģ4„4mÚTũ¯Ŋ” téŌjŨW_}Ĩ–ŗ x AYjP6ÖcG9ašRĨJž<ŗ^`=DœĖø>}z%6 *äåŊÅqõĮ*fáÅÄ:<XģøamÚ´QÛ|÷ŨwU^„ēčeí ‡Åĩ†đ†÷YŗgĪu ‘ްŽÄÄŦĻzõę*Ũ S $\ ~͜9Ķķ[!V[ƒ°+kšu° 9S÷ .‰+m 6ô¤áÁК†‡p œÖ´f͚yŌā”°Ļ!lMŖcæĩáaT3dȝ´×_Ũ“Ļī;ÚđЁcÚ֊S'Oę­ixđ×ôėŲĶ+ížō/ųˆē¤šŗsVnņZßüõÎjũŒĨņjaotëį#2g.ģÔžļxŖĢZöéjšáË­éß=ųf¯øNžZŋĶŗŦÅ,žoŨŪĢo^ęœēp­×ú¤Å, 7Žŗæ0ÄöÁCĒ1ģČ­ī—ä…86ķA°@hŦ1ŗVęׯ¯Ö'GéT’+f!ļĩ˜…įXÅ,¨\š˛ēžđ$ęëņ%BCôâY‘vË-ˇxŧˆđ`bõFŸØ(„(€gßõW mŠY€˜kxŒãÃ9lÃڐúÃŗ+VŦđĒ7ūL×<āúā:áĄH§Ŗ{ĸŅa2¸¸>ē># Á •Wŗhl­f†AŽ„$ęą—P=ŗčîGu}ÛŽ—Ä´ë A:xôi÷v_›ũž\*ß+mģ¨ôõ{~“l9îWëîH“VžĒV[zŧ˙ą,ûî ×v fá° ^X‡—Ú‡ s—{­OĒQĖ’pubV?íb=ŧũŽîÜvSĖbt>bVŽYĮ˛Âcz%L1 /qîÜšÕz<áē”#ŧļ)°zƒ÷U_<<-ZÔŗŦëĐ/ŋ€!Ž\wbß;0A|7ŌuĮõÂ`ÂįŸŪŗÎ:[…i¸&Ą‚ßâouč‰-PŋˆŊDBĖÖnx)ė "uQüOžĪŦVë–`nļxĶ^5đëž{īSųÆÎX¤ÖSĖ’hÁbVĮļjƒˆ„Đƒ|`đB¤Ā†˙­ųMĶmā}5Ķ´éŲ 0ŕuŊqŽøPëzŨŋI^Pŧ$ۀ@Œ¸væ‹ŦĒöVÂp|CzâI‹YÄ<<œĀ2fx€ø…ĮËÖEV0—°uŋ¤Ú3‹‡kŒ1„ĄŽĶ5ÍŗūÂJ žũ‰DSĖˆMĖ)lnS›~qĐÚ0ŽüŖˆeëöüÅÍNž<Ųgŋ0/öi7ÛļmSûCX„5žÄ(_b/‘ŗŲrä”4iīōÉץįĨî4mŨA-cā æŽūŪ+_ŋ.…uõ2J-SĖ’hÁbÂŖéu×-<™SéuIŽ‘Øb seÂë†A pķá‡Ē|ØîŧŲŗ`j%däĮā„%hāeƒ×Rīƒ ōču¤ėŠ`˙D)p Đes†˜Ãŧ 8ˆ,P}~X‘Š—áíƒĮQŸģö(cķĐmĖšŠ<ē\LāDŧĒŪŽ÷ŠrG:^BãD¸D´ŋ—@Paz7äC8f†Ā*S("Ŧ1¨Č‡üÉy+›?1 āŲDŦ0fCĀv1Ģ€Í˜1ÃëÜ18įŠ§`ƒËŖ‰ĄŽ¯ 3DøõR×y\'<(X=Ũv‚žÜ‚} #Î'Ĩîũn"bļØcåUåđĪfĒeÄØbNØ[nŊÔ+× i+ĩūĶ™—œ8ÅË<ቓÅ<´˜Ę ĄķÖlSë(fI´ā1KHrX‡§s¤†ä2vÜû‰7ێž•C7ÉÚ}ŋû<ˇØšŊŋKĩÁņęZNlŗ,Øî›ožŠ–ņJN,_I $…˛eËĒmáÕĻN%Ož|¸Ú/^—ŠÉųņ6¯TŠRŠ×ÜZÁ1á5ŗČ‹—āš^}õÕ*īʕ+Ŋōžđ *ßmˇŨ&šsį–›nēI-wîÜŲ+_r˜:uĒÚļ1×EŪu×]ę! 1đŠ^|†4˜@ÄĻM›VŊęØé<øāƒęX|@ZŒû^Öī?í#ôbŨÖî;-MÆl‘Ą‹˜—…ą]Ė4ėGđPĸášõÖ[ĨUĢVfļd҃yBáĩs*x{RņâÅÍՎĸ oĶ‚ˇĀ“ŽzđŲgŸ9ÃėŖ>ĒD'Øēu̍}úôņʡ|ųruLxú÷ßÕ:}œxÕĒßĮ:\ī“'OĒuč¸īžûÔváN.¸>đÆŪyį˛{÷nĩ×ûiŲ˛Ĩ‘ûx‹Ō!zŗRHĢQŖ†™ä8Ž=Ē~g˙ũˇ™ƌ’˜íŅŖ‡¤NZļmÛæy+Y˛¤úßäŋ˙ūS~ėoŪŧyf˛kąëŪOŧšP'?X|PĒ‹—Ņ+Čō'}D_ŦŲâí'eIJÃōėЍęÜq  7ŗ‡’[nšE&OžŦ–ŦŪāôįŸ9EuuˇnŨZ^zé%õ†§¯ŋūÚ§ûqßž}ĒQÛúõëŊŌ­ėßŋ_Ū~ûmՍŲĩkWŲšs§į{ēKûĀ2DößĸE eū;3Ö÷îŨ[ãk¯ŊĻē8MЍ‰m^ũõJLYWo ¸ĩK 0<Í:/„„áz=^/Šc÷ŠYŗfj;ÖÆBÛÄĩ|ų嗕ô×_õ¤kpė7ŪxŖ§Û‚]ø@÷ūœ9süŠ…äōŨwß)¯šöúučĐA‰ëļQoP7?üđCĪ:œß 7Ü ēū5¨E‹•uëÖyÖ”%žŋeËĩŦÃ(`úšā8ô:ˆg ÎßÅqYy衇$Mš4>×lđŪâœęÕĢįWĖĸWâŖ>Ri… öĒ˖-SytũąÖ €ũéđ <Ä5ū_´h‘|ûíˇŌŊ{wõ†3üΞarüøqõÛÂo=&øũLŸ>]mׂ@Z ûJ „L œŖI“&ęAá¨[x˜°‚‡ė•3gÎTy­b"΁L_wlá$oŊõ–Z^¸pĄļŪZ†z„ũaßävŨû‰ĐÍūÎė=RsØ&5MU$ėŅŗ%sņڒ&g1)ØrœOē]†sÄš2´€ØIDÄ,@WōsĪ=§ūG„FÜ*!NĒTŠĸŽÁ´ėŲŗ{ōZĩjyĨˇiĶÆ+]ƒÆ B͚÷Úk¯õZF ö Ą4÷­IM͚5}ōĀ ­ĀãfæŅ†.rˆ(gŊâlÚ´É+/Šæūûī÷Ŧ‡hÃuąæE¸@÷7<žæ~á%F—ší …įíöÛo—={öxåąrđāAĪõō'⃥}ûöžîvxŲsäČ!›7oö¤>|X• Îb$ˆ{Ä1$Vö\ˆNœûŲŗ—nĻ3fô\ũ T¨P!Ī:]OZúALŖÃĖPƒūũûĢõtēģÛŗđ›eŖ ?ø-˜õe"ÍēŪZgÍēÃCãŌĨK=ûŽW!f>ëīBך2eĘxå1=æÔ—Ė™3ûlĻËÂVĢ—ø 1‹sŊČôī.9sæ”ŋūúK-ãwhūfM(f}Áõ ą ūš7oŽîƒø="nüž{îņ į"$š‰˜˜h¨üũ6lØ ö˙øãĢ"$~xVđ#DŧŖøŽ?A&Ex„GŒĄ~Đđ>i!“/_>åĄÚûƒg vāE# qaõ #F‚Ū\lŊŧyķ*gíÆ†G ĮčĪ3Ģ=‡õ蒆āĐb@üÔŽ][UĖB4āĄ@+ŧˆÅ14lØĐ3°N‹úõëĢķĀ÷ā „P…⁠՘å`.›āZ Vųá‡Ve Ū´ūŽáwŪņ4¨BPŽĄ@^ë 0ˆáĒUĢĒõZĖB,ĸž`UĖ&&| š°Ūz P/Bąībbb×_‹dĶ3ĢŊĮ¨ŋHCÔxgQŽxÂCÖC€Ŗę¸trPŸáŲ2dˆ*o”;Ž ^ãlŲ˛ŠēÚĢW/6Ą‹ē‡ī"žM”ĀCâ¨á•Fzbb#H‡GۄįÛĮ:x_5:ޏAƒ*„9zôąëŪ…z…ų3xu­;Öú‚úŠ?1´W|Ú´if’kÁõ ąîû/žøĸú]c'zd4ø}â^aŊīÍDTĖâįŸV /ŦråĘĒ?~ŧjlë2Ô^*b#ڑ†ŽV+ķįĪWëŸzę)Ī:4€Xą`7ŦG˜„Ą"ÕĢWWų+UǤD/ōAX˜$%fÛ*fēąM̘ØÖgȐÁīh~xš‘ŽŽWĢH‚énīPCĨgΜQŪZ;B ‡,ÃËā„-Ā/ø€Ėdr‚ī[CXā9Ä:̘ÕÂ‚ŅŠaÚc đ@uÅ 11 ’3 qˆ-rƒ™M.x˜ÄųâĄÃŧ?¸\ā!÷zŒSéŲŗ§Ī=ASˇn]•XĀ1b ņC7$ wúöíkfUŗ:uRiˆ-ĩ‚BŦ÷'fҍZ—ëĩ']āēˆ#)f›6męĩ^ŗxņbŸã2 ^ßhâ“O>QĮØW ŧĢ´f ĀC<â¨;ãÆ3“Iŗˆ_Æ:sdX1 īõįŸŽūGW&B†Đ3‚^„Ŗ„čs0Á@J}Œčų@o‡y-­†Ų/ôū‚A‡“@ܓKāzčm'ÂĻЎ 'ÆßĀęÄ@8žÍž'Bĸ ĮˆYĖ €ũÃë„At"ÆUĖŅ4⯠SGR‡G Ëhl1šĀˆnWˆÄ ĄAFW+æÅw´˜…'ą…XŅĄŊ]ĪĄXt%=Ĩēw!‚ģˆ>ėë!Ž9ré„ūn08ŒūĮw§1Œ8`LSxā5‡)Ļ›Âūq^z`Oģv픸‡0wÂëq]pÎ0„Xã1Íō`?¸–¸vl‡¸Sˆ›ącĮzō&ė('#åíĮŽķyä‘GT÷5ĘŨgđR‹§Áq!Ÿ>w}}ā5Ā-kl%BĪāŊ‡øÂ, X‡.āaJŖE$Ę]üzŽRˆ8 NÁû‰äCŊ1c͐õâ e„灸T ‚D؊Ē8?lâëāuÁ2âi5ZĖjCŊEų ‘Â2ēöŸ Ķņąxš .č¸b̘EŲĸaÄõ̓Ũ0Ë–áõwSƒ:/9zPú˛zõj5{⇭ņî(?}͑×ŊcĮŽž…čö„°ļ›Äâ Ũ މ.đ;ÃԄøã÷ėÔyˆ?GĪL$~{„Ø…cÄ,§ĩAļšuNZíYMĖ 44đDabzkēŽĩÔb Ē5=..N­ĮÔFÖõˆ+„WÔߨm-fa´V  Íü0LđŽéŒ€îFOl›čĸ…—ŅĖc5ë[Ļ0Sž™áXØFR€GZo â+RčWVCˇļõåCfĢY§pÀ(ëŦ0ë5ˇz/1˜9žÄ™u áh‚3 ^`=ŸŽö4c42Ѓ­Ŧ/[ĐbOŦĪuá6V! 1$Z÷‡üxx˛ŠYũ€—˜A,kđhĻÃ0đĖúF7ÔxUÍ|Vī3ė†bÖ\` bč1Žm‡žW„œ,xđÄØBĸĮˆYÄŖÂĢ úîģī*„ 9ˇ%ŧKx"ĩv›[͜* PđƒĮöđŨ*8Ox(59úûzt:„Ļ^§ŊŊÛCcˆíÁģ‡˜Kxqu^ĶāuÃčQ|ßĩzĩ4đT!Ą ¸t†íYc:á­ÖûąĻķōBāÅv!`Ņ•äoŽŲä‚AWØ^rēŗÂ<˜Iįƒ›¯šzķÚXÍ|ãę ĘÛCˇ5ēČu^ķíg(wxƑ3 $&d5Ö:eΓl*…Ávq]­ĘlC728.,[įÖbÖ:hÚxpđPˆ}ĸ—u ^i˜žcĖÜĄĪÁ4=° "Ķžio8ÎÅ:zZƒ†õyđ›@œS2Fŧ…/]Đo$$ÄéPĖBˆËáŊ?eĀ@KLņ‡ĖăA—N:q\æ jBœÅ,!„¸Ūû# fŊÁŦ˜ęsK[§ t˜u/§á ÄéPĖBˆËáŊ?2āÅ-˜^ķ™ãíÖiĸá4 64“qŗ„ârxīˇĖ[Žy•‡Š—ģDĶ+Å1ÃĻÃüī„8ŠYBq9ŧ÷Û^r‚ˇBâuąxõy´N ‰×´c†žh„8ŠYBq9ŧ÷‡ŧiąC‡ę͚5ķyŗ_4‚—ĢĀŗlž•“'@1K!.‡÷ūđ€×ĨˇiĶFîŧķNiÕĒ•zUs,7a†ƒpŧpB1K!.‡÷ūĐ8t萴hŅBŌ¤I#mÛļ•Ÿ~úÉĖ3tëÖMJ–,ŠĻ#Ä)PĖBˆËáŊ?8öîŨ+Mš4‘téŌI§Näˇß~3ŗÄ˜á V­Z*˜§@1K!.‡÷ūäącĮiĐ ؅šXOžŦf8˜?ž™DHØ ˜%„ĩœųä5g(ĻYZētŠ™…$Ī>ĻkkŪŧš™Dˆ_(f !„Åß˙-Ç—ŦYŗĘ“O>Šb gΜQa*Æ 3“ņb–BH˛øķĪ?eĐ A’1cF5GčÆÍ,„„ˁTûúë¯Í$Bŧ ˜%„’$ā-ëׯŸq^ĢV-Ų˛e‹™…°˛jÕ*UßvîÜi&âb–BH@N:%={ö”ôéĶKŊzõdûöífBlcėØą’3gN9yō¤™Dˆ‚b–Bˆ_Ž?.]ēt‘téŌɋ/ž({öė1ŗŪxã )_žtÆGÜšŅëŊÆŧ<„øpúôiyđÁåÃ?4“ˆ‹ ˜%„‡€iĒLQįVÃĩ $)ėßŋ_âââdɒ%fq ŗ„â(f/Å,I+VŦP‚v÷îŨfqŗ„â(f/Å,I.ŖF’x€3¸ŠYBqŗ—b–Ãë¯ŋ.*Tā .ƒb–BB(bvöŠīä“)ķ%~˙)Ÿ4;mîĒ­jŋ÷‡÷Meŗ$ bŸzę)iÕĒ•™DbŠYBqĄˆŲ:^–ĢŽēJVūpÄ'ÍNĢߤĨÚī˛īú¤…bŗ$X0ÃAžŠF1K  n€°{PĖBˆC‡˜5bõ:ú’¨ülĻ×zˆĪö=Ū“ē_Q1ąˇŪvģĘWĢAOžō•ĒĒu°\æ“Æ¯ŧ.cg,ōڎŗ‹â÷x­īŖņjũąĶŧÖ'Õ(fI¸Á@0 ÃĀ0[PĖBˆC‡˜5=ŽĻ˜…ąŦXwÍ7Iîŧ¤jíúŌš÷ 1‹øÚ÷GN’§ĒՖÛī¸Ķ#l‹=V^ÖīųMåIl6ŠYâD0U-Ļî"ąÅ,!„8„HˆŲM[Šå7ģŋëãē`Ũĩžfũ—ŧžošāŗ9ËĨdšŠ*fCĀ:ŠYmāe xŠ^Ž@bŠYBq‘ŗ-Ąf2Øtāw¯|oõĒō=[÷ĩŒ˜WĖnđá„Y^ųđęZäĶÃ(fI4‚×Ũâĩˇœá 6 ˜%„‡ 1[ģaSĩüJÛ.˛zûO˛âûĨKŸÁrĶÍ7Ë5×^ĢĻéBž…vɍ7Ũ$YŗįŅĶž’kˇĢiģ -)ŠRĨ–YËŋUų(fI4[ĄByũõ×Í$…PĖBˆCˆ„˜]žå<\čQĩNæˆúé’ˇ@aš+CFOhĻņ²5oēôq2xôĪö)fI´ręÔ)yādÔ¨Qf‰2(f !Ä!„"f“k˜z !˜OÖLŗæŦśÁĶ~Ychí4ŠY vīŪ­„­XąÂL"QÅ,!„8„HŠY§Å,‰K–,Q‚v˙ūũf‰(f !Ä!PĖ^6ŠYI>üđCyđÁ9ÃA”B1K!bö˛QĖ’H͞eKŠTŠ’\¸pÁL"‡b–BB™žkՋ Laį6ÛtčŦ<Ög­yyąĖpđÄOHģvíĖ$âp(f !Ä!<;4^–î8á#îÜfßėų]ž°Ūŧ<„Øf8ȕ+—Œ=ÚL"†b–BBĪŲģeØâC>âÎm6yũOŌtėVķōvíÚ%2d•+WšIÄĄPĖBˆCØvôŦT/közŋËmÖvŌųtÕķō1/^,3f”˜IāPĖBˆƒēč€ŧ4z‹ŦŨ™ų\f“Ö“Ēƒ7Ę˙đ5Ŗ$e>|¸<ôĐCræĖ3‰8 ŠYBq.^”Á_gBo?é#øbŅm;!}ŋÜ'•Ūß [sz$â Z´h!•+Wæ ‡b–BČ÷GÎH—/vIĩÁņjšĒHŲÃ/”4šŠKž&Ã}Ōė´g‡ÄK¯Ų{ä§ß˙6/!)Æ˙ũ'å˗—7ŪxÃL"‚b–BˆbëÖ­ęMHũû÷—ôéĶËōåËÍ,„¸Ž“'OJΜ9eėØąfqŗ„Bdßž}’%K™:uĒZ†Ĩ %ä;wîT3ŦZĩĘL"€b–B\ÎącĮäŪ{ī•‘#Gz­§ %ä2_ũ5g8p(ŗ„âbЅš7o^éׯŸ™¤  %ä2Æ SŋÎpā,(f !ÄĨœ;wNŠ-*íÛˇ7“ŧ  %ä2͛7—*UĒp†A1K!.äŸū‘ *H“&MĖ$ŋPĐr ĖpPŽ\š+>’ČA1K!.Ĩš5k*KŽw‰‚–Kœ8qBîŋ˙~7nœ™DRŠYBqđÆÂ+ īlrĄ %ä;vėP3Ŧ^ŊÚL"†b–B\ēF'‹xŲ`Ą %ä .”L™2ÉÁƒÍ$A(f !Ä%`ÆŒÄÆ ĄBAKČ%† "ųō哺gΚI$BPĖBˆ ž˜Ksʆ ZB.ŅŦY3ŠZĩj˛bĐIø ˜%„oõÂÛŊđ–¯pCAKˆČŋ˙ū+e˖•Ž;šI$PĖBH ƒ˜ž¸¸8ŲēuĢ™6(h š4ÃAŽ9düøņfąŠYB‰QÖŦYŖD&ūÚ -!"ÛˇoW3Dâ7G.C1K!1<ąđČÂ3)(h Y°`šáāĐĄCfą ŠYB‰1‹YÄĘF ZBD,?üpHS⑤C1K!1f+ĀŦ˜Ŋ Ĩ  %D¤iĶĻR­Z5Îp(f !$FĀüą˜GķÉĻ4´Äí`†ƒ2eĘH§NĖ$f(f !$@w&Ūė…7|9 ZâvŽ?Žf8˜0a‚™DÂÅ,!„D9˙üķT¨PAš4ib&Ĩ8´ÄílÛļMÍp°víZ3‰„ ŠYB‰bWŗfMeNÍŖ %ngūüų’9sf9|ø°™DÂÅ,!„D1đÆÂ+ īŦ“Ą %ng⁒?~Îp`ŗ„Ĩ >q˛ŅŌ8RСķŌK/IõęÕåâŋf ŠYB‰B0cf.Ā Ņ-q3čAyėąĮ¤K—.f ŠYB‰20‡,æ’Åœ˛Ņ-q3ŋũö›úũNœ8ŅL"AB1K!QŪꅷ{á-_Ņ -q3?üđƒĒ˙ëÖ­3“HPĖBH”°páB‰‹‹“­[ˇšIQ -q3_~ųĨšáāĮ4“H2Ą˜%„(`͚5Jøáo,AAKÜĖ€¤@ōĮ˜I$PĖBˆÃ'Yxfc Zâf7n,5jÔā !@1K!ąąˆ‘EŦl,CAKÜ f8(UĒ”tíÚÕL"I„b–B f+gĖ^ā(h‰[ųõ×_%{öėōųįŸ›I$ PĖBˆÁüą˜GķÉē ZâVž˙ū{U÷7lØ`&‘+@1K!oô›Ŋđ†/7BAKÜĘÜšsUXŅ‘#GĖ$ŠYBqˆŸĢPĄ‚4iŌÄLr´Ä­ŧ÷Ū{R¨P!ųķĪ?Í$’ŗ„â.\¸ 5kÖT†˙Ũ-q+5’Zĩjq†ƒ$B1K!ŪXxeá%—  %n÷€’%KJˇnŨĖ$âŠYBqˆEœ,âe‰7´Ä`†ƒlŲ˛ÉäɓÍ$b@1K!) f,ĀĖ˜Á€ø‡‚–¸ŧ0õ~ãÆfą@1K!)æÅ\˛˜S–†‚–¸‘ŲŗgËŨwßÍ@1K!)Ūę…ixđ–/’4(h‰y÷ŨwĨpáœá (f !$X¸pĄÄÅÅŠnD’<(h‰iذĄÔŽ]›3øb–B"˚5k”Ã_´Ämüũ÷ßRŧxqéŪŊģ™äz(f !$‚Ā ,<ŗ$4(h‰Ûøå—_Ô Q"—Ą˜%„ØXÄȲ! ´ÄmlŲ˛EÕųøøx3ÉĩPĖBHĀl˜ĩŗđBAKÜÆŦYŗÔ G5“\ Å,!„Ø æÅ<˛˜O–Ø-q}ûö•Gy„3Å,!„Ø Ūč…7{á _Ä^(h‰Û¨_ŋžÔŠSĮ\í:(f !Ä&đ~õ *H“&MĖ$b´ÄM`†ƒbŊIĪž=Í$WA1K!6páŠYŗĻ2üO"-q?˙üŗÜsĪ=2mÚ43É5PĖBˆ Ā ¯,ŧŗ$ōPĐ7ņŨwßÉ]wŨ%›7o6“\Å,!„„ÄĮ"Nņ˛$å  %nbƌ’5kV5sŠÛ ˜%„0‚ 0sf0 )-qŊ{÷–G}Tūúë/3)ĻĄ˜%„09d1—Ŧ=#N†‚–¸‰zõęIŨēuÍÕ1 Å,!„„ŧÕ o÷Â[žˆķ  %n^Y„9õęÕËLŠY(f !$D.\(qqq˛uëV3‰8 Zâ~úé'5ÃÁôéĶͤ˜„b–’âœ[ˇNöׯ/[ŗf•øĢފ:{-ÁÆøYīdû>{vuÍŸ3Į,Ž+Íå5ōĒKåeŽwē…R^$4ĸĩžOē*:ëzü5×ČšrɑΝåüŠSfqø…b–’b\<^~l×NļfÉ"Įzö”“sįĘšøxZėԌrŦGŲ–?ŋl/XPūÚļÍ,ŧĘëwäԗ_úl—fŠōęŪ]ļ=üp’Ë‹„†§žgÎ,Gģu““ fšĐl°åÄäɲŋn]ؚ)“œ[ŋŪ,(f !)ƏmÛĘÎ%äˊž74ZÄėXßž˛%CųkįNŗˆŧPåU˛¤œYšŌg´ČŲą>}’T^$4Pßw-*g–/÷)Zdėį!CäÛ´iå܆ fņxA1KIĐuˇõîģŲP8ÄāyÚ[ŊēYL<åÅGØŅŽ]– Ô÷-™3˙eË|Ž=-˛Aģ%cƀ!ŗ„aŊzrôíˇ}n\´”ąĶ‹ɖ¸8ŗ˜<¨ōęŪŨį{´”1U^2˜ÅDÂÄūįŸ—#;ú\wZĘØžZĩäH§Nf1y ˜%„¤ˆCcĖĨƒlãFŲ|ãf1y@œėŠyķ|ŋGKģBy‘Đ@Ŧ&īOÎąSĻČ÷9r˜Åäb–’"lēî:9ģaƒĪM‹–rõÕf1y`y9Ī• ÖwgŲŲõëeSĒTf1y ˜%„¤˜‚ÅŧaŅRÖP&‰Áōrž*/ŦīÎŗ@õ=ņBą6Îŗ@ËËy¨ŧHh°ž;ĪÕ÷ÄS!ÄFØX8Ī5,/įY ō"ĄÁúî< TßO!„acá< ÔX°ŧœgĘ‹„ëģķ,P}O<…Bl„…ķ,PcÁōrž*/ŦīÎŗ@õ=ņBą‘`‹ų,Ĩ ’ĨcĮú¤…Ķ%rį–kŽšFîŧí6ųÉĪË-^ŦŽeX—.>ivڑĨKÕ~wę䓊j,‚-¯åãÆŠcũōÃ}ŌÂiĢ>ûLyč!šîÚkåö[o•Ũķįûäųí›oÔąôkÛÖ'ÍN;šv­ÚoīÖ­}ŌBą@åEB#ØúŽ2FYŖĖÍ´pÚ äžL™äĒ„ã|ŧX1ŸtØŦaÃÔąāˇaĻŲis~ëØ/~ûfZ(¨ž'žB!6lc1ŽOuŸ>dˆOZ8 ō×_/͟{NÚŋøĸO: ‚ ĮŌŽQ#Ÿ4;m˙×_ĢũļnĐĀ'- ÔX[^3‡UĮ:ļwoŸ´pZۜ9%Õu×I“š5ĨMÆrvãFŸ<ŋŦ\ŠŽyĖ4;íøš5jŋŸ}Ö'- T^$4‚­īĒUSe27ĶÂe[fĖPŲY3f”77–yųĖČ=ÔąØũ iژ^ŊÔ~!ĻÍ´P,P}O<…Bl$ØÆ"bö×ÕĢÕ>žúiŸ4ĢQĖ^Ų"%fᑭ\ē´ĪzĢQĖ’¤l}„˜2p ÚĮ'={ú¤Yb–Bl&ØÆ"˜Ũ6gŽ,1B­ē”Ítm‡—,Qų`Ģ'LĶ–ÉŅ.[&ë>˙\íã…ĒUÕ6ũ…ĀL1ģvŌ$ĩÍÍ_|á“WÛąåËåĢ‘#Už•ãĮËīë×ûä}?{ļʃŧøŽ5Íŗ8^äŨ4mšĪv’c‹`Ë+˜Ũ•pũpÜ_ō‰ü˛j•Oēļ@× eƒ2Â>Ē?ņ„úehnfŠŲS§Ēmn˜2Å'¯uû ˙ŋot›&Ö}ŧãË/=uęĮĨKŊŌL1˙˙ũޟ<Ųg;Éą@åEB#ØúHĖ~3qĸ*÷+uûo9ĶS—đŋ5mīW_ÉāŽÕ>ā‘E}OŦNZÅ,ō,3Fmsß…>yĩé{h ûz=pßDžeŸ~ęŗ̘=ĩngŋ8vs[Éą@õ=ņBą‘` bąĢO•*ĨÖkģãļÛdD÷î^߅0yöņĮåę̝öʛ%CuSFžŽMšxĨÁ‹ąÔbļ^åĘōhž|^ߊXĸ„WƒooŨJ•T÷ 5_†´iՍ^įƒp+_´¨WtŸC¸ęŽs-fąŊĸ?ė•÷‰âÅÕžĖcMŠj,‚-/bV—ƒõ¸ošé&hũ.ÎŪņkũ\3ÄN#OŸ6mŧŌ`›5ķ9˜ŗŊĨyÄë;.ėõĐr"Ąė^Ŧ^]y|­ųŌŨy§|1x°×1V)SÆ+Ž‚Y?$i1[ĩ\9)÷čŖ^yK,čķ°’T T^$4‚­īūÄ,ĀreĪîUî÷ßs„ÖīâĄ*_Μ^ų`Å PāČcŪg`xˆ6ĻÅ,îi™Ō§÷äG~Ģys¯ŧ]@ĖššíByōx‰Pė+{–,^yŌÜ~ģē/ë FŅ8}Ö¯ŸÜœ â˛gÎėÉ÷ęķĪKęTŠdR˙ūĘģŒũ×I­ØŪ¤÷ŪSy´˜…=“ Ö$ė÷섯Į‹u]_yÅįX“b‹`Ë˟˜-S¤ˆ:Į÷ÛˇW×įØā™gT>”‡ÎAˆk†‡ œ3ŽŲ„wßUÂ7[ĻL*O0žY~ā!C#ŪŦvmĩņ‡:oÛ^đ\K4æđļĸ.a0ā]iŌș˙ U4ΨShĀá]†ˇųĨŒī~ô˙XF-fa6 n°ßWęÔQë‚  T^$4‚­īϘŨ>wސxīŨwËŧ>RŋiôåɑCÕ%=PųīŽ‹“¸téԃę*zg^Ģ_ßĢŽ㙅xíōōËōũŦY˛"Ąîåāĩõųp…¸ÆņĖūāõ›BOę6ōáĄNoĮsÁo=$øûĐũ÷Ģ{(~ŖČŖÅŦ¯¸#„1Ö;x7P}O<…Bl$ØÆÂŗŸ –ûŋņ†W>Ü ī˚Õ#R!>ž,YŌoŧ$b-áũÔËCØfËaiæĩšŗšīŊ×#n`h°ŽO¸š?ũØcžuđĘÁƒknŪUlC‡EĀS/Šu{8žVõꊎv,k1‹Č"o"D=“šŸ¤X Æ"Øō2Å,j,›ž!x1{„аjåËû[Æ:lC{ ņ],7LÄf^Ģi1‹Á}V€Fž|\{ë>°s/˙_øęŽÚJ e|Û-ˇ¨îTÂåa —ņĖ åŠ2ŌųPviī¸CyÛĖũ$Å• `ëģ)fņ „2„´XķAXĸ—bË=A/ ¨5îˇŪ|ŗęqŅë&&<ÔbÖĻÅ,ęŦu=ęĨõž Á],~ŸžÄ5Ž ˙ŖŽã{x¸ļæAÉîœ7O-k1‹xk>ũÛīûúë>ûIŠĒBˆÛX˜bV{ÕpÕą^Ú 4Аč.:ĢAĀ Æt|ßžĘŗ€mčnüäŠYÂĶ͘ëa1đāĸQ*øo…öūj ŧ¸éŖ‘3ŋ¯ÅŦŋEƒh˜ĖõIą@E°åeŠŲ7=,c:3ŗŧjV¨ Ō~˜3Įg;ÖkĻ=<Ú›\1 Ŋ™†xŦĖõzûđ|ãÁ ]žØ†n¸ģŋúĒZÆCSĪV­üvųj1 Īą™†ī=xß}>ë“bĘ‹„F°õŨŗ9ŗeS‚ĐŦë0<´&Vį~N¨ĢCŌšŗęŊAXŒNKޘÕ=:Úđ;ÂúÄzpĐÃ€ŪžŪzK…ā÷ĻĶpŋ‚ĮÃ9ļŋgÁŸīk1‹{Ģu=<Ö't% TßO!„ ļą0Å,ēŲąČ´ „¨E×.<š:íæoTqø_{C“+f!@Í4ĊY=}ˆ‹Ä”Q$ˆ&ŗ‹ŠŋĐe˙uÜ$Aˆc¤écD÷#âŪ´÷V‹YíÕąB´'%š¨ąļŧL1ĢC0<=Č{ĨkŦ˜5=F0tŸ>œ+—W^ÄüaŊŽs†ˇņēøŨ°Č‡‡"”ę‘>ūŒwŨĨŽ‚y´˜…Đ1÷‹}bæú¤X ō"Ąl}7Å,ŧĒfũļęŠū.„j$<Đéú ƒFHN(bķžZ×ãÁ ë­bƒŊž{ę)ĩ?Ŋo~oV1‹MĖkkwkõ(k1‹ßžuŋ›ĀzŠYBHĖlcaŠYÜüqc…ˇņcū ]ĘčŪ…(BÀ˜FLkĪŌā1Ã6ƒŗūĻæ˛ŠYxqÃGė×ĮŨēŠx54xēņ3Ą }Ú AĒ‹ķI"îr7g3°šĶÅ,â:ŗœ´ĄŦPhDqÍOkŊfđHcA‹Y?žtSĖÂĢ‹.Ux¨0“ĘDĮ0j1Ģ 8WÔ”ōčŠÂ´˜õįI§˜u&ÁÖwSĖ"Œŋ}ŗŽkĶ~žTt¯" BËēfœ#ęļ…',SĖFÁÖwSĖ–H¸`Āĸ9Ãę,ˆęßbŦņ=Ô{k>܋đPe Y ˇ˜Å=Ë3 O*ΞY ûBŸ0ˆĖšƒņ}„ a™b–â‚m,L1‹‘˛ŧīîօ!æËz#ÆHt|īõ† Ŋ.ŨeĻœp‹YÄĮ!ŸŲŊsĐ]Øz@Ūd…î==2†\}.Ņ,fáŅAš FÕ:+Xxxr$ˆvˆz vÃ÷āEˇn ŽžĒKOn1‹ų@‘¯VŊ^ypL¨kHCOÖ!†ĮA4:<ËxøĀFXϘ>‚­īϘŠ+X6aá!뎂e=ŗ…4Ãī÷ Ŧˇ ˇ˜E8 –ą]k>Ü_°ŋU,ã÷†ž<€[¨BÜZĪ…b–â‚m,L1 ÜŠ“…ˆ÷BW<<˜*#Ô!j‘Bâ7c„āŽų\!D0Į(ļŠckÃ-f1 XĮ^>SļŦšŽķĐb`‡ö2ęéjИA0ĨO´˜é1Ļ8nxųæ ŽōDŗ˜…~įUíčÆĮ€,\ ˜Ž—Å5Í@×Lį ˇ˜EC]$o^õƒ)ŧĐøb0!ē1ĒÜÚPӆē†.Ztšĸŧ0§(7<ÍČC1}[ßM1‹ē‰P(ŦÃ`?ÔĜb§ú!O¨G˜ ƒZŅ‹ƒŠx¸C}Bl­ n1‹3Ä|ã^ˆ‡GˆRĖ,‚đ&<\cj1ũĸL_‡īâØp.øí"qũúá’b–â‚m,0ą8n†čÚ˛ŽG<%nÄčŽÃ<Ļ˜˜ķ͚"7jäA FÖCâæŽmęyŅaŲŧ›††ųĖÆ†šQSĻ—á…DC€ũB"á9Ž—1ģ4ęŽŨsđ c˜3TįEŲ´¨[×S^đĻYg5€ĀwŦskÃ>ąos}R,Py‘ĐļžŖŒQÖÖˇÔA„b~dˆZÔ< áū ¯6đãay0ā€7ßôÄbc›ē'õ ˸o™ûˇę ō™ŋ+ „ÅzŧŲN¯Ã}ƒØ7ĻöC8ūž…ŧÖ'ā! ŊČ ‡ĻŲC˜”NĮoß1ß^†ßŖĨŒĄŧöÖ¨a ûęÔQרŧ1”ÅūēuÍbō@1KIέ['[˛d‘3˖ųܸh‘5”Á–Œåܚ5f1y@ymey9ÂTyÅÅÉŲåEBCŨŸ2eb}w€ŠúžP(“Ä ˜%„¤?ļm+;Š•3˗ûÜĀh‘1\ûí… ËáV­Ėâņå•ōæ)¯–-Íâ!aæĮ6mdĮ#°ž§ áÚŖ ~|ũuŗxŧ ˜%„¤Ο—ÛĩSOŨG:w–“sæøÜĖhöŽõ‘Ž•GöpëÖĒ,ŽË+åĖS^qqrøĩגT^$4T}Ox€ÃoäH§NŦī4Uߎ9Ž=ĘāJõb–’â û1™čÆÆô+ŅfÍüŦsēáZīĢ[7`×]b¨ōĒ_ŸåA ĨŧHhDķũ)ë: ××<Šõb–BBäĒĢx+&X^Ä-¸ĨŽģã, !ÄFÜŌ`Ä ,/âÜR×Ũq–„b#ni0b–q nŠëî8KBąˇ4ąˋ¸ˇÔuwœ%!„؈[ŒXåE܂[ęē;ΒBlÄ- FŦĀō"nÁ-uŨgI!6â–#V`yˇā–ēîŽŗ$„qKƒ+°ŧˆ[pK]wĮYBˆ¸ĨÁˆX^Ä-¸ĨŽģã, !ÄFÜŌ`Ä ,/âÜR×Ũq–„b#ni0b–q nŠëî8KBąˇ4ąˋ¸ˇÔuwœ%!„؈[ŒXåE܂[ęē;ΒBlÄ- FŦĀō"nÁ-uŨgI!6â–#V`yˇā–ēîŽŗ$„qKƒ+°ŧˆ[pK]wĮYBˆ¸ĨÁˆX^Ä-¸ĨŽģã, !ÄFÜŌ`Ä ,/âÜR×Ũq–„b#ni0b–q nŠëî8KBąˇ4ąˋ¸ˇÔuwœ%!„؈[ŒXåE܂[ęē;ΒBlÄ- FŦĀō"nÁ-uŨgI!6â–#V`yˇā–ēîŽŗ$„qKƒ+°ŧˆ[pK]wĮYBˆ¸ĨÁˆX^Ä-¸ĨŽģã, !ÄFÜŌ`Ä ,/âÜR×Ũq–„b#ni0b–q nŠëî8KBąˇ4ąˋ¸ˇÔuwœ%!„؈[ŒXåE܂[ęē;ΒBlÄ- FŦĀō"nÁ-uŨgI!6â–#V`yˇā–ēîŽŗ$„qKƒ+°ŧˆ[pK]wĮYBˆ¸ĨÁˆX^Ä-¸ĨŽģã, !ÄFÜŌ`Ä ,/âÜR×Ũq–„b#ni0b–q nŠëî8KBąˇ4ąˋ¸ˇÔuwœ%!„؈[ŒXåE܂[ęē;ΒBlÄ- FŦĀō"nÁ-uŨgI!6â–#V`yˇā–ēîŽŗ$„qKƒ+°ŧˆ[pK]wĮYBˆ¸ĨÁˆX^Ä-¸ĨŽģã, !ÄFÜŌ`Ä ,/âÜR×Ũq–„b#ni0b–q nŠëî8KBHÔsāø.ųtí@ykNi1š*-€Ŋ:Ĩšô˜×BfoũLūü÷œy)#Ë+éæ„ō"ĄÁúž4ŗĢŽSĖBͅ‹dúˇc¤ķœeúžņ˛ęÜ2ų–Ÿ€ŸÍ Ÿ§Ëčø÷Õu;pbˇyYmÃZ^3ö|ÆōJÂG—רø//ēžwšĶXĻî++Ī-õ)_~.PחœúJ>Šī+į4’ũ'v™—4((f !Ž ÅģKߔõ˙Žķš1ōsåĪüc3¤ũŦ†ōķ™#æĨĩ–WhŸyĮĻG´ŧHh|ņíhéŗ´-ë{Ÿ9ĮĻĘŗę%ڝæeM6ŗ„Į‚Ž;xĒÖ˙ģÖįFČOŌ?“w–Ģûš—7ė°ŧÂķ‰Ty‘Đ@}‡w‘õ=øm‡9/„r@1Kq,c×”É{FûÜųIŪgíßßH‡Ų ÍËvP^SöŒņŲ??ÉûDĒŧHhŒZÛ_&îéS~ü$ī3,ū™žuŦyy“Å,!Äą mÕË}n~ü$īƒ8ĩÖĶj™—7ė°ŧÂķAyŊ6­Ļyy‰Ã€G‘õ=ôĪ’ßĘ[횚—7YPĖBKË)ÕeķÅÍ>7?~’˙yur5ķō†–Wø>‘(/Ŧīáųlž¸IZN}Öŧŧɂb–âX0•‹yãã'¸ŽĨŨ°ŧÂ÷‰Dy‘Đ`}ß'ÔúN1Kq,l,Â÷ ĩąH ,¯đ}"Q^$4XßÃ÷ ĩžSĖB ‹đ}Bm,’Ë+|ŸH” Ö÷đ}B­īŗ„ĮÂÆ"|ŸP‹¤Āō ß'åEBƒõ=|ŸPë;Å,!Äą°ąß'ÔÆ")°ŧÂ÷‰Dy‘Đ`}ß'ÔúN1Kq,v4m´•Âe +›ŗgŽOēū ž3ؓo鯞¯¨œōŨyĻŅ3’!KšęĒĢäú¯—BĨ IŋĪûųäMėŗņߍŌn`;y¤ė#ō@¤rÃĘōÅ_øä Į'ÔÆ")ØQ^]>ęâ)\s3]F,áÉ7īĀ<ŸôÛgHõĻÕ%SļLĒŧR_ŸZō—Č/=ÆöjD:Ę)ËŊYdÁĄ>iáøDĸŧHhØQߟzū)U‡ËT-°^žÜíe•÷ 3 Ÿ3J‰§JČ­wÜĒęûmwŪ&jW‰'úäMė3uëTŠöR5Éûh^ÉS8<]˙iųlũg>ųÂņ ĩžSĖB‹Ef5ÔÍöj¯W}Ōõ§ÔĶĨ<ųžúņ+¯´×úŊ&×\{Ü|ëÍōdŨ'Ĩy÷æR§eš+Ķ]*ĩĢųlĪß§|õōrõÕWĢŋ Ú6ô™Ķ+Qéú ķ™†w’ëR]'7Ütƒë˙^/ˇÜ~‹Ę“ílŪi­—˛ÕĘĒ´¸ģã¤v‹ÚĒžWĒWIŨ[ŽŊîZygÜ;>Û4?à Wŋ™´qiÕo÷'<¸ãûũ&'ũ=ПPë;Å,!ÄąØŅXh1‹s·sú¤ãŗüÄrI•:•ōf˜bļī¤žjŧzK~YâõŊ5įÖHŅ'ŠĒôŽ#ģzĨ™ŸAŗŠ|¯õ}ÍŗnŲo˔ĀĘ](ˇOūP?Ą6IÁŽōŌb sæė™}ŌņYûĮZšé–›<^(̘úåPõĀΎųPQ ~xėÍíúûŒ]=V‹ŗîŎú1  !Ų ]Ÿt|āuŊæšk” 5Å,<Ѝ—uZÕQ=?Ö´ŲģgĢfÜÛfîœé•fũĀ#ŒßZÆ{2Ē{’^ŋâä šûžģåö´ˇË†6ø|/”O¨õb–âXėh,´˜­˙z}õwÖŽY>yŪūämI}CjåͰŠŲø˙â•F#â/ôŸEĮŠī۟÷~Ÿ4ë" žÂuŽķZßúŨÖjŸSˇLõųN(ŸP‹¤`Gyi1 ĪūúķZŋ;å]å)¯ōB/1‹F=Ę#ąp4Đ(O„ęÖÅâÂ8[ŽlRąNEŠY—cG}‡˜Ŋ7ĪŊRŧbq%&ũÕɊĪU”‚”Ŧ÷gõŗ @ũDšŋīáĶZĪũĪL͟éÛĻĢĄÖwŠYBˆcąŖąĐbvܚqęÆī/Ô Hš"ĒëŋÖ+ĩŧÄėˆÅ#ÔrÕÆU}žcũĖÜ1ķŠž‹tĶI’|֏^1ZíŖÃ°>iĄ|Bm,’‚åĨÅ,<ĸđVaŲĖSîŲrR¤|iÜĄą—˜E| –Q–æwŦ<Đ {Ö\o~JV*)-zļPyŅuK1ënė¨īZĖvĶ]Õ/3ÔŊ?7Ū|Ŗtū°ŗ˜}ąĶ‹ę;}&öņúŽõƒrˆUsŊųÁCųęĶĢ}Ö×|šĻڇ?'@(ŸPë;Å,!ÄąØŅXh1ûõҝ%_ą|>Ąˆ‡„— SĖj¯iˇŅŨ|ļ›œ„.„4â<Í´šûæĒ}4|ŖĄOZ(ŸP‹¤`Gyi1‹„âO÷ 5@ƒ ĪkˇQŨ|Äl—ģ¨e ˛3ˇĖĮWK1Kė¨īZĖĸĮáf¨œˇŠž!SĖĢPLÕÉųį{}'\Ü3Îc†6„ãj}§˜%„8; ̘}cĐ>^†C;¨]čū7ÅŦVÃæ ķŲnr>h¨°ŒŽ7Ķ Ļ‘VŗyMŸ´P>Ą6IÁŽō˛ŠYí­˛†`0 f&Ā55Å,ŧîX†0ˇę‡b–ØQßĩ˜Å˙U~Ė'Ô ô3ĨUū7ÅlUD,¸šŨP?ߜũFÍj€}ôP™éĄ~B­īŗ„ĮbGcaŗ ,T)ŦĄ؅)hđŋ)fŅŌåĶøl79xŗčŪCömĻ…ō ĩąH v”—UĖjo•5ÔĶĄĮ˙Ϙm?¤ŊZ†6ˇę‡b–ØQß­bļ×ø^ĒŽ_{)Ô`劕ęÁ­į§=Õ˛)f15 ōû åŗüørÉ[4¯ēWž5â-Ÿôp|B­īŗ„ĮbGcaŗX.XĒ 'ԃ„Đũ¯=¯Ļ˜ÕžAxtÍíZ?æ .ķŗéü&Õ0ø 3ĀܡØ„™™Ę'ÔÆ")ØQ^V1‹exĢ0X ˙c¤5âhĩįÕŗxčĀ2„§š]ëįJååīC1Kė¨īV1ĢCh0ø˸˙`Y‹USĖęééĻ}?Ík›æ'9õŊV˜Á‘Ŋ'ôöI×'ÔúN1Kq,v4ϘíøAGĩŒ›v›÷ÚČéîđLicŠYÄĸAėbZsģÖb;1ĄūâŸû¤éb?1ęØ\?réHĩO đ0ĶBų„ÚX$;ĘËŗŊ>ģä­B¨ŧD ƒA1H3Å,˜õuĄ/°f4Åė˙Ú;›×:Ē0˙K˙€.Ĩ‹Ž¤āÆ.Üņc'(íĘf!EąQŠhąH!ĄĨIS\46­máÆŠ´ĢSÉÆ…!`w"R7Z•:ö™0vrîäfrĪ™Ûšôy~Ė"3s§rŪ×9ŋ9sæŧĖå_īa›7Ũĩ){āĄ•ˇŊÄG—,ÃĩųîÍŲäg“]ĮS*6ß5ŗ"ŌZšč,B3[|đ5úæhļížmų×ēÅšĄ™EÛīߞ€qrņäĒëĸJ×cūZxŦ,Š-`įčŸ{}e*ÃZ×īWąEšˆWhf‹Ņ* (ėxpGžDVqnhfĶx™ž<Ŋęē… įÕ-Æ`­åŒĒ¤™•&ō=4ŗT$ĪÆ?ĪßB8uā˙cĄ™åMËũņ ]<ā…*žčU€ņ°Čˇŧéĩ&m*ÅæģfVDZKEhfKqqĶÆô”G ĒĖ,ķ×0ŗTû ?cš/FQ˜B@yÕōąPŸLä×.¯åČ^L_Ī÷ŖØÎĸMÄ+4ŗˆĨ¸(\@(>Qė¯2ŗŦŊ‰ųŨrĪ–|ÚAŲ°Î|;“~SžNifĨ‰|Í,ĻƒĘũŠø0ĩ8šYDō’ˇ”p.öŗ#ˇLāS.†Šl 1EHz•üNŠØ|×ˊHkiĸŗ¨2ŗÅNTĮ)/ŋTefŖ#ŒĒrŒŽé[īŨš˙qÚ|˙Ēķ×uÕų ŋßõäŽ|Š&+<7VąEšˆW•™-FĢčlËëųV™YĨVQūSˇ2ĩāoFģXÁĸ|~ifĨ‰|Í,ĸÜ2šV|˜Z¨ĘĖō°Ær^œĪÆ}‰û ÷)ūĻčËzŠé ŊļŠKS]ŋ‹QlžkfE¤ĩ4ŅYš;”‘/ŊõÅ/s+ŲŽR@)Ôü܊¯ƒ™žĀ*;Ū™wŦņČ(ëųĢįģÎ]KgŒ/‹ņs Ė3%'ÃķR(ļŗ¨Cņ:|ápƒōHŖUė —Ü:rņHžŋĒ:û˜ͨ.mÍ…‘}#}/ūÎ>˙+,„ĮRhņ’8šČwVā›[ĩWūäZ8õˆ‡0æü¯žÂŠx ÆÔr_"ßšOQˆĨ|ß[KÅ˙sŊļÔkŲÆæģfVDZKÅĒØÎĸÆ+/‰Ã|O§Ø|×ˊHkąŗH§ØÎĸÆ+/‰Ã|O§Ø|×ˊHkąŗH§ØÎĸÆ+/‰Ã|O§Ø|×ˊHkąŗH§ØÎĸÆ+/‰Ã|O§Ø|×ˊHk}lCkĒj҆{f ›79Æ+/‰cĪėŖæ{҆Ü;bĐˊHkŲ×y:ģüûĨŽ›ŸÚ˜ŽüõUļ÷ėãaķ&gŦ3bŧhPņ’8^ę˙íĶėũĪ=‘ũpíû°YÃxõ§Û/‰ƒ|?Ŋ8•Đ2ÅÆ|__´mE›Ņv´a,šY x u|á`örį™|ˇŪÛĢsģŗßLdŋ\˙9lʁPÄë•Îŗ]˙mnŨÛ펗ÄAžO/ŧëũŠÆFŅVąS ĘhfEDDDdhŅˊˆˆˆČĐĸ™‘ĄE3+""""C‹fVDDDD†ÍŦˆˆˆˆ -šYZūpßŦ ߛâIENDŽB`‚trillian-1.6.1/docs/vds/verifiableLog.png000066400000000000000000001157411466362047600203700ustar00rootroot00000000000000‰PNG  IHDRē´‘[4€IDATx^ėŨ ŧ•ĶūĮņkžĮ& JI"c’""(‘[ē"Ã͐!$EäĻAŠHˆŽ E.Ĩ"Ō¤¤DiŌ@Ļ”RiRŋŋīō_ģįŦ}NNįœ}Î>{Ū¯×z9{­gOĪŪ;Ī÷YÃķ …ü#Ŧ 0#čR AR瀔BФ‚.H9EŠąüã …BIŖĸû=‚.H9:⤗čŋũü_¤‚.¤‚.Hi]H?]Ōē~ē Ĩt ũt@J#č@ú!耔FĐ€ôCĐ)  釠 RAŌA¤4‚.¤‚.Hi]H?]Ōē~ē Ĩt ũt@JËMĐũã?ÂĒ„[˛d‰-^ŧØļnŨ6Ĩ¤ß~ûÍŊßhŅ>€Ü 耔–“ ģeËģōĘ+m—]vąĮ{,C›h‹-ėėŗĪļ•+WÚŦYŗėŦŗÎ˛;īŧ3Ãv9ĩëŽģē×üĶO?…M 5bÄ÷>žzę)wûÆotˇ,Xl™ˇú÷īīŪoXJ–,Y '¤‚.Hi9 ēĪ<ķŒģßi§f&L›íĩ×^síO<ņ„ģŨ¨Q#Ûc=ė‡~ļÜy Đ ˜ŋūúkؔP5kÖ´ĸE‹ÚŠ+lá….p˙ķŸ˙ 7Ës>čę}ˇnŨ:VîģīžpSČ6‚.Hi9 ēíÛˇw÷ûâ‹/ÂϘ‹.ēȎ9æ÷÷˛eË뀰nŨē[_~ųĨ{ŋĪ?˙ŧģ­Ūé"EŠäK¯˛ēoŧņFØ9FĐ)-'A÷Ö[ou÷›={vØŖy¤ûīŋŋ}ôŅGîļzw˗/oÛļm˰Ũë¯ŋn§žzĒ­Į<ūøã­aÆöâ‹/fØîúë¯ˇŖŽ:ʎ8âWF•ĄŨĶv~›°ôíÛ7ÃļŖGļZĩjŲnģíæžģjÕĒöôĶOĮŊƛožŲõ¨Ęƍ­XąböÜsĪeØ&jíÚĩÖŦY3ģôŌKmõęÕaķNņAW¯áŪ{īĩ:ØČ‘#ÃÍ`§t@JKTЕ—^zÉ>øā÷ˇæ“ŪtĶMöûīŋĮÚ4õ8ûėŗÕŽ]Û IŪoŋũ\Ũå—_ÛNÔCŦ°ĒžTĩŋųæ›ÚŊK.šÄĩ—-[Öm_ŧxqw[eĀ€ąíž|ōIW§{Æg¸įŪwß}]Âr”zĸũ\\Í;žíļÛâÂpÔäɓcĪ9~üø°y§d5Gˇyķæiŗ €ŧ§GbGęRBô`';ÔS[¯^=wŋE‹…Í;Ĩ{÷îîq:wîė¤hî­æūöėŲ3Øú/Z J÷É*č^{íĩ.,kÁŦ_~ųÅN>ųdˇŊBļ§šÂ{íĩ—•(Q Kö~ūųg×ĢĢŪåO?ũ4VŸę‘ėũ]Đ-ĨšžÚNÌCC† qm ŲaĐõEŊĖÉL=Ԛ_ 9Aи¯–¯ĩßZ`Mû~fĩēMJéŌėÉÖeÄ›¸`U¸ ;tå–[nq÷‹ûÍ õHÖ¨QÃ-đäéīŠ+ēËeÖĢûwA÷Į´*UǏm´p“wá…ÚŗĪ>ëū^ŧxą››ĢíÔķRûĻM›Âęĸ!Ø~8vNmŪŧŲX_ãûīŋīŪßŋūõ¯ õŪ×_m<đ€=üđÙîC č ĖÖmÛėÉąKŦqīiöä¸e6vÎ*û|Ųڔ.īšŌžúķŊļzfĻ]ũė,[ŧbûÂEHŒœŨė.Fõwtų!=NéŌĨ]ŽŽë‡WĢV-ļ†2‡C‹ĩȔnW¨PÁFŒÛÖĪ_Õ<[ŸråĘšē&MšÄļģãŽ;\ŨčVGÖsׯ_ß9äW¯ŽsJķ˜5OxīŊ÷ļ¯žú*lÎ6ŋ¨•^Ŗ^ģ^cŨēumĪ=÷t=ēŸ}öYxGרÕũtB2CĐ˜'Į.ļk}a“­‰ „éP~ô­]üÄ4[ú a7‘ 2čvęÔÉÎ;ī<tõx*Zų¸UĢVŽQĢ^ÍčĨ…ĸEa9zš](ÜÆ—ļmÛÆļĶ"Q}úôqĢ3ûįVҰėĢŽēĘ.\ÛvgŠGØŨÜözkĨčŖ>:öúvŨuW× îWŗÎŒķŌđŧZ 5tB՛ôžn“ŽŽ €éTžˇÔ:žūu¸{‡rtīšįw?]ú&ˇC|= õU@Ôeˆō›†;ëš×­[6åX^ ]ŽÚ°aƒ{ˇŠŗVšÖ°l ]€ŦtĸˈųÖwė’¸ā—neÂü_­a¯éáîAĘIН5k–ģö­îĢ^Y$­(]ŦX1ˇú4d…  ({OˇqsWÆŋt+3ū,įtŸî䡜]Ұå.]稜9sÂ& O>ų$ÃPnČ AP Îzd˛ÍXú[\đKĮĸ՘‘89 ē€Â‹  ( waāK×BĐM,‚.¤‚. @tˇ‚nbt ũt‚ ģŊt‹  釠 (Ũ텠›X]H?]@ čn/ŨÄ"č@ú!č Dn‚n•ĢŲGVŠĢOt9ĩFm+~XЏúÜ‚nbTН7ož <ؕå˗‡Í Ĩį{éĨ—ÜsõÕWa3vÂļmÛėŊ÷ŪsûrčĐĄaķMš4ÉŨī‹/ž›2åˇWųũ÷ßÃæ˛iĶ&?~ŧ{,Ŋ~}åĮ´!C†¸z]/ē ų÷ĒךSüqėqūøã°iŒ  (Ũ텠›XytˇnŨj§Ÿ~ēí˛Ë.6ū|böØcĢ^Ŋz†íŽ>úh÷Ü*ŖGÎЖhMš4‰=÷O<6gę‘GqÛ?účŖîv•*UlŸ}öą… [æ­ÆģįU(ûîģīėĀ´#Ž8Âíįd0yōäØž,Z´hØŧC×]wģßÃ?6ÅYģv­íē뎹įúūûīÃM˛íË/ŋ´Ã?<öXžč3nŲ˛eėöC=Ū5ßč‚+WŽ ›ŗå§Ÿ~ĘđūÖ¯_n’o4hā>?]įY'ôoÂ1Į“ĢÜŅw"öw¤€„"čn/ŨĊė䕙3gēŲ;î¸ÃŨžûîģŨķD{Č>ûė3kŪŧšĢĪī ģhŅ"ģíļÛÜsg'č*tT¨PÁN8áÛ˛e‹ë%Ķ}ŗĐrëÛoŋĩ8ĀZ´hán÷íÛ×=÷ģīžlY0¸_ũu+[ļėNŨ‘#GÚ<āögvŒ3ÆÎ<ķL÷ūst/ŧđBēnŋũv×ĶųØcŲĩ×^ëBØŌĨKcßׂ ēĸ}Ŗ’›ŪkõVŸ|ōÉtĩOwÛm7kÛļ­ģŨ­[7w"ėŖ> ļD~"č D^ŨO­´‡žxÚūyM;û÷÷Û°Ņ“ãļU<|ŦŨ|wģŦÕĩv}ûŽÖsāP›ūͯļųlņjëõÜĢö¯oŗ–×Ūd÷tíicg,ʰēēīÃ}žsĪ{ã÷Ų+˙û8î9wĻt+AW:tč`Ŋs=6:PWPü÷ŋ˙a›Į<tÕ3úá‡ÚÜšs3lĩfÍF´zåvdņâÅn;õ„Ē7P5JAŅŨ~øÁm;}úô Ûxīŋ˙ž FęŊ…ÎãŽ;Î6oŪl™Ŋ{÷ļŊöÚËVŦXá‚åi§æzz3Ŗ^_Ŋ•ŦÂā’%K\ÛÆŨ{ŌļÚˇQjĶ>TYˇn]Ŧ^ÛųúhoÜI'”!č.[ļĖm“Y¸Rx÷áËęÕĢÃÍmĢ×§“$ú [ĩj•iĐÕ°\<ŅļĶĻMs''ÂĪ\4lŊ|ųōV˛dÉØs˙üķĪļŅwÆ]ĩE3jÆ îūz¯žžĶ?ntŋyęÍÔã)ÜgÖî)úĮÉ,čęžjûõ×_Ã&÷P›×°aÃXĐÕû×ķëģžõûįŒŌžV7'îģī>;ė°ÃlÕĒUîûĢ mÚ´ 7C>#č DnƒŽįŅUN°}÷ÛĪJ•)k{ėą§íēÛnÖ­÷ŗąíHĪ>˙"÷?ģŌ‡—ŗSΨeZÄŨ>é”ęą°Ģ˙žVŖŽ;_šĘ‰vüɧ؞{ííûš×GĮOA÷ CĩãĢžj{īŗ¯{^m§€ĐŠ{Ÿ¸×™ŨBĐMŦD]T+PŧúęĢîļÂėAäÂįƒŽ|õ__ęׯŸ! éoߓ-§žzj\āQ09īŧķ2lˇ÷Ū{ģ øÖ[oÅļķAW=‘Ņ!ą'žxbܜáË.ģĖnŧņF÷ˇÖ5dyâĉļ‰R8Sč;äCė›ož ›wšÂÁ)§œë}ūüķĪŨ{R@÷ĻL™âŪKô}ë7Ģ^´č0įŠ+ēļ%JX™2ebÛîŋ˙ūą9ļ ,pīÛÔ§ŪÔW^y%Ããīģīž.I4čęoŋzö5ˇÖSH>†/>ø`lųíˇßÜ …č6ú>{ėąîīčį>v먏ĄČúŽi˙+°{]ô×ŋw™•›ož9ļēÚ?ģīž{l ĩõ!P'ZöÜsOW¯'*Ŧũčˇųå—cŠīŪOôģĻ}|ë­ˇfxrÔQGexmoŋũv†vxŅgâÛ k;ëŦŗbõúŒu2ÆŨ .¸ Ããęģ GŌŖG m: “ūžžœsÎ9ÖŠS'÷õģÍŦ¨­cĮŽîqĻK—.ûn5Ɗ/žãáØČ;ú`ˆģ Ézŧ:Øb_§ ĢíjŸSß>ūō;W7ję×V´øaîšg,ũ-îĩf§t+z°“×Ô Ã:bĈ ĄË]•K/Ŋԅzõęũõ}ëÖ-ļÂq­Zĩ\PҰRõ´]qÅn; 9RO‘ę5jäīŪ{īĩʕ+ģē>}úÄļķA×}gk×vÛú`uå•WFņ¯Ž^aQ(yįw2´‡Ô+­€¤0’W i¸ĩ†„{ Ņ“ęĄĢZĩĒ åîĒ}¤pŦ÷ķÆoÄļĶ~UčWŊÚ=÷ÜãB‰BĒ‚ĢzVõ^Ûĩkį‚’ļkÖŦ™ īz_: Ą:…j…CßÛ胎¨B’ļQæá*zzŨēuëXņá+tŽÎ=÷\WÆg¸6=—æ'ûĪĖ]šƒ>Ø[Ŋfm{à 7Äļ>ˇ†)ë9ÕĻ@}ŅĄā>čĒhnšSÛčļžWĸũ¤“ Ķ>čLJģ}ûöVŗfMˇ­ēz:)Ŗ:Í_÷ßK?Oũškމ=ˇh¸žOÛĒ= ēę‘×īAmú¯z˛_xáûāƒė駟ļråĘš6Ŋ‰†U͕ÕķëwŖ“ûíˇŸÍž=۝(ŅwGÛzčĄv˙ũ÷ģûęqũëŦS§Ž 8ĐŽŋūú¸p–čī2úũ͉‘O?ũ4vGŸiėīH= •Û ģûî{ÄÂĻ/]ú×Aüäy?šÛ ˛mÚŨaĶžY•aģū/ŋåļëōŸîļÄ<Ū?Ãv/Ŋ=Ū†Ŋ?%v[AW=HcĻ/Ȱ]ķĢÚēû‡Cŗ[ĘĶÆ¸{;wļ.]ēÄnë€,zŦarŅšuę}čÚĩkėļ0Ŗ!JųŅš–:đ×ĸ4ž‚U÷îŨcˇīēë.7§ĪĶ]õ†xwŪy§ pžæŠūį?˙‰ŨÖŧĀž={ÆnkŽhtž¨N{õęģ­ ]õtĀ l7Ũt“ žöŸ|ōÉØm îׯ_ėļΟzęŠØíčÁN~ķA7úyi(Ļę2šĢ0§°ĸ,õ)HĒ+Ę`}N _ũĩë=RĪĻz*=tjŖC8K•*e•*UŠŨÎ) gU ÉO Z Ā ;ƒ rŋŊĮč÷WdbŖCQõŊ×ļ <žŸũũéûŖ:õîF)Ôǧ[ŊwjŋęĒĢâz*3ãį;GÃzū;=1ĸ īCēēzĪęÕķęõ*¤jˆˇzyõÛ ‡įŠzŗâƒŽ-‹ŽTŦûéyĸŽ?ūøXĐõžūywt}O¸Nâ脅öw¤€„ĘmĐÕ°á°žÅÕ ŽŸĩ4CŊņķoŽąÎ=ž˛V×ŨbĮ÷×°ÃΏõsíŖ?įzoUwTå*Öú†[íéĄ#ã˛ē>īĩ7wp÷}wōWqmŲ)ôč&Vô`'ŋEįčz .ĒĶ‚Až‚KtHf´¨—)Jč™ cÕ ‰čœÍčŨ( įTTaŖáÁáûö%zâFtĢUĢ–ĄNaVÛ*@yęIWīŦB“ææ*\*TęD@xКčpeíÃÍ?Ę,čęä•ę8C~øqtč˛NÜøaÄžh˜ņđáÃ#÷Ü.ģA7\ŒJīQķÎŖ˛t¸ÃĪ$,ēÜUhGAWŋXPˇī‰UŅI2pÔ~ō|Đ ‡ä‹‚ļNzx>4ûÅĪ´ouÛEŊõÔG{ÄŖE'•4zÉMŸkėīH= •Û ›ŲĒËaĐUĀ=īĸKÜÜ]Õīšį^vė Uíœ šÛ>čĒŧũņ,k|yĢØ^ũŨĩ×ĀØ6Y­ēLĐMn҃ü–Ũ {ņÅĮę4”RĢõę\=ēaĐÕPaÍ]ՊŽęŲV/–Â̰ëĨRĐUUožÂŪ-ˇÜâVA6l˜ëÍ×{ĖiЍ>PŊFEø•ĒäB ęmõCĪ5TW /ũĖ‚ŽūV]fCÄĪ>ûl×ēŦyĻŋüō‹Ŋųæ›nد†+ŧŠgR‹H…ō;čú}ĢP†B_2 õ; ēĸ}ĄĪ\û]‹Ŋé÷ ēuŸč¨tũĩ‚=õvë䈿"GiĩKCšõ[Đ0olōüĐė•đrbH>úœbGęH¨üēį7lęÎüßpÛŊöæĶcÛõüēÛîūGŸŒ{ •—ßųČ­Ļ|ĐÁ‡¸üŪä9Žž [8Evō“Šz‰ôü/žøbŦ^C~U§ƒx-H$šG¨0áWųÕŧK ?WŅ¯‹æ´* E‡ĸjˆŧļ _ŗî§Åƒ¤õ9hA¨p5c-ĻEĸ4GWÃe5ŧ^Ĩm§NēÃ!¯™]ŨGu –š, eĪ>ûŦû7Km>Ā*LęļBŠ˙~¨ˇYSTöīWŒÖgŦá¸~qtÕdŅIŨWÁŪ?Ļ“Ō{Ô~ˆ†>͋ÖÜfŋjĩ)Ķ÷L÷÷S´x˜^ˇzÂŖķŦõ}Չ 5÷ĢNk˙ų×垿Åęļ_øËķíū}ęDŋíW×wÚĪwÖ|h˙Yč;¯iĒW@Öw@õ~„€ŋL˜§ī„Y•ĖVÛŪšÂĄQ:ą…ÄĐgû;R@BåGĐÕĒÉZE9ÜN—Ōvš›ĢÛwuéáVRös{}š­S7ˇŨ€WŪvˇ ē…Sô`'?i§?(WQīîŒ32ŦJĢmŅߨߑz*?‚n™råmŋ?v4,ŲoŖkåîķ˙ûˇß˙ˆĢĶĩxu[‹JéÚŧĒûdÎ÷Võô3Ũe„>ø|ąĢ#čN҃ü¤ƒtÍåͰM- ĨËĩ(¨jQ$?œS!FtāĢ:…ĩ#<Ō eV/˜ĒļS›īyĶB] ÍZWŊ–ęESXŅķyęíÔsúų„ēލ‚†øúįÖPŨpjv)4ĢWYCUŗ3|7/(xé䁆åęŋ-[ļtĄÍŋ-ˆ¤đŖ^@_įISˇ†9ĢN‹¯…×´Õįō¯ũË­¤ŊvŽh…f…=˙9Š>/ÍŗõĪ]œ-”UЕ×^{Í}ÖęÖåĨô<ú\üs}õÕW.¤é=ŠčŌ7Â̐Ļ!ëáu_ß}Q¸ỗkx¯nĢ^īW—ōí+ŋŊžËīõnëŊęģĻ!ŋZņYßAm§°KŦ^~­`­Åĸôžôzõ\ęAõ‹fiŽĩV5_ŖĪŅëLëäęũĐtŊŊfŋXŸžĶųāī¯Q ęõÕŧ\ŊVŊîŦzÛõ{Ņ}ĸ Ųå-$–Õwy‡  (ųt{ęŽ¯Ģšš•Ž=ۊ-î†#wėö„’Üā’+ÜvŸ-Yc6iîîĢ`Ŧ…ŽöÚ{×#ÜŊßāØãt §‚ ē€h8˛žƒŅ•Ã‘Ū´’´æXûKz!1瀑› ûĘģÜĨÂzõÜ|õ]›ūͯąē˙MúĘ:tyĖ Wî1ā%×SĢzŨ?| ÍÍŊŖswˇm÷'ŸˇqŸ“ĄũÕQ“ė…áãâž÷‰_ēįēđ—¸ļė‚nbt‘ŸÔŗė{`՛Ž!´jÎ?EúŌŧgN|$AP rtS­t‹ ‹ü3­ĸyÔcƌ 7CĶ‚^96€ė#č Aw{!č&AųIsBĩ@—æEũõ× €t‚ ģŊt‹  釠 (Ũ텠›X]H?]@8ë‘É6céoqĄ/ŨŠöA?÷‡  釠 (MzOˇqs˙ēfm:íƒFŊĻ…ģyˆ  釠 (ž5ߞû×õnĶšôŗÄî{s^¸{‡ē~ē€ņÕōĩvIŸé6iáę¸đ—.EīŊq¯én_ qō:čnÚ´Él/Ŋô’ģ=wî\w[— É ˙ú×ŋŦuëÖöÛoŋ…MØ ‹-rŸ‹V?–÷ßßŨNäõlĩÂō‹/žh/ŧđ‚mŨēÕ,XāžSĢ0Gũ÷ŋ˙uŸąĘôéĶ3´”įž{.öšæĖ™ãęĢĶ÷üī˘1ÃZĩje•+Wļ#<ŌŽŧōJ›7/ūDŪæÍ›­gĪžVŗfM;âˆ#ėĸ‹.Š}NYųôĶOíÎ;īt¯E˙ÕkKÖßČoŧÛošųwáŨwßuߟå˗ÛÚĩkŨßúî„Ū~ûmģįž{lɒ%aSZ#č Ė“cÛõƒŋ°É‹ÖąĀT/zĪ× še}Æ,w ōX^]šęĒĢÜãŽ?Ū~ųå+VŦ˜;`W¸É­]wŨÕ=öO?ũ6%]>įĀ´>}ú„MIEk—]vqŸ‘Ņ`•*U˛7†›æŠoŧŅ}~īŧķŽ{Ū2eĘX‰%\¸ķÎ:ë,ˇŠBQ28î¸ãb¯iܸqŽî˜cމÕũ]ũä“OlĪ=÷tÛęZÂzĪúûCą… ÆļĶoäŧķÎsm{íĩ—•.]Úũ­ī}VûBOŸĨ-ž<üđÃáĻIáüķĪŊFøČ‰•+WÚ>ûėc'œp‚m؞Аå{nĐ k+UĒ”û}â/Ú'ąŋ#õ$ÜÖmÛū ģK\Īî3}kãæŽŠ „ŠVÆÎYeũ?XfûLsī]û‰=ØÉ++VŦ°ĸE‹Z‹-ÜmĖęyÔ“[ f Ņ`”LtmXŊ×víڅMIcÕĒU.$\}õÕîv˙ūũ]Púāƒ‚-ķۚ5k\āhذĄģũæ›oēũõÚk¯ÅļQØîÚĩĢĢĪ*Üåˇß˙Ũîēë.÷š|ĐUŨwÜáęū.čŽ9ŌŽ:ę(×sî=ũôĶÜrKŦÎīíß#Ģ^Īũ÷ßßʗ/Û.J['*ô/^ėzw_~ųe÷;LFõĄëÜŨ^Ŋzšđ?yō_‹ž|ōÉîdDf˙.Ŧ_ŋŪõrëų† 6§-‚. ĀičîCo-°fO~æ.ĩ“Črú]oÛ1-ļŌg^n”9Ö9ęt;ũî‘qÛ%Ēč=ęŊ2\9˙$"čŠz™Ô#åļĪ9įkܸq°Õ_Ŋ:¸ā\Ґg؇=ļ.ĒĮôeGClŋüōK{ä‘GŦ}ûönį7ß|ģßüųķ]¯Ų{īŊįnΜ93öü>ú¨k­[ˇÎFeŨģwwÛõčŅ#ĶĄĒē¯žWûTī7úz€EC,_ũuW÷ņĮģ:íƒáÇĮļŨ°aCė1§M›Ģ×>Đũēté7ŦWCƒ5LĶīË#Fdz❐Pŧxq×+&'t’Fē# O_|ņEX# ĩģīžģ}ûíˇîļBŨšįž›a…oíĮAƒŲŗĪ>ëŪzÉūųįØ6úŽ :4ļü0`}žNOC[=…ĮįŸ>ļÔŗŦ“'úŒŖ´/ĩ˙´Íc=fSĻLqŸ4čJˇnŨbAW'^}õU÷ŧBûwASŸ§îĢ!Ėž‚°‚ĢžŗQú>iÛhģz‚5 \#?üđØ{ÖkĐHЍmÛļšĮ~đÁŨ{zæ™glęÔŠnøxHĪũÄO¸íô}V˜Ė,Čë{Ēũī÷ĨBä„ bßĢ(=ˇļQĢN¨čw ēúž*¤gU~üņĮ wėąĮēŅĸ÷ĄĐģŖaĐūd›ŸR‚. …é`_sÆúöíkW\q…ZĒ^¸Fš~°¨WęöÛoīŠ’¨ +gŸ}ļ )ĸjõ:E…‚ĀņĮī^CXÚ´iÛNüPO_N3ŖPâ‡7ûĸĀí˙Ö÷|ėØąąÛę) ˇöē‰zŌĸÛ¨(¨ŊōĘ+ļĶc‡Ûųĸ "=ôPŦŽe˖ŽNa.ē­ųÚWŅ÷Ō¤I+Y˛dėļzaÕS( =™íKŊöYŗfũõ˙ßgœ;āW×Đō…2r=–^‹ėšuņÅģĀ-˖-sa-Î|Đ ‹ūōVïŖme˖uõáįĨĄĢĸ ŨŅŌģwīØs+TzęŠqÛ)RÄũ7Ģ Ģ+ŖÛ_sÍ5ąí<h…Cũģ̰Ļí2›WĨú^†=ēūū™E'RęÕ̎J­Zĩ"ø×\ä=öØ#n;•č|bhĒXąbÜ6*:tˆm§3~(v´ø}ŠĒđŽĪ5Ü&,÷Ū{¯{L}Ÿ5*`õęÕîļNh]ũõąįĖ A7žöGėīH=…Ž ū÷ŋ˙Y§N\ī€đu`|Ũu×eč ‰ROˆHÂŪ5¤ŽčÁN^SP‹öN†aJ=ŖzūļmÛē05iŌ$×ã¤įC §6}O}¸É,čúĢy Ÿūš –ģíļ›Ģŋä’K\īæôŨ˙ũŽNáMĐęąSQÖ¨Q#ÃãjūŸzļÔû§VõČę÷Ŗžˇ(=vV=ēz|Qo—z&ĩÚOC† ąęÕĢģúčīMĄûÖ[oėkČą†õęˇ\ŋ~}ˇ“‚‚†ÅĒ]ŋg….Í UĐûĢPĄB†žĀčį žž°÷/¤E|j42ŗŪēœĐ°_ )õÂ×āƒîžûîëzĩ˙üžõ'AôētRNuZÜÉīc}šoŦzí+˙o›öˇęô8ÚV=‹ ŖĒĶwŅĢS§ŽĢĶ\sõŽ3Æ.¸ā‚ØgYĐÕ÷íôĶOwŸvÚiî3÷=ÖQúŧüãømwD¯S˙ë;Ŧæ¨Ī>ûĖ=OØŖĢíÅ~ë­ˇÜsŠį\ß'í}u?Ŋæ(ôPoŋ¡ļĶ}ubD÷Ÿ={vlģģīžÛÕ)ÜëģĻâ‡ëûęiú‚ętUû[ŋs_§â{tīģīžØUYŋh™NŌFŋ‡¯0ŋ#žYûŅūˆũŠ éŠįECôŽŊöZ7wIŊi¨ ͕ôgÃ˙ÎM7Ũ”á =RKô`'ŋé`Z͞Ēn¸ÁÍÛSˆĐÁĩ†+f抧žr÷É,čjd‚ÚtĐå{Úz<tûõ(õ’Ў(JATaW+>ĢwP˙ՂB ! iÖãîhŽî?üāļ Ãŧá‰%õÚĒ^5ŗáÃ>Čhr4덜y晎MAŦ0ņAWCĪ=…rõ¤k¤@”në䈆ĄËW_}å~xÂB'4ô˜UĢVuķbŸ|ōIwEûÆ˙{¨ÚFŊįQꉭV­škË,čúQ:=šRHĶ0ut?@Ŗ2ŖĪuŋũösīYC”ŗĸ`Ē“YŅČ퍊P`Ôsë;ĨĐöökßhßé{Ž0Ģ€¨°­“;Qũúõs¯]+H˙ûß˙vŊķ ĸÚîûīŋwÛ(lëyu'\ˆî /t÷ĪéŨĨ“z-Zü,Ú{ŸÎ´˙cGęH*ęŅ™rÍ!Ķ™ķC=ԅÍũŌÁœzļėúîģī\B؇Ô=ØÉo .a Ā¨×á‹zdðęí(čęr, áw]=aēOfAWŊ{Q:)¤ëŠWÎ÷–†%ŋƒnV+9+d„¯-,šY˜ø .FĨwõ¸FųΞiĶĻîļūŨĶípņ3õ ë$E8\]!ҟXQĪēę4œ<äGdtU¤4ô7ģôoĒBdží†Ô‹­^\]?<=+tE'j4´;úž>ø`7Ÿ7Jķ†uâĀ‚đåŸ˙üg†^SĐcDˇĶ˙wü ,PFU„üB\ ēz,XPđĪĒTŠRÅ}69Ĩ˙it€ž3:':i_ÄūŽÔP ô?m-čĸ!bšKσ!5ׁ‡ u —ÔÛĻë"õDvō›zŦü*¨†ĒĄĮBĒŅ:¸ÍĖŽ‚îå—_îڐŧŖŨŋ ēū1õ[ķ Ué´Ę›Û Ģđå{¤w6čĒMí €aŽ/ęÕ+Lv&čŠNr¨'RŊĨ:ŲĄŪאžÖP°ŌˆáŽ;ē€ęWÖĸczŪ°§_.ŊôRזYĐUoąFčošÉ.…JŊnŋh˜†ÕkŽGķ‰ÃĮ2ķwAWïõģ}÷tRTˇuBIáĪ_rGķiuBDs”õˇž3Ú˙Ú7z=ū1DķĶũ‰ÍŋWī°~_{īŊwlßkôî§ßPčæ›ovm ēzīūšÁ;*úürJ¯WĪįį…ƒ  H:(͇†[逡\šrî[+Ø*č ~GCåō‚‹Q¯n^ÍĪCōˆėä7Íׁļ†,k¸Š˜5ŒTASõžgVŊĒ>°ų9ēRīëü÷RÛjĶŧæÛjĒV'öŊS>čju\Šõú–´Ŋ†6tĐA.‰†ū*iNŦzũ:/ģė2W§@ŽččüWũVô¸:Ĩ9z_ ķš\ģvm׋­ĄpŖ9ž Æú ̧QM÷õ‹\i8­‚€ŸŖĢž(˙žŖ—ROĨ^ˇî¯Ĩašz­š[ĒQz;Z‘öīčߗÛnģÍ eUK4Ŋv˙9넁‚—ö›_­ųčŖŽģŌčŅŖŨöūZĩ ŧQúwTû\aTÀõ]ĶgŖđ¤Å—ôy‰ŪĢæģęqÔĢĒī†>GčĶãĒh*‡^Ŗ†ôúđĢŪK-ФšÂē­Ūyõ@j´hî´öŸžcē¯V2V8ÖkŌIJ¯YŗfîūzMš¯î?oũû¯PĨ÷ŦļpŽŽž7ēč›^Ŗž¯z>õļęųu’IŊ¤z.dWŨVčÔck;Ŋ^ŊvÕGŧöŖ~':!Ąī°ö“žĢĒķC˙õ™i8ŊžģsįÎnk[ Õößu…zÕ'‹QÅĶūˆũŠ atv]‹Ūč DgûÕ[{â‰'ēU%ũZAЂAfŠÔ=ØÉoZˆĮ‡‡hŅA~´÷FßŊp›hQ đtųXGÛũüItũFžh¸ŋhžĄ¯ķÃIušžĖVĄ>G¸Hī ‹Ņ\ɰ=ZÔĢë¯5šYQSxķÔķį{ÃĸP‘›aŸ>DĒøŸ)Ü7ZäHsIŖuę9TŠō‹AéJØĻÛ~čjX4üVŖ <-¤^Ōpģčp^õžëä‚ŋ­ +Ŋßīšû>žŠNĘDyR0 ˇ‰m]˛$l÷%:ôÚ/Â}‡uÂĮķC3+z_Ņ‘B>ЇE‹ĨEW‘Ö(õƆÛE÷ĨN%A7žöGėīH=yFg¸uPĸÕ?u†]ŊD焿ƒiV”L:읃õ†!uDvō›z’4ZA‹Ļé€[ŋ˙üį?îģĨy՛ĮUQĪúŖ4dRC’ĩā3Ō‰#ŊO 3õv)œęū:(÷‹įč= &:ĄŊNŽŽËĢ=ŋBĨ°î¯ëņ†Ŋœē­k]âE=Īē”Lx}^…T=ĻhŖzĩ˜SAKÔ¨žˇđ=Ģdļ(•Ū—öĨz€ĩ2ązŗÕ›œÛŠ š›Š“\:ĄÎN]+×Ū:ë‡Ö*ˆų÷¯Ī$¤:ĩų9ĸ!ĩëûĻu ÔC­ĪG=’™­? ŪtlŅBKę‰ÕPyŨW¯}Ē^f­æŦā¤:ŋ5Ŧ]ß!Õi€’Ŋ^´>ũWßŊđģëŋWY•č‡rˆ[IUsõ´ŠĨ<žˆdĸƒg ACę(Č ›_üeOčÍŌNf¨÷]=ā™ JW]@ŽhnĄÎĸëLö)§œâ†!뚄ęMĐŲ}õ &ę­Đüāė^šÉ/ƒŽz—Î:ë,WüKũ7ė=ú4ˇ]s‡ŗZĖ-]tŲĻáŠJĻECš7oî.å y^Z4ęņĮw‹ÁD­)Ŧ4ė-:'…[*]]/ÚĪĢÕČĢŽē*×Ãw •tYŌŧ1ͅŌJ˜ę9ō‹FiΝæįiž_*ŌÜ,õę&ËÜaäN*]ĀŽt1sæĖągŸ}ÖÍS=öØcŨ%Î;ī<ˇ¨ˆV%õĢaĻ­âéöAáFĐ€ôCĐ€4ĨEŖ´Â§.UrņÅģEŖ4ĮOīŠ§žŠ[­2ŨhĩQ ËfÎcáGĐ€ôCĐ€4ąlŲ2wŠ-U­Z57 šFîŌ ē|‰.Ī€ŒZ´há.QÂ  釠 )H‹FM›6Í]ķī˛Ë.s—(Qĸ„5iŌÄ]ÃsŌ¤I)ąhTĸi(ˇö›ŽŨ‰Â‹  釠 )`ÕĒUööÛoÛŨwßmuęÔqŊĩ't’ŨxãîâņēdrFĢK÷čŅ#ŦF!BĐ€ôCĐ€BčË/ŋ´ÚÕW_m•+Wv‹FÕ¯_ßētébīŋ˙~Z-•hŗgĪļ’%Kē9Í(œē~ēä4löƒ>p+7hĐĀ>ø`;捪ŦUĢVÖŋ›5kVxäąĻM›ZĪž=Ãj]H?]H2K—.ĩĄC‡ÚÍ7ßlUĢVuÐĪ<ķLģķÎ;mĈöķĪ?‡wA‚édBŠRĨlÆ a ‚.¤‚. Í›7Û§Ÿ~jO<ņ„ë5,]ē´&{É%—¸Ä)SĻØĻM›ÂģĄh!¯ŪŊ{‡Õ(ē~ēVŽ\ioŊõ–Ũu×]VģvíØĸQíÚĩŗ—_~Ųžųæ›đ.H3fĖpĢWŗZuáCĐ€ôCĐ€ŌBFĪ<ķŒĩnŨڎ>úh;č ƒė‚ .°|ĐÆŒck׎ ī‚$Ö°aCëׯ_X$W¤HwĀCĄP(”ô)úˇß#č@.(´Ž7Îzč!ģđ ŨĸQ•*U˛ĢŽēʞ~úiûâ‹/Â점Ņĩ‰Ë–-Ëpr ‚.ė„Å‹Û+¯ŧb7Ũt“|ōÉnr­ZĩŦC‡nҍ_~ų%ŧ R€Vž0`@X ’A˛ EŖĻNęēôŌKŨ ŧ*ú[ujĶ6H}úŦ˕+Įį @!AĐ€˙§ŪXõĘĒwVŊ´ę­U¯­zoՋĢŪ\¤¯úõëہÃj„ēŌ–æĪj­æĶj^­æ×jž­æÛjŪ-‹F!jŌ¤IVž|yÛ˛eKØ’ A@ZPhՊĮZųX+ k%d­ˆŦ•‘ĩB˛VJūNŊzõlĐ Aa5H2])I×ĻÕ5ju­Z]ŗVÐu []ËV×´Õĩm5aÂĢXą"Ŋē$9‚.€BO—}™2eŠõėŲĶ.šä+Y˛¤•.]ښ6męúôĶOYDyĻnŨēö /„Õ ‰t:?˙üŗ[4ęÎ;ī´3Ī<ĶõÖV­ZÕnžųf:t¨-]ē4ŧ gƏīæt˙ņĮaH]IoÖŦYÖŋkÕĒ•uÔQnŅ(]×´[ˇnöÁØēuëÂģ U§N2dHX ’A@Rųíˇßėũ÷ߡ.]ē¸ËšxāVšreģúęĢŨĨ]žüōËđ.@žĶĒÜú^nŨē5lI€   @-Z´ČõŒŨxãąEŖÔ[v÷ŨwÛÛoŋmĢV­ ī$…š5kēĄō ųt䛍7ēk‘ūį?˙ą&MšX‰%ŦL™2vŲe—Y¯^ŊlÚ´iŦf‹BcôčŅVĨJÛļm[Ø A@ÂüôĶO6|øpģũöÛ­FގļZĩjvË-ˇØ˙û_[ļlYx PŠ^Ŋē 6,ŦŒ  Ī˜9Ķžzę)kŲ˛Ĩyä‘vČ!‡ØÅ_l?ü°}øá‡ļ~ũúđ.@ĄöîģīÚ 'œ@¯.I†  G´h”†n>đĀvŪyįšEŖŽ=öXkĶĻ=ûėŗ6gΜđ.@J:í´Ķė7ŪĢ@"čȖ… ڋ/žh7Üpƒxâ‰nōYge;v´‘#GÚ¯ŋūŪH úūk!5zuH]q´hÔĉíņĮˇÆ[ņâÅ­lŲ˛ÖŧysëŨģˇ}öŲg,Dhîųˆ#ÂjP@ēėĮ´7ß|Ķnģí6;ãŒ3\oí)§œâŌB;ß~ûmx š ģ 9t4Ŗá•Z4Ē_ŋ~öĪūĶ*T¨ājذĄ=ōČ#6~üxv’~WžŦaĖ ātˇfÍ5j”Ũ˙ũV¯^=;ā€Üĩ?¯šæ4hÍ;7ŧ €ЂTZ˜ <‚.b,X`/ŧđ‚]ũõąEŖÎ>ûlģ÷Ū{íwŪaŅ( AÔĢĢK é’C `tBlÆ öÉ'ŸX=ÜĸQŊŗråĘŲå—_1c†ũņĮáŨ$ˆæ´W¯^=ŦųŒ  "?üđƒŲž}{w0­ŪÚSO=ÕnŊõV{íĩ×ėģīž ī ŠWWStiPpē@’ÚēuĢ}ūųįöä“OڕW^iå˗ˇC=Ô5jdŨģwˇ?ūØ~˙ũ÷đn ØĐĄC­f͚a5ČG] IŦ^ŊÚÍíģīžûėÜsĪu‹FwÜqvíĩ×ÚķĪ?o_ũuxIH'Š*WŽlãÆ ›@>!čdŪŧy6xđ`ģîēëėøãˇũ÷ßßÎ9įëÔŠ“ũī˙sÁ@á4dČĢS§NX ō AČZ4j„ öčŖēĄĮE‹ĩ#Ž8ÂŽ¸â ëÛˇ¯[4JŊ@RƒĢTŠ’ģ.5Č]¤•Yßūf_ŸgMzOˇZŨ&å[9ĸŪuv@™c­ô™—Û1-ļĶīz;n›ŧ.užd—ô™n_ûږŦ`./ßt™¯ēuë†Õ t‘ļnÛf=ŪûÆöšf}Į.ĩąsVŲįËÖĻ|7wĨ{ŋ=1Ív|ĩeËĢXąĸÍōAiáņ÷ŲÕĪͲɋÖąÁt(O_f­δm᎐Pƒ ˛zõę…Õ ÁēHyŗŋũÍõšn“ŽŽ €éTūųôLûdūĒp÷H õęęŌ`“&M ›@t‘ōî}ãkë3fI\đKˇŌoÜRëôÆŧp÷H°ZũúõÃj@]¤ŧFŊĻššĒađKˇĸyÉZœ @ūÚŧyŗ•+WÎĻN6€!č"åõČd›ąôˇ¸ā—nåŗ%ŋš} ˙ 0Ā4hV€!č"åéR;ačK×ĸ} ˙mÚ´Éʖ-kĶĻM ›@t‘ōēÛ A(8ũúõŗ† †Õ ēHyŨ텠 œ7Z™2elƌaČc]¤<‚îöBĐ VīŪŊ­I“&a5Čc]¤ŧœ]-`u}ûŽÖsāиļD—v:ÛŖOŊWŸÛBĐ ֆ ŦTŠR6kÖŦ° ä!‚.R^NƒîgKÖØ?ūņģ¸é•qm‰.{īŗ¯sAøúÜ‚.PđzöėiM›6 Ģ@"č"åtˇ‚.PđÖ¯_o%K–´Ųŗg‡M t‘ōēÛ AH=zô°æÍ›‡Õ t‘ōō"čvčō˜VúpwģX‰’vãí\{tû~/ ˇSΨe{îĩˇÛnßũösaõ­ffØŽ[īgíč*'¸mvŲe;ö„ĒöpŸį2lãƒî}ŨûZéÃËšm‹-n×Üt§M˙æ×¸×šŨBĐ’ÃēuëŦD‰6gΜ° ä‚.R^nƒŽë!EŠē…Šî}¸—Pí4WûũÄļ}âŲ˙ÚŽģîj'Ÿz† §ö`-ŽžŅvÛ}w+[ūHˇ°•ļëũükîžį\ØČmĶųą~v|ÕS]Ã?ž‚Žž÷ ƒqáļS÷>V­zMˇBvøZŗ[ē@ōčŪŊģĩhŅ"Ŧy€ ‹”—Û ĢĐ9ō“ŲąúOæü`ûíŋŋU=­FŦNaõĐ"ÅlōŧŸ2´ˆ{üžƒ_ĪđX ēg× îyĢ×ĒkEŠ•ˆĢĪn!čÉe͚5VŦX1[°`AØr ‹”—Û ›ŲĒËaĐŊųî.n[…P-"ÕöÖģ­× aą\t}yvØ{vÕõˇØQĮ ŊšĶëÛŗZu™  ¤ž|ĐZˇnV€\ č"å%:莟ĩÔͧ=ūäSlęÂ_2lwe›˙tGOvˇG:Ī^•ņõüoŌWVŽBEˇp•æáĒŽ  ¤ÕĢWģ^ŨE‹…M ‡ēHy‰ēšĢí_Ū*Ã6ZdĘ_’č•˙}ėę4lY JiAĢčļuę]č²Ę] ŊtîÜŲÚ´iV€"č"å%:čNûf• ´ē~îģÛĀWßĩÎ=žr‹U)”ę1t]mÛŊß`wûÄj§[×^ŨÜÜ^ĶÎ]OW WųĮ'čéeÕĒUV´hQ[ŧxqØr€ ‹”—Ķ Ģkߖ*SÖ-0ļÕ­ąrF­ØmõęV=ũĖØTŠ|W—öÖG3ŨcD/Ôĩ×3ææ*$ßpÛŊ.0ûmĘW<Ú]š(|^-\UåÄjqõŲ-] yuęÔÉÚļmV€ č"åå4čĻb!čÉkåʕV¤H[ļlYØvA) ģŊtävĪ=÷Ø 7ÜV€DĐEĘ#čn/] š­XąÂõę~÷ŨwaØ ]¤<‚îöBĐ’_‡ėĻ›n ĢĀN č"åõČdˇ°TúŌ­hh_Hn?ũô“ëÕ]ž|yØ˛‰ ‹”ר×47we\đKˇĸ} } ųŨ~ûívë­ˇ†Õ ›ēHyŪ˜g}Æ,‰ ~éV´î{s^¸{$Ą~øÁõęęŋ`įt‘ōf÷›5ę5Ũ&-\ūŌĨčŊk|ųŨÚp÷HRęŅUĪ.Øy]¤…žŖžąĢŸ›e“­‰ Š^ôžõ۟ũM¸[$1ÍŅU¯Žæė€CĐEZØēm۟awą5ė5ÍúŽ]jcįŦŠ „ŠVôõ^õžŸŊØí…‹V_Ö*Ė`įt‘V4Œųž7æÛ%}ĻģKí$ŧthG^ÔŪ=ē†U¸đfĢųĐ'ņÛ$¨č=ęŊ2\(ŧt=]õęęúē ûē@‚,\¸ĐjÕĒåʇ~huëÖĩĶO?ŨfĪžn Yēá†ėž{î ĢĀt<ļmÛ6ëŨģˇ+VĖũWˇŊZņâÅ­sįÎļyķæČŊ s˖-sŊē+WŽ ›@ē@ŠöâęīĖh™ÆÛqĮgSĻL › NÛļm­S§Na5ČAČ;ęÅÍʰaÃŦTŠRÖž}{[ŋ~}Ø 1‹/ļĸE‹ÚĒUĢÂ& ‚.KŲéÅ͊†"ļjÕĘ*T¨`cĮŽ › ĻM›6nÚø{] ‡rŌ‹›•QŖFYšrå܁ė¯ŋū6€-Z´Čũ{ŗzõę° ē@äĻ7+k׎ĩvíÚYéŌĨmøđáa3XëÖ­íÁ Ģ@€  ė„ŧėÅÍĘĉ­råĘÖŦY3ûņĮÃfilÁ‚îߟ5kքM ‚  dS"zqŗ˛qãFëØąŖģŅāÁƒÃfiŦe˖֭[ˇ°Dtŋ‘Ŋ¸Y™9sĻUĢVÍÎ?˙|[˛dIØ Í›7ĪĶt9‚.°ųŲ‹›•-[飯>ę‚vŸ>}lëÖ­á&ŌL‹-Ŧ{÷îa5ø] Ų‹›•ųķį[íÚĩ­F6wîܰ@™3gŽ•(QÂÖ­[6#čq’Ą7+ Üũû÷wŧk×ŽŽˇ@zjŪŧšõčŅ#ŦFĐb’ą7+ß~û­]tŅEvâ‰'ÚôéĶÃfi`öėŲV˛dI[ŋ~}Ø@Ú#č–ÜŊ¸;ōōË/Ûa‡f:t°ß˙=lâš6mj={ö ĢH{]¤ĩÂԋ›•+VØW\a+V´>ú(lÂf͚eĨJ•˛ 6„M¤5‚.ŌVaíÅÍĘȑ#íđÃˇë¯ŋŪÖŦY6HQMš4q'ęĀv]¤TčÅ͊ނޝ‚/€Ô7cÆ +Sόmܸ1l mt‘VR­7+ÂŦĄĖŌŦĄÍR[Æ ­_ŋ~a5i‹ ‹´ĘŊ¸YŅâTZ¤J‹UiŅ*ŠkÚ´iVļlYÛ´iSØ@Z"č"åĨK/nVtų!]†H—#Ōe‰¤Ļ ؀ÂjŌA)+{qŗ˛eËëÚĩĢÛũû÷Oë}¤ĒŠS§ZšrålķæÍai‡ ‹””îŊ¸Y™;wŽÕ¨QÃj׎mķįĪ›rõëס†Õ¤‚.R Ŋ¸oëÖ­Ö§Oˇ}ôQ×Û 5Lš4Éʗ/ĪīöēHôâîœ%K–ØųįŸoÕĒUŗ™3g†Í ŠzõęŲ AƒÂjŌ A…Ŋ¸š3xđ`+^ŧ¸u먑ëp)`„ îōbôęŌA…Ŋ¸yãĮ´f͚Yåʕmâĉa3€BĻnŨēö /„Õ¤ ‚. %zqcøđáVētik׎­]ģ6lPHŒ?Ū*UĒdüņGØ@Z čĸĐĄ7ą~ũõWkĶĻģLɨQŖÂf…D:ulȐ!a5i ‹Bƒ^Üü5vėXĢPĄ‚ĩjÕĘVŽ\6HrãÆsĶ´Ō:醠‹B^܂ą~ũzkßžŊ•*Uʆ 6Hr5kÖ´ĄC‡†Õ¤<‚.’Ŋ¸Éaʔ)vÜqĮYãÆmųōåa3€$5zôhĢRĨ ˙vŌAI‹^Üä˛yķfëÜšŗģҁÃfIĒzõęŒČ¤‚.’Ŋ¸ÉmöėŲvúé§ģ˗,Z´(ldŪ}÷];á„øˇVēH*ôâēdIĪž=ŨÉũ—K˜Éí´ĶNŗ7Ūx#Ŧ et‘čÅ-œÔŖĢž]õđǧ@r9r¤tŌIüÛ H]8zq ?ÍŲÕÜ]ÍáÕ\^ɧZĩj6bĈ°€”DĐEĄ7ĩh5f­ĘŦՙĩJ3€äĸĢ° @: čĸ@Ћ›ē´ēĢŽģĢëīę:ŧ’ƒN&jø˛†1ęēČWôâχ•+WZĢV­ŦB… 6v먰@Ņ‚TZ˜ €TGĐEžĄ7ũŒ5Ęʕ+gmÚ´ą_ũ5lĪtrQ—Ō%‡He]$Ŋ¸émíÚĩÖŽ];+]ē´ >îĀ@ū2dˆÕŠS'Ŧ %t‘'čÅEN͟?ßj׎m5jÔ°šsį†Íä?ū°J•*ŲøņãÃ& =‚.r…^\ä}oú÷īīžG]ģvuŊŊī…^°ēuë†Õz]äŊ¸Čkß~û­]tŅEvâ‰'ÚôéĶÃfyL'•*VŦh&L›(ÔēØiôâ"Ņ^~ųe;ė°ÃŦC‡öûīŋ‡ÍōĐ AƒŦ^Ŋza5…A;…^\ä—+VØW\áz›>účŖ°@Q¯nųōåmŌ¤Ia…AŲB/. Ęȑ#íđÃˇë¯ŋŪÖŦY6Č´úõë‡ÕZ]ü-zqQĐptx|ä­Í›7[šrålęÔŠa…AYĸÉFC˜5”YCš5´@Ū0`€5hĐ Ŧ P"č"Sôâ"Yiq*-RĨÅĒ´h€ŧąiĶ&+[ļŦM›6-l Đ!č"zqQXčōCē ‘.G¤ËČŊ~ũúYÆ Ãj ‚.bčÅEaŖÕbģvíęNĖôīߟ3@.mܸŅʔ)c3fĖ›(Tē …ŪÜšs­FVģvm›?~Ø `'č˙Mš4 Ģ(TēiŽ^\¤Š­[ˇZŸ>}Ü ›G}ÔõöØy6l°RĨJŲŦYŗÂ& ‚nšĸŠjɒ%vūųį[ĩjÕlæĖ™a3€lčŲŗ§5mÚ4Ŧ Đ čĻ!zq‘lŋˇŽ;ēy‡˛oũúõV˛dI›={vØ@Ą@ĐM#ôâ"ŨüøãÖŦY3Ģ\š˛Mœ81l°=zô°æÍ›‡Õ Ũ4A/.ŌŲđáítéŌÖŽ];[ģvmØ ëÖ­ŗ%J؜9sÂ&’A7ÅŅ‹ üå×_ĩ6mÚXšrålÔ¨Qa3€LtīŪŨZ´hVôē)Œ^\ ŪØąc­B… ÖĒU+[šreØ B# 4×}ŪŧyaI ›‚čÅvL í´oßŪ]Beذaa3€ˆnŨēY˖-Ãj’A7ÅЋ dߔ)Sė¸ãŽŗÆÛōåËÃfZŗf;qē`Á‚° €¤EĐMôâ9ŗyķfëÜšŗž9pā°Ÿ|đAkŨēuX @Ō"čĻzqÜĶõBO?ũtĢ[ˇŽ-Z´(lŌÚęÕĢŨ‰T~€Â‚ ›GžZžÖzk5{ō3ĢÕmRž–ōõ˙mG^ÔŪjuז×ĨÎÓėĘŸÛ3.ĩĩļ„ģČSųũģĒųĐ'Vá›Ũo*lKdáw…Â@#´r9…A7—ļnÛfOŽ]b—ô™n?úÖ>œģĘ>_ļ6eˌ?Ëģ_übßœgMū|Īs–sMRä=˙ģŌwė™ņßÚiđģzoö/öĀđųîß~WHFĢV­˛ĸE‹ÚâŋÃ&’A7—žģØŽü…M^´&îā5ÕË˓—[ƒžŸrPŽ<×÷ĪßÕĩΧīīęĸ'>ĩšßķģBōéÔŠ“ĩmÛ6Ŧ étsAÃ*›ôžn“ŽŽ;XM—ĸƒōFŊĻ1ÜyFŋĢÆüŽūÜüސ|tíé"EŠØ˛eËÂ&’ A7猘o}Į.;HMˇŌņĩ¯­˙¸%áîräūáķŦΘ%qßŗt+÷žūĩ=ũáŌp÷îž{îąn¸!Ŧ ŠtsAŊNãæŽŒ;@Mˇōî+ė˛~Ÿ…ģȍāwõ×īĒųS3ÂŨ¸+V¸^Ũīžû.l itsáŦG&یĨŋÅ Ļ[ųlÉon_yĄŋ+Wôģ:›ß’T‡ėĻ›n ĢHŨ\ĐeAƒĶt-Ú@^āwĩŊđģB˛ú駟\¯îōåËÃ&’A78 ß^8 G^áwĩŊđģB2ģũöÛíÖ[o ĢH Ũ\ā€|{á€y…ßÕöÂī Éė‡~pŊēú/Ɇ › o/#¯đģÚ^ø]!ŲŠGW=ģ$‚n.p@žŊp@ŽŧÂīj{áw…d§9ēęÕ՜]’ A7rs@~Öy Ŧõ ˇÆÕ'ēÔoÔĖ.o}]\}n äČ+9ũ]}đųb;åŒZv_÷žqm‰,įū螷C—ĮâÚr[ø]Ą0ĐęËZ…€dBĐͅœĢ|hĢqVŊ¸úD—Ō‡—ŗ“NŠWŸÛÂ9ōJNWīM™k˙øĮ?ėęßזČōņ—ßšįåԕާĢ^]]_€dAĐͅœĢtĖåôwEĐ Î 7Ü`÷ÜsOX @!čæBNČUĸAwÜįߨĀWßĩˇ?žˇ/Ÿ-YcÃFOvÛŊōŋmú7ŋÆmŖōɜėų7ĮØs¯ļ _-kŨņŗ–ēĮūጸívļp@Žŧ’ĶßUtG~2Û}ŋĮ~ļ0n[_Ļ.Xa/ŋķ‘ÛîõąŸÆĩûōáĖ%n›ßúĐĻĖ˙9C[tߝü•Ûvô§ķâgg ŋ+˖-sŊē+WŽ ›(Ũ\Č隊‚îigže—ļø—í˛Ë.î@YåÄj§Į˜ßÖŠ›tđ!ąmTtģk¯ļû׍ˇŲž{îÛf×]wĩ ›4ˇÉķ~ŠmŖ {|ÕS­ÅÕ7ÚŽģíÛö˜ãOļ÷&Ή{Ų-#¯äôwåƒî%-Z[õZu3ü^47ũĶE+cÛęoĶčīEEŋÁÃĮÆļĶoįü‹/ÍđŨs¯ŊíÚ[î˛KsÛø Ûā’+ŦΚdxŧŗĪŋ(Ãīog ŋ+&mÛļĩN:…Õ‚n.äô€\EAW•Ž=Ūz=÷ĒëUUO”ęNũv=ņô˙D_îzœū7é+ë5h˜VúpwŽž&m×cĀKnģ6íîp=YīLüŌ_Õ]{s‡Øãé@^uå+mb#Æn7ŪqŸĢĶYáëĖná€y%§ŋ+tUjÖ=ߞ{m” {ŠÕkĐÄÕ鄑ßöĘ6˙vu7Üv¯IĄßKįOŲŪûėk‡Q!ļ~O:!Ôå?ÜoOŋĶs/lėîûøĶ/ģm|ĐU9­F{úŋīØkcĻÚE—ļpuסī÷Zŗ[ø]Ą0Yŧxą-ZÔV­Z6īēšĶrŨŊöŪĮ [ŽÖW<úXbũí–movaxÚ7Ģ2lwûũ¸ƒh QÖíV×Ũânŋ?m~†íÔsûĀãũcˇtwÛ}w ĸÛŠ—÷ƒÎPˇ3…r䕜ūŽ|Đ=âČJ†öOúú'Ûc=3œČŅ´Öđ1Ô+ĢĮĐJĘē­QE‹ëŊUŅ”Äxō…7Ümtõģēđ—Øvę5Ūo˙\ø Ÿ'ģ…ß ›6mÚXįΝÃjōA7rz@Žĸ ĢpÖkčãūWīËGŗŋuáö‚Æ—šƒë§‡ŽtõôänW8ǞŨrĪCŽ'+ŧ¯Š‚î‘•Ž‰Ģ×ĐN uëŗ[8 G^ÉéīĘŨf-¯‰k+Q˛´ģPXīËØ‹\pÕüu=Æø/–šz]LˇĢž~Ļģl‘zuÃûnē|y\›FNhZ@XŸŨÂī …ÍĸE‹ŦXąbļzõę° €|EĐͅœĢ¸Å¨ęœW¯^'õųÛZ„ĒS÷>Võ´æéR¤h† Ģĸ!’ûíŋl›b%Jē]?ŧYEAWķ€ÃįÕpiÍC ëŗ[8 G^ÉéīĘ] ŲÛÔÛZ­zÍØmõōjČ~å*'Ú>ûîëî§!Ęū7Ļ…Ú´æ×6lvĨëöŋĢ#Ž<ĘnžģKŦ÷ÖŨæWĩ{^x:渓âęŗ[ø]Ą0jŨēĩ=øāƒa5ųŠ › 9= WÉęōBaĐՂ9:ˆVo”æöüēë}ē÷‘ŪŽ~Ā+og¸ŋV‘UĪ”æ –-¤Ûæč*'Ć^fuy!‚.’ENWáĒËŅ ēú-čđF0œwŅ%vW—öĖ˙gŸĖųۚ^yu† ë‹ÂŦFM4žŧ•ĘŦm.nzeŦMˇ3ģŧAéhÁ‚ŽWw͚5aų† › 9= WÉNĐU¯Ķîģī‘i0ŊæĻ;ŨÁu˙—ßrˇûŊ4܅ßp;­úĒíFNøÂŨ&č"Ųåôw•Ũ ûâˆ˛ ĻZÄJm|žØŨV¸}Ŧ˙‹ļŅÉ$ÍöŋS‚.¯e˖֭[ˇ°€|CĐͅœĢd'čęZá3jŦy‚‡)æŽ{?˙šĢĶđJõPŊōî„ ÛÖ>§žv鯊KĐE˛Ëéī*ģAW׸Íl¨ņŗÃŪs ĩŠÍ_W Á鎿ƒ¯Š†,—)WŪ…]Ũ&čņæÍ›gŋˇĩk׆Mä ‚n.äô€\%;AWåœ ēƒčZuĪwspĩЎĢŌj°Ēŋ§kOˇ.tšļ&W\å.)¤áÎÚæēö÷ď ‹d—ĶßUvƒŽVCVH՜\ ŌīJß]ZH‹Né1^>ÎmÛwđë.üj1+MĐâTGsœ;ŠôŸg^qÛtĖĩhŅÂēwīV/ēšĶrßvßÃqõē<–aÕØ)ķv‹æč%'T;Í­ėĒž' ŸlÜŧĨŨõāãąmuũ\Œ+ājEg]>E×č>ž.CÔŽCį¸įՂW:čëŗ[8 G^ÉéīJ+%ë;ŦkJ‡mēL—ōˇ5Ī] ĩ|ęnžîe­Žĩ7?˜îŽŠĢĮĐĩĒũļ/žõĄĢSÖč ũũŌÛãcí~ÁĒŽŊÆ=¯ŽÃ=Ņ´ŗ…ß ŗ9sæX‰%lŨēua GĐͅœ§bá€y…ßÕöÂī …]ķæÍ­Ga5 GĐÍȡȑWø]m/üŽPØÍž=ÛJ–,iëׯ›H(‚n.p@žŊp@ŽŧÂīj{áw…TĐ´iSëŲŗgX @Btsōí…rä~WÛ ŋ+¤‚YŗfYŠRĨlÆ a CĐͅŗ™l3–ūwpšnEû ÎŸûČ ú.ņģúëwĨc€TФIëŨģwX @ÂtsĄqīi6nîʸÔt+ڍzM w#ú.ņģúëwÕ¸÷ôp÷…ŌŒ3ŦL™2ļqãÆ° €„ čæBįáķŦΘ%q¨éV´î{s^¸{€š÷¯ų]-ûëwõˆųáî ­† Zŋ~ũÂj‚ › _-_ëz\&-\wš.EīŊQ¯én_yaöwkŨw*ŨWúˇ…ßRÉ´iĶŦlŲ˛ļiĶϰ €c[›Aŗlōĸ5qĢŠ^ôžÛ<7ËzŊ˙M¸[€\é5úģúšôũ]]ķįŋ)}˙üˇH5 4°„Õä9‚n.mŨļÍ 1lÔ{šõģÔÆÎYwāšjEīQīUīš÷û‹Ũ>ō’žSŊūünižnēũŽ4÷ŋīŸ˙ĻđģB*š:uĒ•+WÎ6oŪ6§ēyDC ģŒX`—öũĖ]$ŋĘ ×öˇCŽa'^÷t\[ĸŠŪc—ķV‰„ĶwėáķķõwuâuΏßÔ ×ô‹kKdŅ{|đĪCø]!ÕÕ¯_ßV§煨ĉ­xņâÖŖG+Qĸ„[Õ@Î}ņÅvØa‡šß”~[ãĮ7K“&M˛ōåËۖ-[Â&ō Aˇš2eŠ;3fŒģ=bÄ+Y˛¤Í™3'Ø@v,Z´Č]ūdذaîļB.aHŒzõęŲ AƒÂjō AˇŌʕęÁ}īŊ÷2Ô2Ä?üpûæ‡vÆ÷ßo*T°gžy&C=aHŒ &XŊéÕ$ AˇŅđd ­|įwÂ&į駟vė˗/›dbÕĒUvüņĮ[÷îŨÃ&‡° $FŨēuí…^ĢČŨBdæĖ™.äžõÖ[aSš_xĖ1Į؊+Â&ëÖ­ŗęÕĢ[‡ÂĻ ģ@ŪĶīŠRĨJöĮ„MäAˇ˜={ļ›ƒûÆo„M™ē˙ūû­jÕĒļzõę° ĀŸ6mÚdįwž]sÍ5aSĻģ@ŪĢS§Ž›v@^#čZ`ĒTŠRöę̝†M;t뭷ڙgžiëׯ›€´ļuëVkÚ´Š+ú;ģģ@Ū7nœUŽ\y§~‡dA7É͛7ĪJ—.m/ŋürØôˇļmÛfmÚ´qĢ[nܸ1lŌ–zq՛Ģ^ŨEØōV͚5mčĐĄa5šBĐMb ,pĢ(įfą%oŪŧš5jԈyPŸ4Wķr5?7§ģ@Ū=z´UŠRŝœā˙Ú;¸¯Ļüۘ13fli!‘ČžÆX†ąÄ`(˛6–FI %[–Č>1B%ÛD‹-؊DO R Æ(BÆ2ų7”A–ķŪĮœĮ}Îoy~ËŊŋßŊĪũŧĪëûĒß9įˇ<÷{î9įsļē1…gzļlŲԌ1ÂO*š+V˜C=ÔtîÜYËÃDĒádeNXæ¤år‘Ø"<|rΰB!Â@B7†,Z´ČlŧņÆöQAaņå—_ÚG9tīŪŨO"đŒ\ŊÅ3sÃBbWˆpxüņĮÍļÛnĢY]!„Ą!Ą3Ū{ī=Û2dˆŸT6˖-ŗŖæįwžŸ$DŖ†™ĸ-Zؕa#ą+D8´k׎ā' !„ !Ą#/^lZˇnmnēé&?)4X˛šŨvۙË/ŋÜOĸQÂū?ž?=oŪ;í´“7nœ-„B„n øčŖLÛļmíA9•‚%Ō­Zĩ å°+!â3¸Ėä2Ŗ[)$v…(D.bW!„( Ũ*ķÉ'ŸØĮ* 8ĐOŠœ Ø}‹ŖGö“„H4ėÅĨlWãW‰]!J‡eË,_fŗBQēUdɒ%ö”ÉøIcūüųvÖkâĉ~’‰„mčÆ)ËÕBbWˆŌá@*ĻB!ĘABˇJp(Ô;ė`.ŧđB?ŠâĖž=Û4iŌÄL™2ÅO"Qp_ņœÜJnȅĮĨÁŦ.ƒĀcÆ ?IˆD°|ųrûøŦ8ŨWģB”Û¸Ÿ…BˆR‘Đ­0Ÿ}ö™Ųu×]MīŪŊũ¤Ē3iŌ$ģŒyîÜš~’ąæë¯ŋ6íÛˇ7]ģvõ“ĒŽÄŽÅÃŦ.įWTō09!„ Ũ ˛lŲ2ŗûîģ›^ŊzųIąáÁ4͚53ožųϟ$D,ųîģīL§NŦņ˙8"ą+DņŒ5Ęėšįž~´BQē‚e•{íĩ—éŅŖ‡Ÿ;îžûn͞eKŗhŅ"?IˆØÁ,.ŗšĖ毉]!ŠƒĢ-ļØÂLž<ŲOB!DBˇ|ņÅfŸ}ö1ŨēuŗËą’Ā-ˇÜbZˇnm>üđC?IˆØĀ~\öņ1”$v…(Ž‘#GšŊ÷ŪۏB!DB7būûß˙šũ÷ßßtéŌ%ļË*s1hĐ ŗõÖ[ÛĮ 78Y™–9i9IHė Q8ß~û­iĶĻî!„E#Ą!_}õ•9đĀÍ 'œ8‘ëčßŋŋŲe—]ĖįŸî' Q5xF.ĪĘ噚IDbWˆÂšįž{ĖžûîëG !„y‘Ѝö |đÁæ¸ãŽŗ#ŌIĻgĪžæwŋûĸÚđؑ-Z˜… úI‰BbWˆÂøæ›oėVšiĶĻųIB!DN$t#`Ŋæ°Ã3GuTâE.°¯ø¤“N2tPėü5Â#°æÍ›į'%‰]! ãÎ;ī4p€-„BäDB7dyîØąŖ5ūßX@°yä‘ÖƒxÉcúôéVōocBbWˆ†Ą=mÕĒUŖģ˙…BD‡„nˆ ™Åe6—YŨÆŗšĖę2월ͪEã€\fr™ŅmŒHė Ņ0ǡmBQē!Če?.ûrķō^öé˛_—}ģBTöâ˛'—Ŋš‰]!ōÃ2ĪxŸ9sϟ$„Bd Ąœ¨ĖÉƜ°ĖIˍN`æ$fNd"J8U™Ķ•9e9 Hė ‘ŸÛoŋŨrČ!~´B‘„n™ ryF.ĪĘMĶŠÄ<[—gėōŦ]!ĸ€įãōœ\ž—›&$v…Č +Ļ6Úh#3{öl?I!„¨‡„n°Oĩ[ˇnfŸ}ö1_|ņ…ŸÜčųđÃí#nšå?Iˆ˛Xž|šŲmˇŨLßž}ũ¤T ą+Dn† bĪÂB!ō!Ą[=zô0{íĩ—í”§•E‹Ų=Swß}ˇŸ$DI0cĶž}{ĶĩkW?)UHė ‘ļąo˙•W^ņ“„Bˆ:$tK¤W¯^f÷Ũw7˖-ķ“RĮ›ožiš5kf|đA?Iˆĸ`+@§NŦņ˙´#ą+Dvnēé&û?!„"ē%ĐģwoŗëŽģšĪ>ûĖOJ-sįÎĩ™4i’Ÿ$DÁ0‹Ëlnc>šŧX$v…ČäË/ŋ´Ŧ´=B!D6$t‹„=ƒ;īŧŗYētП”zf˘a;äS§Nõ“„hî-öåĻy+@.$v…ČäÆo´Ģ?„BˆlHčÁ…^hvØa{ŦČΔ)SL“&Mt"Ļ( NVæ„eŨ[š‘Øĸ>Ų´iS3ū|?I!„Đ-”˜mˇŨÖ>VGägâĉvŗ:ĸxF.ĪĘ噚"?ģBÔįēëŽ3ĮsŒ-„BHčÍV[me>ųä?Iä`ôčŅöTĖ øIBÔ1vėX[N.\č'‰Hė ņ#lu`Ņë¯ŋî' !„H9ē Ā’Ęļmۚ>úČO 0bÄĶĒU+ķŪ{īųIB˜'Ÿ|ŌÎüĪ›7ĪO ą+ďĐNüņ~´Bˆ”#Ą›‡n¸Á´iĶFK*Ë`đāÁfķÍ77üąŸ$RĖôéĶ­Pã_QģBüųã^āQwB!„CB7<Ŗ¯uëÖfņâÅ~’(’Ë/ŋÜlˇŨv:hHX˜Áe&—]QģBüĀ•W^iN8á?Z!DŠ‘Đ͐!Cėá8Zrįwž}t #ī"Ŋ°—=šėÍá ą+„ąĪĩ_oŊõĖ[oŊå' !„H)ēC‡5oŧąY´h‘Ÿ$ʤ{÷îfß}÷5_~ųĨŸ$R[@â”e.ģBü°zčä“OöŖ…B¤ ŨžÔ˛eKß}÷éÜšŗ9ôĐC͊+üd҈aŲ:ĪÉåĐ ģ"íüį?˙ąŗējÅB€„î˙¸įž{ˆn¨eOķíˇßšÃ?Ü>÷á+?<ūƒeë}ûöõ“DČH늴Ã3īO9å?Z!D ‘Đ­åžûî3͛7׉â̝ž2p€íŒ|˙ũ÷~˛hD|ũõ×Ļ}ûöĻk׎~’ˆ‰]‘fX=˛îēëšwŪyĮOB‘2R/tĮŒcš5kχÍW˜/žøÂėąĮæėŗĪö“D#ûN:YĶė}e‘Øiæĸ‹.2§žzĒ-„"e¤Zč>ôĐCĻiĶĻfūüų~’¨ė§ÚqĮÍ%—\â'‰Fŗ¸Ėæ2Ģ+*ÄŽH+Ÿ~úŠYguôä!„H9Šē>ú¨}–į̝žę'‰ ōī˙Û´mÛÖ\wŨu~’H0ėĮe_.ûsEõØiĨ˙ūĻG~´Bˆ‘JĄ;qâD+r_yå?IT>øĀ>v†G;‰äÃÉƜ°Ė^9Q}$vEa•YŨŋûIB!RBę„î¤I“L“&MĖėŲŗũ$QEŪ~ûm{ęõȑ#ũ$‘ xF.ƒ<3Wĉ]‘FXYŌĢW/?Z!DJH•Đ}úé§mgoƌ~’ˆƞéqãÆųI"Œ;Ö´hŅBΰŒ)ģ"m|üņĮvV—UCB!ŌGj„î”)Sl'īÅ_ô“DŒ`993î Jˆäđä“OÚíķæÍķ“DŒØiãœsÎŅéūB‘R ēËgĖ0 =ÖĖmŅÂŧ´ŌJ‰ŗŗjmx–ø8Ûŧ62 ;u2KK˜áÄ_ow\"ũug­™%>öļōĘfūĻ›šÅũú™o—.õ]Ō Ë^|Ņ,<ę(3ˇyķĖĪŽšá/üæĮĮÚūį¯÷Ī;¯$-Ÿ>Ũ,<účÄųkØJÉžŋŪ?÷ܒü%Ę#ŠõĶ“+%ŧŧ—X? !„(@č~˙íˇæũ>}ĖÜĻMÍâķĪ7˙7~ŧYūŌK˛ ØŌ‡6‹/ēČü}Ë-ÍkÛlcž|í5ß=XsŽ™ÛŦ™YÜŋŋüUI›=Û|:j”YĐŠ“Ŋū 6>{ˇG3ˇIŨc•´ ŋjëˇe55žk˛‚ŋŪ;ã 3wƒ ä¯JZĐ_ĩ×~Ų´ižkDØúéôĶUŪ+m~ũ4}ēī!„ Đ Đ}ŋwoķFģvæķįžËŦˆeŗ.ŊÔŧēŪzæËüÃwQ=”xc×]å¯*ÛG7ŨdæŦŗŽY>k–īĸ ŪíŪŨŧžĶNōYÍúkíĩ œ Ķ/Uלŋ˜eŅōîi§ŠŧWŲęĘ{õ“BˆÉ+téôąLéķgŸÍ¨xe•ˇÅ}ûšˇ?ÜwSøk^‹ōWLŒÎÉŧfÍō.;û|ęT;S"ŸUßđ3'ųüÅŦŠüŗūĒõE>‰ōø|Ú4•÷˜X!õ“Bˆúäēowîl_pAF…+ĢŽ}öôĶæÕõ×÷ŨTūúā’K2Ū'Ģž-<î8{åbA‡vO¯˙>YuŒ=ˆyũÕŠ“ü#sūҰ cG•÷Ųę§ūũ}7 !„ČA^Ą;¯ysŗt„ŒĘVV%›=Ûŧ˛Æž›ę`6wéĉ™ī“UÍ>;ÖĖßl3ßUu°/W÷X|ėĶ1cĖüÖ­}7ÕÁė–üÃ_ķ6ŲÄw“ •÷x™­Ÿ6ŨÔw“Bˆäē/¯ēĒY6kVFe+Ģžqc.䝸؞™3ÍËĢ­æģĒŽ—WYE>‹‘É_ɲ†ü%ĘCå=^Ļō.„ő[5Ղ¨ō+ZYu-ŸĐ•ŋâiōY˛LūJ–åķ—(•÷ø™ĘģBNŪS\ü,_#'ÅĶäŗd™ü•,Ëį/Q*īņ3•w!„(œŧ5ĻšøYžFNūЧÉgÉ2ų+Y–Ī_ĸlû{m{ŲgXŠ-Ī~:6ũūûíuãž÷Ķĸ´ŲcĮÚī]l›5fŒš¸G[ŪsĩĩŽžX6{vFZ”öėŨwÛīũŋššŒ´rLå]! 'oYj#÷Ā_ūbVĒ}īߎž:#-LëvÔQö{°Ÿüä'æÃŠS3ō|:}ēM?šC‡Œ´¨mĩUW5GļoŸ_ŽåkäJõcŽŅų]ģf¤…iƒû÷¯ķ–kđaĶ62ģlŊuF|Ôļũ[˜-6Ų$#ž\‹Âgkūüįæûė“ĻŅq\u•UęüusŽgjļīžæįkŦ‘ĩu>ôPûģĖōĶĘą(üĩëļۚM6Ü0#>L{câDķĢ_üĸÎ_§sLF Q@zށύėĘŗÎ˛ß[S+´ũ´r,ŸŋDy”ZۛގžŲ§]쌸0íų{ī5̝ļZ]yŋęėŗ3ō`úãm:ã~Z”ÆũĮ÷f€/ĮTŪ…ĸpō֘Ĩ6r•ēŋūÕ¯Ŧ0ų(ĪĖ„„nÃV)ĄģĮŽ;š_Ô 4füũ´ Ič6l•ēn ‰?-hē [%„ŽH|ūųiA“Đ…Rjy¯„Đ=įä“mybĩ„Ÿ4 ]!„H/ykĖRšJ ŨUV^Ų{đÁņA“ĐmØ*%tˇiĶÆšnÃV ĄËŊŌnCKū$tļJŨË{õ˛×ãĨąc3Ō‚&Ą+ ĨÔō^ ĄÛĩS'[ž>ž6-#-hēB‘^ō֘Ĩ6rš„.3Cŋ6olEřú“y÷™g2Ū?ío3'~¸ŲĄm[͞Y3ŗ×N;™§Ÿnūũâ‹6}Ü_˙j/–+Ķyä˙÷åØ˙ēŧī =÷´ŸŲ~÷ŨŗîĮ|ųėLÖÎ[meķíļŨvVūëšįęå{fÄûˇđũ[oļ™9ŽVđģƒyœĐeãĄ{īm?oŋŨv3w_uUÆ÷jųšRũ•KčÎyč!ëūž-7ŨÔūîÃg}¯Îmgžŋ‘kwÖ '˜wžzĘĻŗ71ŋöZkŲ˙_wãĖ ]ö‡rũxÍlđ_úõË] žxœÛĨ‹MįģwÜrKĶũčŖí2Î`>~7ŋ!KųCœų3Nč"Ž>č Ķēö{ņ˙Õ}ú”ÕIŠÂgŲ„.כëÎ}ŗY˖æˆČē<üũ)Sėž[:ĸ\ŗí6ßÜŪø›tĘ:>jÕŧšYyå•í˙Ī>ņČĪqæ„.3õ|×p§Z? ¨Ü=ëŒÕWœyĻŲ˙ˇŋ5×~ˇģwüũáÔ ü- Œ´jŅÂŪˇˇ^rI=Që„î?Üvz)ŖüíĖô”ŗ˙4 eētĐŲOûÛíˇˇ×â{íeîŋîēŒ÷R‡qŋ¸Įö3ÚÖ^_î‹āL;ežŋëqØīo}ÆûüĪœĐåš÷9é$ŗí˙ ¨÷Ü=ëė?3gš!_lūŨīl™r÷ui0ߒÚīē´gOĶn›ml™ĸláį ˙ĐĨLöīÖ͖;ü˙į#Ž0oMš”ņ; ĩ|ūåQjyĪ&tŠwÎûķŸmŨ@~xm9ĘvÎõe‰ļ’˛DŲä^g?.é”kĘ7å‘ōDũAų÷?Į™ēÔ”ņ|õå•úžūõyi ØoˇxåoĄ sī’Ÿ•Áv ŨųãÆ™Ķ;ÎlÕēĩmch+ũūD1Ļō.„…“ˇÆ,ĩ‘Ë&tošč"ÛiĻ“FAŏđŲpƒ Ė›ĩ PđŊĖ"ŅYĻÖī”Sl'‘Īë¸˙ū6O)Bˇųúë›5V_ŨtØo?ĶķøãmCLüČkŽŠË‹xũŲOj;øtėéŒíŊË.6ߞĩB*˜ßˆ¨"OīÚŧÍj?˙§ĩŸ?õŪ{ëō!tųžQrFí÷nÔ´ŠũŧÛ Čø­…XžFŽTe瓆5kūėgö÷#8đŋĨĮÁFËž@ōҀ͉F”đyøaRŠĐ]į×ŋļâé€Ú׍Īâ3ų|—aÕduėgŌŠ ˙2Ķŋ?yᛏßMĮ…Î eŠ Ÿĸ|ãĮŽGi¯3ˇėuã-zú钄.e_p¯rŨo\7Ō\>ĝVî‡;Ė~7˙Rθīč’{vķV­Ėojƒaäc ƒĪŖēĪsB_s/ōŊ Ї˙üßY¨Eá/_č"øé$sŨč¨ķ7" øí}këG—û‡ē‡úæ˜?üÁæÃo”SâØ§HžR„.× áJ}ˆ%Žzqëō"F¨gų—÷qŸ­ˇöÚ6oŽËwHíûŠ˙\™â>$uËã„.ßËĩčqėąļ|PÆ(kū HĄ–Ī_ĸcá“OÚ×4œtÖé¸ģ<ˆEDBĘÅņųÄ=vÛmuq|68B ø…ZžFŽTųB—kŒč|gÜÅf–“†›Yc֐ŋĶ_閕Í5Ē.ŽŲĤ˙ũž!tyođD]:Áü&: .ŽÎ i×Ųwæ:ōnŊÕžæ^ģŽ F‡jƒu×­×įˇ‘ī†ž}ëâ(¯üÍ‹āwcQøĖēt´ˆ .[ĨãFĮ !ųŪäÉ6îĸĶNŗŖ?›Íl9ņœæėâV`˙ģ}s÷ĸ4ĪũŽÜ)ģthÉįō0ØD<3)ŧæ´V^sĸēËÃLž›ųteĪ ]:ÎÁĪsĸ­ĄŊāš, ųB—N/ׯ?]œŽ3ņnŒU'ü-ĖŽķ=5|¸Ö7n鲛™Īeîū ėE­ë˜ķŲŧfæ5›_ų|â¤ā5ŗąŧfeE0~Ą~w™Nč2xáŧø‡šËį/QĨ–w_č2PB ¸ę‰û˜ēƒ:ƕ‘kĪ9Į–…{ŧUO HĪ>tįژ`;œÍœĐE´W18Cŧ[ũC}ÃëëĪ;¯Ūû]}Ä*^ģûâ&īp>eėseÛŨOÔŖÁ•(ŧ&žĄ-šLå]! 'oYj#į ]:°ŧ >gt–°n ŗ,öķ1ēËg÷ģ#tũeTŗz,'r¯_}øáŦûĮ˜æ3Hįĩ;¤‡Ņ_ú4 ËxũxˇŧĪ/Äō5rĨúËēD^g›uuž|îž{ėk!å MŒ÷’Īušąb„îúk¯ߊ}{ës×Ya%ÛŌ\–†ķŨn–ßԃˆđ1‚ÆocĻĶ_ĻÜĨcGûū|žÎgQø,(t€Aeۇî|ɲ_^3c›í1,!$_đ Ŗb…ŽmŲn@ŧ[rČŌEĘCp° ãž'Ÿ›­gI.¯éˆR6ũīs愎?ČâĘ^ļ˛QˆEá/_č2+J=āį×üvfYyM}ĮßáīCďä Î +tũú˜ĮīļV0ØČę !ƒ?ˆÄ ¯Da@ûį…‘#3žĪ™ēŖo¸Ą^ŧ+{ŊđŒ÷bųü%ĘŖÔōē”#ĘGļķ*hGđũ5˙äĸ,=Y[?ųu0m ų(ã.ŽXĄËĀz0žö;XO0 Įj-eB˜|nUƒˇŧfpŅõ ˛™ē<Î+Ī@ņĨ>~Iå]! 'oYj#į ]'RŲwÉŌ !øH îŅc&†&„ ŗ3Ė䑪ŗėō#tŗåcų3ČÁ8:št¸hÔĖjēī~åÁmLFn‰C˞Ŧ’NŊß)'ÍuƒÆßÍßäĮbųšRũå ]7ĒÎ@€ī/ˇ„xx`É/>aö•å­üŊ,‡t×,(NJēøÆw–`'ˆNKsYŌĘė2îģŨ˛t;ÅŌ1Īg3xáwĘųmĖŪúßËĖ1īķ;ü…Z> ] ŋŲNß_Üs¤—Ķ1d€kĀā[âL>î;—¯Ą›-ß ŪŊíg;zˆ$ęÄŸ/Ü#BXÎH2Ül †ON­õ/˜ĐõŸSéfŽƒƒ,ÅXū ]ø}ˆ]ß_õZpĨ åí<+œ%• žą”“Ī`аËWŦĐõ;énæĘ&2B”ŊÄėqįī`…ų‚ËŌŨŦ,Æ6|C úÆ ]˙,7Cœ­+ÆōųK”GŠå=(tHe›’_ÖŨļ¤`9ĻÍfЅöˆũąÔË´ĨäŖē|Å ])?í<ņkĮ V°ō…%Ņ,¯fõųX…áōņģ\y§ĀęîŅāĖ­ēū9$n V+„"zō֘Ĩ6ržĐe ¯éØŌčd3wČŖŦ4jℎ8 Åg”*tŗÍvųBÁD’ĪEäąĪŒÎŗÎ|†ēû阹\‰ĨȤ33œs‡QųßKgĄË2I^søī'gîk–'’ŸkÉ2tD?ƒĕ*tŗēė ]:Čëūæ7?t6Z´°âˆN<qÁũ×t¸ņ/b˜Ŋ¨¤cÁgæ:u9îB—eČü>:žŸœš fOŨĀO–¯"x)Učf;uŲē¯O˜Pˇ¯Ž"ÄÜ S'tQļ¸÷XŽîüÅRl7#œëÔå¸ ]ōø}Ü/žŸœš=dˇ¤ž}ëė)dļ×-ŗ,Gčú3ážĐeuqė—fk÷ –rēŗŋü6įcŒû‰ŲgŌsē,Ą_J-īAĄëVh°?Ö/įÎÜ>]‘ŠËÉĪ€ mƒ˜7öíkãĘēū,ą/täĻ"ŽA(ęÄ-u'qAĄ‹MŧõVģÚĮģąÅĘÍį:uYBW!*GŪŗÔFÎētfyM'ÛĪ\’Į!f–XŌįö:sûZ‚ņa ]„+u^û'€ŌĀņÁ%ÕÁÆ …Ø@tąôÕuŧ“*t‡]v™}ítč ŋt"î !gtHøŒ(….jDkp0Æ2Ũ`ųÃüΝkw •ëđ'U貂ß<ÄČ> ÎĒąú€ –čķąW”ĪîO[č2pÄ=ëīKe?j°üaĖÂ÷ĶąŸA%ōšĨ¯Iēt„c`ÆĪ‡ī1ˇMÂß×Ė ņÁũÉa ]—oā™gÖķŋŸøāŲ ÄgĖķü6ō]ÜŖ‡“ĐMĨ–÷ ĐĨmĀŋÁAEgÔåAĄĘĀ}€ņC†Ôˇå38ŲŅ-tOøß€ļßîš=é Ūē8V?zęcōšAE ]!„¨>ykĖR9_čŌhų F•ĨAt’éÜqŠ'ųXĸĖGŖÂÁA¤ŊØĻĐe+ų8 4˜‘ā–âēÌXęɨŗßpŌņŖ‘vÂ"ŠB—N*q:*ūã|œ¸@\r€˙÷˙FŪ㖤1ęíâÃē\cū΁G~>åDš;m˜%hļājŒDō9!–TĄËr;fHŲāNšvæÄ…ģĒÅōs˙ķÜ>dˇW [貙Á ?|î0#ˇĸĀßcK}BŧëH&UčbÔ 8 XƒųXūČow‡L1CD<4 sû YNėâÂēn;„ĪĪļ^ĶaįĩŋĮ–0Äs*=¯%t“GŠåŨ?ŒŠåŊÔQ~ęîUį{V™dk9|î@(,lĄËŒ3÷¤ßæšúˆíŧvg>ø{lŨŊëVÅHč !DõÉ[c–ÚČųB—†ƒ%w˜ÃŖj˜á –‘’ĪuęNtŦŲkȌąDŅÃá'ä î+ Sč"ĻY‚Ë %âŒīĻÆīv{Ũs+™áā5K˛éĺ܊%Ë[÷$,ŠBc "qü~öÔ!lŨĖ<Īû$:4ˆ.öTq͘s{6É{āąOa ]^ŗ—īAÄņŨt ÉãžÛužÜ ×Ũ•+–5ŗÔ=‰|IēĪ:å7ōÜ_:nˆ –üsß0ķí:ƒt>šočŒq¸鐚ûË……-t:đ@ûš>žA†Čeģuŗ—äcˆ'ŋ›Î yš'ņ BŨíyK˛ĐĨna0‰˛Î 5ĸÉ@Ŗ; ÖŨ‡Ŧ*á>ĨŽAĐrŊy?ŗäî3ÃēĖžņš{Šīåû™ņgÕ å‚Õäcp…eĻ<æ‹Sģņ˘Š;éî°: ŨäQjy÷…ޤBLr/ĶŽãoĘ1KŨS,&e…rD=Æ@‰Û“<ĢõK.đ§|“ŋ’e ųK”‡Ę{ŧLå]!Š#¯Đ×ŧšYę=įQV=Ãs›4ņŨTĮŧ-䯘ūĀ/šĀŸōY|ĖúĢļŪËÅÜ 6ŋbdÖ_͚ųn!Ąō/k¨~BQŸŧB÷íã7‹ķ‘U×÷ëg~¸īĻ:ŪîÜŲ|pŅEī“UĪđ~ÉūįūûdÕ1ę;ęŊ\,8âų+F†/sŒī& :tPy‘5T? !„¨O^Ąģ|Æ 3ˇY3ķy‰Ī•…gø€ŅõΟyÆwSÖ_͛Ë_11ëŗZā—\|>yō~•ĪĒnÖ_ĩõ]>-›6MūЉY5mš×_ĸ<ęÚ•÷Ē[!õ“BˆúäēđūŲg›×wŪŲ|ŪĀ3JeŅ×ūõv2‹N9ÅwOī÷écŪh×NūǞqũņÃûŊ{û.Ęŋâ_ųŦzfīąÚzŽúŽ!ŪíŪ]ūǞ9Ŋ×Ģ—ī2‹ēuSy¯˛ÕÕOgåģG!Dēßû­y¯ļreT—e3˙7~|F%,‹Æ¸Ö\söqŌŲø~Å ß=ā/Ä3ōWå­Îgĩ×?ā†øū›oŦņŗ|VYĢķWmũF=Wŋjķŧ{ÚiōWŦžŋjEn!ūåĄúŠzV¯ŧŸyĻĘģBIƒB׹ŧĻÆ,<ę(ģ“ãí“f§f‰‹ģąL魎í’ĨbayĶÂŖ–ŋ*l\oŽ{)ËËđ3ūÆīūįÆŨí¯Úzú­X>ūyģgWūǜᯝ:™eͧûîÃŦâÕO5[?Ֆ÷å*īBQ Ũ¤ŗŌJŠųSōW˛ŋ’…ü%Ō„ĘģB¤“ÔÔūj蒅ü•,ä¯d!‰4Ąō.„é$5ĩŋēd!% ų+YČ_"M¨ŧ !D:IMí¯†.YČ_ÉBūJō—H*īB‘NRSûĢĄKōW˛ŋ’…ü%Ō„ĘģB¤“ÔÔūj蒅ü•,ä¯d!‰4Ąō.„é$5ĩŋēd!% ų+YČ_"M¨ŧ !D:IMí¯†.YČ_ÉBūJō—H*īB‘NRSûĢĄKōW˛ŋ’…ü%Ō„ĘģB¤“ÔÔūj蒅ü•,ä¯d!‰4Ąō.„é$5ĩŋēd!% ų+YČ_"M¨ŧ !D:IMí¯†.YČ_ÉBūJō—H*īB‘NRSûĢĄKōW˛ŋ’…ü%Ō„ĘģB¤“Ô‚\%ŦœIDATÔūj蒅ü•,ä¯d!‰4Ąō.„é$5ĩŋēd!% ų+YČ_"M¨ŧ !D:IMí¯†.YČ_ÉBūJō—H*īB‘NRSûĢĄKōW˛ŋ’…ü%Ō„ĘģB¤“ÔÔūj蒅ü•,ä¯d!‰4Ąō.„é$5ĩŋēd!% ų+YČ_"M¨ŧ !D:IMí¯†.YČ_ÉBūJō—H*īB‘NRSûĢĄKōW˛ŋ’…ü%Ō„ĘģB¤“ÔÔūj蒅ü•,ä¯d!‰4Ąō.„é$5ĩŋēd!% ų+YČ_"M¨ŧ !D:IMí¯†.YČ_ÉBūJō—H*īB‘NRSûĢĄKōW˛ŋ’…ü%Ō„ĘģB¤“ÔÔūj蒅ü•,ä¯d!‰4Ąō.„é$5ĩŋēd!% ų+YČ_"M¨ŧ !D:IMí¯†.YČ_ÉBūJō—H*īB‘NRSûĢĄKōW˛ŋ’…ü%Ō„ĘģB¤“ÔÔūj蒅ü•,ä¯d!‰4Ąō.„é$5ĩŋēd!% ų+YČ_"M¨ŧ !D:IMí¯†.YČ_ÉBūJō—H*īB‘NRSûĢĄKōW˛ŋ’…ü%Ō„ĘģB¤“ÔÔūj蒅ü•,ä¯d!‰4Ąō.„é$5ĩŋēd!% ų+YČ_"M¨ŧ !D:IMí¯†.YČ_ÉBūJō—H*īB‘NRSûĢĄKōW˛ŋ’…ü%Ō„ĘģB¤“ÔÔūj蒅ü•,ä¯d!‰4Ąō.„é$5ĩŋēd!% ų+YČ_"M¨ŧ !D:IMí¯†.YČ_ÉBūJō—H*īB‘NRSûĢĄKōW˛ŋ’…ü%Ō„ĘģB¤“ÔÔūj蒅ü•,ä¯d!‰4Ąō.„é$5ĩŋēd!% ų+YČ_"M¨ŧ !D:IMí¯†.YČ_ÉBūJō—H*īB‘NBĢũ?]ū‰šwÖÍæ‚ §˜ĶG;ÛĨĶqÕļžc:Ô^¯?›Û_¸Ę|øŲûū%œˇ–ŧfn¯šĘœ?áäŒßVm‹Ŗŋ°^c0—>ÖÃÜ7kˆY˛ü#˙’FĘÂ%o˜Ą5ƒä¯"Œ{ėĸ ]ÍĐUü{{É?˰šĢå¯"ŦšūåĄúŠxSyBˆh EčŌĀûHg3âīƒÍÔe“Í…‚ôåĪ™û˙9Üôwĸų×gīų—6žûū;3rÎĶwü‰æūˇ†›į—OÉø] ŲCÍW/šį>{Æ<üú=Ļ߸“ėOÔā¯Qsn—ŋJ Ücãūy_Eũ5fÎ0ųĢĀŋúįŊÖ_•ĒEéPŪGĪĒō^b ŧ?ōĪ‘̟„"M”-tŋüæŋæüņ'› ˙z ŖW(<Œ}įn3đÉŗĖ÷ĩ!jšWL9ËĖ\1#ãw(zũns˙ė!ūå DŽüU~ũúđŠøkėœáf┺å¯2ÃŖīŒ2ƒžėS‘:Q”"WõSųaÔëÃ*R? !Dš([čNzķs㌋3*m…âÀ§N7ķ>˜å_âPašrŋņ'ÕvJj2ž_Ą¸ĀĖî€ĮNķ/q¨°üUū '<ûŲĶæ’Įēû—8TŪYōĻø“ŋ Ÿ:ËĖ˙`ļ™ELPũ^¨Dũ$„iŖlĄ;hr3ņŖ‡2*m…âè…w˜a5×ø—8TnĢšĘÜ÷Ö°ŒīV(>Ėūv–9ëŖüK*”ų+œP ¨š^ū 1<¸đsW͍ūe1axÍĩ*ī!…JÔOB‘6Ęē}>ÎĖøzzFĨ­P|`oS˙ ]üK*ėŖbO˙Ũ ŇWjCĪŅüK*Ė–Č_á„Jø‹Ų\ų+ŧ@Ča="ž¨ŧ‡*Q? !DÚ([črjā+ßŋ’Qi+^ųūesƘ#üK*=Įt”ŋB œœ%”ų+ŧ %+TĸNĨŖōnˆē~Bˆ´QļĐĨbö+k…ŌCÔ ünŋ’ä¯ä…¨}&JGå=Ü ˛.„á"ĄŗuC'…ä¯dų+y!jŸ‰ŌQy7¨Ŧ !D¸HčÆ,DŨĐÉ_áų+YAūJ^ˆÚgĸtTŪà *ëB.ē1 Q7tōW¸AūJVŋ’ĸö™(•÷pƒĘēB„KŖē/ûrF\aÆg˜ë¸Îô¸Ŧ‡šzôÕfÚĻeä)6DŨĐĨŲ_ÁpÃÃ7˜[ŋ%#žØF^ū.zŸQ.núvĶį†>ֆ>3´ėƒoRë¯ ŨcwNģĶôšžšāÖ ĖķČH/%Dí3Q:i/ī.PüÛ@s˙Ë÷g¤Tօ"\Đxī@spįƒ3âÏūķQĶlãffĨ•V2M[65+¯ŧ˛ųõēŋ6wŋxwFŪbBÔ ]Üü5lĘ0ŗĶŪ;eÄGîq¯Y}ÕÍąg›‘VlH›ŋzí!ŗũۛg˙ũlFZ˜áąw3›nĩŠŊŋ~ŗŪoĖZëŦe˙ßv§ļ晟ÉČ_hH›ŋ¸Víno†?;<#-ĖĀ ß^‡ėe}ô‹ĩ~aÖü՚ö˙ūÜĄėA‘¨}&J'nå}‚ ĻŨžíĖġ'f¤Ez]Õ˖÷S.8%#­˜ ˛.„áŌč„îúÍ×7ŋm˙یø0ŗJmwlk…íč9Ŗm܄…ĖÆ[llÖkļžŠųĸ&ã=…†¨ē¸ųk÷w7ë6]7#>Ē0ä‰!æ—ŋūĨí”HčN<÷D{íĸēÛūv[ķŗ5VoÖũʑWšUV]ÅėvĀnų iķ×÷\aũĩĐuåâŧÁįŲ׈ÛĶ.=­^\Š!jŸ‰Ō‰[y?ũōĶm™Ģ¤ĐŊoö}fÕÕV•ĐBˆ"Ą[B`’FíœĪŠĪ2Kâ/~IÆ{ Q7tqķWĨ„.ÂŦc׎væŊõÖ­%tK •ēŧņˆũžËOcĩÆO~ō3å“)i…„´ųĢB—eĸŋúͯėL0žA´đßSLˆÚgĸtâVŪ+-tÔŪxķm—ĐBˆøQuĄËRß=ŪĶvˆÖZ{-+RīzáŽzyÎŋå|ŗoĮ}mãĩߑûŲĨŒäßŋĶūv‰#yžûô9ŗķ>;›ÕēēũūĪlĐŦ¯gŲ˙_3æsDˇ#l‡lĮßíh$â'Ŋ;)ã7ąįv?ėaf¯˜‘FāŊ4jãŪW/ūĨo^2kü| û[ũ÷ĸnčĘõKļųĶ!væúįŋøšmā¯{mŊ<\wŽ-žétZ'ͤEģœąŨ~íĖßfū­.ß>‡īc}ĩÚęĢŲüũ‡ôˇņ|ūŲמm mąÃÖ'äšãš;ę}ŊQ֗īeúŌüĪrånu3Ī|ôLj„.‚”ŋsŖÍ6˛÷ÆfÛnfg؂ËIųĮ#öúá›S/9ÕvÜ~úŗŸš­Ûmmn|äÆē|Īîl6Øp{í5Nˆv쏛9ĒĮQæú¯ˇåbÃÖš3Ž<Ã~æew]Vī÷¸ŋsų’đÄûO˜Ū×õ6Ŗ^•‘vôéGÛīg…ŸVHˆģŋf~9ĶvÖYļÍ}Á*‘.įw1Ķ—O¯Ëãę4Ž÷EC/˛>Å_ āôģš_]žž7õĩīįzmžũæfŋ#öŗņ—ŪyŠ`b& _3ĐÔs`OŗËīwąžôĶĶ˙z:§/]x~éķuuą ”1ęéöÜ!#1!jŸ‰Ō)ˇŧSnNčs‚-‡”wĘđ™WŸiÛR—‡6ÚÕg:Ķ´jÛʖwļ1 ēP]>Úåæ­šÛōžÍnÛØ:‰xŪķĮ“ūhÛ%ę/ō¸ú‰ž…˙›¨w\˙ÁOķí÷ī‘ĐBˆøQUĄ;øŅÁv)â&[nb;h`˛åÎ[Ú¸›&ÜT—īOŊ˙dÖüåšV0ą˙†%qˆ!fvh ™QxqŲ‹6žŊa-6ma˙OãCĮ‘ˆi4`,;žíŠÛl<`đ7ą×Œ|8ūõâƒá€Ŗ°ŋ1Øģ@#Œųņ…†¨ērü5vŪXģė—†=IĖ\3HÁuD˜¸|t¤‰Ã¯mļkcÅå1=ąB‹k‹Đ$BQ„oņ×­OŪjãeø‹ŽƒtL8ØfåUVÎęüšŲ6›eĝp:ėüŸÎU„."—{‰.ũē˜ËīžÜvōavVûĐ­Ë7rÖH{=(ˇė7?šīÉÖØKŪ{kîĩųx˙vģogķ’Î~xâPl´]jL˙1€E9A„ųŋ‹īæ7qĪúių÷&ßŗöúk—|ØLœũE}BũD9gPŽëÍ`z”f~5Ķæã:8q?Öņöã^"~ˆ6õßwøŊÃīE<ƒ܋-6iaīD0īAč◠¨&poķÅ.ÅÁ|Ü_ŧ%į~z1!jŸ‰Ō)§ŧŋđŲ v@‡ōØĨŧyę‘ļmĨNqĪš”wŌ¸'üYgƒul€•TäģzÔÕļ “—{­ŦB䒗ASú´-´˙Ü/´-ūw”[îį–83”mé2B‰|7Oŧš^<ŗėˆŠ @ĸķM^Nå æÍŌ"t™m@¨rn0žÁū~7°ā„.ĸø…Ī_¨Ëį–čĶ uqŲ–.Ķ)%Î4B ?vîØē8|‡˜:ŧËáõō,áķ(G~ZĄ!ÎūbEŗ´Áxîƒ`wB—Õ#ÁYTr Áú/ÛŌe„.qūĄ} \ī SÄ0!—/PO¸=‹Ųfˆ‹ QûL”N9åŨÕœ›Œw÷O4āĩēô Xšåōą"x…\\ļĨË]â|zú?ä ֏ô9X…ĀĘą`^?0Xˀu¯%t…"žTMčŌÉĻač÷×—Úšpņ°‹ëuΜĐķę˜zųLļĄœôcC™Kč"Nƒī%¸_p‰äîín—7å;)”S‚iäüxŽ{íhJ?žĐuCWĒŋžúā){­:uvĪô{~đå˙–M:ĄË’â`>$ˆÎūæēŲŽ/K¤y?w‡đĸSíf‰ iēn$3°~3)`fOxí„.ŗžÁ| :œũÍ't}<üúÃ6ū¤ķNĒ‹sÂ+ײå\Īā}{`Æ L1!Žū"P¯0”mļšŪŨ^W'tYMáįãŪa…ƒ{Očē™0ØoȊ ę@ĮãRțm‰gŽpÖ5gŲåņ,ogƋŲf?O1!jŸ‰Ō)§ŧSĻYĒėĮ#6ŲfÂŊÎk'tŨRä``›U îu>Ąč&°Õ…z0ØĻąUƒŧÁeŲ'ŒŗšĖ<)Ą+„ņ¤jBKÀ ĄÁ 3ĸ¤ ¸ã‡%xNčúiž]Kü_ûk]\.ĄËc.‚ī%°ĪŽ˜›ąBĖąl°ĄÆŠ&B?žĀl4BŲ/4DŨЕę/:Ę\G–JúūbI9inÆŨ ]ž{ü 7ûĘ,—Kčrƒq„ËiŨŠģFų÷>līŒŧųB„.Ëîø™÷ũ…ŅÁcĪ4yĐ=÷/įf|÷Gpæ/—ĐeųĄ˙^âŒ{Ō ņ,™-TŦâķCN8Ä~'Û˛m(&ÄÕ_îm|_atčŨāēŲę4–Šŗ]ĀŊÎ'tĮŋ5>ãũĖŽ1+ėáÄ=m—qfŌ øÜmmîÍ/6Dí3Q:Ĩ–wÄ,å‚zÃ/ëļ~Ēm‡]ā„.į4øŸÃŲÁú?ŸĐÍvæ}ΉpÛÍô?ōÕ3ląâ>eFŲÅIč !D<КĐåÄböŽą?3›ņŧSō:ĄëŸ´ZŒĐegđŊ.°'Ž&ĸ‰™F:öūȝX2M>:œ~{ŗ‰´BCÔ ]Šūr'JĶqõũäĖ‚;ĄëŸôZŒĐõOpuáØ^ĮÚNƒĖäķy7<|CFž|! BĄÂßȒSßOÎÜ@’ēÁƒŒ\(TčŌáôßK`.ųôp3(Ų:ŦŲâŠŧŋë…] ĮųB\ũEā=É|?9c_<ųœĐ .ŲtĄĄëoA ¸Õ zĐŲG\s&Ÿ¯Đđ—qąŸĮ~|?­ĐĩĪDé”ZŪ™ Ĩ\°,Ū/įÎÜá„Nčr`š˙9… ]ę1˙Ŋöõē6„ÁA΅ČvÚ{0ĐĮ`Ā—žƒ3wę2ÛĄxío)4¨Ŧ !D¸TMč˛˙††Áu'Ø ‹Rč—Rnŗë69V0 x?KAĮœŽĨSNyguNŽAažhāÎîˆRčŌ?`ģ‡r īmčĐ5ęģ Č•ĐBˆøR5Ą‹˜ĩØąmƒ"ˇįĪí-Fč2#ÂɋîuCB×íKc?đBžK‘YEŋ*§šō]…<– WˆēĄ+Õ_tž9Ĩ’Y'ßŨtˇˇÛ;[ŒĐe†˜Ī æË't œ˞k–˜wæqé …4]Ëđ(§ŖįŒŽī™ÜŪŲb„Ž;@Æ-m%äē÷8*:€ŲöĘû˛F>îĮāãÂqö{YšļÁGĻzí!ëGˇwļĄË>yō÷ã6$tš?šös`Ũj?O0Ø-k¯eī[Điī?îmËPđ@˛bCÔ>ĨSNygKeÜ ;ŋÃĩ¯Å]ō7¸*+ŸĐ%đČ2fhi‹Š9t-´tY!âIՄ.CKh8Ĩ“-‘N1qÁÎu1B—gë! é3ĒڐĐ%¸Ķ›™ybĘOĪÜŖ3Z:žËĮ˛§l3hń¨ērüÅŌd:Ü-Û´´'ŗ2ãÃu&Ž‚°(FčēũSø‡į‚אĐef÷`ŲžĩÚPH‹Đå0(ŨÄ@{âņ÷q˜ÉOļųŠēn0qË^5âēC'˙03ˆšGå î@~#[˛Y.‘ÖPˆŗŋĻūßTû¸âađq0ƒwÄq0ųŠēœhK^ęX–ƒאĐu§7“'xXžpáíÚü,5g(u2ĪÉ&Î?-ŋØĩĪDé”SŪ™ue€‘ÉŒ?坉Ã\ų,Fčē­åÎhHčēĶ›ąb]  ]!„ˆ'Uē–%"œ\CÃr&ž‘œåå1Pሂđ"žĮ ¸8ÅÁėŸEg‘Ųō°Į/øŪ``D™üˆl?-W`i(Ë8yT īĨcČō'ˇŧ°ÔuCWŽŋ˜b‰ˇķƒ<*&čNGæšģį¯ēĀr4â/ŧíÂē8fzX*Ég!˜ˆcæo˙Nû×{o0°Ŧ•%­Á}1Á ~~?­Øwņėc–‡ģĮŊđ/Ā &ü˜§Ö\ācŸ\@x… âØí›åž%ŽAŠ|Ī1f†–%}l˙­Ų÷Ē8oĖrúī+$ÄŨ_ˆL8DĖŨcœœÍŪY—‡Á;Žõ¤˙~f}y —{M=Úūčöv0Š@ę'ÄīgŸģ˙~Ü!RÅ\gVtP^ÜēråBÔ>ĨSny§M`֟Ī(3”SļËuå5Û¨w‚[…Äd=ŸÅ6âN8į[.ũ÷Û$J=tĀŊÄo,g/:Ae]!ÂĨęBמߙ¯ãe`ö†Ņ?%¸@'ž=‡î1冨ē°üÅB!{-Ŗ/zÜ.­ĖvJpĨCRüÅ ¸øËß&P‰Āw2ĢĖžj?­Ō!)ū"°÷ĩ(3Ā[ˇÛ:#žĀ˛v˙„ürBÔ>Ĩfy§~r§W2¸¸r] +¨Ŧ !D¸ÄFčV+03Ë~@–Ëf;ĻŌ!ę†.éū"Ķķ;“\ęč{˜Aūj80ƒĪ@’x[5‚üÕp`ËK=9œĮOĢFˆÚgĸtCyŋęžĢJč;¨Ŧ !D¸¤Vč"pYúęžŲ{͘k2ōT#DŨĐ%Õ_–Üō VüÅÁ=~z5‚ü•;đ((–â/ÆōĶĢä¯Ü=ÚôÆōöMļÜ$ã`Šj…¨}&J'ÉåŊKŋ.ļŧ3¨SČ!y•*ëB.ŠēöÂŅĀeۛX­uC—d!nŲŋË#$: ļRAūĘ8” qĒé‹Ë^ĖH¯FŋrÂ_œ5Ā3ũôj…¨}&J'ÉåÃ,)īęÆAp~z5‚ĘēB„KŲBˇį˜ąXōÛ×ņŒ1Gø—8TzŽé(…¸Ž\Ī(Ą<Č_á„üÕÁŋÄĄ"…*Q'ŠŌQy/Tĸ~Bˆ´QļĐíķđqfÆ×Õ90Ĩą…i˟3ũƟä_âPé;ūDû=ūw+¸Ž\Ī(Ą<Č_áę)ęĢ(9üÉōWˆkyÁø?û—YÄ•÷đB%ę'!„He ŨA“û˜‰ū …Üឡ†™a5×ø—8TnĢšĘ~˙Ũ ÅŽ#×3J(ōW8zŠú*JFÔ\/…Æžu—šĢæF˙2‹˜0ŧæZ•÷B%ę'!„He ŨIo>`nœqqFĨ­P\˜šĸÆÎŪŊŊäū%•ˇ–ŧfŋ‡īķƒBáÁų‹ë%”ų+œ@=E}%ī,yĶÎrÉ_åŽ!ŗš\SOđę§põĶãoŽõ/ąBˆ2([č~ųÍmĮnÂŋȨ¸ 3WĖ0WL9ˌšsģy#aäœ!öûø^˙ˇ(4œŋ¸Ž•`ôœĄōW™ú‰zŠú*jÆÎnN9[ū*#p펚ŌĮ<4įN˙ōŠ˜1fÎ0ÕOeę' *Q? !Dš([čÂÂ%o˜sélFü}°™ēlrF%ސ=<ŋ|Šš˙­ávŸ'"÷ģīŋķ/m$đ=ˆ4ž—īįwøŋM!3ũÅõ̤ŋ(ōWņúˆz‰ú‰zĒā/:˙ōWņÁŨctúœ3ĸb÷˜(|Ä`œĘ{ņĄõ“B¤‰P„.|ēüsīŦ›ÍNąGäËļķ'œl†Ö ĒZˇ`ÉëæöšĢėīđ›,͏N\¯¨—+į‚rBy‘ŋ 7ę#ę%ę§JÃ˛ķa5WË_EX˙ ]Ė5×jšrĄ~Ry/ÎĒY? !DMč !„B!„q@BW!„B!DŖBBW!„B!DŖBBW!„B!DŖBBW!„B!DŖBBW!„B!DŖâ˙ۂ}-¯kŖIENDŽB`‚trillian-1.6.1/docs/vds/verifiableMap.png000066400000000000000000001367211466362047600203650ustar00rootroot00000000000000‰PNG  IHDRŊŽ\a°€IDATx^ėŨ ŧŒÕ˙đö_Jʒ$‰ė!!dI–"YBŲ*˛V˛+[$˛e߲/)Ę.kv˛eŋČž$[ÖlĄÂ÷ßįûûŸņÜsīŒëŪ™kæ™Īûõ:/æyÎėsgžīsžį{î"""""""—ēËŪ@DDDDDDä z‰ˆˆˆˆˆČĩô‘k1č%""""""×bĐKDDDDDDŽÅ —ˆˆˆˆˆˆ\‹A/šƒ^"""r•¤I“Ę]wŨÅÆÆÆÆF ßũŪ0č%"""WÁÁ…_ßũŪ÷… _>DDäNžžûŊī!""" Až|ˆˆČ|}÷{ßCDDD‚|ø‘;ųúî÷ž‡ˆˆˆ(ų:đ!""wōõŨī}QōuāCDDîäëģßû"""ĸä뇈ˆÜÉ×wŋ÷=DDDD!Čׁš“¯ī~ī{ˆˆˆˆB¯""r'_ßũŪ÷… _>DDäNžžûŊī!""" Až|ˆˆČ|}÷{ßCDDD‚|ø‘;ųúî÷ž‡ˆˆ(€~9zQ>ŸšW* Ø(/ąÚĩ핮ĢĨúW›eøŌCrņĘ?öË@ā뇈ˆÜÉ×wŋ÷=DDDpũÆ °đ Tčŋ^->$ wœ‘Íŋ]tmÛôo›qJÚMŲĨĪÁ>–¯""r'_ßũŪ÷@ŋJŅ˛f˙ų(ĸÛÛˇkŽĘk}ÖÉžåëĀįVĻN*}ûö•ëׯGÚ~čĐ!YētŠėÛˇO/¯[ˇN/_ŧxķŊÄ˙ģuë&Ÿ}ö™ôīßßŗ=>āņâqãžŅbj˖-ú<Μ9#W¯^Õ˙¯\šŌîæWGŽŅûŲŊ{ˇ^Ū¸qŖ^>wî\¤~ͧO÷<ŸŊ{÷FÚį˯ŋūĒ×ôĐCzŊÜšsÛģo NšÜ}÷ŨúŪã3gŪĪ‘#GÚ])„á=õÆû"""?ûtÚn°đ×(`¸ĩļ“wɰ%‘ƒ*ō_>ž<ņÄōĖ3Īčč[tžüōKŊíŸūYŽ]ģ&šrå’Ė™3GęsāĀyōÉ'ã=č“yōäŅĮ“ wĈÚwÁ‚zšPĄB’6mZÕ 4Œâž/^Ŧ÷:uęH#ėįΟ—f͚iŋÛ z1bŒQ<ŒÆÛ>õÔSRŗfM9}ú´nÛąc‡¤H‘B'NŦīĢ '<œÁą¯ ˇû^ž|šŊ+ZVgĪž­ˇ— wîÜšz¯ŋūēŪ?ÚĉeŌ¤Iv×ۂ‘YÜFIm§N’’%Kú z¯ķ̝žzÛAo•*U<¯y\ƒŪ?ūXƒô]ģvékŽĪĀË/ŋŖŋ žžûŊī!""ō3Œp.Ųéî9ŧ1isˇž’ĒC˜V(ž||A`ƒāË,įȑCęÔŠŖ—ׯ_¯ŖĢYtB∠ŠĐC‡ÕŅāč0ĀČ RĒ{÷î-=zôˆr[N¨,"uöģīž“ũû÷{R„ „ ¸=z´ŪæĸE‹"õ1 W¯^]˙ŋ}ûvŊŪŧyķŦ^7ũũ÷߲zõjŊ‹+ˇųķį—ˇŪzK/ãūxā()žx]LĐkŌ¯MCPl ØÄkį܏†1QŦX1ŊŒ`Ú*VŦ¨û^{íĩhƒ^<—UĢVEšooī%Ō¯‘Ž‹ttŧ7xŖ z€ã„D÷îŨ5]ˇ‰×ÉN%F@jFŒ‘­`î?ēÔf|&ņš" ¯5‚UĶߎxŸ‘ĩā|.hxīm8y`‚^ÁĻ/ŽŋãwŪŅ~xüß|ķtíÚUu{ądÉí_ @Í ˆKЋĪFōäÉĨcĮŽziķøŦy;šEĄË×wŋ÷=DDD~öJˇ5˛éЅ(A`¸ĩŋ^ĸŨĸŽœø:đņåVA/Ŧ]ģVS‚Í}ãÆĨ^Ŋz‘ú čEZ)Ō¤ņXLÃh“͐!C"í7 #\ö]gú˛it2A/ęũ*UĒé61â…ëš@A“ @ŊÁķÄm•*UĘŪ+[ˇnՔqÄĩmÛVĒUĢŠ3čũôĶO#=§wß}×Ķķ>í×͉wBˆļyķæú^ŊôŌKvY¸pĄŪFMúˇô"ˆŗī ī—­K—.úŲpöCvūuŊx_Joß&Rs Žö~͐Î{øđaO_š÷Ũw_¤>(Í˙ÍgŋW¯^Qn -UĒTžÛ2œA/æH;û;į› ×ū\"ŨØNŋÆßVöėŲõ=Áœ[|NA/2’$Iåņ9^{ŧ/€ųÚ3fԓ&đÆočÉrŧ÷ŪxßCDDägXÂĮÃĩáĩ Āđuāã æ"¸š•={öxūåʕ(ķôâ1āĀ}ōäÉ:˛”%K=ˆĮh­QHŒcÄAî×u“¨-Ü.¤#0A5('ô>ũôĶ2vėXM+\¸°nsŽø"°ĀHąûˆn”Ķ An§AƒöŽXsž–aD*ą“ zņ:"ØÆ˙1ø‹/žĐ´dãˇß~ĶQmS0 íŅGõô:į ãõÅČŊú €Úŧyŗ'¸ĩƒ^¤Sc4ßyßø ØAī˜1côúĪ>ûŦŪFqčâžąŨôļiĶFˇÕ¯__ßC|†jÕĒĨÛ¤8a0`Ā-ƅ}8šaŪsĸ×Į~ŽŖFŌ}ˆĸU¨PÁ3*Ž ŠüÎįķøãû z‘É€×˙Įg×wĻ<›ûJ–,™f+üøãšŠm&85đ|°ŨœL˛ƒ^Ōåí`ę &ØģT‰%4¸Æčē“ –‘ ī N¸ø zÍûƒį]@i‚^gĩd¤„c›sÔ)Ōr1}ōäIŨfŊDŪāķä÷=DDD~Æ ÷fcĐ8ž|ĸc Ą!Љ+3§×ÉPĩ30rj§@›fŗß˙Ŋ¤L™2RH‘v2A¯3•éØßË(ųƒ zŅđZy ęĸsĢ ×ŠFz3gÎÔËH…ÅH1ļĄ`Føá2ŌŽíāŅfŊ¸=\ˇtéŌŽ^˙ƒQdėsŊČ @ǎŽŒyØÎzãVAoúôéõķc3)öq zÍįŅ-7LĐë,d…ŅalèļaN⤑ĩÅk€“˜‡ (ŽUšrå(ŖģÎVŧxņhį “ģáŗã÷=DDD~Æ ÷fcĐ8ž|ŧÁčŽQĐ(Žbôâ€#oHuE•]Œ6Ŗ Q‹-´Ÿ3čEē)V6Â\\ąH§ÅČ!nA“áÖ Ŗ­I“&ÕĀŠŨ1q;A/Ēã~Ėh8Šd™`În˜—ęĢāØA/ 6aųö|m3ī zq"Å āaųˇß~+5ŌÛÄŌDļ[Ŋ ÔëÚŖĖH¯Įõâôbž:Rí1G;ē5côbžļũz›†Qi@Ú4RûíũÎ†Š øģĸđ‚÷Ūī{ˆˆˆüŒAīÍÆ 7p|øø“BVˇ‚ôMŒRa “ʉƒtmx\ŨE@õõ×_ëe:üņ‡UÂzĩ‘Ãv&@Āļ}ôŅG˛sįNDåYŒvaģ ‘ūš3gNŨf–ĖÁ}Ą–š/_KËÜ îģuëÖōĶO?Ųģ#š&Â|gœĀzą¸Œųž<í"HNŅŊx]W˜3ž0aB=y„yĀæs‚ûÂČĢųŦâ>ÃÖmUĢVÕ9Ų6œėÁ\nԘĮ]Zw `ž>>k8ądŌŦ)øáķä÷=DDD~Û wŅÆ}úcönÃ&Qö˛-ßvXīˇZí÷Ŗė‹kcĐ8ž||‰kĐ{âÄ OQ"4üs) h¨ž‹yŠ&€ķÖ°ļ)˜ųŅ5Ėų4K™t\ĶĀŲUÍEąaÖME€p˜ĮmÖé5ë›æ+X‹.čÅšũš†JÜŅA€j÷E1(_ĸ zŧ>˙üķQnËŲđÄH0ģŊ %–;20'Ųîc‚f&Ū{ģųŧúzÁz1Īܙš ™˜{îŧũM›6IĻL™"m›1c†ÎMvn‹.Ãĸ\šr‘úÄ×įp ÷éí3AÁ ī™7Ū÷ųƒŪ›Aoāø:đņé§H Œēa¤ í‡~Đm{÷îõlC3Ŗ­‚´`Š ÷Ū{OSk1 gúaMSĀH0æôbÉŒ6bd E¯ōę,„Ks]¤v"A îŧo<–ØBē-_¤ĮŦ?kˇIËŨ°aƒgFË}UœF†`͆bbH#Gņ04ŒĒ;ĢeGk);īךFptĖ\Tæö"`Æû '%œĪÉTØÆ|T¤8ŖČFģņž# Ø|& ûũu6ŧ_6|>1Œį%…Ėh뭂^œ<@ î„å’Ė}aäP‘ÚlÃgÁ6Nž˜mÆITŧFA0ŗ=ē9Ōõv>oķ…Y˜GŒ×—B‡¯ī~ī{ˆˆˆüŒAīÍÆ 7p|øø‚åi0Ō†ôĶ[Ĩ¯RpCj´ŋæhß.œ¤@Š;Ō–ƒǁãīÅ×eœpĀßEŪŧyí]Ž„“EH#wއLĄÁ×wŋ÷=DDD~揠wÕÎßĨû 1ōAËOĨ×°oe힓QúŖ™ļPÚté# ›ĩ•Î}‡ÉÜ5ŋDéķķžĶŌĖdų E{ų¤Ķ—zį~gĐģf÷ é1dœöũrčxŊlßŪí4ŊãëĀĮĖAD°‚ÍŖGÚģ)ˆan, ™ęŊZđ9œĐ@Ã(%ÖĮ5÷mĒl›jÃÁŖŽá6ķ{ņ81Šë<ɃbŦÛkúam]ôCąŦp€šĘö’c|}÷{ßCDDägq z -)ÉS¤Ôšd zHˇĨN›NmÚŠīsĪįŌ}I’>Žũņ˙{īģO:ôäé7˙į]ōtšgu_ĘTŠåŅÄIô˙y ‘Ÿ÷žŌ>&čÍ_¸¸¤xęi˙öĐÃëļ§ž~F~\ˇ;ĘcicĐ8ž|néÂH;ĻĐbŠ}™†‚`HĨHãuÎoEЋ Ķv•æ; )ČÎĒĮ8šƒ`Ũ>Á3pāĀH¯%æžßjŗ›āäæâSčņõŨī}‘ŸÅ5čEĢûQ+ŨŨtč‚4iķŋĒ´ī4¸™ö\âĩŠr˙ũȐofxļM_ēIƒßGKŦ×Ãļ Uߑ<ôīžzÛ?lÕAo¯í}u› zŅjŊßÔ3ēÛ˛cwŨVĩvÃ(5ĻAoāø:đ!""wōõŨī}‘ŸÅ5č͞+O¤í=/'|D^zš˜g[F-ĨqëNQnŖrÍ˙-éąbûŊŒë$zô1ųé—Ŗž>ëöŸ‘Ž_‘Ë6ëeôfΚ#Ōm!@~,IRɕ¯`”û‰i{Ļx-bc`ūŠÖŸ~úŠtéŌÅsš]ģvēĈĸ6ÎôÉO>ųDzôčṌâ<¨úk´lŲR×<5š7oŽ•lĻM›jaŖI“&2`ĀĪe,—cÖ0ĖDãũ÷ߗ¯žúĘsÅrœUfQ€iĈžËuë֕‘#Gz.Ŗ°ŗ˜NíÚĩĩxb>( dŧũöÛZØÉ¨QŖ†v_>DDäNžžûŊī!""ōŗ¸ŊoŊ[?Ę>¤įĖ“?ĘvŒOZ°VúŒ˜(ī7oįIe^ēåWŨßēsoŊŒāĩ\•ŌmāhYq(Ōm˜ ŖÂöí§I—Q͍íí1mé _>DDäNžžûŊī!""ōŗ¸ŊŅUoļƒŪY+ˇÉËÅJÉ=÷ŪĢ×AKŸé9I—1K¤ ­]ˇūžíh¸Î+%ĘxæęúĒŪĖ 7xų:đ!""wōõŨī}‘Ÿ:čE5fĨz8aBiŪū ™0g…V{ÆžJÕkG zM›ŗú-r…‚U蓧@aŨΠ74ų:đ!""wōõŨī}‘Ÿ:čņũ\í‡bWvŋ^|I÷-Ū|@/×ŦÛHĶží~YsäÖ ˙gĐš|ø‘;ųúî÷ž‡ˆˆČĪôbdũĸlĒ4Ŗ}ÜŠ§nG3ëõæ+TTūķ`ŊŽé‡´fĖņE€ŒË zC“¯ŸØZˇn›5k–^F.\ūíˇßŦžˇoƌēj§Nė]Aë¸ÚKÛ#cÃûröėY9tčū?ĐËíŦ\šRīįĮÔËC† ŅËŋ˙ūģÕ3°Ž9"­Zĩ’ĸE‹ęįéwŪ‰TüÍļqãFųī˙Ģ}Qč..öėŲ#K—.•å˗ߑõmąÆļYWx˙ūũöîÛō÷ßëß;žĪ/ŋübīžmøģAŅŊZĩjIęÔŠũōú`i5>üâ‹/ô˙›6mŌĪžKl("ˆûÆįÃí|}÷{ßCDDägzŅ Uû"(-TŦ”°ÂrE(F…íƒÆMÕ~S­“ÄI“éúŊŲ^Č-šōĐ%Œô~7o•öaĐš|øÄTSĨJ%O?ũ´\ģvMfΜŠ÷ƒĒŲq… ˇõÖ[oŲģ‚FÛļm偐sįÎŲ삯ļmÛôuŦRĨŠ^F ‡ËæDE `]įĮ\2eʤ—ą>0î×Y}=ĐΟ?¯ŸMsrĪ4Ŧŋ .:ŗgĪ–„ jŋ’%KÚģĩZŧ}{Ļá3 v_~ųåHû|đA]ÛˇtéŌzßÎĩŅ°A>Of[:uôöÚ÷eFüíŲ°-GŽž~‘ö,X0Ęm­_ŋ>RcÕĒUōĖ3ĪDęûėŗĪĘ#<íÚŊ9sæŒrÛh?ü°üúë¯ZßŪį wÚ´iz[øģ„ōåËËũ÷ß/[ˇnÔkEgȐAûâ5 ļĩŖũ ĪĶī{ˆˆˆü,ļA/*1Ūg¨|;{y”}Ŋ†~ã dŅ6øCēôĄKU­Õ@>ëõ•^ŲÖßô6&Î[éé‹jÍmēôŅųž¨ ÂVčgöc #\güËĸÜ/ĒB÷39Ęö˜6ŊãëĀ'.ĖĻ ĸ*VŦ(O<ņ„× "Ļp|đāA9yō¤Ŋ+h`‰)“zmÖģÆ{`RĄŅ0jjüųįŸúšÂë ŧ6}ûö•K—.yú.#¨ÃHŪ7æö‚oė-Z´H_C|†Úˇo¯ŠĀ¸^tA¯^OôsŽ­įŠmÎõĩ÷™1cFyūųį=ÛĒW¯Ž}qbŸŧyķęe|žņ\Ŗƒ´\ô9pā€ŊËŖėfq˛¯úÛA/\šrEûĨH‘BG>1kg-˜“ZøL:aplßž}{¤í6ķ`Ô=:oŧņ†× #´8aâ­!ŨÚĀ 2œh@@ x¯1Ī˜71y-ŨĀ×wŋ÷=DDD~Æ ÷fcĐ8ž|â F 04aÔ Ŗ:x ŅĩŸūŲĶoʔ)‘ö=õÔSŽ[š #vH­ļoËŲP™ƒn4;÷ã Øy°=qâÄ(ˇ†‘ëãĮ{ú™@Ā[Ãķ‡Ä‰{ļ™ ãšįžķlĢ]ģļį61wŲlĮ¨Ú+¯ŧé6ņ\8aÄ#fö}ĸŊúęĢ:ĪØ@€] @Ī('æŗšØ›B… émáuķ­HÁ5Œ“€Å<öĖ™3ë6QÎįä æ1:j?g´_|Ņķ؃9sæDécÚŨwß­' œ8°û˜ŅŅÛ z1’Ž ŪØ:á æ &čE‡püÁwtéĘFLĩ÷ß_û đoŅĸ…ū?ē ×¤Mã$ųû4' œ¨2¯…3ëb÷îŨŌŗgĪ[ŽÚĮ6čÅ *û}‰ŽM˜0Aû6LOTHß6ķÉŊ‰ÉkéxŽŪxßCDDäg zo6ŊãëĀ'>`n%‚@ĖâwĮŽ:Z‡ĮåL‘D`‡>&đôš9‰H_ÄČFB1‚k†qĀ‹@ûL_t8ø˙ã?4đÂ6gQ#<.Œ#XD‹Q/ŒŖŸ3X4ņĩ×^Ķ}‘5ŖO(d0L›Įd‚đ1iˇÎ Á*FoŗeËæyGЈĮ„g@ }šsįրÁĆ 4(ÁvŒpÅÅđáÃ5P6Šĩ†ɓ'÷Ŋ€Ī‚?<gЋô_ŧáÃ(?FÔņĄæ:­]ģVį–cFoÍûƒ9În û1ũ1*ˆ9ŊųÄöÛ zņøė÷Õô>ôĐCú/^¤˙úrĢ@ õŊ÷ŪĢŠ×˜bā+čEj1ú⤲0×iÕNøŦ#mØ|&ŗfÍĒY89å+87bôbڙņ]à |b éÚ¸ī[Ĩģ‡:géͧ×ĮˆfNĻ8!xFöŋ ĐŅp_ŨTtFĘŗÛ 7>˜‘nœŒûūûīíŨށįč÷=DDD~öJˇ5‘– ׆נČŋ¯†¯Ÿø€‘$¤åÚÕtT:ƒQ'oA/ĒÜâē7ļw遚}m‚^g0ąpáBŨæ zMec4īAZŗšŸA/ oĖ(¤¯f‘P“ §ÍFÃëāLMЋ$Ļē´>'¸ž z1ŠËHv2ŠMЋŦ\ƈŽIЋ4b\FúĻ3Ö,Ãä+ču6ķņ| s_ą z‘ĸ:&e×nˇšglôĸȓ ”á52A/‚\ÆŌ>ΑY|v°=6A/* #Ĩ×.ėe˛LЋ—p:áŗƒíHW7đn×,O†ųŊ¸Œŋ+'œÔ²AŪRĄŊŊ(‡}ûų8[‰%"ũ­Ū.3÷ļ›á9zã}‘Ÿ}:mˇ Xøk” 0ÜÚĀEŋJ§7+ĩ’ų:đ 4¤…âūqЏ4If˝sŲĖ)5)ģXįՆÍeŗ* `Äu‘ОA@m΃hĖM5)°(…ĘžØgFÕ°Œ‚VĖ#Åe¤ĖĸŌ?ü #ÉM2ũĻOŸîšhÕĒ•îkŌ¤‰>'<Ü.3Ē ƒY×ĩQŖFL!­Û,ƒš¤˜ H¯ÄsDŅ' ÎÔegãŠS§ęu1r‡CÜ&s‰eʔŅĮ— sœPÄįČRļ‘fŒā¯1æz›ĸcxŸ1JˆjȸŒÔsVxŊ0"‰× ÛQĨķŦiÆxŨĸĢŪėLc5A1Nt ’8ŪCŧoøÜa;>OXSEĶp#;vÔÛĀŋ¨vŒ÷Ō9¯ÔĖÆûˆ÷éō(†÷ÁŖ zqŌ)ôč‹Ī‘Šhm–yÂķĖ3‘ú Đ”å5͌öÖĢW/Ę}&L˜Đķ;ËŅĨc´Ī`ņ3‚if4 ÷ ž*Q#ĀrBđáíuÂüҏŊĻj4čø‚ĸHĻb˛iÎׁ=>& ķÖĪ,dF‰ŖkxŒb.´ŗFjÍ˙|ŖĐæP#ČuöÃüi|žlqâÞoœ@éÜšŗÎ=Gė܇QgĀŧeœÂ6<.|~pĸČž-ĶđüŖ{ŋąnļŨKaY,į6Œt›âYÎtq4Ė7'qœ¯3Ū/įÉ*L9ĀČĢ}Ÿø<ÚÅĻđyļû™†‘éøÂ —A/Åŗū JŨŅaøâ9×ŗU.&M×nf­['ĖcÄČFíPÁŲ¤2›uN‘ZĀ×ĮˆŅ&lCJ¨3h@_Á,‘ÂiŠ”lÃí›ĩņė‚A€@#r¨ŧŒ4\¸MŒōšôlô1ĶŲ0]j(^ Œbūųįúü1Úk§éÆ–ĸA*7FKãæâ51b„ž$Áč&žŋs”!FáņœQč ¯ ŪsôCA4Æ€× ûė×rŊž“&NéĮ{‡÷#—øė˜ë8SŠą՝‘€eĢĖũE'eŦc„ŖŠxÜÎôzĖ˙5÷ązõęHU›ÍgĶŦ Ęø\9Ÿ 2(ė€Ō ÁēékŌĨņYĮgŪlw.C„“OøĖc;‚X°ÂsÄ}āņa”‘ˇ×6°#ÛxđŧĸãíũÁį?ē÷'PĖ|koĪÅ-|}÷{ßCDD×˙=¨AĐWĄ˙z´ø7Y¸Ãũs|ņ‡,ųM*ø÷9/:¨¯ޝˇ@Āfæ*yƒų­F&N*¸™¯ī~ī{ˆˆˆéŊgî•J6ęō=jųZĪ’,ÕģĘSĢĘ#Šž“ÄéķČÃOf/ž.é^o./ŧ?R ~ļ4ĘõüŲ* ܍Ε)ÍņÃׁO¨ÂČ–\Ázž)SĻÔįˆ"O]#"Š‚ÜbŊi*t™nãëģßû""ĸƒx¤’!•8ĶĻMĢŖa(ō‚t>“ō‡TK ²2(p‚9gX§)`ũû÷—UĢViĘ…&_>Ą )˜˜ ‰ ķmąjtU|‰ˆÂ•¯ī~ī{ˆˆˆ‚æΟ?_‹… °Šæ :)–WÁæyÅæs!¨ĀÜ,TžEU[ÂŲ˛eĶāŲĖy´—š āä뇈ˆÜÉ×wŋ÷=DDDAg°ÔÖĖ‘#‡VŒ#¨Ŧ‰ĸ+ū, ‚$HCŅ,į‚åbpeÃ2#Ŋ{÷–Å‹kq .ž|ˆˆČ|}÷{ßCDDtaŠ ,…‚ā˛bŊēn$–āĀ:ˆ¨ ‹eT8OHŸŪļm›ŪM›6ÕeE0ēŒš–XËw åÔ,KCw†¯""r'_ßũŪ÷Å#,—‚e"Zĩj% ÔQUˎÅ(+–+9tč}• ącĮ]ēĸyķæē+Ö¨ÄÜKë]ģvÕ%<đü(~ø:đ!""wōõŨī}QaÄë ÖĒUK2fĖ(=ö˜”)SFētéĸiÃĄ^H ëONžûė3M‡N™2ĨŽ7\˛dI=q0eĘŲģw¯}5úž|ˆˆČ|}÷{ßCDD , „偰L– Â(.–Â2B¨jŒe…Č˙Nž<Н;RÄqB!uęԚ&^ŦX1iŅĸ…ŽŦīÜšĶžZXōuāCDDîäëģßû""ĸíÚĩKF-uë֕ŦYŗę=(ĖÔącG™?žœ?Ūž œ3gÎČŧyķ¤{÷îēdRēté$QĸDR¸pa]Ré›ož ›ųŌNž|ˆˆČ|}÷{ßCDDaįōå˲bÅ éŅŖ‡ĻÕ"Ĩ6mÚ´RŖF 4hlŪŧY×ĒĨā…‘øE‹I¯^Ŋ¤J•*’)S&I˜0Ą(P@įX;V"""äúõëöU]Ãׁš“¯ī~ī{ˆˆČõ0wՄ›5k&yķæÕTeü‹Ë؎ũú0ŋlŲ2éͧT¯^]Gė$H īuŖFdäȑzBķŗŨUąqđÃÆÆÆÆ> ßũŪ0č%" ĄE`ƒ[ŒÜb#šŅÅČ.Fx1ŌKááâŋ˛råJ-@öÎ;īHöėŲ5Ν;ˇÎĪ:t¨lܸ‘EȈˆ(ä1č%"r)ŒîaÎ-æŪb.æâb„ss1Gsu‰œūüķOYģv­ 0@Ū{ī=-P†@8gΜR¯^==a˛~ũzšråŠ}U""ĸ Å —ˆČ%P5Փ1J‡`ŠĘ¨ŽŒ*˨ú‹šžDˇ .–¤}ē´k×Nʖ-+?ū¸6üÛ°…ŨˆˆČ‰A/Q %sôčŅRˇn]ɚ5Ģ<ōČ#RĸD …š?žœ?Ūž ųF{1ę‹ŋ;Œc4ŖÂÆ(1F‹y‰ˆ(|1č%"ŠĄË—/ëÚŖ=zôōåËëA5ŠīÔ¨QC $›7oÖjĩDtįaū/æc>0æc~0æ cž0æ cū0NZ‘û1č%"ōâØąc:B„ ŗyķæÕTeü‹Ë؎ũD:P•ĄQ!•ĸqŌ •Ŗ1ר(ĘŌDDä. z‰ˆäë‰b¤#ļšÅÁ0Fr1ĸ‹‘]Œđb¤—ˆÜkc­`ŦŒĩƒą†0ÖFEuŦ-Œ5†ąÖ0ŗ8ˆˆBƒ^" K˜k‹9ˇ˜ˆ9¸˜‹‹9š˜›‹9ēL{$ _įΝ“%K–h…õĒUĢJ–,Y䡇’|ųōÉG}$ŖF’-[ļhuv"" ~ z‰(, ˆ Ē'ŖŠ2Ē)#U)¨˛Œj˨ēLDäͅ 4ãˏÕŦYS˛gĪ. $_|Q>øā6l˜lÚ´‰K!ŊDä:8čÄúˇĨÁz¸Xëãb\°bŨ\ŽĐQ\]ētIV¯^- Úĩkë 5šrå’úõëËāÁƒõûæĘ•+öU‰ˆ(1č%ĸwęÔ)™1c†´jÕJįáa7–īžûŽkvQŧA€ģ~ũz­P¯^=ə3§Âˆß{ī= ×Ž]+ūų§}U"" ŊDrļmÛĻŠ„ĩjՒŒ3ę2$eʔ‘.]ēČâŋuô…ˆ(X ûdãÆ2tčPb‘;wn „‘"ũÎ;īhĘʕ+åâŋöU‰ˆČôQPC‹@-[¸tđ"đELDj0ÅãGŽ)5ŌåĐŖ ^õęÕĨOŸ>˛lŲ2-ēGDDqà—ˆ‚ R‘‘’ŒÔd¤(#U)ËH]F 3R™‰ˆÜčúõë!cĮŽÕīĀ čōI™2e’*UĒH¯^ŊdŅĸE,ŧGDt›ôŅƒ‘yAjŠLĄØŠNĄøŠPĄ+ĄQ¸CFË7ß|#M›6•Â… KĸD‰$]ētRŠR%éŪŊģĖ›7OΜ9c_ˆˆūƒ^"Š7Āō@X&ËaÅ]0Į Ë aY!""ēĩ;wjVL‹-¤Xąb:õ#uęÔzŌĶAđ]{ōäIûjDDa‰A/ĖŽ]ģdôčŅRˇn]§öČ#H‰%¤cĮŽ2ū|ÎU#"ōŖŊ{÷ʔ)St:Hɒ%%I’$’2eJ)_žŧ|öŲg2sæL9vė˜}5""×cĐKD~qųōeYąb…ôčŅC°p°•6mZŠQŖ†.Ũ‚-7nܰ¯FDDtđāA™6mš´iĶF‹&K–L’'O.e˖•öíÛËôéĶåˇß~ŗ¯FDä* z‰(V0Z0uęTiÖŦ™VEĒ2ūÅelįhQp:räˆėĐĄƒŧūúëZK!iŌ¤RētiiŨēĩ~‡8pĀžQČbĐKDˇ„ZŒÔbÄ#ˇÁÅH.Ft1˛‹^ŒôQhúũ÷ßå‡~Ī?˙\ŋÛQX0qâÄ:%Ĩe˖2yōdŲŗg}5"ĸĀ —ˆĸĀ\[ĖšÅÜ[đ`..æäbn.æčbŽ.š–ˆ›3gŽtíÚU dĨI“F}ôQ)Z´¨4oŪ\&Nœ(;vė°¯FDtô‘VMFõdTQF5e¤*Ŗē2Ē,Ŗ(ׄ$""8{öŦüøãŌŗgO]2)}úôzbôå—_Ö%•đ[‚%–XÁˆ‚ ƒ^ĸ0ƒuoąū-ÖÁř{ĖåBÖÉÅzšX7ëįÅÄšsįdņâÅúģōÖ[oIæĖ™õäiūüųĨqãÆ2fˉˆˆk׎ŲW%"Š z‰\éi(X‚%, ,¨"šråŌŦņxčĐ!û*DDDqráÂYž|šôíÛWkAd˖M$H /žøĸ4jÔH†.›6m’ŋ˙ūÛž*‘ß1č%r¤• 6LjÕĒ%3f”Į{L—ŠčŌĨ‹ž‰ŋté’}""ĸ€ÃīĪĒṲ˙ūōîģīĘķĪ?¯0NÄ6hĐ@† ĸŲFW¯^ĩ¯JD' z‰B Č" E`‹.^ž€‰ˆˆn×/G/Jį™{ĨōĀōōĢÖ ~ļT^x¤¤{Ŋš¤xņuyøÉ ’ĻDũ(ũŲŪŧQŸëĘ=g헁ˆ\‚A/QA*2R’‘šŒ3ãHUFĘ2R—‘ÂŒTf""ĸØē~ㆠZtPŪ°AF,?,‹vž‘Íŋ]tu[°ũŒŒXvXjˆ:Ŗ"äā).ÁGä6 z‰‚ŠI!Í ÅĨPd ÅĻPt ŧP,ŨP”ŠˆˆČ_.<( Įn•5ûĪG ÃĄYyDĘõ[/‡N3đ%rŊDAËay ,„å‚0Š‹åƒ°Œ–€Ā˛BDDD‚”æ7l ۀ״áË~“vS¸=‘›0č%ēCvíÚ%ŖG–ēuëJÖŦYuÃ%JHĮŽeūüųrūüyû*DDDķųĖ=2xņĄ(A`¸ĩŸöü!åûm°_" a z‰âÁå˗eŊŌŖG)_žŧ$I’DŌĻMĢË8 4H6oŪ,7nܰ¯FDDo*öß KÂ`ī­ÚĻÛ{Ŧĩ_" a z)Ŧl?rQ:LÛŖ?ėvõÆ@ļ4%Č#Šž“§ V•,ÕģJžÖŗĸôņg{ĨëjŠ:d“ YüĢ\ŧōũ2EQ¤ÛŲtčB” 0~K‰Č=ôRX@5Ę>ķhqЁ‹ÉÂî>“ŗÔs"NI›Éģô9o;|Á~Iˆˆˆ"A g˙ž„kcĐKä. z),ôšˇ_Ū–Å9ž]sTĘôY'ŋaāKDDŪ1čŊŲôš ƒ^r=Œrĸ E8ŧĻ!đ-ßo=S‰ˆČ+Ŋ7ƒ^"waĐKŽ×vĘ.°đ×(?háÖÚNŪ%C—˛_"""Å ÷fcĐKä. zÉõ0§•Õ(/ĘÜ­§¤ęöËCDD¤ôŪl z‰Ü…A/šŪ+ŦFŠm㯤čŋ¯QtâôV­ŨPJ•Ģe{ ÛÛõKņ2åŖlkcĐKä. zÉõâō#îļÆq""ō&.ŋ—9ķ䗔ŠRGŲč–¯PQI–hĢvū.cĻ-Ô~sV˙eŋ3č]qHû›žX6ø#JßÛiüŊ$rŊäzqųw[ã8y—ßKŊO>•Zę~ÔJîŊī>šëŽģ´!ūnŪĒH}?ī3T'MæéƒöĀ˙‘úM[Gę×ĸC7ų΃ "õ˕ˇ€,ܰ×ĶĮŊ ›ĩ•ûîģßĶ/EĘTōíėåQgL/‰Ü…A/š^lÄ×î9ŠgŒįމzf9íį}§õ~g¯Úe_\ĉˆČ›Øū^ĸ!č5AnīátTöĶî5͜5‡§Fuīžûn)P¤„LY´Nû}=sŠd͑[¯˙ÍŦeÚ˙ĸßÕkÉŦ•Ûdé–_ĨCArĪŊ÷JɲoxnA¯ rŋ:^o¯SīĄD?›!s”ĮĶÆßK"waĐKŽÛņË6ëé-?˛/ A6îgËí}qmü'""obû{‰f‚Ū‰ķVFÚūß24x]ŗû„^ū¸SOɐ9Ģ,Ú´?Rŋc§čõ?ëõ•^n×­ŋ^9i^¤~MÚt––ģ{.› )ÍÎ~e*žĨۗo;i{L/‰Ü…A/š^lÄôQ8‰íī%‚ŪD>eû; ščoÚĸûĸėC[ļõ7=uTĢũžök×ĩŸnŸ0g…ÜsĪ=’ôņ'ô÷pÔäų˛ūĀŲ(×GĐ›āĄ‡ĸlĮup{ąÍÖâī%‘ģ0č%׋í8ƒ^"" 'ąũŊDķVŊšÖûMõ7Í9ˇû 1ōbū—5HÆ>´ĮŸx2RЋ†šŋf;Úà ĘkoT•VDxú čE`lßoŊ&Ÿčuĸ+~“ÆßK"waĐKŽÛqgЋ3ĖåĒԐŧ^‘ęī} Ķ—nŒŌ?Ŧ(ÂQ¤äkúcūzåŌ}đXŲtčB¤~¨VYĩV탞}ō™Vœ4ûAīØé‹¤Â[oKž…Ĩjí†:˙ÉžßÛiü'""obû{‰Ķ ÅŠpsx›ļí"ÆNŅ}ޛ%čEC•gTonĐŦdĪųĸöI’ôqYšã˜î÷ļdƒ^"rbĐKŽÛqôĻĪôœVĸĖũR!yšX)š˙ūäÁÉ´%<}ņc “øá-]áMK„ĸ¸ūģ ›xú ?]o Å5D{õuŊŊO=­)^čc‚Ūtŗh_TĒ|åŋĨĩ(îcŌkĸ<֘6ūˆ‘7ąũŊD‹iЋß;ü>کʘˋ~mēôŅË8ŲܸÍįôFw{_};S/3č%ĸ˜`ĐKŽÛqô>đŸu­AŗgĨą#žfT¤`9GlŪ{JŌ¤Ë($zÔŗ ?ÎXŌaŨū3žmŊ†~Ŗˇ‡ŗß¸l‚^èriú đƒn¯\ŗN¤Įy;?âDDäMl/Ņbô&M–\/ąRéƒßNœ vū6ú¸Ŗ^FƔķö)…íS¯×Ë z‰(&ô’ëÅöGÜŊĩunGē2Fz‘šŒË8 ŨĖd-ÄaßRœqüĄ—‘ŌŒ9Iãøß’ æúķŪåšl‚^Tŧ´o4–y°ˇĮ´ņGœˆˆŧ‰íī%ZLƒ^S`*Köœ:%SwxåŋēŊFĩߊíG$Uę4ēä~Ä*VJû SĘÜ>ƒ^"Š Ŋäząũ7A/~8í}&N"‹–Œ´mõŽüâ,5ÎDįȝOŦqfdˇīČītAlÏyåīIŋŅ“"ņ6A¯3-Ú´ä)Ręŧb{{LĉˆČ›Øū^ĸad•šíí=†ŒĶ õ§_ŽęeœčÅrDø ʼn`ŦšÛoÔ÷ēīízIŊÆ{Ž‹`üŋôr1OŒ/úŒT+ķ‚™WĻa­`Ü/Ö÷ĩ÷Ťņ÷’Č]ô’ëÅöGÜWõf;čí6p´$|$‘öĮhėķšķĘÛõKŽ|#ŊhßĪ_-UŪŽĢ횰 Š^_Ī\Ēû}UofĐKDDÛßK76ū^š ƒ^rŊØūˆĮ4蝷f‡ÎŋÍø\ö(Օ‹—)¯ˇáÉĩīãũæítôU)ąA/Ũ ąũŊtcãī%‘ģ0č%׋íxLƒŪ^ÞÕ~-;vÔ…Ŧ¤bߚŨ't^o†,ŲĸĢ›)ëķZÍ˙gĐKDDwBl/ŨØø{Iä. zÉõbû#Ķ KĄFj—l>¨Ûf­Üæ)¸ļxķŨ^âĩŠrĪ=÷č|Ļĩ{NjÚs—~#t¤÷ęĩ´ƒ^""ēbû{鯯ßK"waĐKŽÛņ˜Ŋh(´q÷Ũwk¤:#°EÕgŒūb[Ÿĩ‚â^|É ›†uxWíü]û0č%"ĸ;ĄHˇ5‘ŠD…kÃkđĘŋ¯šƒ^rŊØŊHGÆRĻâ¤ŗÍ[ģSmÚiúöüęk­Æl– ÂH.ļcég_ĖũEEKTsƨpt÷k_ ˇkßīí4ŊDDäM…ūdÉΛ…Ãĩá5(×oŊũōQcĐKŽ۠׍A/yĶiƸ(vKü¸Š XøĢ´›ēË~yˆ(„1č%×cĐ{ŗ1č%""o~9zQ*öß köŸōû. ĪŊ|ŋ ˛íČûå!ĸÆ —\AīÍÆ —ˆˆ|°đ ÔŊ5,_<į÷FEHŸųė—…ˆBƒ^r=Ŋ7ƒ^""ōåú2pŅAŠĐŊ \|Hîp˙_<Į‹Ių~ëĨߏô5 "waĐKއ ŒŦFųŋj”¨ĖIDDt+Huū|Æ^ycĀF=a­Pį•ōl™$IĻü’ĒpMyŠŨŧ(}Ņđ?›žGŸ3šƒ^r=ĖO ‡3Õˇjx *ũûÃNDDl"""$OžŊ,]ēÔŪ+sįŜ9sjŦ Øģ‰ˆ<ôųŅáÇ5e+I’$ŌĒU+9qâD¤ũüņ‡tîÜY‹rTŠRE6oŪi?‘Û ŋ{üą\ž|ŲŪg“&MŌšÁEŠ‘UĢVŲģ‰ˆôųÃĄC‡ty…¤I“Ę'Ÿ|ĸg´}ųķĪ?ĨoßžZ”ŖL™2Z˃ˆˆČMđÛXētiŨ¸1°Kæ]ģvMƌ#iŌ¤‘×^{MļlŲbw!ĸ0Æ —(<(õë××`ˇM›6rúôiģ‹Oũõ— >\ĶŊ .,ķįΎģ…˜0`€<ūøãŌ­[7ųįŸė.ƒßÕAƒɓO>)UĢV•ŨģwÛ]ˆ( 1č%ŠŦXˇn]ŖÛž}{9sæŒŨåļā õ„ ${öė’+W.™:uĒܸqÃîFDDÔ°ŧPūüųõDîž={ėŨņU¸ąūo:utԙˆÂƒ^ĸÛ°oß>yīŊ÷4ØíĐĄƒœ={Öîg3gΔ|ųōI–,Ydܸqņz†œˆˆ(6°vn§Nttkéˉ[TˆÆÉiddĄĀ¤]kƒˆÂƒ^ĸĀŲęwß}WĖņŖŽ‚Tļxņb)^ŧ¸ÎO&üPwėØņļ‹fų ÖÅM›6­fAÅĩŽÅ°wī^ũMÅ åūũûk,"r/ŊDŋüō‹V{Äz‚Ũģw—‹/Ú]î8Ė+FÅh¤hĩhŅBŽ;fw!"" ¸tđ"đ u[ˇnÕ´ėÔŠS˨QŖXGƒČĨôÉ˙FQß|ķM=ãÛŗgΐHw:zô¨g~RÆ ĩĸ4Q  …ŠĖHi…ßÉہyɨŖ‘1cF™8qbĐâ""˙`ĐKa-""B*UǤÁnīŪŊu‰ƒPsęÔ)ųôĶO5íšf͚:ZMDDä/(N…ŅPĢBpčf("ųŌK/IŽ9döėŲön" Q z),mŪŧY*VŦ¨‹×÷íÛWŽ\šbw 9įĪŸ×”lđxnëׯˇģÅF;ąüYšË… ,˜={v)P €,]ēÔŪMD!†A/…•7j%d¤gĄp…‚]žĶå™gž‘’%KōĮšˆˆn–ę+\¸°äΟ_vėØaī úŋũö[ɐ!ƒ”(QBÖ­[gw!ĸÁ —ÂF=˖-+ŠRĨ’Aƒ…Åšˇ8#?fĖɔ)“žŠž5k–Ũ…ˆˆ(ręÖ­›Žî0@Ž_ŋnw ;xM†.O?ũ´fRmßžŨîBDAŽA/šæ•)SFĢ22$,—$ĀËäɓå…^Đ9Jß}÷bˆˆ( dCåĖ™SJ—.-‡˛w‡=œ0īͧN#B ŦĻ@DĄA/šŌęÕĢĨTŠR’&MŽÁntæĖ™#… Ōę”#GŽ ĢųYDDŊ˗/ËĮŦËõ?ŪŪM,gØšsg oĐ ú"ĸ⯠—\姟~Ōy7X?pĈ ęŧXž|šŧúęĢšĒ…šÍ8ā!"ĸđƒēéͧ—jÕĒÉɓ'íŨäÖ,nŨēĩ.Ø˛eK]Mˆ‚ƒ^rqX_/]ēt\\ū6lÚ´I—lÂŲũŽ]ģĘšsįė.DDäBøž¯W¯žN˙áŌŠYŗĻ¤H‘BúôéÃēDˇ‰A/ųĒ÷Ž;V2dČ EŠaęE‚´ŧļmÛę|Ĩwß}—•*‰ˆ|ĀÔ čH_ˇnŨX!?Ėmßž]*VŦ(O?ũ´ >œŸĸbĐK~ƒ/^Ŧ ˜.]:)^ŧ¸,_žÜîBäĸ+]ģvÕ4ŊJ•*qB""ˎ;´˛Ĩ0U„ČXˇn”(QBžũö[-JDŪ1čĨ8Ãs#FŒĐÖņüĶO?Ų]ˆŧœ´ūũûëYëW_}•'Kˆ(ėáw1ē‹%āА7ČĻ+P €dĪž]fΜiī&ĸ˙Į —bí¯ŋūŌã4iŌHŠRĨtŠĸØÂAŪȑ#%cƌZđŒKYQ8Zģv­.éWŽ\9-bDXž*GŽōŌK/ÉâŋíŨDaA/Ũ6ģC† Ņĩ˔)Ŗ?ĐDū‚ųkß}÷ūxŋđ 2yōdŨFDäfXĶŧiĶĻ’2eJ]˞čv!#KCâä1Ļ™ņøŒč&Ŋc¨8hĐ I•*•”-[VÖ¯_ow!ōĢYŗfiÚ*€3FGƒ‰ˆÜKųaŠŠûq=VŠ+ScƒČØēuĢŨ…(ė0čĨ[ēråŠÎšÄŲįōåËsŠŠw˜ŗT˛dIyæ™gdāú™$" upč"āåöäoČĖÃņ–9Ē^ŊēėŨģ×îB6ô’W,úöí+O>ų¤–ĮßŧyŗŨ…(^!ģŸEü€wīŪ]Ο?ow!" HaÆÉd¤4#ĩ™(Pđųúâ‹/´0ZŊzõäđáÃv"×cĐKQüųįŸŌģwo ,°”LDD„Ũ…čŽúå—_¤f͚úū駟ʩS§ė.DDA ÅŠrŠbUœsIņé?ūļmÛJ’$I¤YŗfrōäIģ ‘k1č%œ ėŲŗ§ģožųĻlÛļÍîBT8 6ôü€=zÔîBDPd+ād–#bēSNœ8!7–¤I“JûöíåÜšsv"×aĐKrņâEM}â‰'¤jÕĒ:ŠFJŽ;&-Z´Đāˇ~ũú˛oß>ģ ŅŗgĪ)\¸°äΟ_vėØaī&ē#:$uęԑäɓKˇnŨ4ĶČ­ô†ą .č|ŲĄĀÁΝ;í.D!åôéĶŌącGIÁgšŲ Dt'ĄŠ.‚ |' 0€Ë¯QPÚŊ{ˇz † Vé@,"ˇaІÆŌšsg vß~ûmŲĩk—Ũ…(¤á„Η_~ŠŠú¨8ūķĪ?Û]ˆˆ +äĖ™SJ—.­#jDÁn˖-ōÚk¯Iš4it™Āk׎Ų]ˆBƒŪ0‚˜G„3ÎX"éVDn†ĩĨŦ?āŋ—Å‹Û]ˆˆüęōåËōņĮ딡ņãĮÛ쉂ŪĒṲH‘"’9sf™4i’Ŋ›($1č gĪž•:H˛dÉäŊ÷Ūã|G ;H17nœdɒEōåË'3gδģÅÖOŸ>ŊTĢV•q)äaí蜅;ˆ(V¤J•J>üđC­'@äF(†tũtéŌIŠRĨdÆ vĸ Æ ×EJõÉ'Ÿh°ûūûīŗpQ Ŧ\šRʔ)#O=õ”ôíۗK6QŒ?~\*WŽŦķņ=B°ž4֛ÆofĨJ•¸… Ŋ.€EÆ[ĩjĨk”âLķáÇí.Dt ›7o–*UĒhņT7Gá7"ĸčŒ5JW@h׎Ė# 7WŽ\‘^Ŋzéoæ;īŧÃ)tôô†°ß˙]š7o.‰'–ƍËŅŖGí.Dt›°„WíÚĩu.|ëÖ­õ¤ėßŋ_+ÁŖ¸ODD„Ŋ›(ė Ĩ+ƒā7Y†ĮŽŗģŊ!Ŧ{÷îŌ´iSMą""˙Âô€>úH˙Έˆ gĪžŌģwoĀ#˛ ž –ęęŅŖ‡Ŋ‹((0č%""""""×bĐKDDDDDDŽÅ —ˆˆˆˆˆˆ\‹A/šƒ^""""""r-ŊDDDDDDäZ z‰ˆˆˆˆˆČĩô‘k1č%""""""×bĐKDDDDDDŽÅ —ˆˆˆˆˆˆ\‹A/šƒŪsõęU9xđ œ:uĘŪED~túôiũ[Ãß…ˇcĮŽé÷Áõë×í]Dô¯ãĮëßČĩk×ė]DAAoˆų駟äŽģî’ÚĩkÛģˆČęÔŠŖk˗/ˇwQ˜É›7¯~œ={ÖŪED˙*\¸°ū=zÔŪEô†˜­[ˇJ‘"E¤{÷îö."ōŖž={ęßZDD„Ŋ‹ˆÂLÆ õûāâŋö."úWãÆõoYRDÁˆA/šƒŪsîÜ9YētŠėÚĩËŪED~„ŋ1ü­áoŽˆÂÛÆõûāŸūąwŅŋ6oŪŦ#ũõ—Ŋ‹((0č 1œĶK?8§—ˆ Îé%ōsz)Ø1č 1{öė‘ZĩjÉȑ#í]DäGŖGÖŋĩŨģwÛģˆ(ĖtęÔIŋūüķO{ũĢk׎ú7ōĮØģˆ‚ƒ^""""""r-Ŋ!æĐĄCōŲgŸÉŒ3ė]DäG3gÎÔŋĩ_ũÕŪEDafĈú}påĘ{ũk˘1ú7ráÂ{QP`Đb8§—(~pN/œĶKäįôR°cĐbNœ8!cĮŽ••+WÚģˆČV­ZĨkŋ˙ūģŊ‹ˆÂĖėŲŗõû€•i‰ĸ7oŪ<ũš|ų˛Ŋ‹((0č%"" R˙ũˇ?~ÜŪLDDDˇAoˆaõfĸøÁęÍhČ"˜>}ē´k×NjÔ¨!/ŋü˛<ķĖ3š"hZ‚ äÉ'ŸÔ˙'MšTŌ¤I#¯ŧōŠĻß÷íÛWæĪŸ/§N˛oúޏ~ũēlßž]ĻL™"]ēt‘>ø@˙†J”(!EŠ‘´iĶJŽ9ô˙ĻU­ZUZ´h!C† Ņį˛wī^Ŋ`ÃęÍDžąz3;Ŋ!†sz‰âįô’ŋ!`š4i’ÔŦYSĀ$I’Čë¯ŋŽÅ_Ə¯ŸĩƒÚWķ8}ú´î_ļl™ :T7n,ŋ—Į{L˛eË&-[ļ”ü1ŪŌ ņx&L˜ ēĪ?˙ŧ<đĀ’%KŠXąĸ´mÛVY¤;.X°@–.]Ē}ëÖ­úĶž˙ū{ųōË/ĨaÆR˛dII—.üį?˙‘B… éÉ<ŸK—.Ųwī8§—Č7ÎéĨ`Į 7Ĝ;wNvíÚeī""?ÂßūÖđ7G[׎]“ŠS§J•*UäŅG•RĨJi%`~‡ß¸qC~ūųg]Ł'‚i‘Øæo;wî”O?ũTōåË'‰%’ *hžyķfŋU6FĐžxņbéĐĄƒŽj'L˜PGÁq?w*čܸqŖ~üķĪ?ö."úžđ7ÂyīŦôų–í@ú1Ō‘°a9ø Ø0ŌŌ­[7ɐ!ƒŽcÁwl]ŊzUGt€"Õú“O>ŅŅfĖ7ŽĻįÎĢŠĐÕÆ „YŗfÅé9QxaĐKDD!sŐ"kFœmŨēuēīüųķöÕâ´Ī?˙\įßVĢVM6lØ`w‰W‹-Ō`5SĻLōíˇßŪÖ|Yîm}üņĮåÕW_Õë;=Ō‰÷#å ĐįôÍ7ßÜÖs""ĸđÄ —ˆˆ‚F׎]ĢķA›5k&e˖Õāæž{îŅQ>ŒœæĖ™3R1$4¤ÛbRn‘FŒÎŌĨKKĶĻMu”iš6qâD-DõÖ[oųœ›{' ]¸`Á‚ú:ĄØ”/x $)R¤Đųēû÷īˇģœė@:7æ÷ŨwšæMDDŊDDtĮ`”nɒ%:O´hŅĸ:Amƒ ¤W¯^2sæL Xo'•ķ°Q0iΜ9z•+WÖI†QIiĮ¨ė/XRE˜rįέŃŲđáÃuäļcĮŽŅĻ'c­M¤Eã„^ÃP°páBɟ?ŋŽūúķ}%""÷`ĐKDDņ ,FØ&Ož\ōäÉ#íÛˇ×%kR(˜S‹ ē^Ŋz’*U*ɘ1Ŗ´jÕę–#Ÿž HL™2ĨV`žĀüNœ_TF…ä'Nč6ŸÁ¨8FĒ‘j0ʋŅiôũúõcĘ3E —ˆˆâ–¸ÁZŽ‘fÛŗgĪ;šŧiĶ& ļŸzę))5jÔm­ÃÚšsg WŦXaī z1ڋĮšē]Įˆx¨¯ą‰Tl¤<Ŗad"""`ĐKDDĩoß>ÕMœ8ąÔ­[WļmÛfwšŖ0B‹jĀå˗בÂN:ų\Ē c“&M4P4#ĨĄ A?ÖÅEq(ˇĀûƒ÷đŲgŸ•ŨģwÛģ‰ˆ( 1č%"ĸ€Ā¨a˖-%Y˛d„œú¨ū‹ezŪxã ųđÃeȐ!˛nŨ:šté’}÷ˇÔĄCɔ)Ķ]~(pĸ iŌ¤!_¸ °LT¸V4F ŋđ""ĸ𠗈(ˆ :0ĒÍöęÕKGŗeË&>ø äʕKęÕ̧ķJQœkĖūöÛoöÕc 4æÜbŠžūũûëRBšsįÖ^ĖamÔ¨‘Lš4é–E¨°tOžÖ .DÚî6YŸ6mšŊ™BN&…Ûœfĸø€˛e˖•ŲŗgÛģˆî8Ŋ!)v3f”ƍKÚ´iåôéĶv" r¨rŒTSŦ÷š(Q")WޜۉËčm üũ÷߲páBMÆō=˜Š‘ĪņãĮkzu°=Ū@@ĀņâE{3…¨ũû÷ë2Fá8¯™(P&OžŦË 5J 6ĖîBtG1č 1H}ŦPĄ‚|ôŅGzšmÛļR´hQūx…,ŸRĨJ=čnĐ ,X°@Ž^Ŋjw Jׯ_—ųķįëHY„ ĨZĩjräČģQĐ+\¸°ĻöQ܍9ROŠĸ "ü_{goå´˙˙{qÍŠîm6… ‰ČMe •_ČÕ LID’!RIŗT¨š”&SŌ )’”!¤A„KqČŧūįŊÜuūĪY{Ÿ}ÎŲĶyö۟÷z­×9{­õėũėg}÷ķ|ŋk}×w1°ÄäĖwŪéĩĸôŅ›a0ãpĘ)§ØŲ@ Å͙Y_!D8ŲŧyŗŠjÕĒ­ĸ”`Í=xd´k×.įđ˗/ˇ^'ĖjkKvîģī>ë‰á@‘_¸pĄ°BŽyžvīŪŨŦ_ŋ>p”š ŽĘÄuhĐ A‰ü1¸tĀ˜>øĀ¯"-ČčÍŽģî:Ķŧysŋ8*<ÄYC‘)q„ČXĪT¯^=ģæūíˇßöĢs” ļ^bá?üĐ¯"4°g/Ûq[ŪtĶMĻ|ųōÖ=sƌ~!r 3Y΂ËrxQTĢVÍ 8Đ¯"eČč )ß˙Ŋ9äCĖĉũĒbAÕ *X÷-!Dō!xûvėØQÛ1‘cđlŽRĨŠißžŊŨ’LˆlåŗĪ>ŗĪē=zøU Áī5kÖ´[n2%DĒ‘ŅBEkŌ¤‰]K”¯žúĒøŦ B$—E‹ŲAĨącĮúUBˆeG<§+WŽl]6…Č6>úč#|ŠĨ;Šo)bˇ&Ķ÷ŽáGFoéÕĢ—]7ņë¯ŋúU%fȐ!Ö%E‘T…H/ŧđ‚Ũšį•W^ņĢ„9ĖDl'd2\?…˖-ŗ'ŠÜÅSęōË/ˇAåŠÚūHˆDŅ2^~ųe;jœĖm‡:@xy!DbĖ™3ĮāxķÍ7ũ*!DÃöd VHRËDĻCtew§OŸîWĨ‹Ø*ŒĀ­,Ī"Čč Ŧ›Āeōĩ×^ķĢ‚°ōŪčÛˇ¯_%„(&ē(¸6 !„QžŲĘå /´˙ ‘‰ŦgŨŧyķüĒ”3lØ0;ņ“ËÛū‰Ô!Ŗ7$`˜ÖŽ]Û <دJ  āFōâ‹/úUBˆ"ā÷CКYŗfųUB‘˒˜íe/Ķd,Q"°.“/K—.õĢŌŗËŨ,%"™Čč W]u•šä’Küâ¤Â Ž™Ÿ|ō‰_%„(\O<ņD3`ĀŋJ!"`ĶķÎ;ĪtîÜŲ¯"´<ōČ#ĻjÕĒæÃ?ôĢŌΒ%KėzâĮܯ"ndô†€QŖF™#<ŌlÛļͯJ:=ö˜ũ,"æ !ŠæŽ;î°îŠBQ\Øv°zõęfüøņ~•ĄŖwīŪæĐC5ëÖ­ķĢJÕĢWÛßĐ=÷ÜãW 2zK™wŪyĮēq°įgēčĐĄƒšā‚ eRˆ"XŧxąĘúÍ7ßøUB“•+WšōåËÛxB„ôĀ›ožŲ{ėąæë¯ŋöĢKžŊuęÔ1mÛļÕr‘02zK‘īžûÎtĐAæŲgŸõĢR îšõë×7Ũģw÷Ģ„˙e€uöĪ<ķŒ_%„ł-؃Tˆ°ū•W^iˇ Â3!ŦüđÃĻI“&æŦŗÎJ‹G¤Č^dô–ŋ˙ūģ9įœsL—.]üĒ´Āč؁hĻL™âW !ō3fŒU„"^RyČ!‡Øí΄ Dg0CŖ2ė` _{íĩĻV­ZæĢ¯žōĢ…(2zK Ö(œqÆö‡\Z8×ę÷Ū{Ν"§áwÉZĸ×_Ũ¯BˆÁārŨēuũb!J…íÛˇ›F™-ZdܞŌ}úôą’é\(˛ŊĨa؉†õ¸n2 ŊyķfŋJˆœeŌ¤IšåB$<ģ>øāRŨF@×;ųä“íŽ!Če&2nÜ8ģÉ… ũ*!b"Ŗ7ÍŦ]ģÖūXŲ>(,tíÚÕ4lذTg… 4HûZ{!Dö2hĐ ĶĒU+ŋXˆ´ņå—_šŖ>Úę|™ËđTœđĀ~•Q‘Ņ›FšŪ˛eKŋ8Iš=Úp"—aPŠˆ–B‘LØzeÁ‚~ą)åũ÷ß7UĒT1Ç÷Ģ2ž 6˜ŖŽ:ĘtîÜ9cŨĩEúŅ›&FŽiŨJ V>øāë.ĸuG"—iÚ´Š?~ŧ_,„ q×]w™;īŧĶ/"e,Y˛ÄT¨PÁL˜0Á¯ĘØn‰Ā°撇–ˆ…ŒŪ4€É:ŪO>ųį ĶĻM3ÕĒU E-!J\›Yû$„ÉdƌæüķĪ÷‹…H nŨ+ÁSŗļ`ēôŌKÍ駟n=…ˆ†ŒŪÃ:öÃ}îšįüĒĐrīŊ÷šzõęŲ›ˆšÄƍí¨¸B$Ö ÖŦYĶ/"éL:5į"˙ņĮæÖ[o5Gq„Yŋ~Ŋ_-„ŒŪTÂú‚ƍ›nŨēųUĄ†.ž×\s_%DV3oŪ<;R,„Éf˖-fŸ}öņ‹…H*ŖG6•*U2˗/÷Ģr‚‡~ØŽaÎÕī/ GFo aũ[ŸdâV@Ûļmŗkŗ1đ…1vėXsÅWøÅB‘0čģ랋_,DŌ ’1Ū…™°œ.•0Ķk÷Ë/ŋėW‰FFoŠ˜9sĻ]ûÍ7ßøUçŸ~šsî1"ˇyä‘GL‡üb!„H˜üŅėĩ×^~ąIĄ{÷îĻFæķĪ?÷Ģr’E‹ŲåJcƌņĢDŽ"Ŗ78c‘¨y™ÎėŲŗ­› aá…Čvú÷īo× !D˛!Ęl™2eüb!‚%i;v4ĩk×6ß~û­_Ķ|üņĮæāƒ6={öôĢD"Ŗ7É0’{ėąĮšaÆųUˁÍ 'œ`vėØáW ‘U°Gī 7Üā !D°ÆPŦD2ųõ×_Íå—_nęׯoļnŨęW‹<؍„öíÛgärC‘čP.šäkôŽ=ÚĖ›7ĪŦ[ˇÎÜ{īŊvíB$ŧÁʖ-ĢũSEÜā)Āō2žU"yp]ĢTŠb~øaŋJd!2zāšįžŗŠķĻM›üĒŦįįŸ6õęÕŗ‚aeÕĒUæļÛnŗëw:č ģj„ 1•Ī•+WÚļĐB$fy;uęä Q,ܲ2ÍHφõëכ#Ž8ÂÜzë­zîg92zã7nBK—.õĢrĸßą–yÚ´i~•Ĩ-¤N?ũtSŽ\9sŨuוxŨÎņĮo^{í5ŋX!Joeʔ1“'OöĢ„(’^xÁz&͙3Į¯Iäģīžŗ:ÃĨ—^j'uDv"Ŗ7Øã‹5­#GŽôĢrŒ~nČ|đ_%DÚAądÄļf͚öSÅÃĐĄCŗrŋm!Dú`ĢŋcŽ9Æ.ƒâ9ŠXĸ$ā•D°%vŠį§Ÿ~2-Z´°ņ=ž˙ū{ŋZd2zã e˖ĻmÛļ~qÎ2nÜ8s衇ڑ2!JÖëžrĘ)æČ#4'NLØE YÖ~šBˆDčĐĄƒÕĪ‘ōåËkË"Q,ĘZĶ÷ß߯)„ø<;w6Gu”ŨĻSd2zKČC=djÕĒeGpÅ˙įæ›o6gŸ}vÎôĨ ŅĶīžûnë>8bÄɟ" 8Đn-ŗe˖ü˛éͧ[CæŗĪ> ´ĸ ¸˛Ŋ՚5kü*‘&zYĩjU; .˛Ŋ%€uŦã]ģv­_•ķ`|4lØĐtíÚÕ¯"%|ųå—v NŨēuĩo´"4 4Čz?m܏ҝ2ƒ6ĩk×ÖēAt(–Īņ|Ĩ K¤´ž:ģŅ[LÚĨADt6oŪlG'ŲûTˆTÂr‚¨ąŨ.B˜ĨÃāũâ‹/üĒ|Øîī(!x)]uÕUæä“Oļē”.r6ËøDæ#Ŗˇ Tŗ°[ÄæŊ÷Ūŗ#cīŧķŽ_%DRx÷Ũw힗›Bˆ0đÃ?Øõģ'œpBÔŪ Ä `}Á‚~•ČA¸HĨFŲ@Š"\m ûôéãW‰ CFo1`ŊsÎ9Gë‹É”)SėūÅß|ķ_%DBāÆ\ąbEķØcųUBQ*°äéØc5mÚ´)vŧö\%XNŧæEvĀ`ÉYgeš7o.—÷ķÕW_Ųx>×^{­ŧË2ŊEđėŗĪÚE&.Ũģw7õë××]$ ˇUذaÃü*!„H;D‰ôŅGmTæGyį.’ķÎ;Ī<øāƒ~ąČØįÔSO5W^yĨ Š `Ûļmv€ĸI“&v°Bd2zc€Kƒ\uãeā‚ .°[6‘ Úˇo¯­Â„Ą€Čē,{"ēB<ļRĨJæĮôĢD–CœŧX۝č{"}üúë¯VŠS§Žŧ3Ŋ…Āˆ{~Ž5Ɲń­¸†rE‰2ū|¸jëÖ­~•B¤ūũûÛmM]ö„[+[!ŠÜaŨēu6ØYīŪŊũ*‘!ã§zõęfõęÕ~•12z á’K.ą‘ôDb|ōÉ'6ōÛ= /'t’Ũ>@!˛ ĸÃ28,rƒ?üĐ1‹Į^„‹ĮÜÆY˛d‰_%BŠŒŪ(¸}ô~úé'ŋJÄÁ‹/žh*WŽs ! ãå—_6ĮsŒ\Ā„Y Fīë¯ŋî‹,céŌĨĻB… æé§ŸöĢD†Â6Ļ,ƒœ>}ē_%BˆŒ^×^{ÍŪ”>ûė3ŋJ${2[§QRØĘaĈ~qÎÃ= 79?ŗæ(Ŧ_ŋžĀšĻĪ?˙ÜF¸t°fmÆ ų¯ Čų%ęˆ˛:oŪ<ģ'9ë2K›oŋũÖ^[‘™Ü}÷ŨĻsįÎ~ąČ"¸_`͜9͝ÎÛoŋm'vd3üČč đå—_ZÁefI$ŸK/ŊÔ´nŨÚ/ĸPđ˛Ī>ûØ(—ĸ eʔ1ųË_"ōßūö7]Ŗ4•ėąĮæâ‹/ÎÍ6e§vZūë3Ī<Ķē~9nšå{~˙ųĪōË2b~øáų×ģ]ģv~“´sų嗛ŋūõ¯~ąČ0ˆX(˛f1xĩ/söÂļe5jÔ0Ũēu“WZˆ‘Ņû?˜AaëÕĢ—_%’ĖņĮo† âW •W_}ÕF¸‘`ô–+WÎôčŅŖ@ff|įwļJtq÷ ļn ˜‡Ŗ(Ŗ—QpĸŨfúƘ1cŦąÛącGķŪ{ī…b†UFofƒƒHAĪ‘Œ;ÖŪ—-[æW‰,cĶĻMv *îĮÚŽ3œČčũ7Ũt“ŅMjaö‰ƌE[|ĢV­übaū4zYŽÆíˇßn ŗņãĮûU)Ŗ(Ŗ7[0`€ŊļapkvČčÍ|4h`ĻL™â‹ †¨Üp€ųčŖü*‘Ĩ0ĐLDv~Īx‰p!Ŗ7‰'šC9$ãg 2ļŸaŨ4î BÄâę̝ļåD$ąŒ^–h`˜ 4¨@9äŅŖG›kŽšÆ´iĶÆ\ũõæšįž+°Î–ˆ”ūėq0˙÷ŋ˙ĩíØncŌ¤IųĮeôrNŋ}ûöü2xã7ėzF·ŋ‹/.PĪ,Į†í!nģí6ÛöŽ;î0Ÿ~úiļ@@ 1isà 7Ø@#ŅhķŦ¯äØëŽģÎL›6­@=oÆ íĩåÚq^Ŧī.Ģ÷ŨwŸHåƒô•W^igŠŖ­ģf@°gĪžöŗéŽųíˇßüfvmôƒ>hÛá2Î9fô˛ÖŦK—.ļ-ûōÚ™ ĪŲ­€vœC´ë*RK÷îŨmŲ÷†Ã;Lņarž§:u25kÖTא‘ķFī|`ʗ/ĒQû\`čĐĄ6"¯¯ ¤iĶĻf„ ~ą0ąŪ[oŊÕfA#å렃2ģíļ›uÁÂ՘Á'ÚgĶ›5kf Ø`f‹ ÚíēëŽųÁĢ’ąĻŀ2۟ķqŸ ęÃh9eÄØsĪ=íw¨UĢ–-Ûk¯ŊĖōåËķÛēYXĸá6nÜ8í-Æ\Q`øązŋũöŗįÂ{p,īÃŌ 8á„ō×R˙īüÃ?xīô'¸~s.ģė2{ŪėĀē>ŽÅĢ(.m÷Ū{oûŲÜiW¯^ŊüA`ƈ>ŖčCÚņŪGuT„Ņ‹ŌíŽ-†úÁlÛôéĶ'ŋ /ß ™āŗøŽûîģ¯}˙ŲŗgŪM¤ž‰ōjÉ|äēņÆíRŽL_ cāĻZĩjvŒ9mônŨēÕ*O>ų¤_%Ō3˙÷˙'—rQ(ˌ‰Hĸ­é%ˆFŖFėš^fƒüûß˙6ģ랋yë­ˇō˘Ĩ<ũôĶ­1ŒÄėÃĖ#ŗÄŽD^îģŧnßž}ūĖ'ë ÚļmkËĮg˜ŅģĶN;å—Áa(w†ĮbãZ¤e˖ļ],Õ+WÚk†!42‰?ĀąĖ;úõëgËØo3Ŋ´Ã¨tŖũœŖ›)v­Ŧ ÆčDIörOŋ öúõë[#—Yo‡;Į ŅË6q”qŦģļĖwčĐÁļcØ:…vÁˆ˛t,[ļŦŠ[ˇn~™H=,EĀ%Rd.üÆÖÉ}0xš ƒö˙üį?ÍÜšsũ*Q ä´Ņ‹r„ ›(p[Dąō•s!—\r‰yôŅGũba ŪLfļîŪ{ī-āFËzAf3}øũqĖûīŋīWYœ‘‡;qD^ LfUŨ,Ēƒ×”;ŖËŊÁ÷vāĨà %0ãŠaŒŋmÛļü6|=ą‚z13ÃgD‘?å”SŦ1íܘKjôâ>„Ā6”O:Õžv3˛l—įsîšįځ h‚a°@ˇ`oAŖ÷üķΎÆqđ:זr~W0|øpûŲ¸Ká:°•HĖŦŗ­ŸČLĐg.ēč"û›õīi"ˇ!j7†¯ög.}rÖčÅ ŽN:…ŽĮéYÜī´wˆÆH˙ūũũbaĸģ7cŊųæ›Öƒc†uœ>4ŦÅ%HÆcÚF[âÛ-†$Ą}ŒD^ IfĨŖA9īÎčeÍŠk悆ŗ,´Å°CųĘ[ŗfMāˆč0ƒĘõŒ† æJjôŽZĩĒ@9÷:Ęšļ€ĸŒ[u´užČ>m.\hf˘a˙Įhöa}qĐčåēãJí¯Į&SÎuöRv.׸áq'OžŦe'ĨŅ}qW™ƒKĖŌŗCQ{E4x%¨ĪQzä¤Ņ‹kJF˛ÃÁ’%KŦâí+‡B0…Á%"‰fô:¤Ap>Üf™Öž˛ö‡Ė`kĻ1ŖŊ„Ú}÷ŨÍÉ'Ÿu–4Ŗ—ķÃHķ]‘”ĶwAgôbØų°f7hôŌ~äȑvļ—R÷]/ŧđ˜†.Č($ҏ˙ūûí{0˜%5z}Ŗ{ÖŦYŒ^Ž{QG/ÚžôŌK抧ž˛˙ģâ wŨuWŖ—…ũ÷ßߎŽ–YˇíĀõšža­´ģ^Ŧ-–‡Ez!€kÕEfÁ65܃ŽŊöÚ…đŲ¸qŖõĘáY-HĄH=9gô"t•*U2sæĖņĢD)ÂZAX­ƒAĈˆ$–Ņ įœsŽ5`ÜcŖCŖ)¸•c}Ŗ—ˆĀŦëÄ*,K"F/p.Î5ŲĮš>CIŒŪ Äl ú2ŗÆOT⠀†b4ĨÕš>9’mô2¨Ãlz´€XÎõ™5ŧ¸ŋō˙ˆ#üf3ŊUĒTąk„K ßņá‡ļÁ˛8§0ėAœ+°üā_˙ú—_,B ú$īđĸ8đ\♄‡ÜāĶONŊ¸°>‹‘{>ˆäzŪyįEU+‚ŗ3.ũˆÅ\;Ž.ĀlŖl^ˇÜəųÃgĻe‰™w?ō3į…^ÜÛ Ėåo=Äļ|Ė4`Č‘œ™ũ÷gÁ]lđJ¤č7‘đÛbPßŠ%[„ŒÄ­āų$ŌGNŊėŲČčŠŋ6M„LFēũ€9"ˇÁm ŖDŖŖ’(ŋÁĖ:ĐC=Ô-.HÜæÍ›íēQÖr:Ŗ“Qf~k´#ŗnWh™åÃû‚™>?ģāH‰Ŋnr§Ü^–ėUËė/Á? ¸F/į…‘ĪŒļ[*Á=Å´ŖF Z oŽ'ßÁEFÆëC™c‰ėHļŅ‹!Ęubp‡>ŒPVŅ.(ûîĸhķŨ0†ŨļOAŖWlÜģ;î¸ü¨ÕD†fP‘ļĪ<ķŒ-ã/¯‰˜ífšYįËā.×Úģ>}0[¨g_f@´wļ,‹PˆârįwÚg˛˙ŒŠ#gŒ^ĸĻ!\rŸ 7ĖP1zęĪzˆÜ…ĩ¨¸ÚúŗQšNŦčÍĖLâŅācĪ@ŒLf 1î˜AÄ(b†‰cxāk ųīågÖĖBĸF/°mƕ;gūōŊ‚ŋ˙âŊĀV=ĖPĶžĪ& ˙Ĩŋ¨ÁÎE‹åīLThŽ3ҏEMļŅ Č8k°)GÖ1X¤`ŋåāÚ/%.ŋürێuˇô%ŽĖôƒŋO/[ā¸kÁÖVüå}ƒF4ߋA%ęø<‚Yņ?ƒ/ŧđBāŨDĒ!’ļg ?oŧņ†]rCĀ7!…å*,š n%(RGąŒŪßō”ޝ 2|˛Yž÷};īĄ˜išS^žĨ<ėyYž’ō^ž2´ž}{ķĶÚĩ~×D%ŋŋęÖÍČū˙—?ûË/Έœ§xŽČSœ?Ŋč"ŗŖĨX ¨ÁƒûÅ9 Ļp? fĘÜŦi4Xŗ‰bÍŦ'ŗŠ=¸Įr,ŽĖĖūúīég܏™Úā6GâÁxsŧûîģVAtāČņ~tSfQ ™Yf{FĮąÎ؇MĸŪÁ ˜÷á;bTû.žąĀ¨ÄØã\Ø_1ÚŪÅDČæ|ĸ­Á ÂŦ8íü ` ėQîŋ7×÷j>›XwŖÁúkÚa8ķ}1ŦįΟī7ŗŗßŦáæZđž…oäÚ˛2íhīou$R ŋC ŠX{Ig č'_õécVtRFę'7æåĄQĘCŸŅMĒT1Ÿ^pAVę&Vīí×ĪŦĒS'ãäjČ_ū”+ŋ<#ō˙äju“&!WEŊÛ확•+›OÎ;Ī|3l˜Ų:wŽŲūöÛĘiĘ[^yÅ|—§~~Ë-fEŪCqGEõWéįīgÎ4ģw/V‰âqƒ;Y0“BdėÃĶ!ÛŲžh‘YQĄ‚ųäÜsĨŸ”BF7ųĸ[7ķnųōf{Í,ĸ÷ލXQrUJŲĘU׎æŨråB/W1ŪŸ×¯ˇ?Žo†ø’ĘéĪ{ô0Ÿļšđąũ•÷āäGī̜ūŒáĢŋDÉh׎iŲ˛Ĩ_,„ČpØ3÷^ļfĘÅ@†Ŋ{÷ļm˛ô“åe˚˙<ôPÄŗR9Ŋųëŧ>`räˇ,XŗoõŪģöڈī§\:yM‹æ‹oáĶč]Y­šų.īFė)åRĘK—šwöØÃīĻ|č/Ü "ŽS.\D‰’ƒ›3AyŠ "$„ČLXŖGŊîžûîŦ7~š—áÚĖzōlfEåĘæģéĶ#Ÿ“ĘĨ’7OœhŪ?ôPŋ›2Ž•UĒHŽB”­\U¯îwShˆiôDiÛ[oE|)åŌË,/ õWørŦūņÁL/‘yeø ‘Ŋ°}[)a˛e[4e#­[ˇ6Ũēu틺Že;ī,ũ$DyA˙ö7ŋ›2éŊáĘa—Ģ˜9 ģ˙…”K7Į2ĸÔ_áËąúKÄÛėíôŊ÷ŪķĢ„Y7nlŽ=öØŅĀŗ {ļՊq=[~žœ ú‰ä*|9ĖrķĖ$LáËą„IũžĢŋDb<ūøãĻB… vĢ!DvÃŪÍDpŋũöÛ#ļŊĘT:vėh:tčāg%ŌO—ŗA?‘\…/‡YŽbž™„)|9–0Šŋ—cõ—Hœ_|ŅÎø>øāƒ~•"Ë`oã .¸Āœx≅î9œ)Ŧ]ģÖÆ'ظqŖ_••H? _ÎũDržfšŠyfĻđåX¤ū _ŽÕ_"9Ŧ[ˇÎœ|ōÉĻAƒV‘Bd7´ŗž‹-ōĢ2\ļ āg-ŌO—ŗA?‘\…/‡YŽbž™„)|9–0Šŋ—cõ—Hėí9hĐ ņõŽ;îČúˆ¯Bä:/ŋü˛ rõüķĪûUĄį‰'ž0ĩjÕĘÚā\Ґ~žœ ú‰ä*|9ĖrķĖâĻןzĘüģIŗpܸˆēdæ÷ĻM3Í52‡TĢfŽĢQÃŦ啈6ßŧūē=—'zõЍKeūnņbûš#zôˆ¨K$ĮĻxû‹ëČš>˙čŖuÉĖkgĪ6—{Ž9ėĀMÍÃ3ī<ûlDrÛĻM͡ŨQžęÜūâ‹M˙[n‰(O$Įę/‘|pl׎U†ģwīnŨ!…ŲÉ[oŊe×õ?÷Üs~Uh!ø^ųōås.ú|ŧúI—ļmÍiØßˇīÍ7›ZGiĒ\ŲôËû߯'ŧ÷^Ģ+m~㍈ēTæŅ÷Ũg?÷?¯ŊQ—HÎũ$^šzøÎ;í5õ˓' dN;ás@ĨJĻõ…Fԓg?ö˜=—ÂtâTåšŖGÛĪ}s„ˆēDr˜å*æ™Å+Lã4É;ö™"꒙ku”Ųi§LãSN1M64ۖ.hķÅÜšö\ŽģôԈēTfŒm>ˇ]ŗfu‰äXÂo!øœkĒ ÍķëסŸszíÚæÂ Ė—ķįG´!īžëŽļ?ũōTį}öŪۜwúéå‰äXũ%RĮęÕĢMŨēuÍžûîkš7onžÍ{˜ėØąÃo&„ČpŪ}÷]ģŽnŪŗ>ėlŪŧŲvØa抧žō̞žxõ“Ã:ČŖ~y2ŗĶYĢV¨`Zžwžy˛OŸˆ6dÆi—lãŗ¨|}˖ös7ŧújD]"9ô“xåŠ~æšFŗ’•?œ9Ķüm—]Ė~ûėc.nÜØô¸ūúˆ6äGīžÛžË‹#FDÔĨ2ęŨÛ~ލK$‡YŽbžYŧ”ŖAÅā=ˇ#EFoŅ9]FoÅōå‹õđ’Ņ+åŅGĩkūæĪŸoFįÉw×ŋœGŧkBb SŧũÍčåÁp×ĩך}˙ū÷üīX%ĪP{˙ũĮķŊ+”+Wāš}čĄö}Š~Ī=ęČWįūû¸ŒŅ{Áŋūe:æ]cū§ũÎ;ídš7n1ÂúāwØķ ž7k†ũõÉ|—ŋīĩW~ūG΂@ŒŪŗëÕ37ˇnßˇxÄrÅ.*Įę/‘|úöíkˇ3Á°(ÖŦYcƍgēvíjZæÉZũúṍy˛ŗķÎ;GČk=üÅ!ãŧÕ¯ŸŠVąbū1螝ŽU í’ņãMũŧg:„kˇ×ž{ÚYå ĄÅqG|p~›ŋūõ¯æĖ“O﯇kãŒ^Œ(ÖģļUŠb¯…žÅÍŲ ŸÄ+WҌŪ÷ĻO7N9Åöë tB&t‚ĮōēÉg˜Ũū§Ÿ’ųŨŪÉā y2ëę\^:iRÄyŅ‹<Ž\™2æąž= ´e"Wé ũ]~üu@?^5k–9㤓 |>zyP^œŅ; KkcšvØ_EÍNĮĘa–Ģ˜g¯0E3z/:ķLûãŋŠU+k@bėÖĢUËė’§`§ôŠįX~ä¯<ū¸Ŋ!pƒĸÂ@:sÆ#ØvĖĖq|aŖ^ÎčÅh:ūˆ#ė͊QsN;͖ UgLc¤ž4r¤™7fŒ nĀ •`YŽĮđcāŧp‡áķYWĖą,Z§3zų\n΋|g~(”oē%Éą„)ŪūŠfô^{É%Žߑ5š|ī áKĀÚaÜŋ0|¸}Č ŧõVûā`} F%ŖGĄyōąĮÚ˙WLq.sŊšnį43OßxkâD;DĖ÷šÎčĨœĀ^|.íöÎû~|Žoė7gƒ~¯\ųF/3Ą,Á+ŋ˙ūÖāCŽ\öΞe­NȌ=í˜(ĢqđÁVŸÅXĨo‘Ĩ 4( ÛcįÜ~ÕUļ ų¤Ī° üķ ;Ŗũ“÷c€ƒāWØ>˧Ląí8׏>ÚĘē+ŸžJ€XŽĮ~rī‰ŽÍ6˛øËúą›Äqē1Ÿ{iŪ{`Ŗ ›|_ʐG˙\‹“Ã,W1Ī,^aō^ŒW^3ƒlˇ)Ī(ĄS1KæĮO #˙=11š[ĒsonßŧyDÛ`vF/Âtā§aŨēųe|.ŸīŋŖ7ŧ‡s}áÆĘ&Ø÷ŒÂ1˙ €āŒ^Fj܏…ĖwæFE47˙sŠ“c SŧũåŊ+Ÿ{Î^kn Áv°<|œ1KŖbGU¯á"rÕ˙ũŸ}Ī {IܛyXáîãĘčwú+cdëĀ<ųņ]†Üú7ĒFÄ<~Ā\{׆c˜Áõ^>;xÎ|/dôĐ(đÅÍąúK$UĢVY—eĸš !r—ŠS§Z7įŌ62ß˙}spžrLų\ui¯~âŊ§ÖĒeu‰ĪæĖ)ЎÁvžûΘÅp!€?‹Ą@ģ w`IŨ›wíZ ü–6mlšķÄĀ˜}vȐíĐ­hįīį<ņ„}lĮÄ z§3ĸŅœ¤!՚r˙;7gƒ~¯\ųF¯“įĄč˛Ķ1ûZžü`û;}`“ ?b¸2įŪ\ԚYgô2y,Ļ˙ōÁdz°Í}yãˇÁkä†ãühŅČã˙u–y÷“NÎčE—öÛQÎ p°ŧ¸9ĖrķĖâ&ßčedŠ×ŅĻö™QdöĐŋšLįq#aV˜÷p#Z%5z™)ôë ŽĻųÈŅÜx֍R~ãWØ×÷ĶyB4j]vF/3Ü~F"ŗŋ~yqr,aЎŋ|Ŗ×=<yôÛēŲđÂ>`œâփ{2ípqu%1zq ōˑFĒür—šy12ånfnËŦû;wļ¯yO~ä…ÉF/Ŗc~9ŽFŒūųåÅÉąúK$3Î8à >Ü/Bä ė;*Ī(-ølļ%ĘÅ(ͅ¯~4z™Ąb@žÅŲgG´c$Īy–ŅųuÁ6lUC;–7šō’ŊžgĄķ:cÖÍ?ÆeÜRŲzˆvnyûĖZcDŨÛącĄ3kÎčuƊËîũ˜Eö)NÎũ$^šō^–Å=:ƒšō?ūaN9ūøˆr—™ôb–” ­āDZI^K‚åØL”#ū1.cāeÉŦ.ŗĀޜŲZ&ö:ˇje'ũÉ!˛3zũ-]ŨāŒo\7‡YŽbžYŧÂäŊÜ x+ãvJ[\0ž04™Åsõnm§s(ŠŅÍ=!Î2c°1:HDčƒĢVÍ÷ëwŸíf2Ú¸sc‘7S7íŒ^\ĨũĪÅā ÎX–$ĮĻxûË7zŨ Vžúŋ…ųŒ0ąæc–Aˇ~Å]3övŸSŖ—YXŋœ™xn>Á2\­1°éKˇnÚ}6#r´aā÷āÚnÃÁĩÕŊ¸ŊûŸËlļ?ŗ_ÜĢŋDr˜™§€sĖ1æ÷ß÷Ģ„9[”ą5PēgX –wÉ%—˜š5kæÜ>ŧE¯~4z+ãĒėŽeė¸5ėķ=¨$bô×N’ҁ(NāRŠqËš3ãüė`LŧŅÜų3“‡^‹ááÚ8Ŗ×°G˙ĄÜŸQ.nÎũ$^šō^–äų˛ĖØ#îX&˜Ũg,ۜÔ3xŽ™ˆŅëĮĸ!ŽåÁ­Žd›ÃÅÂũ78‰‡GvŒ; rftŖ­éuúŧËLVQŽ{ļŽÅÉa–Ģ˜g¯0ųF/žâŧ&‚~íŅ27„Ą-N×+¯´ūåīΘ‘ŋļ2^Ŗ7ږEžŅ‹@ЖŲ>\ąXÜ ¨DšqšķÔĩĢ5”\€.nŠÔųŅ›ƒ9ėF¯[WÍčßO.ģ›/î´=)ī!Έ?"Ö¸ŲũxŪh[ųF¯{hŒŲwÖ_đPdũåÎču™ŧˇ{]Q Û˛HFoxÁ…ņČŧßđK/ŊäW !rŒŨ#Ž8Âŧ÷ĖN܇|đAŗžÚĨKŧJ$^ũ$hôžũ?ŖĪ=_'qŲÍ–âæŒ!Ā,*ąIĐÕPøņRã=1zũ€šžŅ‹N……ËiĢ .°Ž°ĖƏ†v~ OôY <YJEôĖʏŪ람۸åĘ7zŅá‘3_ž\v.äĖōcčŌŋ¸ ãrÎL;“aŦ—MÄčås‚åžŅËēct` lŧHŅuY‡ŽÃ2ŧhžĢ¸Đ#ëččLäa˜ģhŌ~ôf—eô–0ûF¯[wĀģߖ2.8nËíĸ˛¸˜o6É6zpāČoįŒaÁ7°`ÜnUq‘˙2Ųč%¯]°…`ÆÕœ¸Ûú‰vAÃŅe7H X•LŖ—›?3ėŦö×ģ#]ŧæGī?7f{iįõËčÍË‹Ņ›y\sÍ5æđ‹…9{nīwOßąc‡_•Ū}÷]sŲe—ŲhŅį)“Z^›xõ?ƒė xûQ–]Ŧ•=zØ×čY<÷ƒ‘Ņ39žv´wåÉ6z‰?Â9÷yŸrQvÎäŒ"ŒÜāûšĩēîģČč-œxåĘ7z lÆkv$ ļsרŲm.ēČžv1c\ž3OĄœžweÉ6zŒûņv8Ešŗ+ĐÁąš˜‰ļskuŨw‘Ņë¯0ųF/Ų…îÆĸq]ƀÁŨũ`1¤iaļ÷X~đŦÅŝāOdŊŧûŸa\säŗ ˆ€!äöƒsFÁĢ(–ŗ˙+kCŨĄ‹œÉF/yHˇnö;âjÃL=?hÖėRÆ”6 `PĪÍŊCŪßk†‹3Æ-ßĪ˙q&Ķč%ĶwœטĪf°„€SŽŋ\PŽŒŗ&ƒ6ŒžēđōÁĩŒŪĖƊ+šĩk×úUBa?üp3gÎŋ8!؉}wËæé*}ō”Ú­[ˇúMDâÕO|Ŗ—ŲQ&ԃÚĢS'„į9Á*ŨL3bˆhKô[žûėÎÁûĄįƒ›&Ûč%ē3¯q7E—"8(ËæĐAØęÆÍT3āŽŽ„~ÂÚcÚq.č"č§n9ŸŒŪ‰WŽ|Ŗ—Ũ=č/tJօ#Wč–Č ŽÃ¸Ķ‘6xv"SčüÄøÁ•ũ=Ų}F˛^tuôm& qĢÆ+“sEϐ-ô_÷}Ü ;ß`ČŖëbã [~ôf˙üdô–03…Žk‰?GømŒCˆĖĘĩÎâĘ]ŒÚ0ĒÂ{:C†€WŧöGČüĖ͉vÜLü:„>h4s3edááŗda9î2ŧú\[|âŲ›×įŋN:ÉûŽ#šc0ˆũĪe”u~yqr,aЎŋX'ÚΟL$B~Ėî;rŖö]:>zūyģf›m@FŽ%ī ÅĪ{]Š ËĖĀF‹ĮÚę 1ĖMˆëČÍ‡Īæ8\:XoĖgÃû#“ēîģđ tŖ¨.sîŅÎC>š wqrŦū‰Á– 5jÔđ‹…Ârà 7˜žyIĸ°Fw„ æÄ<ŖĒZžR;`ĀŗmÛ6ŋ™ˆAŧú qCüÉŧ™ČĀ€åyN\ ƒāļ„xßąæŖ’6ŗę™'’č}¸ ;ÝÁPågôMÚųûâĸķPœũÃđe’‡Īæ˜8Ā›Ų:Ō×#†6ē ;´e2Ã7hāō^ŧŋŋSËė(÷õčâælĐOâ•+ä…k,C6(Gž\_°ÎÚŧ.c‡ÄĘĩaPIŒKŪĶšÔģū™˙䓟ĖŦįĻ čë2kĀ)FWÆ#ũ•Īf`„íAYgüH÷îļ­ €†„ü3`D[ŧ4ąy‚¤‰v~üž(w\%Ía–Ģ˜g¯0)§.Į&õWørŦū‰1;īAÔ¸qcŋX!,ĒM›6õ‹‹Å/ŋübfå)Wä)'@MÆfʔ)Ĩž˙oĻ"ũ$|9ôÉUør˜å*æ™I˜Â—c “ú+|9V‰Ä7nœUH…"ķįĪ7Į{Ŧ_\(˙ũī͌3Lûöím$fļēīžûˆ üĻĸ„H? _ÎũDržfšŠyfĻđåX¤ū _ŽÕ_"1`ÕšsgŋX!,|đ)Wޜ_œĪ×_m&MšdŨ 1Žwß}wĶ AĶŋķ駟úÍEH? _ÎũDržfšŠyfĻđåX¤ū _ŽÕ_"1ƎĢ™^!DĄ|ûíˇfĪ=÷4}ô‘™9sĻŊUĢVĩŽ‹BáŗdÉSG÷āPđiķæV‡ņŸ“ĘĨ“ŋčÖÍꌙΚK.‘\…(ĶôIX‰iônĪ{`Ŧ¨\Ųl??â‹)§7ĶŒ”n{ũuŋ›ōĄŋ˜íU…#¯>ī<ķu˙ū~7‰$ŌĨKs÷ŨwûÅBaˇjžgl‰ŌgÛĸEV‡‘~RúŲę“yē=:cĻc픊%W!ČVŽ*U ĩ\Å4záķΝÍĒ“N’@•bæÚx æŗëŽķģ'Û_uę¨ŋJ9˙įĄ‡ė˙ˇ­[ũ.IdŲ˛eĻzõę~ąBØõģ7ß|ŗ_,J‰ :X]FúIéeŽ=:ũį7ŨäwOÆ˛á†ˇĩkKŽJ1[;%¯>īÔÉīžPQ¤ŅûĮoŋYCŠ‘”/nŋŨ|7}zėUNMæZã‚Âčč†ë¯ˇ}Qļŋōō\ę¯ôįī§Nĩƒī–-kļ/^ėwHĮwœ L#„A^õ /øÅĸ”@?ŲĐąŖÕiĐm¤Ÿ¤/įë“yē<:}qôÉLÁĘUžą%šJ. W7Ūzš*Ōču0]ŊļeKëA8ęLËWG) {æZ¯iŅÂlã ŋ;ŠÄõîÎūûfBÎÄū"¯¨VÍŦoßŪüŧ~Ŋß%"EĖ;×|đÁfĮŽ~•"GŲŧyŗŨ’č§Ÿ~ōĢD)À0ëū2QŸĖXŨ}ō˛ËBízš(|ˇ5—^*šJcļr•wÍ3Eފmôf:ųKÎ|ÕŦ@ũ%JB‹-L¯^Ŋüb!DŽōÔSO™ĻM›úÅB$„t‘ $Wé!gޞ*ŗP‰’đŲgŸ™ōåËÛ5žBҏqc3nÜ8ŋXˆ„n"Rä*=äĖU–@eę/QRĻOŸn÷äܰaƒ_%„Č!-Zd—<üōË/~• !ŨD¤ÉUzșĢ,Ę,Ô_"† bjÖŦiļlŲâW !r„† šQŖFųÅB$Œt‘ $Wé!gޞ*ŗP‰xéÔŠ“Š[ˇŽųúë¯ũ*!D–ŗ`ÁģŲ¯ŋūęW ‘0ŌMD*\Ĩ‡œšĘ¨ĖBũ%ĄgĪžæ€0+VŦđĢ„YĘöíÛÍá‡nž}öYŋJˆ¤ ŨD¤ÉUzșĢ,Ę,Ô_"Q&Ožlūņ˜I“&ųUBˆ,äĘ+¯4íÚĩķ‹…HŌMD*\Ĩ‡œšĘ¨ĖBũ%’Ҝ<ōHͤIŗnŨ:ŋZ‘%L˜0ÁÔ¨QÃÎö ‘*¤›ˆT šJ9s•%P™…úK$ "¸Ū˙ũĻ\šrĻOŸ>槟~ō›!2˜wŪyĮzu,_žÜ¯"ŠH7Š@r•ræ*K 2 õ—H6ëׯ7͛77˙üį?M=Ė7ß|ã7Bd+WŽ4+V4ĶĻMķĢ„H:ŌMD*\Ĩ‡œšĘ¨ĖBũ%RÅG}dŽģî:SĻLĶž}{ķÚk¯™ß˙Ũo&„9~øĄŠTŠ’W‰´!ŨD¤ÉUzșĢ,Ę,Ô_"ÕlÚ´Éē=Ÿp ÖõšM›6fęÔŠZ(˛\ú{÷îíg4¸4WŽ\،?Ū¯"eH7Š@r•ræ*K 2 õ—H'6l0<ōˆiÔ¨‘Ųc=ĖGa.žøb͝_?3kÖ,;ŖD›ĸĀeš€YķįΎŗOC† 1×_ŊŠUĢ–šīžûüæB¤…7ß|ĶyĘFm×đ2H%D:‘n"Rä*=äĖU–@eę/QZüņĮæŊ÷Ūŗ3H]ēt1įœsŽ]ĩjU+—ûíˇŸ9đĀķķß˙ūw[žûîģįם~úéĻYŗfĻS§NÖđ]˛d‰Ųąc‡˙QB¤…ĄC‡š-ZøÅĮĪ?˙lŽšæû{d™‚éFē‰H’Ģô3WY•Y¨ŋDXųī˙kgs]ūá‡lų /ŧ`*T¨`V­Zå!DéŌēukkøf2DfŽ]ģļ5ŪˇmÛæW ‘¤›ˆT šJ9s•%P™…úKd"cĮŽĩ3Ŋ_|ņ…_%DŠđë¯ŋڈåkÖŦņĢ2‚īŋ˙ŪtėØŅ(5Ɲ"­H7Š@r•ræ*K 2 õ—ČT `Ž>úhķõ×_ûUB¤™3gšzõęųÅĄ‡eŦŨe;ĸ:˜īžûÎo"Dڑn"Rä*=äĖU–@eę/‘ÉôęÕËrČ!æ“O>ņĢ„H+¸9Ō/-Ŧ}ôŅGÍa‡fõe˖ųM„(5¤›ˆT šJ9s•%P™…úKd:O<ņ„Ĩ"rŽĨŪ{îš§iŪŧš™={ļ= +D>īŅŖ‡uÅžčĸ‹Ė… ũ&B”:ŌMD*\Ĩ‡œšĘ¨ĖBũ%˛įŸŪn­2mÚ4ŋJˆ”sÛmˇ™víÚŲí¸U­Z5ĶŊ{wŗvíZŋiŠ@P¸'Ÿ|Ō4iŌÄF>ŋöÚkÍĮė7"4H7Š@r•ræ*K 2 õ—Č–.]jƒ[ŨxãvË!ŌÁĻM›LŲ˛eÍįŸž_ļråJĶšsg;›ĘžŊˆbętFC& kuĪ=÷\ŗīžûšĻM›š§žz*­į DŧH7Š@r•ræ*K 2 õ—Č&ˆ@{ņÅ›ã?>§g˛ú÷īšYÆl‡ũloēé&ŋ8ŸwŪyĮôë×Ī4lØĐî5ÍŪŌ´1b„ŨWz˖-ū!%÷ęšsįšûīŋß\xá…Ļ|ųōvŋëK/ŊÔîƒŊ}ûv˙!Bt‘ $Wé!gޞ*ŗP‰lƒwįáǛß˙Ũ¯ÎzzöėiÎ9įŋX$™ˇß~ÛŽ'Į}¸8üøãfΜ9fđāÁæĘ+¯4'žxĸŲ{īŊM•*UĖgœa Ö6mژnŨē™{īŊ׌3Æ 6ĖŽÁ æļmۚ3Ī<ĶzčĄfˇŨvŗ3ĘŖēå–[ĖÔŠSÍÆũ"Ŗn"Rä*=äĖU–@eę/‘­|đÁæ´ĶN3't’yë­ˇüęŦ†ČŧÕĢW7&LđĢD’ā×ŦYĶē 'ĘēuëĖŧyķėšt ]flīžûnkãíŊo{å•WŦ7ÃO?ũäŋt‘ $Wé!gޞ*ŗP‰lgėØąĻRĨJæę̝6›7oöĢŗ–ųķįÛīËˇH>7ÜpƒuB$é&"HŽŌCÎ\e TfĄūšî§¸b­ã=÷Ücžûî;ŋIVÂÚŅf͚ųÅ"A&Nœh:č bģ5 !J†t‘ $Wé!gޞ*ŗP‰\‚ˆļíÛˇ7åʕ3wÜq‡ŧ›ÍŚuŖŦ!ÉWy֋¯XąÂ¯B$ é&"HŽŌCÎ\e TfĄūšČúõëÍu×]gˇšéÔŠ“Ũb&[áģVŽ\Ų˘1ï%ä“O>ą×ræĖ™~•"‰H7Š@r•ræ*K 2 õ—Čeˆr‹ģ3ÛģÔŠSĮ<öØcYĩŊ ‘Ģ_zé%͏qc%˜mmD|ā%P­Z5ģ÷­"ĩH7Š@r•ræ*K 2 õ—‡Ī?˙ŧiÚ´Š)SόšâŠ+ėēÍmÛļųM3‚>úČÜ~ûívVcūŅGĩŗ“¸åõW”Œ?üĐpĀfäȑ~•"H7Š@r•ræ*K 2 õ—ųú믭qÃ>ˇûėŗ%ÅhdĻ/Ŧ°˙ë‹/žh# ŗw+ûžvíÚÕkA-Zd _ zQ<.\h÷Á7nœ_%„HŌMD*\Ĩ‡œšĘ¨ĖBũ%DáāęøāƒMëÖ­íēãĩk×ú‡ !2 é&"HŽŌCÎ\e TfĄūB$›~øÁ,_žÜLš4ÉôéĶĮŋlƒTļlYSĻLsāšSO=Õ4hĐĀ´iĶĻ@fļöŒ3ΰ™Ŋsiģ÷Ū{Û{ÆmÆ ÍÕW_ml]Ģŋ˙ū{˙ã…Žt‘ $Wé!iWyķöoĖčˇ0ˇĪlk:L¸0tųċkD”•vž~ÂEöz=ē°ˇųjËįū%M9īozĮ ]ÜĶÜ6ŖuÄš•vs [ØĢTúK‘:žûî;ŗnŨ:›_{í53oŪ<ķĘ+¯˜1cÆØüĖ3ĪØ2ōš5klģíÛˇûo#DNđņĻ÷˰ÅŊCŠ?¤2‡Q7IuF÷šcæ•fÄÂ>)×}>ŨôĄytņ}’ĢČNކ/ŧ/åråHŠŅ‹Ūō\KķÄûCĖü­sĖrĨb§…Ûį›§?inv…ųrË˙ŌĻ„_˙øÕŒ^>ÄtŅĘ<ŗú1ŗ`Û܈ķRŠžč¯§>‘ÖūB!ÂĀīünžZūˆô‡Kč>?eēNk•Ũšŋ|¸ä*ĮrõĖ'ĨLŽ|6zwüúŖ‘™ųåäˆ/ŖTü4iŨĶûåÍ&õÁMF-Āôš{Ŗyķ—%įĄTŧDõzšSZúK!„ŧŌr7M]÷”š˙囓ŽûLX>Br•Ãiōē'Í}/ߔtšōIØčõņ3xI÷ˆ/ TōtĪėf寎üKœT>ÜôŽIĶ%ņDŊģq‰‰…BˆŦcÍĻUv’CúCn§^ŗ;™÷6.õÅ#nÖmúØt›ŅFr•ãéŪŲ“*WŅHØčŊ˙՛ÍŦ¯§DœŧRÉĶø5›‘‹ûų—8Š ]ÜË<ŊzdÄg+•<Ņ_Ãßī_b!„"빸¯ô%3yÍ3zņ`_<âæ‰Å%WJV§ĩx/I%aŖ÷抗™%?ŋqōJ%O¯mŸk%Ĩfyū0?âŗ•JžŌŅ_B!D`–WúƒēĪ]3Ûûâ7ĖōJŽ”Ģ;gļķÅ#Š$lô^?ņ"ķÎīDœŧRÉĶ;,3'6ķ/qRš~bSõW’R:úK!„<ī¤?(%[÷‘\)‘’-WŅHØč%ė´âJņ'Žg*Q%7Ĩēŋ„Bˆ0 ũAÉĨdę>’+%—’)WҐŅ˛”ęW%7Ĩēŋ„Bˆ0 ũĄčôâg/šW˙ķjDyQéĨĪ_2¯}˙ZDyXS2uÉUŅiÎWsĖ‹^Œ(/*!‹sžÎœ­d“)WҐŅ˛”ęW%7Ĩēŋ„Bˆ0 ũĄđ4iÅ$Så*æđã7ĒU0§œuŠy}ëëļnÂō æ/ųKĸ]ØR2å*YiôĻËEdæ§3ÍUŨ¯2´šĀ\wīuöĩßϤ)ÕžËũLíīlo†Ŋ<,ĸŧ¤)Õũ%„B„0ęé˜ŊZúËRĶob?ĶĸC Ģīa<Ė\SPßë< sˇftŒĻí›Ú˙/ŊáR{Ėë[^ˇ3wŽ ˙īą×æÍoæ—ņ‹Xœ˙:iöÆŲææ7Ûso~ussīč{ |f<)™ēOå*]zéˆWG˜wūˇí›6]ۘąKƨŋ˙™û­§€{ũĀ´ĖaĮf˙ī=ļˇ9ãÂ3l_ŌĮÁãĒV¯jžzëŠü×üNRų†ŋ2<–9‘”LšŠFÖŊ͙ÍΌ(Ovb$nˇ=v3ûîŋ¯uاĖ>öõІF´-IJu‡‡­ŋ¸×:ŊVDy*Ÿ‰H—ēDԕ4Ĩēŋ„Bˆ0&ũá•/_1'Ô?ÁôyēOD]2Ķ‚ī˜#kiuÜKŅW0TwŨmWkøíIˇ¨húNčk_×:­–9°ÆÖíwÔķū}ž5¤Gŋ>Ú|äÁvōd¯ŋīeõČŽvxŋD:éž{īiõSÜdqŋæģp>žĄT’”LŨ'LrÅ,}“ÖMĖå7]Q—ĖÄėëŲ—mûĸԁ•Ŧą˙?öˇ¯ÛŪÖ6ĸ=‰ŲŪēëš+nšÂžÆXŽ|Pe+W{īģˇŠyrMģ~÷͟Ū4ũë_Í]#î2å*–ŗ2wņĩÛīæŋg2ԌÕ3LŲ–ĩŽ×~]IS2å*YgôrĶ`äÃ/OfbŽl…˛æŖą7DĘø{D­#L™ōeŦ‹ŠLqSĒ;a’+fD‘ĢTŊ÷%MĖđ œ20ęä×žĪŽ×å5™ēO"rEŋŗž7^§k˛æ0؎ëŠnNÔll×ö¤'™YkgŲ6cÉ¯Ã E–XOKĀ(ūg]7ú)õ¸ŧ㞌^m 6*ŗŽ~šKŦˇüÜāˆr"3ķūŦį5žŖØ#ȋ?øƒî‹ží^Ķ×čŪüĪų˛^ØÕuÚÍΏO$ņÛÁmYf‹ë!Ŗˇˆ„ŅAįbÜāúAB´1ʂ#St>7Ęŋī÷w{Ķd†đ.ÛÅT¨ZÁūhŒžczÚĩÃü 3#Éû1ũް"øį]qž5ž(īx_Įį„Ģ †+î&ūųēÄh 7cŋœÄCĪ//nJu‡'Ō_ F0zÄēŒ_Ö5ŗöÁŋŽÜ°š™âãĪõĻŋxĐ7¸îđāaBņ` ƒ˙§|0ÅĪŽ}ËņüP§~8Õ~ÎĨ/-pNüظ9ņ@ôĪ7˜™s#š2z…Bˆ’¯ūĀķ#­ŨíŦû.3¤Lxā~Ë ŠkËk Zt6ŋņŦÃ`å™M$[ÚLZ9ÉFąĨŒ-~Đ(ŋsøųúFFN÷‘Ũķ]”ŨV/.ŨūČíļ}´Áôĸz †;L1ŧPúņ2äģšüėûĪÚ:f9wŒÖņōŨú ) ÚctaŦŖīøŸ—ĖÄ93ášhŋޏ)™ēOŧrEâēŌ‡įˇ:ßPāēŽŠtÆÃôEdA ôIä‚ČÛ´ap€IŪųAŽæ};Īz R†^Ę  ŸÅēZįĸė¯íÆ Ĩų*xļE'fų9›×Č7Æ$ōâäjėâ?ƒ]]ØöBsrŖ“­CŸbė3@Ķ¸xŅb aœûЉ$ė.ŪÛŊ–Ņ[Db–#ˆÅÛA÷ n'ūëDŗoŲ}ķgî0zü׃īÁl-åãߟ_æģ7;Ŗ#5čĀįpÃõ;É9bd'XLė§Æ(Š_Nbԍ´Ä“RŨáņö.üāšyûŖMŒv1âã\˜1zš†ūĀ?BĘŨÚR4÷fŒ^rđAHbĻž›VĐčļúëb%ŊB!DɈWpKÂn{øļå<ãŅÜve„Įnö„‚!Ô­ĸš7;Ŗ—-^ū˙§,ˇq)w[šTŗNMë%,+NēyПz" ¯ ….ëg&g¨~ŨķÖ=Ŋ‘I™ū“úįŋ“]yQ~ ūį%31+ÉĖ!ā~}qS2uŸxåęšUĪYŖ•É—`9:*FúĨ+sËÚÜūČ.1 Â{8û š{ŗ3zņÄŖĶ•3{O0&î‚ī‰1‹ũ,/*13‹ė3qæė"E|šjÜĸą­c˛ĖáÜđŽpĒĪļELöq-Y˙ķ’™dô‘X‹áŪė×12G sF¯"uʃ‹ū 3zŖEtžļĮĩļveŦû¤ķüļÁ„Ņ‹KŒ_NÂ($T¸_^ܔꏷŋ¸9r­:õíQ‡yđáãŒ^˙†ĘåÁQąÂŒ^7ōLŽŋųˇšÕIĖ\ĮZRƄ5ŗģ|īxf!ƒ)™ēOŧrÅŦ&߅ĩŅ~nˇÔ1āĀkŒ^fƒF+ÉÍŦēĨŽąŒ^"‡„ÁÍûēIŪnô"ÚÆJxN2ˆˇ(Ë5ũú°'ŊE$'Ŧą˛sYqF¯ŋčœĩ”;ã˜T˜Ņ\î?nÄN¸ņŨ§-žī~Û`âρ[Ž_NÂÆpöˋ›RŨáņö—Š•eĸ­3zũĩ:ŧôˆ-įŊ\YaFo´7#ÖkģQ\FW­d?<ŋmŦ$ŖW!„(ņęÁØ…eØ ƒÁl˙=0Z¨s¯cŊū€;Ééœ.¸.Ž+ŅÖģFK Ŧ_qķö=8ŋh|Ēî¤Ė3ë•ũvŅŗęDđEgbÁ¯/iJĻî¯\9Û Vv1ōmY"ŗĄ´s“jąŒŪh:ããķˇunĢ)Öéō:čŅXTb9'3ŧĖČF‹3”ŽÄD“/[žˇeŦ$Ŗˇˆän@Üx0nŖåiOŗmķŪ@ RIŒ^ˇ¯•ŸXÄÎú֜pC+ŽKĄčĐhŅųŠņ]J’RŨáņö—3zé ŋŸ\vh;Ŗ×õŸK%1zq™–šÄÚ]ö›CĄhëtŠJ2z…0ū|ĶĻM3kÖŦü˛ÕĢWÛ˛#Fä—íØąÃ–uīŪ=ŋLˆ\#^ũŖ—˜,žÎĖnÃŏ’Ŋ¸3ûĮģx-,‹Ã”ĩâęj¸ģx3Ɗ7øSĸ ŨÔwuÅmÚoį'Œ6–§áŠô”K$%S÷‰WŽ‚K}yrŲíEėˏK%1z}÷|˛Äd—ĶcĪlvĻŊÖŅėƒh ˆ÷Æs (Û#• o _ļJ˛ŗŒŪ"ŌĀgڎæ&å×a,1zâ"ĸĨŌč%˜õŒ1˛+ښKn=‡3ō\b}åWŨõį:ŽxRĒ;<Ūūbt”ī毉!1D¸5Š4z‰ČĮ{āžÄ_ĸzûmŠJ2z…0räŸ īŋ˙ūü˛E‹ũŠô\~y~ŲÖ­[mŲ1Į“_&Dޝū€w3ŒōëŪ\b–*Ŗ—„ČÚ`ˇÆ7¸ļļ°„ 3“# °G[ŪöôôŌ§mQ 1_gM$%S÷‰WŽ\ Xųu;cÆß ĻøōãRĸF/‰čßČŪx#dÍo-š \Læønי–dô‘,ĸ¨14f RÄ4;#rÎŋ$F/kCN9û˙ģÆeôbXsŦ+ĄŨCŗŠhã'΋ķ _ ą Á̍”ꏷŋŲdí,ŗâÁ=ĘåbkGÄ:ĘJbôâ NÄš`ģXF/ŸĮ~`<„pMg+*ŋMQIF¯žüōK3oŪ<ķŲgŸå—m؞Ŗ}øá‡ųeŋũö›-[ēti~™šFŧúCß îiëī}ŠŽĀķžlWV\Ŗ—X.ŧgp§ĸŒ^ļtĄžˆˇ¸ēúA9Ŗ%v á7“z6z“A%õˆ+*%S÷‰WŽfŦžaõ@–gßąØīį†îˏKžŅK{^ņÛĩ)Ęčeâ‡ķ@ŽhGÔnŋŸzũsã…mšIIFo1F+‚‚á‹ÂZZfî„āliIŒ^Ö7¸ŊrŲŠ(Ŗ—äöŨÅ +ŽÛJ›Žmė1Ŧ=Ņ‚‘^ģŊĩâMŠîđDú‹5 n›¨ûŨh÷Ęcm ߛMĒ]ģ’Ŋ.ĸ.!.œz,Ŗ—änRŒ¨ųëŧ‹“dô !„%#^ũõ°§Ÿē}î6hÚĀÜķø=VO@įbšRĐH-ŽŅ‹~ÁûũŗĘ?íĻ+EŊĖĻ11R\]õ•č“´÷×;ēĖļ5ūqaInĮ ܚũķvÃØ?Ž8)™ēOŧrEÂûīˆĮ Áš4†!eșkįˏKžŅKÂXæšą”ƒˇ(Ŗ—ä&Ίŗe)ŽĪČ-íņ<đû„ėļįʔ$Ŗˇ˜ FGFB×#0ūMã˙rčĀ%ŒZʃ[Z8ʆcGh0Ę0”iÃŪ[Ácƒ ×f„/ZdļÂ#3¸V&œc™}äS\?ūÂRĒ;<ŅūâaBuöāÅf––›L0z2#X\ķ—>ŠĀąė…Gyp{!fÅYË@]wīuļ y(lŗx’Ûŧģ¨Ŋy KĪyAܯ+iJu !„a ũ "%ãŨÅĀ6ŗč BÛá9ÖöļļĮS\ēFÂŨ˜Iļ2Âha‡žíąöšu[]go^&TüuŽ~N†‘ĒÄ䌞~fÛ˙¸â¤dę>‰Č Oö¤ÅcĩÖiĩːéC ´‰&?$<¸Á€fŖBˇE/Åû“ÚøÛ“_ČUqĸbķY~?ø™A!˙¸0'<]ũ­ŖâIɔĢh”ē҆„Đ#ŦÅqIHuJu‡gC }a¨í¯’ėÍ›Ē”ęūB!Â@6č,‹go^Ĩ‚)™ēO6Č{23qWšÁ¨˛!%SŽĸ‘ķFīüMķí ŲFŋŽ4RĒ;<Ķû‹™{FņŲ Ã:ˆT÷—B2]`0KębšŠ*/%S÷Étš"ÎŪįˇ:?ĸNŠd)™rœ5z1žp…āȚ ˇw[i§TwxĻö‰õ?¸T,ėiDԗFJu !„a Sõ–Ėą„1Ö‘ŲoŖT˛”LŨ'SåŠŅDCŽöúû^fÖÚYm”J–’)WŅČYŖ—Äžŧ`r›W‡!ĨēÃ3šŋXķKÄėâDØNWJu !„a Sõâx\ØöBĢķ×n*ş’ŠûdĒ\<(ĪŦg´bRDŊRÉS2å* Ŋ×Oŧ(nĻؐ¸Ž'6ķ/qRš~bSõW’R:úK!„<ī¤?(%[÷‘\)‘’-WŅHØčŊyęefÉĪîƒĨ”XZøÃ|ĶmF˙'•Ž3ZŲĪņ?[Šä‰ëxیÖū%B!˛žwŌ”^˙ašcƕžxÄÍí3ÚJŽ”Ŧ Š$aŖˇī̎˜Y_O‰8yĨ’§§W4-îī_â¤2lņ}ösüĪV*yâ:Ž\ÜĪŋÄB!Dց~"ũAiōę1fôâÁžxÄͨŃĖ3Ģ‹øĨÜJȲJ6z_úøY3xI÷ˆ“W*Yzķ—%v–wŨĻũKœT>ŲôžíåķüsP*~âú1ęŊvĶGū%B!˛ôž{Ōr7Ņ÷Ėō&SWåŊ˜á“\ånĸdĘU46zwüúŖ5Öf~99âK(/ŅŲŊįŪd&.é_Ū”0nųPĶkîēÁę¸n\ŋņˇû—V!„ČZ&,!ũ!G}~˙Ü[ĖŗËŸđÅ"a&/ÜÜ7ˇŗä*}Nß#Š&aŖ˜íęōÜåfÔûšų[įD|!ĨčiÁļšv:Ÿ‘S Ūß˙øŨŋ´)Īyzų0;ãËįsūš)E&×_\7üéę/!„" đÜC_‘ū;Éé>LpMY>*%ēī‰Ņƒ>,šĘ´HI…\ų$Åč…ÍÛŋ1ãŪzØÜ9ŗ 9­\tf*˙ąÅũR>_k6­2#÷ĩį៛rdvũ%—f!„š ĪAž‡Ō˛?ãÎ "$tmpfile" mv "$tmpfile" "$f" done trillian-1.6.1/examples/deployment/docker/log_server/000077500000000000000000000000001466362047600227675ustar00rootroot00000000000000trillian-1.6.1/examples/deployment/docker/log_server/Dockerfile000066400000000000000000000015361466362047600247660ustar00rootroot00000000000000FROM golang:1.23.0-bookworm@sha256:31dc846dd1bcca84d2fa231bcd16c09ff271bcc1a5ae2c48ff10f13b039688f3 as build WORKDIR /trillian ARG GOFLAGS="" ENV GOFLAGS=$GOFLAGS # Download dependencies first - this should be cacheable. COPY go.mod go.sum ./ RUN go mod download # Now add the local Trillian repo, which typically isn't cacheable. COPY . . # Build the server. RUN go install ./cmd/trillian_log_server # Run the licensing tool and save licenses, copyright notices, etc. RUN go run github.com/google/go-licenses/v2 save ./cmd/trillian_log_server --save_path /THIRD_PARTY_NOTICES # Make a minimal image. FROM gcr.io/distroless/base-debian12@sha256:1aae189e3baecbb4044c648d356ddb75025b2ba8d14cdc9c2a19ba784c90bfb9 COPY --from=build /go/bin/trillian_log_server / COPY --from=build /THIRD_PARTY_NOTICES /THIRD_PARTY_NOTICES ENTRYPOINT ["/trillian_log_server"] trillian-1.6.1/examples/deployment/docker/log_signer/000077500000000000000000000000001466362047600227505ustar00rootroot00000000000000trillian-1.6.1/examples/deployment/docker/log_signer/Dockerfile000066400000000000000000000015361466362047600247470ustar00rootroot00000000000000FROM golang:1.23.0-bookworm@sha256:31dc846dd1bcca84d2fa231bcd16c09ff271bcc1a5ae2c48ff10f13b039688f3 as build WORKDIR /trillian ARG GOFLAGS="" ENV GOFLAGS=$GOFLAGS # Download dependencies first - this should be cacheable. COPY go.mod go.sum ./ RUN go mod download # Now add the local Trillian repo, which typically isn't cacheable. COPY . . # Build the signer. RUN go install ./cmd/trillian_log_signer # Run the licensing tool and save licenses, copyright notices, etc. RUN go run github.com/google/go-licenses/v2 save ./cmd/trillian_log_signer --save_path /THIRD_PARTY_NOTICES # Make a minimal image. FROM gcr.io/distroless/base-debian12@sha256:1aae189e3baecbb4044c648d356ddb75025b2ba8d14cdc9c2a19ba784c90bfb9 COPY --from=build /go/bin/trillian_log_signer / COPY --from=build /THIRD_PARTY_NOTICES /THIRD_PARTY_NOTICES ENTRYPOINT ["/trillian_log_signer"] trillian-1.6.1/examples/deployment/kubernetes/000077500000000000000000000000001466362047600215205ustar00rootroot00000000000000trillian-1.6.1/examples/deployment/kubernetes/README.md000066400000000000000000000057031466362047600230040ustar00rootroot00000000000000# Deploying onto Kubernetes in Google Cloud This document guides you through the process of spinning up an example Trillian deployment on Google Cloud using Kubernetes and Cloud Spanner. ## Prerequisites 1. You should have this repo checked out :) 1. A recent [Debian](https://debian.org) based distribution (other platforms may work, but YMMV) 1. You must have the [`jq` binary](https://packages.debian.org/stretch/jq) installed (for command-line manipulation of JSON) 1. You have `gcloud`/`kubectl`/`go`/`Docker` etc. installed (See [Cloud quickstart](https://cloud.google.com/kubernetes-engine/docs/quickstart) docs) 1. You have a Google account with billing configured 1. You may need to request additional Quota for Compute Engine "in-use IP addresses" (need >= 11) [link](https://console.cloud.google.com/iam-admin/quotas?service=compute.googleapis.com&metric=In-use%20IP%20addresses) ## Process 1. Go to [Google Cloud Console](https://console.cloud.google.com) 1. Create a new project 1. Edit the [example-config.sh](example-config.sh) file, set `PROJECT_ID` to the ID of your project 1. Run: `./create.sh example-config.sh`. This script will create the Kubernetes cluster, node pools, and Spanner database, service account and etcd cluster. It should take about 5 to 10 minutes to finish and must complete without error. 1. Now you can deploy the Trillian services. Run: `./deploy.sh example-config.sh` This will build the Trillian Docker images, tag them, and create/update the Kubernetes deployment. 1. To update a running deployment, simply re-run `./deploy.sh example-config.sh` at any time. You should now have a working Trillian Log deployment in Kubernetes. **NOTE: none of the Trillian APIs are exposed to the internet with this config, this is intentional since the only access to Trillian should be via a personality layer.** ## Next steps To do something useful with the deployment, you'll need provision one or more trees into the Trillian log, and run a "personality" layer. To provision a tree into Trillian, use the `provision_tree.sh` script (which uses `kubectl` to forward requests to the Trillian Log's admin API): ```bash ./provision_tree.sh example-config.sh ``` Make a note of the tree ID for the new tree. Next, you may wish to deploy the [Certificate Transparency personality](https://github.com/google/certificate-transparency-go/tree/master/trillian). The CT repo includes Kubernetes [instructions](https://github.com/google/certificate-transparency-go/tree/master/trillian/examples/deployment/kubernetes/README.md) and [deployment configurations](https://github.com/google/certificate-transparency-go/tree/master/trillian/examples/deployment/kubernetes/). ## Known Issues - This deployment is quite tightly coupled to Google Cloud Kubernetes - Only CloudSpanner is supported currently - There is no Trillian Map support currently (because there is no map support in the current CloudSpanner storage implementation). trillian-1.6.1/examples/deployment/kubernetes/create.sh000077500000000000000000000076241466362047600233330ustar00rootroot00000000000000#!/usr/bin/env bash # # This script (optionally) creates and then prepares a Google Cloud project to host a # Trillian instance using Kubernetes. function checkEnv() { if [ -z ${PROJECT_ID+x} ] || [ -z ${CLUSTER_NAME+x} ] || [ -z ${REGION+x} ] || [ -z ${NODE_LOCATIONS+x} ] || [ -z ${MASTER_ZONE+x} ] || [ -z ${CONFIGMAP+x} ] || [ -z ${POOLSIZE+x} ] || [ -z ${MACHINE_TYPE+x} ]; then echo "You must either pass an argument which is a config file, or set all the required environment variables" >&2 exit 1 fi } set -e DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" if [ $# -eq 1 ]; then source $1 else checkEnv fi # Check required binaries are installed if ! gcloud --help > /dev/null; then echo "Need gcloud installed." exit 1 fi if ! kubectl --help > /dev/null; then echo "Need kubectl installed." exit 1 fi if ! jq --help > /dev/null; then echo "Please install the jq command" exit 1 fi if ! envsubst --help > /dev/null; then echo "Please install the envsubt command" exit 1 fi echo "Creating new Trillian deployment" echo " Project name: ${PROJECT_ID}" echo " Cluster name: ${CLUSTER_NAME}" echo " Region: ${REGION}" echo " Node Locs: ${NODE_LOCATIONS}" echo " Config: ${CONFIGMAP}" echo " Pool: ${POOLSIZE} * ${MACHINE_TYPE}" echo # Uncomment this to create a GCE project from scratch, or you can create it # manually through the web UI. # gcloud projects create ${PROJECT_ID} # Connect to gcloud gcloud config set project "${PROJECT_ID}" gcloud config set compute/zone ${MASTER_ZONE} gcloud config set container/cluster "${CLUSTER_NAME}" # Ensure Kubernetes Engine (container) and Cloud Spanner (spanner) services are enabled for SERVICE in container spanner; do gcloud services enable ${SERVICE}.googleapis.com --project=${PROJECT_ID} done # Create cluster # TODO(https://github.com/google/trillian/issues/1183): Add support for priorities and preemption when Kubernetes 1.11 is GA. gcloud beta container clusters create "${CLUSTER_NAME}" --machine-type "${MACHINE_TYPE}" --image-type "COS" --num-nodes "${POOLSIZE}" --enable-autorepair --enable-autoupgrade --node-locations="${NODE_LOCATIONS}" --addons=Istio --istio-config=auth=MTLS_PERMISSIVE gcloud container clusters get-credentials "${CLUSTER_NAME}" # Create spanner instance & DB gcloud spanner instances create trillian-spanner --description "Trillian Spanner instance" --nodes=1 --config="regional-${REGION}" gcloud spanner databases create trillian-db --instance trillian-spanner --ddl="$(cat ${DIR}/../../../storage/cloudspanner/spanner.sdl | grep -v '^--.*$')" # Create service account gcloud iam service-accounts create trillian --display-name "Trillian service account" # Get the service account key and push it into a Kubernetes secret: gcloud iam service-accounts keys create /dev/stdout --iam-account="trillian@${PROJECT_ID}.iam.gserviceaccount.com" | kubectl create secret generic trillian-key --from-file=key.json=/dev/stdin # Update roles for ROLE in spanner.databaseUser logging.logWriter monitoring.metricWriter; do gcloud projects add-iam-policy-binding "${PROJECT_ID}" \ --member "serviceAccount:trillian@${PROJECT_ID}.iam.gserviceaccount.com" \ --role "roles/${ROLE}" done # Bring up etcd cluster # Work-around for etcd-operator role on GKE. COREACCOUNT=$(gcloud config config-helper --format=json | jq -r '.configuration.properties.core.account') kubectl create clusterrolebinding etcd-cluster-admin-binding --clusterrole=cluster-admin --user="${COREACCOUNT}" for f in "etcd-role-binding.yaml" "etcd-role.yaml" "etcd-deployment.yaml"; do envsubst < ${DIR}/${f} | kubectl apply --namespace="${NAMESPACE}" -f - done # Wait for Custom Resource Definitions (CRD) to be installed before creating Etcd cluster kubectl wait --for=condition=Established crd/etcdclusters.etcd.database.coreos.com envsubst < ${DIR}/etcd-cluster.yaml | kubectl --namespace="${NAMESPACE}" apply -f - trillian-1.6.1/examples/deployment/kubernetes/db-deployment.yaml000066400000000000000000000014301466362047600251450ustar00rootroot00000000000000apiVersion: apps/v1 kind: Deployment metadata: annotations: kompose.cmd: kompose convert kompose.version: 1.3.0 (HEAD) creationTimestamp: null labels: io.kompose.service: db name: db spec: replicas: 1 selector: matchLabels: io.kompose.service: db strategy: {} template: metadata: creationTimestamp: null labels: io.kompose.service: db spec: containers: - env: - name: MYSQL_DATABASE value: test - name: MYSQL_PASSWORD value: zaphod - name: MYSQL_RANDOM_ROOT_PASSWORD value: "yes" - name: MYSQL_USER value: test image: gcr.io/${PROJECT_ID}/db_server:latest name: db resources: {} restartPolicy: Always status: {} trillian-1.6.1/examples/deployment/kubernetes/db-service.yaml000066400000000000000000000005331466362047600244300ustar00rootroot00000000000000apiVersion: v1 kind: Service metadata: annotations: kompose.cmd: kompose convert kompose.version: 1.3.0 (HEAD) creationTimestamp: null labels: io.kompose.service: db name: db spec: clusterIP: None ports: - name: headless port: 55555 targetPort: 0 selector: io.kompose.service: db status: loadBalancer: {} trillian-1.6.1/examples/deployment/kubernetes/delete.sh000077500000000000000000000022511466362047600233210ustar00rootroot00000000000000#!/usr/bin/env bash # # This script (optionally) deletes resources in a Google Cloud project to host a Trillian instance using Kubernetes. set -e DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" source ${DIR}/config.sh # Check required binaries are installed if ! gcloud --help > /dev/null; then echo "Need gcloud installed." exit 1 fi if ! kubectl --help > /dev/null; then echo "Need kubectl installed." exit 1 fi if ! jq --help > /dev/null; then echo "Please install the jq command" exit 1 fi # Connect to gcloud gcloud config set project ${PROJECT_ID} gcloud config set compute/zone ${MASTER_ZONE} # Delete cluster & node pools gcloud beta container clusters delete ${CLUSTER_NAME} --quiet # Delete spanner instance & DB(s) gcloud spanner instances delete trillian-spanner --quiet # Delete service account and key(s) gcloud iam service-accounts delete trillian@${PROJECT_ID}.iam.gserviceaccount.com --quiet # Remove roles for ROLE in spanner.databaseUser logging.logWriter monitoring.metricWriter; do gcloud projects remove-iam-policy-binding "${PROJECT_ID}" --member "serviceAccount:trillian@${PROJECT_ID}.iam.gserviceaccount.com" --role "roles/${ROLE}" done trillian-1.6.1/examples/deployment/kubernetes/deploy.sh000077500000000000000000000050111466362047600233500ustar00rootroot00000000000000#!/usr/bin/env bash # Script assumptions: # - Cluster has already been created & configured using the create.sh script # - Go 1.9 is installed function checkEnv() { if [ -z ${PROJECT_ID+x} ] || [ -z ${CLUSTER_NAME+x} ] || [ -z ${MASTER_ZONE+x} ] || [ -z ${CONFIGMAP+x} ]; then echo "You must either pass an argument which is a config file, or set all the required environment variables" >&2 exit 1 fi } set -e DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" if [ $# -eq 1 ]; then source $1 else checkEnv fi # if IMAGE_TAG is unset, we'll create one using the git HEAD hash, with an # optional "-dirty" suffix if any objects have been modified. GIT_HASH=$(git rev-parse HEAD) GIT_DIRTY=$(git diff --quiet || echo '-dirty') export IMAGE_TAG=${IMAGE_TAG:-${GIT_HASH}${GIT_DIRTY}} # Connect to gcloud gcloud --quiet config set project ${PROJECT_ID} gcloud --quiet config set container/cluster ${CLUSTER_NAME} gcloud --quiet config set compute/zone ${MASTER_ZONE} gcloud --quiet container clusters get-credentials ${CLUSTER_NAME} # Configure Docker to use gcloud credentials with Google Container Registry gcloud auth configure-docker # Get Trillian go get github.com/google/trillian/... cd $GOPATH/src/github.com/google/trillian images="log_server log_signer" echo "Building and pushing docker images: ${images}" for thing in ${images}; do echo " - ${thing}" docker build --quiet -f examples/deployment/docker/${thing}/Dockerfile -t gcr.io/$PROJECT_ID/${thing}:$IMAGE_TAG . docker push gcr.io/${PROJECT_ID}/${thing}:${IMAGE_TAG} gcloud --quiet container images add-tag gcr.io/${PROJECT_ID}/${thing}:${IMAGE_TAG} gcr.io/${PROJECT_ID}/${thing}:latest done if [[ "${NAMESPACE}" != "default" ]]; then # Create the namespace if it doesn't already exist. kubectl create namespace "${NAMESPACE}" --dry-run -o=yaml | kubectl apply -f - # Enable Istio sidecar injection. kubectl label namespace "${NAMESPACE}" istio-injection=enabled fi echo "Updating jobs..." # Prepare configmap: kubectl delete --namespace="${NAMESPACE}" configmap deploy-config || true envsubst < ${CONFIGMAP} | kubectl create --namespace="${NAMESPACE}" -f - # Launch with kubernetes kubeconfigs="trillian-log-deployment.yaml trillian-log-service.yaml trillian-log-signer-deployment.yaml trillian-log-signer-service.yaml" for thing in ${kubeconfigs}; do echo ${thing} envsubst < ${DIR}/${thing} | kubectl apply --namespace="${NAMESPACE}" -f - done kubectl get all --namespace="${NAMESPACE}" kubectl get services --namespace="${NAMESPACE}" trillian-1.6.1/examples/deployment/kubernetes/etcd-cluster.yaml000066400000000000000000000016271466362047600250100ustar00rootroot00000000000000apiVersion: "etcd.database.coreos.com/v1beta2" kind: "EtcdCluster" metadata: name: "trillian-etcd-cluster" annotations: etcd.database.coreos.com/scope: clusterwide spec: size: 5 version: "3.2.13" pod: annotations: # Do not inject an Istio sidecar, because Etcd nodes require a network # connection on startup and don't retry on failure. This is a problem for # Istio because it takes a moment to start its proxy sidecar and, during # that time, all network connections will be blocked. sidecar.istio.io/inject: "false" affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - podAffinityTerm: labelSelector: matchExpressions: - key: etcd_cluster operator: In values: - trillian-etcd-cluster topologyKey: kubernetes.io/hostname trillian-1.6.1/examples/deployment/kubernetes/etcd-deployment.yaml000066400000000000000000000013661466362047600255070ustar00rootroot00000000000000apiVersion: apps/v1 kind: Deployment metadata: name: trillian-etcd-operator # Cluster-wide etcd-operator, so should always be in default namespace. namespace: default spec: replicas: 1 selector: matchLabels: name: trillian-etcd-operator template: metadata: labels: name: trillian-etcd-operator spec: containers: - name: trillian-etcd-operator image: quay.io/coreos/etcd-operator:v0.9.4 command: - etcd-operator - -cluster-wide env: - name: MY_POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace - name: MY_POD_NAME valueFrom: fieldRef: fieldPath: metadata.name trillian-1.6.1/examples/deployment/kubernetes/etcd-role-binding.yaml000066400000000000000000000004061466362047600256720ustar00rootroot00000000000000apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRoleBinding metadata: name: etcd-operator roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: etcd-operator subjects: - kind: ServiceAccount name: default namespace: default trillian-1.6.1/examples/deployment/kubernetes/etcd-role.yaml000066400000000000000000000012131466362047600242570ustar00rootroot00000000000000apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRole metadata: name: etcd-operator rules: - apiGroups: - etcd.database.coreos.com resources: - etcdclusters - etcdbackups - etcdrestores verbs: - "*" - apiGroups: - apiextensions.k8s.io resources: - customresourcedefinitions verbs: - "*" - apiGroups: - "" resources: - pods - services - endpoints - persistentvolumeclaims - events verbs: - "*" - apiGroups: - apps resources: - deployments verbs: - "*" # The following permissions can be removed if not using S3 backup and TLS - apiGroups: - "" resources: - secrets verbs: - get trillian-1.6.1/examples/deployment/kubernetes/example-config.sh000077500000000000000000000011131466362047600247510ustar00rootroot00000000000000DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" # Set the following variables to the correct values for your Google Cloud # project and Kubernetes cluster. export PROJECT_ID="GOOGLE CLOUD PROJECT ID HERE (https://cloud.google.com/resource-manager/docs/creating-managing-projects#identifying_projects)" export CLUSTER_NAME=trillian export REGION=us-east4 export MASTER_ZONE="${REGION}-a" export NODE_LOCATIONS="${REGION}-a,${REGION}-b,${REGION}-c" export CONFIGMAP=${DIR}/trillian-cloudspanner.yaml export NAMESPACE=default export POOLSIZE=2 export MACHINE_TYPE="n1-standard-2" trillian-1.6.1/examples/deployment/kubernetes/mysql/000077500000000000000000000000001466362047600226655ustar00rootroot00000000000000trillian-1.6.1/examples/deployment/kubernetes/mysql/README.md000066400000000000000000000042231466362047600241450ustar00rootroot00000000000000:exclamation: **EXPERIMENTAL** :exclamation: # MySQL on Kubernetes ## Usage To run a Galera MySQL cluster on Google Cloud, install the [Cloud SDK](https://cloud.google.com/sdk/) and configure it for your project. [Provision a Container cluster](https://cloud.google.com/container-engine/docs/clusters/operations), then run the following command: ```shell kubectl apply -f $GOPATH/src/github.com/google/trillian/storage/mysql/kubernetes ``` This will start the Galera cluster. You can monitor provisoning of this cluster by visiting http://127.0.0.1:8001/ui/ after running: ```shell kubectl proxy ``` This dashboard will also show the external IP of the cluster on the "Services" page, on the row for the "mysql" service. Once the cluster has been provisioned, prepare the database for use by Trillian by running: ```shell $GOPATH/src/github.com/google/trillian/storage/mysql/kubernetes/resetdb.sh ``` ### Firewall By default, the load balancer that exposes the MySQL service will only accept connections from 10.0.0.0/8. To allow connections from a wider range of IP addresses, change the CIDR IP ranges specified under the `loadBalancerSourceRanges` field in galera.yaml, then run `kubectl apply -f galera.yaml`. ## Derivation Based on [the mysql-galera example from the Kubernetes GitHub repository](https://github.com/kubernetes/kubernetes/tree/v1.5.4/examples/storage/mysql-galera), which is available under [the Apache 2.0 license](https://github.com/kubernetes/kubernetes/blob/v1.5.4/LICENSE). The following modifications have been made: - Increased CPU limit per replica to 2. - Each instance will use a persistent SSD for storage of its database. - The cluster will be managed as a [StatefulSet](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/). - The cluster is accessed via a load balancer, enabling a transparent multi-master setup. - Updated image to use Percona 5.7. - Simplified scripts by removing unnecessary options. - Added some utility scripts: - image/env.sh - image/push.sh - Added readiness probes to the Kubernetes config. - Moved usernames and passwords into [Secrets](https://kubernetes.io/docs/concepts/configuration/secret/). trillian-1.6.1/examples/deployment/kubernetes/mysql/galera.yaml000066400000000000000000000056561466362047600250200ustar00rootroot00000000000000apiVersion: storage.k8s.io/v1beta1 kind: StorageClass metadata: name: fast provisioner: kubernetes.io/gce-pd parameters: type: pd-ssd --- apiVersion: v1 kind: Service metadata: name: mysql labels: app: mysql spec: ports: - port: 3306 name: mysql type: LoadBalancer # Make all connections from a particular client go to the same database # replica. This avoids a sequencer connecting to different replicas in # parallel and causing write conflicts that result in transaction rollbacks. sessionAffinity: ClientIP selector: app: galera # Only connections from 10.0.0.0/8 will be accepted. # To allow connections from elsewhere, replace 10.0.0.0/8 with the appropriate # CIDR IP range(s). To allow connections from any IP address, delete the # loadBalancerSourceRanges field. loadBalancerSourceRanges: - 10.0.0.0/8 --- apiVersion: v1 kind: Service metadata: name: galera labels: app: galera spec: clusterIP: None ports: - port: 3306 name: mysql - port: 4444 name: state-snapshot-transfer - port: 4567 name: replication-traffic - port: 4568 name: incremental-state-transfer selector: app: galera --- apiVersion: v1 kind: Secret metadata: name: mysql-credentials type: Opaque data: # These values must be base64 encoded. sst-username: c3N0 sst-password: c3N0 root-password: emFwaG9k --- apiVersion: apps/v1beta1 kind: StatefulSet metadata: name: galera spec: serviceName: galera replicas: 3 volumeClaimTemplates: - metadata: name: mysql-db annotations: volume.beta.kubernetes.io/storage-class: fast spec: accessModes: - ReadWriteOnce resources: requests: storage: 500Gi template: metadata: labels: app: galera spec: terminationGracePeriodSeconds: 30 containers: - resources: limits: cpu: 2 readinessProbe: exec: command: ["mysql", "-h", "127.0.0.1", "-u", "dummy", "-e", "SELECT 1"] image: us.gcr.io/trillian-test/galera:experimental imagePullPolicy: Always name: mysql ports: - containerPort: 3306 - containerPort: 4444 - containerPort: 4567 - containerPort: 4568 volumeMounts: - name: mysql-db mountPath: /var/lib/mysql env: - name: WSREP_SST_USER valueFrom: secretKeyRef: name: mysql-credentials key: sst-username - name: WSREP_SST_PASSWORD valueFrom: secretKeyRef: name: mysql-credentials key: sst-password - name: MYSQL_ROOT_PASSWORD valueFrom: secretKeyRef: name: mysql-credentials key: root-password trillian-1.6.1/examples/deployment/kubernetes/mysql/image/000077500000000000000000000000001466362047600237475ustar00rootroot00000000000000trillian-1.6.1/examples/deployment/kubernetes/mysql/image/Dockerfile000066400000000000000000000045341466362047600257470ustar00rootroot00000000000000# Original work: Copyright 2016 The Kubernetes Authors. # Modified work: 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. FROM ubuntu:jammy@sha256:adbb90115a21969d2fe6fa7f9af4253e16d45f8d4c1e930182610c4731962658 # Patch the OS-level packages and remove unneeded dependencies. RUN apt-get update \ && apt-get -y upgrade \ && apt-get -y autoremove \ && rm -rf /var/lib/apt/lists/* # add our user and group first to make sure their IDs get assigned # consistently, regardless of whatever dependencies get added RUN groupadd -r mysql && useradd -r -g mysql mysql ENV MYSQL_VERSION 8.0 ENV TERM linux # Install perl. # Install dig, for discovering IP addresses of other Galera nodes. RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y dnsutils perl --no-install-recommends RUN rm -rf /var/lib/apt/lists/* RUN apt-key adv --keyserver keys.gnupg.net --recv-keys 1C4CBDCDCD2EFD2A 9334A25F8507EFA5 RUN echo "deb http://repo.percona.com/apt jammy main" > /etc/apt/sources.list.d/percona.list RUN echo "deb-src http://repo.percona.com/apt jammy main" >> /etc/apt/sources.list.d/percona.list RUN { \ echo percona-server-server-${MYSQL_VERSION} percona-server-server/root_password password ''; \ } | debconf-set-selections \ && apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y percona-xtradb-cluster-client-"${MYSQL_VERSION}" \ percona-xtradb-cluster-common-"${MYSQL_VERSION}" percona-xtradb-cluster-server-"${MYSQL_VERSION}" \ && rm -rf /var/lib/apt/lists/* COPY my.cnf /etc/mysql/my.cnf COPY cluster.cnf /etc/mysql/conf.d/cluster.cnf RUN mkdir -p /var/lib/mysql RUN chown -R mysql:mysql /etc/mysql /var/lib/mysql VOLUME /var/lib/mysql COPY docker-entrypoint.sh /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"] EXPOSE 3306 4444 4567 4568 CMD ["mysqld"] trillian-1.6.1/examples/deployment/kubernetes/mysql/image/cluster.cnf000066400000000000000000000016751466362047600261310ustar00rootroot00000000000000[mysqld] # Path to Galera library wsrep_provider=/usr/lib/galera3/libgalera_smm.so # Cluster connection URL contains IPs of nodes #If no IP is found, this implies that a new cluster needs to be created, #in order to do that you need to bootstrap this node wsrep_cluster_address=gcomm:// # In order for Galera to work correctly binlog format should be ROW binlog_format=ROW # MyISAM storage engine has only experimental support default_storage_engine=InnoDB # Slave thread to use wsrep_slave_threads= 8 wsrep_log_conflicts # This changes how InnoDB autoincrement locks are managed and is a requirement for Galera innodb_autoinc_lock_mode=2 # Node IP address wsrep_node_address=127.0.0.1 # Cluster name wsrep_cluster_name=galera_kubernetes #pxc_strict_mode allowed values: DISABLED,PERMISSIVE,ENFORCING,MASTER pxc_strict_mode=ENFORCING # SST method wsrep_sst_method=xtrabackup-v2 #Authentication for SST method wsrep_sst_auth="sstuser:changethis" trillian-1.6.1/examples/deployment/kubernetes/mysql/image/docker-entrypoint.sh000077500000000000000000000102721466362047600277700ustar00rootroot00000000000000#!/bin/bash # Original work: Copyright 2016 The Kubernetes Authors. # Modified work: 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. # This script does the following: # # 1. If starting the first replica in the cluster, and MySQL has not been # initialized, creates the database according to the following environment # variables: # - $MYSQL_ROOT_PASSWORD # - $WSREP_SST_USER # - $WSREP_SST_PASSWORD # 2. Configures MySQL for the Galera cluster. set -e if [ "${1:0:1}" = '-' ]; then set -- mysqld "$@" fi # The MySQL "datadir", where the databases are stored. readonly DATADIR="/var/lib/mysql" if [ -z "$MYSQL_ROOT_PASSWORD" ]; then echo >&2 'error: MYSQL_ROOT_PASSWORD not set' exit 1 fi if [ -z "$WSREP_SST_USER" -o -z "$WSREP_SST_PASSWORD" ]; then echo >&2 'error: WSREP_SST_USER or WSREP_SST_PASSWORD is not set' exit 1 fi # Make sure that the datadir exists and is owned by the MySQL user and group. mkdir -p "$DATADIR" chown -R mysql:mysql "$DATADIR" # If this is the first node, initialize the mysql database if it does not exist. # This database will be replicated to all other nodes via SST. if [[ "$(hostname)" == *-0 ]]; then if [ ! -d "${DATADIR}/mysql" ]; then mysqld --initialize --user=mysql --datadir "${DATADIR}" --ignore-db-dir "lost+found" fi fi # This SQL script will be run when the server starts up. INIT_SQL=$(mktemp) chmod 0600 "${INIT_SQL}" # Create/alter the following users: # - root user for administrative purposes. # - dummy user with no password or rights, for use by health checks. # - SST user for use by Galera to replicate database state between nodes. # TODO(robpercival): Restrict root access. cat > "$INIT_SQL" </dev/stderr; exit 1 ;; :) echo "Option -$OPTARG requires an argument." >/dev/stderr; exit 1 ;; esac done gcloud auth configure-docker docker build -t ${tag} . docker push ${tag} trillian-1.6.1/examples/deployment/kubernetes/mysql/resetdb.sh000077500000000000000000000012561466362047600246600ustar00rootroot00000000000000#!/bin/bash -e readonly TRILLIAN_PATH=$(go list -f '{{.Dir}}' github.com/google/trillian) # Pick a random port between 2000 - 34767. Will fail if unlucky and the port is in use. readonly port=$((${RANDOM} + 2000)) kubectl port-forward "galera-0" "${port}:3306" >/dev/null & proxy_pid=$! options_file=$(mktemp) chmod 0600 "${options_file}" echo > ${options_file} </dev/null trillian-1.6.1/examples/deployment/kubernetes/provision_tree.sh000077500000000000000000000013011466362047600251210ustar00rootroot00000000000000#!/usr/bin/env bash # Script assumptions: # - Cluster has already been created, configured, and deployed using the # create.sh and deploy.sh scripts. set -e DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" if [ $# -ne 1 ]; then echo "Usage: $0 " exit 1 fi # Set up tunnel: PORT=35791 export LOG_RPC="http://127.0.0.1:${PORT}" echo kubectl port-forward service/trillian-log-service ${PORT}:8090 kubectl port-forward service/trillian-log-service ${PORT}:8090 & trap "kill %1" 0 1 2 3 4 6 9 15 sleep 3 echo "Creating tree..." TREEID=$(go run github.com/google/trillian/cmd/createtree --admin_server=${LOG_RPC}) echo "Created tree ${TREEID}:" # stop port-forwarding kill %1 trillian-1.6.1/examples/deployment/kubernetes/trillian-ci-mysql.yaml000066400000000000000000000005341466362047600257600ustar00rootroot00000000000000apiVersion: v1 kind: ConfigMap metadata: name: deploy-config data: STORAGE_SYSTEM: mysql GOOGLE_APPLICATION_CREDENTIALS: /var/secrets/google/key.json SIGNER_BATCH_SIZE: "--batch_size=500" SIGNER_INTERVAL: "--sequencer_interval=20ms" SIGNER_NUM_SEQUENCERS: "--num_sequencers=10" SIGNER_MASTER_HOLD_JITTER: "--master_hold_jitter=7200s" trillian-1.6.1/examples/deployment/kubernetes/trillian-ci-spanner-config.sh000077500000000000000000000005741466362047600272030ustar00rootroot00000000000000DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" export NAMESPACE=default export PROJECT_ID=trillian-opensource-ci export CLUSTER_NAME=trillian-opensource-ci export REGION=us-central1 export MASTER_ZONE="${REGION}-a" export NODE_LOCATIONS="${REGION}-a,${REGION}-c" export CONFIGMAP=${DIR}/trillian-cloudspanner.yaml export POOLSIZE=10 export MACHINE_TYPE="n1-standard-4" trillian-1.6.1/examples/deployment/kubernetes/trillian-ci-spanner.yaml000066400000000000000000000012211466362047600262530ustar00rootroot00000000000000apiVersion: v1 kind: Secret metadata: name: trillian-secrets type: Opaque stringData: STORAGE_FLAG: --cloudspanner_uri=projects/${PROJECT_ID}/instances/trillian-opensource-ci/databases/trillian-opensource-ci-db --- apiVersion: v1 kind: ConfigMap metadata: name: deploy-config data: STORAGE_SYSTEM: cloud_spanner GOOGLE_APPLICATION_CREDENTIALS: /var/secrets/google/key.json SIGNER_DEQUEUE_BUCKET_FRACTION: "--cloudspanner_dequeue_bucket_fraction=0.0078" SIGNER_BATCH_SIZE: "--batch_size=1800" SIGNER_INTERVAL: "--sequencer_interval=2ms" SIGNER_NUM_SEQUENCERS: "--num_sequencers=10" SIGNER_MASTER_HOLD_JITTER: "--master_hold_jitter=7200s" trillian-1.6.1/examples/deployment/kubernetes/trillian-cloudspanner.yaml000066400000000000000000000010161466362047600267130ustar00rootroot00000000000000apiVersion: v1 kind: Secret metadata: name: trillian-secrets type: Opaque stringData: STORAGE_FLAG: --cloudspanner_uri=projects/${PROJECT_ID}/instances/trillian-spanner/databases/trillian-db --- apiVersion: v1 kind: ConfigMap metadata: name: deploy-config data: STORAGE_SYSTEM: cloud_spanner GOOGLE_APPLICATION_CREDENTIALS: /var/secrets/google/key.json SIGNER_DEQUEUE_BUCKET_FRACTION: "0.0078" SIGNER_BATCH_SIZE: "1800" SIGNER_INTERVAL: "2ms" SIGNER_NUM_SEQUENCERS: "10" SIGNER_MASTER_HOLD_JITTER: "7200s" trillian-1.6.1/examples/deployment/kubernetes/trillian-ct-log-config.sh000077500000000000000000000005261466362047600263260ustar00rootroot00000000000000DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" export PROJECT_ID=trillian-ct-log export CLUSTER_NAME=trillian export REGION=us-east4 export MASTER_ZONE="${REGION}-a" export NODE_LOCATIONS="${REGION}-a,${REGION}-b,${REGION}-c" export CONFIGMAP=${DIR}/trillian-cloudspanner.yaml export POOLSIZE=2 export MACHINE_TYPE="n1-standard-2" trillian-1.6.1/examples/deployment/kubernetes/trillian-log-deployment.yaml000066400000000000000000000050141466362047600271570ustar00rootroot00000000000000apiVersion: apps/v1 kind: Deployment metadata: labels: io.kompose.service: trillian-log name: trillian-logserver-deployment spec: replicas: 4 selector: matchLabels: io.kompose.service: trillian-log strategy: {} template: metadata: labels: io.kompose.service: trillian-log spec: volumes: - name: google-cloud-key secret: secretName: trillian-key restartPolicy: Always containers: - name: trillian-logserver # TODO(RJPercival): Pass STORAGE_FLAG via --config to protect any # sensitive contents (e.g. passwords) from being seen in process list. args: [ "$(STORAGE_FLAG)", "--storage_system=$(STORAGE_SYSTEM)", "--quota_system=etcd", "--etcd_servers=trillian-etcd-cluster-client:2379", "--etcd_http_service=trillian-logserver-http", "--rpc_endpoint=0.0.0.0:8090", "--http_endpoint=0.0.0.0:8091", "--tracing", "--alsologtostderr" ] envFrom: - configMapRef: name: deploy-config - secretRef: name: trillian-secrets image: gcr.io/${PROJECT_ID}/log_server:${IMAGE_TAG} imagePullPolicy: Always resources: limits: cpu: "1.0" requests: cpu: "0.4" livenessProbe: httpGet: path: /healthz port: 8091 failureThreshold: 3 periodSeconds: 30 timeoutSeconds: 5 ports: - containerPort: 8090 name: grpc - containerPort: 8091 name: http-metrics volumeMounts: - name: google-cloud-key mountPath: /var/secrets/google - name: prometheus-to-sd image: gcr.io/google-containers/prometheus-to-sd:v0.5.2 ports: - name: profiler containerPort: 6060 command: - /monitor - --stackdriver-prefix=custom.googleapis.com - --source=logserver:http://localhost:8091/metrics - --pod-id=$(POD_NAME) - --namespace-id=$(POD_NAMESPACE) - --scrape-interval=5s - --export-interval=60s resources: limits: cpu: 20m requests: cpu: 20m env: - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace trillian-1.6.1/examples/deployment/kubernetes/trillian-log-service.yaml000066400000000000000000000006031466362047600264360ustar00rootroot00000000000000apiVersion: v1 kind: Service metadata: labels: io.kompose.service: trillian-log-service name: trillian-log-service annotations: cloud.google.com/load-balancer-type: "Internal" spec: type: LoadBalancer ports: - name: grpc port: 8090 targetPort: 8090 - name: http-metrics port: 8091 targetPort: 8091 selector: io.kompose.service: trillian-log trillian-1.6.1/examples/deployment/kubernetes/trillian-log-signer-deployment.yaml000066400000000000000000000051731466362047600304520ustar00rootroot00000000000000apiVersion: apps/v1 kind: Deployment metadata: labels: io.kompose.service: trillian-log-signer name: trillian-logsigner-deployment spec: replicas: 2 selector: matchLabels: io.kompose.service: trillian-log-signer strategy: {} template: metadata: creationTimestamp: null labels: io.kompose.service: trillian-log-signer spec: volumes: - name: google-cloud-key secret: secretName: trillian-key restartPolicy: Always containers: - name: trillian-log-signer # TODO(RJPercival): Pass STORAGE_FLAG via --config to protect any # sensitive contents (e.g. passwords) from being seen in process list. args: [ "$(STORAGE_FLAG)", "--storage_system=$(STORAGE_SYSTEM)", "--etcd_servers=trillian-etcd-cluster-client:2379", "--quota_system=etcd", "--etcd_http_service=trillian-logsigner-http", "--http_endpoint=0.0.0.0:8091", "--sequencer_guard_window=1s", "$(SIGNER_INTERVAL)", "$(SIGNER_NUM_SEQUENCERS)", "$(SIGNER_BATCH_SIZE)", "$(SIGNER_DEQUEUE_BUCKET_FRACTION)", "$(SIGNER_MASTER_HOLD_JITTER)", "--alsologtostderr" ] envFrom: - configMapRef: name: deploy-config - secretRef: name: trillian-secrets # Update this with the name of your project: image: gcr.io/${PROJECT_ID}/log_signer:${IMAGE_TAG} imagePullPolicy: Always resources: limits: cpu: "1" requests: cpu: "1" livenessProbe: httpGet: path: /healthz port: 8091 failureThreshold: 3 periodSeconds: 30 timeoutSeconds: 5 ports: - containerPort: 8091 name: http-metrics volumeMounts: - name: google-cloud-key mountPath: /var/secrets/google - name: prometheus-to-sd image: gcr.io/google-containers/prometheus-to-sd:v0.5.2 ports: - name: profiler containerPort: 6060 command: - /monitor - --stackdriver-prefix=custom.googleapis.com - --source=logsigner:http://localhost:8091/metrics - --pod-id=$(POD_NAME) - --namespace-id=$(POD_NAMESPACE) - --scrape-interval=5s - --export-interval=60s env: - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace trillian-1.6.1/examples/deployment/kubernetes/trillian-log-signer-service.yaml000066400000000000000000000006601466362047600277260ustar00rootroot00000000000000apiVersion: v1 kind: Service metadata: annotations: kompose.cmd: kompose convert kompose.version: 1.3.0 (HEAD) cloud.google.com/load-balancer-type: "Internal" creationTimestamp: null labels: io.kompose.service: trillian-log-signer name: trillian-log-signer spec: type: LoadBalancer ports: - name: http-metrics port: 8092 targetPort: 8091 selector: io.kompose.service: trillian-log-signer trillian-1.6.1/examples/deployment/kubernetes/trillian-mysql.yaml000066400000000000000000000034011466362047600253630ustar00rootroot00000000000000apiVersion: v1 kind: Secret metadata: name: trillian-secrets type: Opaque stringData: # TODO(RJPercival): Pass this flag via --config to protect the password from being seen by `ps` STORAGE_FLAG: --mysql_uri=${MYSQL_USER}:${MYSQL_PASSWORD}@tcp(trillian-mysql:3306)/${MYSQL_DATABASE} --- apiVersion: v1 kind: Secret metadata: name: trillian-mysql-secrets labels: app: mysql type: Opaque stringData: MYSQL_ROOT_PASSWORD: "${MYSQL_ROOT_PASSWORD}" MYSQL_USER: "${MYSQL_USER}" MYSQL_PASSWORD: "${MYSQL_PASSWORD}" MYSQL_DATABASE: "${MYSQL_DATABASE}" --- apiVersion: v1 kind: Service metadata: name: trillian-mysql labels: app: mysql spec: ports: - name: mysql port: 3306 selector: app: mysql --- apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: fast provisioner: kubernetes.io/gce-pd parameters: type: pd-ssd --- apiVersion: apps/v1 kind: StatefulSet metadata: name: trillian-mysql spec: selector: matchLabels: app: mysql serviceName: trillian-mysql replicas: 1 template: metadata: labels: app: mysql spec: containers: - name: mysql image: gcr.io/${PROJECT_ID}/db_server:${IMAGE_TAG} envFrom: - secretRef: name: trillian-mysql-secrets ports: - name: mysql containerPort: 3306 volumeMounts: - name: data mountPath: /var/lib/mysql subPath: mysql resources: requests: cpu: 4 memory: 48Gi # TODO(RJPercival): Add livenessProbe/readinessProbe volumeClaimTemplates: - metadata: name: data spec: storageClassName: fast accessModes: - ReadWriteOnce resources: requests: storage: 500Gi trillian-1.6.1/examples/deployment/kubernetes/undeploy.sh000077500000000000000000000014551466362047600237230ustar00rootroot00000000000000#!/usr/bin/env bash # Script assumptions: # - Cluster has already been created & configured using the create.sh script # - Go 1.10 is installed set -e DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" source ${DIR}/config.sh export TAG=$(git rev-parse HEAD) cd $GOPATH/src/github.com/google/trillian envsubst < examples/deployment/kubernetes/trillian-cloudspanner.yaml | kubectl delete -f - # Delete log-[service|signer] envsubst < examples/deployment/kubernetes/trillian-log-deployment.yaml | kubectl delete -f - envsubst < examples/deployment/kubernetes/trillian-log-service.yaml | kubectl delete -f - envsubst < examples/deployment/kubernetes/trillian-log-signer-deployment.yaml | kubectl delete -f - envsubst < examples/deployment/kubernetes/trillian-log-signer-service.yaml | kubectl delete -f - trillian-1.6.1/examples/deployment/scripts/000077500000000000000000000000001466362047600210405ustar00rootroot00000000000000trillian-1.6.1/examples/deployment/scripts/download-wait-for-it.sh000077500000000000000000000005201466362047600253430ustar00rootroot00000000000000#!/bin/bash set -e download() { COMMIT=8f52a814ef0cc70820b87fbf888273f3aa7f5a9b URL=https://raw.githubusercontent.com/vishnubob/wait-for-it/${COMMIT}/wait-for-it.sh curl -sO $URL chmod a+x wait-for-it.sh } download sha256sum --check <( echo "c238c56e2a81b3c97375571eb4f58a0e75cdb4cd957f5802f733ac50621e776a wait-for-it.sh" ) trillian-1.6.1/experimental/000077500000000000000000000000001466362047600160505ustar00rootroot00000000000000trillian-1.6.1/experimental/batchmap/000077500000000000000000000000001466362047600176275ustar00rootroot00000000000000trillian-1.6.1/experimental/batchmap/README.md000066400000000000000000000120171466362047600211070ustar00rootroot00000000000000# Experimental Beam Map Generation Generates [Verifiable Maps](../../docs/papers/VerifiableDataStructures.pdf) using [Beam Go](https://beam.apache.org/get-started/quickstart-go/). Generating a map in batch scales better than incremental for large numbers of key/values. ## Motivation This experimental map replaces a more interactive DB-backed map which was deleted and tracked by [#2284](https://github.com/google/trillian/issues/2284). Unlike Logs, Maps don't have efficient consistency proofs. Without efficient consistency proofs, clients of the data structure will struggle to keep in sync with the freshest version of it while verify that everything that they had previously relied on in the data structure is still present. One solution proposed to this (e.g. in [Think Global, Act Local](https://arxiv.org/abs/2011.04551)) is to put the roots of the map in a log. Clients will verify their own keyspace within the map *for each revision* and providing their area is correct, and that everyone else has also done this for *their* own keyspaces, then evolution of the map has been verified. If these map revisions are being created quickly, then the number of revisions to be verified can require too much computing power for all but very dedicated clients, and this could easily lead to a situation where verification is not being performed. This observation that verification cost scales linearly with the number of map revisions came along at a similar time to performance problems that were identified with large maps built on top of the interactive DB-backed map. Using a parallelized batch solution solves both of these issues; the temptation to create map revisions every few seconds (or less!) is removed, and massive scale is possible through parallel processing of the subtrees within the map. ## Status > :warning: **This code is experimental!** This code is free to change outside > of semantic versioning in the trillian repository. This batch map has been run at scale to create a map with 2^30 leaves in under 25 minutes. The resulting map is output as tiles, in which the tree is divided from the root in a configurable number of prefix strata. Each strata contains a single byte of the 256-bit path. Tiles are only output if they are non-empty. The design of this `batchmap` is as a library rather than a service. The tiles that are returned must be stored by the client and serving them to users is outside the remit of this library. This may change in the future as common deployments are identified. ## Running the demo ### Building the map The following instructions show how to run this code locally using the Python portable runner. Setting up a Python environment is beyond the scope of this demo, but instructions can be found at [Beam Python Tips](https://cwiki.apache.org/confluence/display/BEAM/Python+Tips). These instructions are for Linux/MacOS but can likely be adapted for Windows. Ensure that the Python runner is running: 1. Check out `github.com/apache/beam` (tested at branch `release-2.24.0`) 2. `cd sdks/python` within that repository 3. `python -m apache_beam.runners.portability.local_job_service_main --port=8099` In another terminal: 1. Check out this repository and `cd` to the folder containing this README 2. `go run ./cmd/build/mapdemo.go --output=/tmp/mapv1 --runner=universal --endpoint=localhost:8099 --environment_type=LOOPBACK` The pipeline should run and generate files under the ouput directory, each of which contains a tile from the map. Note that a new file will be constructed for each tile output, which can get very large if the `key_count` or `prefix_strata` parameters are changed from their default values! If these parameters are set too high, one could run out of inodes on your file system. You have been warned. The demo intends only to show the usage of the API and provide a simple way to test locally running the pipeline. It is not intended to demonstrate where data would be sourced from, or how the output Tiles should be used. See the comments in the demo script for more details. ### Verifying the map This requires a map to have been constructed using the previous instructions. Verifying that a particular key/value is set correctly within the tiles can be done with the command: * `go run cmd/verify/verify.go --logtostderr --map_dir=/tmp/mapv1 --key=5` The `map_dir` must match the directory provided as `output` in the previous stage. The parameters for `value_salt` and `tree_id` must also match those used in the map construction as they are used during the value construction/hashing. If the expected value is committed to correctly by the tiles, then you will see an output line similar to: ``` key 5 found at path 11cd1b2203ad4a3a11ff479d1ee75a59c9f33a73c5f5cf45bda87b656237e9ed, with value '[v1]5' (1e27e661ca57f2231fb41b7ef861ab702ce7412921e4df9eb106db0d8b442227) committed to by map root 4365e3c65742fdfeb60079b677ccf4a264405c0d18fc7db1706690a1b06db73c ``` Setting the `key` parameter to a key outside the range generated in the map will show non-inclusion, as will changing the `tree_id` or `value_salt` parameters. trillian-1.6.1/experimental/batchmap/batchmap.shims.go000066400000000000000000000251561466362047600230700ustar00rootroot00000000000000// Licensed to the Apache Software Foundation (ASF) under one or more // contributor license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright ownership. // The ASF licenses this file to You 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. // Code generated by starcgen. DO NOT EDIT. // File: batchmap.shims.go package batchmap import ( "fmt" "io" "reflect" // Library imports "context" "github.com/apache/beam/sdks/v2/go/pkg/beam/core/runtime" "github.com/apache/beam/sdks/v2/go/pkg/beam/core/runtime/exec" "github.com/apache/beam/sdks/v2/go/pkg/beam/core/runtime/graphx/schema" "github.com/apache/beam/sdks/v2/go/pkg/beam/core/util/reflectx" ) func init() { runtime.RegisterFunction(entryToNodeHashFn) runtime.RegisterFunction(partitionByPrefixLenFn) runtime.RegisterFunction(tileToNodeHashFn) runtime.RegisterType(reflect.TypeOf((*Entry)(nil)).Elem()) schema.RegisterType(reflect.TypeOf((*Entry)(nil)).Elem()) runtime.RegisterType(reflect.TypeOf((*Tile)(nil)).Elem()) schema.RegisterType(reflect.TypeOf((*Tile)(nil)).Elem()) runtime.RegisterType(reflect.TypeOf((*context.Context)(nil)).Elem()) schema.RegisterType(reflect.TypeOf((*context.Context)(nil)).Elem()) runtime.RegisterType(reflect.TypeOf((*leafShardFn)(nil)).Elem()) schema.RegisterType(reflect.TypeOf((*leafShardFn)(nil)).Elem()) runtime.RegisterType(reflect.TypeOf((*nodeHash)(nil)).Elem()) schema.RegisterType(reflect.TypeOf((*nodeHash)(nil)).Elem()) runtime.RegisterType(reflect.TypeOf((*tileHashFn)(nil)).Elem()) schema.RegisterType(reflect.TypeOf((*tileHashFn)(nil)).Elem()) runtime.RegisterType(reflect.TypeOf((*tileUpdateFn)(nil)).Elem()) schema.RegisterType(reflect.TypeOf((*tileUpdateFn)(nil)).Elem()) reflectx.RegisterStructWrapper(reflect.TypeOf((*leafShardFn)(nil)).Elem(), wrapMakerLeafShardFn) reflectx.RegisterStructWrapper(reflect.TypeOf((*tileHashFn)(nil)).Elem(), wrapMakerTileHashFn) reflectx.RegisterStructWrapper(reflect.TypeOf((*tileUpdateFn)(nil)).Elem(), wrapMakerTileUpdateFn) reflectx.RegisterFunc(reflect.TypeOf((*func(context.Context,[]byte,func(*nodeHash) bool) (*Tile,error))(nil)).Elem(), funcMakerContextÛ°ContextSliceOfByteIterNodeHashГᏘTileError) reflectx.RegisterFunc(reflect.TypeOf((*func(context.Context,[]byte,func(**Tile) bool,func(*nodeHash) bool) (*Tile,error))(nil)).Elem(), funcMakerContextÛ°ContextSliceOfByteIterᏘTileIterNodeHashГᏘTileError) reflectx.RegisterFunc(reflect.TypeOf((*func(nodeHash) ([]byte,nodeHash))(nil)).Elem(), funcMakerNodeHashГSliceOfByteNodeHash) reflectx.RegisterFunc(reflect.TypeOf((*func() ())(nil)).Elem(), funcMakerГ) reflectx.RegisterFunc(reflect.TypeOf((*func(*Entry) (nodeHash))(nil)).Elem(), funcMakerᏘEntryГNodeHash) reflectx.RegisterFunc(reflect.TypeOf((*func(*Tile) (int))(nil)).Elem(), funcMakerᏘTileГInt) reflectx.RegisterFunc(reflect.TypeOf((*func(*Tile) (nodeHash))(nil)).Elem(), funcMakerᏘTileГNodeHash) exec.RegisterInput(reflect.TypeOf((*func(*nodeHash) (bool))(nil)).Elem(), iterMakerNodeHash) exec.RegisterInput(reflect.TypeOf((*func(**Tile) (bool))(nil)).Elem(), iterMakerᏘTile) } func wrapMakerLeafShardFn(fn any) map[string]reflectx.Func { dfn := fn.(*leafShardFn) return map[string]reflectx.Func{ "ProcessElement": reflectx.MakeFunc(func(a0 nodeHash) ([]byte, nodeHash) { return dfn.ProcessElement(a0) }), } } func wrapMakerTileHashFn(fn any) map[string]reflectx.Func { dfn := fn.(*tileHashFn) return map[string]reflectx.Func{ "ProcessElement": reflectx.MakeFunc(func(a0 context.Context, a1 []byte, a2 func(*nodeHash) bool) (*Tile, error) { return dfn.ProcessElement(a0, a1, a2) }), "Setup": reflectx.MakeFunc(func() { dfn.Setup() }), } } func wrapMakerTileUpdateFn(fn any) map[string]reflectx.Func { dfn := fn.(*tileUpdateFn) return map[string]reflectx.Func{ "ProcessElement": reflectx.MakeFunc(func(a0 context.Context, a1 []byte, a2 func(**Tile) bool, a3 func(*nodeHash) bool) (*Tile, error) { return dfn.ProcessElement(a0, a1, a2, a3) }), "Setup": reflectx.MakeFunc(func() { dfn.Setup() }), } } type callerContextÛ°ContextSliceOfByteIterNodeHashГᏘTileError struct { fn func(context.Context,[]byte,func(*nodeHash) bool) (*Tile,error) } func funcMakerContextÛ°ContextSliceOfByteIterNodeHashГᏘTileError(fn any) reflectx.Func { f := fn.(func(context.Context,[]byte,func(*nodeHash) bool) (*Tile,error)) return &callerContextÛ°ContextSliceOfByteIterNodeHashГᏘTileError{fn: f} } func (c *callerContextÛ°ContextSliceOfByteIterNodeHashГᏘTileError) Name() string { return reflectx.FunctionName(c.fn) } func (c *callerContextÛ°ContextSliceOfByteIterNodeHashГᏘTileError) Type() reflect.Type { return reflect.TypeOf(c.fn) } func (c *callerContextÛ°ContextSliceOfByteIterNodeHashГᏘTileError) Call(args []any) []any { out0, out1 := c.fn(args[0].(context.Context), args[1].([]byte), args[2].(func(*nodeHash) bool)) return []any{out0, out1} } func (c *callerContextÛ°ContextSliceOfByteIterNodeHashГᏘTileError) Call3x2(arg0, arg1, arg2 any) (any, any) { return c.fn(arg0.(context.Context), arg1.([]byte), arg2.(func(*nodeHash) bool)) } type callerContextÛ°ContextSliceOfByteIterᏘTileIterNodeHashГᏘTileError struct { fn func(context.Context,[]byte,func(**Tile) bool,func(*nodeHash) bool) (*Tile,error) } func funcMakerContextÛ°ContextSliceOfByteIterᏘTileIterNodeHashГᏘTileError(fn any) reflectx.Func { f := fn.(func(context.Context,[]byte,func(**Tile) bool,func(*nodeHash) bool) (*Tile,error)) return &callerContextÛ°ContextSliceOfByteIterᏘTileIterNodeHashГᏘTileError{fn: f} } func (c *callerContextÛ°ContextSliceOfByteIterᏘTileIterNodeHashГᏘTileError) Name() string { return reflectx.FunctionName(c.fn) } func (c *callerContextÛ°ContextSliceOfByteIterᏘTileIterNodeHashГᏘTileError) Type() reflect.Type { return reflect.TypeOf(c.fn) } func (c *callerContextÛ°ContextSliceOfByteIterᏘTileIterNodeHashГᏘTileError) Call(args []any) []any { out0, out1 := c.fn(args[0].(context.Context), args[1].([]byte), args[2].(func(**Tile) bool), args[3].(func(*nodeHash) bool)) return []any{out0, out1} } func (c *callerContextÛ°ContextSliceOfByteIterᏘTileIterNodeHashГᏘTileError) Call4x2(arg0, arg1, arg2, arg3 any) (any, any) { return c.fn(arg0.(context.Context), arg1.([]byte), arg2.(func(**Tile) bool), arg3.(func(*nodeHash) bool)) } type callerNodeHashГSliceOfByteNodeHash struct { fn func(nodeHash) ([]byte,nodeHash) } func funcMakerNodeHashГSliceOfByteNodeHash(fn any) reflectx.Func { f := fn.(func(nodeHash) ([]byte,nodeHash)) return &callerNodeHashГSliceOfByteNodeHash{fn: f} } func (c *callerNodeHashГSliceOfByteNodeHash) Name() string { return reflectx.FunctionName(c.fn) } func (c *callerNodeHashГSliceOfByteNodeHash) Type() reflect.Type { return reflect.TypeOf(c.fn) } func (c *callerNodeHashГSliceOfByteNodeHash) Call(args []any) []any { out0, out1 := c.fn(args[0].(nodeHash)) return []any{out0, out1} } func (c *callerNodeHashГSliceOfByteNodeHash) Call1x2(arg0 any) (any, any) { return c.fn(arg0.(nodeHash)) } type callerГ struct { fn func() () } func funcMakerГ(fn any) reflectx.Func { f := fn.(func() ()) return &callerГ{fn: f} } func (c *callerГ) Name() string { return reflectx.FunctionName(c.fn) } func (c *callerГ) Type() reflect.Type { return reflect.TypeOf(c.fn) } func (c *callerГ) Call(args []any) []any { c.fn() return []any{} } func (c *callerГ) Call0x0() () { c.fn() } type callerᏘEntryГNodeHash struct { fn func(*Entry) (nodeHash) } func funcMakerᏘEntryГNodeHash(fn any) reflectx.Func { f := fn.(func(*Entry) (nodeHash)) return &callerᏘEntryГNodeHash{fn: f} } func (c *callerᏘEntryГNodeHash) Name() string { return reflectx.FunctionName(c.fn) } func (c *callerᏘEntryГNodeHash) Type() reflect.Type { return reflect.TypeOf(c.fn) } func (c *callerᏘEntryГNodeHash) Call(args []any) []any { out0 := c.fn(args[0].(*Entry)) return []any{out0} } func (c *callerᏘEntryГNodeHash) Call1x1(arg0 any) (any) { return c.fn(arg0.(*Entry)) } type callerᏘTileГInt struct { fn func(*Tile) (int) } func funcMakerᏘTileГInt(fn any) reflectx.Func { f := fn.(func(*Tile) (int)) return &callerᏘTileГInt{fn: f} } func (c *callerᏘTileГInt) Name() string { return reflectx.FunctionName(c.fn) } func (c *callerᏘTileГInt) Type() reflect.Type { return reflect.TypeOf(c.fn) } func (c *callerᏘTileГInt) Call(args []any) []any { out0 := c.fn(args[0].(*Tile)) return []any{out0} } func (c *callerᏘTileГInt) Call1x1(arg0 any) (any) { return c.fn(arg0.(*Tile)) } type callerᏘTileГNodeHash struct { fn func(*Tile) (nodeHash) } func funcMakerᏘTileГNodeHash(fn any) reflectx.Func { f := fn.(func(*Tile) (nodeHash)) return &callerᏘTileГNodeHash{fn: f} } func (c *callerᏘTileГNodeHash) Name() string { return reflectx.FunctionName(c.fn) } func (c *callerᏘTileГNodeHash) Type() reflect.Type { return reflect.TypeOf(c.fn) } func (c *callerᏘTileГNodeHash) Call(args []any) []any { out0 := c.fn(args[0].(*Tile)) return []any{out0} } func (c *callerᏘTileГNodeHash) Call1x1(arg0 any) (any) { return c.fn(arg0.(*Tile)) } type iterNative struct { s exec.ReStream fn any // cur is the "current" stream, if any. cur exec.Stream } func (v *iterNative) Init() error { cur, err := v.s.Open() if err != nil { return err } v.cur = cur return nil } func (v *iterNative) Value() any { return v.fn } func (v *iterNative) Reset() error { if err := v.cur.Close(); err != nil { return err } v.cur = nil return nil } func iterMakerNodeHash(s exec.ReStream) exec.ReusableInput { ret := &iterNative{s: s} ret.fn = ret.readNodeHash return ret } func (v *iterNative) readNodeHash(value *nodeHash) bool { elm, err := v.cur.Read() if err != nil { if err == io.EOF { return false } panic(fmt.Sprintf("broken stream: %v", err)) } *value = elm.Elm.(nodeHash) return true } func iterMakerᏘTile(s exec.ReStream) exec.ReusableInput { ret := &iterNative{s: s} ret.fn = ret.readᏘTile return ret } func (v *iterNative) readᏘTile(value **Tile) bool { elm, err := v.cur.Read() if err != nil { if err == io.EOF { return false } panic(fmt.Sprintf("broken stream: %v", err)) } *value = elm.Elm.(*Tile) return true } // DO NOT MODIFY: GENERATED CODE trillian-1.6.1/experimental/batchmap/cmd/000077500000000000000000000000001466362047600203725ustar00rootroot00000000000000trillian-1.6.1/experimental/batchmap/cmd/build/000077500000000000000000000000001466362047600214715ustar00rootroot00000000000000trillian-1.6.1/experimental/batchmap/cmd/build/mapdemo.go000066400000000000000000000122471466362047600234500ustar00rootroot00000000000000// Copyright 2020 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. // mapdemo is a simple example that shows how a verifiable map can be // constructed in Beam. package main import ( "context" "crypto" "encoding/json" "flag" "fmt" "os" "path/filepath" "reflect" "github.com/apache/beam/sdks/v2/go/pkg/beam" "github.com/apache/beam/sdks/v2/go/pkg/beam/io/filesystem/local" "github.com/apache/beam/sdks/v2/go/pkg/beam/x/beamx" "k8s.io/klog/v2" "github.com/google/trillian/experimental/batchmap" "github.com/google/trillian/merkle/coniks" "github.com/google/trillian/merkle/smt/node" ) const hash = crypto.SHA512_256 var ( output = flag.String("output", "", "Output directory in which the tiles will be written.") valueSalt = flag.String("value_salt", "v1", "Some string that will be smooshed in with the generated value before hashing. Allows generated values to be deterministic but variable.") startKey = flag.Int64("start_key", 0, "Keys will be generated starting with this index.") keyCount = flag.Int64("key_count", 1<<5, "The number of keys that will be placed in the map.") treeID = flag.Int64("tree_id", 12345, "The ID of the tree. Used as a salt in hashing.") prefixStrata = flag.Int("prefix_strata", 1, "The number of strata of 8-bit strata before the final strata. 3 is optimal for trees up to 2^30. 10 is required to import into Trillian.") ) func init() { beam.RegisterType(reflect.TypeOf((*mapEntryFn)(nil)).Elem()) beam.RegisterType(reflect.TypeOf((*writeTileFn)(nil)).Elem()) } func main() { klog.InitFlags(nil) flag.Parse() beam.Init() output := filepath.Clean(*output) if output == "" { klog.Exitf("No output provided") } // Create the directory if it doesn't exist if _, err := os.Stat(output); os.IsNotExist(err) { if err = os.Mkdir(output, 0o700); err != nil { klog.Fatalf("couldn't find or create directory %s, %v", output, err) } } p, s := beam.NewPipelineWithRoot() // Get the collection of key/values that are to be committed to by the map. // Here we generate them from scratch, but a real application would likely want to commit to // data read from some data source. entries := beam.ParDo(s, &mapEntryFn{*valueSalt, *treeID}, createRange(s, *startKey, *keyCount)) // Create the map, which will be returned as a collection of Tiles. allTiles, err := batchmap.Create(s, entries, *treeID, hash, *prefixStrata) if err != nil { klog.Fatalf("Failed to create pipeline: %v", err) } // Write this collection of tiles to the output directory. beam.ParDo0(s, &writeTileFn{output}, allTiles) // All of the above constructs the pipeline but doesn't run it. Now we run it. if err := beamx.Run(context.Background(), p); err != nil { klog.Fatalf("Failed to execute job: %v", err) } } // mapEntryFn is a Beam ParDo function that generates a key/value from an int64 input. // In a real application this would be replaced with a function that generated the Entry // objects from some domain objects (e.g. for Certificate Transparency this might take // a Certificate as input and generate an Entry that represents it). type mapEntryFn struct { Salt string TreeID int64 } func (fn *mapEntryFn) ProcessElement(i int64) *batchmap.Entry { h := hash.New() h.Write([]byte(fmt.Sprintf("%d", i))) kbs := h.Sum(nil) leafID := node.NewID(string(kbs), uint(len(kbs)*8)) data := []byte(fmt.Sprintf("[%s]%d", fn.Salt, i)) return &batchmap.Entry{ HashKey: kbs, HashValue: coniks.Default.HashLeaf(fn.TreeID, leafID, data), } } // writeTileFn serializes the tile into the given directory, using the tile // path to determine the file name. // This is reasonable for a demo with a small number of tiles, but with large // maps with multiple revisions, it is conceivable that one could run out of // inodes on the filesystem, and thus using a database locally or storing the // tile data in cloud storage are more likely to scale. type writeTileFn struct { Directory string } func (fn *writeTileFn) ProcessElement(ctx context.Context, t *batchmap.Tile) error { fs := local.New(ctx) w, err := fs.OpenWrite(ctx, fmt.Sprintf("%s/path_%x", fn.Directory, t.Path)) if err != nil { return err } defer func() { if err := w.Close(); err != nil { klog.Errorf("Close(): %v", err) } }() bs, err := json.Marshal(t) if err != nil { return err } _, err = w.Write(bs) return err } // createRange simply generates a PCollection of int64 which is used to seed the demo // pipeline. func createRange(s beam.Scope, start, count int64) beam.PCollection { // TODO(mhutchinson): make this parallel values := make([]int64, count) for i := int64(0); i < count; i++ { values[i] = start + i } return beam.CreateList(s, values) } trillian-1.6.1/experimental/batchmap/cmd/verify/000077500000000000000000000000001466362047600216765ustar00rootroot00000000000000trillian-1.6.1/experimental/batchmap/cmd/verify/verify.go000066400000000000000000000155251466362047600235410ustar00rootroot00000000000000// Copyright 2020 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. // verify is a simple example that shows how a verifiable map can be used to // demonstrate inclusion. package main import ( "bytes" "crypto" "encoding/json" "flag" "fmt" "os" "path/filepath" "github.com/google/trillian/experimental/batchmap" "github.com/google/trillian/merkle/coniks" "github.com/google/trillian/merkle/smt" "github.com/google/trillian/merkle/smt/node" "k8s.io/klog/v2" ) const hash = crypto.SHA512_256 var ( mapDir = flag.String("map_dir", "", "Directory containing map tiles.") treeID = flag.Int64("tree_id", 12345, "The ID of the tree. Used as a salt in hashing.") valueSalt = flag.String("value_salt", "v1", "Some string that will be smooshed in with the generated value before hashing. Allows generated values to be deterministic but variable.") key = flag.Int64("key", 0, "This is the seed for the key that will be looked up.") prefixStrata = flag.Int("prefix_strata", 1, "The number of strata of 8-bit strata before the final strata.") ) func main() { klog.InitFlags(nil) flag.Parse() mapDir := filepath.Clean(*mapDir) if mapDir == "" { klog.Fatal("No output provided") } // Determine the key/value we expect to find. // Note that the map tiles do not contain raw values, but commitments to the values. // If the map needs to return the values to clients then it is recommended that the // map operator uses a Content Addressable Store to store these values. h := hash.New() h.Write([]byte(fmt.Sprintf("%d", *key))) keyPath := h.Sum(nil) leafID := node.NewID(string(keyPath), uint(len(keyPath)*8)) expectedString := fmt.Sprintf("[%s]%d", *valueSalt, *key) expectedValueHash := coniks.Default.HashLeaf(*treeID, leafID, []byte(expectedString)) // Read the tiles required for this check from disk. tiles, err := getTilesForKey(mapDir, keyPath) if err != nil { klog.Exitf("couldn't load tiles: %v", err) } // Perform the verification. // 1) Start at the leaf tile and check the key/value. // 2) Compute the merkle root of the leaf tile // 3) Check the computed root matches that reported in the tile // 4) Check this root value is the key/value of the tile above. // 5) Rinse and repeat until we reach the tree root. et := emptyTree{treeID: *treeID} needPath, needValue := keyPath, expectedValueHash for i := *prefixStrata; i >= 0; i-- { tile := tiles[i] // Check the prefix of what we are looking for matches the tile's path. if got, want := tile.Path, needPath[:len(tile.Path)]; !bytes.Equal(got, want) { klog.Fatalf("wrong tile found at index %d: got %x, want %x", i, got, want) } // Leaf paths within a tile are within the scope of the tile, so we can // drop the prefix from the expected path now we have verified it. needLeafPath := needPath[len(tile.Path):] // Identify the leaf we need, and convert all leaves to the format needed for hashing. var leaf *batchmap.TileLeaf nodes := make([]smt.Node, len(tile.Leaves)) for j, l := range tile.Leaves { if bytes.Equal(l.Path, needLeafPath) { leaf = l } nodes[j] = toNode(tile.Path, l) } // Confirm we found the leaf we needed, and that it had the value we expected. if leaf == nil { //nolint:staticcheck // Remove "related info" lint message for suppressed lint check below. klog.Fatalf("couldn't find expected leaf %x in tile %x", needLeafPath, tile.Path) } if !bytes.Equal(leaf.Hash, needValue) { //nolint:staticcheck // Suppress false +ve due to linter not understanding that klog.Fatal() above will exit if leaf == nil klog.Fatalf("wrong leaf value in tile %x, leaf %x: got %x, want %x", tile.Path, leaf.Path, leaf.Hash, needValue) } // Hash this tile given its leaf values, and confirm that the value we compute // matches the value reported in the tile. hs, err := smt.NewHStar3(nodes, coniks.Default.HashChildren, uint(len(tile.Path)+len(leaf.Path))*8, uint(len(tile.Path))*8) if err != nil { klog.Fatalf("failed to create HStar3 for tile %x: %v", tile.Path, err) } res, err := hs.Update(et) if err != nil { klog.Fatalf("failed to hash tile %x: %v", tile.Path, err) } else if got, want := len(res), 1; got != want { klog.Fatalf("wrong number of roots for tile %x: got %v, want %v", tile.Path, got, want) } if got, want := res[0].Hash, tile.RootHash; !bytes.Equal(got, want) { klog.Fatalf("wrong root hash for tile %x: got %x, calculated %x", tile.Path, want, got) } // Make the next iteration of the loop check that the tile above this has the // root value of this tile stored as the value at the expected leaf index. needPath, needValue = tile.Path, res[0].Hash } // If we get here then we have proved that the value was correct and that the map // root commits to this value. Any other user with the same map root must see the // same value under the same key we have checked. klog.Infof("key %d found at path %x, with value '%s' (%x) committed to by map root %x", *key, keyPath, expectedString, expectedValueHash, needValue) } // getTilesForKey loads the tiles on the path from the root to the given leaf. func getTilesForKey(mapDir string, key []byte) ([]*batchmap.Tile, error) { tiles := make([]*batchmap.Tile, *prefixStrata+1) for i := 0; i <= *prefixStrata; i++ { tilePath := key[0:i] tileFile := fmt.Sprintf("%s/path_%x", mapDir, tilePath) in, err := os.ReadFile(tileFile) if err != nil { return nil, fmt.Errorf("failed to read file %s: %v", tileFile, err) } tile := &batchmap.Tile{} if err := json.Unmarshal(in, tile); err != nil { return nil, fmt.Errorf("failed to parse tile in %s: %v", tileFile, err) } tiles[i] = tile } return tiles, nil } // toNode converts a TileLeaf into the equivalent Node for HStar3. func toNode(prefix []byte, l *batchmap.TileLeaf) smt.Node { // CodeQL mollification: if pLen := len(prefix) + len(l.Path); pLen > 1*1024*1024 { panic(fmt.Sprintf("absurd path length %d", pLen)) } path := make([]byte, 0, len(prefix)+len(l.Path)) path = append(append(path, prefix...), l.Path...) return smt.Node{ ID: node.NewID(string(path), uint(len(path))*8), Hash: l.Hash, } } // emptyTree is a NodeAccessor for an empty tree with the given ID. type emptyTree struct { treeID int64 } func (e emptyTree) Get(id node.ID) ([]byte, error) { return coniks.Default.HashEmpty(e.treeID, id), nil } func (e emptyTree) Set(id node.ID, hash []byte) {} trillian-1.6.1/experimental/batchmap/tile.go000066400000000000000000000042041466362047600211130ustar00rootroot00000000000000// Copyright 2020 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 batchmap // Tile represents a perfect subtree covering the whole height of a stratum // within a sparse map. type Tile struct { // The path from the root of the map to the root of this tile. Path []byte // The computed hash of this subtree. // TODO(mhutchinson): Consider removing this. RootHash []byte // All non-empty leaves in this tile, sorted left-to-right. Leaves []*TileLeaf } // TileLeaf is a leaf value of a Tile. // If it belongs to a leaf tile then this represents one of the values that the // map commits to. Otherwise, this leaf represents the root of the subtree in // the stratum below. type TileLeaf struct { // The path from the root of the container Tile to this leaf. Path []byte // The hash value being committed to. Hash []byte } // Entry is a single key/value to be committed to by the map. type Entry struct { // The key that uniquely identifies this key/value. // These keys must be distributed uniformly and randomly across the key space. HashKey []byte // Hash of the value to be committed to. This will literally be set as a hash // of a TileLeaf in a leaf Tile. // It is the job of the code constructing this Entry to ensure that this has // appropriate preimage protection and domain separation. This means this will // likely be set to something like H(salt || data). // // TODO(mhutchinson): Revisit this. My preference is that this is set to // H(data), and the map synthesis will set the hash to H(salt||H(data)). // This allows the map to always be constructed with good security. HashValue []byte } trillian-1.6.1/experimental/batchmap/tmap.go000066400000000000000000000330041466362047600211170ustar00rootroot00000000000000// Copyright 2020 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 batchmap is a library to be used within Beam pipelines to construct // verifiable data structures. package batchmap //go:generate go install github.com/apache/beam/sdks/v2/go/cmd/starcgen //go:generate starcgen --package=batchmap --identifiers=entryToNodeHashFn,partitionByPrefixLenFn,tileHashFn,leafShardFn,tileToNodeHashFn,tileUpdateFn import ( "context" "crypto" "fmt" "github.com/apache/beam/sdks/v2/go/pkg/beam" "github.com/apache/beam/sdks/v2/go/pkg/beam/register" "github.com/google/trillian/merkle/coniks" "github.com/google/trillian/merkle/smt" "github.com/google/trillian/merkle/smt/node" ) var ( cntTilesHashed = beam.NewCounter("batchmap", "tiles-hashed") cntTilesCopied = beam.NewCounter("batchmap", "tiles-copied") cntTilesCreated = beam.NewCounter("batchmap", "tiles-created") cntTilesUpdated = beam.NewCounter("batchmap", "tiles-updated") ) func init() { register.DoFn1x2[nodeHash, []byte, nodeHash](&leafShardFn{}) register.DoFn3x2[context.Context, []byte, func(*nodeHash) bool, *Tile, error](&tileHashFn{}) register.DoFn4x2[context.Context, []byte, func(**Tile) bool, func(*nodeHash) bool, *Tile, error](&tileUpdateFn{}) register.Function5x1(createStratum) register.Function6x1(updateStratum) register.Function1x2(tilePathFn) } // Create builds a new map from the given PCollection of *Entry. Outputs // the resulting Merkle tree tiles as a PCollection of *Tile. // // The keys in the input PCollection must be 256-bit, uniformly distributed, // and unique within the input. // The values in the input PCollection must be 256-bit. // treeID should be a unique ID for the lifetime of this map. This is used as // part of the hashing algorithm to provide preimage resistance. If the tiles // are to be imported into Trillian for serving, this must match the tree ID // within Trillian. // The internal hash algorithm can be picked between SHA256 and SHA512_256. // The internal nodes will use this algorithm via the CONIKS strategy. // prefixStrata is the number of 8-bit prefix strata. Any path from root to leaf // will have prefixStrata+1 tiles. func Create(s beam.Scope, entries beam.PCollection, treeID int64, hash crypto.Hash, prefixStrata int) (beam.PCollection, error) { s = s.Scope("batchmap.Create") if prefixStrata < 0 || prefixStrata >= 32 { return beam.PCollection{}, fmt.Errorf("prefixStrata must be in [0, 32), got %d", prefixStrata) } // Construct the map pipeline starting with the leaf tiles. nodeHashes := beam.ParDo(s, entryToNodeHashFn, entries) lastStratum := createStratum(s, nodeHashes, treeID, hash, prefixStrata) allTiles := make([]beam.PCollection, 0, prefixStrata+1) allTiles = append(allTiles, lastStratum) for d := prefixStrata - 1; d >= 0; d-- { nodeHashes = beam.ParDo(s, tileToNodeHashFn, lastStratum) lastStratum = createStratum(s, nodeHashes, treeID, hash, d) allTiles = append(allTiles, lastStratum) } // Collate all of the strata together and return them. return beam.Flatten(s, allTiles...), nil } // Update takes an existing base map (PCollection of *Tile), applies the // delta (PCollection of *Entry) and returns the resulting map as a // PCollection of *Tile. // The deltas can add new keys to the map or overwrite existing keys. Keys // cannot be deleted (though their value can be set to a sentinel value). // // treeID, hash, and prefixStrata must match the values passed into the // original call to Create that started the base map. func Update(s beam.Scope, base, delta beam.PCollection, treeID int64, hash crypto.Hash, prefixStrata int) (beam.PCollection, error) { s = s.Scope("batchmap.Update") if prefixStrata < 0 || prefixStrata >= 32 { return beam.PCollection{}, fmt.Errorf("prefixStrata must be in [0, 32), got %d", prefixStrata) } // Tile sets returned from this library have tiles present at all byte // lengths from [0..prefixStrata]. This makes this a perfect partition fn. baseStrata := beam.Partition(s, prefixStrata+1, partitionByPrefixLenFn, base) // Construct the map pipeline starting with the leaf tiles. nodeHashes := beam.ParDo(s, entryToNodeHashFn, delta) lastStratum := updateStratum(s, baseStrata[prefixStrata], nodeHashes, treeID, hash, prefixStrata) allTiles := make([]beam.PCollection, 0, prefixStrata+1) allTiles = append(allTiles, lastStratum) for d := prefixStrata - 1; d >= 0; d-- { nodeHashes = beam.ParDo(s, tileToNodeHashFn, lastStratum) lastStratum = updateStratum(s, baseStrata[d], nodeHashes, treeID, hash, d) allTiles = append(allTiles, lastStratum) } // Collate all of the strata together and return them. return beam.Flatten(s, allTiles...), nil } // createStratum creates the tiles for the stratum at the given rootDepth bytes. // leaves is a PCollection of nodeHash that are the leaves of this layer. // output is a PCollection of *Tile. func createStratum(s beam.Scope, leaves beam.PCollection, treeID int64, hash crypto.Hash, rootDepth int) beam.PCollection { s = s.Scope(fmt.Sprintf("createStratum-%d", rootDepth)) shardedLeaves := beam.ParDo(s, &leafShardFn{RootDepthBytes: rootDepth}, leaves) return beam.ParDo(s, &tileHashFn{TreeID: treeID, Hash: hash}, beam.GroupByKey(s, shardedLeaves)) } // updateStratum updates the tiles for the stratum at the given bytes depth. // base is a PCollection of *Tile which is the tiles in the stratum // to be updated. // deltas is a PCollection of nodeHash that are the updated leaves of this layer. // output is a PCollection of *Tile. func updateStratum(s beam.Scope, base, deltas beam.PCollection, treeID int64, hash crypto.Hash, rootDepth int) beam.PCollection { s = s.Scope(fmt.Sprintf("updateStratum-%d", rootDepth)) shardedBase := beam.ParDo(s, tilePathFn, base) shardedDelta := beam.ParDo(s, &leafShardFn{RootDepthBytes: rootDepth}, deltas) return beam.ParDo(s, &tileUpdateFn{TreeID: treeID, Hash: hash}, beam.CoGroupByKey(s, shardedBase, shardedDelta)) } func tilePathFn(t *Tile) ([]byte, *Tile) { return t.Path, t } // nodeHash describes a leaf to be included in a tile. // This is logically the same as smt.Node however it has public fields so is // serializable by the default Beam coder. Also, it allows changes to be made // to smt.Node without affecting this, which improves decoupling. type nodeHash struct { // Path from root of the map to this node. Equivalent to node.ID, but with // the significant benefit that it will be serialized properly without // writing a custom coder for nodeHash. Path []byte Hash []byte } func partitionByPrefixLenFn(t *Tile) int { return len(t.Path) } func tileToNodeHashFn(t *Tile) nodeHash { return nodeHash{Path: t.Path, Hash: t.RootHash} } func entryToNodeHashFn(e *Entry) nodeHash { return nodeHash{Path: e.HashKey, Hash: e.HashValue} } // leafShardFn groups nodeHashs together based on the first RootDepthBytes // bytes of their path. This groups all leaves from the same tile together. type leafShardFn struct { RootDepthBytes int } func (fn *leafShardFn) ProcessElement(leaf nodeHash) ([]byte, nodeHash) { return leaf.Path[:fn.RootDepthBytes], leaf } type tileHashFn struct { TreeID int64 Hash crypto.Hash th *tileHasher } func (fn *tileHashFn) Setup() { fn.th = &tileHasher{fn.TreeID, coniks.New(fn.Hash)} } func (fn *tileHashFn) ProcessElement(ctx context.Context, rootPath []byte, leaves func(*nodeHash) bool) (*Tile, error) { nodes, err := convertNodes(leaves) if err != nil { return nil, err } cntTilesHashed.Inc(ctx, 1) return fn.th.construct(rootPath, nodes) } // convertNodes consumes the Beam-style iterator of nodeHash and returns the // corresponding slice of smt.Node. Nothing clever is attempted to ensure that // the data structure will fit in memory. If the iterator has too many elements // then this will cause an out of memory panic. It is up to the library client // to configure the map with an appropriate number of prefix strata such that // this does not occur. func convertNodes(leaves func(*nodeHash) bool) ([]smt.Node, error) { nodes := []smt.Node{} var leaf nodeHash for leaves(&leaf) { lid, err := nodeID2Decode(leaf.Path) if err != nil { return nil, fmt.Errorf("failed to decode leaf ID: %v", err) } nodes = append(nodes, smt.Node{ID: lid, Hash: leaf.Hash}) } return nodes, nil } // tileUpdateFn merges the base tile from the original map with the deltas that // represent the changes to the map. Note this only supports additions or // overwrites. There is no ability to delete a leaf. type tileUpdateFn struct { TreeID int64 Hash crypto.Hash th *tileHasher } func (fn *tileUpdateFn) Setup() { fn.th = &tileHasher{fn.TreeID, coniks.New(fn.Hash)} } func (fn *tileUpdateFn) ProcessElement(ctx context.Context, rootPath []byte, bases func(**Tile) bool, deltas func(*nodeHash) bool) (*Tile, error) { base, err := getOptionalTile(bases) if err != nil { return nil, fmt.Errorf("failed precondition getOptionalTile at %x: %v", rootPath, err) } nodes, err := convertNodes(deltas) if err != nil { return nil, err } if len(nodes) == 0 { // If there are no deltas, then the base tile is unchanged. cntTilesCopied.Inc(ctx, 1) return base, nil } if base == nil { cntTilesCreated.Inc(ctx, 1) return fn.th.construct(rootPath, nodes) } cntTilesUpdated.Inc(ctx, 1) return fn.updateTile(rootPath, base, nodes) } func (fn *tileUpdateFn) updateTile(rootPath []byte, base *Tile, deltas []smt.Node) (*Tile, error) { baseNodes := make([]smt.Node, 0, len(base.Leaves)) for _, l := range base.Leaves { leafPath := append(rootPath, l.Path...) lidx, err := nodeID2Decode(leafPath) if err != nil { return nil, fmt.Errorf("failed to decode leaf ID: %v", err) } baseNodes = append(baseNodes, smt.Node{ID: lidx, Hash: l.Hash}) } return fn.th.update(rootPath, baseNodes, deltas) } // tileHasher is an smt.NodeAccessor used for computing node hashes of a tile. // This is not serializable and must be constructed within each worker stage. type tileHasher struct { treeID int64 h *coniks.Hasher } func (th *tileHasher) construct(rootPath []byte, nodes []smt.Node) (*Tile, error) { rootDepthBytes := len(rootPath) if err := smt.Prepare(nodes, nodes[0].ID.BitLen()); err != nil { return nil, fmt.Errorf("smt.Prepare: %v", err) } // N.B. This needs to be done after Prepare but BEFORE HStar3 because it // fiddles around with the nodes and makes their IDs invalid afterwards. tls := make([]*TileLeaf, len(nodes)) for i, n := range nodes { nPath, err := nodeID2Encode(n.ID) if err != nil { return nil, fmt.Errorf("failed to encode leaf ID: %v", err) } tls[i] = &TileLeaf{ Path: nPath[rootDepthBytes:], Hash: n.Hash, } } rootHash, err := th.hashTile(uint(8*rootDepthBytes), nodes) if err != nil { return nil, fmt.Errorf("failed to hash tile: %v", err) } return &Tile{ Path: rootPath, Leaves: tls, RootHash: rootHash, }, nil } func (th *tileHasher) update(rootPath []byte, baseNodes, deltaNodes []smt.Node) (*Tile, error) { // We add new values first and then update with base to easily check for duplicates in deltas. m := make(map[node.ID]smt.Node) for _, leaf := range deltaNodes { if v, found := m[leaf.ID]; found { return nil, fmt.Errorf("found duplicate values at leaf tile position %s: {%x, %x}", leaf.ID, v.Hash, leaf.Hash) } m[leaf.ID] = leaf } for _, leaf := range baseNodes { if _, found := m[leaf.ID]; !found { // Only add base values if they haven't been updated. m[leaf.ID] = leaf } } nodes := make([]smt.Node, 0, len(m)) for _, v := range m { nodes = append(nodes, v) } return th.construct(rootPath, nodes) } // hashTile computes the root hash of the root given the prepared leaves. // The leaves slice MUST NOT be used after calling this method. func (th *tileHasher) hashTile(depthBits uint, leaves []smt.Node) ([]byte, error) { h, err := smt.NewHStar3(leaves, th.h.HashChildren, uint(leaves[0].ID.BitLen()), depthBits) if err != nil { return nil, err } r, err := h.Update(th) if err != nil { return nil, err } if len(r) != 1 { return nil, fmt.Errorf("expected single root but got %d", len(r)) } return r[0].Hash, nil } // Get returns hash of an empty subtree for the given root node ID. func (th tileHasher) Get(id node.ID) ([]byte, error) { return th.h.HashEmpty(th.treeID, id), nil } func (th tileHasher) Set(id node.ID, hash []byte) {} func nodeID2Encode(n node.ID) ([]byte, error) { b, c := n.LastByte() if c == 0 { return []byte{}, nil } if c == 8 { return append([]byte(n.FullBytes()), b), nil } return nil, fmt.Errorf("node ID bit length is not aligned to bytes: %d", n.BitLen()) } func nodeID2Decode(bs []byte) (node.ID, error) { return node.NewID(string(bs), 8*uint(len(bs))), nil } // getOptionalTile consumes the Beam-style iterator and returns: // - nil if there were no entries // - the single tile if there was only one entry // - an error if there were multiple entries func getOptionalTile(iter func(**Tile) bool) (*Tile, error) { var t1, t2 *Tile if !iter(&t1) || !iter(&t2) { // Only at most one entry is found. return t1, nil // Note: Returns nil if found nothing. } return nil, fmt.Errorf("unexpectedly found multiple tiles at %x", t1.Path) } trillian-1.6.1/experimental/batchmap/tmap_test.go000066400000000000000000000257641466362047600221740ustar00rootroot00000000000000//go:build !race // +build !race // Copyright 2020 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 batchmap import ( "crypto" "fmt" "math/rand" "testing" "github.com/apache/beam/sdks/v2/go/pkg/beam" "github.com/apache/beam/sdks/v2/go/pkg/beam/register" "github.com/apache/beam/sdks/v2/go/pkg/beam/testing/passert" "github.com/apache/beam/sdks/v2/go/pkg/beam/testing/ptest" "github.com/apache/beam/sdks/v2/go/pkg/beam/transforms/filter" "github.com/apache/beam/sdks/v2/go/pkg/beam/transforms/stats" ) const hash = crypto.SHA512_256 func init() { register.Function1x1(countTilesFn) register.Function1x1(testFilterRootOnlyFn) register.Function1x1(testFnTileRootHash) register.Function1x1(testLeavesSortedFn) } func testFnTileRootHash(t *Tile) string { return fmt.Sprintf("%x", t.RootHash) } func TestMain(m *testing.M) { ptest.Main(m) } func TestCreate(t *testing.T) { tests := []struct { name string prefixStrata int entries []*Entry treeID int64 hash crypto.Hash wantRoot string wantTileCount int wantFailConstruct bool wantFailRun bool }{ { name: "single entry in one tile", prefixStrata: 0, entries: []*Entry{createEntry("ak", "av")}, treeID: 12345, hash: crypto.SHA512_256, wantRoot: "af079c268bd48eb89532b2b0c96d753c8f98eb8ce03f5dd95fa60ab9cc92f3a4", wantTileCount: 1, }, { name: "single entry in one tile with different tree ID", prefixStrata: 0, entries: []*Entry{createEntry("ak", "av")}, treeID: 54321, hash: crypto.SHA512_256, wantRoot: "8e6363380169b790b6e3d1890fc3d492a73512d9bbbfb886854e10ca10fc147f", wantTileCount: 1, }, { name: "single entry in stratified map", prefixStrata: 1, entries: []*Entry{createEntry("ak", "av")}, treeID: 12345, hash: crypto.SHA512_256, wantRoot: "af079c268bd48eb89532b2b0c96d753c8f98eb8ce03f5dd95fa60ab9cc92f3a4", wantTileCount: 2, }, { name: "3 entries in one tile", prefixStrata: 0, entries: []*Entry{createEntry("ak", "av"), createEntry("bk", "bv"), createEntry("ck", "cv")}, treeID: 12345, hash: crypto.SHA512_256, wantRoot: "2372f0432e04dc76015f427ce8a1294644e36421b047ddfd52afdfdba60aff25", wantTileCount: 1, }, { name: "3 entries in stratified map", prefixStrata: 1, entries: []*Entry{createEntry("ak", "av"), createEntry("bk", "bv"), createEntry("ck", "cv")}, treeID: 12345, hash: crypto.SHA512_256, wantRoot: "2372f0432e04dc76015f427ce8a1294644e36421b047ddfd52afdfdba60aff25", wantTileCount: 4, }, { name: "duplicate keys", prefixStrata: 0, entries: []*Entry{createEntry("ak", "av"), createEntry("ak", "av")}, treeID: 12345, hash: crypto.SHA512_256, wantFailRun: true, }, { name: "invalid prefixStrata (too small)", prefixStrata: -1, entries: []*Entry{createEntry("ak", "av")}, treeID: 12345, hash: crypto.SHA512_256, wantFailConstruct: true, }, { name: "invalid prefixStrata (too large)", prefixStrata: 32, entries: []*Entry{createEntry("ak", "av")}, treeID: 12345, hash: crypto.SHA512_256, wantFailConstruct: true, }, } for _, test := range tests { test := test t.Run(test.name, func(t *testing.T) { // t.Parallel() would be great, but it seems Flume tests are sketchy about this? p, s := beam.NewPipelineWithRoot() leaves := beam.CreateList(s, test.entries) tiles, err := Create(s, leaves, test.treeID, test.hash, test.prefixStrata) if got, want := err != nil, test.wantFailConstruct; got != want { t.Errorf("pipeline construction failure: got %v, want %v (%v)", got, want, err) } if test.wantFailConstruct { return } rootTile := filter.Include(s, tiles, testFilterRootOnlyFn) roots := beam.ParDo(s, testFnTileRootHash, rootTile) assertTileCount(s, tiles, test.wantTileCount) passert.Equals(s, roots, test.wantRoot) err = ptest.Run(p) if got, want := err != nil, test.wantFailRun; got != want { t.Errorf("pipeline run failure: got %v, want %v (%v)", got, want, err) } }) } } func TestUpdate(t *testing.T) { tests := []struct { name string prefixStrata int baseEntries, updateEntries []*Entry treeID int64 wantRoot string wantTileCount int wantFailRun bool }{ { name: "update single entry in single tile", prefixStrata: 0, baseEntries: []*Entry{createEntry("ak", "ignored")}, updateEntries: []*Entry{createEntry("ak", "av")}, treeID: 12345, wantRoot: "af079c268bd48eb89532b2b0c96d753c8f98eb8ce03f5dd95fa60ab9cc92f3a4", wantTileCount: 1, }, { name: "update single entry in stratified map", prefixStrata: 3, baseEntries: []*Entry{createEntry("ak", "ignored")}, updateEntries: []*Entry{createEntry("ak", "av")}, treeID: 12345, wantRoot: "af079c268bd48eb89532b2b0c96d753c8f98eb8ce03f5dd95fa60ab9cc92f3a4", wantTileCount: 4, }, { name: "3 entries in one tile", prefixStrata: 0, baseEntries: []*Entry{createEntry("ak", "ignored"), createEntry("bk", "bv")}, updateEntries: []*Entry{createEntry("ak", "av"), createEntry("ck", "cv")}, treeID: 12345, wantRoot: "2372f0432e04dc76015f427ce8a1294644e36421b047ddfd52afdfdba60aff25", wantTileCount: 1, }, { name: "3 entries in stratified map", prefixStrata: 3, baseEntries: []*Entry{createEntry("ak", "ignored"), createEntry("bk", "bv")}, updateEntries: []*Entry{createEntry("ak", "av"), createEntry("ck", "cv")}, treeID: 12345, wantRoot: "2372f0432e04dc76015f427ce8a1294644e36421b047ddfd52afdfdba60aff25", wantTileCount: 10, }, { name: "duplicate keys", prefixStrata: 3, baseEntries: []*Entry{createEntry("ak", "ignored")}, updateEntries: []*Entry{createEntry("ak", "av1"), createEntry("ak", "av2")}, treeID: 12345, wantFailRun: true, }, } for _, test := range tests { test := test t.Run(test.name, func(t *testing.T) { p, s := beam.NewPipelineWithRoot() leaves := beam.CreateList(s, test.baseEntries) base, err := Create(s, leaves, test.treeID, hash, test.prefixStrata) if err != nil { t.Fatalf("failed to create pipeline: %v", err) } delta := beam.CreateList(s, test.updateEntries) tiles, err := Update(s, base, delta, test.treeID, hash, test.prefixStrata) if err != nil { t.Errorf("pipeline construction failure: %v", err) } rootTile := filter.Include(s, tiles, testFilterRootOnlyFn) roots := beam.ParDo(s, testFnTileRootHash, rootTile) assertTileCount(s, tiles, test.wantTileCount) passert.Equals(s, roots, test.wantRoot) err = ptest.Run(p) if got, want := err != nil, test.wantFailRun; got != want { t.Errorf("pipeline run failure: got %v, want %v (%v)", got, want, err) } }) } } func testFilterRootOnlyFn(t *Tile) bool { return len(t.Path) == 0 } func TestChildrenSorted(t *testing.T) { p, s := beam.NewPipelineWithRoot() entries := []*Entry{} for i := 0; i < 20; i++ { entries = append(entries, createEntry(fmt.Sprintf("key: %d", i), fmt.Sprintf("value: %d", i))) } tiles, err := Create(s, beam.CreateList(s, entries), 12345, hash, 1) if err != nil { t.Fatalf("failed to create pipeline: %v", err) } passert.True(s, tiles, testLeavesSortedFn) if err := ptest.Run(p); err != nil { t.Fatalf("pipeline failed: %v", err) } } func testLeavesSortedFn(t *Tile) bool { return isStrictlySorted(t.Leaves) } func TestGoldenCreate(t *testing.T) { p, s := beam.NewPipelineWithRoot() leaves := beam.CreateList(s, leafNodes(t, 500)) tiles, err := Create(s, leaves, 42, crypto.SHA256, 3) if err != nil { t.Fatalf("failed to create pipeline: %v", err) } rootTile := filter.Include(s, tiles, testFilterRootOnlyFn) roots := beam.ParDo(s, testFnTileRootHash, rootTile) assertTileCount(s, tiles, 1218) passert.Equals(s, roots, "daf17dc2c83f37962bae8a65d294ef7fca4ffa02c10bdc4ca5c4dec408001c98") if err := ptest.Run(p); err != nil { t.Fatalf("pipeline failed: %v", err) } } func TestGoldenUpdate(t *testing.T) { treeID := int64(42) strata := 3 hash := crypto.SHA256 p, s := beam.NewPipelineWithRoot() entries := leafNodes(t, 500) base, err := Create(s, beam.CreateList(s, entries[:300]), treeID, hash, strata) if err != nil { t.Fatalf("failed to create v0 pipeline: %v", err) } tiles, err := Update(s, base, beam.CreateList(s, entries[300:]), treeID, hash, strata) if err != nil { t.Fatalf("failed to create v1 pipeline: %v", err) } rootTile := filter.Include(s, tiles, testFilterRootOnlyFn) roots := beam.ParDo(s, testFnTileRootHash, rootTile) assertTileCount(s, tiles, 1218) passert.Equals(s, roots, "daf17dc2c83f37962bae8a65d294ef7fca4ffa02c10bdc4ca5c4dec408001c98") if err := ptest.Run(p); err != nil { t.Fatalf("pipeline failed: %v", err) } } // assertTileCount adds a check into the pipeline that the given PCollection of // tiles has the given cardinality. If the check fails then ptest.Run will // return an error. func assertTileCount(s beam.Scope, tiles beam.PCollection, count int) { passert.Equals(s, stats.Sum(s, beam.ParDo(s, countTilesFn, tiles)), count) } func countTilesFn(t *Tile) int { return 1 } // Copied from http://google3/third_party/golang/trillian/merkle/smt/hstar3_test.go?l=201&rcl=298994396 func leafNodes(t testing.TB, n int) []*Entry { t.Helper() // Use a random sequence that depends on n. r := rand.New(rand.NewSource(int64(n))) entries := make([]*Entry, n) for i := range entries { value := make([]byte, 32) if _, err := r.Read(value); err != nil { t.Fatalf("Failed to make random leaf hash: %v", err) } path := make([]byte, 32) if _, err := r.Read(path); err != nil { t.Fatalf("Failed to make random path: %v", err) } entries[i] = &Entry{ HashKey: path, HashValue: value, } } return entries } func createEntry(k, v string) *Entry { h := crypto.SHA256.New() h.Write([]byte(k)) hk := h.Sum(nil) h = crypto.SHA256.New() h.Write([]byte(v)) hv := h.Sum(nil) return &Entry{ HashKey: hk, HashValue: hv, } } func isStrictlySorted(leaves []*TileLeaf) bool { for i := 1; i < len(leaves); i++ { lPath, rPath := leaves[i-1].Path, leaves[i].Path if string(lPath) >= string(rPath) { return false } } return true } trillian-1.6.1/extension/000077500000000000000000000000001466362047600153675ustar00rootroot00000000000000trillian-1.6.1/extension/README.md000066400000000000000000000005051466362047600166460ustar00rootroot00000000000000# Extensions Trillian defines a number of extension points to allow for customization by forks. At runtime, implementations are acquired via an [extension.Registry]( https://github.com/google/trillian/blob/master/extension/registry.go), which contains the comprehensive list of all supported extensions (bar the following). trillian-1.6.1/extension/registry.go000066400000000000000000000032301466362047600175640ustar00rootroot00000000000000// 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 extension provides an extension mechanism for Trillian code to access // fork-specific functionality. package extension import ( "github.com/google/trillian/monitoring" "github.com/google/trillian/quota" "github.com/google/trillian/storage" "github.com/google/trillian/util/election2" ) // Registry defines all extension points available in Trillian. // Customizations may easily swap the underlying storage systems by providing their own // implementation. type Registry struct { // AdminStorage is the storage implementation to use for persisting tree metadata. storage.AdminStorage // LogStorage is the storage implementation to use for persisting logs. storage.LogStorage // ElectionFactory provides Election instances for each tree. ElectionFactory election2.Factory // QuotaManager provides rate limiting capabilities for Trillian. QuotaManager quota.Manager // MetricFactory provides metrics for monitoring. monitoring.MetricFactory // SetProcessStatus sets the current process status for diagnostic purposes. SetProcessStatus func(string) } trillian-1.6.1/gen.go000066400000000000000000000024571466362047600144630ustar00rootroot00000000000000// 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 trillian contains the generated protobuf code for the Trillian API. package trillian //go:generate protoc -I=. -I=third_party/googleapis --go_out=paths=source_relative:. --go-grpc_out=paths=source_relative:. --go-grpc_opt=require_unimplemented_servers=false trillian_log_api.proto trillian_admin_api.proto trillian.proto --doc_out=markdown,api.md:./docs/ //go:generate protoc -I=. --go_out=paths=source_relative:. crypto/keyspb/keyspb.proto //go:generate mockgen -package tmock -destination testonly/tmock/mock_log_server.go github.com/google/trillian TrillianLogServer //go:generate mockgen -package tmock -destination testonly/tmock/mock_admin_server.go github.com/google/trillian TrillianAdminServer trillian-1.6.1/go.mod000066400000000000000000000203261466362047600144640ustar00rootroot00000000000000module github.com/google/trillian go 1.22.6 require ( bitbucket.org/creachadair/shell v0.0.8 cloud.google.com/go/spanner v1.67.0 contrib.go.opencensus.io/exporter/stackdriver v0.13.14 github.com/apache/beam/sdks/v2 v2.58.1 github.com/cockroachdb/cockroach-go/v2 v2.3.8 github.com/fullstorydev/grpcurl v1.9.1 github.com/go-redis/redis v6.15.9+incompatible github.com/go-sql-driver/mysql v1.8.1 github.com/golang/mock v1.6.0 github.com/google/btree v1.1.3 github.com/google/go-cmp v0.6.0 github.com/google/go-licenses/v2 v2.0.0-alpha.1 github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 github.com/letsencrypt/pkcs11key/v4 v4.0.0 github.com/lib/pq v1.10.9 github.com/prometheus/client_golang v1.20.2 github.com/prometheus/client_model v0.6.1 github.com/pseudomuto/protoc-gen-doc v1.5.1 github.com/transparency-dev/merkle v0.0.2 go.etcd.io/etcd/client/v3 v3.5.15 go.etcd.io/etcd/etcdctl/v3 v3.5.15 go.etcd.io/etcd/server/v3 v3.5.15 go.etcd.io/etcd/v3 v3.5.15 go.opencensus.io v0.24.0 golang.org/x/crypto v0.26.0 golang.org/x/sync v0.8.0 golang.org/x/sys v0.24.0 golang.org/x/tools v0.24.0 google.golang.org/api v0.194.0 google.golang.org/genproto v0.0.0-20240814211410-ddb44dafa142 google.golang.org/genproto/googleapis/api v0.0.0-20240730163845-b1a4ccb954bf google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 google.golang.org/grpc v1.65.0 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 google.golang.org/protobuf v1.34.2 gopkg.in/yaml.v2 v2.4.0 k8s.io/klog/v2 v2.130.1 ) require ( cel.dev/expr v0.15.0 // indirect cloud.google.com/go v0.115.1 // indirect cloud.google.com/go/auth v0.9.1 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect cloud.google.com/go/compute/metadata v0.5.0 // indirect cloud.google.com/go/iam v1.1.12 // indirect cloud.google.com/go/longrunning v0.5.11 // indirect cloud.google.com/go/monitoring v1.20.3 // indirect cloud.google.com/go/profiler v0.4.0 // indirect cloud.google.com/go/storage v1.43.0 // indirect cloud.google.com/go/trace v1.10.11 // indirect filippo.io/edwards25519 v1.1.0 // indirect github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver v1.5.0 // indirect github.com/Masterminds/sprig v2.22.0+incompatible // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/avast/retry-go/v4 v4.6.0 // indirect github.com/aws/aws-sdk-go v1.51.8 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/speakeasy v0.1.0 // indirect github.com/bufbuild/protocompile v0.10.0 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b // indirect github.com/coreos/go-semver v0.3.1 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/distribution/reference v0.5.0 // indirect github.com/docker/docker v25.0.6+incompatible // indirect github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/envoyproxy/go-control-plane v0.12.0 // indirect github.com/envoyproxy/protoc-gen-validate v1.0.4 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/gofrs/flock v0.8.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/licenseclassifier/v2 v2.0.0 // indirect github.com/google/pprof v0.0.0-20240320155624-b11c3daa6f07 // indirect github.com/google/s2a-go v0.1.8 // indirect github.com/google/uuid v1.6.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect github.com/googleapis/gax-go/v2 v2.13.0 // indirect github.com/gorilla/websocket v1.5.1 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 // indirect github.com/huandu/xstrings v1.2.0 // indirect github.com/imdario/mergo v0.3.16 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect github.com/jackc/pgconn v1.14.3 // indirect github.com/jackc/pgio v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgproto3/v2 v2.3.3 // indirect github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 // indirect github.com/jackc/pgtype v1.14.3 // indirect github.com/jackc/pgx/v4 v4.18.3 // indirect github.com/jhump/protoreflect v1.16.0 // indirect github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24 // indirect github.com/jonboulle/clockwork v0.4.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.17.9 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect github.com/miekg/pkcs11 v1.1.1 // indirect github.com/mitchellh/copystructure v1.0.0 // indirect github.com/mitchellh/reflectwalk v1.0.1 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mwitkow/go-proto-validators v0.2.0 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/onsi/ginkgo v1.16.5 // indirect github.com/onsi/gomega v1.29.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0 // indirect github.com/otiai10/copy v1.14.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect github.com/prometheus/prometheus v0.51.0 // indirect github.com/pseudomuto/protokit v0.2.0 // indirect github.com/rivo/uniseg v0.4.4 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sergi/go-diff v1.3.1 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/soheilhy/cmux v0.1.5 // indirect github.com/spf13/cobra v1.8.1 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 // indirect github.com/urfave/cli v1.22.14 // indirect github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510 // indirect go.etcd.io/bbolt v1.3.10 // indirect go.etcd.io/etcd/api/v3 v3.5.15 // indirect go.etcd.io/etcd/client/pkg/v3 v3.5.15 // indirect go.etcd.io/etcd/client/v2 v2.305.15 // indirect go.etcd.io/etcd/etcdutl/v3 v3.5.15 // indirect go.etcd.io/etcd/pkg/v3 v3.5.15 // indirect go.etcd.io/etcd/raft/v3 v3.5.15 // indirect go.etcd.io/etcd/tests/v3 v3.5.15 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect go.opentelemetry.io/otel v1.24.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0 // indirect go.opentelemetry.io/otel/metric v1.24.0 // indirect go.opentelemetry.io/otel/sdk v1.24.0 // indirect go.opentelemetry.io/otel/trace v1.24.0 // indirect go.opentelemetry.io/proto/otlp v1.1.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect golang.org/x/mod v0.20.0 // indirect golang.org/x/net v0.28.0 // indirect golang.org/x/oauth2 v0.22.0 // indirect golang.org/x/text v0.17.0 // indirect golang.org/x/time v0.6.0 // indirect golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 // indirect gopkg.in/cheggaaa/pb.v1 v1.0.28 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect sigs.k8s.io/yaml v1.4.0 // indirect ) trillian-1.6.1/go.sum000066400000000000000000006055041466362047600145200ustar00rootroot00000000000000bitbucket.org/creachadair/shell v0.0.8 h1:3yM6JcAfaGWzjzcCamTblzSIWXm/YSs0PFGIzBm2HTo= bitbucket.org/creachadair/shell v0.0.8/go.mod h1:vINzudofoUXZSJ5tREgpy+Etyjsag3ait5WOWImEVZ0= cel.dev/expr v0.15.0 h1:O1jzfJCQBfL5BFoYktaxwIhuttaQPsVWerH9/EEKx0w= cel.dev/expr v0.15.0/go.mod h1:TRSuuV7DlVCE/uwv5QbAiW/v8l5O8C4eEPHeu7gf7Sg= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= cloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw1U= cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= cloud.google.com/go v0.115.1 h1:Jo0SM9cQnSkYfp44+v+NQXHpcHqlnRJk2qxh6yvxxxQ= cloud.google.com/go v0.115.1/go.mod h1:DuujITeaufu3gL68/lOFIirVNJwQeyf5UXyi+Wbgknc= cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4= cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E= cloud.google.com/go/accesscontextmanager v1.3.0/go.mod h1:TgCBehyr5gNMz7ZaH9xubp+CE8dkrszb4oK9CWyvD4o= cloud.google.com/go/accesscontextmanager v1.4.0/go.mod h1:/Kjh7BBu/Gh83sv+K60vN9QE5NJcd80sU33vIe2IFPE= cloud.google.com/go/accesscontextmanager v1.6.0/go.mod h1:8XCvZWfYw3K/ji0iVnp+6pu7huxoQTLmxAbVjbloTtM= cloud.google.com/go/accesscontextmanager v1.7.0/go.mod h1:CEGLewx8dwa33aDAZQujl7Dx+uYhS0eay198wB/VumQ= cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= cloud.google.com/go/aiplatform v1.27.0/go.mod h1:Bvxqtl40l0WImSb04d0hXFU7gDOiq9jQmorivIiWcKg= cloud.google.com/go/aiplatform v1.35.0/go.mod h1:7MFT/vCaOyZT/4IIFfxH4ErVg/4ku6lKv3w0+tFTgXQ= cloud.google.com/go/aiplatform v1.36.1/go.mod h1:WTm12vJRPARNvJ+v6P52RDHCNe4AhvjcIZ/9/RRHy/k= cloud.google.com/go/aiplatform v1.37.0/go.mod h1:IU2Cv29Lv9oCn/9LkFiiuKfwrRTq+QQMbW+hPCxJGZw= cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4= cloud.google.com/go/analytics v0.17.0/go.mod h1:WXFa3WSym4IZ+JiKmavYdJwGG/CvpqiqczmL59bTD9M= cloud.google.com/go/analytics v0.18.0/go.mod h1:ZkeHGQlcIPkw0R/GW+boWHhCOR43xz9RN/jn7WcqfIE= cloud.google.com/go/analytics v0.19.0/go.mod h1:k8liqf5/HCnOUkbawNtrWWc+UAzyDlW89doe8TtoDsE= cloud.google.com/go/apigateway v1.3.0/go.mod h1:89Z8Bhpmxu6AmUxuVRg/ECRGReEdiP3vQtk4Z1J9rJk= cloud.google.com/go/apigateway v1.4.0/go.mod h1:pHVY9MKGaH9PQ3pJ4YLzoj6U5FUDeDFBllIz7WmzJoc= cloud.google.com/go/apigateway v1.5.0/go.mod h1:GpnZR3Q4rR7LVu5951qfXPJCHquZt02jf7xQx7kpqN8= cloud.google.com/go/apigeeconnect v1.3.0/go.mod h1:G/AwXFAKo0gIXkPTVfZDd2qA1TxBXJ3MgMRBQkIi9jc= cloud.google.com/go/apigeeconnect v1.4.0/go.mod h1:kV4NwOKqjvt2JYR0AoIWo2QGfoRtn/pkS3QlHp0Ni04= cloud.google.com/go/apigeeconnect v1.5.0/go.mod h1:KFaCqvBRU6idyhSNyn3vlHXc8VMDJdRmwDF6JyFRqZ8= cloud.google.com/go/apigeeregistry v0.4.0/go.mod h1:EUG4PGcsZvxOXAdyEghIdXwAEi/4MEaoqLMLDMIwKXY= cloud.google.com/go/apigeeregistry v0.5.0/go.mod h1:YR5+s0BVNZfVOUkMa5pAR2xGd0A473vA5M7j247o1wM= cloud.google.com/go/apigeeregistry v0.6.0/go.mod h1:BFNzW7yQVLZ3yj0TKcwzb8n25CFBri51GVGOEUcgQsc= cloud.google.com/go/apikeys v0.4.0/go.mod h1:XATS/yqZbaBK0HOssf+ALHp8jAlNHUgyfprvNcBIszU= cloud.google.com/go/apikeys v0.5.0/go.mod h1:5aQfwY4D+ewMMWScd3hm2en3hCj+BROlyrt3ytS7KLI= cloud.google.com/go/apikeys v0.6.0/go.mod h1:kbpXu5upyiAlGkKrJgQl8A0rKNNJ7dQ377pdroRSSi8= cloud.google.com/go/appengine v1.4.0/go.mod h1:CS2NhuBuDXM9f+qscZ6V86m1MIIqPj3WC/UoEuR1Sno= cloud.google.com/go/appengine v1.5.0/go.mod h1:TfasSozdkFI0zeoxW3PTBLiNqRmzraodCWatWI9Dmak= cloud.google.com/go/appengine v1.6.0/go.mod h1:hg6i0J/BD2cKmDJbaFSYHFyZkgBEfQrDg/X0V5fJn84= cloud.google.com/go/appengine v1.7.0/go.mod h1:eZqpbHFCqRGa2aCdope7eC0SWLV1j0neb/QnMJVWx6A= cloud.google.com/go/appengine v1.7.1/go.mod h1:IHLToyb/3fKutRysUlFO0BPt5j7RiQ45nrzEJmKTo6E= cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4= cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0= cloud.google.com/go/area120 v0.7.0/go.mod h1:a3+8EUD1SX5RUcCs3MY5YasiO1z6yLiNLRiFrykbynY= cloud.google.com/go/area120 v0.7.1/go.mod h1:j84i4E1RboTWjKtZVWXPqvK5VHQFJRF2c1Nm69pWm9k= cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ= cloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk= cloud.google.com/go/artifactregistry v1.8.0/go.mod h1:w3GQXkJX8hiKN0v+at4b0qotwijQbYUqF2GWkZzAhC0= cloud.google.com/go/artifactregistry v1.9.0/go.mod h1:2K2RqvA2CYvAeARHRkLDhMDJ3OXy26h3XW+3/Jh2uYc= cloud.google.com/go/artifactregistry v1.11.1/go.mod h1:lLYghw+Itq9SONbCa1YWBoWs1nOucMH0pwXN1rOBZFI= cloud.google.com/go/artifactregistry v1.11.2/go.mod h1:nLZns771ZGAwVLzTX/7Al6R9ehma4WUEhZGWV6CeQNQ= cloud.google.com/go/artifactregistry v1.12.0/go.mod h1:o6P3MIvtzTOnmvGagO9v/rOjjA0HmhJ+/6KAXrmYDCI= cloud.google.com/go/artifactregistry v1.13.0/go.mod h1:uy/LNfoOIivepGhooAUpL1i30Hgee3Cu0l4VTWHUC08= cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o= cloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s= cloud.google.com/go/asset v1.8.0/go.mod h1:mUNGKhiqIdbr8X7KNayoYvyc4HbbFO9URsjbytpUaW0= cloud.google.com/go/asset v1.9.0/go.mod h1:83MOE6jEJBMqFKadM9NLRcs80Gdw76qGuHn8m3h8oHQ= cloud.google.com/go/asset v1.10.0/go.mod h1:pLz7uokL80qKhzKr4xXGvBQXnzHn5evJAEAtZiIb0wY= cloud.google.com/go/asset v1.11.1/go.mod h1:fSwLhbRvC9p9CXQHJ3BgFeQNM4c9x10lqlrdEUYXlJo= cloud.google.com/go/asset v1.12.0/go.mod h1:h9/sFOa4eDIyKmH6QMpm4eUK3pDojWnUhTgJlk762Hg= cloud.google.com/go/asset v1.13.0/go.mod h1:WQAMyYek/b7NBpYq/K4KJWcRqzoalEsxz/t/dTk4THw= cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY= cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw= cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVoYoxeLBoj4XkKYscNI= cloud.google.com/go/assuredworkloads v1.8.0/go.mod h1:AsX2cqyNCOvEQC8RMPnoc0yEarXQk6WEKkxYfL6kGIo= cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0= cloud.google.com/go/assuredworkloads v1.10.0/go.mod h1:kwdUQuXcedVdsIaKgKTp9t0UJkE5+PAVNhdQm4ZVq2E= cloud.google.com/go/auth v0.9.1 h1:+pMtLEV2k0AXKvs/tGZojuj6QaioxfUjOpMsG5Gtx+w= cloud.google.com/go/auth v0.9.1/go.mod h1:Sw8ocT5mhhXxFklyhT12Eiy0ed6tTrPMCJjSI8KhYLk= cloud.google.com/go/auth/oauth2adapt v0.2.4 h1:0GWE/FUsXhf6C+jAkWgYm7X9tK8cuEIfy19DBn6B6bY= cloud.google.com/go/auth/oauth2adapt v0.2.4/go.mod h1:jC/jOpwFP6JBxhB3P5Rr0a9HLMC/Pe3eaL4NmdvqPtc= cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8= cloud.google.com/go/automl v1.7.0/go.mod h1:RL9MYCCsJEOmt0Wf3z9uzG0a7adTT1fe+aObgSpkCt8= cloud.google.com/go/automl v1.8.0/go.mod h1:xWx7G/aPEe/NP+qzYXktoBSDfjO+vnKMGgsApGJJquM= cloud.google.com/go/automl v1.12.0/go.mod h1:tWDcHDp86aMIuHmyvjuKeeHEGq76lD7ZqfGLN6B0NuU= cloud.google.com/go/baremetalsolution v0.3.0/go.mod h1:XOrocE+pvK1xFfleEnShBlNAXf+j5blPPxrhjKgnIFc= cloud.google.com/go/baremetalsolution v0.4.0/go.mod h1:BymplhAadOO/eBa7KewQ0Ppg4A4Wplbn+PsFKRLo0uI= cloud.google.com/go/baremetalsolution v0.5.0/go.mod h1:dXGxEkmR9BMwxhzBhV0AioD0ULBmuLZI8CdwalUxuss= cloud.google.com/go/batch v0.3.0/go.mod h1:TR18ZoAekj1GuirsUsR1ZTKN3FC/4UDnScjT8NXImFE= cloud.google.com/go/batch v0.4.0/go.mod h1:WZkHnP43R/QCGQsZ+0JyG4i79ranE2u8xvjq/9+STPE= cloud.google.com/go/batch v0.7.0/go.mod h1:vLZN95s6teRUqRQ4s3RLDsH8PvboqBK+rn1oevL159g= cloud.google.com/go/beyondcorp v0.2.0/go.mod h1:TB7Bd+EEtcw9PCPQhCJtJGjk/7TC6ckmnSFS+xwTfm4= cloud.google.com/go/beyondcorp v0.3.0/go.mod h1:E5U5lcrcXMsCuoDNyGrpyTm/hn7ne941Jz2vmksAxW8= cloud.google.com/go/beyondcorp v0.4.0/go.mod h1:3ApA0mbhHx6YImmuubf5pyW8srKnCEPON32/5hj+RmM= cloud.google.com/go/beyondcorp v0.5.0/go.mod h1:uFqj9X+dSfrheVp7ssLTaRHd2EHqSL4QZmH4e8WXGGU= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA= cloud.google.com/go/bigquery v1.43.0/go.mod h1:ZMQcXHsl+xmU1z36G2jNGZmKp9zNY5BUua5wDgmNCfw= cloud.google.com/go/bigquery v1.44.0/go.mod h1:0Y33VqXTEsbamHJvJHdFmtqHvMIY28aK1+dFsvaChGc= cloud.google.com/go/bigquery v1.47.0/go.mod h1:sA9XOgy0A8vQK9+MWhEQTY6Tix87M/ZurWFIxmF9I/E= cloud.google.com/go/bigquery v1.48.0/go.mod h1:QAwSz+ipNgfL5jxiaK7weyOhzdoAy1zFm0Nf1fysJac= cloud.google.com/go/bigquery v1.49.0/go.mod h1:Sv8hMmTFFYBlt/ftw2uN6dFdQPzBlREY9yBh7Oy7/4Q= cloud.google.com/go/bigquery v1.50.0/go.mod h1:YrleYEh2pSEbgTBZYMJ5SuSr0ML3ypjRB1zgf7pvQLU= cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY= cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s= cloud.google.com/go/billing v1.6.0/go.mod h1:WoXzguj+BeHXPbKfNWkqVtDdzORazmCjraY+vrxcyvI= cloud.google.com/go/billing v1.7.0/go.mod h1:q457N3Hbj9lYwwRbnlD7vUpyjq6u5U1RAOArInEiD5Y= cloud.google.com/go/billing v1.12.0/go.mod h1:yKrZio/eu+okO/2McZEbch17O5CB5NpZhhXG6Z766ss= cloud.google.com/go/billing v1.13.0/go.mod h1:7kB2W9Xf98hP9Sr12KfECgfGclsH3CQR0R08tnRlRbc= cloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM= cloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI= cloud.google.com/go/binaryauthorization v1.3.0/go.mod h1:lRZbKgjDIIQvzYQS1p99A7/U1JqvqeZg0wiI5tp6tg0= cloud.google.com/go/binaryauthorization v1.4.0/go.mod h1:tsSPQrBd77VLplV70GUhBf/Zm3FsKmgSqgm4UmiDItk= cloud.google.com/go/binaryauthorization v1.5.0/go.mod h1:OSe4OU1nN/VswXKRBmciKpo9LulY41gch5c68htf3/Q= cloud.google.com/go/certificatemanager v1.3.0/go.mod h1:n6twGDvcUBFu9uBgt4eYvvf3sQ6My8jADcOVwHmzadg= cloud.google.com/go/certificatemanager v1.4.0/go.mod h1:vowpercVFyqs8ABSmrdV+GiFf2H/ch3KyudYQEMM590= cloud.google.com/go/certificatemanager v1.6.0/go.mod h1:3Hh64rCKjRAX8dXgRAyOcY5vQ/fE1sh8o+Mdd6KPgY8= cloud.google.com/go/channel v1.8.0/go.mod h1:W5SwCXDJsq/rg3tn3oG0LOxpAo6IMxNa09ngphpSlnk= cloud.google.com/go/channel v1.9.0/go.mod h1:jcu05W0my9Vx4mt3/rEHpfxc9eKi9XwsdDL8yBMbKUk= cloud.google.com/go/channel v1.11.0/go.mod h1:IdtI0uWGqhEeatSB62VOoJ8FSUhJ9/+iGkJVqp74CGE= cloud.google.com/go/channel v1.12.0/go.mod h1:VkxCGKASi4Cq7TbXxlaBezonAYpp1GCnKMY6tnMQnLU= cloud.google.com/go/cloudbuild v1.3.0/go.mod h1:WequR4ULxlqvMsjDEEEFnOG5ZSRSgWOywXYDb1vPE6U= cloud.google.com/go/cloudbuild v1.4.0/go.mod h1:5Qwa40LHiOXmz3386FrjrYM93rM/hdRr7b53sySrTqA= cloud.google.com/go/cloudbuild v1.6.0/go.mod h1:UIbc/w9QCbH12xX+ezUsgblrWv+Cv4Tw83GiSMHOn9M= cloud.google.com/go/cloudbuild v1.7.0/go.mod h1:zb5tWh2XI6lR9zQmsm1VRA+7OCuve5d8S+zJUul8KTg= cloud.google.com/go/cloudbuild v1.9.0/go.mod h1:qK1d7s4QlO0VwfYn5YuClDGg2hfmLZEb4wQGAbIgL1s= cloud.google.com/go/clouddms v1.3.0/go.mod h1:oK6XsCDdW4Ib3jCCBugx+gVjevp2TMXFtgxvPSee3OM= cloud.google.com/go/clouddms v1.4.0/go.mod h1:Eh7sUGCC+aKry14O1NRljhjyrr0NFC0G2cjwX0cByRk= cloud.google.com/go/clouddms v1.5.0/go.mod h1:QSxQnhikCLUw13iAbffF2CZxAER3xDGNHjsTAkQJcQA= cloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY= cloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI= cloud.google.com/go/cloudtasks v1.7.0/go.mod h1:ImsfdYWwlWNJbdgPIIGJWC+gemEGTBK/SunNQQNCAb4= cloud.google.com/go/cloudtasks v1.8.0/go.mod h1:gQXUIwCSOI4yPVK7DgTVFiiP0ZW/eQkydWzwVMdHxrI= cloud.google.com/go/cloudtasks v1.9.0/go.mod h1:w+EyLsVkLWHcOaqNEyvcKAsWp9p29dL6uL9Nst1cI7Y= cloud.google.com/go/cloudtasks v1.10.0/go.mod h1:NDSoTLkZ3+vExFEWu2UJV1arUyzVDAiZtdWcsUyNwBs= cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= cloud.google.com/go/compute v1.12.0/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= cloud.google.com/go/compute v1.13.0/go.mod h1:5aPTS0cUNMIc1CE546K+Th6weJUNQErARyZtRXDJ8GE= cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo= cloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63rR+SXhcpA= cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU= cloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE= cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU= cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY= cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY= cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck= cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w= cloud.google.com/go/container v1.6.0/go.mod h1:Xazp7GjJSeUYo688S+6J5V+n/t+G5sKBTFkKNudGRxg= cloud.google.com/go/container v1.7.0/go.mod h1:Dp5AHtmothHGX3DwwIHPgq45Y8KmNsgN3amoYfxVkLo= cloud.google.com/go/container v1.13.1/go.mod h1:6wgbMPeQRw9rSnKBCAJXnds3Pzj03C4JHamr8asWKy4= cloud.google.com/go/container v1.14.0/go.mod h1:3AoJMPhHfLDxLvrlVWaK57IXzaPnLaZq63WX59aQBfM= cloud.google.com/go/container v1.15.0/go.mod h1:ft+9S0WGjAyjDggg5S06DXj+fHJICWg8L7isCQe9pQA= cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= cloud.google.com/go/containeranalysis v0.7.0/go.mod h1:9aUL+/vZ55P2CXfuZjS4UjQ9AgXoSw8Ts6lemfmxBxI= cloud.google.com/go/containeranalysis v0.9.0/go.mod h1:orbOANbwk5Ejoom+s+DUCTTJ7IBdBQJDcSylAx/on9s= cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs= cloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc= cloud.google.com/go/datacatalog v1.7.0/go.mod h1:9mEl4AuDYWw81UGc41HonIHH7/sn52H0/tc8f8ZbZIE= cloud.google.com/go/datacatalog v1.8.0/go.mod h1:KYuoVOv9BM8EYz/4eMFxrr4DUKhGIOXxZoKYF5wdISM= cloud.google.com/go/datacatalog v1.8.1/go.mod h1:RJ58z4rMp3gvETA465Vg+ag8BGgBdnRPEMMSTr5Uv+M= cloud.google.com/go/datacatalog v1.12.0/go.mod h1:CWae8rFkfp6LzLumKOnmVh4+Zle4A3NXLzVJ1d1mRm0= cloud.google.com/go/datacatalog v1.13.0/go.mod h1:E4Rj9a5ZtAxcQJlEBTLgMTphfP11/lNaAshpoBgemX8= cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM= cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ= cloud.google.com/go/dataflow v0.8.0/go.mod h1:Rcf5YgTKPtQyYz8bLYhFoIV/vP39eL7fWNcSOyFfLJE= cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo= cloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE= cloud.google.com/go/dataform v0.5.0/go.mod h1:GFUYRe8IBa2hcomWplodVmUx/iTL0FrsauObOM3Ipr0= cloud.google.com/go/dataform v0.6.0/go.mod h1:QPflImQy33e29VuapFdf19oPbE4aYTJxr31OAPV+ulA= cloud.google.com/go/dataform v0.7.0/go.mod h1:7NulqnVozfHvWUBpMDfKMUESr+85aJsC/2O0o3jWPDE= cloud.google.com/go/datafusion v1.4.0/go.mod h1:1Zb6VN+W6ALo85cXnM1IKiPw+yQMKMhB9TsTSRDo/38= cloud.google.com/go/datafusion v1.5.0/go.mod h1:Kz+l1FGHB0J+4XF2fud96WMmRiq/wj8N9u007vyXZ2w= cloud.google.com/go/datafusion v1.6.0/go.mod h1:WBsMF8F1RhSXvVM8rCV3AeyWVxcC2xY6vith3iw3S+8= cloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I= cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ= cloud.google.com/go/datalabeling v0.7.0/go.mod h1:WPQb1y08RJbmpM3ww0CSUAGweL0SxByuW2E+FU+wXcM= cloud.google.com/go/dataplex v1.3.0/go.mod h1:hQuRtDg+fCiFgC8j0zV222HvzFQdRd+SVX8gdmFcZzA= cloud.google.com/go/dataplex v1.4.0/go.mod h1:X51GfLXEMVJ6UN47ESVqvlsRplbLhcsAt0kZCCKsU0A= cloud.google.com/go/dataplex v1.5.2/go.mod h1:cVMgQHsmfRoI5KFYq4JtIBEUbYwc3c7tXmIDhRmNNVQ= cloud.google.com/go/dataplex v1.6.0/go.mod h1:bMsomC/aEJOSpHXdFKFGQ1b0TDPIeL28nJObeO1ppRs= cloud.google.com/go/dataproc v1.7.0/go.mod h1:CKAlMjII9H90RXaMpSxQ8EU6dQx6iAYNPcYPOkSbi8s= cloud.google.com/go/dataproc v1.8.0/go.mod h1:5OW+zNAH0pMpw14JVrPONsxMQYMBqJuzORhIBfBn9uI= cloud.google.com/go/dataproc v1.12.0/go.mod h1:zrF3aX0uV3ikkMz6z4uBbIKyhRITnxvr4i3IjKsKrw4= cloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo= cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA= cloud.google.com/go/dataqna v0.7.0/go.mod h1:Lx9OcIIeqCrw1a6KdO3/5KMP1wAmTc0slZWwP12Qq3c= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/datastore v1.10.0/go.mod h1:PC5UzAmDEkAmkfaknstTYbNpgE49HAgW2J1gcgUfmdM= cloud.google.com/go/datastore v1.11.0/go.mod h1:TvGxBIHCS50u8jzG+AW/ppf87v1of8nwzFNgEZU1D3c= cloud.google.com/go/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo= cloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ= cloud.google.com/go/datastream v1.4.0/go.mod h1:h9dpzScPhDTs5noEMQVWP8Wx8AFBRyS0s8KWPx/9r0g= cloud.google.com/go/datastream v1.5.0/go.mod h1:6TZMMNPwjUqZHBKPQ1wwXpb0d5VDVPl2/XoS5yi88q4= cloud.google.com/go/datastream v1.6.0/go.mod h1:6LQSuswqLa7S4rPAOZFVjHIG3wJIjZcZrw8JDEDJuIs= cloud.google.com/go/datastream v1.7.0/go.mod h1:uxVRMm2elUSPuh65IbZpzJNMbuzkcvu5CjMqVIUHrww= cloud.google.com/go/deploy v1.4.0/go.mod h1:5Xghikd4VrmMLNaF6FiRFDlHb59VM59YoDQnOUdsH/c= cloud.google.com/go/deploy v1.5.0/go.mod h1:ffgdD0B89tToyW/U/D2eL0jN2+IEV/3EMuXHA0l4r+s= cloud.google.com/go/deploy v1.6.0/go.mod h1:f9PTHehG/DjCom3QH0cntOVRm93uGBDt2vKzAPwpXQI= cloud.google.com/go/deploy v1.8.0/go.mod h1:z3myEJnA/2wnB4sgjqdMfgxCA0EqC3RBTNcVPs93mtQ= cloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4= cloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0= cloud.google.com/go/dialogflow v1.17.0/go.mod h1:YNP09C/kXA1aZdBgC/VtXX74G/TKn7XVCcVumTflA+8= cloud.google.com/go/dialogflow v1.18.0/go.mod h1:trO7Zu5YdyEuR+BhSNOqJezyFQ3aUzz0njv7sMx/iek= cloud.google.com/go/dialogflow v1.19.0/go.mod h1:JVmlG1TwykZDtxtTXujec4tQ+D8SBFMoosgy+6Gn0s0= cloud.google.com/go/dialogflow v1.29.0/go.mod h1:b+2bzMe+k1s9V+F2jbJwpHPzrnIyHihAdRFMtn2WXuM= cloud.google.com/go/dialogflow v1.31.0/go.mod h1:cuoUccuL1Z+HADhyIA7dci3N5zUssgpBJmCzI6fNRB4= cloud.google.com/go/dialogflow v1.32.0/go.mod h1:jG9TRJl8CKrDhMEcvfcfFkkpp8ZhgPz3sBGmAUYJ2qE= cloud.google.com/go/dlp v1.6.0/go.mod h1:9eyB2xIhpU0sVwUixfBubDoRwP+GjeUoxxeueZmqvmM= cloud.google.com/go/dlp v1.7.0/go.mod h1:68ak9vCiMBjbasxeVD17hVPxDEck+ExiHavX8kiHG+Q= cloud.google.com/go/dlp v1.9.0/go.mod h1:qdgmqgTyReTz5/YNSSuueR8pl7hO0o9bQ39ZhtgkWp4= cloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU= cloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU= cloud.google.com/go/documentai v1.9.0/go.mod h1:FS5485S8R00U10GhgBC0aNGrJxBP8ZVpEeJ7PQDZd6k= cloud.google.com/go/documentai v1.10.0/go.mod h1:vod47hKQIPeCfN2QS/jULIvQTugbmdc0ZvxxfQY1bg4= cloud.google.com/go/documentai v1.16.0/go.mod h1:o0o0DLTEZ+YnJZ+J4wNfTxmDVyrkzFvttBXXtYRMHkM= cloud.google.com/go/documentai v1.18.0/go.mod h1:F6CK6iUH8J81FehpskRmhLq/3VlwQvb7TvwOceQ2tbs= cloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y= cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg= cloud.google.com/go/domains v0.8.0/go.mod h1:M9i3MMDzGFXsydri9/vW+EWz9sWb4I6WyHqdlAk0idE= cloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk= cloud.google.com/go/edgecontainer v0.2.0/go.mod h1:RTmLijy+lGpQ7BXuTDa4C4ssxyXT34NIuHIgKuP4s5w= cloud.google.com/go/edgecontainer v0.3.0/go.mod h1:FLDpP4nykgwwIfcLt6zInhprzw0lEi2P1fjO6Ie0qbc= cloud.google.com/go/edgecontainer v1.0.0/go.mod h1:cttArqZpBB2q58W/upSG++ooo6EsblxDIolxa3jSjbY= cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU= cloud.google.com/go/essentialcontacts v1.3.0/go.mod h1:r+OnHa5jfj90qIfZDO/VztSFqbQan7HV75p8sA+mdGI= cloud.google.com/go/essentialcontacts v1.4.0/go.mod h1:8tRldvHYsmnBCHdFpvU+GL75oWiBKl80BiqlFh9tp+8= cloud.google.com/go/essentialcontacts v1.5.0/go.mod h1:ay29Z4zODTuwliK7SnX8E86aUF2CTzdNtvv42niCX0M= cloud.google.com/go/eventarc v1.7.0/go.mod h1:6ctpF3zTnaQCxUjHUdcfgcA1A2T309+omHZth7gDfmc= cloud.google.com/go/eventarc v1.8.0/go.mod h1:imbzxkyAU4ubfsaKYdQg04WS1NvncblHEup4kvF+4gw= cloud.google.com/go/eventarc v1.10.0/go.mod h1:u3R35tmZ9HvswGRBnF48IlYgYeBcPUCjkr4BTdem2Kw= cloud.google.com/go/eventarc v1.11.0/go.mod h1:PyUjsUKPWoRBCHeOxZd/lbOOjahV41icXyUY5kSTvVY= cloud.google.com/go/filestore v1.3.0/go.mod h1:+qbvHGvXU1HaKX2nD0WEPo92TP/8AQuCVEBXNY9z0+w= cloud.google.com/go/filestore v1.4.0/go.mod h1:PaG5oDfo9r224f8OYXURtAsY+Fbyq/bLYoINEK8XQAI= cloud.google.com/go/filestore v1.5.0/go.mod h1:FqBXDWBp4YLHqRnVGveOkHDf8svj9r5+mUDLupOWEDs= cloud.google.com/go/filestore v1.6.0/go.mod h1:di5unNuss/qfZTw2U9nhFqo8/ZDSc466dre85Kydllg= cloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE= cloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk= cloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg= cloud.google.com/go/functions v1.8.0/go.mod h1:RTZ4/HsQjIqIYP9a9YPbU+QFoQsAlYgrwOXJWHn1POY= cloud.google.com/go/functions v1.9.0/go.mod h1:Y+Dz8yGguzO3PpIjhLTbnqV1CWmgQ5UwtlpzoyquQ08= cloud.google.com/go/functions v1.10.0/go.mod h1:0D3hEOe3DbEvCXtYOZHQZmD+SzYsi1YbI7dGvHfldXw= cloud.google.com/go/functions v1.12.0/go.mod h1:AXWGrF3e2C/5ehvwYo/GH6O5s09tOPksiKhz+hH8WkA= cloud.google.com/go/functions v1.13.0/go.mod h1:EU4O007sQm6Ef/PwRsI8N2umygGqPBS/IZQKBQBcJ3c= cloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM= cloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA= cloud.google.com/go/gaming v1.7.0/go.mod h1:LrB8U7MHdGgFG851iHAfqUdLcKBdQ55hzXy9xBJz0+w= cloud.google.com/go/gaming v1.8.0/go.mod h1:xAqjS8b7jAVW0KFYeRUxngo9My3f33kFmua++Pi+ggM= cloud.google.com/go/gaming v1.9.0/go.mod h1:Fc7kEmCObylSWLO334NcO+O9QMDyz+TKC4v1D7X+Bc0= cloud.google.com/go/gkebackup v0.2.0/go.mod h1:XKvv/4LfG829/B8B7xRkk8zRrOEbKtEam6yNfuQNH60= cloud.google.com/go/gkebackup v0.3.0/go.mod h1:n/E671i1aOQvUxT541aTkCwExO/bTer2HDlj4TsBRAo= cloud.google.com/go/gkebackup v0.4.0/go.mod h1:byAyBGUwYGEEww7xsbnUTBHIYcOPy/PgUWUtOeRm9Vg= cloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o= cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A= cloud.google.com/go/gkeconnect v0.7.0/go.mod h1:SNfmVqPkaEi3bF/B3CNZOAYPYdg7sU+obZ+QTky2Myw= cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0= cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0= cloud.google.com/go/gkehub v0.11.0/go.mod h1:JOWHlmN+GHyIbuWQPl47/C2RFhnFKH38jH9Ascu3n0E= cloud.google.com/go/gkehub v0.12.0/go.mod h1:djiIwwzTTBrF5NaXCGv3mf7klpEMcST17VBTVVDcuaw= cloud.google.com/go/gkemulticloud v0.3.0/go.mod h1:7orzy7O0S+5kq95e4Hpn7RysVA7dPs8W/GgfUtsPbrA= cloud.google.com/go/gkemulticloud v0.4.0/go.mod h1:E9gxVBnseLWCk24ch+P9+B2CoDFJZTyIgLKSalC7tuI= cloud.google.com/go/gkemulticloud v0.5.0/go.mod h1:W0JDkiyi3Tqh0TJr//y19wyb1yf8llHVto2Htf2Ja3Y= cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= cloud.google.com/go/gsuiteaddons v1.3.0/go.mod h1:EUNK/J1lZEZO8yPtykKxLXI6JSVN2rg9bN8SXOa0bgM= cloud.google.com/go/gsuiteaddons v1.4.0/go.mod h1:rZK5I8hht7u7HxFQcFei0+AtfS9uSushomRlg+3ua1o= cloud.google.com/go/gsuiteaddons v1.5.0/go.mod h1:TFCClYLd64Eaa12sFVmUyG62tk4mdIsI7pAnSXRkcFo= cloud.google.com/go/iam v0.1.0/go.mod h1:vcUNEa0pEm0qRVpmWepWaFMIAI8/hjB9mO8rNCJtF6c= cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc= cloud.google.com/go/iam v0.6.0/go.mod h1:+1AH33ueBne5MzYccyMHtEKqLE4/kJOibtffMHDMFMc= cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQEYOeg= cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= cloud.google.com/go/iam v0.11.0/go.mod h1:9PiLDanza5D+oWFZiH1uG+RnRCfEGKoyl6yo4cgWZGY= cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB2GYAY= cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= cloud.google.com/go/iam v1.1.12 h1:JixGLimRrNGcxvJEQ8+clfLxPlbeZA6MuRJ+qJNQ5Xw= cloud.google.com/go/iam v1.1.12/go.mod h1:9LDX8J7dN5YRyzVHxwQzrQs9opFFqn0Mxs9nAeB+Hhg= cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc= cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A= cloud.google.com/go/iap v1.6.0/go.mod h1:NSuvI9C/j7UdjGjIde7t7HBz+QTwBcapPE07+sSRcLk= cloud.google.com/go/iap v1.7.0/go.mod h1:beqQx56T9O1G1yNPph+spKpNibDlYIiIixiqsQXxLIo= cloud.google.com/go/iap v1.7.1/go.mod h1:WapEwPc7ZxGt2jFGB/C/bm+hP0Y6NXzOYGjpPnmMS74= cloud.google.com/go/ids v1.1.0/go.mod h1:WIuwCaYVOzHIj2OhN9HAwvW+DBdmUAdcWlFxRl+KubM= cloud.google.com/go/ids v1.2.0/go.mod h1:5WXvp4n25S0rA/mQWAg1YEEBBq6/s+7ml1RDCW1IrcY= cloud.google.com/go/ids v1.3.0/go.mod h1:JBdTYwANikFKaDP6LtW5JAi4gubs57SVNQjemdt6xV4= cloud.google.com/go/iot v1.3.0/go.mod h1:r7RGh2B61+B8oz0AGE+J72AhA0G7tdXItODWsaA2oLs= cloud.google.com/go/iot v1.4.0/go.mod h1:dIDxPOn0UvNDUMD8Ger7FIaTuvMkj+aGk94RPP0iV+g= cloud.google.com/go/iot v1.5.0/go.mod h1:mpz5259PDl3XJthEmh9+ap0affn/MqNSP4My77Qql9o= cloud.google.com/go/iot v1.6.0/go.mod h1:IqdAsmE2cTYYNO1Fvjfzo9po179rAtJeVGUvkLN3rLE= cloud.google.com/go/kms v1.4.0/go.mod h1:fajBHndQ+6ubNw6Ss2sSd+SWvjL26RNo/dr7uxsnnOA= cloud.google.com/go/kms v1.5.0/go.mod h1:QJS2YY0eJGBg3mnDfuaCyLauWwBJiHRboYxJ++1xJNg= cloud.google.com/go/kms v1.6.0/go.mod h1:Jjy850yySiasBUDi6KFUwUv2n1+o7QZFyuUJg6OgjA0= cloud.google.com/go/kms v1.8.0/go.mod h1:4xFEhYFqvW+4VMELtZyxomGSYtSQKzM178ylFW4jMAg= cloud.google.com/go/kms v1.9.0/go.mod h1:qb1tPTgfF9RQP8e1wq4cLFErVuTJv7UsSC915J8dh3w= cloud.google.com/go/kms v1.10.0/go.mod h1:ng3KTUtQQU9bPX3+QGLsflZIHlkbn8amFAMY63m8d24= cloud.google.com/go/kms v1.10.1/go.mod h1:rIWk/TryCkR59GMC3YtHtXeLzd634lBbKenvyySAyYI= cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= cloud.google.com/go/language v1.7.0/go.mod h1:DJ6dYN/W+SQOjF8e1hLQXMF21AkH2w9wiPzPCJa2MIE= cloud.google.com/go/language v1.8.0/go.mod h1:qYPVHf7SPoNNiCL2Dr0FfEFNil1qi3pQEyygwpgVKB8= cloud.google.com/go/language v1.9.0/go.mod h1:Ns15WooPM5Ad/5no/0n81yUetis74g3zrbeJBE+ptUY= cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= cloud.google.com/go/lifesciences v0.8.0/go.mod h1:lFxiEOMqII6XggGbOnKiyZ7IBwoIqA84ClvoezaA/bo= cloud.google.com/go/logging v1.6.1/go.mod h1:5ZO0mHHbvm8gEmeEUHrmDlTDSu5imF6MUP9OfilNXBw= cloud.google.com/go/logging v1.7.0/go.mod h1:3xjP2CjkM3ZkO73aj4ASA5wRPGGCRrPIAeNqVNkzY8M= cloud.google.com/go/longrunning v0.1.1/go.mod h1:UUFxuDWkv22EuY93jjmDMFT5GPQKeFVJBIF6QlTqdsE= cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc= cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo= cloud.google.com/go/longrunning v0.5.11 h1:Havn1kGjz3whCfoD8dxMLP73Ph5w+ODyZB9RUsDxtGk= cloud.google.com/go/longrunning v0.5.11/go.mod h1:rDn7//lmlfWV1Dx6IB4RatCPenTwwmqXuiP0/RgoEO4= cloud.google.com/go/managedidentities v1.3.0/go.mod h1:UzlW3cBOiPrzucO5qWkNkh0w33KFtBJU281hacNvsdE= cloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM= cloud.google.com/go/managedidentities v1.5.0/go.mod h1:+dWcZ0JlUmpuxpIDfyP5pP5y0bLdRwOS4Lp7gMni/LA= cloud.google.com/go/maps v0.1.0/go.mod h1:BQM97WGyfw9FWEmQMpZ5T6cpovXXSd1cGmFma94eubI= cloud.google.com/go/maps v0.6.0/go.mod h1:o6DAMMfb+aINHz/p/jbcY+mYeXBoZoxTfdSQ8VAJaCw= cloud.google.com/go/maps v0.7.0/go.mod h1:3GnvVl3cqeSvgMcpRlQidXsPYuDGQ8naBis7MVzpXsY= cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4= cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= cloud.google.com/go/mediatranslation v0.7.0/go.mod h1:LCnB/gZr90ONOIQLgSXagp8XUW1ODs2UmUMvcgMfI2I= cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE= cloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM= cloud.google.com/go/memcache v1.6.0/go.mod h1:XS5xB0eQZdHtTuTF9Hf8eJkKtR3pVRCcvJwtm68T3rA= cloud.google.com/go/memcache v1.7.0/go.mod h1:ywMKfjWhNtkQTxrWxCkCFkoPjLHPW6A7WOTVI8xy3LY= cloud.google.com/go/memcache v1.9.0/go.mod h1:8oEyzXCu+zo9RzlEaEjHl4KkgjlNDaXbCQeQWlzNFJM= cloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY= cloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s= cloud.google.com/go/metastore v1.7.0/go.mod h1:s45D0B4IlsINu87/AsWiEVYbLaIMeUSoxlKKDqBGFS8= cloud.google.com/go/metastore v1.8.0/go.mod h1:zHiMc4ZUpBiM7twCIFQmJ9JMEkDSyZS9U12uf7wHqSI= cloud.google.com/go/metastore v1.10.0/go.mod h1:fPEnH3g4JJAk+gMRnrAnoqyv2lpUCqJPWOodSaf45Eo= cloud.google.com/go/monitoring v1.7.0/go.mod h1:HpYse6kkGo//7p6sT0wsIC6IBDET0RhIsnmlA53dvEk= cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4= cloud.google.com/go/monitoring v1.12.0/go.mod h1:yx8Jj2fZNEkL/GYZyTLS4ZtZEZN8WtDEiEqG4kLK50w= cloud.google.com/go/monitoring v1.13.0/go.mod h1:k2yMBAB1H9JT/QETjNkgdCGD9bPF712XiLTVr+cBrpw= cloud.google.com/go/monitoring v1.20.3 h1:v/7MXFxYrhXLEZ9sSfwXdlTLLB/xrU7xTyYjY5acynQ= cloud.google.com/go/monitoring v1.20.3/go.mod h1:GPIVIdNznIdGqEjtRKQWTLcUeRnPjZW85szouimiczU= cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA= cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o= cloud.google.com/go/networkconnectivity v1.6.0/go.mod h1:OJOoEXW+0LAxHh89nXd64uGG+FbQoeH8DtxCHVOMlaM= cloud.google.com/go/networkconnectivity v1.7.0/go.mod h1:RMuSbkdbPwNMQjB5HBWD5MpTBnNm39iAVpC3TmsExt8= cloud.google.com/go/networkconnectivity v1.10.0/go.mod h1:UP4O4sWXJG13AqrTdQCD9TnLGEbtNRqjuaaA7bNjF5E= cloud.google.com/go/networkconnectivity v1.11.0/go.mod h1:iWmDD4QF16VCDLXUqvyspJjIEtBR/4zq5hwnY2X3scM= cloud.google.com/go/networkmanagement v1.4.0/go.mod h1:Q9mdLLRn60AsOrPc8rs8iNV6OHXaGcDdsIQe1ohekq8= cloud.google.com/go/networkmanagement v1.5.0/go.mod h1:ZnOeZ/evzUdUsnvRt792H0uYEnHQEMaz+REhhzJRcf4= cloud.google.com/go/networkmanagement v1.6.0/go.mod h1:5pKPqyXjB/sgtvB5xqOemumoQNB7y95Q7S+4rjSOPYY= cloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ= cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU= cloud.google.com/go/networksecurity v0.7.0/go.mod h1:mAnzoxx/8TBSyXEeESMy9OOYwo1v+gZ5eMRnsT5bC8k= cloud.google.com/go/networksecurity v0.8.0/go.mod h1:B78DkqsxFG5zRSVuwYFRZ9Xz8IcQ5iECsNrPn74hKHU= cloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY= cloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34= cloud.google.com/go/notebooks v1.4.0/go.mod h1:4QPMngcwmgb6uw7Po99B2xv5ufVoIQ7nOGDyL4P8AgA= cloud.google.com/go/notebooks v1.5.0/go.mod h1:q8mwhnP9aR8Hpfnrc5iN5IBhrXUy8S2vuYs+kBJ/gu0= cloud.google.com/go/notebooks v1.7.0/go.mod h1:PVlaDGfJgj1fl1S3dUwhFMXFgfYGhYQt2164xOMONmE= cloud.google.com/go/notebooks v1.8.0/go.mod h1:Lq6dYKOYOWUCTvw5t2q1gp1lAp0zxAxRycayS0iJcqQ= cloud.google.com/go/optimization v1.1.0/go.mod h1:5po+wfvX5AQlPznyVEZjGJTMr4+CAkJf2XSTQOOl9l4= cloud.google.com/go/optimization v1.2.0/go.mod h1:Lr7SOHdRDENsh+WXVmQhQTrzdu9ybg0NecjHidBq6xs= cloud.google.com/go/optimization v1.3.1/go.mod h1:IvUSefKiwd1a5p0RgHDbWCIbDFgKuEdB+fPPuP0IDLI= cloud.google.com/go/orchestration v1.3.0/go.mod h1:Sj5tq/JpWiB//X/q3Ngwdl5K7B7Y0KZ7bfv0wL6fqVA= cloud.google.com/go/orchestration v1.4.0/go.mod h1:6W5NLFWs2TlniBphAViZEVhrXRSMgUGDfW7vrWKvsBk= cloud.google.com/go/orchestration v1.6.0/go.mod h1:M62Bevp7pkxStDfFfTuCOaXgaaqRAga1yKyoMtEoWPQ= cloud.google.com/go/orgpolicy v1.4.0/go.mod h1:xrSLIV4RePWmP9P3tBl8S93lTmlAxjm06NSm2UTmKvE= cloud.google.com/go/orgpolicy v1.5.0/go.mod h1:hZEc5q3wzwXJaKrsx5+Ewg0u1LxJ51nNFlext7Tanwc= cloud.google.com/go/orgpolicy v1.10.0/go.mod h1:w1fo8b7rRqlXlIJbVhOMPrwVljyuW5mqssvBtU18ONc= cloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs= cloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg= cloud.google.com/go/osconfig v1.9.0/go.mod h1:Yx+IeIZJ3bdWmzbQU4fxNl8xsZ4amB+dygAwFPlvnNo= cloud.google.com/go/osconfig v1.10.0/go.mod h1:uMhCzqC5I8zfD9zDEAfvgVhDS8oIjySWh+l4WK6GnWw= cloud.google.com/go/osconfig v1.11.0/go.mod h1:aDICxrur2ogRd9zY5ytBLV89KEgT2MKB2L/n6x1ooPw= cloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E= cloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU= cloud.google.com/go/oslogin v1.6.0/go.mod h1:zOJ1O3+dTU8WPlGEkFSh7qeHPPSoxrcMbbK1Nm2iX70= cloud.google.com/go/oslogin v1.7.0/go.mod h1:e04SN0xO1UNJ1M5GP0vzVBFicIe4O53FOfcixIqTyXo= cloud.google.com/go/oslogin v1.9.0/go.mod h1:HNavntnH8nzrn8JCTT5fj18FuJLFJc4NaZJtBnQtKFs= cloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0= cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA= cloud.google.com/go/phishingprotection v0.7.0/go.mod h1:8qJI4QKHoda/sb/7/YmMQ2omRLSLYSu9bU0EKCNI+Lk= cloud.google.com/go/policytroubleshooter v1.3.0/go.mod h1:qy0+VwANja+kKrjlQuOzmlvscn4RNsAc0e15GGqfMxg= cloud.google.com/go/policytroubleshooter v1.4.0/go.mod h1:DZT4BcRw3QoO8ota9xw/LKtPa8lKeCByYeKTIf/vxdE= cloud.google.com/go/policytroubleshooter v1.5.0/go.mod h1:Rz1WfV+1oIpPdN2VvvuboLVRsB1Hclg3CKQ53j9l8vw= cloud.google.com/go/policytroubleshooter v1.6.0/go.mod h1:zYqaPTsmfvpjm5ULxAyD/lINQxJ0DDsnWOP/GZ7xzBc= cloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0= cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI= cloud.google.com/go/privatecatalog v0.7.0/go.mod h1:2s5ssIFO69F5csTXcwBP7NPFTZvps26xGzvQ2PQaBYg= cloud.google.com/go/privatecatalog v0.8.0/go.mod h1:nQ6pfaegeDAq/Q5lrfCQzQLhubPiZhSaNhIgfJlnIXs= cloud.google.com/go/profiler v0.4.0 h1:ZeRDZbsOBDyRG0OiK0Op1/XWZ3xeLwJc9zjkzczUxyY= cloud.google.com/go/profiler v0.4.0/go.mod h1:RvPlm4dilIr3oJtAOeFQU9Lrt5RoySHSDj4pTd6TWeU= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= cloud.google.com/go/pubsub v1.26.0/go.mod h1:QgBH3U/jdJy/ftjPhTkyXNj543Tin1pRYcdcPRnFIRI= cloud.google.com/go/pubsub v1.27.1/go.mod h1:hQN39ymbV9geqBnfQq6Xf63yNhUAhv9CZhzp5O6qsW0= cloud.google.com/go/pubsub v1.28.0/go.mod h1:vuXFpwaVoIPQMGXqRyUQigu/AX1S3IWugR9xznmcXX8= cloud.google.com/go/pubsub v1.30.0/go.mod h1:qWi1OPS0B+b5L+Sg6Gmc9zD1Y+HaM0MdUr7LsupY1P4= cloud.google.com/go/pubsub v1.41.0 h1:ZPaM/CvTO6T+1tQOs/jJ4OEMpjtel0PTLV7j1JK+ZrI= cloud.google.com/go/pubsub v1.41.0/go.mod h1:g+YzC6w/3N91tzG66e2BZtp7WrpBBMXVa3Y9zVoOGpk= cloud.google.com/go/pubsublite v1.5.0/go.mod h1:xapqNQ1CuLfGi23Yda/9l4bBCKz/wC3KIJ5gKcxveZg= cloud.google.com/go/pubsublite v1.6.0/go.mod h1:1eFCS0U11xlOuMFV/0iBqw3zP12kddMeCbj/F3FSj9k= cloud.google.com/go/pubsublite v1.7.0/go.mod h1:8hVMwRXfDfvGm3fahVbtDbiLePT3gpoiJYJY+vxWxVM= cloud.google.com/go/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4= cloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o= cloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk= cloud.google.com/go/recaptchaenterprise/v2 v2.3.0/go.mod h1:O9LwGCjrhGHBQET5CA7dd5NwwNQUErSgEDit1DLNTdo= cloud.google.com/go/recaptchaenterprise/v2 v2.4.0/go.mod h1:Am3LHfOuBstrLrNCBrlI5sbwx9LBg3te2N6hGvHn2mE= cloud.google.com/go/recaptchaenterprise/v2 v2.5.0/go.mod h1:O8LzcHXN3rz0j+LBC91jrwI3R+1ZSZEWrfL7XHgNo9U= cloud.google.com/go/recaptchaenterprise/v2 v2.6.0/go.mod h1:RPauz9jeLtB3JVzg6nCbe12qNoaa8pXc4d/YukAmcnA= cloud.google.com/go/recaptchaenterprise/v2 v2.7.0/go.mod h1:19wVj/fs5RtYtynAPJdDTb69oW0vNHYDBTbB4NvMD9c= cloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg= cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4= cloud.google.com/go/recommendationengine v0.7.0/go.mod h1:1reUcE3GIu6MeBz/h5xZJqNLuuVjNg1lmWMPyjatzac= cloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg= cloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c= cloud.google.com/go/recommender v1.7.0/go.mod h1:XLHs/W+T8olwlGOgfQenXBTbIseGclClff6lhFVe9Bs= cloud.google.com/go/recommender v1.8.0/go.mod h1:PkjXrTT05BFKwxaUxQmtIlrtj0kph108r02ZZQ5FE70= cloud.google.com/go/recommender v1.9.0/go.mod h1:PnSsnZY7q+VL1uax2JWkt/UegHssxjUVVCrX52CuEmQ= cloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y= cloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A= cloud.google.com/go/redis v1.9.0/go.mod h1:HMYQuajvb2D0LvMgZmLDZW8V5aOC/WxstZHiy4g8OiA= cloud.google.com/go/redis v1.10.0/go.mod h1:ThJf3mMBQtW18JzGgh41/Wld6vnDDc/F/F35UolRZPM= cloud.google.com/go/redis v1.11.0/go.mod h1:/X6eicana+BWcUda5PpwZC48o37SiFVTFSs0fWAJ7uQ= cloud.google.com/go/resourcemanager v1.3.0/go.mod h1:bAtrTjZQFJkiWTPDb1WBjzvc6/kifjj4QBYuKCCoqKA= cloud.google.com/go/resourcemanager v1.4.0/go.mod h1:MwxuzkumyTX7/a3n37gmsT3py7LIXwrShilPh3P1tR0= cloud.google.com/go/resourcemanager v1.5.0/go.mod h1:eQoXNAiAvCf5PXxWxXjhKQoTMaUSNrEfg+6qdf/wots= cloud.google.com/go/resourcemanager v1.6.0/go.mod h1:YcpXGRs8fDzcUl1Xw8uOVmI8JEadvhRIkoXXUNVYcVo= cloud.google.com/go/resourcemanager v1.7.0/go.mod h1:HlD3m6+bwhzj9XCouqmeiGuni95NTrExfhoSrkC/3EI= cloud.google.com/go/resourcesettings v1.3.0/go.mod h1:lzew8VfESA5DQ8gdlHwMrqZs1S9V87v3oCnKCWoOuQU= cloud.google.com/go/resourcesettings v1.4.0/go.mod h1:ldiH9IJpcrlC3VSuCGvjR5of/ezRrOxFtpJoJo5SmXg= cloud.google.com/go/resourcesettings v1.5.0/go.mod h1:+xJF7QSG6undsQDfsCJyqWXyBwUoJLhetkRMDRnIoXA= cloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4= cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY= cloud.google.com/go/retail v1.10.0/go.mod h1:2gDk9HsL4HMS4oZwz6daui2/jmKvqShXKQuB2RZ+cCc= cloud.google.com/go/retail v1.11.0/go.mod h1:MBLk1NaWPmh6iVFSz9MeKG/Psyd7TAgm6y/9L2B4x9Y= cloud.google.com/go/retail v1.12.0/go.mod h1:UMkelN/0Z8XvKymXFbD4EhFJlYKRx1FGhQkVPU5kF14= cloud.google.com/go/run v0.2.0/go.mod h1:CNtKsTA1sDcnqqIFR3Pb5Tq0usWxJJvsWOCPldRU3Do= cloud.google.com/go/run v0.3.0/go.mod h1:TuyY1+taHxTjrD0ZFk2iAR+xyOXEA0ztb7U3UNA0zBo= cloud.google.com/go/run v0.8.0/go.mod h1:VniEnuBwqjigv0A7ONfQUaEItaiCRVujlMqerPPiktM= cloud.google.com/go/run v0.9.0/go.mod h1:Wwu+/vvg8Y+JUApMwEDfVfhetv30hCG4ZwDR/IXl2Qg= cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s= cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI= cloud.google.com/go/scheduler v1.6.0/go.mod h1:SgeKVM7MIwPn3BqtcBntpLyrIJftQISRrYB5ZtT+KOk= cloud.google.com/go/scheduler v1.7.0/go.mod h1:jyCiBqWW956uBjjPMMuX09n3x37mtyPJegEWKxRsn44= cloud.google.com/go/scheduler v1.8.0/go.mod h1:TCET+Y5Gp1YgHT8py4nlg2Sew8nUHMqcpousDgXJVQc= cloud.google.com/go/scheduler v1.9.0/go.mod h1:yexg5t+KSmqu+njTIh3b7oYPheFtBWGcbVUYF1GGMIc= cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA= cloud.google.com/go/secretmanager v1.8.0/go.mod h1:hnVgi/bN5MYHd3Gt0SPuTPPp5ENina1/LxM+2W9U9J4= cloud.google.com/go/secretmanager v1.9.0/go.mod h1:b71qH2l1yHmWQHt9LC80akm86mX8AL6X1MA01dW8ht4= cloud.google.com/go/secretmanager v1.10.0/go.mod h1:MfnrdvKMPNra9aZtQFvBcvRU54hbPD8/HayQdlUgJpU= cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4= cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0= cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU= cloud.google.com/go/security v1.9.0/go.mod h1:6Ta1bO8LXI89nZnmnsZGp9lVoVWXqsVbIq/t9dzI+2Q= cloud.google.com/go/security v1.10.0/go.mod h1:QtOMZByJVlibUT2h9afNDWRZ1G96gVywH8T5GUSb9IA= cloud.google.com/go/security v1.12.0/go.mod h1:rV6EhrpbNHrrxqlvW0BWAIawFWq3X90SduMJdFwtLB8= cloud.google.com/go/security v1.13.0/go.mod h1:Q1Nvxl1PAgmeW0y3HTt54JYIvUdtcpYKVfIB8AOMZ+0= cloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU= cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc= cloud.google.com/go/securitycenter v1.15.0/go.mod h1:PeKJ0t8MoFmmXLXWm41JidyzI3PJjd8sXWaVqg43WWk= cloud.google.com/go/securitycenter v1.16.0/go.mod h1:Q9GMaLQFUD+5ZTabrbujNWLtSLZIZF7SAR0wWECrjdk= cloud.google.com/go/securitycenter v1.18.1/go.mod h1:0/25gAzCM/9OL9vVx4ChPeM/+DlfGQJDwBy/UC8AKK0= cloud.google.com/go/securitycenter v1.19.0/go.mod h1:LVLmSg8ZkkyaNy4u7HCIshAngSQ8EcIRREP3xBnyfag= cloud.google.com/go/servicecontrol v1.4.0/go.mod h1:o0hUSJ1TXJAmi/7fLJAedOovnujSEvjKCAFNXPQ1RaU= cloud.google.com/go/servicecontrol v1.5.0/go.mod h1:qM0CnXHhyqKVuiZnGKrIurvVImCs8gmqWsDoqe9sU1s= cloud.google.com/go/servicecontrol v1.10.0/go.mod h1:pQvyvSRh7YzUF2efw7H87V92mxU8FnFDawMClGCNuAA= cloud.google.com/go/servicecontrol v1.11.0/go.mod h1:kFmTzYzTUIuZs0ycVqRHNaNhgR+UMUpw9n02l/pY+mc= cloud.google.com/go/servicecontrol v1.11.1/go.mod h1:aSnNNlwEFBY+PWGQ2DoM0JJ/QUXqV5/ZD9DOLB7SnUk= cloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs= cloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg= cloud.google.com/go/servicedirectory v1.6.0/go.mod h1:pUlbnWsLH9c13yGkxCmfumWEPjsRs1RlmJ4pqiNjVL4= cloud.google.com/go/servicedirectory v1.7.0/go.mod h1:5p/U5oyvgYGYejufvxhgwjL8UVXjkuw7q5XcG10wx1U= cloud.google.com/go/servicedirectory v1.8.0/go.mod h1:srXodfhY1GFIPvltunswqXpVxFPpZjf8nkKQT7XcXaY= cloud.google.com/go/servicedirectory v1.9.0/go.mod h1:29je5JjiygNYlmsGz8k6o+OZ8vd4f//bQLtvzkPPT/s= cloud.google.com/go/servicemanagement v1.4.0/go.mod h1:d8t8MDbezI7Z2R1O/wu8oTggo3BI2GKYbdG4y/SJTco= cloud.google.com/go/servicemanagement v1.5.0/go.mod h1:XGaCRe57kfqu4+lRxaFEAuqmjzF0r+gWHjWqKqBvKFo= cloud.google.com/go/servicemanagement v1.6.0/go.mod h1:aWns7EeeCOtGEX4OvZUWCCJONRZeFKiptqKf1D0l/Jc= cloud.google.com/go/servicemanagement v1.8.0/go.mod h1:MSS2TDlIEQD/fzsSGfCdJItQveu9NXnUniTrq/L8LK4= cloud.google.com/go/serviceusage v1.3.0/go.mod h1:Hya1cozXM4SeSKTAgGXgj97GlqUvF5JaoXacR1JTP/E= cloud.google.com/go/serviceusage v1.4.0/go.mod h1:SB4yxXSaYVuUBYUml6qklyONXNLt83U0Rb+CXyhjEeU= cloud.google.com/go/serviceusage v1.5.0/go.mod h1:w8U1JvqUqwJNPEOTQjrMHkw3IaIFLoLsPLvsE3xueec= cloud.google.com/go/serviceusage v1.6.0/go.mod h1:R5wwQcbOWsyuOfbP9tGdAnCAc6B9DRwPG1xtWMDeuPA= cloud.google.com/go/shell v1.3.0/go.mod h1:VZ9HmRjZBsjLGXusm7K5Q5lzzByZmJHf1d0IWHEN5X4= cloud.google.com/go/shell v1.4.0/go.mod h1:HDxPzZf3GkDdhExzD/gs8Grqk+dmYcEjGShZgYa9URw= cloud.google.com/go/shell v1.6.0/go.mod h1:oHO8QACS90luWgxP3N9iZVuEiSF84zNyLytb+qE2f9A= cloud.google.com/go/spanner v1.41.0/go.mod h1:MLYDBJR/dY4Wt7ZaMIQ7rXOTLjYrmxLE/5ve9vFfWos= cloud.google.com/go/spanner v1.44.0/go.mod h1:G8XIgYdOK+Fbcpbs7p2fiprDw4CaZX63whnSMLVBxjk= cloud.google.com/go/spanner v1.45.0/go.mod h1:FIws5LowYz8YAE1J8fOS7DJup8ff7xJeetWEo5REA2M= cloud.google.com/go/spanner v1.67.0 h1:h8xfobxh5lQu4qJVMPH+wSiyU+ZM6ZTxRNqGeu9iIVA= cloud.google.com/go/spanner v1.67.0/go.mod h1:Um+TNmxfcCHqNCKid4rmAMvoe/Iu1vdz6UfxJ9GPxRQ= cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM= cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ= cloud.google.com/go/speech v1.8.0/go.mod h1:9bYIl1/tjsAnMgKGHKmBZzXKEkGgtU+MpdDPTE9f7y0= cloud.google.com/go/speech v1.9.0/go.mod h1:xQ0jTcmnRFFM2RfX/U+rk6FQNUF6DQlydUSyoooSpco= cloud.google.com/go/speech v1.14.1/go.mod h1:gEosVRPJ9waG7zqqnsHpYTOoAS4KouMRLDFMekpJ0J0= cloud.google.com/go/speech v1.15.0/go.mod h1:y6oH7GhqCaZANH7+Oe0BhgIogsNInLlz542tg3VqeYI= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y= cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4= cloud.google.com/go/storage v1.43.0 h1:CcxnSohZwizt4LCzQHWvBf1/kvtHUn7gk9QERXPyXFs= cloud.google.com/go/storage v1.43.0/go.mod h1:ajvxEa7WmZS1PxvKRq4bq0tFT3vMd502JwstCcYv0Q0= cloud.google.com/go/storagetransfer v1.5.0/go.mod h1:dxNzUopWy7RQevYFHewchb29POFv3/AaBgnhqzqiK0w= cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I= cloud.google.com/go/storagetransfer v1.7.0/go.mod h1:8Giuj1QNb1kfLAiWM1bN6dHzfdlDAVC9rv9abHot2W4= cloud.google.com/go/storagetransfer v1.8.0/go.mod h1:JpegsHHU1eXg7lMHkvf+KE5XDJ7EQu0GwNJbbVGanEw= cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= cloud.google.com/go/talent v1.3.0/go.mod h1:CmcxwJ/PKfRgd1pBjQgU6W3YBwiewmUzQYH5HHmSCmM= cloud.google.com/go/talent v1.4.0/go.mod h1:ezFtAgVuRf8jRsvyE6EwmbTK5LKciD4KVnHuDEFmOOA= cloud.google.com/go/talent v1.5.0/go.mod h1:G+ODMj9bsasAEJkQSzO2uHQWXHHXUomArjWQQYkqK6c= cloud.google.com/go/texttospeech v1.4.0/go.mod h1:FX8HQHA6sEpJ7rCMSfXuzBcysDAuWusNNNvN9FELDd8= cloud.google.com/go/texttospeech v1.5.0/go.mod h1:oKPLhR4n4ZdQqWKURdwxMy0uiTS1xU161C8W57Wkea4= cloud.google.com/go/texttospeech v1.6.0/go.mod h1:YmwmFT8pj1aBblQOI3TfKmwibnsfvhIBzPXcW4EBovc= cloud.google.com/go/tpu v1.3.0/go.mod h1:aJIManG0o20tfDQlRIej44FcwGGl/cD0oiRyMKG19IQ= cloud.google.com/go/tpu v1.4.0/go.mod h1:mjZaX8p0VBgllCzF6wcU2ovUXN9TONFLd7iz227X2Xg= cloud.google.com/go/tpu v1.5.0/go.mod h1:8zVo1rYDFuW2l4yZVY0R0fb/v44xLh3llq7RuV61fPM= cloud.google.com/go/trace v1.3.0/go.mod h1:FFUE83d9Ca57C+K8rDl/Ih8LwOzWIV1krKgxg6N0G28= cloud.google.com/go/trace v1.4.0/go.mod h1:UG0v8UBqzusp+z63o7FK74SdFE+AXpCLdFb1rshXG+Y= cloud.google.com/go/trace v1.8.0/go.mod h1:zH7vcsbAhklH8hWFig58HvxcxyQbaIqMarMg9hn5ECA= cloud.google.com/go/trace v1.9.0/go.mod h1:lOQqpE5IaWY0Ixg7/r2SjixMuc6lfTFeO4QGM4dQWOk= cloud.google.com/go/trace v1.10.11 h1:+Y1emOgcyGy6OdJ2KQbT4t2oecPp49GtJn8j3GM1pWo= cloud.google.com/go/trace v1.10.11/go.mod h1:fUr5L3wSXerNfT0f1bBg08W4axS2VbHGgYcfH4KuTXU= cloud.google.com/go/translate v1.3.0/go.mod h1:gzMUwRjvOqj5i69y/LYLd8RrNQk+hOmIXTi9+nb3Djs= cloud.google.com/go/translate v1.4.0/go.mod h1:06Dn/ppvLD6WvA5Rhdp029IX2Mi3Mn7fpMRLPvXT5Wg= cloud.google.com/go/translate v1.5.0/go.mod h1:29YDSYveqqpA1CQFD7NQuP49xymq17RXNaUDdc0mNu0= cloud.google.com/go/translate v1.6.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= cloud.google.com/go/translate v1.7.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= cloud.google.com/go/video v1.8.0/go.mod h1:sTzKFc0bUSByE8Yoh8X0mn8bMymItVGPfTuUBUyRgxk= cloud.google.com/go/video v1.9.0/go.mod h1:0RhNKFRF5v92f8dQt0yhaHrEuH95m068JYOvLZYnJSw= cloud.google.com/go/video v1.12.0/go.mod h1:MLQew95eTuaNDEGriQdcYn0dTwf9oWiA4uYebxM5kdg= cloud.google.com/go/video v1.13.0/go.mod h1:ulzkYlYgCp15N2AokzKjy7MQ9ejuynOJdf1tR5lGthk= cloud.google.com/go/video v1.14.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= cloud.google.com/go/video v1.15.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= cloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4= cloud.google.com/go/videointelligence v1.8.0/go.mod h1:dIcCn4gVDdS7yte/w+koiXn5dWVplOZkE+xwG9FgK+M= cloud.google.com/go/videointelligence v1.9.0/go.mod h1:29lVRMPDYHikk3v8EdPSaL8Ku+eMzDljjuvRs105XoU= cloud.google.com/go/videointelligence v1.10.0/go.mod h1:LHZngX1liVtUhZvi2uNS0VQuOzNi2TkY1OakiuoUOjU= cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0= cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo= cloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo= cloud.google.com/go/vision/v2 v2.4.0/go.mod h1:VtI579ll9RpVTrdKdkMzckdnwMyX2JILb+MhPqRbPsY= cloud.google.com/go/vision/v2 v2.5.0/go.mod h1:MmaezXOOE+IWa+cS7OhRRLK2cNv1ZL98zhqFFZaaH2E= cloud.google.com/go/vision/v2 v2.6.0/go.mod h1:158Hes0MvOS9Z/bDMSFpjwsUrZ5fPrdwuyyvKSGAGMY= cloud.google.com/go/vision/v2 v2.7.0/go.mod h1:H89VysHy21avemp6xcf9b9JvZHVehWbET0uT/bcuY/0= cloud.google.com/go/vmmigration v1.2.0/go.mod h1:IRf0o7myyWFSmVR1ItrBSFLFD/rJkfDCUTO4vLlJvsE= cloud.google.com/go/vmmigration v1.3.0/go.mod h1:oGJ6ZgGPQOFdjHuocGcLqX4lc98YQ7Ygq8YQwHh9A7g= cloud.google.com/go/vmmigration v1.5.0/go.mod h1:E4YQ8q7/4W9gobHjQg4JJSgXXSgY21nA5r8swQV+Xxc= cloud.google.com/go/vmmigration v1.6.0/go.mod h1:bopQ/g4z+8qXzichC7GW1w2MjbErL54rk3/C843CjfY= cloud.google.com/go/vmwareengine v0.1.0/go.mod h1:RsdNEf/8UDvKllXhMz5J40XxDrNJNN4sagiox+OI208= cloud.google.com/go/vmwareengine v0.2.2/go.mod h1:sKdctNJxb3KLZkE/6Oui94iw/xs9PRNC2wnNLXsHvH8= cloud.google.com/go/vmwareengine v0.3.0/go.mod h1:wvoyMvNWdIzxMYSpH/R7y2h5h3WFkx6d+1TIsP39WGY= cloud.google.com/go/vpcaccess v1.4.0/go.mod h1:aQHVbTWDYUR1EbTApSVvMq1EnT57ppDmQzZ3imqIk4w= cloud.google.com/go/vpcaccess v1.5.0/go.mod h1:drmg4HLk9NkZpGfCmZ3Tz0Bwnm2+DKqViEpeEpOq0m8= cloud.google.com/go/vpcaccess v1.6.0/go.mod h1:wX2ILaNhe7TlVa4vC5xce1bCnqE3AeH27RV31lnmZes= cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE= cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg= cloud.google.com/go/webrisk v1.6.0/go.mod h1:65sW9V9rOosnc9ZY7A7jsy1zoHS5W9IAXv6dGqhMQMc= cloud.google.com/go/webrisk v1.7.0/go.mod h1:mVMHgEYH0r337nmt1JyLthzMr6YxwN1aAIEc2fTcq7A= cloud.google.com/go/webrisk v1.8.0/go.mod h1:oJPDuamzHXgUc+b8SiHRcVInZQuybnvEW72PqTc7sSg= cloud.google.com/go/websecurityscanner v1.3.0/go.mod h1:uImdKm2wyeXQevQJXeh8Uun/Ym1VqworNDlBXQevGMo= cloud.google.com/go/websecurityscanner v1.4.0/go.mod h1:ebit/Fp0a+FWu5j4JOmJEV8S8CzdTkAS77oDsiSqYWQ= cloud.google.com/go/websecurityscanner v1.5.0/go.mod h1:Y6xdCPy81yi0SQnDY1xdNTNpfY1oAgXUlcfN3B3eSng= cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M= cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA= cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw= contrib.go.opencensus.io/exporter/stackdriver v0.13.14 h1:zBakwHardp9Jcb8sQHcHpXy/0+JIb1M8KjigCJzx7+4= contrib.go.opencensus.io/exporter/stackdriver v0.13.14/go.mod h1:5pSSGY0Bhuk7waTHuDf4aQ8D2DrhgETRo9fy6k3Xlzc= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.0 h1:oVLqHXhnYtUwM89y9T1fXGaK9wTkXHgNp8/ZNMQzUxE= github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.0/go.mod h1:dppbR7CwXD4pgtV9t3wD1812RaLDcBjtblcDF5f1vI0= github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60= github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY= github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0= github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4xei5aX110hRiI= github.com/apache/beam/sdks/v2 v2.58.1 h1:bx0nCi3q3o9TcMGT8/5wWnuf7oCm/46DrSRVE/uVhsU= github.com/apache/beam/sdks/v2 v2.58.1/go.mod h1:jo2HHkE4jRS0lZSkUK2zyEun5Lj5U+HxvI6B/vAuIlA= github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= github.com/avast/retry-go/v4 v4.6.0 h1:K9xNA+KeB8HHc2aWFuLb25Offp+0iVRXEvFx8IinRJA= github.com/avast/retry-go/v4 v4.6.0/go.mod h1:gvWlPhBVsvBbLkVGDg/KwvBv0bEkCOLRRSHKIr2PyOE= github.com/aws/aws-sdk-go v1.51.8 h1:tD7gQq5XKuKdhA6UMEH26ZNQH0s+HbL95rzv/ACz5TQ= github.com/aws/aws-sdk-go v1.51.8/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/bufbuild/protocompile v0.10.0 h1:+jW/wnLMLxaCEG8AX9lD0bQ5v9h1RUiMKOBOT5ll9dM= github.com/bufbuild/protocompile v0.10.0/go.mod h1:G9qQIQo0xZ6Uyj6CMNz0saGmx2so+KONo8/KrELABiY= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b h1:ga8SEFjZ60pxLcmhnThWgvH2wg8376yUJmPhEH4H3kw= github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/cockroach-go/v2 v2.3.8 h1:53yoUo4+EtrC1NrAEgnnad4AS3ntNvGup1PAXZ7UmpE= github.com/cockroachdb/cockroach-go/v2 v2.3.8/go.mod h1:9uH5jK4yQ3ZQUT9IXe4I2fHzMIF5+JC/oOdzTRgJYJk= github.com/cockroachdb/datadriven v1.0.2 h1:H9MtNqVoVhvd9nCBwOyDjUEdZCREqbIdCJD93PBm/jA= github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/docker/docker v25.0.6+incompatible h1:5cPwbwriIcsua2REJe8HqQV+6WlWc1byg2QSXzBxBGg= github.com/docker/docker v25.0.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJJM//w9BV6Fxbg2LuVd34= github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f/go.mod h1:sfYdkwUW4BA3PbKjySwjJy+O4Pu0h62rlqCMHNk+K+Q= github.com/envoyproxy/go-control-plane v0.12.0 h1:4X+VP1GHd1Mhj6IB5mMeGbLCleqxjletLK6K0rbxyZI= github.com/envoyproxy/go-control-plane v0.12.0/go.mod h1:ZBTaoJ23lqITozF0M6G4/IragXCQKCnYbmlmtHvwRG0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A= github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fsouza/fake-gcs-server v1.47.7 h1:56/U4rKY081TaNbq0gHWi7/71UxC2KROqcnrD9BRJhs= github.com/fsouza/fake-gcs-server v1.47.7/go.mod h1:4vPUynN8/zZlxk5Jpy6LvvTTxItdTAObK4DYnp89Jys= github.com/fullstorydev/grpcurl v1.9.1 h1:YxX1aCcCc4SDBQfj9uoWcTLe8t4NWrZe1y+mk83BQgo= github.com/fullstorydev/grpcurl v1.9.1/go.mod h1:i8gKLIC6s93WdU3LSmkE5vtsCxyRmihUj5FK1cNW5EM= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= github.com/go-fonts/liberation v0.2.0/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg= github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-licenses/v2 v2.0.0-alpha.1 h1:2EMzW/1PWYvgOxBXsWl7b350vI0c/kf5Fh7z4AR1skM= github.com/google/go-licenses/v2 v2.0.0-alpha.1/go.mod h1:HlMUpsa+mbs8EqdlY0BDfCn0ZK7Y7NQoRCGYhNoox64= github.com/google/go-replayers/httpreplay v1.2.0 h1:VM1wEyyjaoU53BwrOnaf9VhAyQQEEioJvFYxYcLRKzk= github.com/google/go-replayers/httpreplay v1.2.0/go.mod h1:WahEFFZZ7a1P4VM1qEeHy+tME4bwyqPcwWbNlUI1Mcg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/licenseclassifier/v2 v2.0.0 h1:1Y57HHILNf4m0ABuMVb6xk4vAJYEUO0gDxNpog0pyeA= github.com/google/licenseclassifier/v2 v2.0.0/go.mod h1:cOjbdH0kyC9R22sdQbYsFkto4NGCAc+ZSwbeThazEtM= github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc= github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20240320155624-b11c3daa6f07 h1:57oOH2Mu5Nw16KnZAVLdlUjmPH/TSYCKTJgG0OVfX0Y= github.com/google/pprof v0.0.0-20240320155624-b11c3daa6f07/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg= github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4= github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM= github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= github.com/googleapis/gax-go/v2 v2.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDPT0hH1s= github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A= github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE= github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 h1:/c3QmbOGMGTOumP2iT/rCwB7b0QDGLKzqOmktBjT+Is= github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1/go.mod h1:5SN9VR2LTsRFsrEC6FHgRbTWrTHu6tqPeKxEQv15giM= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.2.0 h1:yPeWdRnmynF7p+lLYz0H2tthW9lqhMJrQV/U7yy4wX0= github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4= github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= github.com/jackc/pgconn v1.14.3 h1:bVoTr12EGANZz66nZPkMInAV/KHD2TxH9npjXXgiB3w= github.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM= github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc= github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag= github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA= github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= github.com/jackc/pgtype v1.14.3 h1:h6W9cPuHsRWQFTWUZMAKMgG5jSwQI0Zurzdvlx3Plus= github.com/jackc/pgtype v1.14.3/go.mod h1:aKeozOde08iifGosdJpz9MBZonJOUJxqNpPBcMJTlVA= github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= github.com/jackc/pgx/v4 v4.18.2/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= github.com/jackc/pgx/v4 v4.18.3 h1:dE2/TrEsGX3RBprb3qryqSV9Y60iZN1C6i8IrmW9/BA= github.com/jackc/pgx/v4 v4.18.3/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.3.0 h1:eHK/5clGOatcjX3oWGBO/MpxpbHzSwud5EWTSCI+MX0= github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jhump/protoreflect v1.16.0 h1:54fZg+49widqXYQ0b+usAFHbMkBGR4PpXrsHc8+TBDg= github.com/jhump/protoreflect v1.16.0/go.mod h1:oYPd7nPvcBw/5wlDfm/AVmU9zH9BgqGCI469pGxfj/8= github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24 h1:liMMTbpW34dhU4az1GN0pTPADwNmvoRSeoZ6PItiqnY= github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4= github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/letsencrypt/pkcs11key/v4 v4.0.0 h1:qLc/OznH7xMr5ARJgkZCCWk+EomQkiNTOoOF5LAgagc= github.com/letsencrypt/pkcs11key/v4 v4.0.0/go.mod h1:EFUvBDay26dErnNb70Nd0/VW3tJiIbETBPTl9ATXQag= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= github.com/lyft/protoc-gen-star/v2 v2.0.1/go.mod h1:RcCdONR2ScXaYnQC5tUzxzlpA3WVYF7/opLeUgcQs/o= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU= github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.1 h1:FVzMWA5RllMAKIdUSC8mdWo3XtwoecrH79BY70sEEpE= github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-proto-validators v0.2.0 h1:F6LFfmgVnfULfaRsQWBbe7F7ocuHCr9+7m+GAeDzNbQ= github.com/mwitkow/go-proto-validators v0.2.0/go.mod h1:ZfA1hW+UH/2ZHOWvQ3HnQaU0DtnpXu850MZiy+YUgcc= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg= github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU= github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w= github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks= github.com/otiai10/mint v1.5.1/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM= github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pkg/xattr v0.4.9 h1:5883YPCtkSd8LFbs13nXplj9g9tlrwoJRjgpgMu1/fE= github.com/pkg/xattr v0.4.9/go.mod h1:di8WF84zAKk8jzR1UBTEWh9AUlIZZ7M/JNt8e9B6ktU= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v1.20.2 h1:5ctymQzZlyOON1666svgwn3s6IKWgfbjsejTMiXIyjg= github.com/prometheus/client_golang v1.20.2/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/prometheus/prometheus v0.51.0 h1:aRdjTnmHLved29ILtdzZN2GNvOjWATtA/z+3fYuexOc= github.com/prometheus/prometheus v0.51.0/go.mod h1:yv4MwOn3yHMQ6MZGHPg/U7Fcyqf+rxqiZfSur6myVtc= github.com/pseudomuto/protoc-gen-doc v1.5.1 h1:Ah259kcrio7Ix1Rhb6u8FCaOkzf9qRBqXnvAufg061w= github.com/pseudomuto/protoc-gen-doc v1.5.1/go.mod h1:XpMKYg6zkcpgfpCfQ8GcWBDRtRxOmMR5w7pz4Xo+dYM= github.com/pseudomuto/protokit v0.2.0 h1:hlnBDcy3YEDXH7kc9gV+NLaN0cDzhDvD1s7Y6FZ8RpM= github.com/pseudomuto/protokit v0.2.0/go.mod h1:2PdH30hxVHsup8KpBTOXTBeMVhJZVio3Q8ViKSAXT0Q= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 h1:6fotK7otjonDflCTK0BCfls4SPy3NcCVb5dqqmbRknE= github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk= github.com/transparency-dev/merkle v0.0.2 h1:Q9nBoQcZcgPamMkGn7ghV8XiTZ/kRxn1yCG81+twTK4= github.com/transparency-dev/merkle v0.0.2/go.mod h1:pqSy+OXefQ1EDUVmAJ8MUhHB9TXGuzVAT58PqBoHz1A= github.com/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk= github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA= github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510 h1:S2dVYn90KE98chqDkyE9Z4N61UnQd+KOfgp5Iu53llk= github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= go.etcd.io/bbolt v1.3.10 h1:+BqfJTcCzTItrop8mq/lbzL8wSGtj94UO/3U31shqG0= go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ= go.etcd.io/etcd/api/v3 v3.5.15 h1:3KpLJir1ZEBrYuV2v+Twaa/e2MdDCEZ/70H+lzEiwsk= go.etcd.io/etcd/api/v3 v3.5.15/go.mod h1:N9EhGzXq58WuMllgH9ZvnEr7SI9pS0k0+DHZezGp7jM= go.etcd.io/etcd/client/pkg/v3 v3.5.15 h1:fo0HpWz/KlHGMCC+YejpiCmyWDEuIpnTDzpJLB5fWlA= go.etcd.io/etcd/client/pkg/v3 v3.5.15/go.mod h1:mXDI4NAOwEiszrHCb0aqfAYNCrZP4e9hRca3d1YK8EU= go.etcd.io/etcd/client/v2 v2.305.15 h1:VG2xbf8Vz1KJh65Ar2V5eDmfkp1bpzkSEHlhJM3usp8= go.etcd.io/etcd/client/v2 v2.305.15/go.mod h1:Ad5dRjPVb/n5yXgAWQ/hXzuXXkBk0Y658ocuXYaUU48= go.etcd.io/etcd/client/v3 v3.5.15 h1:23M0eY4Fd/inNv1ZfU3AxrbbOdW79r9V9Rl62Nm6ip4= go.etcd.io/etcd/client/v3 v3.5.15/go.mod h1:CLSJxrYjvLtHsrPKsy7LmZEE+DK2ktfd2bN4RhBMwlU= go.etcd.io/etcd/etcdctl/v3 v3.5.15 h1:7zUC+X/N0tbffJ6VIcNU9GEOc0WBXUunSDXvBaH0pjM= go.etcd.io/etcd/etcdctl/v3 v3.5.15/go.mod h1:aHrtvS6FXVU3inBSBZHgzroUhA3Pao9PCfdzwHWR2hk= go.etcd.io/etcd/etcdutl/v3 v3.5.15 h1:EBMtdngexC5s65NY4QKr7dCpXmzdfSVnnueJ4URg6vY= go.etcd.io/etcd/etcdutl/v3 v3.5.15/go.mod h1:4Kia4UPkWnD+qrUodawwd1ZcvteGTW97BpXI5zkSUS4= go.etcd.io/etcd/pkg/v3 v3.5.15 h1:/Iu6Sr3iYaAjy++8sIDoZW9/EfhcwLZwd4FOZX2mMOU= go.etcd.io/etcd/pkg/v3 v3.5.15/go.mod h1:e3Acf298sPFmTCGTrnGvkClEw9RYIyPtNzi1XM8rets= go.etcd.io/etcd/raft/v3 v3.5.15 h1:jOA2HJF7zb3wy8H/pL13e8geWqkEa/kUs0waUggZC0I= go.etcd.io/etcd/raft/v3 v3.5.15/go.mod h1:k3r7P4seEiUcgxOPLp+mloJWV3Q4QLPGNvy/OgC8OtM= go.etcd.io/etcd/server/v3 v3.5.15 h1:x35jrWnZgsRwMsFsUJIUdT1bvzIz1B+29HjMfRYVN/E= go.etcd.io/etcd/server/v3 v3.5.15/go.mod h1:l9jX9oa/iuArjqz0RNX/TDbc70dLXxRZo/nmPucrpFo= go.etcd.io/etcd/tests/v3 v3.5.15 h1:uB+/LC87TVtvfZId9Dwf18rMTDZSDhRQOAIKZEqUy4k= go.etcd.io/etcd/tests/v3 v3.5.15/go.mod h1:ZNLelksKfGuvHLSHam4Oa4zPYUldh0Rbv2IWAcKGb64= go.etcd.io/etcd/v3 v3.5.15 h1:gUdDHm4PbA0kl4EdYt1hEH1lkmROYZDcvP25W5j+8is= go.etcd.io/etcd/v3 v3.5.15/go.mod h1:4OAgDV9NIESiRvw0XCbp460ivPAw/5O4OV7jsdVhR60= go.etcd.io/gofail v0.1.0 h1:XItAMIhOojXFQMgrxjnd2EIIHun/d5qL0Pf7FzVTkFg= go.etcd.io/gofail v0.1.0/go.mod h1:VZBCXYGZhHAinaBiiqYvuDynvahNsAyLFwB3kEHKz1M= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 h1:t6wl9SPayj+c7lEIFgm4ooDBZVb01IhLB4InpomhRw8= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0/go.mod h1:iSDOcsnSA5INXzZtwaBPrKp/lWu/V14Dd+llD0oI2EA= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0 h1:Mw5xcxMwlqoJd97vwPxA8isEaIoxsta9/Q51+TTJLGE= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0/go.mod h1:CQNu9bj7o7mC6U7+CA/schKEYakYXWr79ucDHTMGhCM= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0 h1:Xw8U6u2f8DK2XAkGRFV7BBLENgnTGX9i4rQRxJf+/vs= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0/go.mod h1:6KW1Fm6R/s6Z3PGXwSJN2K4eT6wQB3vXX6CVnYX9NmM= go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw= go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg= go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.opentelemetry.io/proto/otlp v1.1.0 h1:2Di21piLrCqJ3U3eXGCTPHE9R8Nh+0uglSnOyxikMeI= go.opentelemetry.io/proto/otlp v1.1.0/go.mod h1:GpBHCBWiqvVLDqmHZsoMM3C5ySeKTC7ej/RNTae6MdY= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ= golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw= golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20210607152325-775e3b0c77b9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= golang.org/x/image v0.0.0-20220302094943-723b81ca9867/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 h1:LLhsEBxRTBLuKlQxFBYUOU8xyFgXv6cOTp2HASDlsDk= golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY= gonum.org/v1/plot v0.10.1/go.mod h1:VZW5OlhkL1mysU9vaqNHnsy86inf6Ot+jB3r+BczCEo= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g= google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI= google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= google.golang.org/api v0.99.0/go.mod h1:1YOf74vkVndF7pG6hIHuINsM7eWwpVTAfNMNiL91A08= google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= google.golang.org/api v0.102.0/go.mod h1:3VFl6/fzoA+qNuS1N1/VfXY4LjoXN/wzeIp7TweWwGo= google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0= google.golang.org/api v0.106.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= google.golang.org/api v0.107.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI= google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0= google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg= google.golang.org/api v0.194.0 h1:dztZKG9HgtIpbI35FhfuSNR/zmaMVdxNlntHj1sIS4s= google.golang.org/api v0.194.0/go.mod h1:AgvUFdojGANh3vI+P7EVnxj3AISHllxGCJSFmggmnd0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= google.golang.org/genproto v0.0.0-20220329172620-7be39ac1afc7/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE= google.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc= google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= google.golang.org/genproto v0.0.0-20220829144015-23454907ede3/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= google.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw= google.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U= google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= google.golang.org/genproto v0.0.0-20221024153911-1573dae28c9c/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo= google.golang.org/genproto v0.0.0-20221109142239-94d6d90a7d66/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= google.golang.org/genproto v0.0.0-20221114212237-e4508ebdbee1/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= google.golang.org/genproto v0.0.0-20221117204609-8f9c96812029/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= google.golang.org/genproto v0.0.0-20221201204527-e3fa12d562f3/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE= google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= google.golang.org/genproto v0.0.0-20230112194545-e10362b5ecf9/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= google.golang.org/genproto v0.0.0-20230113154510-dbe35b8444a5/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= google.golang.org/genproto v0.0.0-20230123190316-2c411cf9d197/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= google.golang.org/genproto v0.0.0-20230124163310-31e0e69b6fc2/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= google.golang.org/genproto v0.0.0-20230125152338-dcaf20b6aeaa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= google.golang.org/genproto v0.0.0-20230127162408-596548ed4efa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= google.golang.org/genproto v0.0.0-20230216225411-c8e22ba71e44/go.mod h1:8B0gmkoRebU8ukX6HP+4wrVQUY1+6PkQ44BSyIlflHA= google.golang.org/genproto v0.0.0-20230222225845-10f96fb3dbec/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= google.golang.org/genproto v0.0.0-20230223222841-637eb2293923/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= google.golang.org/genproto v0.0.0-20230303212802-e74f57abe488/go.mod h1:TvhZT5f700eVlTNwND1xoEZQeWTB2RY/65kplwl/bFA= google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= google.golang.org/genproto v0.0.0-20230320184635-7606e756e683/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= google.golang.org/genproto v0.0.0-20230323212658-478b75c54725/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= google.golang.org/genproto v0.0.0-20230330154414-c0448cd141ea/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= google.golang.org/genproto v0.0.0-20240814211410-ddb44dafa142 h1:oLiyxGgE+rt22duwci1+TG7bg2/L1LQsXwfjPlmuJA0= google.golang.org/genproto v0.0.0-20240814211410-ddb44dafa142/go.mod h1:G11eXq53iI5Q+kyNOmCvnzBaxEA2Q/Ik5Tj7nqBE8j4= google.golang.org/genproto/googleapis/api v0.0.0-20240730163845-b1a4ccb954bf h1:GillM0Ef0pkZPIB+5iO6SDK+4T9pf6TpaYR6ICD5rVE= google.golang.org/genproto/googleapis/api v0.0.0-20240730163845-b1a4ccb954bf/go.mod h1:OFMYQFHJ4TM3JRlWDZhJbZfra2uqc3WLBZiaaqP4DtU= google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs= google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= google.golang.org/grpc v1.52.3/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 h1:F29+wU6Ee6qgu9TddPgooOdaqsxTMunOoj8KA5yuS5A= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1/go.mod h1:5KF+wpkbTSbGcR9zteSqZV6fqFOWBl4Yde8En8MryZA= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/cheggaaa/pb.v1 v1.0.28 h1:n1tBJnnK2r7g9OW2btFH91V92STTUevLXYFb8gy9EMk= gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= modernc.org/cc/v3 v3.36.2/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= modernc.org/cc/v3 v3.36.3/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc= modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw= modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= modernc.org/ccgo/v3 v3.16.8/go.mod h1:zNjwkizS+fIFDrDjIAgBSCLkWbJuHF+ar3QRn+Z9aws= modernc.org/ccgo/v3 v3.16.9/go.mod h1:zNMzC9A9xeNUepy6KuZBbugn3c0Mc9TeiJO4lgvkJDo= modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A= modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU= modernc.org/libc v1.16.17/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= modernc.org/libc v1.16.19/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= modernc.org/libc v1.17.0/go.mod h1:XsgLldpP4aWlPlsjqKRdHPqCxCjISdHfM/yeWC5GyW0= modernc.org/libc v1.17.1/go.mod h1:FZ23b+8LjxZs7XtFMbSzL/EhPxNbfZbErxEHc7cbD9s= modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= modernc.org/memory v1.2.0/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= modernc.org/memory v1.2.1/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= modernc.org/sqlite v1.18.1/go.mod h1:6ho+Gow7oX5V+OiOQ6Tr4xeqbx13UZ6t+Fw9IRUG4d4= modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw= modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= trillian-1.6.1/integration/000077500000000000000000000000001466362047600156765ustar00rootroot00000000000000trillian-1.6.1/integration/README.md000066400000000000000000000010031466362047600171470ustar00rootroot00000000000000# Integration tests This directory contains some integration tests which are intended to serve as a way of checking that various top-level binaries work as intended, as well as providing a simple example of how to run and use the various servers. ## Running the tests ### Log integration test To run the Log integration test, ensure that you have a mysql database configured and running, with the Trillian schema loaded (see the [main README](../README.md) for details), and then run `log_integration_test.sh`. trillian-1.6.1/integration/admin/000077500000000000000000000000001466362047600167665ustar00rootroot00000000000000trillian-1.6.1/integration/admin/admin_integration_test.go000066400000000000000000000433661466362047600240630ustar00rootroot00000000000000// 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 admin import ( "context" "net" "sort" "testing" "time" "github.com/google/go-cmp/cmp" "github.com/google/trillian" "github.com/google/trillian/server/interceptor" "github.com/google/trillian/storage" "github.com/google/trillian/storage/testdb" "github.com/google/trillian/storage/testonly" "github.com/google/trillian/testonly/integration" "google.golang.org/genproto/protobuf/field_mask" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/status" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/timestamppb" "k8s.io/klog/v2" sa "github.com/google/trillian/server/admin" grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware" ) func TestAdminServer_CreateTree(t *testing.T) { ctx := context.Background() ts, err := setupAdminServer(ctx, t) if err != nil { t.Fatalf("setupAdminServer() failed: %v", err) } defer ts.closeAll() invalidTree := proto.Clone(testonly.LogTree).(*trillian.Tree) invalidTree.TreeState = trillian.TreeState_UNKNOWN_TREE_STATE timestamp := timestamppb.New(time.Unix(1000, 0)) // All fields set below are ignored / overwritten by storage generatedFieldsTree := proto.Clone(testonly.LogTree).(*trillian.Tree) generatedFieldsTree.TreeId = 10 generatedFieldsTree.CreateTime = timestamp generatedFieldsTree.UpdateTime = timestamp generatedFieldsTree.UpdateTime.Seconds++ generatedFieldsTree.Deleted = true generatedFieldsTree.DeleteTime = timestamp generatedFieldsTree.DeleteTime.Seconds++ tests := []struct { desc string req *trillian.CreateTreeRequest wantCode codes.Code }{ { desc: "validTree", req: &trillian.CreateTreeRequest{Tree: testonly.LogTree}, }, { desc: "generatedFieldsTree", req: &trillian.CreateTreeRequest{Tree: generatedFieldsTree}, }, { desc: "nilTree", req: &trillian.CreateTreeRequest{}, wantCode: codes.InvalidArgument, }, { desc: "invalidTree", req: &trillian.CreateTreeRequest{Tree: invalidTree}, wantCode: codes.InvalidArgument, }, } for _, test := range tests { createdTree, err := ts.adminClient.CreateTree(ctx, test.req) if s, ok := status.FromError(err); !ok || s.Code() != test.wantCode { t.Errorf("%v: CreateTree() = (_, %v), wantCode = %v", test.desc, err, test.wantCode) continue } else if err != nil { continue } // Sanity check a few generated fields if createdTree.TreeId == 0 { t.Errorf("%v: createdTree.TreeId = 0", test.desc) } if createdTree.CreateTime == nil { t.Errorf("%v: createdTree.CreateTime = nil", test.desc) } if !proto.Equal(createdTree.CreateTime, createdTree.UpdateTime) { t.Errorf("%v: createdTree.UpdateTime = %+v, want = %+v", test.desc, createdTree.UpdateTime, createdTree.CreateTime) } if createdTree.Deleted { t.Errorf("%v: createdTree.Deleted = true", test.desc) } if createdTree.DeleteTime != nil { t.Errorf("%v: createdTree.DeleteTime is non-nil", test.desc) } storedTree, err := ts.adminClient.GetTree(ctx, &trillian.GetTreeRequest{TreeId: createdTree.TreeId}) if err != nil { t.Errorf("%v: GetTree() = (_, %v), want = (_, nil)", test.desc, err) continue } if diff := cmp.Diff(storedTree, createdTree, cmp.Comparer(proto.Equal)); diff != "" { t.Errorf("%v: post-CreateTree diff (-stored +created):\n%v", test.desc, diff) } } } func TestAdminServer_UpdateTree(t *testing.T) { ctx := context.Background() ts, err := setupAdminServer(ctx, t) if err != nil { t.Fatalf("setupAdminServer() failed: %v", err) } defer ts.closeAll() baseTree := proto.Clone(testonly.LogTree).(*trillian.Tree) // successTree specifies changes in all rw fields successTree := &trillian.Tree{ TreeState: trillian.TreeState_FROZEN, DisplayName: "Brand New Tree Name", Description: "Brand New Tree Desc", } successMask := &field_mask.FieldMask{Paths: []string{"tree_state", "display_name", "description"}} successWant := proto.Clone(baseTree).(*trillian.Tree) successWant.TreeState = successTree.TreeState successWant.DisplayName = successTree.DisplayName successWant.Description = successTree.Description tests := []struct { desc string createTree, wantTree *trillian.Tree req *trillian.UpdateTreeRequest wantCode codes.Code }{ { desc: "success", createTree: baseTree, wantTree: successWant, req: &trillian.UpdateTreeRequest{Tree: successTree, UpdateMask: successMask}, }, { desc: "notFound", req: &trillian.UpdateTreeRequest{ Tree: &trillian.Tree{TreeId: 12345, DisplayName: "New Name"}, UpdateMask: &field_mask.FieldMask{Paths: []string{"display_name"}}, }, wantCode: codes.NotFound, }, { desc: "readonlyField", createTree: baseTree, req: &trillian.UpdateTreeRequest{ Tree: successTree, UpdateMask: &field_mask.FieldMask{Paths: []string{"tree_type"}}, }, wantCode: codes.InvalidArgument, }, { desc: "invalidUpdate", createTree: baseTree, req: &trillian.UpdateTreeRequest{ Tree: &trillian.Tree{}, // tree_state = UNKNOWN_TREE_STATE UpdateMask: &field_mask.FieldMask{Paths: []string{"tree_state"}}, }, wantCode: codes.InvalidArgument, }, } for _, test := range tests { if test.createTree != nil { tree, err := ts.adminClient.CreateTree(ctx, &trillian.CreateTreeRequest{Tree: test.createTree}) if err != nil { t.Errorf("%v: CreateTree() returned err = %v", test.desc, err) continue } test.req.Tree.TreeId = tree.TreeId } tree, err := ts.adminClient.UpdateTree(ctx, test.req) if s, ok := status.FromError(err); !ok || s.Code() != test.wantCode { t.Errorf("%v: UpdateTree() returned err = %q, wantCode = %v", test.desc, err, test.wantCode) continue } else if err != nil { continue } created := tree.CreateTime.AsTime() updated := tree.UpdateTime.AsTime() if created.After(updated) { t.Errorf("%v: CreateTime > UpdateTime (%v > %v)", test.desc, tree.CreateTime, tree.UpdateTime) } // Copy storage-generated fields to the expected tree want := proto.Clone(test.wantTree).(*trillian.Tree) want.TreeId = tree.TreeId want.CreateTime = tree.CreateTime want.UpdateTime = tree.UpdateTime want.StorageSettings = tree.StorageSettings if !proto.Equal(tree, want) { diff := cmp.Diff(tree, want, cmp.Comparer(proto.Equal)) t.Errorf("%v: post-UpdateTree diff:\n%v", test.desc, diff) } } } func TestAdminServer_GetTree(t *testing.T) { ctx := context.Background() ts, err := setupAdminServer(ctx, t) if err != nil { t.Fatalf("setupAdminServer() failed: %v", err) } defer ts.closeAll() tests := []struct { desc string treeID int64 wantCode codes.Code }{ { desc: "negativeTreeID", treeID: -1, wantCode: codes.NotFound, }, { desc: "notFound", treeID: 12345, wantCode: codes.NotFound, }, } for _, test := range tests { _, err := ts.adminClient.GetTree(ctx, &trillian.GetTreeRequest{TreeId: test.treeID}) if s, ok := status.FromError(err); !ok || s.Code() != test.wantCode { t.Errorf("%v: GetTree() = (_, %v), wantCode = %v", test.desc, err, test.wantCode) } // Success of GetTree is part of TestAdminServer_CreateTree, so it's not asserted here. } } func TestAdminServer_ListTrees(t *testing.T) { ctx := context.Background() ts, err := setupAdminServer(ctx, t) if err != nil { t.Fatalf("setupAdminServer() failed: %v", err) } defer ts.closeAll() tests := []struct { desc string // numTrees is the number of trees in storage. New trees are created as necessary // and carried over to following tests. numTrees int }{ {desc: "empty"}, {desc: "oneTree", numTrees: 1}, {desc: "threeTrees", numTrees: 3}, } var createdTrees []*trillian.Tree for _, test := range tests { t.Run(test.desc, func(t *testing.T) { if l := len(createdTrees); l > test.numTrees { t.Fatalf("numTrees = %v, but we already have %v stored trees", test.numTrees, l) } else if l < test.numTrees { for i := l; i < test.numTrees; i++ { tree := proto.Clone(testonly.LogTree).(*trillian.Tree) req := &trillian.CreateTreeRequest{Tree: tree} resp, err := ts.adminClient.CreateTree(ctx, req) if err != nil { t.Fatalf("CreateTree(_, %v) = (_, %q), want = (_, nil)", req, err) } createdTrees = append(createdTrees, resp) } sortByTreeID(createdTrees) } resp, err := ts.adminClient.ListTrees(ctx, &trillian.ListTreesRequest{}) if err != nil { t.Errorf("%v: ListTrees() = (_, %q), want = (_, nil)", test.desc, err) return } got := resp.Tree sortByTreeID(got) if diff := cmp.Diff(got, createdTrees, cmp.Comparer(proto.Equal)); diff != "" { t.Errorf("post-ListTrees diff:\n%v", diff) } }) } } func sortByTreeID(s []*trillian.Tree) { less := func(i, j int) bool { return s[i].TreeId < s[j].TreeId } sort.Slice(s, less) } func TestAdminServer_DeleteTree(t *testing.T) { ctx := context.Background() ts, err := setupAdminServer(ctx, t) if err != nil { t.Fatalf("setupAdminServer() failed: %v", err) } defer ts.closeAll() tests := []struct { desc string baseTree *trillian.Tree }{ {desc: "logTree", baseTree: testonly.LogTree}, } for _, test := range tests { createdTree, err := ts.adminClient.CreateTree(ctx, &trillian.CreateTreeRequest{Tree: test.baseTree}) if err != nil { t.Fatalf("%v: CreateTree() returned err = %v", test.desc, err) } deletedTree, err := ts.adminClient.DeleteTree(ctx, &trillian.DeleteTreeRequest{TreeId: createdTree.TreeId}) if err != nil { t.Errorf("%v: DeleteTree() returned err = %v", test.desc, err) continue } if deletedTree.DeleteTime == nil { t.Errorf("%v: tree.DeleteTime = nil, want non-nil", test.desc) } want := proto.Clone(createdTree).(*trillian.Tree) want.Deleted = true want.DeleteTime = deletedTree.DeleteTime if got := deletedTree; !proto.Equal(got, want) { diff := cmp.Diff(got, want, cmp.Comparer(proto.Equal)) t.Errorf("%v: post-DeleteTree() diff (-got +want):\n%v", test.desc, diff) } storedTree, err := ts.adminClient.GetTree(ctx, &trillian.GetTreeRequest{TreeId: deletedTree.TreeId}) if err != nil { t.Fatalf("%v: GetTree() returned err = %v", test.desc, err) } if got, want := storedTree, deletedTree; !proto.Equal(got, want) { diff := cmp.Diff(got, want, cmp.Comparer(proto.Equal)) t.Errorf("%v: post-GetTree() diff (-got +want):\n%v", test.desc, diff) } } } func TestAdminServer_DeleteTreeErrors(t *testing.T) { ctx := context.Background() ts, err := setupAdminServer(ctx, t) if err != nil { t.Fatalf("setupAdminServer() failed: %v", err) } defer ts.closeAll() createdTree, err := ts.adminClient.CreateTree(ctx, &trillian.CreateTreeRequest{Tree: testonly.LogTree}) if err != nil { t.Fatalf("CreateTree() returned err = %v", err) } deletedTree, err := ts.adminClient.DeleteTree(ctx, &trillian.DeleteTreeRequest{TreeId: createdTree.TreeId}) if err != nil { t.Fatalf("DeleteTree() returned err = %v", err) } tests := []struct { desc string req *trillian.DeleteTreeRequest wantCode codes.Code }{ { desc: "unknownTree", req: &trillian.DeleteTreeRequest{TreeId: 12345}, wantCode: codes.NotFound, }, { desc: "alreadyDeleted", req: &trillian.DeleteTreeRequest{TreeId: deletedTree.TreeId}, wantCode: codes.FailedPrecondition, }, } for _, test := range tests { _, err := ts.adminClient.DeleteTree(ctx, test.req) if s, ok := status.FromError(err); !ok || s.Code() != test.wantCode { t.Errorf("%v: DeleteTree() returned err = %v, wantCode = %s", test.desc, err, test.wantCode) } } } func TestAdminServer_UndeleteTree(t *testing.T) { ctx := context.Background() ts, err := setupAdminServer(ctx, t) if err != nil { t.Fatalf("setupAdminServer() failed: %v", err) } defer ts.closeAll() tests := []struct { desc string baseTree *trillian.Tree }{ {desc: "logTree", baseTree: testonly.LogTree}, } for _, test := range tests { createdTree, err := ts.adminClient.CreateTree(ctx, &trillian.CreateTreeRequest{Tree: test.baseTree}) if err != nil { t.Fatalf("%v: CreateTree() returned err = %v", test.desc, err) } deletedTree, err := ts.adminClient.DeleteTree(ctx, &trillian.DeleteTreeRequest{TreeId: createdTree.TreeId}) if err != nil { t.Fatalf("%v: DeleteTree() returned err = %v", test.desc, err) } undeletedTree, err := ts.adminClient.UndeleteTree(ctx, &trillian.UndeleteTreeRequest{TreeId: deletedTree.TreeId}) if err != nil { t.Errorf("%v: UndeleteTree() returned err = %v", test.desc, err) continue } if got, want := undeletedTree, createdTree; !proto.Equal(got, want) { diff := cmp.Diff(got, want, cmp.Comparer(proto.Equal)) t.Errorf("%v: post-UndeleteTree() diff (-got +want):\n%v", test.desc, diff) } storedTree, err := ts.adminClient.GetTree(ctx, &trillian.GetTreeRequest{TreeId: deletedTree.TreeId}) if err != nil { t.Fatalf("%v: GetTree() returned err = %v", test.desc, err) } if got, want := storedTree, createdTree; !proto.Equal(got, want) { diff := cmp.Diff(got, want, cmp.Comparer(proto.Equal)) t.Errorf("%v: post-GetTree() diff (-got +want):\n%v", test.desc, diff) } } } func TestAdminServer_UndeleteTreeErrors(t *testing.T) { ctx := context.Background() ts, err := setupAdminServer(ctx, t) if err != nil { t.Fatalf("setupAdminServer() failed: %v", err) } defer ts.closeAll() tree, err := ts.adminClient.CreateTree(ctx, &trillian.CreateTreeRequest{Tree: testonly.LogTree}) if err != nil { t.Fatalf("CreateTree() returned err = %v", err) } tests := []struct { desc string req *trillian.UndeleteTreeRequest wantCode codes.Code }{ { desc: "unknownTree", req: &trillian.UndeleteTreeRequest{TreeId: 12345}, wantCode: codes.NotFound, }, { desc: "notDeleted", req: &trillian.UndeleteTreeRequest{TreeId: tree.TreeId}, wantCode: codes.FailedPrecondition, }, } for _, test := range tests { _, err := ts.adminClient.UndeleteTree(ctx, test.req) if s, ok := status.FromError(err); !ok || s.Code() != test.wantCode { t.Errorf("%v: UndeleteTree() returned err = %v, wantCode = %s", test.desc, err, test.wantCode) } } } func TestAdminServer_TreeGC(t *testing.T) { ctx := context.Background() ts, err := setupAdminServer(ctx, t) if err != nil { t.Fatalf("setupAdminServer() failed: %v", err) } defer ts.closeAll() tree, err := ts.adminClient.CreateTree(ctx, &trillian.CreateTreeRequest{Tree: testonly.LogTree}) if err != nil { t.Fatalf("CreateTree() returned err = %v", err) } if _, err := ts.adminClient.DeleteTree(ctx, &trillian.DeleteTreeRequest{TreeId: tree.TreeId}); err != nil { t.Fatalf("DeleteTree() returned err = %v", err) } treeGC := sa.NewDeletedTreeGC( ts.adminStorage, 1*time.Second /* threshold */, 1*time.Second /* minRunInterval */, nil /* mf */) success := false const attempts = 3 for i := 0; i < attempts; i++ { _, err := treeGC.RunOnce(ctx) if err != nil { t.Error(err) } _, err = ts.adminClient.GetTree(ctx, &trillian.GetTreeRequest{TreeId: tree.TreeId}) if s, ok := status.FromError(err); ok && s.Code() == codes.NotFound { success = true break } time.Sleep(1 * time.Second) } if !success { t.Errorf("Tree %v not hard-deleted after max attempts", tree.TreeId) } } type testServer struct { adminClient trillian.TrillianAdminClient adminStorage storage.AdminStorage lis net.Listener server *grpc.Server conn *grpc.ClientConn cleanup func(context.Context) } func (ts *testServer) closeAll() { if ts.conn != nil { if err := ts.conn.Close(); err != nil { klog.Errorf("testServer: conn.Close()=%v", err) } } if ts.server != nil { ts.server.GracefulStop() } if ts.lis != nil { if err := ts.lis.Close(); err != nil { klog.Errorf("testServer: lis.Close()=%v", err) } } if ts.cleanup != nil { ts.cleanup(context.TODO()) } } // setupAdminServer prepares and starts an Admin Server, returning a testServer object. // If the returned error is nil, the callers must "defer ts.closeAll()" to avoid resource leakage. func setupAdminServer(ctx context.Context, t *testing.T) (*testServer, error) { t.Helper() testdb.SkipIfNoMySQL(t) ts := &testServer{} var err error ts.lis, err = net.Listen("tcp", "127.0.0.1:0") if err != nil { return nil, err } // TODO(jaosorior): Make this configurable for Cockroach or MySQL registry, done, err := integration.NewRegistryForTests(ctx, testdb.DriverMySQL) if err != nil { ts.closeAll() return nil, err } ts.adminStorage = registry.AdminStorage ts.cleanup = done ti := interceptor.New( registry.AdminStorage, registry.QuotaManager, false /* quotaDryRun */, registry.MetricFactory) ts.server = grpc.NewServer( grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer( interceptor.ErrorWrapper, ti.UnaryInterceptor, )), ) trillian.RegisterTrillianAdminServer(ts.server, sa.New(registry, nil /* allowedTreeTypes */)) go func() { if err := ts.server.Serve(ts.lis); err != nil { klog.Errorf("server.Serve()=%v", err) } }() ts.conn, err = grpc.Dial(ts.lis.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { ts.closeAll() return nil, err } ts.adminClient = trillian.NewTrillianAdminClient(ts.conn) return ts, nil } trillian-1.6.1/integration/admin/doc.go000066400000000000000000000015421466362047600200640ustar00rootroot00000000000000// 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 admin contains integration tests for the Admin server. // Unlike other integration tests, this is a pure Go test and doesn't require // an accompanying bash script. // Like the other tests, it requires MySQL to be running locally. package admin trillian-1.6.1/integration/cloudbuild/000077500000000000000000000000001466362047600200245ustar00rootroot00000000000000trillian-1.6.1/integration/cloudbuild/docker-compose.mysql.yml000066400000000000000000000003101466362047600246170ustar00rootroot00000000000000version: '3.1' services: db: image: ${MYSQLD_IMAGE:-mysql:8.0} restart: always environment: MYSQL_ROOT_PASSWORD: 'bananas' MYSQL_USER: 'test' MYSQL_PASSWORD: 'zaphod' trillian-1.6.1/integration/cloudbuild/docker-compose.network.yml000066400000000000000000000001121466362047600251430ustar00rootroot00000000000000version: '3.1' networks: default: external: name: cloudbuild trillian-1.6.1/integration/cloudbuild/prepare.sh000077500000000000000000000014421466362047600220220ustar00rootroot00000000000000#!/bin/bash set -ex # TODO(pavelkalinnikov): This script can be made a definitive "how to" for # setting up dev environment. Eliminate duplicating these steps in many places. # Install the tooling used for auto-generating code. Specifically, these are the # tools mentioned in //go:generate comments throughout this repository, and used # by the "go generate" command. In CI this is used for ensuring that developers # commit the generated files in an up-to-date state. go install \ github.com/golang/mock/mockgen \ google.golang.org/protobuf/proto \ google.golang.org/protobuf/cmd/protoc-gen-go \ google.golang.org/grpc/cmd/protoc-gen-go-grpc \ github.com/pseudomuto/protoc-gen-doc/cmd/protoc-gen-doc \ golang.org/x/tools/cmd/goimports \ golang.org/x/tools/cmd/stringer trillian-1.6.1/integration/cloudbuild/run_integration.sh000077500000000000000000000023711466362047600235750ustar00rootroot00000000000000#!/bin/bash set -ex # Use the default MySQL port. There is no need to override it because the # docker-compose config has only one MySQL instance, and ${MYSQL_HOST} uniquely # identifies it. MYSQL_PORT=3306 export MYSQL_HOST="${HOSTNAME}_db_1" export MYSQL_DATABASE="test" export MYSQL_USER="test" export MYSQL_PASSWORD="zaphod" export MYSQL_ROOT_PASSWORD="bananas" export MYSQL_USER_HOST="%" # See: https://docs.docker.com/compose/extends/#multiple-compose-files. COMPOSE_CONFIG="-p ${HOSTNAME} -f ./integration/cloudbuild/docker-compose.mysql.yml -f ./integration/cloudbuild/docker-compose.network.yml" docker-compose $COMPOSE_CONFIG up -d trap "docker-compose $COMPOSE_CONFIG down" EXIT # Wait for MySQL instance to be ready. while ! mysql --protocol=TCP --host=${MYSQL_HOST} --port=${MYSQL_PORT} --user=root -pbananas \ -e 'SHOW VARIABLES LIKE "%version%";' ; do sleep 5 done export TEST_MYSQL_URI="${MYSQL_USER}:${MYSQL_PASSWORD}@tcp(${MYSQL_HOST}:${MYSQL_PORT})/${MYSQL_DATABASE}" # If the test will use etcd, then install etcd + tools. if [ "${ETCD_DIR}" != "" ]; then go install go.etcd.io/etcd/v3 go.etcd.io/etcd/etcdctl/v3 github.com/fullstorydev/grpcurl/cmd/grpcurl fi go test -alsologtostderr ./storage/mysql/... ./integration/integration_test.sh trillian-1.6.1/integration/cloudbuild/run_presubmit.sh000077500000000000000000000021671466362047600232670ustar00rootroot00000000000000#!/bin/bash set -ex # Use the default MySQL port. There is no need to override it because the # docker-compose config has only one MySQL instance, and ${MYSQL_HOST} uniquely # identifies it. MYSQL_PORT=3306 export MYSQL_HOST="${HOSTNAME}_db_1" # See: https://docs.docker.com/compose/extends/#multiple-compose-files. COMPOSE_CONFIG="-p ${HOSTNAME} -f ./integration/cloudbuild/docker-compose.mysql.yml -f ./integration/cloudbuild/docker-compose.network.yml" docker-compose $COMPOSE_CONFIG up -d trap "docker-compose $COMPOSE_CONFIG down" EXIT # Wait for MySQL instance to be ready. while ! mysql --protocol=TCP --host=${MYSQL_HOST} --port=${MYSQL_PORT} --user=root -pbananas \ -e 'SHOW VARIABLES LIKE "%version%";' ; do sleep 5 done # Presumbits need a user with CREATE DATABASE grants since they create temporary # databases. For the same reason, this is a URI prefix - tests will add DB names # to the end. export TEST_MYSQL_URI="root:bananas@tcp(${MYSQL_HOST}:${MYSQL_PORT})/" ./scripts/presubmit.sh $* # TODO(pavelkalinnikov): Make the check more robust. if [ $1 = "--coverage" ]; then bash <(curl -s https://codecov.io/bash) fi trillian-1.6.1/integration/cloudbuild/testbase/000077500000000000000000000000001466362047600216365ustar00rootroot00000000000000trillian-1.6.1/integration/cloudbuild/testbase/Dockerfile000066400000000000000000000024551466362047600236360ustar00rootroot00000000000000# This Dockerfile builds a base image for Trillan integration tests. FROM golang:1.23.0-bookworm@sha256:31dc846dd1bcca84d2fa231bcd16c09ff271bcc1a5ae2c48ff10f13b039688f3 WORKDIR /testbase ARG GOFLAGS="" ENV GOFLAGS=$GOFLAGS ENV GOPATH /go RUN apt-get update && apt-get install -y \ build-essential \ curl \ docker-compose \ lsof \ mariadb-client \ netcat-openbsd \ socat \ softhsm \ unzip \ wget \ xxd # Install golangci-lint. See docs at: https://golangci-lint.run/usage/install/. RUN curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.55.1 # Install CockroachDB, see https://www.cockroachlabs.com/docs/v22.2/install-cockroachdb-linux RUN curl https://binaries.cockroachdb.com/cockroach-v22.2.7.linux-amd64.tgz | tar -xz && cp -i cockroach-v22.2.7.linux-amd64/cockroach /usr/local/bin/ RUN mkdir protoc && \ (cd protoc && \ PROTOC_VERSION="3.20.1" && \ PROTOC_ZIP="protoc-${PROTOC_VERSION}-linux-x86_64.zip" && \ wget "https://github.com/google/protobuf/releases/download/v${PROTOC_VERSION}/${PROTOC_ZIP}" && \ unzip -o ${PROTOC_ZIP} -d /usr/local bin/protoc && \ unzip -o ${PROTOC_ZIP} -d /usr/local 'include/*' \ ) ENV PATH /usr/local/bin:$PATH ENV PATH $GOPATH/bin:/testbase/protoc/bin:$PATH trillian-1.6.1/integration/doc.go000066400000000000000000000015471466362047600170010ustar00rootroot00000000000000// 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 integration contains some integration tests which are intended to // serve as a way of checking that various top-level binaries work as intended, // as well as providing a simple example of how to run and use the various servers. package integration trillian-1.6.1/integration/docker_compose_integration_test.sh000077500000000000000000000030421466362047600246720ustar00rootroot00000000000000#!/bin/bash readonly DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" # Should be run from the root Trillian directory. docker_compose_up() { local http_addr="$1" # See: https://docs.docker.com/compose/extends/#multiple-compose-files. docker-compose -f examples/deployment/docker-compose.yml \ -f integration/cloudbuild/docker-compose.network.yml up --build -d # Wait until /healthz returns HTTP 200 and the text "ok", or fail after 30 # seconds. That should be long enough for the server to start. Since wget # doesn't retry DNS failures, wrap this in a loop, so that Docker containers # have time to join the network and update the DNS. for i in {1..10} ; do health=$(wget --retry-connrefused --timeout 30 --output-document - \ "http://${http_addr}/healthz") if [[ $? = 0 ]]; then break fi sleep 5 done health_exitcode=$? if [[ ${health_exitcode} = 0 ]]; then echo "Health: ${health}" fi if [[ ${health_exitcode} != 0 || "${health}" != "ok" ]]; then return 1 fi return 0 } if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then # Change to the root Trillian directory. cd "$DIR/.." if docker_compose_up "deployment_trillian-log-server_1:8091" && \ integration/log_integration_test.sh "deployment_trillian-log-server_1:8090"; then docker-compose -f examples/deployment/docker-compose.yml down else echo "Docker logs:" docker-compose -f examples/deployment/docker-compose.yml logs docker-compose -f examples/deployment/docker-compose.yml down exit 1 fi fi trillian-1.6.1/integration/format/000077500000000000000000000000001466362047600171665ustar00rootroot00000000000000trillian-1.6.1/integration/format/format.go000066400000000000000000000132021466362047600210030ustar00rootroot00000000000000// 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 format contains an integration test which builds a log using an // in-memory storage end-to-end, and makes sure the SubtreeProto storage format // has no regressions. package format import ( "bytes" "context" "crypto/sha256" "fmt" "sort" "strconv" "strings" "time" "github.com/google/trillian" "github.com/google/trillian/log" "github.com/google/trillian/monitoring" "github.com/google/trillian/quota" "github.com/google/trillian/storage" "github.com/google/trillian/storage/cache" "github.com/google/trillian/storage/memory" "github.com/google/trillian/storage/storagepb" "github.com/google/trillian/types" "github.com/google/trillian/util/clock" "github.com/transparency-dev/merkle" "github.com/transparency-dev/merkle/rfc6962" "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/types/known/durationpb" "k8s.io/klog/v2" ) func run(treeSize, batchSize int, leafFormat string) (string, error) { ctx := context.Background() ts := memory.NewTreeStorage() ls := memory.NewLogStorage(ts, monitoring.InertMetricFactory{}) as := memory.NewAdminStorage(ts) tree, err := createTree(ctx, as, ls) if err != nil { return "", err } log.InitMetrics(nil) leaves := generateLeaves(treeSize, leafFormat) if err := sequenceLeaves(ctx, ls, tree, leaves, batchSize); err != nil { return "", err } // Read the latest LogRoot back. var root types.LogRootV1 if err := ls.ReadWriteTransaction(ctx, tree, func(ctx context.Context, tx storage.LogTreeTX) error { latest, err := tx.LatestSignedLogRoot(ctx) if err != nil { return err } return root.UnmarshalBinary(latest.LogRoot) }); err != nil { return "", fmt.Errorf("ReadWriteTransaction: %v", err) } return latestRevisions(ls, tree.TreeId, rfc6962.DefaultHasher) } func createTree(ctx context.Context, as storage.AdminStorage, ls storage.LogStorage) (*trillian.Tree, error) { tree, err := storage.CreateTree(ctx, as, &trillian.Tree{ TreeType: trillian.TreeType_LOG, TreeState: trillian.TreeState_ACTIVE, MaxRootDuration: durationpb.New(0 * time.Millisecond), }) if err != nil { return nil, fmt.Errorf("CreateTree: %v", err) } logRoot, err := (&types.LogRootV1{RootHash: rfc6962.DefaultHasher.EmptyRoot()}).MarshalBinary() if err != nil { return nil, fmt.Errorf("MarshalBinary: %v", err) } if err = ls.ReadWriteTransaction(ctx, tree, func(ctx context.Context, tx storage.LogTreeTX) error { return tx.StoreSignedLogRoot(ctx, &trillian.SignedLogRoot{LogRoot: logRoot}) }); err != nil { return nil, fmt.Errorf("ReadWriteTransaction: %v", err) } return tree, nil } func generateLeaves(count int, format string) []*trillian.LogLeaf { leaves := make([]*trillian.LogLeaf, 0, count) for i := 0; i < count; i++ { data := []byte(fmt.Sprintf(format, i)) hash := sha256.Sum256(data) leaves = append(leaves, &trillian.LogLeaf{ LeafValue: data, LeafIdentityHash: hash[:], MerkleLeafHash: hash[:], }) } return leaves } func sequenceLeaves(ctx context.Context, ls storage.LogStorage, tree *trillian.Tree, leaves []*trillian.LogLeaf, batchSize int) error { for i, size := 0, len(leaves); i < size; i += batchSize { if left := size - i; left < batchSize { batchSize = left } if _, err := ls.QueueLeaves(ctx, tree, leaves[i:i+batchSize], time.Now()); err != nil { return fmt.Errorf("QueueLeaves: %v", err) } sequenced, err := log.IntegrateBatch(ctx, tree, batchSize, 0, 24*time.Hour, clock.System, ls, quota.Noop()) if err != nil { return fmt.Errorf("IntegrateBatch: %v", err) } if got, want := sequenced, batchSize; got != want { return fmt.Errorf("IntegrateBatch: got %d, want %d", got, want) } } return nil } type treeAndRev struct { subtree *storagepb.SubtreeProto revision int } func latestRevisions(ls storage.LogStorage, treeID int64, hasher merkle.LogHasher) (string, error) { // vMap maps subtree prefixes (as strings) to the corresponding subtree proto and its revision vMap := make(map[string]treeAndRev) memory.DumpSubtrees(ls, treeID, func(k string, v *storagepb.SubtreeProto) { // Relies on the btree key space for subtrees being /tree_id/subtree/id/revision. pieces := strings.Split(k, "/") if got, want := len(pieces), 5; got != want { panic(fmt.Sprintf("Btree subtree key segments: got %d, want %d", got, want)) } subID := pieces[3] rev, err := strconv.Atoi(pieces[4]) if err != nil { panic(fmt.Sprintf("Bad subtree key: %v: %v", k, err)) } if rev > vMap[subID].revision { vMap[subID] = treeAndRev{ subtree: v, revision: rev, } } }) // Store the keys in sorted order. keys := make([]string, 0, len(vMap)) for k := range vMap { keys = append(keys, k) } sort.Strings(keys) // The map should now contain the latest revisions per subtree. out := new(bytes.Buffer) for _, k := range keys { subtree := vMap[k].subtree if err := cache.PopulateLogTile(subtree, hasher); err != nil { // TODO(mhutchinson): This error should be propagated. klog.Errorf("PopulateLogTile(): %v", err) } fmt.Fprintf(out, "%s\n", prototext.Format(subtree)) } return out.String(), nil } trillian-1.6.1/integration/format/nochange_test.go000066400000000000000000000041251466362047600223400ustar00rootroot00000000000000// 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 format import ( "os" "strings" "testing" "github.com/google/go-cmp/cmp" "github.com/google/trillian/storage/storagepb" "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/testing/protocmp" ) // TestDBFormatNoChange ensures that SubtreeProto tiles stored in the database // do not change. This test compares against a previously saved output. func TestDBFormatNoChange(t *testing.T) { for _, tc := range []struct { name string size int }{ {name: "dump_tree_output_96", size: 96}, {name: "dump_tree_output_871", size: 871}, {name: "dump_tree_output_1000", size: 1000}, {name: "dump_tree_output_1024", size: 1024}, } { t.Run(tc.name, func(t *testing.T) { out, err := run(tc.size, 50, "Leaf %d") if err != nil { t.Fatalf("run: %v", err) } saved, err := os.ReadFile("testdata/" + tc.name) if err != nil { t.Fatalf("ReadFile(%v): %v", tc.name, err) } got := parseTiles(t, out) want := parseTiles(t, string(saved)) if d := cmp.Diff(want, got, protocmp.Transform()); d != "" { t.Errorf("Diff(-want,+got):\n%s", d) } }) } } func parseTiles(t *testing.T, text string) []*storagepb.SubtreeProto { t.Helper() parts := strings.Split(text, "\n\n") tiles := make([]*storagepb.SubtreeProto, len(parts)) for i, part := range parts { var tile storagepb.SubtreeProto if err := prototext.Unmarshal([]byte(part), &tile); err != nil { t.Fatalf("Failed to unmarshal part %d: %v", i, err) } tiles[i] = &tile } return tiles } trillian-1.6.1/integration/format/testdata/000077500000000000000000000000001466362047600207775ustar00rootroot00000000000000trillian-1.6.1/integration/format/testdata/dump_tree_output_1000000066400000000000000000007574511466362047600250110ustar00rootroot00000000000000prefix: "\x00\x00\x00\x00\x00\x00" depth: 8 leaves: { key: "CAA=" value: "\x8auO\x06ԁ..\x8e\xee\xef\xa4\x06\x17vĖŦ\x0c\x80j\xdd\x029\x98h`\x1el\x85y\xb3\xc2" } leaves: { key: "CAE=" value: "s\x82l\xc7;\xa0\x1f_\x08\xcc\xcc\x0e\x1a\xfbD\x84\xadŅŖS{Uh*V\xbc-\xe7\xb7i~\x81" } leaves: { key: "CAI=" value: "\xbb\x91zJ\xbb\xbbR\xc7\xe0>\xde\x1e\xe5\x9fC\x11\xe5J\x85\x0b\x00\x99\xd1^\xa2\xd8RB?;S5" } internal_nodes: { key: "BwA=" value: "\xc6\xee@\x91R\xb3TI\x03\xf2\x86B-}c:\x9aÛĸ\xef\x0f/\xdb\xf1V\x8e-\r\xcf\x17?\x92" } internal_node_count: 1 prefix: "\x00\x00\x00\x00\x00\x00\x00" depth: 8 leaves: { key: "CA0=" value: "\x9e0}ë§ļ9q\xfd\xc6\xfeI\xcd\u0097\xab\xb0\xe0y.\xc6\x0c\xc5SpΊf+Eh\xce" } leaves: { key: "CA4=" value: "\xe3\xe5kq\xcc\xec\x9a\xe7Ņĩ>\x0e\x16Ũ\xc6j\x9fS\xe7\xb4\x15S\xeeT=\xc2U\x0c\x99\xed\xd1" } leaves: { key: "CA8=" value: "\xa7\x97\xc0\x16R\x94R^s\xf7F[\xa7\xbba\xb1\x9f\xbf\xa3bj0\xe84\xcdR\xd5\x08\xc6|^\x89" } leaves: { key: "CAA=" value: "\xe5\x918\x856\x85\xee]?.\xd6\xae{\x1bw\x82\xe1" } leaves: { key: "CAQ=" value: "\x8c\xa6\xb1\xbf놛\x94\xb0Ūļ\xc9\xcdp12Ò\xb4\x8f:Û˛3ejb\xaa]\x96\xb3\x18" } leaves: { key: "CAU=" value: "S\xc1\xd1n\x89\r1)\x86\xd9\xc3-\x10\x9fm@\x00\xf4\xa2F\x81\x0e\xe3\x95\xff\xcc\xf4{Y\xc8\"$" } leaves: { key: "CAY=" value: "\x91Čš.\nx/\xe3\xca-\x0c??;\xb1r\x8fViB\x9d\x9f\xa2\xc6\xd5\"\x8bŞ\xe41\x9b" } leaves: { key: "CAc=" value: "\r\xd76!L\x05o'^i\xff\xe6\xa1\xdaD\xd1q\xafƎ\xae\xdb\xeeIߕsR>\x05YI" } leaves: { key: "CAg=" value: "DH\x1au\xf5Éĩ\x0ep\x05\x8c\xd0\xe1\x95\xc9\xeeÚ˛\xff\xf7Z \x17\x7f\xfa\x9d\xc6(\x81\x9f\xe97" } leaves: { key: "CAk=" value: "\xc8\x07^\x7fL7\xe6\x1boQ\x85x\x7f\x00\xb8\xf1\xfes\t\xd9ę¸ĢL\xc5en$\xac-\x1b\xbf" } leaves: { key: "CAo=" value: "Ђa\x90\\\x1f\xa1h\xf5\x80\xcd\xe1i\xdb\xc68\xcfŪ\xfaq\x10O\xb8\xaaÅļ\xa7\x06h\xf3_" } leaves: { key: "CAs=" value: "\xd05\x1a\xe3FT\x03\x80~\xb6äƒŋ\x9fn#/u\xa7\xb5\xf7\xfa3HÔš\x97\x88\xbdJ\xf6_" } leaves: { key: "CAw=" value: "7V\xcf\xf2\xd8\x0c\x05\xe7\xf0 _BS\xecÕ›Đ‹U\x8eR\xf1\xc0\x0fK\xfe\x0fR\xf4\x87x\xe5" } leaves: { key: "CB0=" value: "\x81\xf5\xb0\x96\xe7;\xf6\x97\xdb\xd6\x7fm\xfb\x05\x80\xd6\xff-\xe5\xc5PßĻ\x82\x07`\xfca\xf77\xf0>" } leaves: { key: "CB4=" value: "\x92KAS\xbb~\x84\x17\xe2+\xdf\x03R\xbc\xbc\x89=\x8d\x85\x1e\x7f\x82\xe1\xe7\x1akh\x8aY\xd6\xdf\x02" } leaves: { key: "CB8=" value: "\x1d\xe6f\xde\xef\xef\xef\xc5C/)\xac2\x90\xe2\x08\xf1\x13\xbc\x0f\x93\x00M\x88/}\x1b\xa4\xad\x85\xac\x7f" } leaves: { key: "CBA=" value: "\xff\xa8nT\xc5\x1f\xabr\xbd\xfe\xa8'\x19\xe5k&\x12\xd3\xe9\xf8փ\xb5\xd2sCe#\x88G.K" } leaves: { key: "CBE=" value: "j\xed\xadGL\xa9{^\x13g\xf0B\x0b\xaf\xc1\xebsG\xce\x1c\xd5H\x11&\x9e3O\x81\xa4ܗ\xfd" } leaves: { key: "CBI=" value: "\x04CZ\xa9b\x01Į›\xc44F\xf6\xc3\x17\x02\x8e\xbe\xf1\xa2,@!L\xe6\xf3\xf3\xa69\xaf\"\xd6K" } leaves: { key: "CBM=" value: "z\xd4\x00\x15\xb1\x1a \x83nC\xfe\xdaDwq%\x95\xb0>h(\xfetn&\x95t\xae\x19O\xce\x0c" } leaves: { key: "CBQ=" value: "\x08\x85\xe85\xa8I\xe3\xcb\xfb\xa7\x08\xf0\x165G\xd3\xef`\xb8k\x14\xc6\x1c\xb2\xa8\xfd\xf0_y\xa2p\xd2" } leaves: { key: "CBU=" value: "kŨ¯\xfd\x92r3\xfe\xdewL\xd4Z4-\xd4Į”\xafx\x06\xef\xd5\xf4\x17\xed\x9eHP\x08\xc6\x1e" } leaves: { key: "CBY=" value: "~Z\xb8\xc0~K\x04\x80\x1b\xba1\x18\r\xcd\xc4g\xd4\xeeskj\xc2]|{ \x82ZK\xc8" } leaves: { key: "CBg=" value: "\x17\xc2Q\x93P\xe5\xbeE\xbb\x88\x1cF\xd3g\x10\x8a\xa7\x07o\x1c\x80\xfb\x86q\x16\xe4\xd1\xcb\x1e\xab*\x7f" } leaves: { key: "CBk=" value: "\xf9\xb1\x11Č´1\xa4\xa0\xeaz\xc6;\xa2\xc1p^\xd9\xf7\x0c\xf8\x95\x91\xd1\xed\xec\x84\xf5\xb4\xbd\x8c\xaa\xe9" } leaves: { key: "CBo=" value: "\xaf.\x9ca~~\xd2UJ\xd55\xf4 d\xd9w\xd3\xf3\x82\xe76\x88\xe0\xe8KÜŊ\xef\xf3i'\xbb" } leaves: { key: "CBs=" value: "Yf\x0c\xd2\x0cb\x0bL\xb9]\x02\x15\x0eL\xb4\xbd4\xb1\xea\xa7\xe7{`k\u009d7\x1d\xc9\x12\x1c&" } leaves: { key: "CBw=" value: "\xb8\xfd\x1cr\x10\xd1l.E\xb8\xa7\xef\xa1\xfc\x8d\xf4*Õą;,L\xbf\x16\xe8['\xee\xe5DY\xfa" } leaves: { key: "CC0=" value: "\xa9\xb8\t\x11|\xf2\xf2\xfbo׋\xe9\xfb\xdf\x11=\x8bey_1M\x19\xab\x8d\xa6\x12:\xd6\"ڄ" } leaves: { key: "CC4=" value: "\xb3\x01/\x86\xff\xf5\xac\xa4\xdc\x14\xfb]a\xe4\xe8\xf6J\xf1\xc2R\x1dn\x1e\xe0\x9e\x120I\xb4\xdep\x97" } leaves: { key: "CC8=" value: "\xe1\x80\x02\xf80ᎈ*n\x1f\x8c\x0fSLŃ\x8b9\xfe\xbc\xeb\x01m\xf2ؑ\xa8/\x16az" } leaves: { key: "CCA=" value: "\x15i\xb6[~=\x81\xa7\x180\xbb\x7fu\x08\xad\x95\x86\xa0\x9e\xffDTeIY\x96y\xcaO\x9fŨĒ" } leaves: { key: "CCE=" value: "\x81Uԉ$L\x8f\xeb\xd45{\xe5#\x8f!\xba\xb1A\x10\x96\x000Įž\\t\xb1\x07(\x12\xf4e" } leaves: { key: "CCI=" value: "\xa7\x92\xd7<0\xa9e\xe0\xa9\xebe\x82\x82ˎ\x0b#\x0f\x83c\xad\xa8\x12\x92\xa7\xb2\xe5>[\xe6<\xab" } leaves: { key: "CCM=" value: "\x8e\xb1y\x01\x91ʉ\x81⤗&>.\xfe\xa7K\xee9\xa2\xef\x18\xa6\x82\x92Wk\x80f\xbe\x8f\xc3" } leaves: { key: "CCQ=" value: "\xf7\xa8\x93\xf6B\xad\xd1\xf8\x7fw\x04X\x1f\xd8'\x1e-\xfdF@v\xb9@\x03p\x98AV\xec\x931\xb4" } leaves: { key: "CCU=" value: "\xb1\x7fC\x19\x159\x1b\xe3\x1f!\x99ذ\xf65\xb8\x1f4\x11-M\x83\x95Z\xe7\xadk\xefd\x1dÉŦ" } leaves: { key: "CCY=" value: "u\x86\x9a2\xc6\xedk@\x0ei\xcei\xf7\xb2[F\x1b\xb1\x1e\xdfYX\xf9No׋\x12\\8\x9f\x9c" } leaves: { key: "CCc=" value: "c-\x8bk\xac\x91\x82\xbb\\Qy\xd9Å\xac)\x13\x8e:\x03\x1c\x006\xc6\xcd\x1d\xc0NI\xc7\xedY" } leaves: { key: "CCg=" value: "cX\xe7\xd4\xd9u\xaepKsh\x00H\x07\x7f\xba2\xa4UK\xee\xa8\xfb\xebŨ“\x03\xe4\xffX\xc0\xee" } leaves: { key: "CCk=" value: "\x1d\xc2f]\x98\x0e\xed\x18\xc0\xde\xffb\xed\x05i%\xf2\x8d\x15]\xcc\x7f\xeb j)\xe7\x12Ëŗ\x06?" } leaves: { key: "CCo=" value: "\xb5\xbc`\"\xfe\xe9Q\xa2\xe3V\x94\xedRp\xe04\xe6\x85|#\xbb\x7f3\x16\x7fPk\x98F\x16\xa2]" } leaves: { key: "CCs=" value: "Y\xfbo\x80>}a8\x98Đĸ\x8f\x1c\xba3\x86\x8e7\x01\x11{\xb8\xd37~^\x9d\xd0" } leaves: { key: "CD0=" value: "\xc9_?\xd5\xc1X]c\xd8\xea\x0fÄ Q/K@ \xcc\x14A\xff\xa6\x96.\xe1\xc5\xe1\xdd\\\xa3," } leaves: { key: "CD4=" value: "X\xa6x\xff\x1d\xa2\xb5ŲĨ\xe8\xbd7\xaeP\xe4\xfd\x1f#\xd0t\x04\xb6@\x95\xdak\xb6\xb3\xe3Ūē\xfb" } leaves: { key: "CD8=" value: "xȈ\xa7\x9c\x86\xda&\xceE!f\x86'x\x08\x97\xeab,U\x0fÜ­\xc2\xfd8\xeb\xb4\xccs\xe5" } leaves: { key: "CDA=" value: "\xeb\x16\x83\xc5w\xe5I\xf0U\xa7\xc6\xc2\xe7\x87+b\xdboc\xb0\x01^\xe0V$\x95\\-\x83}*\x04" } leaves: { key: "CDE=" value: "\x90\xbe9\x92\xd0tTOuWД\x87}>z3\x08\xea\xbcrj\xfaY\xab\xc1\xf8eW\x12\x99\xdf" } leaves: { key: "CDI=" value: "\x18,Ύ\x9d\xda1\xd69\xa2m\x80\x16Fq\xb9\xc0Ķē\x80\xdf=Y\x19L\xb38\x1b\xb4I\xeb\xf4" } leaves: { key: "CDM=" value: "\x06*\xb2k\x11m^7\xf2*\xbaK@\xc9\xeaM\xa7\xcb/^âǝÆĒz\xa4KW=L^" } leaves: { key: "CDQ=" value: "\xee\xd3l\x02M\xc5m\x83\xf3\xa6(\x14\x8bIj\xbc\x94\x0e\xda\xf8\x93\xddD<'l1\xa5\x05\x9d\xdc%" } leaves: { key: "CDU=" value: "Ý(^n\xb7\x0eta\xb3\x99\x9eB\xb7]{\xa7Jl\x00hT\xd3\xe3(\xeeUO&\xfc\x90\xa2" } leaves: { key: "CDY=" value: "g䙃\x1c^\x07,Ķf\x1c\xaf\x0ez\x97/\x03\x90\xe8\xbe\x1c\xbc\xc9\ne\x1fwc\n\xa0\xa7" } leaves: { key: "CDc=" value: "P\xf2%\x151~vv\x90NWS\xa9\xbb#\xaf\x9bT\xcb\xcb\x05Ilk\xb8\x04\x19]D\xd3t\xf1" } leaves: { key: "CDg=" value: "\x8a\xd2ĐŽ\xdd;\xabp+\xff\xe0\x1ax\xe4\x15T\xa2\xcc\xeaה\x19\x10\x8f" } leaves: { key: "CDw=" value: "\"\xb0<>\xc6\xe7\xef\x02f\xa6\xd2Ōŋi,\xfb\x0e\xd0\x07\xc7\x04\xf9\xefo*\xbeE&p+\xcaT" } leaves: { key: "CE0=" value: "\\%\xe5\xb4\xf0\xf3\xbc?\xa4;\x8aV\xf1|\xdb\x18t8\xf3IRLr\xd8ĖŦ\xb1}\xd8\x10M\x0f" } leaves: { key: "CE4=" value: "\xfa5}k\x82e\x8cW\xb0\xff\xb40\xdfi\xac\xab\xf7\xb5q\xa8\xf1r\x11?P\x96}\xa8\x1c" } leaves: { key: "CE8=" value: "\x04\x96\x05\xa9\xe9\x17\xfd\xbe\x1d\x9f\x16\xf5\xf0o}huJ\xb3\x88T@\xf7e9\xc2p_cp\xc1\xf2" } leaves: { key: "CEA=" value: "}\x00\xb3\xa4h\xea\xa3j\xac\x1a\xff\xf4=SM\x91,\x95\xf2\xd1\x1a\n\xb8\xca\xdeK.9\xea'\xef>" } leaves: { key: "CEE=" value: "y\xbc\x18!\xb3xY<\x91-ԅ\x80H\xc4\x00Hj\xaa\x17t4\x07\xa4DF\x85*\xd7,\x18\xc4" } leaves: { key: "CEI=" value: "\xfb׏\xc5\xceley\xf3[\xc7\xe4\x8d\xf0͑ů\xc1ÃŽ\xfa\x91dZ\xadB(\xc4E\xd4\x10" } leaves: { key: "CEM=" value: "@\xf6\x93b\x9b\xe3$>N\xfb~\x88\xd17\x8a>\xd4b2\x9a\x1d\xa7\xa2\x8f\xd2)\xa2\x92\xa9\x104\x1c" } leaves: { key: "CEQ=" value: "\xae\xaeI\x84\x16\xa3y\xd7\x19\xe1d\x0f\x8f\x03P,\x97g\x1f8֗7ֆ\x92\xc5@3\xbc\xea\x82" } leaves: { key: "CEU=" value: "\xb4\xc2\xfeW:k+\xe4\xc6\x1bF\x82\xa0\xb1vk\x99v\x9b\xaaX\xfc~\x97\xfb\x9f~\xec0\x05:\x1e" } leaves: { key: "CEY=" value: "\xb2Ö°\x17\xb1{\x0eVNT\x831\xa2un\xf2\xd3k\xc1\xee\xe0\xe6\x1f\xc8b\xd50\x12\xef\xf9\n\xd5" } leaves: { key: "CEc=" value: "\xd8\xe9\xb9$\x86Ũ›\xc0\xda\x11@\xb4\x15\xf6\xd3}\x7fD\xf615\xf4\xf4^=~\x02ZĖ—\xe6p" } leaves: { key: "CEg=" value: "+\xb7R\x06&<+\xa6\xd9u\xb8'5'|\xf7\xc5OhD\xc1|Äē\xa4\x8c\xc0O\xd6/\xa3=" } leaves: { key: "CEk=" value: "-\xcb\"\xad\x1f\xa3 {\xff\x7f\xaa3.\xb4jPuy\xe7$u\xfcN\x90\xf8`\xa7\x9c\x82%G6" } leaves: { key: "CEo=" value: "w{\xa7\x1e-\xd7?'o&S\xaf\t\xc1J\xf7e\x9d\x08it4\xb8\x0c\xaf\x0cЉ\x16\x9f\xb4\x83" } leaves: { key: "CEs=" value: "\x80\xcdz\xef\x0c\xb4\xe0\xb3\xf3x\xd3\x1a\xc3n \x9d\xb2!yX\xf6W\xb5\xe79}\xac-\x91KЇ" } leaves: { key: "CEw=" value: "^\x003]f<}Q\xab\x8f.7\x82P\xeb\x0fHp\x8c\x03!\xf9B\xa0\xa1\xb1\xa9\x05\xf1~'\xef" } leaves: { key: "CF0=" value: "\xf6\xcbU5\xd8\xcf\xe0\x8d\x17\xd6H\xcc'o\xb0\xef\x10\x91\x01s\x93a\xc9Tj\xcd-3\"_\xe1L" } leaves: { key: "CF4=" value: "6\xf3\x972i\x80B\x88o\xf3\x15\xf8wՄu\xfet$\xa4\xdd<\x8b\x87\xd8\t2\xa2\xed\x93\xd7\x15" } leaves: { key: "CF8=" value: "K\x7f\x03鷋\x06\xb4ĐĻB\xf7\xca\xdfBa\xa3\x91[\xb8 \x03\xa0&\xf30\x87\xd3{\x82Xv" } leaves: { key: "CFA=" value: "\xab\x16p.aY\xa4j\xbc&\xbc\xfe'\xbce5J%Θ^Kw\xfdv\xd6!LT\xa5\xea`" } leaves: { key: "CFE=" value: "\xc7\xedN\xef!)\xfd\x87[\xbf1I2\xc4&e.\x81S\xcfD+-t-\xa0\x8fJ2s\xd3e" } leaves: { key: "CFI=" value: ">\xa5\xa2a\xf0\tA\x1f\xc8\xf5\xdc\xca\xf1\x13\xc0\xe2\xe5\xde\x05\x9c\x13\x7f'\xdd\x15\x83H&g\xb2\xfa[" } leaves: { key: "CFM=" value: "\x84\x9b\xf6\"\xc0`\x9a\xb0\xbfʇ\x82\xe9\xa0\xe7\x13\x87|FI\xc1\x81\xf3\x8d\xa6\x1e5\xe2\xc1\x92\xb9\x8c" } leaves: { key: "CFQ=" value: "\x80U\xc32\xdb`\xa6\x166*H\xe9(\x80\xa9\x9e\x98\x96\x0bf\xb5@Ę”Øƒ\x10\xfcx\nOo" } leaves: { key: "CFU=" value: "v\x93\x813\"\xb2qŅ\xba\x7fhix^\xab_\xdd.\xf3\xc0\xf5\x05\xed9\xc8\x10\xd3Ô´\"\xda" } leaves: { key: "CFY=" value: "΄Zz\xe5_\xa1\x99#\x80:\xb9\x15\xa2$\xe72\x04\xd9\xc9d\x92\xfble\xac!\xd8\xe2;\\B" } leaves: { key: "CFc=" value: "\xc1\xeb\xea\xb4\xce\xf5\x0e\xc5|\x15\xbb/\x96uu\x91\x0b\xd6\xe8a!\x15\x87qC\xcb\x0e\x036\xb1\xa4\xd3" } leaves: { key: "CFg=" value: "@\x15\x0eG\xc3\xea\x17\xf1!,O\x87\x01\xebe\xb8\x08\xe4\xac,@]\x82\x95Wi\xf6:\xf6\xe9I\xd9" } leaves: { key: "CFk=" value: "\x0eG%\x84\xf1\xd34\x06\xe1ꉠ\x9e\x12\x947\xa1\xa7\\\xb0U\x10\xba\xafu&\xbf\x05\x1c\xf2\xef\x85" } leaves: { key: "CFo=" value: "\x01\x06\\\xbcj\x82':\xc6\xc1\n\x17\xc97o\\\x005" } leaves: { key: "CFw=" value: "J\x1f\x06\x1b_\x03\xae\xd7-h\xd0\x17\x14\xd0c\xd9\x16\x14m\xd3\xc0Q\xa7nŅy\xf0\x1b\xb0\x92\x8f" } leaves: { key: "CG0=" value: "R\xccÎģ\x18nv\xa8\x8c\xd9\x14\xf6\x8a\xbf\xed\xf4:*\x0e[dO\xa3\x0e\xa5\"A*\xe8\xbe\xfe," } leaves: { key: "CG4=" value: "\xe7\xcd\x1bi\xc0ish\xe5\x050\x973M4:k\x0c\x89HB9\x17\xb4\xa0\x97\x10a\xf2#\xfa\xa0" } leaves: { key: "CG8=" value: "\xf3͞=\x11}*&C\x12HH\xbb\x8b\x15\xc9b\xf3{\x84I\xa4\x8b\xbb\xfe\x98\x7f9\x892\xd6l" } leaves: { key: "CGA=" value: "\x1a\x0c\xd1b\xb1\xcc;\xf9r6čģĩ\xee\xb0 \xb1[\x90\xcf\x0cP\xefO}J\x12Q\x93\x07?l" } leaves: { key: "CGE=" value: "\xfa\xffO\xf5\xe0\xd645\x07*\x965&j\xfa\x8a\xc8\xc8p\xa3\\\x86\x9d\x9d)\xbd\x01\x98ň\x15(" } leaves: { key: "CGI=" value: "4\x80`\x0e\xaa:\x18\xces\xa6\xe8\"\xfa\xbaŌ„\xeeV\xd1\xf8Ũ {\x96\xb04\xd6m\xd5L,\x0e" } leaves: { key: "CGM=" value: "T\xdf\x7f\x90\xc5ؓ\xf2%$4\xd7\xdd\xde\x06q\xd4\xf6\xaa\xfe\x01\xfb\xe5t)\x02=\xb5\x00\xf1\xf5\x85" } leaves: { key: "CGQ=" value: "O\xfb\xa7\xb3A\x16\xac\xdcB\xde\xe3\x94O\xcc^.o\xba\xfc\xfa6\xce\x1c\x8c\xc1\x83m\x16\x08\xa1\xdc\xca" } leaves: { key: "CGU=" value: "+\xa1\xda\xdcHdl\xe7\x1b``Y^\xacĪ€Õ\xbc\xe7\xa73\xeb\x89(\x84\x97\t\x99Îĩh" } leaves: { key: "CGY=" value: "\xf2\xde6΍\xb0\x9a\xd6ŅĻ\x14\x8fm\xb2uA\x1ah\xe6Ɯ.M&\xdd\x1a(&\xebx\x0b," } leaves: { key: "CGc=" value: "\x1c5ԇͤ\x82L\xb5\xe6\xb9V|Q\xa5\xdc4$\xdeyAVr\x89\xc08(\xba\xfb\xbf\\\xe4" } leaves: { key: "CGg=" value: "e\xea\xb6ČĻ\x07\xd0\n\x81U\x07\x0c#%\xf7\xd8\x14e\xac\t\x14O\x95\xb0@\xfdf\xe3\xb4\"\x03\x08" } leaves: { key: "CGk=" value: "\xe3{T\xc8\xfd\xbct\x19\x17)\xdd\xea8\xb5Xy\xaeqa\xd5l\xf8`C\x8b\xde\xd81\x14J\x06E" } leaves: { key: "CGo=" value: "[R\x11\x147ZHT\x8d\xdf\xc5\xea\xa8\xeb\x98\xe2ܐi\r\xf3\x15~\xea\x04\x93\x8b\xb2\x0f.\xee\x17" } leaves: { key: "CGs=" value: "\x94\x04\x1b9(\xba\x1cO\xe5\x01(\xe5:\xa3\x05\x1a,ʓ5\x07\x0e\x93\x17\x88\xc2Y\xd3t\xd4\xeb." } leaves: { key: "CGw=" value: "8\xe9\x16\xa8\x81\xb6bĪŽj\x08 \x02F\x8c\xbe\x8f\xa8\x90A\x06}O\xb9\xeb\xad:\xa4ČŠB\xa7" } leaves: { key: "CH0=" value: "E\x88\x00\xc4?E\xf1\xae\xc2\x1e\xd92\x02\xe4w)\xf9\xc1\x1cQ\xc0N\xc8\xc8Z\x96\xae\x0bR\xf1:L" } leaves: { key: "CH4=" value: "{\x16%{\x85Vʉ~\x03\x97鈞(\xb3*\x85C\x01\xbc\x1bV \x05\xa4>\xa1\x16\xf6z\r" } leaves: { key: "CH8=" value: "_\xcdRf@\xdf\xeaŲ–'+\x12\x83[\xbb\x02\xb1E)~\xa4S\x8eb\xc0\x80\x86\x8eq\xa6Aw" } leaves: { key: "CHA=" value: "\x0bÕ°^\xa6\x0fD\xf2\xeeMy\x14\x02\xe6!\x1fZt\xb2\xf8\x8bX\x91^J\xf9\xaauT\xab8\xc0" } leaves: { key: "CHE=" value: "\x9c\xc6\x11q\xebl\xa6j^{\x0c\x9f\x11E\xd2[\xc5S\x15\r\xe7;Üš`*\x1d\xb2\xaf5\xc2\xcd" } leaves: { key: "CHI=" value: "W\xee\x11\x8b]\xf3\x81\xda\xc7#E\ti|\xb1TÍ­O>\xe9\xea\xd7\xc9\xc9eu\x84H\x93\xae\x06" } leaves: { key: "CHM=" value: "\x17\xd2\xef\xb3\x15\xcf\xd1'\x863u u\xc7\xe9\x1b*8\xab\xc1\xee\xba\xe11\xb4\xb5\x8d\xb6\x92\x02,z" } leaves: { key: "CHQ=" value: "\x05\xe7\x0fG\xb8\x01)z.\x97\xab\x18m\x0b\xef\xfd!V\xa0\xd0r)\xb5v\x01\xba\xf5T\xb7$B\xef" } leaves: { key: "CHU=" value: "\x94;\x9d\x0fYQ\xe7\x9by\x9f\x90\xfe:\x19|\xceo\xb0\xd24\xb2X\x08\xc3\xe3\xc7_\x99_?sX" } leaves: { key: "CHY=" value: "J\xd3\xfc\x8fw>Yrja:\xc7\xdd\xd4\xd7^{\x1b-^\xf8\xa0\x8e[\x8am\"\x86f\xd8\t\xa2" } leaves: { key: "CHc=" value: "@lx\xb0\xef\x16\u008a\x1dy\xa4?~\x1e\xce\xf0/ax\xd4tu\xd6\xe4\xaaĶĸ\x007iI\x1f" } leaves: { key: "CHg=" value: "\xcb\x10\xdbʤ\xc1\xfbc\xb8x\x1b\x9b\x13\x80,Na\t\xc0\xb37^\x89\xa6@H\xb7\xeaIN\xe7k" } leaves: { key: "CHk=" value: "-\xac.G\x1c\xc7r.\xd0?o\xd5\xf0\x15K\xad\xe4\xf3\xb3\xa2\tz!\x16\x03\xcf\xdd\xe1jn)\x04" } leaves: { key: "CHo=" value: "\r\x93\xb6]\x17\xbaGo~\u009a\xa2 \x8c[\xf7.(F\x0c6\x89\x84\xc3\x00\x8e\xd3\xe6*\xe2\xdfI" } leaves: { key: "CHs=" value: "\xb5M/\xf8z\x8fL\x15\xc0\xa7\xed\xe6͘\xc8\xd9\xd6*ɘN\x06\x03\x9cl\xe0I*\x9e\xd92E" } leaves: { key: "CHw=" value: "\xb4\xba>\xda\"\x10\x9f\xab\xa2\xd6\xcf)\x9b\xdc\n\\\xeb\xd0jW\xde%u\xdd\xee/%" } leaves: { key: "CI4=" value: "ČĒq\xdcꚅ\x95\xf6\xa9Æ \xfa_\x99\xf0jL\xb8\xe3Ƹ\x8b\xa4\x108\xa4C#nz\x8a" } leaves: { key: "CI8=" value: "2H͉\xbdtWs\xd8\xeeVtĖĨ\xb7\xebC\xdc/\t\xb5C\xa3ßĩ{\xf0)Mz\xfc5" } leaves: { key: "CIA=" value: "\xa4\xe1Z\xe8K\xa5怔\xbc\x80\xe4p\xa5#5\xbd^\r-*,\x91Z\x01\xc2>qz-r\x19" } leaves: { key: "CIE=" value: "\x0b\xa2$Yqp';\xd0\u0084\xb9\x85U\xc0\xf2O5\xf0\xf9\x18\x80\xff\x07\r\x15\xc1\xd3ÅĻ\xfd^" } leaves: { key: "CII=" value: "\x8a\xa9\x12HL\xef\xdem\xe5\x13\xbb\x0e\x87\xf8 {E\xf1\xc8E\xa8e(\xcb\xc7Ej\x19r{?^" } leaves: { key: "CIM=" value: "Z\x1d\xa0?\xb7\x83\x00w8\xc1\x94K\x83\xae\xbb\xab=1\xd8Ôģ\xbe\x1d\xc0_\xb1\x08!va\xe4_" } leaves: { key: "CIQ=" value: "o)'k\xc4')؞\xc1\x10.\xa3h:\xfaYT5ě\xb3\xc55>)u\x11\x97[\xa9\xb5" } leaves: { key: "CIU=" value: "t\x0b\x15\xd8\xe0a\x91\x8a\x92éŊ‹;\x8d~k\x8b\x83~\xea\x7fx0Z:P\xf4\xe9f\xf8\x86$" } leaves: { key: "CIY=" value: "^\xd3i\\\xe8Q\xe7\xf4\x8e\xc7\x00ęŗ•ÆĄ\xee\xd9vUO/\x1c\x1ec\xae{\xa0e\x83\x19T" } leaves: { key: "CIc=" value: "\xa6\xef\xad\xf6\xea\xe6âļ’\x96~\x8c\x99]\xaeP?q\xa4\xe10]\xec8;\x96\xa3\xcc\xec\xe4\x9c8" } leaves: { key: "CIg=" value: "\xfe\xf7vĪ…\xe7S\xf2\xcb\x1e\x9b\x92 \x10\xff\xc5\x7f\xe1]]ÆŠVt\x8alÆš_M\xc8E" } leaves: { key: "CIk=" value: "\xec\xad_Km\x9aØŖh7\xf0Įŋ\x97\xb0VȂ\xf4sz\xdaC]\tZ'\xe1[\x84V\xbd" } leaves: { key: "CIo=" value: "\xbbqse\x81\x84\x97Ō\xbe<\xa8\xd4\xfa\x9f\x152^\x07\x18\x98\x04\x03\x0c\x87\xbdK\xb0\x03\xca0)\x11Ō¨\xba\x1a" } leaves: { key: "CK8=" value: "o\xc2XN\xa1\xaf\x92\xa8:\x88!2O\xed\xc7*\xda~\x0cL\x9fI\x85\x107Yp\x97\xde\x00\x82\x81" } leaves: { key: "CKA=" value: "\xd3҈\xb0\x14X*)\xcb)4\xfb\xdc\xf5\xc9!\x98\xf6\xb7\x94\xaf\x90\x06\xcds\x0c\xe1\x07\xb5\xf9\xc9\xee" } leaves: { key: "CKE=" value: "\x96r\xcfe)\x18\x0e\xdeF\xdb#\xcc~\xc1\xa1\x17\xe5b}\x08\xb3\xb0˃\xf1A<\xeb\xc3a\xe7l" } leaves: { key: "CKI=" value: "^\xbc&F\x04\xca\xc0Z\xbc\xb3\xa3fA\xbd\xfe+\t\x9d\x91\xcfDA\x11J\xb9\xcf\xe6\xb3?\x07\x8f\xad" } leaves: { key: "CKM=" value: "\x84\x9f,\x0c\xb4\xa0ÚēU\x11e\xf5ɧ\xbe\x8ex\xfa\x14\x0b\xeb\xec\xc6\xf3gT\x8c[\x88؟0" } leaves: { key: "CKQ=" value: "ReOk\xa6\x9d\xcbe\xaa\xe5q-ȟÛĸ\xdd\nj\xa8\xd0h\xe7\xfa*\x8b\x135ŗ\x15\xa9" } leaves: { key: "CKU=" value: "\xd6T\x885\x90L\x12Dk\x05\xd1|\x08\xa5\xd5?h\xf4\xe5\xbdr\xe4\xfe\xfcV\xbf.E}߁U" } leaves: { key: "CKY=" value: "\xf9m2RB\x9d\x97#\xf9z4\"\xe1\xca\xff\x80\x12F/\xa2\xb6\xf8\xb8\t0\xd1<ØŽ8\xa26" } leaves: { key: "CKc=" value: "Xl\xc5\xdc\xce%9\x18\t\xd07WŪˇ\xfem\xf0M\xd7[\xa1.Td\xf9\nO\x1f#ˑ\xd5" } leaves: { key: "CKg=" value: "I\xb22\r\xf0\x8f\xa3\x84vOB\x14\xd9(Smv\x89V\x0c]o\xf3\xf7D\xfb\x1cd\x1b\x13\r\xfa" } leaves: { key: "CKk=" value: "{kf\xe9\x11b͆\xacU\xd6\x0f\xb0z\x1c\x0bÕ¯4\xc8\x11\x9e\xa4\x8c\xf5>\x1e\xb0\xe9\xcd#\xe4" } leaves: { key: "CKo=" value: "\x90J?+\xd2%9\x84\xfa*\xc4Whwc\x8d\xfc\xc1\xf8\xc9E\x7f\xb3d>\x99$\xee\xfcL\xcb\xd5" } leaves: { key: "CKs=" value: "+Z!\x94\xea\x96t&\xfb\xfdM\xc7#rW\xdd#\xa8\xcf\x0c43P\xaaV-\x066\xc9߸\xe7" } leaves: { key: "CKw=" value: "\xbf\xb5k曗[\x93\xdd\xe2\xc3[\xed\xad'\xc0\x883\xdch\x1f\x93H\x0f\xf5RG\xe7t\x83\x05\xd0" } leaves: { key: "CL0=" value: "Rĸ\xf2\xd4sY\x01P\xf29g{~(\xae\xf4\x19\x822r\xbd\xafp\xa9\x05PN\xf6\xbd\xe2A" } leaves: { key: "CL4=" value: "\x157ÖŦ\xc06\x14_h5\xb6`bBɛ\x9a\x1b\x9e\r\xf4yČĒ\xb2\xe0Z,\x8d#\x83\xf4" } leaves: { key: "CL8=" value: "r\x126\x1b\x18ls\x04f}5Q\x7f\xe8ŨÔ—]B\xb2ÆŦ\x8c\xbdk^\xa1\xa0\x93\x01\xc6" } leaves: { key: "CLA=" value: "\xcb?\x83\x13Įœ\xd4\x04J\x82[\x84\xb6\x197\xb5\xec\xa2\x10!\x9a}Øŋ_/\xa8\xac>\r\xb60" } leaves: { key: "CLE=" value: "\x9e\r\x87R\x12\xff1\x9b/\xbc\xce\x15\x82\xaev/C4>\x9f\x12a\xabO\xf5\xf3\xddvJ\xe8G\xbc" } leaves: { key: "CLI=" value: "\xa8\xbaV}v\r\r\xe1\xc3FЃ\x19B\x80E\xa8\xc3\x11p\xb7\xaf\xef|\xba9\xaa˩ě\xa8" } leaves: { key: "CLM=" value: "\x93\x07\xf5\x02\x9d\xebQ\x92\x02`\x02\x8b\\U\xe0r\x8cu\xae\xf80\xfb\xa7\xc7\xc8\x13'\x9b\")\x90L" } leaves: { key: "CLQ=" value: "(\x90F\xc0G\x86\xee¸\xca1\xcar\xdf7,l9\xf0\x07BD\x98\x11j\xc8H*WB\xf9\x1d" } leaves: { key: "CLU=" value: "\\9\x92\xec\x1d\xc0\xec?\xc8\xfbUŲ”\xec\xe9\x19BS\xdd\xd7\xd0\x01w2y0d\xa6dX\xc1\xc8" } leaves: { key: "CLY=" value: "B0\xe0\x0b\xc0\xff\x8dIJ4ב_\xb3r\xddo9\xceb\x02\xdc32\xf7\x11\xe4\xf29Čē\x1e" } leaves: { key: "CLc=" value: "\x81\x1f\x12s\xe0(I\xfc\x1d\xeb\x1e\x0c\xe4\x92\x19\xedy\xf9\x97\x9b\xa2s\x8f\x9a\x14{\x88D\xb4\xab\xe4&" } leaves: { key: "CLg=" value: ",$\xb6(\xab\x95\xe0\xcbW3'E\x0elcOĮšØ„\xb0Re\xeb\x03\xf9\x19\xdc\xf5@\xf7b" } leaves: { key: "CLk=" value: "\x05\x0c\x8cd\xe9\x0e\x86Ņ™\xad\xc8m~m\xc1Īŗ\xe8۟\x94:H}\xe5\xc5\xe2\xc4\x13w\xf6\xd7" } leaves: { key: "CLo=" value: "S(\x0f\xda\xfa\xfd\x85`\xc6}?\xe9%\xe0\xe4\x05(Q\x88\xac\xf1\x8f*}ZĪĨM\x93;\x0e\xb1" } leaves: { key: "CLs=" value: "\xde.3=\x16\x85r\xaa\xfb\n\x1d\x91MsÈԞ\xcd\xd9\x7f\x98K\x1d\xac\x98\xbf$vv\xe0\xf0" } leaves: { key: "CLw=" value: "\xd2Z\x98l_\x81\xba\xea\xedD\x15\xe2\\\x83@\xfe\xee\xb4\x0e\x1b\xce\x17;\x91N\x0e#\xc1\xc4\xe6~\x05" } leaves: { key: "CM0=" value: "\xfd#\xa1\x05f*\xe9pŲž\xd3n,\xc2U\xd9\xde\xfb\xf9\xedP\xbd\xc2Ũ¸\xf0\xde\xf6\xeb\x9c\x07\xdc" } leaves: { key: "CM4=" value: "Y\xdf\xf6\xcd{\x86\x91М>BE\x12\xe4\xa8e\xb3\xa0\x15\x86\xdc@\x8a\x0b\xc5A\xbao\xf5n\xdcH" } leaves: { key: "CM8=" value: "\xba\xb8\xd31\xd4\xe5L\x03LtX\x90\xb1\x8dis\xefV\xa8\x7f\x82O{J\n\xb5D]\xa3x\xe0T" } leaves: { key: "CMA=" value: "ɀ\xbe\xaf\xa3\x99$\xf7\x0cR\x1eB\x86\xb8\x10\x7f\xb3\xbb\x87&A\xdc\xfa\xf5ğ%q\xb9SM\xc7" } leaves: { key: "CME=" value: "\x03\x05#\xdb\xcd\x04(\x80ɜ\xd4_`â‚ĸ\xf8\x9d\xcfcō†ž¯Wg\x0f1\xeeL\x83\xd2" } leaves: { key: "CMI=" value: "wgb\x1e\x9d\x80o\xee1\xb5l'f\xda\x17\xd7\t'%\xab\xaa\x15\xf2\xe4\x16[.\x14v1,\xda" } leaves: { key: "CMM=" value: "\xba\xe4\xe7)~\x0b\xa8[\xdb5<\x02\x86\xcc^\xbf\xb5\xd9\xdc1\x9b\x9e\xabYQ IL\xa9rr\x89" } leaves: { key: "CMQ=" value: "|\xee\xdbߨ)\xd0Ū™`\x8f&\xa6\xbe\t\xb7~\x00\xd3)1\x00\xc7\x05\xee\xc4\xf2\x07-\xd2k\x1c" } leaves: { key: "CMU=" value: "\xd4!V8\xa6&\x8bo#CK\x01.\x99e\xef9\x19e\x00\x9d*\xe3ͤ\x81\x9f\xea\x0f\xe9҃" } leaves: { key: "CMY=" value: "\xdcW\xe9\x8bu3^\xe2) yX\x96\xb2ßT\xffp\xe7\xf18\xf6\xc0O\x85IyC\x00um" } leaves: { key: "CMc=" value: "\xe8փ:\tW\x05\xa7\x86;H\xe73\xe5\x9c\xeb\xf5\xedn\xd0\xe4\x145Y\x01\xeec}\x1fL\xd1\xd0" } leaves: { key: "CMg=" value: "\x8f;\x80\x9e\xad`c>\x1b\xd7Ds\x8d\xa8ş\x9a0ČŠ\xee\xac\xe0\xe9\xdc\x10nJvQj\"" } leaves: { key: "CMk=" value: "\xee\x13\xdf\x14\xb4\xadw\xa0~\x95\xdf\\\x1atk.JQΗ\x87\xd3\xffK\xf1\xceX\x99:ĪĒ\xf6" } leaves: { key: "CMo=" value: "\xae\xf1N\x1c\xb7\xe4\x87\xf4\xa0\xc9\xf3)U\x81\xfd\xed\x87\x00\xaf\xba\xdb\x05\xfa\x9an.ߕ\xbdA\u0081" } leaves: { key: "CMs=" value: ".Q\xeb\x0f|a\xd8卧Q%\xe3\xf3g\x0chX\x91(\x98\xf6\x9f\x04(\x9fl#2x\xf7\xbc" } leaves: { key: "CMw=" value: "\\\x0e\xa7\xdfULP\xe9|b\xad\xfeM\xbd9\xd2\x1d\xb7\x06\x7f*\xa3ՃCS\xb0q#\x04_\xcf" } leaves: { key: "CN0=" value: "\x07\xac\x00 EZ\xf0\xa2\xf4\xe2\n!94d\xd9\xf4ZĐĨ\x87Jg\x88s\xe05\xe0\xe6D`\x1a" } leaves: { key: "CN4=" value: "\x8b7\x0b\xe1\xe9w\xec_\x9b\xa5\xbd\xd6\xef\xc2\x18\x0f\xda;\xf5\xc1\xba\x98\x92\x15++Q\x9d\x05\x80ž" } leaves: { key: "CN8=" value: "\xeb\\I\x8dO!Ĥ^\xdc(v\xd4\xef\xa1\xd6Ė—\x9d\xb8\x1b\x95XA\x1a\xe3DK\x13}P\x9e" } leaves: { key: "CNA=" value: "\x85~\xac\xa7=\xa5\xfc\xd1\"\x96i=uYk\x04w\xff\x18b\xaa\xed\xe7+j\x97\x16\xed(I\x12\x05" } leaves: { key: "CNE=" value: "\xb5\xd8O\xeb\x9b\xd6T\xc5\x1b\x14p\xd2ר(\xce6L\xcd\xe39c\xfa\n,\xfb+r-^2C" } leaves: { key: "CNI=" value: "q\xc5\x06\xef^m\n\x0e\x0f\x1e\x89\xd8D):X*q\xdf\x10U:3'>\x16\xbc\xf0\xed\x12_\xcf" } leaves: { key: "CNM=" value: "4^Íĩ0\xbfiŌē\x07P\x8bb\xf3\xb3\x8el\xfc\x18\x9fs\xa3\x89\xda\x1f\x81\x84\x00\x06\xa8\x8a4" } leaves: { key: "CNQ=" value: "\xbf_=I\x0c\xbd\x9e\x9c\x88k\xf6\xea\xcc8\xa7pG\x88\x823\xc7\xc9\xfaf1\x7f\xdc\xda\xf5\x9flV" } leaves: { key: "CNU=" value: "\x16dR\xe7\xc2\xc7'n\x01æĨ‘\xab\xb8^\t|\xf3\x93\x98\r¡5CL\xbd\xdc\xc5\xf9v\xd5" } leaves: { key: "CNY=" value: "\xc6'\xd2h\xad)MԖ\x9f\xfe{o\xfcŨ¸\xe7\xbb3\x1e\xbbU\xa4x\x9b1Y\xacP\x92\xdf#" } leaves: { key: "CNc=" value: "\\_\xc4\xfb\xbb\xbfT\xaf\x91\xaa\x9eiŪ—\x86&\xa3\xe2\xf6\x06\x94\xb5\x13\xcc\x19>\xe5o\x91\xd9GY" } leaves: { key: "CNg=" value: "\xb6\xdcu\x06\xfba\x0e" } leaves: { key: "COU=" value: "\x0c0\x0f\xe7C˞\x97\xf7\xf5\xd2g\xe4]\x92\x9e4k\x1b\xb0\xdf\xf1\xaa\xc4fY\x84\x1f\xfe\x89>\x93" } leaves: { key: "COY=" value: "5\xc1)=\xff\xc1\x1b\xaa\xd5,\xb5\xe3\xc2Ûĸ\n\xd9СH͟\x98\x8dX~\xf2ZxKmV" } leaves: { key: "COc=" value: "5\xa5\xf0`n\xb8\x9d\xc7߄\xec\xd9\x0e`\x14\xecĘ´c/\x0f\xf1@\x88\"O\x8a\xf9\x06PS\x02" } leaves: { key: "COg=" value: "\xc1*`B057\"S\x0f\x16KR\x06eX\xde\xfb@\x86\x92\x14()\x1e\x9a\x8e~B7\xa6j" } leaves: { key: "COk=" value: "\x9fh\xae\xce#\xe1K\x12xŅĄc\xa4?\xc6\xd1;\xbed\xfa\xdaz)Th\xd3\\\x99\xb8u\xea!" } leaves: { key: "COo=" value: "D\x08Χ~\xb2\x1f\x1fl\xfc\xbf\xcd\xd0\xd5Zx7}\xff)Y\x1a\x90\x83\x10,\xbf0\xa0\xa4\xcb\xd5" } leaves: { key: "COs=" value: "\xb9\xa2\xc43\x0f#\xf0\xe8\x93K\x83\x97Z\xb7\xff\xbf\xf9\x8c\n\x8e\x89L\x0f\xb1А\x87\x9c\x96k\x8c\xa2" } leaves: { key: "COw=" value: "\xf98߀V\x8c\xb0-\x8b\x8a\xd2d&{\x12A\xf4\xdct\xb3Wh\xf4\x88؁ßĻ\xd4\xd5\xd7D" } leaves: { key: "CP0=" value: "\xe4[\x83\x8d:\x82i1햑\xc64\xd5.\x0f\x88W$\x1d\x01\xa7)_\x92\x93\xb2\xed\xd1\xfew\xe1" } leaves: { key: "CP4=" value: "\xca0\x82\xffE\xea\xbd\xc7ko\xfd\xf3s`\r1\xdc0\x05\x93\x8b@\x14\xe8\xa7\xc1m_Si\xe1h" } leaves: { key: "CP8=" value: "\x94\xe5'|/.\x8e\x1aM\xe8(\x8f\x8c\xf3y#u5\xfd\xfaD>\xff\xc1\xe1\xf8$\t\x04>OJ" } leaves: { key: "CPA=" value: "\xf9o\xc4Q\x81\x1c\xc8\xf4\xe1l1\x15n\xcf#(\xb8\xec'\xff\xc3P\xdfS\xfb\xcd\xcbh\x92|\x9e2" } leaves: { key: "CPE=" value: "J\x94\xe3\x04>\xc9\x14\x92\x9f\xa4\x07\x1c\xd0\xd3\x04QW\x0f)\x8a:\x8c\xa2.m\xbb\x99xi\x16!\xe0" } leaves: { key: "CPI=" value: "\xa7\xf4\"\xdc\xd7M\x82\x02\xc0\xaf\x0e\x11^9\xeb\rp\xea\x82 \xbc\x01\xe1u\xac\xcc\xd3]\x1b\xf6\x95\x93" } leaves: { key: "CPM=" value: "h\xb5\xb3\x87\xbc\xad\x8a\xcf\xd3\x1b\x81\x03\x91\x90K\xa0z\x81<\t\xf3\xd13\x9d<\xc5F\x06<\x81\x06\xbe" } leaves: { key: "CPQ=" value: "\x81-\x0bu%Ê\x9d\xfb\xd2\xcb(\x1an\x9e\x0cq\xf4\x18\x9cG\x8eK\x8f\xb4p\x8bڟt\xa2\xe3" } leaves: { key: "CPU=" value: "\x1e\xd2ŨĻ\rSC\xbf\x10\xdfOĖŽ`\x83\x14\xef\x83r\xfb\xc0\x82y,\xfb\xe9\xf0\xc1bB\xb4\xae" } leaves: { key: "CPY=" value: "\xb0|v\xc3@\ts -\x90\xe6\x1c\xaf\xb5f2\x913\xd3Ty\x80\xab=\x187l\xc5\xc7WßŊ" } leaves: { key: "CPc=" value: "y\x1a\xb0\xc7\xf3\xd0\x06\xe8\xa5\x15\x0f\xe6\x89\xc5\xc4]\x92\x05\x8f\xe6\x98\x02^W\xf5\x95\xa0ĮEb\x9d" } leaves: { key: "CPg=" value: "A\x88wg\xb1\xc7Rp\xc4\x0e\x17(\xe9Z\xb5Î“Ō \x164\xb8 \xb1\xe0" } internal_nodes: { key: "AgA=" value: "\x8f\x05[\xe4\x9bG\x8fW\x12\xeb\xe2\xd8[\x89˔m\xccP\x87\x18_\x1d.\xb66S\xa5\x99\x81\x08\xce" } internal_nodes: { key: "AkA=" value: "\"3\x03\x1d[%\xa4\xec\x90\x1b\xd1\xe19\x8d\xda8\xe5oT\xdd\xd4y~\xfa\xb5tT[艊\xee" } internal_nodes: { key: "AoA=" value: "\xc0\xe3l-:\xcfj\x92ᯅ\xd0\x14\x98M\x0f\x9bĮŒWEX\x8d\xc2;\x0f\xd7b\xa4\xb5_\xb0" } internal_nodes: { key: "AsA=" value: "\x0b\x08\x8c\x91\xcd\x11\x0f\xefh\xbdB(\xf0\x1e\xf2V\xb1i\xf8\x1d\xae\xa4\xf3\xc0f\xa3\xb7\xf4\xd8)N\xf8" } internal_nodes: { key: "AwA=" value: "\\\x99\x84e\xee\xd0i2j\t\xcc>\x8bwC\xcb\x08\xa0\x9a\xbc\xb5\xe4\x14r\x03\xb7\xc5A\x9e\x9f" } internal_nodes: { key: "B0I=" value: "\xb1k\xb7n\xcbs\x14\"ĖŦL\x0c\x9f\x96\x87w9\xc7+\x97\xfae\\A\x02]/\xb7\x19NY\xaa" } internal_nodes: { key: "B0Q=" value: "\xea|6\x15q\x14*b\xc6\xc7\xf5\x18p\xba\x1e\xd1\xf3\x1b\x1au\x8c\x02-\xa0\x1eq\xc6\x1bn\xb8\xc7\xf9" } internal_nodes: { key: "B0Y=" value: "\n\x93\xca+u&:Go\x83\x05<\xf4\xfb\xe0V\xaf\x19\xe8\x94\xff\x03|\xee\xe1\x06\xc4:~s\x87\xc1" } internal_nodes: { key: "B0g=" value: "\x8c\x8f\xdb\xcb\xed\x11p\xb4ėžą\xce\x0e\x03\xff\x81\x87\r\xb3\\ads\x8dˁ\xe4\x0c\x83\x9a\xa8\xd7" } internal_nodes: { key: "B0o=" value: "\x9e\x87Ų’\xe8\xf8\xa8\x87\xee\xd72\xda\x17C-\xa8![J\xab\xf7\x89ɞ\x156סh+\xef\x1c" } internal_nodes: { key: "B0w=" value: "\xcfb\xfem7P&\xd4Z\x18gG\x92Z\x08G7\xc6}\x15H\xf1\x84fLOM\x83\x194\x80\xf3" } internal_nodes: { key: "B14=" value: "\xe2\xbe\x03\xde5\x01\xb8|\xa7\x8a\x9d\xa4\x1b%\x022P\x83l~\xb0\x1f\xb9\xa8\x95R3\x83m\xc8\x12]" } internal_nodes: { key: "B1A=" value: "/\xc0=\x0ew\xe5\xc1k\x8c\xb4\x9f\x94]|\x98\x11\x14\xaf\xef\xedҐ\xa7\x11\xad}\x7f\x80Km\xed\xf1" } internal_nodes: { key: "B1I=" value: "\xddA\xa6\xc7\x00r\x00ab\xd0\xf9\xc4s\xa4n\x96\xee\x1e|\x89#\x96a>M\xad\x88\xd1Ԋ\x1c\x19" } internal_nodes: { key: "B1Q=" value: " \xd7\xf7\x0f\xda\xc8}\xa9<\xb1\xa7\xe3\xc4\t\xcez\xf4ÖĒ\xf5\"\x99/\xb1-\x89\x8c\xf3\xa6/-\xf9" } internal_nodes: { key: "B1Y=" value: "\x8a\xa9\xd5\x02&\xebm=\x9a\x97\xd5\x07S\x00\xdf\x07\x10sR\xa7&K\x1c\xf5\x0e9\xa3\xd1\xee\x00\xf2\x98" } internal_nodes: { key: "B1g=" value: "8(\xcdG\"\xfe\xa4\x02\xf8\x19=\x9ax\xb4H7\xdaXKy5\x8a\xde\xffhL\x18\x19^\xe9\x83\xc7" } internal_nodes: { key: "B1o=" value: "\\|RΉ\xedV\xbd5(\x8e&\xc0p\xbd\xd0U\x1c\xb9\xfa\xd7\xd1cr\xa7Ķē\xbe\xbc\xeaD*" } internal_nodes: { key: "B1w=" value: ">j\x15A\x0c\xd5_\xa4\xf2\xe98\x83\xe2\xc2z4\xee\xc5\r^q\xee8\xac{Z\x18f.z\x82\x00" } internal_nodes: { key: "B24=" value: "B/{\xe7\xdezn+\xb6\xafb\xc8ۍ\x97\xf8x\x03\xd9u\x1f\xb4\x91\xa5Y·o@\x05\xe65" } internal_nodes: { key: "B2A=" value: "2\x9d\xf5w\xdd\"\xe4$KB\x9f\x8f\xdfB6\xe3H\x93\xefZ;z\xffK\x06H\xe4q\xed\xf4\x9e\xb3" } internal_nodes: { key: "B2I=" value: "\x9e\x11\x10\xf0\xbb\x8e\xed\xe6\x88\xebEbF;\x1d\xe5\x19.\xf1\x93\xd6y\xa0\x83\xc8^X>M{\xe7s" } internal_nodes: { key: "B2Q=" value: "\xf7\xb8\x88\x03\xa4U\xd6\"P\x8f;K9\xf1\x99\xa7\x14trs\xcaipL{\x94@\xe2-\x9c\xef\xc9" } internal_nodes: { key: "B2Y=" value: "\xe7\x155\xa7\x10\x17\t\xaf\xb0{\x86\x8cG\xfe\xb5\xd7U\x1c\xcc\x06\x8f\x15{\xeaÃļ\xb5z\xf6ix\x15" } internal_nodes: { key: "B2g=" value: "\x82\xbf\xaa\xc7\xf4T\xf16\xb6\x1c\xed\"\xa7\xc8'v\xa3\xda\xe7\x94\xe7\x97>\xa36\x1b\xa0CS\xb4x\x97" } internal_nodes: { key: "B2o=" value: "\xb3\x0c=\x1e\x1e\xd4n\x98^w\x85\xeaA\xd1`lx1]\xa1G\x1d\xe5F$dį–Ģ_\x0e\xea" } internal_nodes: { key: "B2w=" value: "\xba\xe4\"\x07\x9a\x02\x98\x9fj\xcd7\xff2\xac\xa7o\xf3\x96zn\xa5z<\xa1J\xc8Ũ“=AĶž" } internal_nodes: { key: "B34=" value: "Nd\xe1\x14C\x81\xa6\xea\x17ŲŽ\xabÍē\x99\xed\xf4\x90sE\xb8o\x16\xf5\xd7ܯGS\x00Ws" } internal_nodes: { key: "B3A=" value: "\xed\x95.\xa3\xfeŅ‘xŲ‹\xb3h\xf5U\xfeF\x02\xfc\xd0q\xe1Xf0\x9b\xb2\x1d\x93\\cY\xc3" } internal_nodes: { key: "B3I=" value: "Ęĸ\xf5\x98\xda\xeet\xc2\"\x1f\x0e1\xae\xe8I\x04\x82\xcd؟%\xe5UA|\xd9S\x96?5ߒ" } internal_nodes: { key: "B3Q=" value: "\x11\xf0\x162\x0b \x13\x1ao\xd6\x02\xe0\x0f8\xa1H\xbaI\xd3c\xa6\xca\xd7D\x88L\"\x03\x9dC>8" } internal_nodes: { key: "B3Y=" value: "\xfc\x9b\xe4\xbbSLoimP\xf6\x80\x9a\x08\xc8i(\xc0\x95\xb9\x92\xe3\x87\xeb\x94d0\x19\xe1T\xcc\xed" } internal_nodes: { key: "B3g=" value: "ߐ\xaf\xc0\x04\x1e\xf9$p#\xab>\x05\xb9\\&\x8d\x83\xdaP^\xc0\x98\xf4o\x06\x97\xe7\xc6\xe3\x9a[" } internal_nodes: { key: "B3o=" value: "#@\x1c*\x9c\xbe5(Ku\x8b\x85-v\xa5\xa7\xb4\\&w\x13\xa5x\xb0\x18cHN(;\xcdW" } internal_nodes: { key: "B3w=" value: "J:\x85\xbdC\xe8\x1d\x13\xd6\xd1\x1f\xbb\xe4\xdf\x00\x10\x844p\xa0\xa8\x83P\r-e\rW\xcf\x1e~\xa3" } internal_nodes: { key: "B44=" value: "\xb1\xe1b\xac~mQ\x1d\xe9\x05\xe9\xe8\xe8\xaf|\xb1p\x84\xfb\x14\r^U\xfb\xa7\x80(\x8a?\xe5\xdf:" } internal_nodes: { key: "B4A=" value: "\xb5B\x18\xb0\xdb\xfb\xe3\x1fP\x8d\xceF\x11\x81\xb0\xdf\xd4]\xa3VrŌ´&~\xbd\xb63\x96x\xc0~" } internal_nodes: { key: "B4I=" value: "\xba⌠\xd6/WC\xf9\xa4\x0c\x0bccn\xec\xad\xcd$͡Ɍ(\x03x\x1da\xc4\xecFd" } internal_nodes: { key: "B4Q=" value: "\xbdo\x0c\xeb\xcenf\x8aF\x10^l\xc1n\xf1\xb6g\x934\xc4\x0fY,>7\x83\x19Dw\x84\xe5\x16" } internal_nodes: { key: "B4Y=" value: "F\xad\xb4T^\x90\xbd\xff\x90d\x0b\xf8\x11\xee\xaf\xe9\x16\x88\xe3\xe5\r\xc9a\x03\xb4יe\xc7Xy\xa0" } internal_nodes: { key: "B4g=" value: "\xd8\x19\x9a\x88x\xf3K\xddv\x08Me\x15\xcc:\x07XN\xea\x83.\x8bF\xb12\xdcס0u]\xc9" } internal_nodes: { key: "B4o=" value: "\xea\xea6\xfb\xcb\xe6\x03\x99؇\xd2\xd2\x03o\x1e\x8e\xa7\xe6\x90)\xb8\x16\xe4\xfe\x1dY'\xbev\xb1C\xbc" } internal_nodes: { key: "B4w=" value: "D\xe3S\xb8\x85d\xcc-(Ī“\x14\xf0\x0e:m\xe9`=\xfe}\x83\xa9\xc1\xf6\xb1\x93V\x07=\xcb\xec" } internal_nodes: { key: "B54=" value: "s\xd6\xe8KT\x0cm\xaeTs*_MD\xb5\xd03D+\x9e\x87\xec\xaf\x03\xa4\xd6)\xa2Z\xe3\xbf\xd5" } internal_nodes: { key: "B5A=" value: "2\x169N\x03?d\\\x80T\"\xfb]\xdf\xf2\xdc\x10y\x15_\xe3Ū‘/5!:\xbd\x84\x8a\x0ca" } internal_nodes: { key: "B5I=" value: "\x87\xd8 \x9a\x8fN\x8a7;\x07k\x12\xb09\x19Ō›\x955\x1c\xcc6\xf4\xd2.\xe5jâĨŒ7J" } internal_nodes: { key: "B5Q=" value: "\x08\x93ؒ\x93\x8am\x9d\xd2\xde$\x82\x94\xc1\xfa\xb7\xd3;\xdb\xf5\xabaÉĸ5r\x8e\xf3\xebo.\\" } internal_nodes: { key: "B5Y=" value: "K3\xdc\xd4\x7f\xd9~-x\xee0bɈXe.\xa6\x8f5\x89Y\xa0\xdf\xc7j\x11\xbd\xc5!\xb5\xf9" } internal_nodes: { key: "B5g=" value: "\xfbÛ\xed]H\xa45\xde\x1dm\xc9\xf8ŅˆÜ\xa2\xf3\xb0\xbd\x18\x10\xb4\x1cL9y\xde\xd1\x1a\x1a" } internal_nodes: { key: "B5o=" value: "K%\x91s\x12\x12\x10\x95E]\"\xad\x04Z>d\xef\xa2\xe8\x9d\xe2\xd5\xda\xdb\x05)\x82\x1f\x1eV\xa0f" } internal_nodes: { key: "B5w=" value: "1\x89eR\xe6\xf2\xff\xc9\xdfn\x83~\xc0G\xb7\xf0v8\x85\x00ڕ\xf2\x03\x97sZ\x9a\xab\xb6\xe1\xf1" } internal_nodes: { key: "B64=" value: "\xad\xd3 \xcd\xcd\xee\"\xfeTW\x00\xc0\x8b\x9d\x1e\x82ds\xec\x1a\x8céĢ*\xe8@\xfc\xc1k\xfeA" } internal_nodes: { key: "B6A=" value: "B\x91\x87O\xdc\xde\x14]\x9b\xd2ƌ'\x1e[\xb5\\&\xc89^Cx1\x8e\xe7×Ĩ\x94i>\xc4" } internal_nodes: { key: "B6I=" value: "\xbeD\x1avNz\x8e4MA5\xfcR\xf9\x1fF\x8d\xc0C\xb3x\x01\xc2\x17S^\x95\x05\xa0\xa1YD" } internal_nodes: { key: "B6Q=" value: "\xb1\xc18\xfbs\xb6{@\x8d\xef\x87\xd4\xfcE\xaa\xa0E\xa7\r\xbf\xe2,rB\xb9\x90Y\x14Ä\x83\x87" } internal_nodes: { key: "B6Y=" value: "\x89\xcf\n\xb2\xbb\xaae\xb4\xd1\x06iZL\x00\xe9JT\xe04Oæēĸ\xc0\t\xbc]\xf9\xeab\x95\xed" } internal_nodes: { key: "B6g=" value: "\xfa6\xde\"\x16tT\x8d \xe1\xee7`\xc9\xee\x8dm^\xb0\xec\xc8\xf7\xd1?0\xa2\xbdqO" } internal_nodes: { key: "B6o=" value: "\xb4\x93\t\x9f\x81(\xe5,m\x05\xbf\xa6\x88\xf1\x0c\x10\x8d\x8b\x81G\x17\x1c\xb4\xb3N\"\xb6\x046\x92\x15\xc9" } internal_nodes: { key: "B6w=" value: ":\x17\x14\xa2\xa2\x05\x01\xfe4\xa0\x9e\x9d\x0b\xbb\xe37\xfe\xd3+\xdb\x0eu\x7f\xac6\xac|\xf6yb.\xef" } internal_nodes: { key: "B74=" value: "u\xf5T\x835%\xa6\xee\xb1\xd3\x02\xf1|SL\xcb\x07\xe3\x80\x055\x9b\xf6\x9d)\x81\xdf\x1d\x1eZ\xa7N" } internal_nodes: { key: "B7A=" value: "BB\x01`\x17\x11\x12\xad\x8c?\xba\x80RS\x96\xef\x9b\xd74\x1d\xbb\xb7\xbb\x02\xf5\xf4M\r_\xc2\x1b\xc8" } internal_nodes: { key: "B7I=" value: "M\xb4\xae\x9c\xff\xb6\x0f\x8fQ)\xe4\xf3\xdf\xcbh\xf2{\xafL\xfa׀\xed3G=\xf3aUs\xcfq" } internal_nodes: { key: "B7Q=" value: "\x8bC\x88)\xcc\xd4\x13=\x1c\xdb\xf7\xbdŌ„\xc3R\x84W\x84\xdb\x03\n\x9c\xa5\r\x06f\xe7\xbbr\x95Z" } internal_nodes: { key: "B7Y=" value: "\xbdB\xc4\x16\xff\xc4j\r#\xac\x105\x85c^\x02bs]\xf8^\xa2\xd6!n\xc6an\xbf\xdc\x0e$" } internal_nodes: { key: "B7g=" value: "`\x18Z\x8607\xdd_\x06\x97SF\x12dw,\x98R\xa4$\x98\xd1\x18\"\xb1<[r(v\x96\x98" } internal_nodes: { key: "B7o=" value: "\xf1\xee\xe5\xb8rzmcWvF\x8a#W\x89\xcf\xf5\x83\x17Y\xcb\x12ŌŧH^+e\xf6\x96\x0cp" } internal_nodes: { key: "B7w=" value: "RV\x14\xec^.פ\x85\xc1ThIC\xff\x10'\x8bXb0\xcf\xc8H\x11^7Bf\x87\xff\x08" } internal_nodes: { key: "B84=" value: "\xf5?},$\xab\x17FØŠ\xfd\x88;\xd0J\xc9s\x06\xe2\xd1\xec\x07\xdc\xf3\xf3w\x7fz`\x0b\x7fv" } internal_nodes: { key: "B8A=" value: "p\x03M\xbd\x87\xab\r\xe4ï\x8f\x0f\xac\x13U\xf3\x1a\x85\x95T\xd1R\xd2t\x95\xc4\xd1\xfc\x99X\xe1\x00" } internal_nodes: { key: "B8I=" value: "\xccL&\x94˲\xdbd\xbek^\xce-\xe3\xd1\x0e:\xb86\x1f\x8d\xcd\xde,\xed\x9c\x07,J{b\x1c" } internal_nodes: { key: "B8Q=" value: "\xed\xd7\xedu\x81|3\x85\x1b\xc1\x13P>4\x9b}a\x1c\x11$\x7f\x92F\x19a\x90~\x05\x92\x99\xb1\xdc" } internal_nodes: { key: "B8Y=" value: "Ô´\xe4R\xde\xe4\x88\xe8Ǝ\x83\r\xaa\x1a\xbe\x1f\xb2\x99!\x8e\xcai~f\x9b\xc3\x18\xe3L\xc2\r\x07\x97Į˜&C\xac\xefV" } internal_nodes: { key: "BBA=" value: "\xf8\xcd \xe2l\xf2\xa8\xa3ؚ\xda\x12͚\x12\xa0\xbaCb\xe4\x05\xde\xc1%O(\x81\x13\x19\xc0\xc9\x0b" } internal_nodes: { key: "BCA=" value: "RF\xff\x08\x15#\xf5^w\xa9\x8e8&t\xb8\x9c\x1a+\xb7l|Ξ\xb3W\x15r\xa6R\xb6\x07\xea" } internal_nodes: { key: "BDA=" value: "FT\xc2Ř\xf8v\"\x93\xe0;\xddjF0\xed\xcd[\x00\xb9zX\x90\xbc\x1d\xb3r~}\x03TY" } internal_nodes: { key: "BEA=" value: "QÛ {~\xf0g=\xa3\x9f\xbc\x96)\x85\x05t\xd6\xe9\xb0H'\x07ZD]x\x10\xbeE\x0c\xd2{" } internal_nodes: { key: "BFA=" value: "\x1b\x8f\x04\x99y\xc7\x0c\xbe\xfc\xael \xd4%tx\x0b<$\xb7\x1f\x90E\xfc\xf6!\x88k\xbc7\xbb\xb5" } internal_nodes: { key: "BGA=" value: "\x9c&\xf7\xfc\xa6\xd8830\x1bO\x06nc$\xd87\x88l\x0b\"\xe9\x10\xfb\xb98\n\x01\xf1\xf2\xea\x13" } internal_nodes: { key: "BHA=" value: "\x84\x14\x17\xa5\xa85\xedã€˛i\xbf?\xea\xacZx?s\xc5/\x14lIZ\"\xdd>0\xcbqC" } internal_nodes: { key: "BIA=" value: "\x96\x8c\xdaN\xdc8\xb3W\xc8j\xbdn{\x8cu\x04`\x92Xi\xe3\xc5m\xfbă$\xe1\xe6\xf5\xa4\xd5" } internal_nodes: { key: "BJA=" value: "\xc2\x0c\x92W\xd9#Kr\xf6\x03\x04\xb7\xe0\xdc\xee'\xabčēƒ\xf9\x99 o'\xe0\x0b\xb3\xba\xceJ\x07" } internal_nodes: { key: "BKA=" value: "\x1aSf\xeb\xbd\xfc~\x05\xc0\x9ctV\x8b<\"}\xa9CG\xe9\x97 k\x92w\x91^\xe9Ƥ~B" } internal_nodes: { key: "BLA=" value: "2\x8872˛\xe3*TxM\xbagw\xc3\xce K\xfb\xe6z8;\xb5\xae}z\x80\xd7U-\xc6" } internal_nodes: { key: "BMA=" value: "0m$\xe3\xd6ߊoЅ\xc7\x1f\xa1_h\x1eQF\x0f\xefQ9i\x95\xf7\x11+\x9bp~\x8a\x91" } internal_nodes: { key: "BNA=" value: "&\x1bj\xbc\x91\xa9\x0f7#\x1e\xde\x0f\x00\x81\x1d\xbfM\xba^C2\xe4\xb0\x15'\xaa\xba\x19R\x1a\x07\xed" } internal_nodes: { key: "BOA=" value: "\xad\xdc%j]\xbd\x8ep\xeb\x9e\xe0,%ZM(6\xfd\x0b\xd5(n'k\x8f\xa8\xa4\x8e\xd97\xcaJ" } internal_nodes: { key: "BPA=" value: "c\x80\x1b\xe4\xe8z\xa9\x8fNP\x88>\x0c\xeb\x97s\xe8\"\xaaI\x9f\xe5t\x05!P\x1dx\xf0g\xd6|" } internal_nodes: { key: "BQA=" value: "\x01d\xda\x05q\xfc\x8f-\x10J\xec\xc0r/\xf4\x91ar\xb0$i\x032\x99\x9eɸ\xea\x1d\xe8\xce+" } internal_nodes: { key: "BQg=" value: "\xc9$\xaeʛ\xf8\xffn\x0f\x8e\xa1I-H\x1aV\xc0g\xf8\xa1l$\x03`$\x08\xfafh\xfc\x80\x01" } internal_nodes: { key: "BRA=" value: "w\x08\xeb\xeb3\xf2\x1b\x97\xd5%\x19}b\x00Lc\x1e;W\xb7?K\xaa~N\x9f\xa1i\xbc\xb8\xd0C" } internal_nodes: { key: "BRg=" value: "\xe9\xde\\y\x17\xdd\xeeL2\x1a\x81U\xe3\xcbb\xf3\x1b&\xabl\x9b\xd3;\xef]Ũžq+\x12\xfbd" } internal_nodes: { key: "BSA=" value: "\xe9\xcd^Ή\x0f\xf3\x9e\x13dt\x08\x06\xfb\x1f\xb4\x83厃\xba\xc9\xf4c\x1a\xb2pUlZ\ru" } internal_nodes: { key: "BSg=" value: "\x05\x83\n\x06\xd0b\xfcKa\xa3\xe2\x99`\x7f\x1b)2\xf2\xd6\xdde\xb0\xfe\x07\x99]\x81\xbe\x9b\x1e\xa9 " } internal_nodes: { key: "BTA=" value: "z\xc4W\x0f\xf1\x87:\xe1t^\xf0\x0f\xd0\x1e,\xddT\xa8\xba`\xac\xb5\x9c\x16J0\xfbp\xc5y\x8f\x0f" } internal_nodes: { key: "BTg=" value: "\xf8\x85\x97\xe1\x1e\xaa\x00)4hR\xcaD\xc4&Ė‹@\xae\xe5\xe6k9.F\x1d\x04\xa1?\x82\r\xbf" } internal_nodes: { key: "BUA=" value: "\x02\xa1\xfds\xfc\xb0\xfe\xc2\xd5\"Piz\xf2\x1c\x7f\xe9z\xce\xca\x1c\x89\x11\x9d\xc2=$%\x9a+!\xa6" } internal_nodes: { key: "BUg=" value: "\xcb_\xbdC\x9f2\x8d\xf5P\xc30V\xff|\xf8\x16\xfc\x8em\x96>\x1ec@\xe9X\x8aŌŽ=\x11\xb0" } internal_nodes: { key: "BVA=" value: "\x85y\x9a\xcb3\x17\xee\x85s\x859_n\x87\xb3q-\x0e\x95:\xd6^m\x80\xa3\xbd^?Y\x90b\xf5" } internal_nodes: { key: "BVg=" value: "\xa6\x91q\x15B\xdbqo\xb6&\xbb\x08\xa2\xaf\t\xf6@\xee\xff/@\xc7\xfd\xf3\"\x16o\x94\x8e\xa2,v" } internal_nodes: { key: "BWA=" value: "F\xa6\xf9\xd1ËŦ\x83\x0e\xd1\x0e\xf7\xd7F\xa9\x9a\x1b\xb7\x89\xe0\x83\x19,\x80\x88\xccŲˇ\xfa\xa0\x02\"_" } internal_nodes: { key: "BWg=" value: "\x8c\xbd\xf9F\rĔ\x96ˡV\x1b-&\x03\x06\xd0\xfeA\x16_z\x11P\xc5\xf28\xc0\xbe\xed\xe8\xbe" } internal_nodes: { key: "BXA=" value: "6\x1c\xd9\x7f\xd6IV]\xbej\x95\xaf.\x81\x8d\x13HU\xedm\xad\x05\xfbM\x16\x93Aײ79\xc9" } internal_nodes: { key: "BXg=" value: "c\x9ea\x00\xf7\xe5\x88vG\r\xbc x\xf9\x87\xee\r\\'D[\xbd00\xb0\xd7\xf4\x19mL\x91\x97" } internal_nodes: { key: "BYA=" value: "?',\xe1\xeb\x1f\x83+\xcdX\x127\xd0,V\x1c;gx\x9f;\xbeB\x12\xe1\xe5\xe1\xc2\xf1{]\xe0" } internal_nodes: { key: "BYg=" value: "I\r\x83\xae\x07\x98\xf0A\x12\x96$&\xfcU\xb2\x04IR\xa5\xff\x9drL\xc8V\xf2(\x9d,]K\x02" } internal_nodes: { key: "BZA=" value: "\tr6b$,aXA\xd9:\x9d\xf9S\x18\x1f7Ҏ\x1d\xd8\xfeu\xec\xe2\xdcá̍\x1b\x16i" } internal_nodes: { key: "BZg=" value: "\xae|\xad]\xc9\xe4\xd4X\xa5\xa8\x08-W\xd45\x18\xeeY\x07(\xe88}\x11=H\xbb\x7fErÆˇ" } internal_nodes: { key: "BaA=" value: "T\xd4\xea\x8d\xfc8\x05B\xc3\x17\x854\x85\xf2\x86\xd7sE\x86\xf9\xbe\x16>\xe5I\xddJ\xe5\xbap\x89\x16" } internal_nodes: { key: "Bag=" value: "\x84\xe2P#\xa4{\xb3U\x00\x99$zi\r\x15K\x13G6B\xb4\xef\xd7\x0e\xaa\xb3\xac_\xae.\xc3s" } internal_nodes: { key: "BbA=" value: "\xf4ˁ\xb2\xc0.%U\xc3]\x94o\xd5k\xf6\x1c\xb4\x95\x96Su\x95\x9c\xf6\xc6N\xbf\xa3+-\x87\x7f" } internal_nodes: { key: "Bbg=" value: "\x86\x990\xa4\x1e\xe9؁\xe03@K|\x88\x04" } internal_nodes: { key: "Bcg=" value: "\xf7\x18\xed\x9bR:\xb9\xa8M\x8c\xaa)$\xb6\xaf\xa6\xf8\xd68MΚ!4\xb092-\xd1R\xb0+" } internal_nodes: { key: "BdA=" value: "\xcc\x05\xcc\xfb\xff\xef\xff\x1b\xd2\xf7\r.\xb8\xfa\xedk\x0eN\x11\x9c\x03\x92\x9c{ 4\x84\x04zÖš1" } internal_nodes: { key: "Bdg=" value: " \xe8\xc9\xc7O\xff-zwÃē\x19\xa6\xef\x0e\x0c\xe7\x97éģ–\xbe]\xf9J/\xab\xa1\xc1\x1b\xef\xf4" } internal_nodes: { key: "BeA=" value: "\x9f\xacf\xe5V\xa9\xc3^\xecV᛾\xc6\x1f0NA\x01\xd7Ug'\xd20\xeb\x9e\xfd:)}\x87" } internal_nodes: { key: "Beg=" value: "\\X\x8d\xa2\xbeŲŧ\xfd\x0b\xd7h\xf4\x14\xa1\"\xfd\xa5\xc0\x04B\xcf\xc3c{\xe6+^\xea\xb6\x1b\xc8\xcc" } internal_nodes: { key: "BfA=" value: "yJn\xa9Z\xb6h\x1d[\x94\xc9\x08\x1a΋>\x12E\xa3\xe2Gb]\x8f\x18i}\x1c\xdc" } internal_nodes: { key: "Bfg=" value: "\x84\xea*\x99pv*\xd4R\x95%\xf8\xf0\xec8\xa1\x9c\xab\x00a\x85FaÛŽ)\x13.@qw\xcb" } internal_nodes: { key: "BgA=" value: "t7w\xa2>\x8a\x9e\xf0]\x92\x83\x8b\xaa\xeb\x8c\xf9uQ#\xb3؞B\xb6\x802\xe3,\xfdJM\xa0" } internal_nodes: { key: "BgQ=" value: "\xe0\x1a\xb6Yw\xa8\x91\xc1\x14\x8c\xb4\x1ed\x8a\xa8\xcc\xf2P\xcb8\x04A\xd81\xb1BŌŋ" } internal_nodes: { key: "BhA=" value: "o!i\xbf+\xd4ꐀ\xb2\xf4J7\x9c\xf6\x80{\xfe\x94\x89\x1e\x90\xcbl\x06\xa3j\xa7%&\x96\x8b" } internal_nodes: { key: "BhQ=" value: "7\xd4aR~\x12\x07'\x13\xf3P\x9eiC\xe4\xdef\x01\x9b)Ėļ\xc0\xfbKG}v\x06\xb2\x08r" } internal_nodes: { key: "Bhg=" value: "\x14\x7f\xb8iExGvg\x97\xc4u\xc4\xde\xde/az:3=W\xd9O\xec\x02\x07pj\xe44\x1d" } internal_nodes: { key: "Bhw=" value: "u\xff\xefÅž?\x8c\xd6TyJ\xf1\x06\xdb\x02\xa8F[\x93\x81\xe0\x99\x91\xee\x1c\x86\x0b\x03\x0f\x9f\x8f\xaf" } internal_nodes: { key: "BiA=" value: "\xba\x82/\xdaX\x9b\xf2o\xf8\xdd\xf3\xf1S\xf0\x1f<\xbeËĢ\x10l\xdaN\xc0\\\x8c(\x9b\xe3\x18\xabA" } internal_nodes: { key: "BiQ=" value: "h\x99e\x9e?8}\xc9\r\xac\xf2 tc\xb3.L\xdc[\xf5\xc9Ѐ$\x03h\x9d=΂<." } internal_nodes: { key: "Big=" value: "7p\xf0j\x163\xbee\x9el\xd6/\x9f\x9e\xa3z\x8d(\x9a'U\xe5\xdc\xeb\xf7\xb7\xb6\xf0d\xee\xee\xef" } internal_nodes: { key: "Biw=" value: "\xa0\xb7\xa0\xa98WLRha\xefN\xc7\x12\xb1X\xb1V\xd3\xd8T\x90\xf1<įļ‚\xd9|\x01\xaf\x16" } internal_nodes: { key: "BjA=" value: "\xa8C/\xd9\xdb\x1b\xe8\x11\x8d\xa4\x1c\xb0\x83`\x92\xe8K\xabq|\x08\xf0q1\x81\x82_@\xdd&\x19k" } internal_nodes: { key: "BjQ=" value: ";8Ў\xde4\x03\x95\x0f\xb3U=\xf6\xf8*\xd0}Oo\xe1\xc7\xfct\r\x81;=\xdc\xe5U+t" } internal_nodes: { key: "Bjg=" value: "X\xac(\"\x03x\xd7KLO1\xe9dȋ\x14\x83,o\x87\x13\x8c\xb9o1\xd3,\x8d\x9c\x181Q" } internal_nodes: { key: "Bjw=" value: "\xbc׌\x95\"Íŋ\xaf\x85]\x08\xdb\x00\x93\x03\xe2PĘģ\\|.N\xd8x\xf6P&7\xd6É´" } internal_nodes: { key: "BkA=" value: "D\xdf\xf9W\x0cc\x1fOj\x0f\xdfok\xf3\x8f\x01S\xfc\xab\xf9\x93\xc0Q\xaa1t\x9cAÕ¤\x08\xa0" } internal_nodes: { key: "BkQ=" value: "3RNq\xdb3\x13=\x07\x89\x89wo^MFZ\x07X\x1d\x92Ņ…]\x9c\x12}\xfbB$\xda\xd4" } internal_nodes: { key: "Bkg=" value: "#\x81+\x9e\x196FGA\xaaS\xf8`Īģ\xb3܌vw\x05\x12\x04\x97\xc1\x9c\x86X\x18\x05\x8bT" } internal_nodes: { key: "Bkw=" value: "\x86\x84\xebc\xd1?x\xccW\xcf\xc5^ؤ\x07\xa0\x8e\x99\xc3\xc9T\x84Lj\x07\xad.\xbc\xc5\x02\x8f!" } internal_nodes: { key: "BlA=" value: "\x123\xa5+\xf8\x1c,\x9b\xca{Éĸ\x80\x9aQ\x9c\x1ffQ\x83\x93\x12\xd5\xc2d\xcb\xe3M\xc7\xd9!x" } internal_nodes: { key: "BlQ=" value: "\xfd\xc1\xef\x16\x00p$q\x97\x0e\xe1\xf6\n\xb0\xb9s\xd15\xe7\x02LM\x14\xc0F\x07\x92<;\xa8\xc1\xb2" } internal_nodes: { key: "Blg=" value: "\xa0\xc9Ԇ\x0b\x86\xea\xe0\xbe\xd2*\xb9/\xa1\x0bM)?\x03\xd9\xddH\x1b\x861\xac\x84\x0f!o\xa4\x15" } internal_nodes: { key: "Blw=" value: "J\xed\xe5$\x967K\x13+\xea\x18\xf8b\x0c\xdcrO\xa7`7\xff\x8b\xdd+\tŨœ\xf0\xe7uJe" } internal_nodes: { key: "BmA=" value: "\x81}\x0eN\x16]\x88\xd3\xc0|\x94\x8f\x10\x8c6\xc66@l\xe8qc/U\xad\xc0P\xacy\xf21\xca" } internal_nodes: { key: "BmQ=" value: "Ô´\xa4)\x14:cPXM\xe2V\xc7W\xeb\xee\xf7%\xbe1Uˏ\xf8\xf4\xedㄏo\x1c\x9b" } internal_nodes: { key: "Bmg=" value: "\xdbeIJT\x07V]`\xe8mG\x00\x18x\x96o\\\xec\xd9*\xff\xd3E\x98\xd8B\x9cD\x07@\x0b" } internal_nodes: { key: "Bmw=" value: "S\xf6\xbe\x10\x99A\x82$\x9e}\xe2\xf6*\xa4\x14\xf9\x8dQA\x16\x92\x8c;LEŌ‘1\x04k\x18\xa6" } internal_nodes: { key: "BnA=" value: "^\x81d\x00L|)\x0c\x9e\xc1G\x11\x92t\xadvz\xa3\xe0\x1fb\xed\x84\x14\xa4\n\xf5ܙ\x13\xdd\x1e" } internal_nodes: { key: "BnQ=" value: "|\xce\xeb$[\xbd\x002" } internal_nodes: { key: "BoA=" value: "?\x8f\xff1b}\x18%r\xf4\xba#\xa6Í K\x81ÆĒ\xda\xdd\x18\x0f\x05>\xe7\xcc-G\x00\xa9!" } internal_nodes: { key: "BoQ=" value: "\xe9\xe48\x88H\xe0~\xab\x94\xa9>\x19{\xb2\xa0\xb3s-\xc8\xea\xb8g7\"\xb7\xc6J\xaf\xd2\xf2c\xb7" } internal_nodes: { key: "Bog=" value: "\xe6\x1b!\xba\xb2\xff\x18u\xae\xa3\xb1\x99k]\xb45\xf4\x03\xda\xf3\xad)\xe7J\n\xbe\x87\xa4\xd6\x08{\xba" } internal_nodes: { key: "Bow=" value: "$\xc5u\xe1\x84\x01\x95\x85=Qt\xf9å ŗ\xf6\xfd\x89\xe7a-\xb6\x07:\xd6o\x91\xa8\xd1j\xbb{" } internal_nodes: { key: "BpA=" value: "\xfd\xbc\x04\x17ËŖ)F}\x93\x9fn;p\xd7?\x9dE\x14\x7f\x00>\xf0\xbe\xa20d\x87Z,Üģ" } internal_nodes: { key: "BpQ=" value: "\xeat&\x87>w\x86\x03\x01\x1a\u0097\xd4\x08\xab,\xde%0\xb1H\xba'\xcd5\r\x97\xf1kL=\x15" } internal_nodes: { key: "Bpg=" value: "0\x1a\xad\xdeF0\x17\xa1Âiy\xf6\n\xb4\xf8Ȓߤ\xa7\x8c\xa4\xfc\n\x14\xbe\xe9\xbb0:\x11" } internal_nodes: { key: "Bpw=" value: "\xe8mĶ™@\xcfE\xf4b\xf7\xbd\xde4\xf1\xdd\xe0\xef킐\x19\x11Ffzh\xd7u\x8b6\x98<" } internal_nodes: { key: "BqA=" value: "t\xb5\xb44\x93\xa2\xab6\xcc]\x00F^\x8e\xf0nΤw#o\xdf.\xfe yf\xf1Dk@\xc9" } internal_nodes: { key: "BqQ=" value: "\xacNTzZORʓ\x11.\xa6g\xcd\xf7\xa7\x9bFY\xb4x}\xf5\xd79\xa7&E\xc8\xdf}\x0f" } internal_nodes: { key: "Bqg=" value: "\xb6\x1b!_\x19\x82f\xce\x02\x8f[v\x15pE\xbc\xc6Z\x1co>8R\xeb\xa0|\xff\t\xffo\x9dT" } internal_nodes: { key: "Bqw=" value: "\xc0\x98v\xf2\x1b\xe1b\xb7*\xc1\xceh\x87|\xc6^\xff\xbe\x03҃\xa7\xb7\x05A\xa0\x9a\xf9\xeb\xd3R\x8c" } internal_nodes: { key: "BrA=" value: "\xea\xf1\xb5+g{\xe0Ú­ l\xa8\xc9S\x1f\x1b\xf7\x16\xfb\x17T\xd0cY\xaa+\x07\x0f\t\x1b\x12C" } internal_nodes: { key: "BrQ=" value: "1\x80įĒ’\xdbL\x18|IÕŠĐ…m\x1d\r\x8b/\x9c\xa9\xaaԒ\xc5r>\x1f;\x8f2\xbe" } internal_nodes: { key: "Brg=" value: "\x88;*\xb4Ãĩ`3C\xec\xd6\xee\xf2\xad\x17\xa0\x0bj\x99C\x82x\xcd^.\xa4\x06\x10̈́t\xa6" } internal_nodes: { key: "Brw=" value: "\xfe%\xa0OJ9/ɒrw#\xb2\x14\xc0\xd3hJ\x9d\xa6\x8a\x99\x93\x9a$\xc7P\x14\x838\xf4\xca" } internal_nodes: { key: "BsA=" value: "7\xc8FæŊ™Vp\x07l=0\xdc\x19?B\x91c\x92\xc7VI\x85F\xed%5$kH\xb4=" } internal_nodes: { key: "BsQ=" value: ".\xa9\x85\xf1\xb7Q>\xff\xd3\x06\x1b:\x06\x8a\xa6\xf0@\xebM\xbfF\xec\x81\xf6\x10\xff`\xd3m\x9c\xe3\xcf" } internal_nodes: { key: "Bsg=" value: "\x8d\xf4\xa0\xec&,\x8efԊ\x8d\xb9\x91\xd8\xda\xc4\xd9\xccgwnsE:\x81\xad\xf7\x8e\xad\xbd \x1f" } internal_nodes: { key: "Bsw=" value: "D\x1f=\xfa\x86*\x16%W>\xce\\+P\xe6a\xaa\x9fhBj*\x00:\xf4\xce&\x87$h\x1b\x98" } internal_nodes: { key: "BtA=" value: "\xeb{y\xb2\x08\xcfoi\x96\xb0v\\\xcf\x1d\xa3W\xacE\xf2\xdc'P\xab\xf2)\"\xc0Vw\xe33\xc9" } internal_nodes: { key: "BtQ=" value: "\x97TpB\xfd\x1aH\xb9$\xf8\xf2\n\xe7\x0b\xfc\xb7\xfd1\xa9z\xbd\x80\xbfm\xc8\xf6Z#\xee\x1d:\xa5" } internal_nodes: { key: "Btg=" value: "i\x1b\x86\x98k\xc9\x1fp\x0f\x16D\x03\x1e\x83Ķļ~8\x04[\xac8@\xae\xe4\xfe\x0e\xb2\nl\t\xe9" } internal_nodes: { key: "Btw=" value: "\xbd\xe0;\x17D\x8a\xbe\xf1 \xfe\xbc\x94\xf7t\n\xcf-\xfc\xcd\xf9\x04\x19\xacL\xfb\xa7\x83\xd4N\xbc\x85\x0f" } internal_nodes: { key: "BuA=" value: "^\xd4W\xed\xd8\xf5#\x9cÃŋj\xcd_n\xb7\x96\xaf\x041~Ah\xb9ˆU\xe8\xd7T\xf2\x05\xfc" } internal_nodes: { key: "BuQ=" value: "\x17of\x95S:8\xa0A\x0e\xba\x14b\xda\x02\xb9\xfa^" } internal_nodes: { key: "Bx4=" value: "Ɂ\xab\x98\xcf^u\xe6Z\x8a\xf1\x0c\xfa\x84\x98˟p\xb8\xc4?\x85] \"X|\x00\xb8\xb8It" } internal_nodes: { key: "BxA=" value: "&\xba\x8a}\xf7\xb8V\x03\xb86\xa8~\xf9-;L,x\xc8\xed\x87\xd3dQ\xbf\xee6\xaa\x9bM\x9a9" } internal_nodes: { key: "BxI=" value: "\xca\x04ڞ\na\xba\n\xbca\xf9\xf4!n&\xfbÔĸ\xb2\xf8\x9b!E\x8aZe\xe5\x1a\xf8\x7f\x83\x82" } internal_nodes: { key: "BxQ=" value: "\xf6N(x2]\xa7\xdc\xe7\xff\xaa\xdd\xeb\x9c\xe3{\x03k\x9b\xfa|\xe9\u009cS\xb7\x04\x1e\x03\xd6\xc0\x10" } internal_nodes: { key: "BxY=" value: "@\xe7<\x01\xbb\xbc\xa3\xd5J\x11^\x8an\xc8H8D\xe1|\xf9K\x84\xc3k\xafq\x17\x98\x98\x1a\xde\t" } internal_nodes: { key: "Bxg=" value: "\xba?3ô„ρ͙\xef\x8a\xcf\xe7 \x91\xa9߆\xda?'5\x82*\x91\x9e\xd5\xc7\xe8\x05\xae\xfe" } internal_nodes: { key: "Bxo=" value: "\xded\xd7>OĶ‘r9\x1a\x17\xf4\xeeVy\x15:S+\xb1\x91\x9b\xd2\xfc\x02\xccq\xca\xf4#Χ" } internal_nodes: { key: "Bxw=" value: "\x1e\xa0G\x1c\xa6\xee0\xabR\x14n\xdaE1\x8b#ju\xd3{Åŗ\xef\xef\x9fHud\xd0\x00\x85\\" } internal_nodes: { key: "By4=" value: "\xe4\xceF\xc67u\xab\t\xc5\x19ܛ\xb8[\x17#\xc7\xc9$X\x0b\xb3\xfa\xa4\xb1Ũ°Ķš\xb9\n?" } internal_nodes: { key: "ByA=" value: "\xf7\x18\xdcj\x8c\x19\x06I\xaa\xaf\x1c\xd8\xe8YW\x98XP\xcd!\xa5Ũ\xcd\xea\xbf\xfe\xaf\x97\x0c\xb6e" } internal_nodes: { key: "ByI=" value: "c\xafv\xf0o\x08\xac\xb0/S\xa7L\xadi\n\xa5N;!Bdc\xa2\x96\x12Nq\xa2\xe3\xdbj\x97" } internal_nodes: { key: "ByQ=" value: "\x83\x8f\x8a\x12\xb4W\xcd]\x9d\x8a\x92\xa5\x97\x85\x0b\x1d\xee:&\x98\xaa\xef\xd1: ÛŊ.\xf6\xa4^\x02" } internal_nodes: { key: "ByY=" value: "\x0cy\x05\x1daq\xdc\xc0Z\xc3\xd2 \x88\xa2\xbc\xdfh\x8d\x1c\xf8\x85\x986\x9d(\x18\x18&-}K\x04" } internal_nodes: { key: "Byg=" value: "\xa9\xbd&\xac\x88Ėą\xa1Įž\xfd\x85\xf4L[\xf7\xc0\xed\xec\xb1G\xe0\xe0\x937@[u\x83gp\xd3" } internal_nodes: { key: "Byo=" value: "\xddf\x91W(6D\x16d\x7f\x83\xd2\x18Z\xe1\x12\xbc#\xab\x0fdo\xfc5V\xc7r\x07\x9e;\xaf3" } internal_nodes: { key: "Byw=" value: "ˍ]\xe9o~\x85\xa8Ņš\x9fmJu\x100ڈ\x9d\x1efGb\x8d\x86\xc2\xf0\xc8\"\xfa\x92\xe1" } internal_nodes: { key: "Bz4=" value: "Q\x14Iv\xbe\xbat26\x14\xfa\xfc;\xa6e\x87!\xa7'\xcct\x02o\x83\xa4\xf7\xa4\xbb\xfa\xb2\x11\x9a" } internal_nodes: { key: "BzA=" value: "\xf3\x91CFTĶž3\x01\xa6\xdd>K\x19p\xe7]\xe1\x1d\xb4\x92\xaa\x7fg\xb4\xe4\xa0\xcf\xff,\xe9K" } internal_nodes: { key: "BzI=" value: "\xd8\xe2\xe7\x1f\xca\x0c\t\xc6[\xbc_=\x1cX\xfe\xd68>\x8aX\xad)\x82(QŞ\xc7\xf4\x8co\"" } internal_nodes: { key: "BzQ=" value: "C\xa9\xf4o߲\xa7\xce\x1c\xa3\x9fq]wDf\xf5u\x99\xf5\x99\xe5\x1d7d" } internal_node_count: 254 prefix: "\x00\x00\x00\x00\x00\x00\x01" depth: 8 leaves: { key: "CA0=" value: "\xd4K\xee\xf0q\xf4\x88\xdf\xdc\x1f\xc8\xd4G\xb2%\x0b\xdb\xc5\xf8\x83qxB\xf2\x08\n\xf0\x0e\xad:X\xdc" } leaves: { key: "CA4=" value: "\x94\x1a\\\xb3\\S\xf4\x91ԟ\xf1>5\xa9a~\x98R9\xfev#\xb7\xcd\x19\xb5}KnPmS" } leaves: { key: "CA8=" value: "\xa1Ņž\xc00XA\x89k\xcc\xca\x0e\xf9\xf3\xc2éĢ‘X\x82\xfd\x05\xab\x9f_\x9aw\xb5\xc1\xf1\x0f\xff" } leaves: { key: "CAA=" value: "\x1f\x14\xa6ŅĨ\x82j\x9c\xa9\x00\\\xf4\xa2\x14E\x13z\x9b\x92(q\x82\x06-\x87\x84\xf9Ôĩ#\x16\x0b" } leaves: { key: "CAE=" value: "\x80\xfe\xbaC\xae\xe8\xa2F\"!oN!\x9b\xfc]\xd5-O\x91Ôž\x9fL\xf81\xdc\xfe,\xea(\x80" } leaves: { key: "CAI=" value: "!\x89\xa1\x13#\xa20<\xe7\xad\xf3\xf8\xe9\x10)\x17\xbcĖ­\xa2\x8ar\xffj\x1e\xf0c^\xf3\x81\x0f\xdd" } leaves: { key: "CAM=" value: "\x0fI\xc3\xf1;y\xe5G \xf4\x1c\x9d\x97d\x91j×ē\xb0D8\xc0`\nیb\xef,V>[" } leaves: { key: "CAQ=" value: "\xe2\x01\xde\xc8k\x14\x0f+>\x0cM\xc9\xf8\x1b\x0bS\xa6\x7f#&2\x07\r\x92\xc2_\xd5C\xdb᠝" } leaves: { key: "CAU=" value: "n\x9a\x9b\x17\x17垔1\x96\xb1Z\xa0\x8c\xe7\x10\xbak\xbb\x89È\xd3\xf4=g\xa4\xf6\x83\xda\xc48" } leaves: { key: "CAY=" value: "\xe4\xf1\xb2=\xb0aH\xfa\x84\xad\xfcg\xb8P\xcb\x08\"!؀\xadIe\xc3(\x89R\xaa\xa5\x92\x1d " } leaves: { key: "CAc=" value: "\xff\xc3\xdfJ\xbeI\xde\x1fāχQ\xf0\x94\xa8hv\x9a\xb6\x94\x85r\x16\x93 &M\xad\xf0\xbd\x80#" } leaves: { key: "CAg=" value: "\"\x90I\xb9j\xcf#t\xee\x8c\xe4Αw2M\xfd\x8cK\xa9m\xb1)4\xc7A\xfaЅ\x80`\xec" } leaves: { key: "CAk=" value: "\xe384\xf6/$hD\x06\xe8\x1f\xbcpa\x05\xefS\x1f\xdc؞\x95K\xab\x13\x9880l\x04B\x83" } leaves: { key: "CAo=" value: "\xd6\xf1\xf4\x80$^\xe1Y\xf0æ‹Ĩ\x9fJ\x17\xea\x18\x0f\x97\xdb\xd17\xde:t\xd2\x16fPXˈ" } leaves: { key: "CAs=" value: "\x9f\xa2\x93\x91\x8bZ\xf2\x0e\xd7{\x14\x84\xde\xec`\xf9\xa0\xd6H\xeb\x87,\xb1\xc1\x96KH:\xd8V\xe8\x07" } leaves: { key: "CAw=" value: "\x0cG\xac%61\x96\x15\x13P4\xbb\x88\xb2\x8a\x92Ū‚\xa4.[c\xc0\xab\xc8\xd5\x00\x9e9\xb8\xac\xfc" } leaves: { key: "CB0=" value: "\xab\x8c\x1c\x0b\x1b\xebZ+\xa5\x17\x90\xf7\xa9\xab\xb2\xd29-\xa6ĮĒBp\xeak\xac\x04\xcf\\\x02\xd1y" } leaves: { key: "CB4=" value: "\x12\x1f\x96J%\x14\x1b\xabme)\xfa\xfcڃ\x93\xbd\xd2\xde\x1d&.\xc8\xda\xfa\xfd\x97d%\x12>\xb4" } leaves: { key: "CB8=" value: "v\x7f\xb7\xc3~?\x97\x13Ce\xe2-:2\x05\x97~\x8esB\xc4B\x15\xda\x0f\xd6Sh0\x962e" } leaves: { key: "CBA=" value: "?\x18\xa1\xc2Į‚\xf1\xd8+/\xe3\xf8\x06\x95\xd0g\x848\x08\x1e\x8c\x13\x1f\xc5\x14x\x88\x06)\x84\x19\xf9" } leaves: { key: "CBE=" value: "\xd0\xf1Ú­\x85\xd6\xf7\xeaIp{MDu*l\xa9\x16Øĸ\x7f\xdc\xd1Z+\xd2\x15 \xc6i\x85\x1a" } leaves: { key: "CBI=" value: "5\xf6\x17\xf6x#}\x82\xfc\xf7t\x95I\xed%\x93D\xb14\xb9\x9av<\xc7&\xf8]\x9c\xc4\xf5\xb4\x1d" } leaves: { key: "CBM=" value: "\xee=\xce\xfdH\x83\x1c\xfdLh3k-\x0c \x97\xeb2{+\x97;\xd5r\xf3\x1f\xfa\x0f\x9f@+\xfb" } leaves: { key: "CBQ=" value: ";\xad\x96\xc9K/\xdb\x0f\npc-e\x8b~v\x16\x8e\xc4\x00\xf4g\xb4!x\xab\xfd\xa2\xf1\x9a[\xd6" } leaves: { key: "CBU=" value: "\x88\xdb\xc0\xe8\xa1\xd1\xe0B\xe6.\x82.\x80\xed\x82Y\xbcԄM\x19\xd8\xf6\xe6\xc2\x06r\xd2\xfc%\x8e\xff" } leaves: { key: "CBY=" value: "\x16)\xb5\xb9\x1aT\xa2 \xfc\xf8\xdf#Kf\x8e$\xa8\x82\xbe\t\xad\x9c\xc0\xac\x8e\x1b\x9f9r\xb1\xca)" } leaves: { key: "CBc=" value: "\xd2Y\xfe\xb9An\xdb\x16\xdd\tQ\x94\xfaqo,]M>\\3q\xf4!\x97E%ĤdH\xd1" } leaves: { key: "CBg=" value: "\x1a\xd0RR'\x1a#\xdf\x14|ÉŠ\xe8\xc1\xb7\xb2ÎŽ\xdf\xc3bXr\x17q\x16^ZT\xdfz\xa1" } leaves: { key: "CBk=" value: "\xa4H\r!\"\x10jY\x16\xd5\xd1@\xcc\r\x83\xa6v\x17\xf5}\xba\xb6|Ũ—0.\xf7\xff\x17\xa1\xc2" } leaves: { key: "CBo=" value: "!\xba_>\x85\x174^;t\xb3\xfe]\xfc\x1du>9\x83\xc0\xe74\xff<\xe3!*\xfc\xd0\xdf\xf0\xf2" } leaves: { key: "CBs=" value: "\x15\x95\x82\xe2Øŧ\xf3\xf3q\xd4\xf4\xc8\xf3\xa6\x10\x13\xa1g^!\xaa\"j\xdev\x9ag\x9fONq\x1e" } leaves: { key: "CBw=" value: "\xdeb\xbd\xe3v\xc62\r\x8f-\x00J]\x9fCT'[\xc9u\xdf\x08%\x14\x0e\x03Kƒ\x00\xf2\t" } leaves: { key: "CC0=" value: "\x86\xac\xf1ĮD\x13\xe03\xa7ĘŖ\xf4\x93\x902\xba\x16\xed6!AS\x18i\x08\xf6\xcc\xc5\xee\x10\xbb" } leaves: { key: "CC4=" value: "\t\x80\x07\x0c\xba\xef\r\xea\xb55\x02e*\xd6䖗V\xf0\xb9\xe1co'\xd0\xc3\xebĐĄ\xa2d&" } leaves: { key: "CC8=" value: "OF\xa15\x84S錰\xa45\x19\xb265\x99\x87J\xc9\x1aג\xb6\x02>f[U\x82\xc5]\x80" } leaves: { key: "CCA=" value: "h\x87\x92иŲŊZ\x18\xbbM!Q\x95\xdeAķ…“Ŋ\x02p\x14m\\\x86I\xd2(1\x9d\\" } leaves: { key: "CCE=" value: "\x83\xb9\xcf3l\x8b\xcab\xcd\xcb'\x03\x11g\xa7\xa5\xa4S\xaf\x96ä¸Ĩ\xb8\xe1\x94\x10\xd9:`\x8b\xa9" } leaves: { key: "CCI=" value: "\xa1w\xff8\xccs;\tf؈\x16W\xab\t\xfdh\x8b\xdb>\xfb\xce\xfa\xe3d!\x04\xeb\xa5\xed\xb2\x0b" } leaves: { key: "CCM=" value: "J\xa3\x9d7V\xa8\xc8\xc0\xe53Ė—\xbd\x9dh\x01\xe5\xea\nt?\x97C\x80xKr\xa1<\x99\xf0\x97" } leaves: { key: "CCQ=" value: "\x02:N\xf0\xd0\x17\x96\xb9\x95H\x1d.\xfd\xf7:\x97\ra\x9b2ߖ\x1c\x9d\x1afuM\x88Xj\x92" } leaves: { key: "CCU=" value: "\xbe\x1c\xad\xf9z\x1a\x93\xe1\xefh\xee\x1d\xff\x11*\xa0h\xf6%\x97H.\x0cD\xe1fN\\\x19\x89\x85i" } leaves: { key: "CCY=" value: "u4\x8f\x04\xd7\xecH\xbf\x91\xf7Ũą\x1c\x8ey\x9d\x14\x07rĊR\xd3\xcc=wÄēQ\xf1\x08\x00" } leaves: { key: "CCc=" value: "\x13\x1c\xdc.\xf82ĖĨ_\xe3\x96X\xd9\xfeÍž\x90\x83<\xb1\x02\xf8\x1d\x12\xedT\x92\x08\xc9dĮ“" } leaves: { key: "CCg=" value: "y\x9a{\xa69\x1f[\xf9\x8aEb\xc4 Gx\x98&Q\x89ËĄ\xaey\x16:\t\xf6\x8a\x86\xa5\x18\xe9" } leaves: { key: "CCk=" value: "\x92\xf8so\x7f/\xb2\xbeéĩ•mO@(\xb2\xd4!r\xf1S\xe6\t\xf4\xc0\x7f|\x1e\x94\x10_=" } leaves: { key: "CCo=" value: "ĮĢ\xce>\x0f\xee\xb2W^\x01\xc2k\xa8\xe6\xd0,\xf5\x03\x15\xf1ėĻĨk\xc9\xc3\x1c\xbbQ|\x0ei" } leaves: { key: "CCs=" value: "\n.\r\xb8\x15I\xe5Ō˛\xaf\xeaG\x1e\x06\xf4*Z\xb2ΎҌ\x1b\xf4\x13\xed\xf2]\x01M\xaaa" } leaves: { key: "CCw=" value: "\xadÆĩBR\x0b\rAg\xd9,\x1e[\xceH|\x1e(4\xb3\x7fbo5\xa2\xc4\xdeE\xa1\x81\x0f(" } leaves: { key: "CD0=" value: "\xbb\xf7\x86\x92\x8aP\x11\xe6\xf6\xf1k\xc9\xdb\x10eð\xe0\xb7\x16Q\xc4\xf8\xb2\r\xf3A\xb3\x02\x89\xdb\xd6" } leaves: { key: "CD4=" value: "\xec\xf0y\xb6\xef\xc9s'\xf72\x95\x8b\x068\xb4\xdb&[\x9e\x12ƈJ\x93Db\x06Ო\xaaA" } leaves: { key: "CD8=" value: "]N>\x9f,\xa5\xdc\xf0\x9dB\xc7'\x8b\xa4\xecs\xfc\xf1\xf5\x0fT*\xe7\xc3\"\\\xd4ħ=rq" } leaves: { key: "CDA=" value: "͏\x80\x14\xba\xa2;V\x17×Ļ\x04s\x9b\xe0\xa3\x0f\xc7?\x08(\xb9}\x1fd\xfa1q\xb4\xf3\x88\x1b" } leaves: { key: "CDE=" value: "O\xdd]\x81\xf2\xe3\"\x02\x1c\x07\x9e}UE^\r\x00OK\xa0\xb9\xe3\xa4\xcdxJ\xdfn\x15\x11)\xcb" } leaves: { key: "CDI=" value: "\xbc\xf0\x0f\xb20\x03;j\xa2\xcd\x05\xa0:\xbd\xbe\xb9o5\x0e*8\xe9\x80|\x96P\xa3`\xc7\xc0 \xfb" } leaves: { key: "CDM=" value: "\xf9\xfa\x8d\xe8_\x8b\xaek\xed\x16Mf\r\xe5\x9e\xd8\xc0\xedD\xd7\xfc\xcd\xe6\xd5T\x81\x95\xafl\xba\xec\xfd" } leaves: { key: "CDQ=" value: "\xb0N\xa1\xcf\x02\xbe\x1a\xb6\xb0\x83\x17GW\xce\x04\xc6\xdf4ßĒĘĩ\xc5\xe2\x02\x105\xfe\xe2g\xf9\xab" } leaves: { key: "CDU=" value: "Վr>Ε\xcevʰ\xf1\xe3\xb2͘U[\r\xa7\xadw\x89\x87\xe0\xe0\x03\x10\x85\x88\xe5\xff\x9d" } leaves: { key: "CDY=" value: "\x14b\xfc\xe50'!w\xbb!\xf6\x16'i\xe1\xb0BՎ+G\xf6\xfc\xc9\xf21\x0c\xb1\xb0\x84\x95N" } leaves: { key: "CDc=" value: "\xf6xb\xfe\x12\xe4\xd3\x16\x8br\xdd(\xad'o[($`\x0e'Sq=B\xc7\x0b-\xd0\x07\xf1\xf5" } leaves: { key: "CDg=" value: "\xcf\xf80\xa7-^`\xfe\xd8@\xfb\xe8\x04\xb5z5\xe8~΃.\x96\x86;e\xe2R\x05\xd3X\x83\x12" } leaves: { key: "CDk=" value: "\x19\xe2c\x95MB\x12\t\xa9F1\xcaBp\xae\x86\x8c\t\xfb6ƃF\xe5K\xec\xea\xb36\xfbZ\xf7" } leaves: { key: "CDo=" value: "\x1e\x02n\x10;)\xe68#UR\x99\x825\xfe\xb4b\xd8=\t\x060\x17\xecM\xa2\x12\x037\xc2SQ" } leaves: { key: "CDs=" value: "\x18\xab\xff&\xf3?\"\x89\xc7Y\x90\xf3\xe6\xa4Xm\xb8\xd5\xc3\\\xc5\xf7\xe2c\xd7\x1f9\xa0\"\x18zS" } leaves: { key: "CDw=" value: "\xf2\x84\"\x02Z\x91_efv\xc0:\xfc1\xb5E\x11\"\x08,\xb1\xd0\xf3\xb2gqĮš/7\xaa\xdf" } leaves: { key: "CE0=" value: "|\xe29\x1c\"\xe8\x8d\\\x8b39e\xb8I\x0fNW\xcd6~j\x13b\xa0\x93P\"ᙓUV" } leaves: { key: "CE4=" value: "\xbf\xecF\x90\n\x88+\xf0\xb8?x/\x87\xdeAi\xca" } leaves: { key: "CEs=" value: "\xd4\x14\xc9\xf2}\xfa\xb9.\x19\xc3\x1e\xce\xed\xe8pėĢ„ÉĒJ2\xb4 Åļv\x16\xacAt*" } leaves: { key: "CEw=" value: "Ûˇ\xd4\x1e5k\x01OL\x00Zir:\xe48J\xa1\xf2\x1f\xb4}\xeb\xc8k\\\xd5\xc6KĖ\xd6" } leaves: { key: "CF0=" value: "\x97\xcea\xf7Z0c]f\xdf==Y\xd8\xf9\\\xb0_]\xba\xee\x9a\xf3.&Ut\xbf\xed\x1f\x10o" } leaves: { key: "CF4=" value: "#\"qЊ\xee\x90\xc0'9\xa3\x91]-\xda+\xe5\xefL\x98Îŋ~P\x8c\x1a\xe2\x06\x15Ôĩ\xf1" } leaves: { key: "CF8=" value: "\xc8\xc4q}\xbd\x8b7\x99t\x8c.\xb4\x98R$\x112\x89\xab\xb5\x82~\xad\xa8\xf0&F\x96\x95\xb0\t\xf3" } leaves: { key: "CFA=" value: "q\x01\xae\x0fb\xa6[c\xe1i\x17\x93\xdbO>i\xaeĖš\xb1\x98B\x92\xdc6y\x8b{\xb4V\x11\x11" } leaves: { key: "CFE=" value: "\x08\r}|Q\xb8PáŽļ\xfe\x12I<\xaeֈ\xe0\x06\xc6T\xf8\x02\x19\x02\xf2~\xf5\xe9+'\xf5" } leaves: { key: "CFI=" value: "\x14#\xfc\xe9>[\xc07\xf7\xad#p\x1f\xff\xf2L;\tW\x87F\x97\xec\xf6\xaaY\xa7G\x10\xb6\x17Q" } leaves: { key: "CFM=" value: "Øŋ\xfahw\xcb\xe2Wb\xa9\\\xb7F\x86\xb4\xc8N\xdf\xcfx\x8f1\xb1\xd0\xe86k\x8f\xbe\xce\xd6\xdf" } leaves: { key: "CFQ=" value: "ÍĻŨ§~\xces\xfb\xc4()\x9f\xa7\x97\xc9\xc6|4k\x8fe3\x9c\xab\xd2\\\xaa#\x07_\x1c\xe1" } leaves: { key: "CFU=" value: "\x1fG\xeb\xffwu\x01\x0c\xb4\x10\xc1\x86\xc9H>\x8eJ\xe1\x14\xc1\x0fz\xba\x0c|a\xe7\xd1&V\x80\xc8" } leaves: { key: "CFY=" value: "=)\xd9n\x95\xc0>\xd6\xe0\xfaz\xe1\xf1\x01z\xf7Åĸ\xa5R\x1fd|\xf2\xaes\x99j\xd3=dF" } leaves: { key: "CFc=" value: "9\xf9\xd4ÎŦho+X\xa3_#\xa7\x1a\xc7\x10\xf9J4\x15>4\xc3w\x8c\xdc\xfe\xb8\xa5M\xf5\x0b" } leaves: { key: "CFg=" value: "\xbdI\x03Sf\xdaS\x97U\xd7\xfc?^\xb9\xe1Jx\xbe\x06q\xdb\xeb\xf0M\xf8\x0c\xe5\xf6c\xa3\t\xc6" } leaves: { key: "CFk=" value: "\xdab\x0f'Z\x81\xa4AC\x1c\xff\x17\xdd\xc7C\xfe\xaa\xa2\t\x97\xdb3;\xa9\xa6\xc1\xfaJ\xf87-%" } leaves: { key: "CFo=" value: "0(\xc23\xc4\xf5ð?\x9f\xa3E\xed\xa8\x04~\xcf\xd0\x06\xf9z\x80\xcdJ\xa3\x9em}\x89\xa5\xf0k" } leaves: { key: "CFs=" value: "]\xde\xd4\x03\xcc\xccCN\"9\x1e\x0c~<ÃĢ\x02\xfc\x98\xaey\xfc\xb4 \xcb{\x92j\xb2\xfai\x1c" } leaves: { key: "CFw=" value: "e\xe9\xafd\x07`\xda\xc6&\x8e\xc1\x9e\x0cV\xed\x02\xffl\xd1\xd8\xd5>\x9c\x8c9\xa6\xda^ut]\xe6" } leaves: { key: "CG0=" value: "\xee\xf1\xcb\xf8\x9e\x93\xd4^\"\xc4\xdf\xc1\x1e#\xdf\xef\xc6;\xa3\x18\xdfiy\xa9\x13\xaf\xddFfn\xab\x0f" } leaves: { key: "CG4=" value: "t%^\x9d\xeec_8\x96s\x05o\xfb\t\xc8{\xae%*ŅĒ^ãšģ\x8a\x06q\xf4dy\x8b" } leaves: { key: "CG8=" value: "\x11\xe6\xf4\xb4\xb1\x98\xecz\xab\xaaqM\xa5q\x0c\xdf:\xee\x1f\xcb\x1ccRj\x9bC\xce\x12\xfff\xca%" } leaves: { key: "CGA=" value: "\xac\xb4r\x12j\x12\\[Q*d\\\xb3\xaem×ģ;poI\xfe^\x00\x02\xcf\xe2jn~\x98)" } leaves: { key: "CGE=" value: "\x97%9\xbc\xcfŪ™\x17\x106\xfc\xf4ß´\xc8\x07\xef\xcd\xe5\xd6\x04\xb8\x14s\x87\xf3\xfa\xd2+\xf0\r\xa6" } leaves: { key: "CGI=" value: "AY\x15P\xe8.\x0f\xa2+\xd8\" .\xe0\x9d\xeb\x0fx1\t\x9c\x19\xd0?\x10\x92i\xd0n\x08\x1a\xaa" } leaves: { key: "CGM=" value: "}p\x8b\xb8\xac\x15\x80W\xc0\x8ct\x1eX\n\xd4o\xd8>\x80\xd4\xde\xfd\x8e\xda\x16\x98\xa2\xda\xf9\xa2I\x14" } leaves: { key: "CGQ=" value: ">T\x90q\x14\x8a\r\xdf\x1aO.\xf3ЄZ\xa31B\xf8J+\xf9\xbd\x18\xbc\xc3\xf3Úˇ\xa8\xf2\xe8" } leaves: { key: "CGU=" value: "\xb0O\x0fC\xf0t\xae\x1d6\x06\xd7\xda\xc2\xfc\x87\xb3{A\xfdQ\xbd\xafu\xd60\x9a\x1a\xadv\x1f\xa0M" } leaves: { key: "CGY=" value: "9E1\xc9&I;\xe1\xbe\xf1\xed\x0f@\xad\x977\x9d(k\xd9\x15\x0f\xda\xe4\xe5U\xdc\x15OČŦ\xe4" } leaves: { key: "CGc=" value: "A=\xde\xdf|v-\xe1Q\xf5\xbd<*c\xa3\xf5\xe2v_pl\x08\x9a\xa9\xeb(\x98\xb8\xaf\xb1K\x9e" } leaves: { key: "CGg=" value: "\xaa\xe0\xa3\xef]p\xdb\x0b\x01\x91$z\xc4\x1e\x15[\x94\xb9)t\xa5\xd7Q.\x8d\xed\xa77\x0bQ\x93\x02" } leaves: { key: "CGk=" value: "8V6dSC\xebs\xdcQE\xf6\xec\xea\xf4z" } leaves: { key: "CGo=" value: "^\\\x9c\x19\xfeI\x8a\xdap\xbd\x1b=\xcct\xc7z\xa53f\xdfD[[\xc1M0\xb0\x10\xbe/\xc1e" } leaves: { key: "CGs=" value: "\x8f\xbe\xb43\xe7$\x1d\x02\xb0^\x93\x7f\xe8/\xe90\x83z.\xa5\x0c\xdc4zn\xe7\xd6\x1e.qkD" } leaves: { key: "CGw=" value: "\xc6\x07+\x8dĶ€m\x01\xa7\x85\xf5\xc5\x12\xf6\x8es\x11\x1dn\x15\x12\x0c@\xba\xf6\x87h\x14\x1b\xf7\x1c\xa3" } leaves: { key: "CH0=" value: "\xec\xce\xc6@U\xc7\x05S\x95\x0b\xb0\x1e9\xb6\xd4Z\xa0pWq\x81\xc0R\xfc\xc2#\xe9Ÿd\xb1Y" } leaves: { key: "CH4=" value: "[f\x99\xbb1\xfa\x1e\x8a\xbc\\v\x93/M\x1f\x84G\x17Uq\x89[C~\xbe\x82\x9e\xe2A\xf3\xfc|" } leaves: { key: "CH8=" value: "ßŗ\x0c\xf1\xca\xd1\xe1\xef\x8e\x7f\x9f\xe4\xe2\xee\xc7NnI\x7f\xa2S\xbfa\xaa=\xd6D\xd0@#\xa3}" } leaves: { key: "CHA=" value: "\x8b䋒\x91%\x02v\n\xd0v\xa9\x08\x11\x8f\x8f_\xf3I^M\xc4\xdcOƞ\x7f+\x8c\x8c\xbf3" } leaves: { key: "CHE=" value: "%f\xe3b;\xa5\x08\xf5\xc3R\xf7h\xeb\xca- \xcc(\xbaeg+!\x05\x95\x8e\xb5g\xcb\x17\x0c\x97" } leaves: { key: "CHI=" value: "i\x16O\x85\x84{O\xf4!\xe7\xc5o\xb0-\x08y\x80\xdb\xd5\xf5\x15\xf1\xbd?\x01^į ­\x1b\xfbZ" } leaves: { key: "CHM=" value: "\xbb\xc1\x88\xa9\xfeCB$\x96\x88i\x81\xe7pԃ\xeb\xd4\xdem\xa5\xf9\xef\x14+\x93y\xa4\xfc;\xc5u" } leaves: { key: "CHQ=" value: "^\xb8\xf8\xfa(\xa0Z\xffO\xc2W\xd5Wۗ\xc9\"\xd1\xe9a\xfb\xad@1\xa9r\xc1J:\xb2\xfa\x99" } leaves: { key: "CHU=" value: "R\xa3r\xf5M.VÜŦ\xd4\\\x9cQi\xc7\x1f0B\x1f\xd4uZ\xad\tĘ\xaa>uuy4" } leaves: { key: "CHY=" value: "\xf6\xd2fÅŖ>\x9c=\x90\xcb\x08\xf9\xaf\xd2\xf2\xd3\x07\xc8P_\x91\u0092G\x8bV\xd0Zꍀ\xe0" } leaves: { key: "CHc=" value: "Ar-\xbb\xf5\xa5\xe5\x1a\xf7\xb2\x8eV\xa5\x0e\xe6\x92ȁ\xb6<\xb4\xd2\x04\x96\x07\xab'\xb85| \x9a" } leaves: { key: "CHg=" value: "\x0fҏ\x07?a>=\x17\xc14U.\x08`%ՓN7\xa7\xeaR\x81-t<\xd1\xfe+@\x0e" } leaves: { key: "CHk=" value: "\xa9\xd5\xdd\x1d[;k\x910\x81\xa1\x1f\x99\x83\xc5\xc34\x9e\xe49~\xff\xd0e\xef" } leaves: { key: "CI0=" value: "ï7\xfe\xb3\xe3\xee\xd9`x\xf52\xf3\xe71@\x05\x17a\xee,\xdf\xe1ij0\x9c\xe5\x0c\xe4\x96\x03" } leaves: { key: "CI4=" value: "O\x15\x0c\xf6\xa0\x14\xbb\x12+\x9b\x96\xf8,to܌^O9\xff$Q!\xbf\xac\xa4\xe3\x05\x0b\xb5\xd5" } leaves: { key: "CI8=" value: "\xe3n\xebt\x0et\x03\xbe\xf6g:\xe4f\xb9͈poY\x81s\xc0:\x1f\x92\x1d\x10]\xd7\xc6mp" } leaves: { key: "CIA=" value: "\xf1\xfe\x043\xa5V\x9eOy\x80Iz\xe7\xd5Gn\xec$\x0cR\x1a\xc1d\x05\xf4c\xdd\xc4\x0bɇ{" } leaves: { key: "CIE=" value: "\xb9\x87\xd2\xed\x08\xe7\xcdUe\xce{\x91zo\x80\x91g\xf0׸\xab\xf7\x10b\x9c\xc5\x0c]\x07\xec\xf6\xbf" } leaves: { key: "CII=" value: "1\xa4\x9fm|\xad\x0f\xca\xf1>\x7f\x10\x9a\xaa\x94\x8deIn\xc5\xd2\x11\xd5\xcc\x0c\xdf\xc5d\xc9x\x01\xa7" } leaves: { key: "CIM=" value: "-S\"\xd1\xf2# \xd5\xc0\x90\x8fP\xd1\x15O\xa0\x941\xe4\xf0,b@M\x103Ōģt\x02SO" } leaves: { key: "CIQ=" value: "u\x89\x1ax7Ř;=\xc7G\x03\xac\xc7\xd0\xfe2\x87\xb6\x8b\xa6U\xe9\x11\x84\\\xe3\xf1\xd9%\t\xa0" } leaves: { key: "CIU=" value: "\xcbX\xf6\xa0 d;>\xc93ş\xc2AUb\x14\xa8,\xcb\xd1\xc5ÄŖ\xd6\xfc%\xf04\x8b\x88\x8e" } leaves: { key: "CIY=" value: "&aLQI\x15)'L\x92\xe9\xce)\xc4\xec\xeb\xa1\xe8\x18\x97\xd5mr\xee\xa8\x04\xa3\xa7\x13\x18\x1c1" } leaves: { key: "CIc=" value: "H\xf9Y\xf9xJ\xee\x17\xd2sīŠ¤&\xbe\x80֛\x80\x07\x14m\x05H\xa3+\xc2yy\x1a{\x12" } leaves: { key: "CIg=" value: "p\xe3\x84>q\xf9j\xa7\xf0\x1d1" } leaves: { key: "CKM=" value: "\x0b\x81`~G\x06\xd4>\xb4\x95\xac{\x85\x00\xfe\xcc\xedQ\x84\x1f\xf8ߥ\x98\x1b\n&Bl|\xd4." } leaves: { key: "CKQ=" value: "\xa7[\x0b\xa8\xec\x13\x08\xbci7\xf3\xab\xb8\xd5\xfc\xd0\x1cW\x19\xc4i\xafp\x06\xd3\xc6Hh\xb7Įƒv" } leaves: { key: "CKU=" value: "\\\xf2j\xfe\xc4i\xd0į‘n\x98\xd5YK$\x15:p$\x7f\xd8\xc8\xe0\x82R\xef\x85\x7f\xb3\x82\x06" } leaves: { key: "CKY=" value: "C\x01f 8j׀\xea3(\x90\xe4\xf2\xc7Of-\xf8m\x16\xdd\x0f\"\xe1\x7fa]y\xa6\xd1e" } leaves: { key: "CKc=" value: "\xaak6YX\x8f\x85#\x9f\xa0bČĨ\xdaD~\x16\xd7\xe7>\xe6'\xec{\xc34X\x06\xe8~}\x9d" } leaves: { key: "CKg=" value: "\xba G\xae\xd33\x9b@\xd48pLUD\xf5\xf4P\x82ΟÆĨ\x83080\x9bc\xb4\xa5\x8b\xe5" } leaves: { key: "CKk=" value: "\x0fl\xc0<\x83\xf7\xb0t[\x84\x92\xc6\xfe\xf4\x83\xe9k\xc9W\xbb\x9f\x11\xfe<\xdf\xd9z\xdf(h\xbcM" } leaves: { key: "CKo=" value: "[\x8d\x1f\xe3\xfb\xe2lt\x8dUf\xfaB.JE(9\xc0+UÔĩ\xaf}bR6J\xecŲą" } leaves: { key: "CKs=" value: " \x19\xbe\xe0\xf7\xdc4\xf8/(\xa4m\xe8\x94U\x1f\n\xfe\x99+\xae\xe0\tZ\xc0ևE\xb9\x98i\x07" } leaves: { key: "CKw=" value: "\xd3\x07|\xa7\x96\xfa\x89V^\xcb0\x94\x86\\aw\x9e\xee\xf0i\x97!\xadT\xbd\x1bV\x01y\x90-n" } leaves: { key: "CL0=" value: "\xb0m\x01\n.凂\xfd*\x81\xe8e,`\x80x\x8b\xd3\xe1\t\x15\x81\x18:]\x9a\xf9I9@X" } leaves: { key: "CL4=" value: "k@'@)\xdaA\x9b\xeeʤ\x85\xc2\xfa\xb3\xf7|\x93\x82\xb4\xbfo\x830O|\x99c&\xae\xa2\x9f" } leaves: { key: "CL8=" value: "r2!\xd5?\xac\xf9l\x99Ɛ\x81Ęļ\xb8\xb8P#\x08\xe3-\xbe\xdc\xc5\xc4`\xa0\x8c\x05\x81v\xd6" } leaves: { key: "CLA=" value: "#V\xdb\xdd\t\xb0Nh\xf3h\x97=\xc0\xf4\xee<į•ŊY\xd8Į›u\xcc9\xeeqj\xacf\x04" } leaves: { key: "CLE=" value: "\xb7%%\xbe:\x0f\x9aB\xf6\x98\xa6Ū…\xc2\xc0\xa3FZ\xb5\x05\xf19\x88XÜĸG^\xee\xcc\x17y" } leaves: { key: "CLI=" value: "]\x1e\xfd'z\x94\x9aU}\xd4\nS1Q\x10zG\xae\x13\x1b\xd8\xfd\xc1\x07b\xb9\xd3꩛\xeb\xa2" } leaves: { key: "CLM=" value: "S\x96n\xa5\xca$d\x9e\x88\x02\xe9\xc2BŅ’\x82Öŋ\x948\x96v\x94\x9c:\xff\x9c\x8f\xfa\xbe\x9b\xba" } leaves: { key: "CLQ=" value: "\xbaY\xebr\x00r\xf06cN\x15%\xf7,\x87I_\xb8\xf6pB\xe6\xe2j\xeaÜŋ\x84\x9b\xaa\xab\x91" } leaves: { key: "CLU=" value: "]\xbd\x06)\xe4\xb2\xfe\x11\xaeu\xef{\xa2!\xe8Úš\xcb8V\xfb\n\x8d\xab\xc60[\xe2\xc0\x86Ķ‹" } leaves: { key: "CLY=" value: "r\xa3\xbbU\x95\xb9\xc6\t\x01\x8fʛ\x92\xea\x0e1g\x05\x04\xb5\xda?\x88F\x89,\xc0\xef\x11\xbb\xb2R" } leaves: { key: "CLc=" value: "\xcbÅ E\xb4\xa8.@6*H2%\xc1\x18\xfa\xbb{A\x8f\xe5.\xf8\xa1ď\xc4'\x1c\xf7eh" } leaves: { key: "CLg=" value: "\xfe\xab\xcb\x0fw\xf2\x8e>P&\xf9\xf4\xa7\xc8\xf8\xbf\xf0\xa1\x89S\xbb\x84)\xc1\xb7\xd8l\x1bI7\x05b" } leaves: { key: "CLk=" value: "J\xa82sO\x0c\u0090\xab\xb9$\xc95\xc3t\xf2\xfcxm\x9aX\xfcT*\xe6N\x84\xd75cT\xc1" } leaves: { key: "CLo=" value: "\xcb\x19\x1e\xc2|N\xf9\x9e\xc1.\x1bw\xb8\xb3\xeeĮŋ|^\x13U\xf1\x0e\xdeLY\xfe,\xb2\x1f\xee\xc5" } leaves: { key: "CLs=" value: "\xa9|\x87\xef\xf7Îŧc\xb0\xc6\x1c\x93\xc6\xd1ÔĨ\x0cT\xefD\x85/Y\x8bV\xcf\x00\x9ed\xd0E\x1a" } leaves: { key: "CLw=" value: "\xef\xe2x6\xb6\x87\x97\x83V\x01\x0cD\xff˨S|B(4?\xb3\x87]h\x9dÄŊ\xa5r$\x13" } leaves: { key: "CM0=" value: "K \xccØŧ\x8c\xa0\xe5\xaa\xd8\xcb\xc3\xffu\xa3\x04G/\xb2\x03\x99\xb66*8J\x0f\xbc\xe4\xa0\xda\x1b" } leaves: { key: "CM4=" value: "\xa2/w#\xea\xe5\x137\xbb`\x18\x85`\xed\xa1\x98$\xbc\x1d\xee\xf3z\xe8Y\x9c\xda\x14\xd2.G\xa0\x87" } leaves: { key: "CM8=" value: "\xc4U\x84\xe4\x15\x17] \xb8WQ\\\x17\xf9XP\xf3\xfd\xbaF\x02\xa6\xb7\x81\x1fC~D\x9c}\xfa\xde" } leaves: { key: "CMA=" value: "\xa5\xdc\xf1\x9b\xe5*\xf6x\xd1\xfe\xd8in\x92\xd9UHI\x0c\x96i\xe0\"M\x15\x04=\x9bM|\xbf#" } leaves: { key: "CME=" value: "\xe0\xfc\xf4 \x14\xca\x07(n.k\x13\x9e\x08?\xe7+\x1a\x91\x9e\xe4\xe1\xca{\xe8A\xfb\xe6\x84\xe5\xc8F" } leaves: { key: "CMI=" value: "\xd8M\x12@\xa6\xb8Nv\x1b\x0e\x15,\x05Ψ*10\xd3\xc8f\x17\x8c\xba\xe8\x1dtΌ8\xcd6" } leaves: { key: "CMM=" value: "\x8aA\xf6\xd2$\xff8%W3`L\xcdW\x97ÔĢs)l\r^\xdcD\x18\x9c\x1a\xb1\x17Vj\xb4" } leaves: { key: "CMQ=" value: "\xbd\x95\n\x18v\\\x04\rd\xf7f\xd7,\xc7\xce/H\xe7\xfe\x87\xf5\xe0]\x87\xb2P\xb2ßŧ;.#" } leaves: { key: "CMU=" value: "m\xa9 ˧B\xcb\x1c\x19\x93\x825\xd1\xd0\x13\xf9FL\xe6\xf1\xe4\x9aqE\x8f\x8a\x18e\xddĖ“\x1c" } leaves: { key: "CMY=" value: "\x1dNC\xd34\xbad\xd3\xce\xd2\x1e\xe2T\x89\xf7\xec\xf6 uwy0\x12?O\xf6\x8d%\xb3v\xe4" } leaves: { key: "CMc=" value: "\xdb\x07o\x91\xb7\xaf4\xec)\xa3m\rk|\x93\xb1\xd9͈\x93\xc3g\x9d4o\x82\xda\xe4\x8b\xc6C\xf7" } leaves: { key: "CMg=" value: "\xceC#v{\x8c\xeb5X\xbb\x9aj\x97\x96\xf7\xe9\x08\x96E\x8b(\x07\xb6\x9c^X\x97\x10\x18\rB\x9f" } leaves: { key: "CMk=" value: "\x8b\xf3.;\xb3\\\xd7\xc1\x94\x99ÂĄ\x9d\x1e\x91\xfcU\xfe\x14\xc1H\xd1R'\xba5\t1\x94Ew\xa2" } leaves: { key: "CMo=" value: "\x7f\xe4\xc0\xf2iĪŽ\x83\xc6jÕĩE\x8a/\x1e\xbc\x9eŅŽ\xaa\\\xdbέëĨŸ+\xd6\x10X" } leaves: { key: "CMs=" value: "\x95T\xfaT\xc8Y\\\x13\xb9\xbe\xef\xde9-\x18\xfc\xcdW\x00\xf2\x9d\xd2DsM\x04_\x91&\xba\x83\xd0" } leaves: { key: "CMw=" value: "\x8e\xc1Ō‰\x07\x12=#\xeaO\xaeI*#\xc5\x0e\x01\x1feHb\xc9s\x10\xc1ˡ\xc60\xa1A\x97" } leaves: { key: "CN0=" value: "Ūŗ,\xc4PO\xb0(7\r\xfa\xd2\x7fu\xa0;\xe5\xdd\xf5\x0b+,\xe8\xa2A\xe4\x82\x18T\x9c\xe4\xe4" } leaves: { key: "CN4=" value: "&\x9c\xd8\x01\xe04\x97u\x8c\xa5eL\xcb_\xa0\xe7_\xa1w\xf1\xb0\xe1PWL\x9fE\xdf\"?k\x17" } leaves: { key: "CN8=" value: "~ܕ!\x11\x92e\x9e<\x89\xbe\xfcmY\xa00Y\xc3q\x1d\xddJ\x0b\xf5?PY\xfb\xdd" } leaves: { key: "CNA=" value: "\xf5\xf9\x86]\xbesw\x82Q\xc2K\x80\r\xc9#x\xac\xbe.Z6e}0df\x0f\xa1$\xd1-h" } leaves: { key: "CNE=" value: "ၐ\x05\xb9Zm؝L\x10\xaf~D\xf7\xa8Whs\x02\xf7\xe9\xd12\xc84F\x96]\x1bsT" } leaves: { key: "CNI=" value: ">\x85\xebۘ\x84\x0b\x01P\xb59\x9bif\x0fd\xab\xa5\x94&\xfd\x19\xdb\xf6\xd7\xd0\x7fÂĢ\x9d\xd3\xd5" } leaves: { key: "CNM=" value: "W\xf0\x99۞2\xba\xee;\xf0`?|f J\x87\x06\x7f\x8c\x02\xf0y\x19/s\xfe\xbe\xd4\xe7P\xd7" } leaves: { key: "CNQ=" value: "m\xf7\xfa\xdf\xc2\xe6\x7f\xe13\xe7{\x9cf\xe2\xb36=\u0085\xd7\xce\x1dQ\xa8\xcfd\xd4kj]r\xa6" } leaves: { key: "CNU=" value: "\x06|\x98/\xa0/,'*\x8a\x13T7\x02L{j\x10\xbe\xc3\x17z\xe2\x05Z\xc0\xd1E\xa3\xa88\xb6" } leaves: { key: "CNY=" value: "_)/J`%k>\x16\x8a%4\xac^!\xa0\xe8\x9cv\xb5\x9d\xe7\xf5j3\xf7\xde\xc3\xf0\x0c&e" } leaves: { key: "CNc=" value: "\xce\xe4\xd3>+e\xb8ä›ŧw`\x16j\x1e\xee\n-\xfd]=\x8c\xb9D.~=\xaa\xffQ\xf1\x95" } leaves: { key: "CNg=" value: "<\xd7䔱\x915\x89Ōž\xf2I\x08\x08t\xea\x02\x9f\x9cs\xad_(U\x8a\xb2k\x1c\xd1e\xeb\x14" } leaves: { key: "CNk=" value: "\x1c\xed^DĮŦ\xc0\xa6\xbdÆĨ\x05\xcdve\x88\xc4ן\xe1\xe6jtJ\xe4\xe0=ȁ\x1d!\x9c" } leaves: { key: "CNo=" value: "xÄĒb\xc6aR\xef\xd0\xc5M\xf3\xe9dD= Y8}I\x88BD.Z\xab\xe4\xc5%Q\x02" } leaves: { key: "CNs=" value: "\xe4%\xfdq\x9aG\x12\x15\x1c\xcbbc\xae\xe1+\xbaJ\x94PG\x94?\xa5į›ŊB\xfe\xf1\xd8#'" } leaves: { key: "CNw=" value: "\x96f`([\x9eN\xadbN\xae{˞L\x96\x1a\xe9\x81\x16qKk%\x96N\xd7u4.Nk" } leaves: { key: "CO0=" value: "\x99#G\x8b\xccH\xfb\x8bM=\xf2\xbf\x84wx\x96=\\\xceV9p7\xdb:ᖛyԐ\"" } leaves: { key: "CO4=" value: "-ę¯ŋ^s\xb9N=\xcf|\x91\xc2\xca\xf4v\xa4\xbd;\x9dvU\x93\x15\xe4;\x97Zv\xb2\xa3\x1c" } leaves: { key: "CO8=" value: "\xddAHU:G5\xa2\xc5o\x82\xdes\xb6\xfdQ\xa4\x0f\xeau5\x91\xa9N\x88\xfc.\xc9,\xb9\x1e\x83" } leaves: { key: "COA=" value: "0\xc6+\x99GJ\xd3\xd7\xc6'\t\xc3-@\xef\xe5\xc8\xceX\x9fP`\xec\xb5.\x89\xc9j60\xe9\xc2" } leaves: { key: "COE=" value: "9\x9bg\x99\x07\x98\xdf\xea՘`O\x1d\xe7\x14`\xa5\x18\xa5\xea;@k3\x04\x16\xa0\x18?Y\x8f\xd3" } leaves: { key: "COI=" value: "`XG\x91\xb5\xd3]\xb7\x17\xac$og \"\x0c\xbeB~v\xc1\xf1\xa1\xf6-U\\[M@F" } leaves: { key: "COM=" value: "\x1d2Jg\x03m\x01a\x7f_\xb3\xbd\xb8ČĒ\xc5\xc5\xe6\xef)\xf9\xce\x0e\x1d\x82b\xedXn\\c\xa4" } leaves: { key: "COQ=" value: "_T\ru\xa1\xd78\xc3s\xa6J1$m$z\x8c\xc4l\x12\xbcsr\x81Is\x19<\x83@\xc0\x9e" } leaves: { key: "COU=" value: "\x0c\x91\xd5U\xa7\xbc\x1b\xc1\x19\xe2\x13-\x19Ά`\x02\x11\x93\xa6w\xf7\xc0\xc0åϝ\xf8N\x12j\x0e" } leaves: { key: "COY=" value: "V\xc1H\xbe\xa7\x11\x1e\x94\x8a\x83\xd4\xd0\xde؁.\x0b\x05āŧž\x9fN\xee\xd3[\xb9\x15\x12e\x7f\x95" } leaves: { key: "COc=" value: "\x8d\xdc\xff9\xa4\x0fc\xb6\xfa\xcb%0\xef\xacm\xb6\xa7\xdb\x145p\n\x87ˇ\xa0\x1f\xc2C\xb3\xfcu" } leaves: { key: "COg=" value: "'(GD\xf9R\xac\xef\x05E7*\xe0\xe2X+lJ\xf5\xb6I\xddf:\x9aN\x06\xa6\x18\x8aI\x0c" } leaves: { key: "COk=" value: "\x8f\"I\x80\xe2\xa1\xf7\nt\xa6Ĩz9\x02\x19\t\xceJ~\xf8\xd7C5\xe3z0ŨŊ\x12S{" } leaves: { key: "COo=" value: "\xb0\x12\x1e\x82\x12jߏu\xa5\x07\xa6\xe2\x9e\xff及\xe7e~\x8b\xc1<\t\xcf\x05'\xd0\x1f\x8d\xce" } leaves: { key: "COs=" value: "<\x8e\x1a\x1a\xfcy6\xc6\xddd\x81/\x88PS\x07W!H5\x08]y(=\xff\x10S\x01\xfa\x10\xbb" } leaves: { key: "COw=" value: "(\xaal(\xb5\xc6\xf7809{oc\xb7\xa4\xb1Üąh\xce`n\xd04\xaf_\xba\xe6^Ũ2" } leaves: { key: "CP0=" value: "\xea\"B\x96M\xb0\xa6J\xbf\xe9\xa02\xed\xbc\x14O\xd3'1\xbb\xeb\x17\xf2qÎŋ)#\x99\xb4}\xa0" } leaves: { key: "CP4=" value: "\xcaM¡\x19\xc5B\xa2J\x1e\xa2\x16\x05\x99\xadØˇ\xe0nW\xeb\x1ex\xfc\x86y\x8d\x90\xc1\x0f\x12N" } leaves: { key: "CP8=" value: "\xdc\x0e\xbdI\x89\xd6\xd0\xe3\xfeI\x15\xf1ę†ļvO&7ėĄžËĨEjG\"\xdb\xdaA\x8c" } leaves: { key: "CPA=" value: "Ho\x81\x0f\xdfJ\xf5\x84h\xc9\xf6\xb4ÜŖ\x1a\x9a6\xdb`\xe1\x1dg\x93\xcaÍ´\xe4R\xfcPŌą" } leaves: { key: "CPE=" value: "4\xc1\\\xfa\x10z\x87\x8e\xe0\xe1\xd2éŧ\xc0\x82\x98ax\x01z\x83\xb8\xd7.q\xf7\xebsm\xc0\xfa" } leaves: { key: "CPI=" value: "!$5\xa0}\xcaze\xbc\x0c^U\x92\xbeX\x1a\xa9\x1c\xa6\x05\x7f|<\x0e\xc5\xe4P2\xbcgZ\xe9" } leaves: { key: "CPM=" value: "\x1biF5\x84\x15\xb8\n&'\x8680\x99\xd2\n\x17+\xa0\xd8Sl\xcf\xdd\xe2l\xcaRs\xcb\r\xd4" } leaves: { key: "CPQ=" value: "y\x12\xedM\xf2\xc3ׅ\xf3\xbb\x108rmp\x82B\xfb\x1a\x97\xe9N\xef<\xc6\xfe\x0c\xfa\x1bnD." } leaves: { key: "CPU=" value: "\r\xe3\xff\xf2\xc60xLdD\xd5\xf3\xe7\xf7\xf5qe\xabĪ>>\xa8\x85\xc9sOd'\xb8\x8f\xa2" } leaves: { key: "CPY=" value: "\x96i\xa8\xe2֐\xdf\x1a\xact\xf7\x10\xec\x91\x16\x1b \x13E*莎\n\x0c.H\xb8\x9c\xea\xcf\xe6" } leaves: { key: "CPc=" value: "2C\x98\x16N\x13\xd5^\xfd\xec\xe5\x0f\r\xc1\xb6\xddm'\x06\xc0\xfc\x83\xf0\xe8\x12\x8f\xd2\xef\xe7\x1b\";" } leaves: { key: "CPg=" value: "\xf4Г\xcd\xe9'\xc37\x90\xb3\xbch\x9e w\xd8W\x05\xed$J\rÕĒ\xa1\xf1\x13\x9b\xedQ\x80h" } leaves: { key: "CPk=" value: "\x81\xe4\xefx\x11\x94\xb6O\x90\x9fW6\xf6\xe7x\xa9\xc1\x1e\x95\xb1-b\x96\xcei\x17lPT\xefi\x10" } leaves: { key: "CPo=" value: "\xe3y\x81\x8a\xaa\x19\xe8-\x0b1\x96^\x85\x1f\xc0\xcb\xd1큂~@Q\x85u\xc8N\x11\xa1Ū—\xd3" } leaves: { key: "CPs=" value: "\xa4\x85\x89\xad\x11\xc1\xd0\xc1\x1f\x01t0\xd2=\xb7\xc9I\xf1\xdb`d69\xb0`\x90\xd28\xbfaZk\xbe" } internal_nodes: { key: "AQA=" value: "\xff\xffd\x12!9<8\xc3\xcf\x10\xab\xa8I\x08]\x81\xcbF\x13\xb8H\xf9\x9eoÎĢ\xbf\xad\xa6\x0c\x1c" } internal_nodes: { key: "AYA=" value: "\x11\x8by[́Q[\xb8\xdc\"\xedt\xde\xe4\xd2[\x8b^H&\x84$\xe43\x8f\xb5\xd2c.9\x93" } internal_nodes: { key: "AgA=" value: "\x07\x864j\xb2K`\xc0Q\xffKØ \xd0)\xb4\xd1b\x7f\xd8\xf1\xa7$\x05d\x9dq\xbc\xfc\xce\xd7\xc4" } internal_nodes: { key: "AkA=" value: "\xab)<\xc4o\x10{d0\xf8\xf8\xe1\xa1~\xb2J{^4D\x1bx{\xa8\xa4\x8a\xda\x1a\xe9Ґ\xf1" } internal_nodes: { key: "AoA=" value: "\xce\xc1\x8cz\xccA\xe7\xb7\xfcN\xe3\x90p\xe5Z\xfc\xf5^&\t\x18\x95\xbbܡ\xd0tQ\n\xf2\x83\xec" } internal_nodes: { key: "AsA=" value: "t\xde,\xeeb\x9b\xa5\x86%\xe8\xca\xff\xf21\x8a\xa1i\xc0\xa9â̘\xfd\x1fJM\xa3\xdd@Y\xb6O" } internal_nodes: { key: "AwA=" value: "y\xbe\xeb\x7f\x93ᄪ\x1b\xf0R\xcco\x89\xf2\xb4-\xd4\xd9\xce͉\xb9\xcf.@`zv$EI" } internal_nodes: { key: "AyA=" value: "Ihb\xe0?\xba\x98\x0b\x16\xedB\xe7\n%Cg\x97\x00\xbd\xf6\x13\tTy\x13\xc2\xd1].\x0b\xe6J" } internal_nodes: { key: "B+4=" value: "\xb3\xcf\x1cx\x08\xeeoysOR\xb4\xf0e\x1b\x9aw\xfaN\xc8\t\xb2\xc1\x03b\xcc\xf2q\xe6\xec҇" } internal_nodes: { key: "B+A=" value: "|\xfa\xf3\xe3\x9aszf\xf6Õ \xf6\xec\xfb\xc9\x0f\xea\xe5\x87*\x84\xb4,\x1e\xabZ,/\xf2s\\Q" } internal_nodes: { key: "B+I=" value: "\x1a\xe75\x95\x8d\x8b\x84\xb9\xa7\x15\xe1\xc3\xf22s#\xb1\xa8,\u0099Z\x8bm\x12\xa2P\xb8\xa1iŨŠ" } internal_nodes: { key: "B+Q=" value: ";\x9e\x1eŖ\xc0W;\xfd\x93\xf3\xdc\x108\x9f]}a\xde}(Đ¯\xe0\x91\xb7\xce\xf4ĐŽ\xbe\x17" } internal_nodes: { key: "B+Y=" value: "\xb5$=\x85\xaf\xa8uq\x87\x80|\xc9\xd89}1D\x03\xbf\n2y\x94j\x8e\xb2\x13\x1dw\x10\xf83" } internal_nodes: { key: "B+g=" value: "p\xa8\xb1Ũ\xbf\xbb⍰$HŅ &I\x06\xdezw!\xff\xafn\xa6\xaa<\x91\x86Z\x84\xfb" } internal_nodes: { key: "B+o=" value: "\xfe@\xed\xeb\x8d6\x86i\x8aiv\x12A\x7f\xc5O%7B퓱\xfd\x94\x8b\xf4\x7f\xa5\x98\xf2\xa1\xc1" } internal_nodes: { key: "B+w=" value: "fU\xff0d\xd3\xd4P\xf9\x82\xfb\x85\x1c(N\xf7\xecP\xdf|O\x9b2h\xfe\xd5\xeb\rŲˇ\x94\xe4" } internal_nodes: { key: "B/4=" value: "\x06B\xb49\xac\xf3\x90Qj\xb9\x17\r+N\x96\x8cL\x95\x94\xbdt\xcfM \x96-q\xab\x9f\x81\x8e\x91" } internal_nodes: { key: "B/A=" value: "9x\x13\x02\xe9T\xe8N}T\xbd\x02\xa0\x12\xf7\x1f\x1e\xb0\xddD\xb5\xa2o\xcfl%\xed~\xee\xd7>k" } internal_nodes: { key: "B/I=" value: "TP5\xba\xf9ĶŠ\xa2Vr\x1d\xbaN>4T\xd8~\xb2\xa6>#\xfed\xe7\x89\xd7_\xba~6\r" } internal_nodes: { key: "B/Q=" value: "\xafU\x93\xbb\x03{VŲ´\x1a\xf3\xfe\x05\xd2\xc6\xcaM\x14\x8155r\x11aq\xbd=\xea\xb2K\x16\xbf" } internal_nodes: { key: "B/Y=" value: "\xad\xc9k\xd3\xf6z\xc90\xda\xc0O\xacl\xb9\xaa'\x82@l\xc7Fl&߄\xd7f\xbdZ\xb5\xf0 " } internal_nodes: { key: "B/g=" value: "\xb2\x1f\xff\xa4\x1f\x03\x1a,LÕģ\xf2:4\x161\r\xca7\x08D\xf7\x03\x9d(o\x0bod\x8dh\xb5" } internal_nodes: { key: "B/o=" value: "=\x0b$\x1d\x8a0\xac\x14\x937\\T\x9b\xc4'qY\x1a-hic\xaaiVFq\x07\x96\x17$\x9e" } internal_nodes: { key: "B/w=" value: "\x84\x1bS\x9e\xfa\xccwh\x7f\x83[\xa7\x19\xb2y\xfa\x18Gc\xb4Z\x889/\x16\xd0@m\x976\xa0\x80" } internal_nodes: { key: "B04=" value: "\xb58\x01G\xbcJQÔ¤\t^ \xced\x93İ\x8ax\xedX\xf08o}\xd4G\x85\x99jx " } internal_nodes: { key: "B0A=" value: "\r\xb4\x07\xbe\xbc\xee\xf4\xde|\xd5b\x85\xf6Ĉ\\\x7f\x8e~\x17-47d\xa0*^\x82]x\xd7\x1a" } internal_nodes: { key: "B0I=" value: "\xb5\x86\xa0膀Cb*hY\xe7}\x1f\x1d\x19o\xca\x1c\x9d\x0e\x94%B\xb5\xcd\xd0ĖŦ\xb7\x89#" } internal_nodes: { key: "B0Q=" value: "\x84<\xcc\x0e\x8c\xce\xf2\xb7\xaa;\xebe\x1bA\x8e\xb0#7\xec1\xeb\xb7<\xa2'\x88\x8c\xb8\xfc\xab\xb8\x01" } internal_nodes: { key: "B0Y=" value: "\x00Y(q\xc4+p\xb2\xb2TU\t!1\n\xa4\xcd\x1e\xab\x82\xe5\xaaÍĒX\xf3\xad\x8a\xe06\xa8\xe0" } internal_nodes: { key: "B0g=" value: "\x06\xf9\xe0L~ \x08\x0e\x8b+\n\xb2/\xde\xfc\x89A^1\x1bv\xbd\xb8\x14\xff+ɀE[m\xd3" } internal_nodes: { key: "B0o=" value: "\xceH\x97\x1a\x9bV\x05\xe1\x19\xd5,MŃv\x9c\x1dI\x14\\\x18q\xb0\xcax\xf3\xa5\x03\xbcv\xa5\xfb" } internal_nodes: { key: "B0w=" value: "\xa0]܍vyŨĄ\x94\xa4#\xb6\xe0]\x8b|z\xee\x86;\xa1\x00;\x192!\x152z\x1f2C" } internal_nodes: { key: "B14=" value: "\xd2\x13>\x0c7]S\x81\xd8Įŗ\xbc\x81lh\x8b \x08R\x1f\xfb\x99\xa1Ûž\xc6\xe7\xe6L\xa4\xc0V" } internal_nodes: { key: "B1g=" value: "g\xe9x;\xef#\x8b\xedJ\xfa\xfbn>\x12K\x1aAؓ\x85\x01\xa2\x08}\xb2\xf8\xdc\xe5\xc2\xf28p" } internal_nodes: { key: "B1o=" value: "\xbe\x9f\xe5\xef?R\xb2\xf4[Be\x97^:\x91\xbf\x92\x19\x019)\x1dK\x93l\x14\xa3\xa7\xe6\x8e\x10\xf5" } internal_nodes: { key: "B1w=" value: "\x07\xeb\x07\x95\xdd\xfc\xc9Bt\xe8\xada{q\xc2C;\xc0\xcbÜą0\x7f\x0f\xca\xf6={U\x99\xe4h" } internal_nodes: { key: "B24=" value: "\x11\xc9+A\xaec\x8a)j\x82>\x1a\xdf\x1c{.\x1d3\xd7{βH\x0e\xe3\x14Hkzrt\xfb" } internal_nodes: { key: "B2A=" value: "'R(\x1d\xafU\t\xb4\x1b\xff\xeb\xfb\xac \xb7\xb3\xdd\xefr%w[X\xf44\xcd\xf8\x12\x1b\xd0s\x81" } internal_nodes: { key: "B2I=" value: "Ãĩ.\xe0b\xc91\xafEB>\xd9\xfd\xd7^\\}G7\xb0fZ\xc3\xe6\xdb_Kj\xb3\xe7.\t" } internal_nodes: { key: "B2Q=" value: "\xdc\xdd\xf7\xe9;\xb6\x05M\xea\x02l\xeai\x17\xb9\x0en:\xca:\x163\xb9\x18\xe54\xf9\xad\xb21\xd56" } internal_nodes: { key: "B2Y=" value: "\xdbN\x96\xc2MX8\xa6(\x84\x96ā\xa52\xf0ŨĨk\xe7\x9aГ\xb2\x80 \xb2ã›ĻZV" } internal_nodes: { key: "B2g=" value: "\xf6\xf6.\xdd\xe9J\x8b\x9e\"\xe7E\xf9\xf2\x83\xd1tÍ\x94U\x87Oq\x01\x85\xfa\xc7)\x97\x19S\x0f" } internal_nodes: { key: "B2o=" value: "\x17)\x83͕u\xfd\xa8&\xc5\x05\xc2۝\xea:\x93+\xb5Q\xb9\xa0=\x0e\xcf|\xc1\xe0Z\xf4\x95Y" } internal_nodes: { key: "B2w=" value: "\x86o\xb8\xb7\xe9\x1b\xdc\x1e\xaa8}\xa3\x0cBŲ#Jw\xb8\xac\xfb\xe4\x81t;?_\xcf\xd7\xfbb" } internal_nodes: { key: "B34=" value: "\xb9,h\x90`\x88\xf3)!\xaf\xe0Z\xd5 \x99!۟\x892\x1f\xe3\x8e\xe6\xaeE\xbe*\x04\xb4\x0c\x9a" } internal_nodes: { key: "B3A=" value: "\xe1\x87{\x1b\xb4_\xb4?\xee5\xf8\x929\x99\x1b\xa26l\x86\x193OM\xb5m\x84\x08w;\x18\xf2~" } internal_nodes: { key: "B3I=" value: "TØˇ2\xbe\x04\xb9\xa1\xdc?\xcfw\x7f=\x14\x87\x92\xb9\x8d/\x8e{O\xc3\x15#\x83\x1f\x82\x9a\xea\x05" } internal_nodes: { key: "B3Q=" value: "\\\x16\x0fb&\xe3\x07(\x858\x91n=\xf5\xf4\xd3\xe51G\x0f\xa8\x12P\x9c\x1a;\xf4D\x05,\x85\xe5" } internal_nodes: { key: "B3Y=" value: ")\x10\xf8\x17\xb7\x11\xc37\xac\xe4\x01f\x87\x95D\x18m\xe93\xadp\x7f\xcbK\xf1\xc0\xec\x87\x0b\xbf\x17\xa3" } internal_nodes: { key: "B3g=" value: "L\xf2\xb5Z\xe1\xb6ëǃ\xa6\xaf$\xc5L\xba\xc5Đ?\xc4\xdd&':͞\x0c6\x9bĐĻ\xf9" } internal_nodes: { key: "B3o=" value: "8o0\x87\xe6\xb5\xc3\xceT\x1b\x86\xec\xe2\xab\xc1Yw\xc1\x9a\x05\x03\xb8\x04\xb3\xc6\x01\x83\xa4\xbe\xbe\xf2a" } internal_nodes: { key: "B3w=" value: "Y\xb3\xd4#\x86W\x1a\x0f\xbb\xa3\x03\x12\x04\xb8\xe7Õŋ\x18Lt)\x98\xfdj6\xa1\xe0*U\x97\x10\x9f" } internal_nodes: { key: "B44=" value: "o*Q\xbb\xaa\\\xee\x9a7\xb1\xfcd/\x83@\xd5x\x15y\xceI\xe1\xcf&1\x14\xa9\x84zÉĄ\x1b" } internal_nodes: { key: "B4A=" value: "sG\xb4\x92\xb9\xcc\x03\xbc@\xa3\x91e\xf4\xceØ­\x11e\xdd\xec%\xe4TK/m\x17T\xfcbp\xa6" } internal_nodes: { key: "B4I=" value: "\xabo\xfa\xc9\x1d\x99\xa7\xc8æēš\xe0\x05_AK\xe3\xb2\xdbG\x87t\xe7\xff\x1c" } internal_nodes: { key: "B4w=" value: "\xdbn'#~\x1b\x84\xa6\x18\x88\x06\xff\xe9r#C\nE\x19B\xa0\x9d\xc6~\xa8\x07\x90:\x05\x0e\x9a\\" } internal_nodes: { key: "B54=" value: "\x11[\x1d\xb6\xa1\xa2a\x10;\xa7\xe4\xba`)\xc3\x1eQ\rЁ\xbb!\xb2\xbf\xa2\xe6\xb8\xfd\x92Å a" } internal_nodes: { key: "B5A=" value: "\xe0\xd2\x15\x03\x1f\x82Ee#\x1b\x9e\xb7?e\x94h\xaeJPN:=Ôˇ\x83\x95\xff\xd58\xba\xa1\xdb" } internal_nodes: { key: "B5I=" value: "\xd5\\\x18F\xd4\xc3\x15uR9\x08@r\x87\x8d\x97\x04\xe8\xc6\x07G\x14\xb8\xf6\xad\x9c\xa5\x1e\xe1\x87\xeb\xc2" } internal_nodes: { key: "B5Q=" value: "0vS\xfc:įŠn2\xe3\x94\xde\xdd^\xbe@\x1b\xe5\xc5ķ…œ…\xc0D\xb8\x11\x86IYy" } internal_nodes: { key: "B5Y=" value: "Y+\xa3\x8e\xc4\x1b\xd9\xcb\xc9\xcc\xea\xa5D\xa6\xf3\r\xb8#6S\xdb\x03h\xe9F\x0be\xba\x18\x0b\x12\x9b" } internal_nodes: { key: "B5g=" value: ".\x15\xa1\x84\x9c\xdb\xebH\x914\xbd\xf3r\xf3\x94=X\x14\xce\xf1\xa6\xdfa\xdb\x12\xd4\xc1Ū´\xf95\xc7" } internal_nodes: { key: "B5o=" value: "\xac\x1a\xa9\xaf\x01\\\x92\xba\x8e=\xc2R\xa9{\xff\xc89\xe4}\x82r͙\x1c\x13@\x85\xd4×Ŧ\x88\x00" } internal_nodes: { key: "B5w=" value: "\x13\x96\xf4qTNA\xa4e\xd66\xbazk\x97\xee`떂\xe5;y\x12J\xfd\x97\xc2s\xa9\x8a\xb6" } internal_nodes: { key: "B64=" value: "\x1e7\xe7\xbcW>\x12b/ČÃ´\x00\xac\xb7\xc5\xce\x1d&\xd8┊K\xb3XT\xa2ŒV" } internal_nodes: { key: "B6A=" value: "u\x0b\x07\xd7ourRF\xb9\xbd\xd2\x1e\x8e\xb7\x8c\xbe{J\xdbJ\xef\x01+\xab\xf0\xd9N\xd5\xfdy\x13" } internal_nodes: { key: "B6I=" value: "\xef\xadn\x17\xea|Fh\xaf\xc0\xa5\xf7\xf9$(@\x90DGVL\x05j\xbc\xa5=YC\x08\xac|\xa7" } internal_nodes: { key: "B6Q=" value: "#:ØŖ\xa9\x8f\x9aU\xc6k\xdeZ\xe1\x12y\x11P\xe5\xdb\xf5\x89\x08\xff\xf0\xe9\xad\x0c\xb8\x16}\xa8(" } internal_nodes: { key: "B6Y=" value: "n[\xa3u~ē1\x1a\xf9Jz\xe6\xf5β\x11<\xbd\xa6\x1c\xb3~\xceJ\r2F\x87\xcc\xe5\xf3" } internal_nodes: { key: "B6g=" value: "\xcbk\xd7\x11\xc20\xcb\xee\x1b\x89a\x10\x02\xd97X&\x1b|\x04c\xb6\x8f\xa4?i8\xb7\x19\xa7\x8a\"" } internal_nodes: { key: "B6o=" value: "\xdc\xf5\xbf->ԋ5ÂĒd\xe0%7\xf5\xad:\\8\xa5S\x1d\xe9Ōž)\xa3\x9d\x7f\xd9ĮŸ" } internal_nodes: { key: "B6w=" value: "Q\xf7\x9bN\x16\x92\xa6\xc7,\x9az\x15m/\xa3:\xefV\xaf\x04\n\x04\x80\xa0\xacŌ°I[\xb8K\xe6" } internal_nodes: { key: "B74=" value: "\x8c\xac\xa1\xb42d\xc11n\xe1\x88\xd29\x83&#\xc4\x17\x10\x92\x83r\xe3Ô­vO`.\x8fl\xd8" } internal_nodes: { key: "B7A=" value: "\x80\xf6\x88ڀ\x13\\Ú¯\xfbBÃ\xcc92\x98*DR\x7f\x89\xda\xe4\xd3Æĸ+\n\x13\xfa|" } internal_nodes: { key: "B7I=" value: "7a\x19\x0e\xc45h\x06\xec\xd4$J\xc1\x9a\xfc\x8b\xc19\x03\xcb\xddN\xb1l\x1dM\xe6)\xc5\xdaz\xf0" } internal_nodes: { key: "B7Q=" value: "\xb9\x8d\xe0\xd6˯x\x86\xc6\xc6\xcag\x88\x17\xf2o\x1fS\xfe\xaa`\xb4\x1f\x98o\xf9\xf2\x17\x0f.\xad " } internal_nodes: { key: "B7Y=" value: "\xc7Orn\xa2\xe6\x8d4\xa2\x16\x91\xea\x9a}\xa3eĒ\x9c\x88\x03P\xb6\xf1\x87\x16D\xdc_\xa2F;" } internal_nodes: { key: "B7g=" value: "\xf1\xcegM\x8f\xe1;\x89\xfc6\xcf#-M\xdc\xfb\x89\x00\xfa\xfbk\xc5E\xa0\x15\x97\x11M-d]S" } internal_nodes: { key: "B7o=" value: "#&\u0098\xf0\\\x97~\x82\x81\xfb\xcb/k\x05\x9e;\x0f\x03T\x11\xef\xc1\x8d\xfb\x99\xe9Z#}\xff\x19" } internal_nodes: { key: "B7w=" value: "i\x94~\r\x1a\xbf\xdbm\xb6\xe8:CL\xf0\xef|\xfd:Sq\xc3ˊ\xe7\x8b\xc0\t\x8d\xe2́\x9e" } internal_nodes: { key: "B84=" value: "\xa0\t\xf9\xe9\xbeL\xf6&\xf4~/n@\xff7Ė‘\xdd\xd4O\xe8W\x1d\xf9\xf9B\xd6\xda\x1f\x02\xcf)" } internal_nodes: { key: "B8A=" value: "İ\xee\xe3;\x0fq\x80\xa9\xd6l\x08\x91\x9f\x93\xc5\xe6\x0f\xdd\xc0EK\x05A CmA\xc5Z\x99\xd1" } internal_nodes: { key: "B8I=" value: "\xebTxFh\xf7\xb2G\xf3\xf3\x00\xb2\xa3\xc8\x17\xf0j?h\xe6:Q\xbf\xfe\xfd\x9a\xeeR\x87f\xebr" } internal_nodes: { key: "B8Q=" value: "\x91PW{<\x8a\x10h\xd11\xf3\xa6\xa0EP\xaa\xe5k\x05\xb8lQ\x05\x04r\x0b\xdfW\xbe\xf8&\x10" } internal_nodes: { key: "B8Y=" value: "Įš\x04\xa0r,\x06cĪ“\xbfY\x99q\xae\xdbU/\xf8\xa2w\x96\xae7\xa4@nW\x08\x17\xa09" } internal_nodes: { key: "B8g=" value: "\x98\xfcęĩĻ9\xf3\xae\x96\x10\xf0sSL\xce\xe9q\xfd\xe1\x8dv#\xc3\x1b\xdd\xc5'\x19f\x89\x90\xf1" } internal_nodes: { key: "B8o=" value: "%\xb1\x01\xf6\xa1!\x00\xb5\xaf^Z\x9c\x06\x83\x99;\xc2\xee_\xd0\xe4\xa4.\xf4\x88\x951\x10\xe1SiJ" } internal_nodes: { key: "B8w=" value: "2\xd5S\xfb\x08ėS!\xc1mU/r\xccm\xb1cK\xd7\xca\xfd2:\x8cQ3w\xceD\xce\xf7" } internal_nodes: { key: "B94=" value: "\xf9\xc3bX<={\x94\xce\xfe\xaeq\u009b/\x100P\x16\x9d\xe9\xd3R\x1bu\xbf\xfdysG}Y" } internal_nodes: { key: "B9A=" value: "\x99Ȩ\xe4\x11\x9b_\xf8\xa5\xc0\xef\xb8\xe2\xf3\xe1ʧ\x14i\xa1T<\xdeY\xc7Æ˜Ę•\xfa\x87\x8b" } internal_nodes: { key: "B9I=" value: "\x1c\x8djCjÚž\n\xb4,\xca'v\x1aR\x19B\x14FS~\xf8\x15$\xcc\x7f\x1e\x8f\xac\xdd\xd7\x0c" } internal_nodes: { key: "B9Q=" value: "\xa7\xfb\xdd\xe7\x08\xf7\xff\xa3\xef\x81?j^~4\xeaĶ‘\xbcR+_nTUf" } internal_nodes: { key: "B9o=" value: "\xb2\xbf\x90\x89`v\x87\x17m\xb6\x12\xce΂*\xfd\x156\xba\x0e\x7f\xc1\xca\x06%\xbb\xeaU\xd1\xc6\xd1o" } internal_nodes: { key: "B9w=" value: "\xac\x98R\x92[L\x05\x1b^1\x1b3wW4\x91|>\xdc\xcc\x7f\x82&\x88\xd81-px\xa99\xb2" } internal_nodes: { key: "BAA=" value: "%\"&\xeaLz%W\x9b\xdeM.\x11K\xadIs\xc7\xdf\xf4\xb3\x1bQ\xca\x1e(¨2\xf3\xb5U" } internal_nodes: { key: "BBA=" value: "T\n\xc3\xd7S;\x15\xf9\x88Ni\xff̀d\xb4t|b\xdc\x1c\xefv\xa0y\xf6\xca7\x8d[\x12\x1a" } internal_nodes: { key: "BCA=" value: "m\xa1IC\tT\xf4H\xab\xf1\xf4\xa4^?\xad\x9b\xf5\x10\x87\xa6\xfc%\x15{\xa3Öģv\xb4ĘŖh" } internal_nodes: { key: "BDA=" value: "$\xa3z7\xc6H\xe9\x02\xfc[\xe5\x14\xc4\xcb\xed4\xfe\x83'\x0b\x11\n\x07\xf1x\x12\x9e\x8a\xc9\xf0&~" } internal_nodes: { key: "BEA=" value: "\x8e\xad0\xdbuA)\xbe\xac\x93/*O\xbe\x06\x9d0&S\xa2T\xf2W\xac\x14\x91\xc8b\xa8\x8fC\x9b" } internal_nodes: { key: "BFA=" value: "R'?\xe4\x1d\xc0\xb4i7`{_h(\xf3\xcbHR\xa5\x90W\xd7M\xd7\xe6\xc1i\xbc:\xe0C\x9e" } internal_nodes: { key: "BGA=" value: "\xb8ԈG\x00E\xf9\xb6\xbeɈ\x00y\x15\x03\x9b\xc4w\xde\x12\x90&\xe0\x08u\xf6\x91l.\\\xf6\x1a" } internal_nodes: { key: "BHA=" value: "K4\xc3\"\xd0c\x93\x03\x98\xc5\x0b+\xf9/\x9b\x0eX\x0bY\t=\xdd^\xc4\xf4\x9c\x03ZJ4\x1aq" } internal_nodes: { key: "BIA=" value: "\r\xc6uz\x05i\xfa%}\xd3mC\xd1V\xe6z\xbd\x01B\x1b\xe0\x94}][\xabÖŖ\x82Â\xfe" } internal_nodes: { key: "BJA=" value: "\xd3\xdd\xc2Bv'\x17H\x9e\x18\xf2\x98c)scI\xbb|S\xbfL\xc1p\x9dPZȄ^\x9e\x91" } internal_nodes: { key: "BKA=" value: "\x88\x02\nn\xf6\x96r1\x83UbN\xa8\x82?\xb0Ͱ\x1b\xd6=\xdcee1MvP\x88\xc3\xdd0" } internal_nodes: { key: "BLA=" value: "\xf6\xf3\xa9\xd4\x01A\xef?\xa9\xb5O9\x81\xa4ÛŗŅĨ\xeb\x14\xabX^\xc6čCi-\xfcU\xb2" } internal_nodes: { key: "BMA=" value: "5\x90_lJ\xd0/\xa3q\xca\xf7\x95\xd2\x14A\xf2f\x84\xc0\xe8,b5\x9d\xeaߖ_\xce\xe0\xe8\\" } internal_nodes: { key: "BNA=" value: "x=2\x04\xdd6>W{\x00Y;UˍU\x0b\x0bV\xa4T\x92\x94\xd3\xc6\xe7\x8a\x03Z\x97<\xd8" } internal_nodes: { key: "BOA=" value: "C\xc0ͯF\xe66\xb3\xb8\x80\x8a\xb5A゘B&\xc5q\xdc\x02\x1a\xcb\xd4\xe0\xd7L\xe6$\xb8h" } internal_nodes: { key: "BPA=" value: "S#\x9c\x97\xackΚNR7\rjq6S#zQM\xaa\xca2c\xe8i\x86\xa3\xc99dx" } internal_nodes: { key: "BQA=" value: "\xc0\x8c\x1bmT+Z\xcdQ\xab]\x9e\xe5\x14\x10\x82Īą\x07-\xd9~F\x83Ų€\x16Z\xc9g\xe0\xd9" } internal_nodes: { key: "BQg=" value: "\x02\xc0\x94\xda7\x90\"cM\x01\xed\xf8\xc3\x02\xeaj\x198+\xc2Äē4\xd3\x7f\xa5\xe9Ō‚\xf4_\xdc" } internal_nodes: { key: "BRA=" value: "\x8e\x85s\x9c\x9fÃą\xeb\xb4`\xd2\xde\xc97\ntI\x1d\xef\x81\x1c\xa3\xa2\x02\x93\xbb\xb4it]\xb3n" } internal_nodes: { key: "BRg=" value: "G\x9a\x04\x1c/\xfb\xd8R\x87&\x82{\x8e\x9fD?y*\xb5\xb6Į‚\xa2\x1d\xc3\xd7\xdcV\x05{\x02 " } internal_nodes: { key: "BSA=" value: "\xd6\xd8\xef\xd0\xfb#\x94\xad\xbc\x07\xa6\x83h\xd3\xeb\x94@\x1c\xa1\xea5\xf0\xfaQ1R\x86\rĶŗg\xce" } internal_nodes: { key: "BSg=" value: "\xe3\x96%D\n\xd4i:\r\xe8|H\x7f\x8a\xac\xa3_MS$:Q%\xeb\x08\xfa$\x8d\rZ\xad)" } internal_nodes: { key: "BTA=" value: "\x06E\xe9\x15\x13Z\x0c\xc9}G\xc9!\x11ژ\x0f\xcey\xdd\xdb\xfd\xd3\x15'gL\x9a\xa6\xed\x01+\x02" } internal_nodes: { key: "BTg=" value: "\x18>\xdeU=t\xfa\x82[\\\x86\xfb\x0e\xbb\x89F\x0f\x18\x8a\xd8\xe2\xb0\\7f\x02\x85\xee\x90Y\x07\xb3" } internal_nodes: { key: "BUA=" value: "\xdfxt\xbe\x81\xdd.\x91\x0cL\xcc\xe2\xbb\r\xd3\xf3\x16g,+i.1\xf2\x1ee\x90\xae\xc3\xca\xc5," } internal_nodes: { key: "BUg=" value: "\xb2\xa3k\x04\x174\xe4\xe8\x9a=`Q@(\xf7ÃĄ\x97i\xac\xce\t\x9aA\x91\x9fN\x8f\xff\xd5Om" } internal_nodes: { key: "BVA=" value: "|\x7f\xbf>\x04\x10\xa2ۈN@U(\xa5\xa9\n\xcd\xcc\xe5q\xe6\xdc\xceWL\xec'\x86\x8bË \xfb" } internal_nodes: { key: "BVg=" value: "l\xd0`\x98N\n\x9aÆģ\x08\xd8\x15\x10cb\xafʗ\xe8\x04Lyv\x19W\x88\xa2\x99\t\x85\x86\x16" } internal_nodes: { key: "BWA=" value: "\xef\x1818ěK~Wo\x95\xd9`\xb65\xedL\xe1N\xf6w9{$\x03Ŝ2\xeb%\xe4\xe6" } internal_nodes: { key: "BWg=" value: "\xfa\xc5f\xda:\x92\x9fo\xaf%\x0fw\\(\xfe\xb6\xa3\x01\xee6\x90\xae\xfb\x87W\xff\x04%+7\xbdW" } internal_nodes: { key: "BXA=" value: "\xaa\x03\xac\xc8A7\x80\xee\xcd\xfe9wu\x88NKO\x05\xbf\x85^rCĪŦ\xc67>jx\x85\xcb" } internal_nodes: { key: "BXg=" value: "\xf0}\xe82X*\x12Y\xefÕĨË­N\xf1\xa8w\xdb\x1cR'\xd2\xc3G\xc7:\xd6Ū¨\xc6KY" } internal_nodes: { key: "BYA=" value: "\xe5Ua\xdb\x01\x96\xec\x0f1\x1d\xcd\x18p\x88\x08p\"\xb0\x8bא:dhL\xc5\xd6\xfc\xc0\xc52\xa6" } internal_nodes: { key: "BYg=" value: "\xe2\xb6aĐĄ\x82^\x0eK\xeeG\x897\x04i\xa6\x96\xb1\xbc\xddhQFS\xf9ŪĩC_ZŌ‡" } internal_nodes: { key: "BZA=" value: "\xa8\xa2\xfe\\\x9f\xa4\xe5o\xe1\xa5\xcer\xca#\x8b\xbc/\xad\xddr\xd2\xe2\xdcA\xc6\xf8\xc2s\"\xe2\x190" } internal_nodes: { key: "BZg=" value: "\x9a\x16\xc2_\x15+b\xb4\xc76Dvz\xac\x07:\xa9\xfa\x9f\xbf\x83\xdf\r\x84\x9e&C\xbd\xf1\xd3Ũˆ" } internal_nodes: { key: "BaA=" value: "\xae'\xbe6\xf4.X\xf0\xa3\x89AZ\x03,\xf4Es\xaaR\x1bk%Ns\\\xf0Bt\x02\xf2\xb13" } internal_nodes: { key: "Bag=" value: "MG:\xf0d\x19\xc2#&\xb1\xd38\xf4\xa1\x1c*\xebr\xe9\xf1\xeaad\x92\x85\x0b\xfd\xe0\x18\x91\x04\xa1" } internal_nodes: { key: "BbA=" value: "k\xdfx\xd5\xd0\x11\x1a\x0bD\xb8Gt\x8eh0-\x81\xba\xe9/VËŽ\xb3\x87n\x9e\x156\xb6\xf1\x19" } internal_nodes: { key: "Bbg=" value: "\x8c]x!\x01\xa1y\x1f\x9evB\x8b=\xd8\xe9\x87\xf3\xdbŨ\xc5\x1d\xee\xedB\xfd\x8a\xf2\xaa\x10\xfa\x82" } internal_nodes: { key: "BcA=" value: "\r\x14X96+i\xac\x9c#\xaeb\x94˃\xd0>N\xfa\xed\xa2\xcaB\x04؛4\\\x06\xe9\x0f\x80" } internal_nodes: { key: "Bcg=" value: "\xa6?\x93\n\xa3\xbc\xbev\x16F\xdc\xea:=b\xe8\xf6ȅ\x08\xce\xd2\x12G=\x88<\xf4\x93\xd1Ōŋ" } internal_nodes: { key: "BdA=" value: "\xa0ÃĒ-N\xf9Mbw\x80\xf8Lq'\x9e{1QL\xfb\xa9\xc2\xef%\x13K\xf8\xf6Ȇ:\x03" } internal_nodes: { key: "Bdg=" value: "\xc1\xc9<\x05\xfaM\xf7U$\xe6\x8d\xf1\xf2KF\xceI\x80N`t8\xfd\xfb豍V({\xb9\xee" } internal_nodes: { key: "BeA=" value: "ņ“Š’\xbe\xb5\xfd\t\x9e\x9f\xa3\x81\xadui\xdb\x1f\r\xb0\xac\xd9\x1aD;\xbb\x967\xc7\x0eY\x07}" } internal_nodes: { key: "Beg=" value: "pm0\xdb2\x03\x9ea\x94M]\x0f\xd5\xfe\xed\xa9ōŒ†›\x0e.d\x05\xee\x11\x92\x1a\xe3\x9bn\xbf" } internal_nodes: { key: "BfA=" value: "\xc2\x08O\xceaWpŪŽ\x91~ńG^!]\x16\x0b\xd1p=\xcd\x10\xe9\xae\n\xdf\x15\xea[\xb7" } internal_nodes: { key: "Bfg=" value: "\x9c5l\xa0\xbf,^X\xec\x04\xeb\x0c6\x06\xed\xaf\xe5\x97$\xf7\xeb\x7f\xbc[\xc7\xe4Y\x16N\x0ek&" } internal_nodes: { key: "BgA=" value: "!\xd0ʨ4K\xc6?\x94H]\x89wH\xfc\xe8\xe6Øš\xc8Q\t\xf0\xccwO+\x0f:2I\x9d" } internal_nodes: { key: "BgQ=" value: "\xc6\r;\xf9\xcb\xe8^\xc1`\xf4\xf1$\x0e\xd5\xe2Ū†î—\xa5\x9d\x05)\xbb\x95\x82\xc8=͂\xfc" } internal_nodes: { key: "Bgg=" value: "\x18E\xac\x02\xb0xؤ\xe9\xa7)#d\xaf\xc5%\x83İ\x93\x82~\xdbs;\xd9a\xac\xb0\xea\x03\"" } internal_nodes: { key: "Bgw=" value: "\x9f=Z\xe6\xc9\"ČŠi\x0c\x08O3gr\x00!\xb2\x8c\xb42\xdc\x1ezęŋ¤-.\x19\xb8\x01" } internal_nodes: { key: "BhA=" value: "\xcb\xfd_x\x057\xba\xbd~\x04\x9b\xc3⏛&\xff)ːQx\xe2\xe2\xd9D\xe2\xa6#R\xd1d" } internal_nodes: { key: "BhQ=" value: "\x1a\nD\x82\xfd0\x1c\xd3l\x05\xf9\x8e\xc1\x95\x1b>\xbf#\x96\x12\xcc\xf0\x84\xc4\xfd6\xd6E~\xd9\xd2\xf7" } internal_nodes: { key: "Bhg=" value: "~L\xff(\xa5\x90\xce\x7fV1Įŗ\x9d\xb8\xfc]`\x1a\xed\xfc\x05[H\x00z-%ʁn\xcd]" } internal_nodes: { key: "Bhw=" value: "1\xad\xbc\x0eq:\x94/:\xd8\x1fBYJ\xc9\xc9\x0f\xa38\xb3\xec\xdc\xf2M\xd5\xf3\xe3yxr\xf1W" } internal_nodes: { key: "BiA=" value: "]#\r\xa7\x18\xb8Īŧ\x05%\xc3\x03ɋ\x10uF\x93<\xef\x1cΟ?4c\xd3JȅÄļ" } internal_nodes: { key: "BiQ=" value: "d\xf1'FJu\x11\x08\x88y\x03\x18\xf8\xf3x\xeaO@\x10\xa72\x8a\xaaΓ\xe6yc\xfcG\x15N" } internal_nodes: { key: "Big=" value: "\xa9H\xe4\x9fo\xb1\x80C_\x02O\xc9\xdf\xf2$\xc3\x11\xafL\x95Ņ‹\x16K\x83\xa3\x13\x80y\xaeTH" } internal_nodes: { key: "Biw=" value: "\xc1\xc1\xfa\\\xe6\x1d\xd0ߔO\x1a__\xb5\xab\xcc\xe7\xc8\xe4\xe9\xeb\xb1\x16Z\xb6\xa4\x9e\x96d\xe4H\xaf" } internal_nodes: { key: "BjA=" value: "\xea5\x1dU\xb5G\xc0\xbc\x9e\xf4\xa4\xbay|\xa5\x9bq\x05\x01ßģo\xe8P=\x89\xc6\xf7\x16|<\xa2" } internal_nodes: { key: "BjQ=" value: "\xc9\xe4\xcf+\xc4X\x8e\x07\xbf)K\xeb\x0c\xde\"\xac/;\x95\xb5\xc6P0\xfd\xf6L\xf2\x10/%\x82N" } internal_nodes: { key: "Bjg=" value: "A-\xc2SÆ \x1a\x1c\xf5b\xa6\x9b\xdcA4\x11\xb2x\xb3\\\xcfiu\xf0)(\x02VU\x01\xff\r" } internal_nodes: { key: "Bjw=" value: "\xbe\x95\xe6e\xa37\xe5\xc5\xf9\xcfX\xc8\xe23\xa6֍5拄\xba\xbc\xf4\u0084\xf5\x8cu{>\x8d" } internal_nodes: { key: "BkA=" value: "n\x1c\xfd\x9e5W\xf4\x11\xa8\x0eu|Έ\xd9DZ\xa8\xdaA%\x99\xf5\xb2\x0c9\x80\x97k\xa1Y0" } internal_nodes: { key: "BkQ=" value: "\x8a\x15BF\xfect\x1c\xb2\xfdq\x97t\xcf_2\x10#\xecD\xe5\xe6\xf0\x9bxGÖĒ\xe9\xed\xb32" } internal_nodes: { key: "Bkg=" value: "\xa3\xf5ÛŽ\x86r\xbb\r_\x8e\x90\x8aŲ–\x07\xbc\xb5#d<\xcfŨ’I(\x03\x9f\xb9\x14c\x19\xea" } internal_nodes: { key: "Bkw=" value: "'Ȳ/Z\x11\x8c\xc0\x0b1\x98\xee~R\x85\x14ib\x07\t\xddÕ |\xa5K:\xdc\x1eN\x02\x95" } internal_nodes: { key: "BlA=" value: "ÂŦ\rA\xd1\x1e\xb1!\xf6\x0b\r\xd9\xc0$\xb5\x02\x13B\xbb\xd5|\x82\xf2J\x19\xf4M\xa3G?\xfb\x9a" } internal_nodes: { key: "BlQ=" value: "\xe2B+Ōŋ\xc6\xdbԚ\x820\x11\x8cAv\xa5\x02o%\xf0\xfa\xe8\xec\x1bE\x13q8\x07A\xcam" } internal_nodes: { key: "Blg=" value: "\"zĮ„\x15\xec7\x8d\x16햧\x80D\xcd\xc3\xf3\x86\xf8\xdfP\xa0\xe9\xae\xe1\xc2\xd7\xf4\x7f\x9b\x86)" } internal_nodes: { key: "Blw=" value: "M\x9e\x8e\x91\xfe\x7f$L\xfc\xc3\x16\x03ԏ͙1\x92\xff\x06\xb1\x0b\x01ߤ`W%+\xe2\x1d'" } internal_nodes: { key: "BmA=" value: "\xa2\xab\x15$g\xdd,\xcc&C!\x13\xf3\xe8\xf7,y\xb7\xa2\xeb\xf7\x94\xa7\xed\xcd<\x7f\x91B\xcd\xfc\xdd" } internal_nodes: { key: "BmQ=" value: "\x08op\xa0/4\xa6\x90\x1chTC\xca9\xff%\x93bɒo\xf7g\xe9u\x19\xfafKmÂē" } internal_nodes: { key: "Bmg=" value: "\xb0$7\xf61\xf7\x1d\xfb\x069\x85)\r\x8a\xa9\xb88Ū…d\xa2\x823\x15\xe3\x94\x0f\x95\xad\"\xa9\xa3" } internal_nodes: { key: "Bmw=" value: "a$\x01\x9eu\x13\u008f\xfd`\xfb\xc0+Nz\x9c\xa6\x0fR\x12\xe9\x1c\xa4\x0b\x97-àB\x9f@\x0b" } internal_nodes: { key: "BnA=" value: "\xe5\xe3\xa9N\xd8\\A\xba};ʝ\xf2\x16\xfbP4\x10\xe1Rx%\x8d\x0b\x16\nŅ‹\xae\xb2\"~" } internal_nodes: { key: "BnQ=" value: "\xd7\xe9YR\x1f=\x988T\x06#,\x8e[\xad\xd6:\x1e\x90\x14p?K\xe9\xd8В\xcdu\xee\xe0{" } internal_nodes: { key: "Bng=" value: "\xf9~\\\xd6\x13\xf1\xbfp\xda\xc4,\x19\xee\x91Z\xc1[7'\xb26z\x823\x7f\xfe\x16\x02^[\xc3\x19" } internal_nodes: { key: "Bnw=" value: "o\xa3\x996Jh-\xcb\xd1c\xf5=\xa6\xbd3\xeb\x1e\x9dp\"\x96\xb6ۗ\xaa\xad\xa5\xef{V/\x0c" } internal_nodes: { key: "BoA=" value: "\xde\x0c\xa5\xe9tU\x15\x94\x84\n\x9dҤT\x17\x1f\x87\xf8\xf5}\xb5}Oo\x8e\xdc\xeb\x8a\r%\x9e\xf1" } internal_nodes: { key: "BoQ=" value: "\x07\x07nD˗$Y\xb5\xf7\xb9d\x9d\xb5&Vl J\xdf5\xe4\xbc\xda\xd49\x07\xb6&F\xce\xcb" } internal_nodes: { key: "Bog=" value: "\xd5B\x0c\xb7\xd1Ô§5I\xa9.\x9a:1\xb1\xa6\xf2\x19E\xcd(\xb2q\xbe\x0b\xc1\xeeN\x18\xaeV\xf3" } internal_nodes: { key: "Bow=" value: "\x93\xa1\x8e\xc1\xef\xc1\xc5\x7f\x9e|}r\xe1\xe0 \xed\xd4\xeb;2zF\x85\x18\x9e\x12U\x14n9\x05q" } internal_nodes: { key: "BpA=" value: "8\xe4\xdc\x0f\xa9\xd5}n\x17\xd0bK\x07\\\xb5\xf0mU\x04B\x13\xa7<\xe4;\x16\xaf\x1f\x87|6\x16" } internal_nodes: { key: "BpQ=" value: "P\">\xad\xe7z۟\xc5%\x10\x0fAH\x85\xe7\xc4\x14\x80F\xaa%\x11}\xa2\xfdƌX\n\xbbl" } internal_nodes: { key: "Bpg=" value: "\x1an\xa7\x85\x11=d\n\xe1[?\x16\x8f\xf8\xd27\xfc\xdbm\xe4aĖz(\xd4\xf4\x0fxDE\x1c" } internal_nodes: { key: "Bpw=" value: "\x01\xbds\x9e\x14\xaa*\xa0\xfc\x7f\"\x91\xfa\xab\xe5\xf5v\xe1Ya,ĪŊ7/\xf6\x02\xa9\x9e\xb2A\x1a" } internal_nodes: { key: "BqA=" value: "\xf3\"h\xc4\x10\xb1\xa0\xbaG~\x80m\x8es\xd3\xe5B\xf9\xdf$\xefnG\xc6\x12\x00:\xb6\xed4\xecy" } internal_nodes: { key: "BqQ=" value: "\x80a\xe8\xf1\xb6s\\\xb8\x16\xce\xc0\x01\xfd\xfb\x9a\xcf>\xc7\xfd\x87\xc9mÅ´\xa9\x01d\x13x\x98\xb4;" } internal_nodes: { key: "Bqg=" value: "T⎋\xa6\xa8\xccSK\xa2E\xff\x97mZ\"8\xc0\xf8\x19\t\xfeR3-\xea\xb5;\x94\xd8\xfa\xa6" } internal_nodes: { key: "Bqw=" value: "\xda\xca\x01P\xceƊ\x94\x847>\xa2|-g\xa4\xf0\xe7\x07\xee\xa0\x00d\x8fi\x87`\xe27\x8e\xb61" } internal_nodes: { key: "BrA=" value: "\x96\x05\xd3\xf7r\xf2'\x8c\x80\n\xe9\xbd\x18\x14\xf7\x91hA\x08\x03+\x04\xcaÖŦ\x94\xb9\xa2\x05\xee\x12&" } internal_nodes: { key: "BrQ=" value: "\xb3o;\r\x04\x18\xe6\x8dG*\xa52 `\x1d\x01\xba{\xa7\xedq\x1e\xbc\xa7\xcdu\xa1Yj\x0b\xc9*" } internal_nodes: { key: "Brg=" value: "h\xbb\x9a.\xa2\x01\xf4ãģŧ\xe5\xdc\xd0\x0b\x04Ãĸԃ&\xc7Ћ\x9f$+\xe5\x15!V\n\x8e" } internal_nodes: { key: "Brw=" value: "\xf7H\th\x1d\xf8\xd0H\x90L\x13\x1e\xef\xa0\xcdßŦK\xe4tT5G\xf1J8\xd5˟\x83\x84\xd9" } internal_nodes: { key: "BsA=" value: "\x9f\x82D\xd2R~1\x8b\xc8\xf1\x10F}M3m0\xf4\xac\xffvfs{\xcb\xc1\x7fW\xf1\xc3\"\xad" } internal_nodes: { key: "BsQ=" value: "\xd4\xedڏw\xdau\x0f`}gK\xa2w\xab\xfe\xea\xed )\xa3\xf4\xd70\xb2Y\xf9u\xefA\x94\r" } internal_nodes: { key: "Bsg=" value: "\x9bP\x1a\x17\xb7\xf9\xffk[M\xaez]\xc0Bˋ׎SJ7\xeb\x10h\xd9XÅ˛Û‚S" } internal_nodes: { key: "Bsw=" value: "\xaa\xde\r4\x17\xd7\x1b*\xb4\x03\xfaYh(T\x87\x1ac9%n\xba\xf7V\x0fwB\xc8i\xf7ܧ" } internal_nodes: { key: "BtA=" value: "J\xe2\x95+\xb2*Ū¸\x93\xbe\x009f\xe3>\xfeK\x87$ K9\xa8\x1f\xbeO\xad6Ɔ\x8c\xd9" } internal_nodes: { key: "BtQ=" value: "K\x96\x9e\xde\xd2\x00\xb8\x11\x1c\x01\rn\xce$(u:2$G\x94\xe9\xedd_[\xaa\x0e\x90\xb3*v" } internal_nodes: { key: "Btg=" value: "\x8eŨŧ\xab\xdd[\xd4:\xd0\x03\"\xf2fÚŠf@#\xd80m;\xbf=\xd3\xea\x16\x14h\xe7\xb69" } internal_nodes: { key: "Btw=" value: "\xb5*\x7fãļ\x8d\x81!\xaf\x1d6Q\xb8'/M\xe8=v\xfb\xe4\xee\\\x87[=\xd5\xf1\xe9F\xb6" } internal_nodes: { key: "BuA=" value: "r\xd6-\xa7\x00_\xab\xfeSz\x1bf\x06B\xca\x12F\x11\x13\xfd\xb8\xf3+\xa4i3V}\xf0" } internal_nodes: { key: "BuQ=" value: "\xf0?\xbeC\xf8dwqk\xc6\x0c\xddj\x1a(\x07\xd6\xe8\xdcåļ¸\x85\xa5\xfa\x81\x95\xcc\\7\x1d\x94" } internal_nodes: { key: "Bug=" value: "\xf5 \xd1Íšd\xeb\x0f\xd2\xc4\xed,\x1c\x9b\xa3\xa7m\xb2>?{J\x1e\x89\xe97+~\xa1%:U" } internal_nodes: { key: "Buw=" value: "\xb9\x05I~u\xd41\xe9\xa7\x7f\xb1\x9f\xaa\x90\xea^\xa7Py\x9e\x18\x15\x85\x95G\xe4-\x8f\xff\xcf\x00\xc8" } internal_nodes: { key: "BvA=" value: "\xaf\xafih\xc6\xcf\xe5\t\x1eéŊļ\x1e\x8d\xd72\xd0\x16\x18\xcf@m\x8b\xeb\xa97\x9a\xbf\xc6\xfb\\\x7f" } internal_nodes: { key: "BvQ=" value: "\x9a*N\x17Ć/\x1fh2K\xa7\x08\x82_/\x82\xdf\xf5z&)R\xb6\x91\xbc\x89\xb0\xebU4+" } internal_nodes: { key: "Bvg=" value: "\"\xa9\xe3g\x06\xa1/t\xca\xc2x~Ah\x00\x8dz\xc9q\x03\xb14\x0b\x15\x9bF\x88\xd5Gz\x00^" } internal_nodes: { key: "Bvw=" value: "H,\xc1\xdb@N\x12K@\x9a|\xd1\xf2\xe9ek\x00\x12\xb3\xb3\x95\xae9\xc0!b\x0c\xe1H\xc3P/" } internal_nodes: { key: "Bw4=" value: "1\xab\xf8\x9b.\xf7\xb9\xa32t_y\xffG\x18\xa0\xd2=z\x16|Z.v\x03in\xe8\x14e\xb6\xf6" } internal_nodes: { key: "BwA=" value: "\xc8ld\xcd\x0e\x8b\xa8g\xecSM\x1erz\x08\xbbY~y\xb2o\"\xc2\xd0\xed*U\xc6\x04\xaf\x1d\x19" } internal_nodes: { key: "BwI=" value: "8M\xf39,\x08'\xdc,Ė‹\x1a\xc7\xf0\x7f\x1e\xa5I\x1c,aۏ\xd6We" } internal_nodes: { key: "BwY=" value: "\xf6R\x9f\xde^&\x00\x8f\xf9\x8d%\xe6\x02\xae\x95\x94<\xbe\xae\x9e'\xb4\xc1C\x84\\\xe5Y\xc0\xebT\x9a" } internal_nodes: { key: "Bwg=" value: "\xda<\xfd\xcf\x03\xaa\xf3\x91\"\x18\x18\xb1\x1f\xcc\xd0Į˛z\x90\xa5\r}G\x82\x97\x86ax\x9c\x8e\x84W" } internal_nodes: { key: "Bwo=" value: "\xca~\xe5\x8a[]\x15W\x1ao\x99\xa2B\xf9\xce1|\xb9\xb1\xe1U\xf5\xed\x04\xaeY^\x98\x1f\xc7?\x1b" } internal_nodes: { key: "Bww=" value: "\x89u\xd59\x1b\x8drLU\xb1\xfa\xad_\x8bh\x11\x1a\x96\xfd\x8aC\xad)n\xe4\xf3W\x8e\x80\xabjA" } internal_nodes: { key: "Bx4=" value: "i\x1a\x81q\xf8p\\\xd6\xd6\r\xaa\x9f\x04\x82\xc4\xf0\x07;\xa1s1\xc3(\xddM\x93\xb8 \x01\xf8-\x9e" } internal_nodes: { key: "BxA=" value: "fm\x1c\xb6\x04po\xb9\xa9\x84\x0e\xcc\xc1\x81L7\xe2{H/r\xbe\xcf\xf1R\x82\x80>w\xbeqt" } internal_nodes: { key: "BxI=" value: "\x1d\xc6(z\x14+E\xdbcFE\xab\x96k\x81\xbb\xa3\x81;\x8c\n\x85\xb5\x1cH)\xdc\xfe\x9a\xe8\xc7\xfb" } internal_nodes: { key: "BxQ=" value: "6\xcc\x05\x864\xc2|\x1e\xccZ\xa8?M\xf7\xe5JlI\x8f\xae\xbbu\x88L\x9b,G\xf9\xc1}\xdf4" } internal_nodes: { key: "BxY=" value: "\r\x03ho\xbc\xc9j\xccpl\x9c.\x03h\xa6}\xf0\xf2˙\xd3\xee%Ų”j\x85\xc4m'\xd7(" } internal_nodes: { key: "Bxg=" value: "\x17\xa5\x85\xc1\xb5\xf8\xd56^:H\x92\x8d@\x01\x0eh\x19%F\xbe\x92\x00\xba\xde&\xec\x88\xfa\xc6r\xb7" } internal_nodes: { key: "Bxo=" value: "\x9bu\x88V,\xdd@\x05˟$K\x18\x08\x89\xb4\x8d\xd6\xf5 O\x90\xff\"ĮŽÉŒÎŋ\x95\x10" } internal_nodes: { key: "Bxw=" value: "/6F\xee\xec\x13tp\xcd\x01\xea\xa8\x03:\xd9q\xee\x11\x8b?\xfd\xe5\xa7\xd6\xeb`؊P(((" } internal_nodes: { key: "By4=" value: "\xe9{\xf0R\xfa\xech\xae\x07\x861X\xcb\xcd\xf5\r\x93\xc1dbAw\x98\n\x15\xd6id\xe4\xfd\xbd\x11" } internal_nodes: { key: "ByA=" value: "\xbaqN\x0f\xfe\x08s\x1e/\x9dr˨2\xbb\xbd\x0e\x99:\x18\xcc2,\xa2F\t\x98D4\xd1ڜ" } internal_nodes: { key: "ByI=" value: "\x7f\xe5\xa6f#\xfb\xd5\xc1x,\xd5\xf0\x98T.\xbdI\xe1n\x8eJ1w\x96&#E7/d\xf8\x9a" } internal_nodes: { key: "ByQ=" value: "}JвdJ!\x04\x0e\xbc\xadI\x7f\xc1\x0c!\x0c\xf9\xccs\xe6|[A@?t\xd30\xcb|\xd6" } internal_nodes: { key: "ByY=" value: "{\xd0VQg\x82T\x08\x02x\xd1-<\xd1\x15\xd5[\xaa\xf9\x17\xf9|\xe2#r\x18\x85\x97\xfd\x06\x12\x0c" } internal_nodes: { key: "Byg=" value: "\xef\xb6oC\xe2\x10Q[\xdd\xd9R\xff\xd8Ed@_\xcbD\x8f^\xa8\xfc\xa4\xaa\x0e\x90W\xea,#\xcc" } internal_nodes: { key: "Byo=" value: "Q\x17OŨŖ\n.\xa2]\x08\x83ڂ\x82\xce:w\x00<\xf02z-\xa1`6\x1d\xf4r\xd7\xe1\x8a" } internal_nodes: { key: "Byw=" value: "\xb0\x1d\xb5#\x835N\xa4\x8d\x8e\x90\xf4;u\xc6\xfd\xa8\x17\xda\xfb\xee\xff>\xab\xa6\xeeÕŠ\xd8\xc6\r." } internal_nodes: { key: "Bz4=" value: "\x1btu\xbd\xc9\xcd\xf59W~\xf6ʛ\x82Qj\xdeG\xe1\xe0\xc1\xd7\x19\x8c\x98CO\x1c\xe51\x12#" } internal_nodes: { key: "BzA=" value: "\xc7\x1fQ\x86}\x97a\x98\xda\x18\xeaq\x97\x05\xb5\xf38;f]\n\xb1\x9b#\x8b\xed\x94;G\x8bc\xad" } internal_nodes: { key: "BzI=" value: "\xac\xfa\x8e\x04\rOL\x11\x0b\xe0\"\xd5i\xa1\x95h\xac\x10H[5\xa8F\xd0\xdet\x01\x17Qk.\xb5" } internal_nodes: { key: "BzQ=" value: "\x19\x81D\x175L\x81\xb7\xee\xceWS\xdaAƄ\xa5\xd8K{\xde\x17\x88\xf7\xf4\x14zB\xa0Y\x80G" } internal_nodes: { key: "BzY=" value: "Z\xfd\x82?S\x12f\xee\xa4\xd1\x118\xdbY\xb5\x9ef8\x16\x16\xfe\x08&py\x87U\xffQ\x02\xa2\r" } internal_nodes: { key: "Bzg=" value: "\xd5@\xec\xe4\x1eæŧ¤\x82\x80\xf4`\xd70d,j\x8b\xb0\u0080\x08iXvÜŋᔖ\x0e6" } internal_nodes: { key: "Bzo=" value: "a\x8b\x1b\x95x\xa6\xb34\x8b\xcd\xc3\xdc\r:\x04\x95i\xe5_\xea\x91;\x1f\xfdC\x11\x18$\x8cÎĸ\xf9" } internal_nodes: { key: "Bzw=" value: "âĢģ\x88d\x1c]B4\x7f\x88\xf0\x8d)d\x83\xbb\xf5\xc0$f\xfa\x9fkc+\xdd*\x9e\x15H\x9a" } internal_node_count: 254 prefix: "\x00\x00\x00\x00\x00\x00\x02" depth: 8 leaves: { key: "CA0=" value: "\xf7T\x9c\xf1T\x89\xa4\x84\x1f!\xc2\xf7j\xb4\x91N\x9dÚģ8\x1c)GŲ§A\xdbe\xb2\xe0%\xd6" } leaves: { key: "CA4=" value: "\xd5;|\x1eig=\xf8kO\x82\x9c\x95djc\x9a\x8b^\xfd\x8e.'\xb6\r\xbc\x84\xdc\xd5\xed:\xaf" } leaves: { key: "CA8=" value: "\x0b\x03\x9d\x86F\xb6\xddy\xf8P\x05\x1e\xfe\xce\xe4\xc6G\x16\x16h\x98\x8b\xc8\xe9\xf9!\xba\xdd\x12\\\xa4\xf4" } leaves: { key: "CAA=" value: "\xf1,'cI\xae\xbf\xccR=v\x9e\xe47\xa1\x01F\xbf\x8e\x00\x14k5\x9dh*\xce؜\xd5\xca+" } leaves: { key: "CAE=" value: "\x93\xfe\x1a\xdb~\x8bZ\xd5\xef\xf1\xf6\xeb*\x1d\x96X/\x95\xcd\xe2\xdfZkΏ\xe6\xa7(\xb3\xa4\x0c\xf2" } leaves: { key: "CAI=" value: "\x80\xc9B\xfe\xfbŲ†g\x95\x80\xa50G\xf1\"\xfbW?hB\xf2\x08\xe6\x17\xd5\x03\xec;\x85\xdf`\x04" } leaves: { key: "CAM=" value: "\\_\xf7\xe4_\x9dQ\xa9\xe7'TE\x01\xe1د6\xc1\xb1Ŋ \x85\x97\x97h.\xb9Z=k\x80" } leaves: { key: "CAQ=" value: "\x83\xf8\xb9\x19\xbb\x07\xe7\x14\x15\xf1\x0f\x89MFÆš\u0084\x1d\x91\x0eO7\xa2@9\x0f\xb3\x08\x16\xd5{" } leaves: { key: "CAU=" value: "\xf7\x8f\xe3\x02\x1a\xe8r(\x85\xab\xcb\"\x86\x84\xca9\x0f\xe3*p\xd9\xe7T\xf8q\xf8\x88G^\x9c\xb7\xd4" } leaves: { key: "CAY=" value: "/\xfaa\x98\xec\xd4\x0e?\x94\xf8\xdd\xcb_ÃĢ<\xcf?\x80J\x88\xf2\xa0\xadl\xfd\xfc\x17\xa9\x90g\xb7" } leaves: { key: "CAc=" value: "\xabq\xb2j\xac\xc0)\x8a \xfaËē\xa6C\x00\xcc\xe0\x03l\xa08\xb7\x9dh\x80\xf2\xf7\xca\xc1\x82)'" } leaves: { key: "CAg=" value: "\x0b\xb9\x8bq\x90\xdb\xf2s\xe4d\x88\xfb\x8e\x9fx\x98\xb7\x92Q\xca\x16\xb4S~Ne\xd3\x14\x12:6\xc3" } leaves: { key: "CAk=" value: "\xbe\x05(\xfe\\\x08\xc3+\xc6\xde\xc7ÔŦ\xff\xb9JO\xed\xc2P\xf6\x19i\x15\xbbe\xcfßŊ\xa1g\n" } leaves: { key: "CAo=" value: "4Y\x06\xc2U\x15\xa4i9d\xbe\x88\x1bGR\x9b\x90\xb8\xb7\x98\xaeM\xab\x17L\xf2\x98c\xcbA\xc4]" } leaves: { key: "CAs=" value: "\xa92\xa0\xa0\xfe\xd83\xa8\xf5\xdf.u6U\xda\"Se\x1fÛ~\x1e\xed\xfbT\xbay\x8f\xa1b\xa4" } leaves: { key: "CAw=" value: "\xbdk\xcb\xd6\x1dF4A\xf8\x99\xee\x12\xff\xec\xf9\r\xb4\xff?6&\xc2\xff+\xaa&\xc2ejSde" } leaves: { key: "CB0=" value: "F!\x97\xff\xef\xcb\x03K\x02\xebÜŊ\x90\x93\xb8dw\xee~\x8d`,ZL0\xe8Øļ:\x03 \x02" } leaves: { key: "CB4=" value: "\xa7>R.u}D\xd1h\xf0y\x88\xf9\x1d\xfb\xc3\xe8\xf5Ú´\xa2\xb9ŪĢ\xfdB\xa7\x04\x0bĖē=" } leaves: { key: "CB8=" value: "\x8b\xbc\x81j\x05I\x93I\xa2\xa0\x1f\xe7\xfdN72\xea\xe9\x00?\x08+\xb1\x89\x89\xc1O߃\x14\xdb!" } leaves: { key: "CBA=" value: "\xfd\x16\xdb1\xae*\x03\x82\x88\xf1\xfc\xb8\xa4\xe7\x8dG\xdeS\x9bU\xb2SN\xf0:\x02r=\xb8\xa5\x0bS" } leaves: { key: "CBE=" value: "\xe7f\x05BT$j\x90\x03t\xd7\x16\x0b\xe5\nU\x11\xb7\xf7J\xc6+\xd0?&\xa3\xd0\xf1\xa082c" } leaves: { key: "CBI=" value: "t\x8d\r\xe5\xc6 \xbdy*\x8e2QZ\x81\x8a\x12\xc5\xd9\xcf Nib\xacx\xde *D\xd3" } leaves: { key: "CBo=" value: "t\xe3\xe8\xba)\x87)\xa2\x89}\x0fV\xff\xf8\x14\xc8'T\x98\x04\xce\"{ˎ6\xbc\xea\x0cX\xb7\xe2" } leaves: { key: "CBs=" value: "\xda\x14\"\xba&\xa4\xe7\xe4\x18I8Z(\xbdKÛĒwK\xfaH\xf3\xd6O2\r\x0c\xd4\xf3\r\xb7\xf2" } leaves: { key: "CBw=" value: "\x04\x18\xec/5G\x1c\x1eb\xf3N\x90\x89\xf2&?C\xdcpJ[G\xd6%mE-nX\xe19\xee" } leaves: { key: "CC0=" value: "n\x8cMW \x9ej\x89\xec\x02\xa2%p\x85;\xe0=\x1a\x90\x03\x91\x0fn" } leaves: { key: "CCA=" value: "\xcd\xce\xf6u\x90gG\xf6q\xafh\xfc\xd1\xc9\xf2b5\x8e\x95c\n\xd6F\xaf#\x11ms\x08\x06\x90+" } leaves: { key: "CCE=" value: "\xd6d\xe9I\xe4\r\xead\xe32\xef\u009a\x80vS\xc9 \x17?\xbb#\xfd\xfdM\xb2\xb1EL\xd6e9" } leaves: { key: "CCI=" value: "|\xeazuD\xab\x10%\x8a\x13\xb7\xa5Z+f\xfa" } leaves: { key: "CCQ=" value: "\xf3\xf5\x0cP)Pk\x04\xc4^\x14C@@gS3\x98(\\ \x1a\x12\x8a\x8bH4\x9f\xe6y\x01\x04" } leaves: { key: "CCU=" value: "\x90q{\xeb\xccy\x9f\xebC\x10\xa2\xb1AI\xc5&'\x0ey\xceڎ\xe3%\x13\n\xbd\x82B8\x8d9" } leaves: { key: "CCY=" value: "brl\xabT\x88\x99\xc6\xee\x07\x1b\x07\x93ͤc8\xf2gzOyY\\\xfaGׯ\xc3,\xb5\xe2" } leaves: { key: "CCc=" value: "\xad\x99)\t.\xa0\"\x93z\xcd\xc6\x06A\x05qs\x1f_F\xea%\xa1\xfe\xb4\xc5{o\x0f\x8aB\x11\xc4" } leaves: { key: "CCg=" value: "t\xd6il\x15{\xfe86\xb4\x9b\xc0Ć\xfa\x1dL\xa5\xed[Yu)\xd7ĶŽ=\x8c\xc9uT\xea" } leaves: { key: "CCk=" value: "\xf5\xdd\x04Ō‰\xb1\x97\xe5\x0b\xa8\x82\x05\xca\xf9\xf9\xf7?F\x87\xed\xa0\x1eiŅ—'V\x89\xd8t\xc9Y" } leaves: { key: "CCo=" value: "\x16\xff\x9c\xd8=^\xe8\x06\xe8郅h\xd5j\x12_\x91\xfe\x14_Z\xfc\x8e\xa1\x17\xaau\xb5\xc0\x83\x0f" } leaves: { key: "CCs=" value: "\x0f\x7f\xc8\\Rr\xbd,\x1a\x89$zl\xd6\x12\xcd\x7f\x83\x9e\x13J:Į¯\x1fÅ­\xa5\xe7\xb3A\xd5" } leaves: { key: "CCw=" value: "\xb8\xbc\xba\xcf\xd6\x11(,\x0b}\xa9\xe1Gd\x00R\xf8^\xcd<\x96HR\xd2\xcaS<\x8d\x8fj\x8a\xf1" } leaves: { key: "CD0=" value: "\xde$\xf6\xbd/]\xecI\xac*\xc4.X\x81o\x04\x1fo\xe2K\x8d\xc0aB&a\xd4\"\xd5\xfd\x11\xcb" } leaves: { key: "CD4=" value: "7\x86H67\xbcW\x9f-B\xd6([\xee\xc9\x14\xa8\xff\xa7\x0e\xeb\x8f:\xbfs\xfc\xe9\x9e+\x15\xfa9" } leaves: { key: "CD8=" value: "\xcaj\x10\xd4g\xc0]\x1aOz\xea\xc8\x02b\xf3\xa36\xfb\x12[G\t\x89\x84\xed:\x9cf\xf4\xa1\xa3k" } leaves: { key: "CDA=" value: "\x0b\xff\xf4\xfe8\xfa\xc7>\x1d\xb0*\xa7ה Y*\x0f\xc2/N\xc1\x81\x94h/\xea\"\x86\xd3D\xd5" } leaves: { key: "CDE=" value: "\xe3\xea>'{\xa1~ŨŠ\xf1\x83K\xce\xcb\x07\xef\xc1x\xd0" } leaves: { key: "CDQ=" value: "\x12\xeeN\xe7\t\xec\r\xa6\xb6\x81Aet\xfe\xca\xe1\xb0\x19(\x7fJ'JC\xec{\x1b\x98#㖐" } leaves: { key: "CDU=" value: "\xc1\xa3\xd3\x04gZ\x1c\xdf\xf4p\xd7l\xf9<\xbbQ\x81ILL'J\xadr\xd8RqzX\x1e\x19\x9c" } leaves: { key: "CDY=" value: "\xa4\xcd\xfdiY)\x12$\xbf\xee\xa2\"\xa3\x12\xe5,k\x94O\xc7\xfbF\x83\xd4\xec\xc4&\xb9p\x8c\x87\xef" } leaves: { key: "CDc=" value: "Zy\xbf\x98\xbdc,\xfd2\xac\xceŌ“\xbbV?\xc8!\xff66$\xa9@\xf1āĨĸJ\xa4\x95\xd3" } leaves: { key: "CDg=" value: "\x8fJ\xaf\x1a\x1c\xadR\x0b\x84\x1cn\xe8\xd7\xe9\xe72'G!\xf5\x7f\xf6`\xc2@0\xb3;\x05\xd1\xe2E" } leaves: { key: "CDk=" value: "\x0c\x91\xd1\xcd\xe1\xcf\xe7%ÃŦL\x98c\x89\xd0\x18rY\x0eA^\xaca+\x04\x85\xba$6\x02\xc4e" } leaves: { key: "CDo=" value: "\x90Tp~uW\xfc|\x85\xa3p\x19\xbd_\xf0/\x01\x0f\xb7\x0f\xcd9T\x81\xbbNZ\xa1/g\x1d\xec" } leaves: { key: "CDs=" value: "17\xdft]\x8c\xd7\x07\x9a\x8b\xe6\xa2X\x94?ÄļMa\xfd\xbfØĸo\x81\xfa%`ÆĒߛ" } leaves: { key: "CDw=" value: "\x1a\xa8uÚ Dl\xad\xeeJ\xba!\xa0\xc1g\xd6\x11~\x87\xc1\xf2'L\xa5\xba\xca\xc1r7\xafd\xbf" } leaves: { key: "CE0=" value: "}\x87\xf5\xcbŋ\x1at3\xd2\x1fg\"/p\xb5M\xb9-\xf9\xb8\xeaf\x86\x03\xb5Úž\x1fO\xcb\xd9" } leaves: { key: "CE4=" value: "ÖŦe-\x88C\x1aK\x8cŨ”\x89\xcd\xcf\xda\xd8`\xbd\x11\x8e\xfdq\\l \xc3\x01wn\x9e8\x08" } leaves: { key: "CE8=" value: "\xb3\xfa\x88 \xc0\x99}\x8er\x87\x80xÅ´\xa3\x02\x97\xeb\x1aĶŊ\xee\xd6\na\xe5ZL/M\x7f\x18" } leaves: { key: "CEA=" value: "\xc2Ū…K\xf6Ҍ\xf2\xd9-\xfc@S\x1d\xa0u\x85\xda^*\xd1#\xf1\xee%\xc8o\xe6Đ¯\xcd\xd8" } leaves: { key: "CEE=" value: "\\T\x90\xacV[[Y\x9f\xe4R#(\xd0y\xed\xe4\xb4\xf9\x1a\x19T\x07\x00\xb6\xfc\x96\x85\xf1\xc7i\xcd" } leaves: { key: "CEI=" value: "\xbb\nC\x95w\xf6d\x11\x95.\x85\x14\xffN\xf7Ķ BR\xcd**\xdc\x14\xe9\x8e\x1c\x87\xf9\x84\xbd\xfd" } leaves: { key: "CEM=" value: "pG\x99\xceN4m\xb5\x968\xb6\xe4\xaf\xfa\x93\xef\xb1]\x1b\xb2\xb9D\x97\x81\xcc\x1aB\x13\xdb\x1d\xc0\xf4" } leaves: { key: "CEQ=" value: "\xe4Mh\xbd6Z\xf2\x1cu\xa3W\xaa\x13\xf5{G\xfcM\xb3ÕĻ\xa2\xf7e\xd1H\xeda܎&\xb3" } leaves: { key: "CEU=" value: "ˌ\xc4}\x83\xc0\\B\x85\x13\x06\x8c\xb6\x99\x81\xadl\xf4\xc5\xd3:\x01ŌŽv(h\x8eW]\x80-" } leaves: { key: "CEY=" value: "jZ)\x1f\xe3D\x9eGm\xa7&i]=\xdbv\xeb~\x12\x86\x1cU\x10\xf3#c&\x8aQ\xe9\xbd\xcb" } leaves: { key: "CEc=" value: "\xf6L\xcc<\xafn\xb6\x1fH\x8eiT\x19~\xc9G\xae)$]\xe8z\xd4\x0b\x1cf\xdf\xd4e\xfcFG" } leaves: { key: "CEg=" value: "t)B\xb1\xb6?'v\xad%\xb5_\x8a\xea\xda8\xe8\xe26\xfc\xf3\x0fM@Q\x12\xe0Lkm" } leaves: { key: "CEo=" value: ")\x0f2\x10\xf6\x98\x10\xafso#\x01\xceDL\x8f\xfad535\x10KYw\xe3\x1d\x98r\xf7\x15\x02" } leaves: { key: "CEs=" value: "\x86jŅē\xea,g\xb0\xa6\xfbh\x02z\xcb\xe2\xd5\xc4\x7f\xab\xe0\xf6D\x8f\xa8\xb4펚¸w\x808" } leaves: { key: "CEw=" value: "\x14\xe4N6\xf2\x11\xead\xcaXĪĄ\x11\xfd\xcaS\xf3\x8d\x1c$?\x91\xb4T\xc7\xea}6T\xabH\x10" } leaves: { key: "CF0=" value: "\x1d\xca(\xfa+\xa5\xe6E\x1f\x87yL\"W\x96\x84\x9b\x06\xd7\xe1fk2\xbf3Ķ–K;q\x8e\xb9" } leaves: { key: "CF4=" value: "\xe2\x1f\xf6\xb5\xb5\xd5\xfa\xeaʑ9\x00\xb3\xa5Ķ‹Y\x8eCCL\x1c\xc5~=`i\xe9\x9et\xab\xbd" } leaves: { key: "CF8=" value: "\x00\xe1\t\xcf\\#\xd6'U\x1e\xaf\xa6\xed\x82y\xb6.JIGn\x83Cxy\xf6\x85\xa0\xe01\x91P" } leaves: { key: "CFA=" value: "\xe2\xea!\xe1;c\xa7\x971\xe6M\x90lbn\x7f\xba\xd288\xfe\xfd\x8fH}\xe5P\x89\xc8\xfe\x1c\xc9" } leaves: { key: "CFE=" value: "\xcf\x1fu4O\xd4\xee=:3\xbdڈ\xfc\x92\x10\xbc\xea̓\x9e\x8a'U\x0ck*\xe1d\xba\x07\xf0" } leaves: { key: "CFI=" value: "ZЊ\xd4qBE\xe4\xc1K\x1c@\xd7$\x02jg\x9d\x00\xbb|1%\x91\x9c\xeb\xc1s\xa8@a\x80" } leaves: { key: "CFM=" value: "\xb3\xb0\"\xcc\xe9\xe9\x08\xc3\x05\x1dX\xb0\x03O\xb7\xad\x06Ш\xfe\"ZiA\xf1Hzt\x7f?\n\xd0" } leaves: { key: "CFQ=" value: "\x80\x1f&\xe2>6\x91\xb9\xb7G\x1e*\xdbv\x12O\x93\xf2AbŞ\x895\xb1=,\x10@A$\xf2" } leaves: { key: "CFU=" value: "p\xa1\xe8\\gŅ‹\x1ac\x13\xb2\x88\x06\xb9-\xf16M\xea8\x9ekdn.v\xa8&\x98\x0f\x9fl" } leaves: { key: "CFY=" value: "\x81ĘŊ]\xed\xe9D\x80H\xfd%\xa8\xe6\\\x06\xb7\xfbn&\x1f\xab\xefĶŽ\xb7X\xf7\x8c\xc0\x87[\xa1" } leaves: { key: "CFc=" value: "\xc5r\xef\x02\xbb\xfc\x0c\n\xb7H\x82\xf7\xfc\x82r1Q1o\"B\xea\x047\x9d\x1bJw\x84\xfc\xf1\xa1" } leaves: { key: "CFg=" value: "\xe9\xce~\x11\xc1\x12őa\x9d`\x81\xb9\xe6\xe78\xfebwx\x85\x885Ux\x14\xb6\x92\xadL\x89\xec" } leaves: { key: "CFk=" value: "\xa3\x95\xa6\xce\x12\xe6sd\x0c\x07\xb7\xbb (\xb4\x16\x19\xed\xba\x8c\xa4O\xec\xbe" } leaves: { key: "CGM=" value: "tv'\x18<\x98\xaaX\xdd\xe5\x86J\x97c\xde!\xd9✷\xa6\x92\xe2d\xe1j\xfd\x99\x12!\xa2h" } leaves: { key: "CGQ=" value: "g\x9c>\x83)n)/$\xe1\xd6\xfb\x89\x8a\x02\xa6\nZ\xf5\xc8)ᛋ\xfb\t\xaa\xb6\xb5\xc4/\x8d" } leaves: { key: "CGU=" value: "\x8aSͨ\x81\x06/\xf8\xcc\x00\xb1R}\x15^\xe50\xe0\xcfG\x9e\xce\x1bx\xeb\x85\xce\nQ6\xad\xc0" } leaves: { key: "CGY=" value: "\x1dI9\xb06d\xb6ofc)\xac]\xb63p\xbc\x9a\xdd\xeaU\x97&\x0btS\xc87\xc0\x93\xd4W" } leaves: { key: "CGc=" value: "p9t\xcd\xf3Q\xea\xe6uM6(Se\xaf\x17[\xb2\xf9Ҍ\x05\x9e\x88XD\xfd\xbe\xcdg\xe6\xb0" } leaves: { key: "CGg=" value: "\xcf\xd4Z\xa7\xac\x92\xb9\x1eaK\xc5Q8\xa3:\x81\xa7\xf1l\xcb?\xa2\x10\x12\x1b\xbed\x93V\xd6@^" } leaves: { key: "CGk=" value: "V$\xf5\xbc\xcd\xf2\xd2\xfe8\xc7 \xf9w\x014\x80i?y\xe5d\xdc,D\xff;~L\xe4y\xc4\x01" } leaves: { key: "CGo=" value: "R\x94\x04\x19zPsK#čŦ‰\x99\xae\xc1!\xc3<@\x16g\xf1\xcb8\xb1M}\x1a\xb1s8\xed" } leaves: { key: "CGs=" value: "g\xe7\x17\x0c㴔A@\xd3\xfb\xbf\x10\xde\"\xc7\xf6\xe3r\x15oe\xf7\x82~\x83b\xb8=m~{" } leaves: { key: "CGw=" value: "\xe6\t\xbc\xb5\xed\xf4\x15%\x92\x9ctB\x8c\x03ytvsn0\x07\x94\x97s;y\x0c\xdf/\xc8\x1a\xc4" } leaves: { key: "CH0=" value: "g\xa0\x81δ\xc7JC3\x8b\xbd=h5\x91\x8b\x1d\x81\x82\xee\x0e\xcc[u\xbb9\xdam%\x88\x16\x0b&\x7fF\x1c>\xb8\xcd\xf3\xf9\xa4\xdd\x1e2\xe2\xc5V\x8aQ" } leaves: { key: "CHA=" value: "\xd7~+M+\x06Ø !U\t\x08\x02\xa7X\xb5\xac\xf1p\x978PnY[4.XB\xbfL\x96" } leaves: { key: "CHE=" value: "T\xb1\xa0\xae\xaeX\xb5^Q\xa2\x92\xa6\"\"\x85\x93\x9a\xaf\xba\xee+{\xb8\xd0F\xb0ŌĢ\xbc×Ŋ\x9c" } leaves: { key: "CHI=" value: "\xfbb\xc32\xdbG`1Q\x97Z\xe2\xa3\xe7\x1e\xa3\t߲\x81\xb0u\x9bq]{\xceI\xa8\x1f\xf6\xe4" } leaves: { key: "CHM=" value: "5Y\x00P\x02\x97\x0b\xf3\xb6\xc6\xc0\x80\xbc\xb7,\xfe\xf7\x04\xf4\xb4\x14S\xf6k\x9c\xe4H\x19؊\xef\xb1" } leaves: { key: "CHQ=" value: "5\x89\xa1\x7f\x17\xfd\xb7E\xe4\t\t\x9bg\xfb\xe7N[\xa2\x06,*\x89\xbb\xaf\x1c\xcaãŦ¨\xbf\x93s" } leaves: { key: "CHU=" value: "2`\xa4\xea\xe2rШ\xf2\xb4wN\x96\xe7\xce\x17\xfeP\x1b\x8d\xd8\x02'\xfa\x86\xff\x1e\x8a8\x84\x95\x90" } leaves: { key: "CHY=" value: "\xcc!\xd6\xe6ŗ\xe3~z\xd3\xce\x05w;y\xfd\xa7T}\x90\xe0e>e3\xe0.\xbe\xbb\xac\xf0\xc1" } leaves: { key: "CHc=" value: "\x14\xecl\xc1❉\xc0\x83V\x82\x06\x12<įŗŠ\xa2\x97#\xd9]y\x03\xcd\x18\x88\xd1\xf8\xb4\x01]" } leaves: { key: "CHg=" value: "\xc8W7_äǁ\xfb?\xc3@\x8f\xbc\xe0\x05\xeb\xade\xb5\xcfB\xc8N\xbfH\x05\xc1\xa5K)\x0e\xb5" } leaves: { key: "CHk=" value: "\xd1w\x00\x8e\xeb\xa1\xfeR\xe2\x17g\xff\x04\\\"b$\x07r\x97=(Fc\"5J\x9e(a\xc4z" } leaves: { key: "CHo=" value: "\xc4%yÕ˛\xc2\x1bAX\xd1\x07\xcfmj\x1a'P\xcb\x0f\x7f;\xdf\x1a\xfa\x0eb\xa8\xd1|\x08L|" } leaves: { key: "CHs=" value: "\\\x1d\x81\xd3\t\xd7\xff\xec\xc6!\x03\x93Y\xf0`\x95}\xe9\xfb\xe0\xe1Y;\"s\xbf\xd7\xe0\xee\xe1v\xd2" } leaves: { key: "CHw=" value: "\x9bQ.\xb0\xffꄸ$\u009d\xf5zh`\xc2ie5\r\x0e#@Rv\x10\xb5\xfc\xdfY\x07\x85" } leaves: { key: "CI0=" value: "\xa3C\x90\x19\xbc\x08?\x89\xb4\xb9\xf4\x9f??\xf3\xd4Đĩ\xdd-S\xcb\xc1(\x89\x99\xafŪ„\xe0\xe9Z" } leaves: { key: "CI4=" value: "\x10\xe6p\x1dR\x08\x19\x19*\xf5x\xbd\xb7\xab\xe0\xbfW\x92$\xe6\x8b\xfdn\x1b\x9dUTA\x86{\xbb\x92" } leaves: { key: "CI8=" value: "h\xceA#\x80=\x9b\xc9\x0e\xad.\xcfrq\xc8\xdeZ\xf7^K \x04V\xc6VtqÖ \x1bz\xb0" } leaves: { key: "CIA=" value: "\xeb2v\xff\xab)Z\x08\x93X\xe0\x91\x9b&om\xc7E\xb6\x05\x87\xb6\x13M\xe1\\\xef\xe5?\n\x00\xe4" } leaves: { key: "CIE=" value: " 035\x84\xf6\xb6\x8e\x0ekqj\xd3\x1c\xe2\xc7\x12\x87\xf3\x8e\x18ÎĄ@\xfb\x9e\xb4\x9bI 7\xab" } leaves: { key: "CII=" value: "\xe3Ãĩ\xc8[\xc9W\x80&Ѐ\x90\xd7v\xf9<\x91É \xe2\xfe\xa6H\xa1\xc8\xd1\xcc\xec\n\x8d\xd7{" } leaves: { key: "CIM=" value: "6,\x1dG\x9a=\x166)\x03t\x917\x01\xae\x90\xee\x19'\xfd\x88\xfd\x99:\xbc\xe6\xcfN\x07v\x99`" } leaves: { key: "CIQ=" value: "O\xee\xde\xf5\x93ß­\x9cwo\xd7:" } leaves: { key: "CJ0=" value: "#\xf4\x0bn=,J\x9f\x80\xc2)\x9b\xe1\x84Ώ\x86I\x8c-T,\xcf+\x12\xc1\x8c\x9f\xad\xb8EZ" } leaves: { key: "CJ4=" value: "\xf1\xcd\x15l=7\xd8\xf7\xc9sj?#j\x12#rÖĻ\xfe\xab\xf3\xed\x89\xe3\x01p\xfa\x16Ȟ>" } leaves: { key: "CJ8=" value: "\x9d\xa2\x85va\xf5\xfe\xcb\xfb1\x92܋Ev\r/\xd6\x026\xe1\x17D>Ęĩ\xdac\x94\xd1\xd04" } leaves: { key: "CJA=" value: "\xf9\x11\x12\x9d`\x12\"\x87\xf3\xeb\xf1\x8b\xcc`\xfcx\xf0æž 5\xde\x13\x9c\xeei\x1e7\x9a\x00@\xcd" } leaves: { key: "CJE=" value: "Jx\xc1D:\x84DI1\x8b_\xb0{\xe4c\x0cDʀ\x95\x8c\xa1ns\x025\xfe*\x0b\xcb\xfe\xcb" } leaves: { key: "CJI=" value: "\xce\x08\xfe\xfe=\xc3\t!7\xdc0{\xaa\xaa\xaeH\x9d\xad\xfc\xe9\xea\x0bpM\xa2\r\xf6\xa2\xfa\x84\xf4\xb4" } leaves: { key: "CJM=" value: "{Iw`Y\xe8\x8a\x12\xe3\x84g\xae//h\x8b\x82\x98BQ8\xe0\xe4\x90\x19\x16[\xb7t\xfc{\xa5" } leaves: { key: "CJQ=" value: "ʍ\x02\x9f\x80\x98H\x8e\x8a\x8f\xcds\xbc\x91\xf3\x07\xc3\x0f0]\x15\x06đŋĄĨ\xa5\xf9\x10\xf4m]" } leaves: { key: "CJU=" value: "\x1c\xb9\x15Öē\xc5\xe5\xdb\x02\x11<>\x0e\x0b\x14\x99\"\x8cqc\x02\xc7\"\xb2\xb5\x98K\xf2\xe4I4\xa2" } leaves: { key: "CJY=" value: "\xb2\x94\x0bGU9\x92\x04<\xe6dYw^V\x93K\x07\xeb\r\xca\x0em\x1e$X94\x8a-X\x18" } leaves: { key: "CJc=" value: "6\xfb\xcb\x07\xa7o~\xcf\n\xc8[\xbfb\x9b\x9fQ6R\x9b\xaf\xcax90\x8d\xf6\xc50)r)=" } leaves: { key: "CJg=" value: "\x8a)\xb6T9\xa0;:rf:l\x1a\xb8LH\xb6XCQt-\xa61\x0bl\xb6\x0f\x83O\x9eD" } leaves: { key: "CJk=" value: "\xbd\r9\x00\xd4\xd7V)~.\x03\xd91\x84O\xfb\xb8\x9dK?\x87p\x87\x07W\xe3'.\xe0Lg\xe5" } leaves: { key: "CJo=" value: "\xcae,\x9a\xfd\x10<\xff\x9c\xe9Z\x00\x133\xc3ÚĻ\x99E\x08-\xbf\x9cl\xad\xf7\xba\xbb\x0b(L\xad" } leaves: { key: "CJs=" value: "k\x9f\xf2\x16\x01D\xaey\x96\xa4\x12\xd35\xa7h4\x8a#mv7\x94\xc1r\xb2^\xff\xbe\x92:ؘ" } leaves: { key: "CJw=" value: "\x0eLC\xf3'\xe7\xab^\xbd|\xa5\x11\xa3\t9U\x80\x80dq5c\xcfe\xb1;؈\xb4\xd30l" } leaves: { key: "CK0=" value: "5Z\xa8[\x8e͙l~^$0\xa6\xa0E\xd9>\x8eؚ\xac3D\x03|\xbd) \x91\x19\xb2\xbb" } leaves: { key: "CK4=" value: "Y\x9d\xa5\xaaԟ\xfci\x11J\xa6\xd7\xe6DQ\xec\xdf\x0c\xac\xdf\xfa\xa8\x9f\xf8\x93\xd7\xfeA\xf2a\xaf\xe4" } leaves: { key: "CK8=" value: "\xaafÉĄo\xd0\xe1\xd4\xc5~\xa4\x81\xc6s\xca\x0cu]a\x1eU\xc5UÛ¤\xf4\x98\xffhz\xfd$" } leaves: { key: "CKA=" value: "\x82X\xa3\xb2>\x18mMY\xd1^\x81\x00\x8cC\xbf\xcaZ\x06\x17\xc3\xde\x7f\xb0a\xbb\x8aP\x07\xdb7\xcf" } leaves: { key: "CKE=" value: "\xe2Cp\xdf)\x04Z\xfb\xe42\xaa\xe66\xb3\xd6p\x82\x91\xe9k'\xe7\x11\xb2\x88\xf3\x1d\xf0\x92?\xba\x0f" } leaves: { key: "CKI=" value: "$\x1e,\x0bg\"\x13\xa5Đ­\x0f\xde֝\xb0\x07\x08V(q\xa2\xf2V\x91\xf6K\xd8BkŌš?" } leaves: { key: "CKM=" value: ":w\x9d\xa3`@\xa0Eb\x15\xf5\xa0$\xd3\xf5\xe1R`\xdfU\x95\xf2\xa2\x06%L\x12\xecw\xce\x196" } leaves: { key: "CKQ=" value: "\xd07\x13\xc3X\xbc\xd8p\x13\xd7\xd97\xbdQd5\x88\xb7\xc1\x08\xef)kZ\xb7\xa8\xca\x18\x9ai=\"" } leaves: { key: "CKU=" value: "\xf2ȁh\x89Y1ne\xe3M\xce\xc4U\xcfŲ­\xa1g\xd8H\x16\xd9\x18\xb1e\x8f\xbc\x81>EL" } leaves: { key: "CKY=" value: "\xac\xab\xb6\xbc\xa8X~v\x0e˧\x81>\xe1\x0e<\x9d\xdd\xe0߲\xf1vs\xc8\xeb\xbd|\xed\x10\x9a/" } leaves: { key: "CKc=" value: "\xda.\xa0\x1d\x07&\x01\x90ŪŊWR\x1a\xfbE\xf7\xd9s\n\x9f\x7f\xb4Ãĸ\x10\x97H\x86\xf2i\xf7y" } leaves: { key: "CKg=" value: "p\x1c\xf8\ri\nņŠēĒMl\xf6\x98*MhVGy\x1bn0\xa7\x85'\x81\xce!\x99d\x8d" } leaves: { key: "CKk=" value: "\xed=H\x99\x98I\x96\x95@\xe4^~\xd7B\xa0k\xc1^\x88:\x8d\xc8\nh\xcd{\xd7^\xced\xcc\xcc" } leaves: { key: "CKo=" value: "\xff\xdcĶž\xc5\xe5\xb7\xc8!\x84KH!\x83\x15'\x9fk\xb8\x84\x14\x9c\xe0\xdf\x02;\xeb\xd23\xa0y\x01" } leaves: { key: "CKs=" value: "\xdc\x07ER\"\x18\x84\xf0\x0e`b\x01VJ&ÎŊ\x88\xe20\x1btW%\x0c5\x9a@\x8c6\x0fL" } leaves: { key: "CKw=" value: "\xe5ØJ\x1a\xf2\xbe\x0fr\xeb\x1c\xa4F\xd3\xed\x8d\xf2\xbd\xf5\x9b\xe6\xe5\xcc@QU>\x95i\xd5R\xee\x16B\x8d3=\x1f" } leaves: { key: "CMk=" value: "֊čēŠ\x15\xd0\x01\xac\xa9\x99'\x82\xcf\x14\x17r;\x1d\xb3%\xd9`\x8d\xd1m\xffiŪŦ\xf8\x99" } leaves: { key: "CMo=" value: "\x0fmC?>u\x06\x94\xa3$\xac\xf2\xe3\x1fu\x05$\x01\xf2S5\xdf\xc0\xb9\x1d\xd8|]\x87W\xe2\xed" } leaves: { key: "CMs=" value: "\xef\xbfҏQ\x85ܸfpŲēA8\x06\xa6\x13\xa5Ųŋu\xa7,\x80\x97/\xcf\x0f\x07\xa0\xa5m" } leaves: { key: "CMw=" value: "\xc5={\xb5\xc9\\}\xf9\xa3A~c$\xa4h\x87\xe9}\x1e֚\xe0\xa5d\xef\xc1K\xb2\x9a\x85\x8cx" } leaves: { key: "CN0=" value: "u\x02\xbaÉģL \x9a9\xc2\x18\xd6\x1c0\xee\xafH\xc4%b\xbe\x08Į§ÛĻv\x07U\xc4i\x04" } leaves: { key: "CN4=" value: "\x10*K5\x07\xfa\xe6\x18\x1fF\xa96\xe3}\xfa\x9a\xe7\x015\x0c\xa7\xb1;\xb9^G\x90Ķŧ\x98\xfb\xe5" } leaves: { key: "CN8=" value: "m\xe8\xf7\x94\x7fM\xc9\xeeJ\xfb7\xc091s<\x8e\xd03-\xea\xc6\xceYU\x19\x9e#\xbdL\xa5\xf9" } leaves: { key: "CNA=" value: "`\xa0\xca+\xc9C]\xd74\x9f\x93P$\\m!A\xf7\xf7U*\x97\xc7\xddu\xd9Rt\x9bĮ¨\xf3" } leaves: { key: "CNE=" value: "}\x90\xaf\x878\x8a\x14T\ty\xc2\xf5\xca\x05\x86\xd5wPl\xbc\xa5\xaevt\xcf@\xf8Z~\xab\x8e\xaf" } leaves: { key: "CNI=" value: "\xa2\xca]|y\xaf\xb9\xbb\x9e\x07\xab\xa6\n\x08\x1c>0O\xb8x+\x06M\xf2\xe0\xfb\xf7\xdb%\x7f\x92\xfc" } leaves: { key: "CNM=" value: "/\xff\xe4<\"\x97\xfe*&\x04\xe8\xd5\\%|\x129l(\x8c\xec\x17ČĄ\x9b\xcbFO\xb0]\x16\xa5" } leaves: { key: "CNQ=" value: "\xb5\x97\x91\x995\x88\xe8o\xf21\x8d\x87\x9b&Ũ\xba=\x80A\xf3\xc0\"\xd9\r\xb1\x8b\x9eG);$" } leaves: { key: "CNU=" value: "?%3\xb37\x1cm\x8d\r+.\x92\xf7\x15\xcaG\xea\xa0\xd9\x02\xe4\xb7\xe8\xb7\xf60\xae\x85\xa6\x96\xc0\xd4" } leaves: { key: "CNY=" value: "j\x83=\x93\x95\xcaW\xe0&\xd8$\xd4V\x9e\x99\xf1{\x80\xae\xf3\xb0\x8e~\xd7\xc6\xd0\xf6\x88\x94\x9c`\x98" } leaves: { key: "CNc=" value: "gV \x11\xb5ⲨC`o\xd0X(\x17do\x0c\x8cŌ§c\xe0\xc3&=\x8d\"=\xa5\xc0\xc8" } leaves: { key: "CNg=" value: "\x86q\x10\xaa\x11~\x15\x10r\xd8\x1dZ'\xbf\xb8\xd5:A\x87\x86[\x1f\xf1\xdb\x10\xcf\x07\xae\x04> \x8a" } leaves: { key: "CNk=" value: "\xc9\xfb\xbc{\x16\xb4Z3\x1a\x99HN\xf8e\x90âŊĢ\xf4\x19\xbe\x9cR\xe2\x7fI\xbe\x1f_\xdeH^" } leaves: { key: "CNo=" value: "\xc2\x14\xed\x03\xe9\xf5(,\x83\x8a\xe3\xf6$4Q\xf7\xdc[\xe3\xc8K\xb7\xaf\xf2G\xd1Ķē\x15\xf9\x0bk" } leaves: { key: "CNs=" value: "\x80Q\xca\x7f\x8f=c\xa0\xbdhHÛŗ\x88fS\xc8\x069\x8f\xa9-\xd1ŲĻ\x8c\xd5\xdf\x16\xecB\xcb" } leaves: { key: "CNw=" value: "\xe4&\xe4\xf0)\x1d\xa0\x185|\xa6ZL\xe8\xbc\x18\xbav\xfd\xcd\xe4\x1d\xbb\x9e\xda_ĪŠW\xe4c\xf9" } leaves: { key: "CO0=" value: "Fb1`\xdb\x15\xe145\xd0\xfbb8U3`\xf5v\x88\x08\n'aV\xba<~\xa6\x0c\xe0\xd5\xec" } leaves: { key: "CO4=" value: "\xaaS\xb5\xbd|\x11\x0f\x18\xd7\xf2\xa7^\xaf\xc1\x97B\xa4>\xcbX\xac\x14\xc0\xf9|Z\xf2f3\x1e\xd3\xec" } leaves: { key: "CO8=" value: "\x0ej\x04\x0c\xe7mUb\"\x1d|\x82\x8a\xfa^\x08{*\xf3\xf6op\x03X\xdc\xee\xf2ßĸH\xe1A" } leaves: { key: "COA=" value: "2\xb7\xbf\xc8Öŗ\x90:\xa0\x05\x94\xbdl\xe8\xa4{Ro\x1baСB\x88\xa3f\x82\x98\x8b\xb3\xf2\x0e" } leaves: { key: "COE=" value: "?K\x97rۘ\xe5>u>^OYŲž\x1bB\x9ag}[\x1d\xa28\x98\xb9\xf1Z\xe1\x81R\x15" } leaves: { key: "COI=" value: "#\x88{\x8b.F?\x06=\xeb\xbcJ\xc8Ԇ\x9eQ\x18\x1f\rh\xa7\xe2\xef|\xa1\xa4\x03\xde_M\r" } leaves: { key: "COM=" value: "s2yU\xa4\xddy\xcfY\xbc\x9c[\x84\xabh\x81\x90\xc1o_\x08-\x92\xa4jÕŊB\xd0\xee{\xe8" } leaves: { key: "COQ=" value: "\xef58\x08\xc53\xba\xa9\xa0?\xf1V\x80w\x14\xb6\x15:\xb3j3Īē~\x1f\x8c\x1a})(+\xff" } leaves: { key: "COU=" value: "\x06x;\xcc3\"\x98DcB\xb8H\x04\xf5<\x84xP\xe3g\x07}\x8e/&\x19ˑ\x90\xbd\x15\xd1" } leaves: { key: "COY=" value: "\x89)X\xb24M\xc0:\x08\xb4\xe5\x0c\xc4ŲŖ\xa7\x15\xd8\x14\x161X,\xb0*.\x8cV%\xee\xb1\x1e" } leaves: { key: "COc=" value: "\xf9\x89\x05.v\xfa\xd2\xcd5\x0e6n3!\x9e6\x0eBy\xf3\xfd\xd1\xf6\x96 ߛ\x0e\xf5\xf4X\x9b" } leaves: { key: "COg=" value: "\x83\xbcu[\xfa\x88\x02\x88\x81\xed\x17\xb4S\xcfr\x91\xb9_\xc18\xadm\xda2Ņ§â‡‚a\x1b\xa2" } leaves: { key: "COk=" value: "\xe1*\x10\xfd\xf8\x18LČĸI\x10\xf0\xe8\x08\xbbl\xe5\x18\xb6#\x11\x97{\xb6K \xd2D\xfe\x94\xe0\x1cȊ\x90\xaf\xcc\xea\xa2\xeaZH \x9d\xe0" } leaves: { key: "COs=" value: "\xb2\nr\x0b\xa2R\xba\x8bpc\xe1\x16\xe5\x13\xb93\x90\x08\xeb\x07\x89\xa6'\xf3\x92N\xd82{́\x91" } leaves: { key: "COw=" value: "\x11\x93&\x10Z\xebaʡE\xb4O1\xa7\x14|\xe9\xb3`v7\x82\xa5e\xccOF\x9f^\xec((" } leaves: { key: "CP0=" value: "{Jh\xf6~\xc1L\xe2\xc6^0\x04\xf7P\xd5\x18\xea\x7f9\x11\x84\\_t\xe2\xe4\x18Fi\x99{R" } leaves: { key: "CP4=" value: "{\x18ŪŠ\x95M?\xf8\xd9\x7f\xfb\x84\xcb\xd4\xf90\xbf%>w\xb2\x1e\xd1x<,\x80\x9dU \xeb\xa7" } leaves: { key: "CP8=" value: "\n@s+\x0c\x9ex\xf5-$mb\xb4\x1d 2D\xb7\x03!0\xfe\xee\xbd\x1f\xc7\xca\xce\x1dbGZ" } leaves: { key: "CPA=" value: "\xd8,\xefO_\x1bi\x1f\xb0\xa5\xaazŨ§\x01Y[\xae\xc0f\x1d]N2\xb6\xf4\xba\xa3@\x00\xb3\xdc" } leaves: { key: "CPE=" value: "\xbc\x0cA\xf9]\xdc\x7f\xaa.P\x8c\xf21g1\x91턜\xd7\xc5\xe7\x8cg\xe5|\xea\\:\xeewu" } leaves: { key: "CPI=" value: "\x02-w\xf5N\xe4\xc0h\xabBC\xdcih\xa0\xcf\n\x1f\xa5\xb1Fv4\xa6s|gS\r\x13;\x96" } leaves: { key: "CPM=" value: "\xddX\x99\xda4_>\x7f \xaa\xe1=i\xb9\xc5\xc3\xf3\xd1\xed\xa2\xec\xf8ĮĒ7\xe9\x83g-\xc4\xf4\xce" } leaves: { key: "CPQ=" value: "\xb3\xbe\x10p\xe81\x84\xc7m\xe17\xa0\xdb9\xc9+\xfd\x07} \x1a\xccf*\xa6\x1e\xb6CG[10" } leaves: { key: "CPU=" value: "\xe8EB\x918\x12\x83!a`\xd2\xef\x90\xdco\xee\xde8/\x96\xa9\x15\"#\xb8Y\xa4\xa4u\xdf\xc8\xc8" } leaves: { key: "CPY=" value: "\xe6\xc4\r\xba\xbcho>Qa*\x8f{g\x9c\x9e\xa9\xfeZ\xc3\xd3\ra\xb3I\x0b\xeb&\xf5\xb0\x1f<" } leaves: { key: "CPc=" value: "\x9d\x9f+\x17d\x9b\xbdd\x8c\xca՞D\xff\xefA\x0c\xef\xd7y\x13lZ\xdbfW\xfb\x14\xd2\xcd܋" } leaves: { key: "CPg=" value: "\xbf\xa1xb\x1a\xd0Ō™z\x9aK\xd9\xf8d\x12\x809į\xe5I\xac\x9e\x0eX\x7f\xa1x]\x13ی" } leaves: { key: "CPk=" value: "v\x82%\xfb{\xf3\xf5\x07\xad!Q\x9a>\xf6#\x93\xa6P\x80\x1ak\xa4\x80\x16\x99 \xef#t\xa9\x9b\x15" } leaves: { key: "CPo=" value: "C]\xe8\xea\x7fe\xc4\xf4mЊ\x85\x00%#H\x91\x9c\xac\xc8KK\x1c\x0c\x875\xb4\xb7\x10\x01S\x16" } leaves: { key: "CPs=" value: "v\x8157\xf9\x9bHR\x1c\xc4\xdb8_\xc2\xf7\x04\x06c\x8dl\x8b\x1f\x06\x1e\x93\xa4\x8f\xfb\x86\x87\xea\xa8" } leaves: { key: "CPw=" value: "AŨēň\x88\xda\x1cĶŗØž\x15\x04Đ­&\xa0\xfe\x03\xae?^f\xb1\xeb\xd7I\x13\xc7,a" } internal_nodes: { key: "A+A=" value: "˜h?\xe3\xd9-.co`\x99\x87\x1f\xd4kĶē)\x14\xe0E\x9d\xebwZ\x15\xd8m\x80i\xd1" } internal_nodes: { key: "A0A=" value: "\xdc\xfeu\xf2 \xac\xefĮŽQ\xd4:`\x9aS(\xf5\x9eBa\xbat\xc1\xe3\xb0|\xc0l\xd6c\x154" } internal_nodes: { key: "A2A=" value: "|\xc8\x08+Į\x9d@> \xdc`\x88\xd2\xe7ÆĩY姏\xbd\xdd]\xcc\x12\xbb\xb4\x89\r\xf5\xae" } internal_nodes: { key: "A4A=" value: "\xae\x7fc\x07V\xf7y\xb8\x7fq\xf2\xe9,\xeb\xc8e\xcf?\tV\xeb\xf3\xcfn\xc3&;c\x9e\x9f\xfa\xfd" } internal_nodes: { key: "A6A=" value: "\xd8^\xb7\xbf\x8f\x81g\xbdR\xbd\x1a\xd6\xc8\xd5\xfe\x19r\xa2\x14\xd0\xfc~^Y\xcd\xceO\")\xdf`\xde" } internal_nodes: { key: "A8A=" value: "\xa2ŪŽ~K\x11a\xec\xc6<{\xcc\x0e\"\x9a\xc2\x0c\xf8z\xe3Ė´\x9f\x023\x8e}\xfb\xc4}M\x19" } internal_nodes: { key: "AQA=" value: "\x81\x18w\xf0 ~\xbb\xc1\x1c\xda×°\xde\xc0\x00<\xeb\xaal6\x0ch\x02T\xf7\xc0c\x06\xa6\x994D" } internal_nodes: { key: "AYA=" value: "\x1e\xb4\x80\xd5X\x9fA0\xf9jd\x8f{\x19\x12h\x7f\xdcS\xa0 \xfbÆŧ\x9d\x9e;\x94ą\xd7\xf2" } internal_nodes: { key: "AgA=" value: "\x15.v9\xd2m\x80\xae\xe9P\xf1\xac\xe6\xe6\xe1h\xa1K\x83ÃĒk\xce\xfe\xba7W\xceu\xa2k\x87" } internal_nodes: { key: "AkA=" value: "\xa5~\x92\n2\x18\xcd\xc8\xde#\x96UV%\xba\xd89D\x1b\x98\xd9\r\x19\xe9C\xee\xd1B\xbfX\xfe}" } internal_nodes: { key: "AoA=" value: "I\xf8\x82\xe7{\xf0I\xf9 \xfb\x0e\x9a\xd3\xcb\x17\x0c\x8c\x98\x03\xb2\xff\x8cu\x9f\xb3\xd5R\xe9Ɩ:\x7f" } internal_nodes: { key: "AsA=" value: "l\x10\xd5Js\xb7c\xc20V\x8cQW܍E\x17\xd9M\x17 \xeb]\x8c\xbb\xdf\xfb\x7f\x92M\x039" } internal_nodes: { key: "AwA=" value: "\xa5\xe0Ėx\xe2R\x0eMuÆŖ\x98\xf5\x16P\xaf\x88F$\xe3;\x9fZ\xb8\xa5kK)g\xbd\x86" } internal_nodes: { key: "AyA=" value: "#;\xd0\xd8\x1a\xfa\xee\xfa4R\xe0\x02\xfax#x\xb3\xa4\xbd\x0cwA@=\x0b\x0e\xae_\x91*Ę­" } internal_nodes: { key: "B+4=" value: "#\xf2\xf8\x981w\xab\x84W\xa4c\xbdې\x00\xb1É­|\xe7y\x98sm\xd4#w(\xadF\xc05" } internal_nodes: { key: "B+A=" value: "\xd1a(\x9bZ\xda\x13@\xdb\x0f\x84\x91\xa3\xbeww\xa0w\x19@\xd9\xd3.\xe9\xdf+$k8\x0084" } internal_nodes: { key: "B+I=" value: "Nc\xec\xe9=\xb2\xaa\x9c6\xb5\x1a吕I\x90\x07t\\\x19#߀\xf8\xf1\xc4viË´ÎŊ" } internal_nodes: { key: "B+Q=" value: "\"\xb2Q)5t\x9f\xdd{\xfe\xf2ÚŠí€ģ\xfb\xefJ\x84\x1d\xfa\x08\xebg\x1f\xce/\xfc$\x9b<" } internal_nodes: { key: "B+Y=" value: "`ˋ\xbf\xbc⹸T'\xa0gei'a\xa9\xd3\xda\xe1q\xee\x016\x97\xc8\xe7`A\xd5=\x86" } internal_nodes: { key: "B+g=" value: "\xd1.?\x97\x8d\x90\xc0OX\xacbs\x18\x00\x82\x1c\xd5b=\xe3\x85P\x12\xaf\x03\xa9\x04Ԁ\x9fJI" } internal_nodes: { key: "B+o=" value: "0g1\xbcS\x93ÆŽ\x9a\xad\xacZv\x90\x8e\x86Ęą\xd2Sx_\xf82\xa1N\x1c\xd9F(\x15m" } internal_nodes: { key: "B+w=" value: "\xc9\xde\xfb\xdfb\x15\xb5a\xe8\xb6Į‘\t%\xa4\xf7C\xa4w\xbfG4\x1f\x97O\xccl=a*}\x19" } internal_nodes: { key: "B/4=" value: "'\xaa\xcb\x0b3\x07\xc9\xf1\x807\xe0\xd7\xeb{\r \xd5\x18֋\x97OL\xe4\xf09=H7\x08\xeb\xf5" } internal_nodes: { key: "B/A=" value: "\xf4\x02\x1d\x892\x9a$t5#\x12\x1dīŗš\xe8DC\x84\xb1\xb1\xc5\x0fn\x10i\x98=u\xba\xca\x1a" } internal_nodes: { key: "B/I=" value: "Á\xf5\x80\xe4\xca\x06\xed\x1eO\xb9\x9e\xa0\x19\xbdq\xbf\x91\xc9\r!\xd3P\x93\xf0`\x9c\xaeMW}:" } internal_nodes: { key: "B/Q=" value: "\xa2U\x05@\xbb\x81\xa1\x02go\xd8_\x8b\xea\xc0\x03D\xf8C\xac\xb9\xfaV@X9\x9fF\xa5t\x0cA" } internal_nodes: { key: "B/Y=" value: "\xb5V\x16Y\x83\xfa\xf1^\xc25M\xe9\xcd\x11\\\x11C5ÜŦ\x14TB\x80\xa1\xd1\xfe\x14\x06\xedn\xfa" } internal_nodes: { key: "B/g=" value: "\x06\x92x\x9d\x89^\xb6\xa0\xf2]RR\x9dÆĢK\xbf_\xab2\xe6\xa3\xf3\x86\xf6C\x16\xc5M\xd4(=" } internal_nodes: { key: "B/o=" value: "q\xb0\x08\x12\xa0!c\xa7\x8e \x02/\xe4\xb5\\CR3\x11\xbd\x96\xf4\xa12\x0f\xa15r\"u\xf1\xa0" } internal_nodes: { key: "B/w=" value: ",\xdar\x02\x17\x96\xd7r\xa5\x01\x10Įļa\x16\xc7\xc1\xe1+\xc8(t\x1b\x90\xfd\xed'\xec\xc7\xcexZ" } internal_nodes: { key: "B04=" value: "\tv?\x06\xf1\x8e\xaa\xd4Ho\xe0\x04W\xb7\x86\xcfՓ\xdbnG\x90D\x0e?\xaa\x16\xa0\xd0\xd9C'" } internal_nodes: { key: "B0A=" value: "\xfb[\xa6\u009e\xd5\xd4\xc3a\x85E\x81\xc44\x9f,΃\xbd?\xe7\x1e\x83o\x06\xd5LXm\xa9Y\x12" } internal_nodes: { key: "B0I=" value: "\xe8D\x97;\xb0\x1b\x9cK\x0b\xfc\xf2\xd3\x144\x8b\x0b\x15\xaf\xa5\xedR\xa6ÛŦ\xd6K:\x8c\xd8#bN" } internal_nodes: { key: "B0Q=" value: "\xf6@\x9cQ\xbc\xbc\xe0\xff\x83@\x1e\x12\x9f\xaa)\x0f\x98\xc3\xec\x17\x0b\x8e\xf5&#K\xc3ڀ\xb3\xd9\x04" } internal_nodes: { key: "B0Y=" value: "I\xdc=G\xcd\xd5\x1a\x06\x19(\xae`\xb6k\xf3\x94D\t\xa2\xfe\x08\x97&Į¤=\xe2ʂx\xed\x1d" } internal_nodes: { key: "B0g=" value: "c\x1f\xff\xa6\x0e\x18\xe3\xf3x8\x91!\xef\x14\xc4u7Ų¤\xae\x88l\xe8\x0bp\xd8\xf5\x0e\xe6\"d\xfd" } internal_nodes: { key: "B0o=" value: "V\x83 \xd7~\x80\x1cK\x04nDe\x98=\xbe\x945V;\x96Č 2M\x94p=BG\"\xae\xa3" } internal_nodes: { key: "B0w=" value: "\x05\x06\x03\x8f_\x8f(\xb4UnbXX6\xf8\x9c\xe3\x81\xfe\x9f\xb2r\xd2D" } internal_nodes: { key: "B1g=" value: "\xf5\x94\xc1E\x1d\xc1ZJp~\x83p\xa6\xe1\x9ck!t\xb2\x88!}\x15\r\x12vu护\x12>" } internal_nodes: { key: "B1o=" value: "\x9c\x9a\x80\x1a\x9b\xa8R\x9d\x96\xe8\x91S\t\xd6|\x92\x120\x04\xad\xb1\xc6\x11e\xad\xaa:\x90I%\xb7\xe2" } internal_nodes: { key: "B1w=" value: "\xd2\x19\xf3\xf7;\xe1\xdc[\xf9\xa0Mk\xe5օj\xdd\xdbT\x1di}\x85r\x88%\x04O\xe0\xc6\xe1\xa8" } internal_nodes: { key: "B24=" value: "]/tm\xf0\xba\x00\x9d\xfb\xe5\xf1\x06aq?\xf0^PF\xcd,\xa4K\x14e\xd9n\x82\xe5" } internal_nodes: { key: "B2A=" value: "(y\xf5\xb7d\xc2\xce\x11\xc5\x0f+w\xa0╝\xda9^\xf8x\xb9\x1c\xb4\x08z\xb6\x0c\x08\xa5\xe5q" } internal_nodes: { key: "B2I=" value: "\x12\x08\xf5\x1cB\x8a\xf4\xf1]v\x08\xa2P\x9aw*`t\xc7;S\xbaڂpO\xbaYg\xd7\xe2\xca" } internal_nodes: { key: "B2Q=" value: "\xa2\x96Nzm9\xaf\xd4=u\x8c\xa6\x98\xe8\xd2\x1f\xaf\x00\x9f\xda\xe93\x95\xc2\x01\x00M\x9a2\xa6*\xfe" } internal_nodes: { key: "B2Y=" value: "\x9b_@]\xac\x17\xf5\xbc9[[Ų¤Ö¯\xa5\x04vx\x12\xb4k\x85\x90l/Ԝ\x95\xec\xf7\xa9" } internal_nodes: { key: "B2g=" value: "\xfbP\x03ߗʧ\xcd\x14\xf4A\xad\xaa\x9a(*p^\xfa!\xcbd\x0b\xa0\x1b^\x7f\nZ\x1d\xef\x87" } internal_nodes: { key: "B2o=" value: "$z\xc1\xca\xc0Ê\xff\xf1\t\x9f\x8c\xff\xfc\x83\x83Z\x9a\xc6\xf3z\\\xf1Ū\tm!\xecu\x9d\xb3" } internal_nodes: { key: "B2w=" value: "WN\x9a\x0c\xa5/i\xe3\xa1)\x10\xe6*\xe9\xd3U\t4VD\x03\x95\xb1\xdbo?Y\xf4V\xf3=\xbf" } internal_nodes: { key: "B34=" value: "\xcay\xbeƅ\xe3\n\xb7\x87A\xbc\xc4\xddˍ\xc1\xb1\xb7\x19k\xba\u0082\xc2|k&\xe7\x11Xbg" } internal_nodes: { key: "B3A=" value: "w$@\xa9|p\x884\xc5\xe3\xaf'y\xa7\x86\xfe\xdeo\xe4\xeb\xdc\xd2\x02\xd1\xe3\x8f,\xa7\x95\xceځ" } internal_nodes: { key: "B3I=" value: "\x953\xc2\xc3\xe6\xa9V\x0ci\xc8N\xeb\xfe\xe7;\xe3vfv\x88e\x7fƌM\xa5#G\xdd-J\xe0" } internal_nodes: { key: "B3Q=" value: "I\xabJ3\xf9\x92_\x16\xc8f\x15\xc1=\r\xaa\x08~\xa4\xb2\xf8O\x93\x84u\xc4F\xd7\x19\xb2\xf8[`" } internal_nodes: { key: "B3Y=" value: "Ņ\xf5\xca\xff\x95\xe4\xb44\xd8\x0c\x94\xfdF\xdd\x02\x83\x878\xe8\xee9T\xed,\xe4\x1d\xc8!\x8b\xa0\xbf" } internal_nodes: { key: "B3g=" value: "ĐĨ\x1b\xfb\xf5\xab\xfdc\xd8\xd4\x04Ai\x05x\xd2O\x00\xea2\x95\xf2}\xf4\naa\x9bS\xab\xd0\xe9" } internal_nodes: { key: "B3o=" value: "}\xf9\xc49\xf6\xf9x_\xb2\xfb\x17\x08QBQuY\x8f\xb3\t\x16\xe0A\xa6U\xef/\xba{\xc3\x1b\x89" } internal_nodes: { key: "B3w=" value: "\x8e6j-@\xce\xdcH\xf0dR\xce\x00\x90s\xdd\xfa\xfc\x04\x9c\x9eA])S\rSHut>e" } internal_nodes: { key: "B44=" value: "\x12\xb4\xa5\xc4(q\x17:\xdef\xc4\x00z+F5\u008b\xcb\xe3\x0cj\xdf6E\x04\xcf\xfb +80" } internal_nodes: { key: "B4A=" value: "^\xe4~\xb3\xd0*\x0f\x9bBn6\x1b&\xfb>\x80\xc5aX\xaep\xa8/\x9a$\x92{\xec\xd6cmD" } internal_nodes: { key: "B4I=" value: "\xe7\x03N\xedx?!\x18E\x8a\x1e\xa3\xcf\\?\xca\xe5\xb9\xdcL\x1f;Yt\x1f\x92\x9e\xc2;z\xe2q" } internal_nodes: { key: "B4Q=" value: "\xbc\x8e\x98\xbag\x12\x1b\xa2\xa3G\x05\xbf\xa8\xd5\xfb\xd0A=\x02\xab-'B*\x87a4\x0euM\xd1\x17" } internal_nodes: { key: "B4Y=" value: "x\x13\xfa\x1bG|\xd24-\x00\xac\xc9\x14u\xfe|r\xe6c\xab\xddfY1\xd4\x18\xa3\x1495\xc7\xd9" } internal_nodes: { key: "B4g=" value: "\xa3\xa666\xf2\xaf\xcdf\x1d\xef>X\x91\xb2C\x0c\xc9+\xd8\xd3]\x98\xfc\x1c)\xe3\xf2\xf12J+\xc3" } internal_nodes: { key: "B4o=" value: "\xb5\x811\xb1~\xaf\x87\r\xc9\x19\x81\xb2\xcc\xdbMo\x1a\xf7\xe0\x93\xaf\x88\x8f\x90\x84M(\x9f\xeeg\x19\xe4" } internal_nodes: { key: "B4w=" value: "o\x88h\xff\x86\xbe\xfah\xa8\x8a\xf2\x15\xd3(\nP]\xb3J\xd2g\xcb|\x1f\x8f\xb6\xff\xd8\x17" } internal_nodes: { key: "B5o=" value: "\x0b\xae\xcd\xe2\"\xff\x9b\x08\x87\x9b;<;\x90\x1bc\xd1\xe9\xeb+_\xb10ld\xcdb\xffb\x89#Z" } internal_nodes: { key: "B5w=" value: "3@y4\x1b\x9c6H\x02Z\x92\xee\x0fFt\xedan\x7f\x0e\x06Ũ„\x97\xba/U\xfb\x8b.\xec\xdd" } internal_nodes: { key: "B64=" value: "ȧ\xed\x96\x11\x7fbB?ua\xc0]\xb5\x08\xa1#@ã˛\x07hc\xdep\x91Ęš'>\xfe" } internal_nodes: { key: "B6A=" value: "\x8b`\x97\xf9E\x15\xa1S;\xa8\r(O\xf9Ec!\xebJ0O48\xce\x05\xdf9#߇*6" } internal_nodes: { key: "B6I=" value: "QЃ\x07\x8e3\xef\x88C\xa3\x88\xfe\xe6Rt\u008f\xf9n\xa88\x18\x968u\xfd\x00p~\xa6\x0cA" } internal_nodes: { key: "B6Q=" value: "\xc1w4{\xd9=A\x15\xeb\xb0g\x0b\xb8y\\\x94P\x99\x19\x8a\x1ba\xf9?\x83\x82_\xec\x06<\x91\x98" } internal_nodes: { key: "B6Y=" value: "EŪ”\x1c\xfeMt\x88\xbao2\xbb\xc6t\xe81\xdd\xd5t\xe123N\xe0E$\xf2X\x83E\x9ap" } internal_nodes: { key: "B6g=" value: "\x16z#-,qS\xdecB\x7f\xe0\xfcɈĖĒ\xb3\x81\x95\x9eN\xb1\xc8\xdd\xec\x1a+\xa6Xe\xd2" } internal_nodes: { key: "B6o=" value: "@\xb28\x98\x8bx\xbef\xaa\xa9\xa1&Į…\xfc\xe8^\x84h#:b^\r\xa4\x8d\xc4q\xcfl\x9eJ" } internal_nodes: { key: "B6w=" value: "\x91\x13c\x80\xbfՎ\xe5\xb0\xecrs\xec\x0cL\x9e\xc3s(K\x9d\x81W\x16y7(s\xdb\xea\x07\n" } internal_nodes: { key: "B74=" value: "\x0e\xa1\xd0_\x0b\xf5\xa7r`\xff1H\x0e\x0b\xb3okM\xfb\x9d\x8d\x05\xa2\xf4\xf5\x10\x03^\xa4\xb2D\x97" } internal_nodes: { key: "B7A=" value: "\x01į°°\xd5\xffz)\xc7JqMŅĩ`q\xb2\xbc\xb6\xe8xB\x80\xe6\xaf/C\xb5\x0f\xc4\x13V" } internal_nodes: { key: "B7I=" value: "\x96\x95\x8fC\xc1\xe5\xd1\xfe\xc4T1,H\x84(\x82\xe9\x03\xda2\xdf\xd5?A\x8d\x92ĮžĪ–S\xb9" } internal_nodes: { key: "B7Q=" value: "4\xc7\x19\xa4\x10\xa5\x85\x85˕\xba\xddШ\x95\x97\xe7PI*\x99)A\xd1NWt\x92\x00\xa9,G" } internal_nodes: { key: "B7Y=" value: "\xa4\xa8\xdf\xd4Ų›c퟊-\xb9i\x0e\xaePŨĩ^\xfb\xa0Ķ’\xf3xxR\x02^7Wc" } internal_nodes: { key: "B7g=" value: "\xe04\x84\xb1\x9b\xfa\xc4\x16A\xd69?a\x0c\xde<>\xf0\x84m\x12'\xfdz\xcc7\xa58\x13\x96_\xbd" } internal_nodes: { key: "B7o=" value: "\x84\xcc\xf1\xc7\x01\xcd0\xfd4\xa0\xe9\x02N>n\x07\xe8\xac8\x8b%\xdd\xceR\x8b#S\x92\x87\rJx" } internal_nodes: { key: "B7w=" value: "\xf7\x8f\xae\x1a\xccfP)\xcf(^\x0f\x86-Qؙ8\xf3\x1b\xb4\xc6#f`Åģ4i\x8a\xe8\x14" } internal_nodes: { key: "B84=" value: "\xe8\x92\x1b\x9dn\x8c\xe5\x18&\x16\xc3ueĝ\xe7%\xa1N\x17\x1co\x97]k6\x8b\xbd\xcc8hB" } internal_nodes: { key: "B8A=" value: "\x9e_gßž\xber\x9aq\xccV\x1cm\x19\xbd\xd7ߍ\xf4\x8f$!U\x14\x18\xe1\x1dm\xad\xef\xee\xdc" } internal_nodes: { key: "B8I=" value: "\xaeF\xe7\x1e\xf5cS\x93\xc9\x00\xafԛˍ\xc5\xff*\x18\x96\xd3\x16\x16~\x98\x17\xbd\x08:\xd5<\xe3" } internal_nodes: { key: "B8Q=" value: "\xc4Ų–N\x1c\x16B*\xf2\"\xb5\xcd\x1cr\x1bC]\xba\xef\xf4\x92SN\xcd\xcd\xd94UŲĸ\xb2\xfb" } internal_nodes: { key: "B8Y=" value: "4\xa0o\xac\x932\x00\xf0J\xccr\x9dÂĩ\x9e'\xbaatA\xa3\x8f\xd3\x1cw\x12[\x81\xef+\xc88" } internal_nodes: { key: "B8g=" value: "\xe31!}\xa7H9\x1c\x0f\x85\x04<\x88'\xfd\xca\x18EŌŠ\xb6\xf3\xc9\xe76\xad=\xe9\x94\xc0b\xc5" } internal_nodes: { key: "B8o=" value: "Bgx\x9e\xe1}\x8b\xab\xa1x!\xd4\xc6\xe2\xb6Vį‚ž\xfb\x94\x90l\x02\x82\xc6\xd5G-N\xaa\xca" } internal_nodes: { key: "B8w=" value: "\xd5WRb\xb0\xea\x85\xd3\x1cFd\xf6\xb3\x13\x88\xc7o\xdc\x1eg\xc9ÍĢ\x13\xf6F\xe6\u009b-\xaa^" } internal_nodes: { key: "B94=" value: "\x82q\x88\xc2[\xcb\x1d\x91.\x1a\xbe\xab:\xa7S\x10$\x7f\xb9\x0fCo\x10\x811Ûˇ\xf0D\x0b\xaf\x81" } internal_nodes: { key: "B9A=" value: "J\xbbF\xb65\xca\x04=AeŨĸT!!\x9e\xfd\x13Y&o\xac\x81\xcc0\x96`z\xd0\xccY\xb5" } internal_nodes: { key: "B9I=" value: "9\xa8\xe7\xb1\xee\xa1\x02\xb1O\x00\xe6\xe8\xf2#\xe1O\x15U\x0c\x89\xf5$(3\xda/\x97\xc0pK\x0b\x97" } internal_nodes: { key: "B9Q=" value: "\xf9\x91\x15\xb5\xdeM\xe2xE\xd69\xc8\t\xcd\x02\x8f[\xe5\"\xd7r\xde\xc6CB#\xe1\xfb`\xc5\xfc<" } internal_nodes: { key: "B9Y=" value: "A\x8a\x02\xd0l\x1e\x98\x1b\xe1\xb8\xfa\xc5pc\x83\x12zk8E_\xff\x01\x0fM\xc8w?\x9f\xeb\xe1s" } internal_nodes: { key: "B9g=" value: "\xf47\xcc`\xeb:\xf69\x9bD\xcb\xf8zOe\xba\xab\x8c\x05Į„|2\x10ˍ\xb3^\n\x9b\x7f1" } internal_nodes: { key: "B9o=" value: "\xdf%\\\xf5\x1et\xb9\xee*\tr\xc3Į\x93\x15\xd8ëŊ§a\xa2pl\xeb`OUz\xfcB\x98" } internal_nodes: { key: "B9w=" value: "\x12\x02\xd3ÂĢq杰P\x8c\x16\xae-a\t\r\xb7\xc2@\x1aLj\xe8&:3h\xf8\x80\x99\xc1" } internal_nodes: { key: "BAA=" value: "\xfd\xe2\xf1\x98\xc3i8\xa2a\x07/X4I\x04w\xb0\x8coVH~\xea\xbdX\x96\x9a\x9e-/f\xb2" } internal_nodes: { key: "BBA=" value: "\xf7A_\xfd\xbe\x98S\x1c\x88\x97\xad\xf8foe#\xdf\x13\xec\xb6\xf7RŌ‡\x85\xdf\x00\xe4\x00\xe1\x1c\x9f" } internal_nodes: { key: "BCA=" value: "\xd5ŌĒ\x89\xb9\xecm\x10[\xb7\x99-ł\xc1\x11#Ō \x9a\xcb?L\xb4\xf9\x82\x06Z\x90\xd1*\x0b" } internal_nodes: { key: "BDA=" value: "\xd6D\xfa\xb5\xe5\xfb\xe8\x196\xb9\x08C\xd0\x03:9E\xc3v?\xefp\xcdQ\x83\x1fcW\r\x96\xe1d" } internal_nodes: { key: "BEA=" value: "\xcex\"\x00\x0eÚ\x8b6%\xcd\x0f8\x01C\x8a\xe4c\xccB'X\xe5$f\xf1@\xe61\xbdy;" } internal_nodes: { key: "BFA=" value: "d,1[˜I\xbfbh\xa2\xe1?\xf8\x19g\xc0\x90\x7f\xd5ËŊ`D\x81\xe5rh\\\x1fp\x1e" } internal_nodes: { key: "BGA=" value: "\xa1\x96\x10$\x93)\xb6\xc4J\x95t\x98Y\xcc\n4\xea\x9d]L\xfb\xb0\xd8ˈ\xec`\"Q\xd76r" } internal_nodes: { key: "BHA=" value: "\xf43\xf2\xa1`'\xc8?y\x80ĶŠX\xa8Q<\xe1\xe8~\x019FFc33\xcdN,\xf2\x7f\xd2" } internal_nodes: { key: "BIA=" value: "\xbb\x1e\x91\x86WTCYA\x04\x1b\xc8\xd11k\x96u\xa1Y-\xbd\x81\xf7?I\x05\xc41\xadz\x86a" } internal_nodes: { key: "BJA=" value: "?\x12\xd1j.\x0e\xde\xd7\x06]Xf\xc2\xe1\xc2\xc6\xce{Ũģ<\xca&\xcaҧ\x92\xeb\x06u+o" } internal_nodes: { key: "BKA=" value: "\xf1\xa3\x8cd@\x96\x8a\xdf\xf2\x95a1}\x8c \x05r\x18i\x97ȏ\xcc\x05\xca\xfb\x12\xa8/\x0e\xb8T" } internal_nodes: { key: "BLA=" value: "\x0e\xf0\x95|E_\x1f\xc2F\xae\xaf\xfa\xc3\xdd\xf9\xba\x90\xb8\x9fH@\x10ov\x92\xd8M\\H\x18\xcc\x13" } internal_nodes: { key: "BMA=" value: "\x9b\x1a\xbd\xdc\xc8K\x02\xa2T\x98\x8cÂŋ\xfb\xf00\xc8,8\x01\xf9+\x065L\x162&\x04\xc7\xc5e" } internal_nodes: { key: "BNA=" value: "~Ųļ\x89a\x8c~\x8b]\xbb\x1e8\xd6\xf1\xa0\xcbR\x7f@\x0f\x14\xd5n\x90\xa2\x13\xc72kw0\x07" } internal_nodes: { key: "BOA=" value: "\xd4v\xbf\xc2\xe8[Nl\xcau\x15x\xf6+\x9e\x16\xf2\xbaV\xc7\xf26\x13\xe2\xa9\x11\x86\x93Û´\xc2\x1b" } internal_nodes: { key: "BPA=" value: ":\xe4\xf2\xc8\x0c~VU\x0c\x19q\xa7fH\xc2~\x8a\xbb\xa0\x92\xfe\x88\xa1|DU7\x99\xb0\x87\xc06" } internal_nodes: { key: "BQA=" value: "\xcf%^Ք\xb4\xab%\xa0\xffx\x1fw\x12\x83\x7fO\x9e]\x10sU\xa9\x17,\x048qI\x87\xf2O" } internal_nodes: { key: "BQg=" value: "\x81\xeb@coPS7\x96\x1d{\xdc[\xf5\xb7\xfc\xcb\xf8\xc2*\x96\xee\x13\x10+ã̈\xd4*4=" } internal_nodes: { key: "BRA=" value: "؝\xbb%\x82\xaf\xdc8ȁ\x13\x88H\xe2\x19\x96\xdc_\x14-\x16\xc2\xc9\xd2\xfc\x81\rŨĻ\xc9]\x10" } internal_nodes: { key: "BRg=" value: "\x9buW\x82\xb6\xf1OA\x81\xd9\xfe\x16\x9e\xcfhcSˏA}X\xa0\x1d$\x89\x8f\xad`a\xaf+" } internal_nodes: { key: "BSA=" value: "\x83\x1f\x9e\xaa,\xa9\x0fX\x10\xfd.\xff\xd7\xc9Q\x9d\xc1\xc9\x19A*\xc6T\x05^\x97-u\x07w\x8c\xbc" } internal_nodes: { key: "BSg=" value: "\xad\x81%îŦž\xc83\xd9.K\xa2\xae\xd6nc\xb9\x08p\xfd!\xb6\x9cY\x88}cBFRҝ" } internal_nodes: { key: "BTA=" value: "B\x93\xa2П\xc1[\xef\xfb\xf7\x00\x01Ũ\xfc\x11\x9d\xa4\xd3n\x85_S\x02\xf9\xddɆ\xecKI\"" } internal_nodes: { key: "BTg=" value: "\x92\x9b\xf1\xa7S\x93\xae\x13R\xc3EK\x12\x93\xef\x9a\x1b\xc6\x0f+=\xc0tJ\x1c\x03P\xec\xc5\x06\x15\xd9" } internal_nodes: { key: "BUA=" value: "\x81tÅŗ\xda\xf4\x14\x11\x1ds\x88\xeb\x08QM\xe6\x16\xb3\xf2\x9d\x9f\x1c\xca\x1a\xe5\xc0.\x8ae\x83\xc3\x08" } internal_nodes: { key: "BUg=" value: "\xdej-X\xe1\x8c\xdbo\xecQ\x8b\x98\xb9\x06XćR\xfb\x7f\x08\xc0\x0b\xd0\x19V\x1d\xe5X\x82\x84\xc8" } internal_nodes: { key: "BVA=" value: "\xf2ˏ\xc7\xe8\xe4 \xfd\x1eb\x97B\xd5u\xcb]\x04\xc8_\x949\x01&GX(\xce\xe7\xde\x1fn\xc2" } internal_nodes: { key: "BVg=" value: "=\x9f\xd2W\xaa\xc6ce\xa7\x98c=OBe\x1f\xfb\xdc?:/\xdbq\xb5\xd3ֈ\xfb\xa8\x8f\x9a\xe4" } internal_nodes: { key: "BWA=" value: "kڗ@9j\x88\x80\xb9\xe6\xd2.\x07\xf7(F\xec\x01{\xc0\xd7~V\xb2--4\x8b\xc7\x18J\x07" } internal_nodes: { key: "BWg=" value: "\tĖģC\xecW#\xaa\xd0]QRa,(ĝQ\x18H\x15\xfd\x06\xfcC&#\xec.X^9" } internal_nodes: { key: "BXA=" value: "\x87\xe8?\x03\xb8\xecX\t\n\x1f\xc6~\xa0\x04\xdf[\xc34^b\x8f9\xc8]\x90B\x86\x9d\x1dE\x88\xe0" } internal_nodes: { key: "BXg=" value: "r\xb3\x99_Ĩ\x8b\x0fz\xb4\xc33\xf5\u0085s\x99\x03\xa3\xfe?\x02\x02\xf2\x9f\xc8e\xb1J\xa7[\xab" } internal_nodes: { key: "BYA=" value: "\x14\xfd9\x03<5\xf5\x84\xf3'q\xce\x1a4\xad:B\xb2\x881j\x0f]\xf3\xac\xe8\xb7\xe1\xb60\x7fW" } internal_nodes: { key: "BYg=" value: "Q\x13Ί\xa6\x92\xcbG\x1dB\x13@\xb4qM\xf9\xc4\r\rKELė\x8e\xbc\xbb\xeb\x1c\x02\xa3" } internal_nodes: { key: "BZA=" value: "\x05/k\xa0\xa7\xa8qD(\x08\x17$\xb7\xff\xfcX\xea~\xa1\xd5ί\x81\xb1!\x80\n\xbfp\x06\x12\x90" } internal_nodes: { key: "Blw=" value: "~\xc2d!\x075,4\xad\xb7\x1a\xeb\x18\x9f\xb7n\xabQ\xfd\x01R\xb0:h\xaa\x0b\xd7*\xb3\x94X\x8c" } internal_nodes: { key: "BmA=" value: "\xb5E\x91_\xe8\xee\x17S\x18\xeb%\xfc\xf0\x8b\xe3\x9b[Z\x9f)\xac\xec\xed\x835\x94\x8fgQ\xcb\xe6\xe8" } internal_nodes: { key: "BmQ=" value: "B\xec\xd5\xe3|\xc8\xf9\x86\xa2\xb2Su\xedВ\xe6w5\x16k\xb27p\x8e\xf5Nk\x89\t@Ëŧ" } internal_nodes: { key: "Bmg=" value: "\x96\xabN\xd8~\x81cQ\x0fW?\x15Äēé„Ģ\xef\x05n\x13\xda\xe80\xbd#\x85\xfd~\x0b\x1c\xbd" } internal_nodes: { key: "Bmw=" value: "\x97\xd3\xf9G\xbbM4\x84\xb4\x87\xf5\xcd\x14\x19Y\xf7\r,\x80Ā\xfc\xbajG\xa2%\xe2\x01\xca}$" } internal_nodes: { key: "BnA=" value: ".ŌēĮ´\xee\xd7rT\xb3\xca6\xa4\xbd]&<\x81 \x9a\x94\xe6\x16\xb9\x02\x16>\xe3<2\xa1\xdb" } internal_nodes: { key: "BnQ=" value: "SF\xfde\xb0\xcc\xc7C\xc0\x1e\x84\x88\xf4\xb8?u3\x9e\xb1\x14\x15#\x96`>\xd0\xf6ʒ\tB1" } internal_nodes: { key: "Bng=" value: "_\xf9{\xe1\x17\x93\xfa#\xe6m\xf2l\x98Īŗ8`\xf5e;\xf2\xf1hB\xba\x05\x7f\x19Ɵ\x17\x1b" } internal_nodes: { key: "Bnw=" value: "\xa3\xfe\\\xbf\x15Q\x19\x14\x0c) \xce\xd6\x14Îĸ\xad\x9e\xb48\x9f\xa0\xe8\x16\x9e\x92S\x0b\xe6M\x10u" } internal_nodes: { key: "BoA=" value: "\x1bl\x1e\xc2\xf6\x15\xf7\xecCL\xcaȉG\n4W\xc7\x17N\x16\xacm\x91\xb8\xf6\x101\xde|\xb5\xe7" } internal_nodes: { key: "BoQ=" value: "|\x81Y\xd8'Q<\xddI\xc5\x018^\xc27@s\xad\xfa\x06\xcbP\xf4\x9d\xb4\x9fͧ\xaa\xb1L\x83" } internal_nodes: { key: "Bog=" value: "\x90\x11,\x81\xf6\xc9A\xbc]\x9fj\xfa\x08\x06\xc9U\xb2p\x82H\x06.\x13\x82C\x89\xdc\xe1)`\xfbz" } internal_nodes: { key: "Bow=" value: "\xc6\\\xc6(\xe5\x07Y\xb4:\x90!\x0b\n;\xe4\x05\x8a\xa2\xb2\x80\x19\x17\xdb\x14l\xa1\t\xdan=iN" } internal_nodes: { key: "BpA=" value: "\xa3\xfa\xa5f\xed\xfeF\xebØĨ\x069\x10\x0b\x10L9D\xe0\xe2\xe5\x1d\x86[,\x08\x84eˁ\xaa\x13" } internal_nodes: { key: "BpQ=" value: "Z\xc3W/Ns\xa7\x87\x88%:\xed\xdb\xcc\x08\xe6><\xea\x04\x1d\x9f\x1c\n_\xb6\xaf\xa3~\xce\xd6\xd3" } internal_nodes: { key: "Bpg=" value: "!\x1a\xf14\x1cj:\xbb\xd4Ũ­\xec\xe3\xd4\xe0\x8e\xe4'p\xe4\xe5T}\xef\x9fhRɇ\xf7\xbd\x11" } internal_nodes: { key: "Bpw=" value: "\xe9\xc1|\xef\xfa\xfbTy)\xa9\xa8\xf1\xdc\xdef\xab\xdc\xf4\xb5qm\x1fu\x98u\x8a\x84\xf9\xd9R\x14\xa1" } internal_nodes: { key: "BqA=" value: "\x82S6\xdb\xdaČļ$M\xbd\xbaxG\x80v\xf4\\\x13Tn\x00r%!\x19\x8b5\xea\x8b'\xb14" } internal_nodes: { key: "BqQ=" value: "Äž\xfaQfR\xf4\xd5\xd3.\x11\x03\xc7\x15`\xcd\xe0OH\xfebԄ\xcd\xddS|\xc48\"\xe26" } internal_nodes: { key: "Bqg=" value: "z\x08\x1bb/k\x9f\x8b[\xcctpI\xb2\xcd)܅8D\xfd>|ɒUB\xccS\xac5\xef" } internal_nodes: { key: "Bqw=" value: "\x07\xce\xeb\xd2\xd4Zk\xbb!\xd5<\xf9\xfe\xf5XR\xe8\x8c\x1c\xfeG\xb0\xbe\xd7\x07\x82\x1e+\x10\xb6\xb4\x85" } internal_nodes: { key: "BrA=" value: ")\x81\x05\xf2\xe4\xc7)\x8dW\x0b76Z|\xee\x11e^\xed\x83\xe1h\xe2!\xd3R}ÅŽ\xd5B\x1c" } internal_nodes: { key: "BrQ=" value: "yHt\xce)\x8f \x88;\x1aG\xd0_\xe5\x07\x85I\xc3\xc2\x02\x81z\xfdZ\x82(\x91\x01\x87(\x04+7h\x8d\x07\x9f\n\x1f" } internal_nodes: { key: "BvQ=" value: "\xb2\xe5ט\x0e\"A\xe5\x1e\x91nG3\u0090\xb2\xf4A!" } internal_nodes: { key: "BwA=" value: "J\x0c\x8f1\xc4\xdf\xc7\xf9\xca\xd0\x04t\xa3\x10\x9e^\x93.ted!\xf7\xe0X\x1a\x99d\x1a\x9f\n\xfa" } internal_nodes: { key: "BwI=" value: "\xf7\xe0\xc5\x0b\xc6}0L\xec\xb0\xebÃēw\xc4P\xcaf\x86\xbc\x9b=\nA}\xa7\xf9\xbc\xeb\x7fp:" } internal_nodes: { key: "BwQ=" value: "\xcf\xe6\x1cV\xe3i$;>\xd3\x13w$y_\xb6\x91\xdefFb\xd3+=V\xc0\xdf\xeb\xbd\x07\xfe\xbb" } internal_nodes: { key: "BwY=" value: "\x8a\xc1y\x19\xabb\xe0\xe3.\x05\xef\xb9|\xab\xb9O\xa8oG}\x80\xf2t\x0b\x88\t\xb6\xc1A\xfa\xee\x97" } internal_nodes: { key: "Bwg=" value: "\x04\xdf\xf1\xe9\x87\xfdd\x0e\xc3;}iM\xd2\x03\xac\x15[\xf1\xb2\x8dbX\x0c\xdcc\xefu\x81\x00m\x8a" } internal_nodes: { key: "Bwo=" value: "T\x84\xb6\x89\xed~P\xc9y\x03\x08\x9f\x9cH\xc6\xc4\x10\x1b\xc9\xce\xc1$NP(\xf1\x98j\xae\xd8\x01\x7f" } internal_nodes: { key: "Bww=" value: "mpÖŊ\xbaTR\r\xbaJ\x17\x8f\x0cU\xdaD:\xa1X@S\x89\xe6K,\xee\xb4@\x00\x99~B" } internal_nodes: { key: "Bx4=" value: "g\xbf\xdf(\x97d\xa9dfrŨ˜\xe0\xeeAL4pqS'\xb9}[nY\x94\xc8\xe7\x15r\x17" } internal_nodes: { key: "BxA=" value: "@\xfa{\xb7\xe1\xf7\x80A\x94\x9c}ĶĨ>\xb9h\xeeEsrF!\x85Õĩ\x1c\x12\x1a\xba\xe1\xd5n" } internal_nodes: { key: "BxI=" value: "Y\xf6$Bi\xc4Æŋ8\xee\xa7Xl\xc9\x1eM\x16\x82Dn\xb5˜\xf6\\\x1e\xcc\xc4^dx9" } internal_nodes: { key: "BxQ=" value: "\x11r\xd50(j\xdeTxf\x9c\xe0U\xa5k`\xb5)R\x9b\xa1Ȍ\xa1%\xf8T\x95\x17\xe4iu" } internal_nodes: { key: "BxY=" value: "\x00.G\x9bw\xb7\xf3\xeb\xccY\x025k\x80#\xaf\xc0Fn\x8cČĩ|a\xfe\x9cr\x05x#\x12." } internal_nodes: { key: "Bxg=" value: "\x12M\x9eb\xed$\xa4'\xbe\x8ak\x85\xff\xd7\x03`\x84\xc1\x12\xfd\x02e\xc6l!\xddw\xc5a\x83\xdf\xe8" } internal_nodes: { key: "Bxo=" value: "\x8d\xacÎĢ*\xfb\xe5\xafr`\xa4&yk\x14A\x08\x02J{Y\xf0\xac\xa8Zf\xad\xfb\xc9KA\xac" } internal_nodes: { key: "Bxw=" value: "\xb0cI\xfdI\xd5*\xa5\tf4n\xae\xe2\xc3,k\xd42\x19\x08\xdat3K\xccF\xbd\x8c\xb6#\x97" } internal_nodes: { key: "By4=" value: "\x92\xa6\x9eJ-ĐŊ\xa6C\xef\xab\xf5m\x81\xac\xaf\x88\xacZ\x06\xe8!Ɖv\x8c\xc6\x01\x1f%\x90v" } internal_nodes: { key: "ByA=" value: "\xa47Tr\xa81Ų„\xa5s'\x0cz}\n\xfc\t\xbf\xd5zIs\x06\xb75Y\xdcZ\x02\xed\xc5W" } internal_nodes: { key: "ByI=" value: "Ņ”)Ņ–\xabIN\xacTØ­\x84dL\x8a\xed\x93\x12\x01ȕ\xf2\x86냜\xb9\x1fg\x9a\x90" } internal_nodes: { key: "ByQ=" value: "\xa4o/\xfa\xfc\xf0\xb5j\x19a41\xe9se\x0b\x1b\xd6ic\x92;\xa1\xa5\x05`\xd9\xe5\xf2\x105\xe3" } internal_nodes: { key: "ByY=" value: "\xbd\x06Ũ˛D\xf7\x1a\"\xc7ÂŽ\x9a\x94\x84\x96l\x80tqQvR\x9a\xde\x07\x16\xd9LGd\xec\xb0" } internal_nodes: { key: "Byg=" value: "\x8a?\xfc\xf8j\xb7c\xe1\x01\xb0\x82\x0c\x82\x96S\x88q\xbcD\xbc\xfc\xb8v1\xa0\x1c\xc7\x0e\xb2C*\x8e" } internal_nodes: { key: "Byo=" value: "\x1b\x1b\xf9\x89\xd7z\xba7w\xa5e[\x13\x9ao\x97TI\xe4&\x08\xb7\xdaQ|\xa7\x90c\xda\xdcYK" } internal_nodes: { key: "Byw=" value: "\x8cT\xc0\x9c\r\xfdS\xeb\xbf\xef\xcb\x1a\xf0\xf5\xab3;\x05B&U9\x0c\xa1\xc8\xf8\xc40\xb3\x0b\xae\xbe" } internal_nodes: { key: "Bz4=" value: "\xed\x9c0V}\xfe\xc5\xee\xec\t\xad\xc2\xfc\x8eG\x1a\xc4O,\x84\x05\xc9y\xb3\xb3F\xac\xf3\xc46#d" } internal_nodes: { key: "BzA=" value: "\xd7\xee\x7f;\xbdŨ–v\xd8Z0\x92\xe9W1'\xa3\x90q\xf6\xc5rXԃ\x99x\x1d3\x12\x90\x05" } internal_nodes: { key: "BzI=" value: "O\xef}'\xd7\xd8\xe7\xd4R\xe19\xad\xfcy?X1wx\xef4Ry\xa1\x8a\x8f\x00\xda2\x01\xee\xbd" } internal_nodes: { key: "BzQ=" value: "3\xe2\xc8!_$\x17j\xe9\x0f\x8aP\xaf\x8eŅĢ\xae\xablo\x95ߥ\xa5\xd5\xef\x97O8\xd7\x1b\xfa" } internal_nodes: { key: "BzY=" value: "\xbc:\xf8\xf9\x1e$\x15H*\xcaH\x18h\xef{\xae\x13\x84\x0fJ\xe0#\x90\xc7AaԘ,\xf6\xf1\x82" } internal_nodes: { key: "Bzg=" value: "\u009d\x98ä\xe8\xc5*\xdc:\xa9\xddA(\xb52\x83\x07\x86\xa6W\xc8H[(t\xed\xd9\x1by^\xe7" } internal_nodes: { key: "Bzo=" value: "\xb8W\xe8\xd1\x1d3\x03 \n\xc0ڍ\x9f\xeaH\xb8ÖļȲ4\xafS:\xe6\xdfj\\\x94\x0e#N" } internal_nodes: { key: "Bzw=" value: "6\x91\x8a\xfcg\xbd\x82[Ī›\\\x9b\xcfN\x8a\xa24\xde\xd4\x07\x02%\x0f\xa7\xf87[\xd4t\x18Õš" } internal_node_count: 254 prefix: "\x00\x00\x00\x00\x00\x00\x03" depth: 8 leaves: { key: "CA0=" value: "\x11L\xa1^\x1f\xe6\xeb\x9d<\xa0H\xd6O:t\x8a͍=14\xaa\xffu\xb0ne\x89D\x88#\x0e" } leaves: { key: "CA4=" value: "*I$+\xaf:}\x15\x13\x97\xb1s\xf4\x15\x14\xb7w\xa0\x97d+\x8e\x9f\xfe=Z\xa5\x9e\x92\xe0\xe7\x8f" } leaves: { key: "CA8=" value: ")\xb5Ņ\x0f\x1e\xf9\x8d\xbd\xa22Hg\n\xd4\xe5\x03\xd1c\x81\x87\xe1\xfc\xf57\xc58^\xe0\xda\xf5p" } leaves: { key: "CAA=" value: "\x0c\x13P\x17J\x9c\xe0\rT\xe1\x7f\xdb\x05eG\x91\xc5\x1f?\x12F\x87i\x11\x9a\x85\xa2\x15Ά\x84\xb8" } leaves: { key: "CAE=" value: "\xef\xe0\xf1O'\x1eqU\xc3L3ix\x12M\xc2a\x0c\x94\xda\x7f\xa4˓\xf0w\x88\xb3,4|Z" } leaves: { key: "CAI=" value: "_\x03(N\x84\x85\t\x87\x9f\xe3k&%\xb907\xd8J\xb3to\xfb\x18\xe0-\x7fs\x94\xab\xe4f1" } leaves: { key: "CAM=" value: "4\x8e\x99I\xb8b\xfb\xde\xde\x18\xfa\xafg=\xac\xe2\xe1\xacJ\xd66\"\xf2\xcc\xf5\xc1\x11[\xccz\xaet" } leaves: { key: "CAQ=" value: "*!o\x7f\x8f\xfc\xe8\x93J\xbb\x19\xc2P\xfbnW1\x8b\x0c,\xc6\x1fK\xbf\xc1t(:\xf4\xfc\xca\n" } leaves: { key: "CAU=" value: "\xeb\x16\xc0\xfc1\xdd\xd3\t\xb6Ɵ\xb0p\x8dr\xf6q\x91\xb9Ɯ\xfd\x15\xc05\xdaڟUy\t<" } leaves: { key: "CAY=" value: "Čą\x8bI\xff3\xd9\xdbq\x04\x8f\x9b\xb5\x8b\xe9i1D\xe562\x1f\x92_\xd3\xe9\xa7rTT\xac+" } leaves: { key: "CAc=" value: "\"\x9f\x80o\xb8gI\x03\xd8_\x94L))\xb9\xf2U\xddNX$;\x0c\xba\x87!O!B\x86\x83$" } leaves: { key: "CAg=" value: "\xe5\x08Y\xab+\xee\xd8G\x08\xc6$\xf0ja\xa8\xc1\x9f\x98=\xa5\xf6\xf7oX}\x7f\xbe\xa8\x82ėš–" } leaves: { key: "CAk=" value: "\xfd\x0f\x1f\xb8\x88r*I\xb7\xdeS;\xb4@\xea;\xcd\xe3]9E9\x14\xde@b\xf1ˁX)\xda" } leaves: { key: "CAo=" value: "\xd4\xebd\xddC\x18\x9e\xaf'\x18yȗ\x1b\x7f\xfe\x12.\x87R\xe6\xdfY\rhd?@\x08t\xc1\x86" } leaves: { key: "CAs=" value: "<,\x91%\xb4\x7f\xad\x92\xc5z\xf9\x0bgorV\xe1u\x96\x91\xa4\x19\x8e\r\xd2*ÚĒf\xadJ\xfe\x88\xfb\x03\xf6\x03\x8e" } leaves: { key: "CB4=" value: "\xf3\xaf(\xcc\xc97\x80\xc9\xf8\xc5\x14\xb6\xcdÆŖR\xd5|ƟQl\xcd#\xe7cR0\xd2vA\xc0" } leaves: { key: "CB8=" value: "b\xeb0^~\xe69\xe3\xce>\xc4}NJ\x00\x0bv\xfc\x98 \xec.\xa5ʆs\xea)\xe7s\xe8\xd0" } leaves: { key: "CBA=" value: "\x82\xbc* \xf7\xc0\x15!\xaa\xf3-j\x8cH\x8cu\x8d\xca\xe1$oÍŋy,\xebtԁ a " } leaves: { key: "CBE=" value: "\x0c\x00\xf4\x86,o\x80$\xdbtՕ\xe6Īž\x06*\xe2t\xd3\xf3;u\xa9\xc0\x8e\xa5\xec^\xcd|\"" } leaves: { key: "CBI=" value: "\xac\xaf\x05g7m\x8a\x82\x94\xf9\xbad\xffȡ\xd9\xe0\n\xae6\x8csA\x01>\x10\xb5A\xd3s\x1e\xc1" } leaves: { key: "CBM=" value: "\xa8\xafP\xd4\x1a[u\x80?\xa4\x87^T,\x10b\\\xc3\x1c\xa4\xf7HYAeQ\x0f\x04m\x9e\xd0W" } leaves: { key: "CBQ=" value: "\x8af4\xe2O\x98\xad\x9f\x8d\x1d~\xf4\x12\xff\x16F\xe0O\x1e\x9b\xc1u\xdf\xcb\x01\xf4\x18\xaa\xcc\xedR\xbd" } leaves: { key: "CBU=" value: "\n\x19\xd9\"CNR-)p\xbd\xb1\x94_e8p\xab\x044\xe2\xafh\r\x8f\xa4\x12\x05\xe3e\x86\xbc" } leaves: { key: "CBY=" value: "\xcdUm\x10]\xdd\n\xf8\xfd\x81\x8a2m\x03%[\\\x14_\x81\xf2w|\xec\xb2fU\xe1)Q\x19\xcb" } leaves: { key: "CBc=" value: "Bi\xd7\xfd\xber\xd6\xc1@\xces\xa5\xda\x17\x98\xa9\xa3W\xa6\xd5\xe0\xe9\x01\x98\xd0\x0e\xca\x0b\xaf\x91\x9a\xfc" } leaves: { key: "CBg=" value: "vV\x8d\x7f\x98\xcc\xe3\xc8\x07\xbb\xbf\t\xbeU\xb3\x0fČ `\x86\xea\x0b\xa4\xdd\xc8{\xc2\xf1ob\xae\r" } leaves: { key: "CBk=" value: "\xa1b\x0by\xbdŪ›\xa6\x0b\x0bT͘EF\xb5^\x98\x81Ų“\xe0\xefe\x0b\xf8\x16I\x15\x91\xb2\xdb" } leaves: { key: "CBo=" value: "j\xde\xdf\xc9\xd4\x0f\xa1+ \xaa\x1ee\n!nˁ\x8c2\xb1b.\xba\xc5\xebW\xb2w\xc9\x1dX\xf7" } leaves: { key: "CBs=" value: "ߊ\x82\x84-\x8f\x9b \xd5I\xc3o|\xe0\x05\x17g\xc1%)ß ^\xf5\x88\x92Qn\xa3\x19\xa2T" } leaves: { key: "CBw=" value: " \\\x7f\x84\xb9sT\xfdQ\x0b-\xech)\xbc\xd34\xeb\xb8\xc1\x93f\xd4uV\x0f\xcf\xf6\x8d\xbe\x0co" } leaves: { key: "CC0=" value: "\x9dO\x11\xc9W\xbb\x1f\x89\xe1<\x9d\xf5\xc9K\xf8\xfe\x86_\x94\xbe\xfa\xf5\xf3\xf0\xbd\xcdZ2\x1b\xba\xb5O" } leaves: { key: "CC4=" value: "\xf0'\xb0G\xe0\x16\xdb\xe6\xe5\n\x94Íą\xefL,kK\xe6lZG\x80\xfd\xc6\x144\x0bŅŖ\xad>" } leaves: { key: "CC8=" value: "\xc8nx\x8cl\xf1\xf3\xa0\xd0\xd1C{\xbe,\x94\t\xf8\x0f_\x8f\x8aO\x802b\xfb\x9d\x01\x97hVG" } leaves: { key: "CCA=" value: "\x08\xb4\xe8\x8c\xe2,\xac\xf2\x96q\x80\xa6\xc0\xb0\xd15\xfc\xda\xe6A\xb3\xa9\x08\xd0P\xf2\xd2\xe0\xdd\tk\xf9" } leaves: { key: "CCE=" value: "\x0cm\xa4V\xcdu\x10\x95LL\x96H\x01\x84\xd5\x0fks\x158\x17\x1e\x01*\x9fd\xb2y\x1e59\xaf" } leaves: { key: "CCI=" value: "\xb9b\xf0\xd5耚mwJ\xb0\x98\xc6r\xd6s\x96\x9a\xf3\xb7Uv\x8f\xf1W\x8ceVnG{y" } leaves: { key: "CCM=" value: "\xcaCœ\xde\xd3!\xf2^\x1b\xf9\xb6\x14\x1e\xfd\x80\"u\x08\xe1|\xa4?`\x8d\xafxl\xe1@\xbf\xb5" } leaves: { key: "CCQ=" value: "bh\xe1\x8a[\x95c{\x0c\xfe\x9d^\xef\x90\\\x13\xa2S?\x19\xa2(\x1f\xdcY(M\x08\xf1\x01\x97\x8e" } leaves: { key: "CCU=" value: "2PaIv\xd6B\x0b\xb5d\xa9\x1fį—Ē\x18\x80\xc0\x8bOU1M,\x96\xdeVZST=\xc5" } leaves: { key: "CCY=" value: "\x9e\x8f\x84\xb0\xd4;\xc6\xeb}e\xcf\xeaD\x90U0\xce\\b\xf6H>\xf0\x96\\,E@\xa1T0\xf8" } leaves: { key: "CCc=" value: "W3\xab\xb2\x19:9\x9f\x19\x91 \x90\x18!\x9cA\xab\x05P\xadc-\xa2\x15\xb8U\x81+<\xda}\xc2" } leaves: { key: "CCg=" value: "\x18\xf3\xcd\x11\xb4K\x10\xc2\xcdQ\x9c\x81\xb6\x14Q\xd7\xdd\x02YC\xa6\xbdP\xd8%\xd2\r\x0e\x0eW\x1dN" } leaves: { key: "CCk=" value: "Y\x81Pa\xcd(\x06\x07ˍ\x89\xb3\xfe\xf6z\xa9\x96n8ȓ4w\xea\x93\x1e\x13\x1d-\xf8,g" } leaves: { key: "CCo=" value: ".H\xf4Q\xc1\xbc\xedk$c;$*B\x8e\xf5\x15b9Ûˇ.\x15<\x0e\xe7\x98\xda$\xd4bG" } leaves: { key: "CCs=" value: "\x07-ËŦ=\xf4n\xa0W\x9d\xbc\xeb)\xb1Y\x83f\x15o\xd0\x19\x91%M3r\x00\xa0\x13}\xe9\x9f" } leaves: { key: "CCw=" value: "\xa6W\x98\xd6Ũ†\x0e֖q]\xfcė‰ļ\x0fa\x12\xdb?E?\x90\r\x82\xcd\xc2׃\xbc\x88\xb0" } leaves: { key: "CD0=" value: "\xea\x88xb}^\x05B{bH\xa5\x82)rU\r\x02b$\xda:H\"\xb7\x8cnj\xff¸\xd8" } leaves: { key: "CD4=" value: "\xc3\xc4,\x01R\x8c}R\xde+=\xe6\x14\x05o\xaf\x95\xb0\xc5\xf6\xeaŨŧ\xb0\xbf\x98\x89 \x9fJ3/" } leaves: { key: "CD8=" value: "\xd5\xc4'\n[\xac\xcbs\xbe\xc8\x04\xb2s\x84k\x90-\x177\x8cT\x0eM\x96\x1a^\xaf\x15!\x8f!\xea" } leaves: { key: "CDA=" value: "*\xac\x00\\؋\xadN5\xd5\x10\x12\xff\x8eh\xb1\xf9\xe6\xe7_\x03\xba\x1d\x0eNQ9\xa4\x16ÖĄ\x04" } leaves: { key: "CDE=" value: "\xc5\x06\xb9X\xa2\x82\x8c\x81\x91˯\x01\x17\xa1\x85\xd1m\x0ey\x91\xec\xe3\xdb\x13\xd3\xdd\x10\xe0\xb8'\x91\xac" } leaves: { key: "CDI=" value: "%%\x17K\x18\xb3\x97\xfc\xf7\x9f\x1e\x14r\x04q\xb7\xed\xf0T\x84\x80f\xe8\xa7\xe0\x88\xd53\xa3S\x8a\x02" } leaves: { key: "CDM=" value: "\x8e\x85\xb3\x96\x98'\xd7\r\xff\xf7\t\x08\x9b\xbbfe\xab\x90olל_\xa8\xd9\x00\xa0\xd3\xfa\x01^." } leaves: { key: "CDQ=" value: "\x1c\x04\xdb\xc0a\xc5E\xde\x1a\xa8\x18C\xe79%\xa2\xc0.\xe1\xfb'2\xdaX7C\xcfԇ5\xee\xe6" } leaves: { key: "CDU=" value: "!ht\x94\xe9\xc9Y\xf9\xc2\xa65\x0c\xf2,\xa4\xd6,O/;" } leaves: { key: "CE4=" value: "\x91\x8f\xf1\xfc\xdd$+c\x820뷉f\x8c\x1em\x8f\xc0\"W5\x90\xb0O\xed\xbdR\x03T\xf1\xe7" } leaves: { key: "CE8=" value: "B\xcd\x06\xb3\x9d\xf6c\x06\xe3CŌĨ\x85\xefE\x82Bߓ\xa9gS\x11\xfa|\xa4\xe7\x07p\xa1\x03+" } leaves: { key: "CEA=" value: "\xdd\xd4a\xe7\xdej\xdaͰ(\x19`\xe7\xdcâē™\x0239\x9eA\xe5g\xba1\xce\xcbp\xedc\x92" } leaves: { key: "CEE=" value: "\xe9\x90Ym'\x9e\xb0,\xdfDSu\x9a\r~\x0f\x1d\xa6-\xd6a\xe3}\xee\xbc0q\xd5H\xcb\xf5n" } leaves: { key: "CEI=" value: "K\x19\xe5w7\xc0S\x87\xff-\x9b\xdeD\xf0M\xf2I\xbeO\xd9\xc812r\x1e\xf8\xff\x06J\x04\xa8f" } leaves: { key: "CEM=" value: "Q\xb8G\x17\x9d\x93\x8a~E\x84o\x849\x96\"\x9a\xe4\tg?\xc7l\x8283ßĩb9\xcbG\x84" } leaves: { key: "CEQ=" value: "@C\x9d\xd2\xf2\xfa\x8d\x87\x9eATv\xa3\xbco\x1aO\xb0\xfa;t\xd7\n\xe9\xd7\xdbywd\xbcF<" } leaves: { key: "CEU=" value: "\x9c\x19:@\xb5\xbe,\x89[\xd5U\xb3\xbb4\x1e\xfd%E\xf3\x9e\x18W!\x14\x8b\x15I\x12;\x04\x1cY" } leaves: { key: "CEY=" value: "\x86Q>)\x8a\x00\x82\xc3\x08\x08\xe7\xd1rU\x88Us\xf3sq\x93\x87}" } leaves: { key: "CFc=" value: "\xdd\xe7\xd3\xd1\x18\xe5\x0e#\xf2\xa9\xf0\xd4\xde\x10:G\xc4PŨ¸A\xdb\xf0\x00\x82~!\xa7*\x98\x96\x02" } leaves: { key: "CFg=" value: "\xday(F(\xe7\x0bz\x12\xff_x\x00C\xe2\xe4\xac`\xfb\x9fvzz\x17K2\x11\x03\xe4\x0f\xe9\"" } leaves: { key: "CFk=" value: "\x81KX\x80:\xbf\xeaE\xdd\x0b\xd5\xfdl\xcc\xfef\x87\x1d\xb7\x86e\xb0\x0et\xa0xW\r\x10\x84\xeb\x92" } leaves: { key: "CFo=" value: "\x02\xdbKH^\x814lR`\x00X\x0c\xf0j@ÉĄ\x19\xa0\nĪļ5\x14\xf6(A\x06\x1b\x8c\xe9" } leaves: { key: "CFs=" value: "6Į\x8aPĐĢ\x92\xdf=\xb2\xfb\xf7IX\x84\xfc|\xfeY~\x93\xfc40\x16\xbb\x8d\xd5\xd6\xd8\xd4" } leaves: { key: "CFw=" value: "\x17~\x06B2\x01\xf0\x03$\xd0Ч\x13\xb9\xa6\"\xeb\xc8r\x96+\x02*\n\x84%\x9b\x93\xeb\xbd \x80" } leaves: { key: "CG0=" value: "\xb01\x99\x9fr\x00u\xb2A\x13^h=\x93%XY\"\xeaĪ”Û„\xed8'\xf7\xe6Ī€Ûƒ" } leaves: { key: "CG4=" value: "\n\x85z\x7f^t>\xfbdd\x7fR+\xfc\xcbQ\xa9UQ9\xf7\xf7\x8b\x16/\xc9\x7fq\xa2\xb9%\xc8" } leaves: { key: "CG8=" value: "\xcd\x06\x8a\xe8\xe4\x85\xd5r\xb7\x96ĪĄ\xd1\xf4\xba\x08E\x12Zw\x85\x89m\xf3\xc4\xd4u}q\xa2\xacJ" } leaves: { key: "CGA=" value: "\xd9>\xa4Õļp\x81\x15&\x97@\xec\xfb\x8a\xc2!\x1c\xf7T\xceIa\x0c\x90hv\t\xa6\xba\xc1E\x14" } leaves: { key: "CGE=" value: "\xe8\x8dp\xd6l\nh\xf7\xea\x94.\xe9z=\xb2\xc8.\x91\x1d\xbe\xc3&8\x9aaU\xee\x9a\x07\x13L\x1d" } leaves: { key: "CGI=" value: "\xb2\x1d\xef\x00\x9d+\x1e\xdci\xfcŲŧ5\xecJO\x88\xff\xca~\x124\xe5\x1a\xd8J\\\xedGm\\\xe1" } leaves: { key: "CGM=" value: "\x16\xfcZt\xc5$\xd8\xc9R\x01\xbc\xde\x02\xa0\xcfS&K\x1d\xd8f\xc3\xc5e\x82\x05\r(1x\x81n" } leaves: { key: "CGQ=" value: "\x90E\xfb@\x1av\xd5\x1c\xa4+\xf9$'3\xff\xd1|W\xd6\n:\x036(\\n\"r)\xa2aG" } leaves: { key: "CGU=" value: "=\xc2\xc2\xf7\r\\\xbf\xa2M\x8d\xed\xb4\xbf\xa8\xf8aF\x07\xad\xda\x07\xe6ֈG\x0b\x11\xa5_d\xe3\xfb" } leaves: { key: "CGY=" value: "\xf2\xed\x85qƌ\xb7\x1eQ5\x99Ņŧ^-\x17b\x89\xfd~\xd2\xec\xcb\x0c\xee\xbeG\xf7\xfa\xd8\x12\xa0" } leaves: { key: "CGc=" value: "\x89\x8eLH\xb4T\x8d\xae\"Q\x91x\x15 w\x1cw-\xcb3\xc7\xfd\xeb\x0b\x9bff\\\x16p\x0ba" } leaves: { key: "CGg=" value: "'\xe4xB\xda0\x8b\x14\x1e/\x98fl\xf8\x99\xbe,\x1b\xe1=J\xff\x9c\xa4<\xff\xfd\x00\xbd{\xda\xc8" } leaves: { key: "CGk=" value: "\x03}\xf5Įĩ1\xd6娍\xd3\xe4\x03Deyr\x8a9\x8b\x9d\x06Yc\xf0\xf7\x06\xfb(\x03˜" } leaves: { key: "CGo=" value: "\xc7W\xb4\xf0R\xa6\xb4.wR\x96'\x19\xa7@\xb0\xe366\x80\x1d\x01\xb2r\xd4\xfb\x8dT\xaa^\n\x0c" } leaves: { key: "CGs=" value: "\xc5\xe6\x1b\x8dwouAq\xd2J}$Dsv\xe0\x17\xbeIe\xac\xef'\x05\x1a!\x9a\xa1\xe3\xdf*" } leaves: { key: "CGw=" value: "}\xa5}l\x11îžļĮŧM\xab\x92\xab}\xbaz\xc5j\x07\xbdNQ\x9d)\xfd\x80\xf2_\xe6r\x0f" } leaves: { key: "CH0=" value: "k\x14ox\xf7\xc2J2V3\xb1\xca\xcb\\D\x12\x9el\xb9\x1e\xd6\xc1\xab\x06w$o\xba\xebf\x04F" } leaves: { key: "CH4=" value: "\xa32`\xee\\\x07\x10slaO\xb1u˰\xa3\xba\xbb)\x17Īŋq8\x95\x18\xeb4i\xacG\xd3" } leaves: { key: "CH8=" value: "Qm\x9a΀\x06t\xa3\xf1w]{\x01*\xfd\xc1\x88\x8f\xb6\xa8\n?\xe1J\xf3\x83\xcawZ\xd4F\\" } leaves: { key: "CHA=" value: "\xa3\xbf\xc9p\x93a\xb0\x9eF\x95E&0\x00\xab\xba'\x92O\x93\x1e\x92\x1b\xef\xae\x13\x95\xbe\x9c4\xf9(" } leaves: { key: "CHE=" value: "\xd7F\xe9^r\n\x8c\x1e^ĸ\xef\t}>\xe1b'\x18wT\xc3l2\x88j\xea\xb6\xf7\xf9\xfe\xd8" } leaves: { key: "CHI=" value: "T\xfff,K\x12\xbb\xac\xa6\x0eؙ\x8e\x12h4\x9fs_ AT\xfdR\x96\x89Di\x917\x9b\x1e" } leaves: { key: "CHM=" value: "n6\xbb0[yv@]\x87-\xf4{p[\xb2Y\x9b\x92\xaa\xbc\xa7\xe4\xbcst\xa2\xff^a\x91\xc9" } leaves: { key: "CHQ=" value: "%\x99\xa7\xf6-3\x0b\xe2u\xaf\x8e܋LS\xff\x01\x15?Χ\t\xa9ID\x8d\xb8\x96\xd9w7\xb0" } leaves: { key: "CHU=" value: "\xb7\xa3\xce\xefF\x15\xf1\xfc\x0b\x95\xc8y\xc8vf\x12\x84k\x18'B\xe4h\x8eÉ­\xc3fv\xcc\x08(" } leaves: { key: "CHY=" value: "\xca2jg\xc0\x84\xab\xdb\xe3\xf7O \xee\x9e=\x98ç{\xb6Z\x84'\xe1\x90A\x0cÚš\x96H\x8b" } leaves: { key: "CHc=" value: "\x8bĐŖ\xc1\xca\xfe\x10GrjY\xf2t\xed\x05|\xe4>\xa6H\t\xbbnVˁ4\xe7\x8b\x1f1#" } leaves: { key: "CHg=" value: "\x00\xfe\xb4\xf0\xf0\xf6\x11\xf0\xe3\xdbB\xa7\xcb&\xc2\xeeqF\xa9K\xef9w\xb6oeH\xca=p|u" } leaves: { key: "CHk=" value: "\xbf\xealÚ¤\x8c=[d\xe3)\xfb\xb7\xd1\x1b\x8c\x1b\xe9\xddA\"\xfa\x84\x9c^C\xe6b\xd97\x80\x93" } leaves: { key: "CHo=" value: "\xa6\xfc/\x9c\xa0 S\x86\xe3&[\x96oˀ\x97(m\x8dJ\xfaO#8\xcc:\xfd\xe8\x05q;\x92" } leaves: { key: "CHs=" value: ">ÆŠ\x91w.\x0eLK\x94\x88\x01\xec{\x0b\xf67\xf7\xbc|ׂ(\x004\x81\x90\xa9\x97ɑ\x9d" } leaves: { key: "CHw=" value: "j\x06(j\xab\xb2\xc7P\xec\xdd\x00\xed\x97\x01^\xafB\x1d\x13~)=Íž\x90\xdd\x18{\xa3י5" } leaves: { key: "CI0=" value: "Ed\r\x0e\x07\x12\x91\xa3\x92G\x97\x9a\xd0H\x94\xb8\x8d\x15\xceH3\x8b\x00&>\x9c\x06*C(\xc7K" } leaves: { key: "CI4=" value: "Q\xc3\xfd\x93c\xfc?\x179\xd8q\xe8\x02\xe2U\xbdWU\xe2\x14\r4\xf66\xe5\x01Ũ’\xb1m\x1d\xf4" } leaves: { key: "CI8=" value: "\x01k`\t\xb7\x0e\xe5j,\xbd\x8d\xed\xaf\x1ed\xae\xa1C.j`z\xf39=֜6\x9c/Lt" } leaves: { key: "CIA=" value: "h\x07\x81\x1e\x0ce\n\x9c\tc\x8b\xf2\xaa\xcbMTo\x8e\x12\x9a\x19\xbdV\xde\x0b\xe8U\"\x83H]\xf6" } leaves: { key: "CIE=" value: "\x97\x99\xbb>\x0e\xe2\xf4r\xc4 ty=<\xadI\xd9G\xfawN\xe7\xfc\xae\x88\xee]\xd64\xa3B\xbc" } leaves: { key: "CII=" value: "\xf2\xde\x1e՟$\x94ڂ\xbd\xd2\xcbP˟\xcbNF\xba\xcc\xccKÔĻ\xacG\x90\x00CԌ\xb8" } leaves: { key: "CIM=" value: "\xe9|\x0b\x8f\x16\x12`Vv\x98\xca \xa7q\x0e[\xb5\x91k[\x86\xa5\xc2\xc2\xc61\x81d\x15rd\xa5" } leaves: { key: "CIQ=" value: "|\xa3\xd6\xc7r\x87\xbde\x9f\xa0\xcbw^\x85\xe5GÃĸ\xa2\xfb+~\xed\x14\x82\xed\xe5\xd9#\x92\xde\xc1" } leaves: { key: "CIU=" value: "\x94\x00A8\xb2\\\xf4ßĢ\x88\n\xf7\xe4\xaf?\x90kŲ—QoV\x107}k\x1f\\\x9d\xc7|&" } leaves: { key: "CIY=" value: "?\xe2\xf4\xb0)\xa0\xa2O6\x03?\x12-\xe8Y\xde|\xdf\x13\xabO" } leaves: { key: "CIk=" value: "\xc4v\x1c\xba\xec\xfb\xc1\xa4\x11y \xc6\xc85 \x9b\xce\xd6(\x02\xb9=ĶŊ\xa5\xaa,\xe5\x99\x1f\tR" } leaves: { key: "CIo=" value: "{\x17\xbe\xeb]\xf2d\xd9\n+\xbf1L\xcc~\xde\"\t\xe8n\xe9\xe3DLi\xe0t\x0bp\xb1\xbc\xb1" } leaves: { key: "CIs=" value: "\x9e\x19.\xb9\x00âŋ™y\xf3\x87\x1f\x12?\xec\xb2\xf2\xa4>\x16`]{Ī›\x81\x0e\xe8\t\x97\u009e" } leaves: { key: "CIw=" value: "\x94#fVl\x8e\xecve\xba\x8b?.D\x1ab?\x99\xab\xd2\xe58\x15\xad\xa0\xb7\xb3\x17\xaf\xd5/1" } leaves: { key: "CJ0=" value: ";(\x11Sa\x0f\x84>\xeb\xb6\xd1\xf2\x1b\xe0\xcb\xfaK\x0fA.>\xee\x88\xe9\x123\xa0d\xf2\xa3\xde\xf3" } leaves: { key: "CJ4=" value: "\xd5[Y\x9c\n\x00\xf0\x81lp\xd4\xc5.\xbdЙ\xbcx\xda\xfcXĖĸ\xa7#\xaa\xbb\xdb%\x9e\xa1\x11" } leaves: { key: "CJ8=" value: "\xbeV\xf5\xa7\xb7\xfe\xb3\x03\x14:==\xb0*\x17\xb4N\xdf9*\x7fRK\x8f\x94\xe4åĸą\xeb\xbf*" } leaves: { key: "CJA=" value: "ÍŠ\xc7\xd4l\x13\xf4\xfaQĘļ\x9c\xd9ßĒz\r\xa3\x8e\xeb\x95\x0b\x92\xee\xd3I^eE7Gq" } leaves: { key: "CJE=" value: "n$B\xac\x12\xa2r\x99K\xdc\x16\x11\xe5T\xe9\x9aC\xa9\xd8|\xf7\xd0\xdf^xܯ\xd5a\xb6d\xc7" } leaves: { key: "CJI=" value: "9퍴\x87\x89,\x86\xc6\xda\xf5\xf3j\xfc\xd2\x1cn\x91\xf0\x94F\xa8\x1f\xbe\x0bS\xa4\x06\x87\xf1\x9d0\xa4" } leaves: { key: "CJg=" value: "Yc\x11q\x02\x1e\xc9^\x93à\xb0\xfd\x8ezfr\xa4\xe4NN\x9br\xeeW\x7f\xc9fxڈw" } leaves: { key: "CJk=" value: "\xbb\x9d\x9c@\xa32\xf3\x9d\xbffx\xc8\xf4\x06\xc8r\x08\xf9\x9f\x80\x17qm.\x97fL\xe9qu\xd1e" } leaves: { key: "CJo=" value: "))åļƒs\xa8\x82\x8f\x04c\x96\x92\x83\xbc4\xfd#\xa1rq\xbc\xae\xb9P@\x08\x82\xa8\xc1\xb4\x9e" } leaves: { key: "CJs=" value: "\xf07\x9e1\xfb\x8f\xe4æšĄ\xc0\xff\xa0i5Q\xb1cO\x9fA\tXR\xaa }yFG\x08x" } leaves: { key: "CJw=" value: "\xe9\xb3=\x100v2]\x80\x10\xba\x80\xaa[\xe14p\xb7pܐ\xf5\xf0\xe7:|Y\xbd\x1b\xd5ÖĨ" } leaves: { key: "CK0=" value: "Sâ§Ļ\x19\x0fh\xa8\x82\xb3\xa8$c\xdd\xdc\"\x8d*\x1d\xbbJ{\x00l\xce\xd2^jÉ­\xdc\xf2" } leaves: { key: "CK4=" value: "t\xa8\xb2\xfe\x1b\xb3|Yd\x18\x8a\xbe\xd2i\x99\x90\xce\r$s\xc6\xf3K\xb6\xf8\x12Z9\xa6\xc1\xbeZ" } leaves: { key: "CK8=" value: "\xa3<\xa9rȀc\x9b?b\xeb\xf7\xef\"\x88\xe3\xef\n\xfa'\xae#[:\x01\xb7W/\x9eA\xd3p" } leaves: { key: "CKA=" value: "\x91\xff\xf7\xcbJ\xc0\xba\xc1\xba\xcbZ\x9e\xb5\x16m_\xe3\xe6\x90\x11\xd5\x19\xb7\xbd\xceAx\xa9\xa9\x0f\xd23" } leaves: { key: "CKE=" value: "b\xcc+H\xba\xc4cE\xa9\xaf\xbb\xa6׹\xb6\xba&:O\xe1\xf4\xba\xfc\xe53\xa8$C\x1b\xf0\x0f\x1e" } leaves: { key: "CKI=" value: "\xc3\r.\xf7\xe4\xe7\xb4$\x98\xab\x82s`\xe2p\xfd\xae\x8aC\xaf3\xb4\xe3g\x9fq\x93\"v\xba\x18\x94" } leaves: { key: "CKM=" value: "\xc3\xf6r\x13P/Ht\xe0u\x84\xa5\xb1\xa5b\x06\xb4W\xa39Dȃ\xad\x9cg:\x01\xaf\xd9+r" } leaves: { key: "CKQ=" value: "x:\x8b\xfeA\xdcɛ\xa7\x04Ķ”n\xe3A\x9e\xfc\x8d\x1b!\xa6\xd0y\x7f\x0e\x8f\x15\x94\xbaVĮž" } leaves: { key: "CKU=" value: "s+u`\xf4P\x91\x9cyÅĢ{\xfa\xbeT\x1614\xed\x83(\x16\xae\xcdhu\x8bZ\xda'w\\" } leaves: { key: "CKY=" value: "\xbc\xb8\xab.6\xaf" } leaves: { key: "CLA=" value: "`\x07\x15\xd8?\xd7H\x02q\x0b՟\x84\xf8\x13W\xb1\xed#4=\x07\x95\x93\x0e\xfc\x95ΉQj\\" } leaves: { key: "CLE=" value: "\x8e\xed\xd2E\xf0U\xd9=\x8d\xb5\x14=%Vj\xe5\xdd?>:\x8c\x1b\xe5\x068\x88\xc0\n\x15\x94-\x8c" } leaves: { key: "CLI=" value: "\x82\xd5\xf2W.\xa1\xe5j\xef\x1e\xd0}d\xefbj\xb0Z\xb8(\x1e\x125+\x0ez\x1f\x1c!(\xbcj" } leaves: { key: "CLM=" value: "\xdaR\x8e\xd5\xe1\xc0\xd3\x117\x95d+\x86B\xe5\xa8L\x8c\"\x9aM\x07\x1f\x1e\xd6'\xb1)\xf0\xa5a\xd2" } leaves: { key: "CLQ=" value: "S\x9fG\xcb\x1a\x89\x8d\xf0\xbd\x8ex~`;iDP\xcd\xc5g\xb5(\x03\xeb:\xca\x18\x06F\x06|\xf3" } leaves: { key: "CLU=" value: "'\x86\"s\xc9\xdb9\xf2)\x85XiK\xc6%;\xfcŅĻqp\xebA\xfbXÛ¨\xef\xa6\xf2~\xb7" } leaves: { key: "CLY=" value: "\xea\x18\x98\xc1\xa6\xad\xd4-\x0eD\xb2╕u\xe9\x00\xb7kÃą\xb0\xfa\xe3\xed\xaf\xa3\xd4=oIK" } leaves: { key: "CLc=" value: "\x10l\x95\xa9\xef\x1d\xf0\xb3\xd7U\x02\xe8\x12\x87?\xba\xe4\x03\xef×Ēpu;\x81\xa8-܎Q\x84\xe7" } leaves: { key: "CLg=" value: "'\xfd89\x98w\x9f\x1a\x8e/\xf1O+\x1a1l\x88=\xcfu\x98 \xbb\rh\xc5\x07a\xb61\xa1\xe0" } leaves: { key: "CLk=" value: "k\x82\x0c\x05\xc6\xe2\x1ci\xd3\x01\xe7\xb5R\x01\xe1\xb5\xc7\xea\x16eC\xf9\xe6h\xda\xe8\x86y\xb3<\x15\r" } leaves: { key: "CLo=" value: "\xa81FR\x00\t\x97\xfeĪŊ\xe4\x87\xf2͒9\xcd9\xb9S\x95Ū­6\x97@\xf1KQ\xd3\xd3;" } leaves: { key: "CLs=" value: "\x1c[Uz\xa5\x9c\x0b\x1eŪĨ9\xc0\xac\xd7\xd4\x08US)X{\x91q×§-\xde\xc9\x19\x84\x7fJ" } leaves: { key: "CLw=" value: "V\x83&\xb8\xa3\xf8\xec]\xd0\xd1Y\x0f\xe8\ti\x97\xe1(G\xf4[?\x1a\x93\xf5\x14\xd3\xcan\x9b~)" } leaves: { key: "CM0=" value: "\x9e\xfbĶŽ\x0c\x84\x90\xa1\x14\xa7\xb1ʐ\xca?\x97k\x13\xa2W\xcf\xf0\x96\xd2\x1c~ßĻ\x1b\xa2\x08\xfd" } leaves: { key: "CM4=" value: "\x8f\xcciT\x91\xcc\xc4\xd6G_\xdf\r\x01\xa4\xa0N\xf5\xb4\xba.\x03s\x0efq\xcaP\xdaĪ‚\xad\x00" } leaves: { key: "CM8=" value: "\xc9\xf3Mf\xe2[\xf0\xb5\xb0l/X&\xaf&2Q\xd0I\x12,p-\xa2\xff\xb1\xba@A#/\xa7" } leaves: { key: "CMA=" value: "-\xdc;\xa5\x92.\xa2\xf5߈W\xb3>Öˇ\x88,E)5\x1d9\xe3_\xe0n\x1a\x88\xef\xdbZ4" } leaves: { key: "CME=" value: "=\xab\x083\x8e\xbap\x16\xb40?\xba\xe5\r\xd4\ru\x8a -ĘĻ|]l>\x18\x95\x89\x8aPe" } leaves: { key: "CMI=" value: "\xef\xc3\xd2B\x8b\x18+K\xf3\\\x98\x1a\x0c*#\x1amn\x8fŨĒ\xe9\xec(\x8a|3\xd1\x0b\xc3l\xdb" } leaves: { key: "CMM=" value: "c\xf3\xf2/G6G\x04\x00[r-$\xe8\x84J\xa9\xeb\xe0\xc0?\xbf.،Z)\xead1\xca*" } leaves: { key: "CMQ=" value: "\x03\xf0\xa6\xb7(\x8a\"\xf8@\x15\x9dw\xf9\x8c\x8c{}\xf4\x86\xfas\x02\x82\xf7\xbf\x9f\"\xb3{\xf0\x9d*" } leaves: { key: "CMU=" value: "nrŅ”c&\x14\x15\x0fp\x15\x1b\x8c\xb3\x91M\xcbM|\xb9\xbc\x13j\xd8V\x03\xfc\xae\x04d\xee\xef" } leaves: { key: "CMY=" value: "L\xd7\xda5(\xe4\xb9q\xff\xef\\!\xb0&\xe7\xa7\xf5\xf0^j*|%\xbcqi\xd7M)%\x1bM" } leaves: { key: "CMc=" value: "\xe3_uqa\x8c\x06\xa47]s\t?n\xf9\xf9:Tx\xd6~DÂŖ\xa8\x97\xd3\xee\x9e2\x81*" } leaves: { key: "CMg=" value: "\xf7\x17\xb4\x089\xf4\x9b\rŅ,\xd0%\x13J*69\xf3*\x1d\xe4\xe4:\xadU\xdcl\r\xf0\xa3T" } leaves: { key: "CMk=" value: "\xa2\x81)\xd1m\xc8ĖŊֈ\x9f~\x08\xf1\n\xb8\xbdS\xcbŪŖ>\x8e@\x85\xc3\x0e\x01\x840\x05\x92" } leaves: { key: "CMo=" value: "\x19\xe6\xf3\xa2\ta6e\xe3\x03Sn\xb4\xb1R\xb8Z\xea\x90\xf7\x9b\x93\x1b\x01X\x97\x02pD\xbc\xbe\x83" } leaves: { key: "CMs=" value: "\x11\xb3M{h\xeaw\xfd i\xcak\xbd\x91\x8cU-W\x0c\xe0\x84\x8d\x97\x06\xf4\x0b\x03\xc2c\xd5L\x80" } leaves: { key: "CMw=" value: "\xe0{_!Ņ€\xaa\xa9\xf5P7c\x18Jg{\x152\xa6FF\x9c\xfc|I\xa3\xac83\x08\xc5\xca" } leaves: { key: "CN0=" value: "\xc2q\xc9@\xfaP\xb6\xbe}\x95\x0bg\xee\"\x92ku\x03\xfd?E\xea\x9f3闌\x12\xecLv\x84" } leaves: { key: "CN4=" value: "\xad\x14\xfeD\xb2E)2m\xc5ŌŽ\x08\xac{s1^Ęą\x0eM\xad\x8d\xa6\xbe\xb4ۈ\x85\xcf^" } leaves: { key: "CN8=" value: "\xe3+\xe9ōģœĢ;\xce\xf8\xbcxo\x12\xa4Ũ‘\xff\xd0;\xc7\xd3\xd5\xc8\xc3C\xa2'\xc3d\x8eG" } leaves: { key: "CNA=" value: "\x89$(\xda(\r\xf6\xd8TV\x9f\x9d\x9bb\xe7\xd0\xda\xed\x07)\xffx={~\x12\x9c\xf1\xee\x1f\x81h" } leaves: { key: "CNE=" value: "\xc1\xf6\x15\x8c\x7f\xa8a\xb9u\x1fØą\xd0\xf3\xc0UЎ\xbbI\xcdv\xb7,N\x9f\x061_\x8e\x11U" } leaves: { key: "CNI=" value: "\x0btF\xfe^)\x01\x18\x8fx\x8b\x8e\xf4\n\x05e\x98\xa9 (\xf2p\x04PqK\xbd\xfb\xddĪŽ\x92" } leaves: { key: "CNM=" value: "\xe6oB$\x189\xbd\xf1\x81T\x8c\x82\x1f^Q\xa7/\xe5{\xea~e\x13Kys=\x01\r\x13W\xde" } leaves: { key: "CNQ=" value: "\x9bvw\x00M\xe0g1\xaaV\xc9O\x04\xf9\xc0\x8f\xdeG\x8a[\xfdi\r\x1e\xefW\xe2+\x92\x98\xe0O" } leaves: { key: "CNU=" value: "#ŲĢD\n\xa2>\x11\x84B\x95\x8f\xe8=\xd97\\\xbc(\x15{5\xcf\x1b)\x19d\xe09T\xae\xb2" } leaves: { key: "CNY=" value: "\x05+\x82\xd0)\x84B#\xf1\x8c*\x1e\xa9\xd8\xef\xebW\xc5+\xee\xa8C\xb5H^>\x9b\xba\x9e\x822\x8c" } leaves: { key: "CNc=" value: "\x03\xf2\xc0\x00\x93y\xe5\xca$\xda\xec\x08\xa8\x8e\xd5\xc7G\xcdKh3`\xccc2_5(e\xb6=\xd0" } leaves: { key: "CNg=" value: "Û°\x01d\x98\x02\xfd{-\x84\xf1\xd3&Z\xcb#\x18\xaf\xa2\xc2\xea\xafJz@2\xb7\xf6F|\x90S" } leaves: { key: "CNk=" value: "\x95\xd2h\x05\xb0\xa3\xa4N\xa3\x18<\xa0mv\xd2sKY;\x87\xdf\x187!X)\x07,SkÎŊ" } leaves: { key: "CNo=" value: "\xec\xa3JH}S\xd5fR\xe6\x81/\x1bÄĨz\xbe)6\xd2I\xd8\xca\xf6\xd5$\x89\x9bQS\xb9\xc7" } leaves: { key: "CNs=" value: "\x97\xf0\xf0,\xf9\x0fx\xdbk\xe2`%4\x83 j\xfd\x84\xea\xaa\xc3\xe8mb\xa5\xbb\xd0+&Đš." } leaves: { key: "CNw=" value: "\x0cW\xe0\x96C\x9c\xce]\xac!\xd0\x01\tc\x82\xd5z$\xe6W\xe0&l\xdfO%\x86\xc9\xd92F[" } leaves: { key: "COA=" value: "lד\xf3\xf0m^k\x05g\xaa\xfc\x1cf\xecVr\xe4\xe5\xf4\xb5\t\xff\x99\xfa\xc70^\xa9!\xc8d" } leaves: { key: "COE=" value: "\x19į´‹\x9e\xb9\x18\xec\x02I\x0c_\xbd+l>!\n\x134\t\xa4\xce7o\xe2\xd8\xf4\xedR\x0e\x01" } leaves: { key: "COI=" value: "\xc6\xfd\xc3^\xd6\xc7\xcd \x0cx=\x0e\xa4,\n\x99\xc5\xe6\x0b\x96\x88\xc83Ų•^\x88[\xf6\x98@y" } leaves: { key: "COM=" value: "CN\x8aM7\x97\x9d\xc1f3\xd2\xea\x19\x85\x02c\xa7\x1em\xac\xf8\xcd5\x17\x05C\xfcF\x0f/v\xf0" } leaves: { key: "COQ=" value: "\xde\x15\xd9\xf7\x99e@~\x9c\x18\xcf\u0085\x86\x9eYB\x8b@\x81\xe8\x99Ō‡\x03\xb1/Õŋ^\xea\x1f" } leaves: { key: "COU=" value: "=-\xcc0o\x87J\x997\x06\xcc\x1a\x89\xb1\xe6/yQ\xa7z\xaf1e\xc4\xea\xf7\x7f\xb0\x82+\xaa\xe7" } leaves: { key: "COY=" value: "\xa7n\x02\xa02z\xfd\x07\xb1\xf2&\x82qI\xf7\xc8I)l \r\xd5\xd0Yt\xa7\xdf\xf4`jHG" } leaves: { key: "COc=" value: "V\xf2\x8cČĻ\xed\xef]\xf3\x88\xf3\xe9\x94\xc9Ëž\xb8\x07\xe5\xa8o\x9b\x99\xf6\x8c\xb1ʓ\xa6\xc0\xc9R" } internal_nodes: { key: "A0A=" value: "K\x1a\xbc\x93\xb8[20.Ō–`\x9d\x12\x1f\x90W\xa3Ȥ~\r>\t0\x02\xfd\x07\tL\x03y" } internal_nodes: { key: "A2A=" value: "\xde\xfc\x05\x94\tȨ\xc4\xe7\xbbLNzmS)\x96@ˤ\x80\x10\x8b\x8c\xd84_\xfe\x8c\x0cI\t" } internal_nodes: { key: "A4A=" value: "\x0e\xdb{<\x9d\xa2JxRV%\x1e\xa2\xaf\x04\x86\xa8\xb3\xc3\xf2\x8e2\xe0\x9a]$|\x87\x94hN\x05" } internal_nodes: { key: "A6A=" value: "\x80McU6=\xc0\xedIwU\x83\x81Q\xed*H\x04Ds\xa0\x16\xa1\x9c\x9c\xe9\xb0,_)\x82\x98" } internal_nodes: { key: "A8A=" value: "Æˇ\xb80\xa1\x95\x08\xae\xa5\x1cx$\xe8cJ\xe1tl\x9f\x12\xdd\xce\xc8D\xbf\x08@CדYD" } internal_nodes: { key: "AQA=" value: "\xb2\nE\x80\x9e\x88X\xb4\xe5\x14\xa7ic\x80\xd1b**.u\n\xfd \t\x90e\xae\xc4\xee\x89Q\xdc" } internal_nodes: { key: "AgA=" value: "A:\x85Z\x8aÄąqBÎē\t9\x02\x0fF\xe9\x91s\x84\x07\"\x08\x08ÅŽ\xfe\xea-\xf6\xe8R" } internal_nodes: { key: "AkA=" value: "W-\x9fEy\\\x93;ʆ\xdby6\xba\x8e\xd8vY6\x9a{LxUw9\xab\x8e\xc7qn\xa4" } internal_nodes: { key: "AoA=" value: "\xb1P\x17A\x02\xe2\xe40\xff\x16q\x9e\x1d\x91\xf6\xf0\x0e\x81\xf6\x9a\x81\xd0\r/\xac\x0e4]?3\x12r" } internal_nodes: { key: "AwA=" value: "\xf9Χ\x1d\xf4?ZŲļ\x82\xfc\\g\xc9\xf2eyÄ­\x03\x0eI\rX\xc5\xdd\xd4\xf7\\K\xd0G" } internal_nodes: { key: "AyA=" value: "\xec{\"\x0c\"[U\x0b].R\x18\x83(\xa2\x1b~dU\x10\xa6\tyŲŖ\xa4-\xd4\x11\xb6\xb7\xbd" } internal_nodes: { key: "B+A=" value: "\xe4\xab\xe2ą]\xb5U\xdeSAB)\x0c~E}\rS\nÅš\x1b\x1cNX|$\xb0\xf3:n" } internal_nodes: { key: "B+I=" value: "\x0f\xf5\xcebĘŽ\xb1F\x02\xf7\xbd\x9c\x82d\xb2\xd3X\xf1\"ÖŽe\xd1`\x0f=h\xd2ŪŽz\x9c" } internal_nodes: { key: "B+Q=" value: "\xa87ߘ7]\xa2\x06\x8d\n\xd0\xc26\xab\x0c&\xbb\x15?_\r\xb0\xf8\x0b\\\x94N9\xfat*\xac" } internal_nodes: { key: "B+Y=" value: "A\x9a\xdb{\x97\xd7\x1a\xe6\x99\x03q\xb3O\xfdS\x16\xa4\x01\x08]\x8d^\xff\x885\x84\x05\xd3ÕĻŅŊ" } internal_nodes: { key: "B04=" value: "\xb6\xa0\xd7SŌ‹[\xfc\xe3\x7f\xf9V\x94\xa1t\xee\xa2|\x01\xe3\xa3T\x01\x7f\x92\xfbR\xe1\n\xd4\x1f\x8e" } internal_nodes: { key: "B0A=" value: "\xfe7\xb1\u009b\xb4\xf1\xa4\x13'\x136N3\xe3g\xf0ɟ/\xa1\x9c\xfb\xf9Ųŋ\xaeĶ•R*\xbd" } internal_nodes: { key: "B0I=" value: "\x9f\xfa\xb6\x87,<\xb8\xab\xab\xc4\xe91z sTj\xaf\x02\xf2}\x0cĖŋ\xfb\xde\xed\x86Îē\x96K" } internal_nodes: { key: "B0Q=" value: "\xc8x\x0c\x91w\xba$\x1b\xcc\xe3\x97\xd4\xe2R|R\x01%\xb3\x9emWV\x0f\x0e\x1d\xb4\x80J\x14{a" } internal_nodes: { key: "B0Y=" value: "\xc3\t\xbb\xeb\xe1\x16O\xa2\xa8\xd7\xf4\x99\x97(7IY\xa3\xf5\x0c\x0b\xa8`\x18\xff\xfa\x81\xbb\x80m9\x83" } internal_nodes: { key: "B0g=" value: "\xf6[3\x9e-\x08\x87!\xabd\xed\xbbܯ\xa7>\x9cp=g\xfe\xa8\x01\xc4E\xdaԍ\x99\x8d\x1d\xfa" } internal_nodes: { key: "B0o=" value: "\x8c\xfe;K\xe8iҰp\x11p\x9f\xd4\xf0\x82\xed\xe4#\xdd7\xc2\xfdH\xf8\xa4߇|\x99\x05O\x1a" } internal_nodes: { key: "B0w=" value: "S\x87\xaa,\xe9\x8d\xdcd8\xf4K֝\x87C\x17W\x9dK\xae\x9b\x01\x90\xba\x10k\x01\xa9\xc5Ŕ\xd9" } internal_nodes: { key: "B14=" value: "N\x12\xb2\x110\xa33'\xb4\xd6\xe0\x91<\xb9\xad\xf2\x1d_\xd1S\x8d\xed\xae\xc93o\xe0ɟ\xc49\xcd" } internal_nodes: { key: "B1A=" value: "\xdbrn\xb8\xfeaDO\xb2@\xfaO\x17\xf2qr\xe1rT\xb3\xbbČĒWd\xcb\xe7w\xeb\xb9a\x9f" } internal_nodes: { key: "B1I=" value: "D\xc5=$h\xb5\x7f\xb9\xebq\xa1\xf7\n\xf9\xb0\xd5\xec\x80T\x8ew\x0b\x82\xb6y\xa3\x1eå˜ŋ\xe2h" } internal_nodes: { key: "B1Q=" value: "\xe0u\xd5\x12\xa7\x05gßĒ\xc9\xd5B>@~tv\x03\xc0\xaa\xa3\x9aC\xf8mΊ\xff7Z\x1dE" } internal_nodes: { key: "B1Y=" value: "/\xe2L\xe3\xe59\xc9^\x02\x9cV\x9d\xc4Ywa\xb53\xbf\x16\xb7\xa4\\\x84\xdf\x12 \xf8\x99\x9b$\x11" } internal_nodes: { key: "B1g=" value: "\x19\xf9>\xb8\xa2\xbc\x01\x07\xda\x10\xbdB\x15\x87g\x1d\x90\x863YR\xfb[\x9a\x14\xcdY\x17U\xc33N" } internal_nodes: { key: "B1o=" value: "f\xbb\xbb\x8e\xed\xc0]`\xff7j/B\xce\xe1\xd6\x0c͜v\x96\xaaRp\xa9\x01Įēs\x10\x00f" } internal_nodes: { key: "B1w=" value: "1L\x01\xb8\x94\xe58\xf3\xb5n\xc4P\x9d\xbfiãĢ?\xba\xcd\r\xb9Wۑ=\xac\r\xc3\x01/" } internal_nodes: { key: "B24=" value: "~\xca#\x0e\x9cf\x15c\"\xddcL\x8a\xc9|M`\xbd4]\x88\xb6II\x03\xa4C\x91\xc2劚" } internal_nodes: { key: "B2A=" value: "Įˇ\xb4\xabGd\xe8\xb1\x05\x05J\x03\xc7#s\x82L/\xec\xec\\\x05\x17\xc8į“ŒIz\x9b\x8c\xed" } internal_nodes: { key: "B2I=" value: "\x07{\xc1\x10\x8a\xaaE\x9b\x9c\x15\x17d\x8e(\x0e`\xa2bi\x0f\x04\xed(u\x1c\xf1.\xf4\x7fTr\xa5" } internal_nodes: { key: "B2Q=" value: "8\xd8b|\x8d~\xd05\xaf\"\xa1n\xd0y.A=\xaaԕ \xb1\n\xfdN\xba,\xae_īĸ‘" } internal_nodes: { key: "B2Y=" value: "$3\x02\r\xcf\xf2\xacؕ\xf1\x06C\xf7\xdav\x9c\xc5D\r\x15;l\x13.&\xe8'\xa1\x1bd@c" } internal_nodes: { key: "B2g=" value: "ی\xe4p\x18\xe0\x8c\xef\xf4D\x13S\xc82m\xfb\x94\x9c\x0b\x94\xc1q1\xdbrH\x98\xbaz\x9bV\x0e" } internal_nodes: { key: "B2o=" value: "y\xe7\no[\xd6z;D\xaa\x03\x01O\xed\xd2\xf1^\xccy\xc3\x07pI\xe5\xb0\x17p\xd4\xdbk\xe7A" } internal_nodes: { key: "B2w=" value: "\xc8T\x8a\xbf\xcf.\xc8\xd1o\xfde\xd4'm\x85\x11\xe5Rp\x1dT[v\x9c\xac\xe3\n)N\x9b5\xfc" } internal_nodes: { key: "B34=" value: "\x8b$-\x85\x9c\xd6{\xc0d\x9b\x8c\x91\xb9\xf1\xf4\x18\x0b\x8e\xf5\x1f\x97\x8c\xf4ŅŦ\xc1Xv\xdaWnH" } internal_nodes: { key: "B3A=" value: "\x85\xab|\xa4\xb1\xc6\xddL;}\x88\xa1n{\xe1sa\x18\t\x7fG\xfa#\xd6\xccM\n\xc7BT" } internal_nodes: { key: "B44=" value: "ȇtG\x1f`ÉŋԐ\x02\x06\xb8\xc2\xf8q\xfam\xc1s2\x91\"\n\xb4\xca\xc8R\x7f\xf8\xd36" } internal_nodes: { key: "B4A=" value: "\xa8[\n;d\xaf\x87E%\xa8qÛŋ\xbb\xa0\xa3\xdayv\xb0\xf1\xee<\xa3z\xe8\xe1\xca\xc4ZJ\r" } internal_nodes: { key: "B4I=" value: "/Xy\x10Y0!\xd7\xe0\xb1\xfc\x8b\x7f\xae \xa1\xd1\xe8؈U\\\xc9\xfd\xe00)u\x0f\xef\xdd\xda" } internal_nodes: { key: "B4Q=" value: "1\xc3C\xa7v\xfc\xe3&.B\x81\xab\xad\xcd\xe5\xb6\xed_\\\xc5\xc7rA\xcb\x15ś\xafyG&\xf6" } internal_nodes: { key: "B4Y=" value: "\x8c\xd0R\xcb\x15\xcd͟q(&\xb3[d\x173V+(\x11R+\xa7\x12HE\xec\x18\x97\x0f\x93\xcf" } internal_nodes: { key: "B4g=" value: "\xf0N]\xd4\x18\xa2\x00OM\"\xab\xcd\x059:\x9cO/9*\xd7#\xf6.\xb5e\xd5X\x86\xdf?\xfa" } internal_nodes: { key: "B4o=" value: "M\xc3\rk\xebS\x1e\xf4\xa6\xfcu$:\xdb|\xbe\xc9\xdb\xeeH²\x86z\xc4\xdek\x00{ËĒ5" } internal_nodes: { key: "B4w=" value: ")G\xf0{y$u4WC\x8ck\xcb>\x10}\xc8\x0f\x98\xfcր\xa0\x9cu\xb69i\xfe<\xe2\x01" } internal_nodes: { key: "B54=" value: "'x\xd1bĐ­E\xc3\x06\xea\x13J\xff\xb2Zo\xb4{\xe8\x10D\xb2&jGx}@1[Z\xfe" } internal_nodes: { key: "B5A=" value: "\xe2\xaf%8g\x13I\xf7%\x87\xdf\xd8e\xa7\xddj\xe4n-\x00\x0co>\xc3\xf1\xbc|U2P\x1d\x1c" } internal_nodes: { key: "B5I=" value: "\xe3\xaf\x04iq\xab?\x817\x0e\xfc~\xa3L\xadN\xb2,\x17\x9e\x87\x1f\xf7\xfe\xfe\x14w\xd4y\xb4\xcd\x04" } internal_nodes: { key: "B5Q=" value: "u\xfc\x0cZ\x15\xc7VJl\x99\xde8\xadz\x9d\xe2\xdd\xe0\xf8\xac\x08\x99Ęŧ^\x0bۂ,\x13\x0b\x15" } internal_nodes: { key: "B5Y=" value: "\x9f$\xb6\x97\xe7\x95JX\x15\x89\xb4\x97\x95jU\x7f\x1d\x9c\xac\xf2\xf7CM\xd3]e\x89\xb2\xf3\xdf\xd7X" } internal_nodes: { key: "B5g=" value: ",\xf0\x0fx\x0c\xb2\xffbc5C/\x9b\xa4~\xfeHN蚌Z{\x8dFX\x91\xb1\x9f\x82jn" } internal_nodes: { key: "B5o=" value: "΍\xe5\x0b\x1fs\xee8\\\x16NÆ´\x14\xb1PIH\xd5EFR\\\x08\x8e\x81ŌŽ\xa8U\xe5\xe6" } internal_nodes: { key: "B5w=" value: "\xf0\xedV/\x9e0\xba\xa2-2\x07\xf4\x14`Tc\x9c\xda\x1f\x98p\x1bX\x90.m\xa24\xc4\xf4D?" } internal_nodes: { key: "B64=" value: "\xf6,w\x9cH\xccdl\x1d\xa3p\x8f\x16L\x06\x98\x9e3\xff[\xcfN\xb1\x16\xd4q\xd6b\xfdy\tb" } internal_nodes: { key: "B6A=" value: ",\xd7\xde\xc0\xee\r\x1c\xe5\xb7\xf8\x96\xef\xf5\xc8,\x97\r\xbe\xc8\xe8\xc0\x1a\xc2\xe8g-\xfaY\xb9\xd5X\xac" } internal_nodes: { key: "B6I=" value: "\xa8\x9b\x17\xf9\xc5p\xff\x88c\xd4@\xb2\xc2\xd2\xf3p\xe9\xbe%O\xfb\xd3n2\xc7R!\xf5\xc1D\x1bE" } internal_nodes: { key: "B6Q=" value: "\x92\xbe5Ũļ\x8a\xa7\x92=\xce\x14/\xcdk\x0b\x99W\xe0\xaf$@\xb7\xec˕Y\xc5\x01\xcb\xea<0" } internal_nodes: { key: "B6Y=" value: "C\x89f\x9dfU\x96\x97\xcc\xd33\xec4w\xea,G\xf2\xc8\\\x88\x0e\x07\xe6߄zK\x97/\xbc\xf9" } internal_nodes: { key: "B6g=" value: "\xc8u\x97\x98\x19\x9a\x1b\xdax\xc7>`\xc9\x17\xac\x87\xc36\x8e\x1b}#\x0b\x16J\xd0U\x10\x16\xac\x8dU" } internal_nodes: { key: "B6o=" value: "\x88^\xffy\x07}I\x1f4\x98eTH\xe7UC\xc0\x10:\xddԔ\xd3P\xad\xe2\x13hl\x0b\x14d" } internal_nodes: { key: "B6w=" value: "6\xc7\t2\xb0\xf5\x95\x19a\x81V\x9a\x85\xef\x0f){J\x98\x0c\xbc\xaa1|{\xcc\x16\xb6a\xd7?\xba" } internal_nodes: { key: "B74=" value: "}X\x9b뚾\xc0\xf8Y\xa4\x8a)\x96\xa0\xb8ÚĄF\x14{\x9eĮ¤\xd3|yV\x95\xa5E˛" } internal_nodes: { key: "B7A=" value: "\xfd\xe9V\xc8\"\xbby\x1a\x9a\x8f\x07a\xd5\xf62\x02e\t\xceY\x0e/\xedŲĸ\x8b\xaeŨˆ\xd5\xe6\xa5" } internal_nodes: { key: "B7I=" value: "\x9c\xa6c\x86'\xca(gXP9p\x97\xc4#a\x81\xae\rwZQ\xe2\x0bz\xef\xe8)\x1b\x8f\x7f(" } internal_nodes: { key: "B7Q=" value: "\x01\xb5E\x14\xa2B\x90dlğ<\x12\x96\x9e\xdf\xe9B\xea2\x7f\xce\xe7\xca\xc75ˀ\x0c\xa1Éž" } internal_nodes: { key: "B7Y=" value: "'\xa4^\x12\x1c\xd9\xc8\x02\x0c\x13%\x03\x12a鑲\xdbT\xffa\xb8\xe4~\x05\x8e\x00\xbd\xdc\x14\xf3\xdf" } internal_nodes: { key: "B7g=" value: "\xc9>qu\xaeÛ§\xa2/jd\xdc\"\x82\x1f\x7f\x00\xefD\xbe\xd5\xf4YI\xecbC7r|0\xb6" } internal_nodes: { key: "B7o=" value: "\x0c\x99`\xdeMF\x1fbO\xf28 \xaf\xb0\x8a\xdd˙xs\xb5\x92_Ķž\x81\x96` }\x80e" } internal_nodes: { key: "B7w=" value: "%\x9d\xb0\xb87\xbe\xc3\x1cU\xac\xf1\xc1\xbbo͆\xe8\xf2AT+\xec\x10\xa41\x19\xfbK\xe8\x83\xc1\xff" } internal_nodes: { key: "B84=" value: "\x01҉\xefpt\x88h\xb9\xa5\x19\xb5\xcc^n\\\x8b\x0fc{Q=\x06Ũ…hy]\xb1%Ū‹" } internal_nodes: { key: "B8A=" value: "\"?\xf0 \xef)(\x9a=\x14\x06g\x16|\x1e\x93WiÕĒQ\xbe9>́\x83b\xdcÕē\xde" } internal_nodes: { key: "B8I=" value: "B\x8aߍb\xbd\xfa\xc5?\x8c\xdai\x13\x1bp\xa7m=\xf7N\xc4L\xbd\x7f\x13\x87P4\xc9Z\xc0T" } internal_nodes: { key: "B8Q=" value: "$J\xa7\xc9\x03r$\xbf\xb6\x96,\xe4\xa7\rqlA\x01\x0cC\x8aÜž\x9c\x06T\xee\n\x8d\xccL\xb3" } internal_nodes: { key: "B8Y=" value: "\x13T\xed`\xbb\xb2y\xd6 \x82#\xf3a\xd5oy\x01\x0c\xc3S\x89\xa4\x8e\xb3Ī”\x8d\xdcT\x91\x9b\xc0" } internal_nodes: { key: "B8g=" value: "\x1e\x04B\x06\x17\xccM\x13\xf9\\K\xb1>\x80\xee\xadx\x0c\xb6\x10\xa0o\xf0\xd6\xec\x8ce\xfa\xf1Æ­\xe0" } internal_nodes: { key: "B8o=" value: ":]Z\xe3\xda!o\n\xf2\x8c\tK\\l\x1fp\x85ËŦ\x05X!\x9d\x1a\xf6ל\x88\xe3\xb5T$" } internal_nodes: { key: "B8w=" value: "\xbc\xfd\xd4\xcb#*^\xdd>\xab-Q\xaarjfjj\xc5W\xdc\x0c\xf1H\xf8\xc9UIB\xbfI\x02" } internal_nodes: { key: "B94=" value: "\xd2\xef\x02\xe8\xe4\xab\xfd\x9f\x1a\xb2\x89\xee+\xf3\x86\xdb\x1d\xe1ۖ\xbdg&\x08s\xaf\xd9\xef\x0f\xb4\x88\x03" } internal_nodes: { key: "B9A=" value: "%\"\xd9\x12t\xf6FD>;u\xa8\x91\xbbL\xd8Q\xd5\x1e\x89\x87R\xe5\x17Z\x92\x01\xf4\x06\x82\xdc\xef" } internal_nodes: { key: "B9I=" value: "\x89\xabz\x0f.0B\x99\xf7\xf8\x8b\x02%\x86P\xf4{(\xae\x91\x146X\x93\xae>_\xa3nߗ\xcf" } internal_nodes: { key: "B9Q=" value: "\x1fb\xfe$\x93\xcbd\xff\xe7QEX\xec\xdd\xe4\xcd|\x1fv\xd65\x99\xfdX\rY\x91Sd\x9c\u0082" } internal_nodes: { key: "B9Y=" value: "g\x90\x8f\xc1\xb0\x97A\xe1\\fk\x16Zi\xe2\xb6{Ō¤\xc0Ø˛\xa7\xb1\xc9\xd6\x1c\xa2`?\x10\x84" } internal_nodes: { key: "B9g=" value: "\x01\x81\xf7\xf1\x9c`%4\xe7d\xf4\x7f\xa6\xedØē\xd6(\x84\xd7;\nR\xc8z\x19(O\x85í\xe5" } internal_nodes: { key: "B9o=" value: "\xfe\xa9\x8aK\x9f\x88\xd6SF\x89\xe6*\x96\xce\x01\xd9\xc8\xe1\xb8e\x19×ģ\xeb\xe0S\xcc7\xd3UIc" } internal_nodes: { key: "B9w=" value: "a\xcc\xf9ظwV\xfb@X\x81\xbb\xf7q\x98\xe3F7\xad:<\xec\xe5$%x\x1f\xe7\x03\xd3\xc4\xd2" } internal_nodes: { key: "BAA=" value: "ÕĻ\xa5\xa5\xa6X\xc1R\xc3Ę´25\x184nU\xbaP\\~ĖĩX\xd1\x1d\x82#.\xaa\xe07" } internal_nodes: { key: "BBA=" value: "\xaf\xa6a/\xdc\r[\x16\x8a\xbb\xd3\tՖËŋ\x14\xed5\xaf]\xae\x92\xde\xcf\x02\x98\xaa\x06(\xd7;" } internal_nodes: { key: "BCA=" value: "\x94\xec@,YI\xf0'\xaab\x04\x05\xfbU\xc7U\rN\xa4\xee\xfd\xa9n\xc5={e^\xc27Ûŗ" } internal_nodes: { key: "BDA=" value: "-A\x08oq\x8bΉ9`\xd1\xeeߤEp\x13\x04\x9d@\xea\xb2\xca5\x98&\x96\xe8 \x96\x04\x97" } internal_nodes: { key: "BEA=" value: "\x0b#O\x04%w\x95\x8b\xa4\xf7\x86c\xe2\xf4؜\x10\x12\xaf\xfcXl\x83iT_\x9d\xebo\xd9L\xd2" } internal_nodes: { key: "BFA=" value: "\x98\x0e\x1e\xf2N(\xc6\x7fQ13\xdf\xda]\xad\xf4\xe6J#\xd6g\xf7\xd3(\xfb\xb8\xff\x9b\x8aH\x86M" } internal_nodes: { key: "BGA=" value: ";!uc\xbb#\x99\x8e7\xb8^\xb3\x92\x8di^E\x98>\xcd\xd4ȁ\x8a\x1a\xf3\xa3×ĩRJ\xf1" } internal_nodes: { key: "BHA=" value: "%\xb3\x8e\x87\x01x\xf6\r\xae\xe3\xf5\x8c\xe1]B\xcaM\xc3Jp\xa4zSw\xf3\x93\x1d\xbe\x1aNG\xb0" } internal_nodes: { key: "BIA=" value: "\x07\x95\xbc[\xab\x80L\x84\xa3\n\x1b\x81\x05\xab\xe4<\xf8sKiŪ \x14\x11\xd9m\xb5E\xda\xeae\x1d" } internal_nodes: { key: "BJA=" value: "'H\x0cd\x8aײM\x1c\x0e}\xfckܞ˪\x13\xf6ã‡Ŧ\x7f_\xedT\xeerܔ\x82\xa1" } internal_nodes: { key: "BKA=" value: "C\xa4\x91\xf75rJ\x831#\x87\xc0\xa7\x06\xb8&5\x15zŲĒ\xc1ËĻk\xba)\x0c\x13$2\t" } internal_nodes: { key: "BLA=" value: "ā/\x13:\x87d\x8c\x16\xcc>>\x93\xe1\x15\x1f\xc3\"P\x83Ú´Y|\xfd\xbb\xe7\xfcb\xf5'\x8c" } internal_nodes: { key: "BMA=" value: "X\xf1G\x1d\x88\x81<\xf9\x93\xaa/JF\x05\x97&\xb2\xde\xff_\xa7\xb2\xf0\x8e\x11\x8cE\xeb\x01=z\x1e" } internal_nodes: { key: "BNA=" value: "\xad\x0eŨž\xee\xea\r\xebD0\x8a\x0b\x1f\x1eŨŠ\x1a\xa4\xb5\xb1\x81\xd0xa\x9c\xf0\xfe\x99ex\x9d\x04" } internal_nodes: { key: "BQA=" value: "\xe37\"\x98\xe6Yd\xa9N\xed\xcbÚ˛\xc3\xc0)\x87s\xdd\x01\xff\x10^\x00m\x81\x98)<\x15\x8b\xb3" } internal_nodes: { key: "BQg=" value: "i\xd0B\xe1*\xe1H\x18\xb4\x8f\x18\xf7\xf2\x85Š\x9c\xdf\x1fp\x8d\xeb\xfa\xad\x12\xb7\xb7\xa4\xc2\xf9.\x15" } internal_nodes: { key: "BRA=" value: "\x01ZT\x18\xee\x1d\x86\xb8\xc7\xd2l~mk{\xfe\xbc\xe1\x11\x8c\x1a \xdf2\x00\xec_\xbf\xed\xe2\xd8\n" } internal_nodes: { key: "BRg=" value: "H\xd7,?O\xffE\x97\"*=\x7f\xf2'P\x1dA\xf1\x82\xa7\xf9\x8b\xdd@t|\xe9\x82-\x94*\xea" } internal_nodes: { key: "BSA=" value: "}\xfc\x87z\x8cZ\x84\x01\xc2xM\x81\xa3\xf3\x06\xca\x0e\x1d~\x84h\x99\xebL\xfe=\x98\xbe\xa2\xce\xf84" } internal_nodes: { key: "BSg=" value: "^\xbf\xf7\xdbu\xcf?sÛ¨T~\x9e]?9\x9b\xc0,\xa7\xaa\xcc\xe3\xf7\xa5p\xe1\xfeg\xfcp\x8b" } internal_nodes: { key: "BTA=" value: "S\xc2\r\x10\x1da5\xeeJjf\xc3'\xef}<;<\xac<\x8a\xa0B6Īļ\x1aoęŧˆ\xbd" } internal_nodes: { key: "BTg=" value: " F(&B\xab\x9d@t,\x82\xf0\xf7=\x99\xf9\nl\xb9\x83ڇ~\x03\xbb\xd4#\xb4\x16\xa2\xe2\x08" } internal_nodes: { key: "BUA=" value: "\x97\xe0\x86V\xd5]\xbb\xac\xd86\xdc\xc0\x12d&\x16\xc8ÃĄq84Np\xac\x05\xb3\x07\xae0\xb0\xb2" } internal_nodes: { key: "BUg=" value: "\x11\xbe\xb2-vuHL\x9b\x1a\xf1\xa2|\xcd\x0cI!\x08\xec\x84hW\r\xfc\x1f\x896_P\xceBf" } internal_nodes: { key: "BVA=" value: "\x16\x02\xd4\xec\xa9\xfd\t\xd7\x05H1\x1a\"N\x9dr^\xd50\xb3\xbe\x98\xae\x96!Q\xb8_\xa4\xb7\xb6\xfb" } internal_nodes: { key: "BVg=" value: "5W\x98P\xfe\x125'\xb9K0\xcf\x08\x17\x92\xdb\xfb\x04\xda^\xf6\x03K\x04B\xd9ifHZ\xb6\xfe" } internal_nodes: { key: "BWA=" value: "o\x1eؐ!\xe1\xcfį€Ģ\x8c\xe9E\x1c\x89\x1f\x80;\x05\xe0Z\xe0\x97N\x7f\xb8G,~I\x8eQ" } internal_nodes: { key: "BWg=" value: "\x83\x00\xc8\xef\xc5\xf8\xa9\x1f\x8d-\xb6>k\xf9\xde\x0e\x05\xa0\r[\xce71K\xedC\xa4\x87\x83\x9fZ\xd7" } internal_nodes: { key: "BXA=" value: "\xc7\x1eU0IV\x1aɧck1yo\x156\xc2\xf6\xd9\xd6CYR\xb3\xf0Y\xbd\x90\xb0\xed\xf53" } internal_nodes: { key: "BXg=" value: "\xd1BJcTwk\xb0\xe7y\x98\x14\xfc\xba)\xcf\xc9K\x1at\xf6D\xbf\xe7\x8d\xfa\xdd[\xf9x\xf1\xda" } internal_nodes: { key: "BYA=" value: "b\x86\xf3\x10\xf1\xc3A\xa7zX\x15\xbb\xfa\xcf\xe6t\xd2\x17Cw\xc1\xf9ÜĨ\xc26W\xf3Į—\xe0l" } internal_nodes: { key: "BYg=" value: "\x05B*\x01\xa0,+\xe6\xe1I\xe5\x1a\x1ck\xc7ak\x073\xc6V!\xaa\xd0\xcd\x10\x80\\\xd7,[\x9f" } internal_nodes: { key: "BZA=" value: "\x978\x8b\xcem\xbcA\x92\xa5',\xa3\xcd'@0\xc8`\x11\x81\xbf\x84$\x14͔\x9d\x89v\x18\xe8{" } internal_nodes: { key: "BZg=" value: "\x14\xc4ȕH\x9e\xcc\xfd\xd4Z`؛Ũ\xa5\xe9\x84\x1a\xf7\x16&\xee\xc3\x0cA\xf5g+bI\x11" } internal_nodes: { key: "BaA=" value: "^\xe3\x88QX\xc3(g\x9a\xd0\xc60;\x0c\x0eh@\x13ΏP\x8e\xf9\x86\xf3\x84\x1b\xb1!\xf0\xf1\x7f" } internal_nodes: { key: "Bag=" value: "Z-\xb2\x82)KM\xee\x99\xd6+\xe5\x94j\x00\x1aw\x19\xae\xa4\xc7DŅ­S}?8\xaa\x03\xbe<" } internal_nodes: { key: "BbA=" value: "/FY\xb4\xffnW\xac\x0f\x13|u,\xdd}\x9a\xe7\xcb\x01\xf8\xbc|\xd8\n\xcdR\xa4\xd5\xf7kl-" } internal_nodes: { key: "Bbg=" value: "o\xdf\xcb/ęŦ•m\xc5\xdf\xdf.\xbd\x17\xb4\xdf\x14\x15\x1d\x98bC\n\xa4]G\xd6sZE0\xe1" } internal_nodes: { key: "BcA=" value: "\x1bq.Z\x98N\xa4\xf7\xaa\xa9\xe2Ý<\xb2El>O\x17=\xc9\xc6\"\x8e9\xf9\x7fF\xa5\xd6\xf4" } internal_nodes: { key: "Bcg=" value: "\x1c\xeeq\xa0\x92-Ũ†\x9d\xb7\x95\xe9\xf8\xeb\xf1\"\x87Úģ\xa3܀\x88|\xea\xa4\xe1\xa6va\xe3D" } internal_nodes: { key: "BdA=" value: "\xc2\xe8\xd0\xecJ\xad\x1d\x88\x82\xd4`\xe2$\xf6\x9a\xd9ni9\xe3\x0c\x86p\xceb\xbc\x99\xbf\xeb~\xcb\xcd" } internal_nodes: { key: "Bdg=" value: "\xfc\x96\xc0c\xfek\x1e׊\xba\xa5\xaa5\xb34z\xc4\xd6\x07\xbc\x0e}kkK\xc8\xc0ÂžĪžl" } internal_nodes: { key: "BeA=" value: "\x01\x0f\x92\x91\x1f\x1bF\xe6\xfc\x81\x12\x95\x99HS4\xa0\xad\x1b\xa7\x91\x90r\x10z\xd26ey,\x95}" } internal_nodes: { key: "BgA=" value: "\xec\x86&v\xd5C*\x15\xb3\xec\t\x89_\xf1*\xc1\xf02Ō°\xbdk\x08\xb0x\xfc\xdfČĨ\xeaÜž" } internal_nodes: { key: "BgQ=" value: "g\r\x1b\xe9\x97}*\xaeN\xbf\xa8{!\x9aJsx\xd4\xc1j\xe2\x0e:\x88Z0e\xe1&\xdd\x16H" } internal_nodes: { key: "Bgg=" value: "b\xfd\xdeQ\xc1\xde8Ri\xbf\x13\xdf-l\xe0\xa0T\x7f\xb3\xfb!\xb5\xbabEVR=\xfc2\xe5\xeb" } internal_nodes: { key: "Bgw=" value: "S\x7fX\x84\t?ې\t{\xfd\xd1s|H~r\x00\x99X \xf0\xcanC\x99p+\x14X\x9f\x16" } internal_nodes: { key: "BhA=" value: "1\x01\x9d\x05N\x80΂\x99M\xe1\xda\x00y\x9c\xe7\xd9\x1eMN^\xae\xab\xb3\xdf\xec\xbfVv\xbf)\xb4" } internal_nodes: { key: "BhQ=" value: "\xa9\x06\xaf&\x96\x08\xeeJb*\x1f\xfd^\xbf\xeet\x8f\xb1G\x06\x7f_\xa5'\xb13M/\xde\xed\xf1\xb7" } internal_nodes: { key: "Bhg=" value: "2g&\xe2\xd2T\rY\x84\xb7\x94\xad_0\xbe\xb8\x84\xef\xef\xd0\xdb\xdcP\x18\x1b\xea\x01ZW\x94G\xa4" } internal_nodes: { key: "Bhw=" value: "JNv\xd8\x15\x8e\xe5/!\x06\x11D\xf4]\xe2[^~\xc68\xd7r]\x1cį…šDhR\r\x84" } internal_nodes: { key: "BiA=" value: "\xc6\xda>\x88\x98\xbc\x15b\xf0a\x98\xa3j\xab\xd0ČŠ\xe2\x9e\x18\x92\x94R\x8e\xd9\n\xbc)\xedOK6" } internal_nodes: { key: "BiQ=" value: "\x84\t\xb9\x15\x1d\xe2\xeb\xc9C\xc1\xc9\x05\xfe \x7fF\xb4M\xe5\xe1\xe2\x00cy\x10K\xfb\xcf@W:^" } internal_nodes: { key: "Big=" value: "\x8a\xe2>\xe5\xcbaÃŋ\x05X\xb8t\x94\xa7;\x12\xe76\x15i\x97o\xf3\xe2x\xa9\x9b\x01{\xa3\x12\xfd" } internal_nodes: { key: "Biw=" value: "\xe6\xca\xc3\xea\xd34\xfe\xb6w\xf9o\xe6\xbd\xf5\xff\x81l%kŲ¨U\x8eY~\x1a\x00\xb1\xe2>\xbd\x99" } internal_nodes: { key: "BjA=" value: "Лo\xafF\xc6UR\x87\xe1E\x8f[AZU{,\x07\xe8Vw\xf1\xe0\x93\x8a\xa3d\x9b\xffX\xce" } internal_nodes: { key: "BjQ=" value: "K\xb8\xa1K/\xaf\xad\x96\x8b\x9d;\xc9[\x9b\xad\x8bp\x9f\x9b.\xbaV\xba+\xa3\xdcW\x9d?^\x81\x07" } internal_nodes: { key: "Bjg=" value: "\x1b\x16GvG\xc3\x0b͚\x0eh\xa4\x92UL\xff֞lB\xae\x87\x04\xfb\x9e\x11|\xcdR\xf5R\x13" } internal_nodes: { key: "Bjw=" value: "\xba\xa3uËĸ\xff2\xddc'\xfaHD\x14\"w\x02\x0eK\xa3\xb9\xb2\x00\xa9}t\xb2Xzw\x9b\x9b" } internal_nodes: { key: "BkA=" value: "\xb9\xa5\x7f\x89O*\x0f\xa18\x1f\xf8E\xea\xab\x16\xd4Čŗ\x1d~\t\xfc\r\xe4)\xea\x0402" } internal_nodes: { key: "BkQ=" value: "S\xf3)\xe5oX\xd8J\xe1k\xc7gc\xab\xaa\xcae\xc6\x1f\x11RU\xdcz\xfe\xa7\xd1\x19-\xe1\xea;" } internal_nodes: { key: "Bkg=" value: "\xdd/Tײ\x02\xe5\x12\xe2\x1e\x18RG\xf5\xbaH\x93\t\x95U~\xabZ\n?N\x0fZ1\x8e\xb8\x88" } internal_nodes: { key: "Bkw=" value: "\x9cs\xef\x89N\x94/V\x1e(!5wc%\x00v\x9e\x1f$\xd1P/\x8a`=G\xad\xe4L@\xdf" } internal_nodes: { key: "BlA=" value: "\x08y\x88w\xad\xa35U\xf3s\x00 *&M\x0c\xb1\xed\xf0\x08\xa3\xad{m\x9fC(\xbe\xa4TVG" } internal_nodes: { key: "BlQ=" value: "\xfa)\x06\xdco\xf0b\x84x\x0bw\x9eD\xeeK0I\xae\x87C_x\x96\x16\x9d\xfcM\xfaď\xb5\xce" } internal_nodes: { key: "Blg=" value: "\x9a\xe39\xb1\xe5\x802q\x9d;^\xb6GP\x82NH@\xc6Z\x02G+\x80\xa5Z\xbe\xc5\xddn:\xc8" } internal_nodes: { key: "Blw=" value: "WD\xaf_w\x8b\x0e\x86\xa1FR\x9f\x7f\xd5\x19ČŖ\xf5b\x13\xa1\xf4\x1f\x82\xe4\xf9<\xc5\xcbėŊ‘" } internal_nodes: { key: "BmA=" value: "\x15\xd6l\x92\xd7$*q\x9f\xbf\xd6\x7f\x88\xb0\xc2`\x1d\xc2y\xc3\xfe\x82bÔĨ\xc4\xe9\r}i\x97!" } internal_nodes: { key: "BmQ=" value: "\x12[G\x15\xdf\xf1\xf8y\xbe]\x86\x88\xd1v\t&\x87đŦĩĄ\xbcD\x9aB\xf4\xaeԚ\xb7<\xaa" } internal_nodes: { key: "Bmg=" value: "\xea\x95\x17\xb1:\xe8Q\x9e\xbc\xe2~3\x7f$\x90\x15\x9e)\xd9ŪĒ\xd7\x1a5\x9evŲŽ\r\x0b\xa6\xfa" } internal_nodes: { key: "Bmw=" value: "|&\xa6KΊÛĩ)\xc9\x15\x95\xffÆąyâĩ™\x8f_O\xb4\x89\xc1\xd9\x15\"\xed\"f\x07" } internal_nodes: { key: "BnA=" value: "\xad\xef\xaf\xf2c\xe9Ɋ\xef\xc4,\xb2s^\xb4\xf5\xb1\xe9=S\xea\xfb\x13\xd0\x1a\x9cK\x8bo\xd3N\xf6" } internal_nodes: { key: "BnQ=" value: "O\xdf?\xf4\xa8W\xf1c\xc3\x1f\xd9[f\xfa\xf7O\x1f\xd4y\x03$\x83\xf8\x16 Į’\xb4\x17W#\x9f" } internal_nodes: { key: "Bng=" value: "\xb8\xf0=9\x0f\x8cq5_^\x05\x0b\x1d\xab1$F\x8a@_y~\xf5\x81\xc1\xb7Q\x83Ԙ\x15\xc3" } internal_nodes: { key: "Bnw=" value: "j\x83\x93p߅\xc5ɜ0t\xca\x10\\|\\8\xda\x11s\xad?\xd8'\xd7KE\x8aa\xed_(" } internal_nodes: { key: "BoA=" value: "4\xf3ÄŦ\xa4\xdb\x1f\xfe!u\xf7\xff\xfd\x8ac\xf7\xc0\xb5\xfdb\x08\xc0 \x8c\xba\xdaC\xdfr\x1b:\x14" } internal_nodes: { key: "BoQ=" value: "\x8f\xcau\x1al\x9e\xaa\xba\x80\x94~\x93!\xf3\x03\xb0BZ\x9a\x90Íŋ\xf2\xf1`!\xd6x\xf1\x03\xec)" } internal_nodes: { key: "Bog=" value: "wI\xbeNl\xb5z\x04\xe50\x99\\\xe7\x11\xf0]\xe8\xc0p*p\xbf떷\x91MЈ\xff;\xbd" } internal_nodes: { key: "Bow=" value: "\xe6\x7f0n\x1cr\x9c}5\x07*\xa8Q1;\xe5ڕ\xff?\xe2\x88\xd9\\\x81\xbd!\x88\x0f\x1b0S" } internal_nodes: { key: "BpA=" value: "\xde\x0c\x0ft-+\t2ę\xd6\x03\xd9u\x051\xd4JH\xb22\xca\x19\xbdo\xdbHÕž\x1fD\xe8" } internal_nodes: { key: "BpQ=" value: "x\xf9;\xa9.\xf0\xb0ZS\xd7L\x88\x08\xa1\xd6\x1a\x0f\x94\xb9\xc5j\x95\n\xc0\xc1l\xc1\xe8\x14\x1d\x8d\x8d" } internal_nodes: { key: "Bpg=" value: "\x87\x9cbesc\xee\x93V}\xd09\xcd\xec\xfd\x99Q\xe0y& \xb5\xc8\x0e\x9e\xe7\xa4\xccml\xe7\xe7" } internal_nodes: { key: "Bpw=" value: "\\-\x8e\xe24\xcb\xdf\xf35AĖĒ\xf2\xb6\xf5p\xb84=\xd3\xec\xea\xea\xfd\x9a\x086\xaa\xc4U\xe6\x0b" } internal_nodes: { key: "BqA=" value: "+\xc2]i\x9bEΛa\x07\x9e\xceȆk\xd9\x16\xa1\xb3i\xcf\xdd\xffr^&\xd3l\xe8r2\n" } internal_nodes: { key: "BqQ=" value: "\xb2;\xd1#$X\xec{-\x02\xa3a\xf9`~\x1f\x19ox\x12rp\x1a\xca\xe6tC:(\xf3\x10\x02" } internal_nodes: { key: "Bqg=" value: "\xcb\xc3F\xa0'wI\x8dx\xf5w\xbf\xd48\xda\xe8t\x03\x15\x83~\xee&`\xb9@\x1c \x0b[\xea\x81" } internal_nodes: { key: "Bqw=" value: "\xb2\x10\xb5\x01\xf4\xd2{\xb4P\x05[\xcag\x06UBËĻ/~ZB7l\xecB\xb3\xbe\xb2\x90\xf9:" } internal_nodes: { key: "BrA=" value: "\xe8'J\xab\xe9\xe5\xc4(\x8b\x99\x1fo2c\xc1\x0f\xf8\xea\xed\xe6\x95#\x1e\xe5\xc2#\\\x8d\x9c\x91\x0e\x1d" } internal_nodes: { key: "BrQ=" value: "!Ì \xc7\xfbFC~{8n\xeaF\xb9X\xa8j\xd6Lb.}\x86\xd0\xd0.\x1f:-\xb5Y" } internal_nodes: { key: "Brg=" value: "\x99\xb1%aČĄj\x8f\x97H\x9f\x1e/\xa0Y\xf22\xcc\xc67\x16\x07\x1c\x85\xfc\xfd\xa6[@@*+" } internal_nodes: { key: "Brw=" value: "\xf2\xc4]q\x00\xbc\x1dV\x1a\x80\xf8xF\x05\xebx\xfe\xf3q\xb4\x12(\x1e\x82S- ıl" } internal_nodes: { key: "BsA=" value: "\x80\xea\x983a\x00\xdcŨ†\xe8ʙ\x9e\x8b\xd5Z\xdb\x15\xa5\x12\x03y\xba\xe1f\xb1\xa8he\xefh\xa1" } internal_nodes: { key: "BsQ=" value: "X\xa5h\x1d\x97\xa9?\xfd\x8aYl!\x93\xe2s7\xd7\x18da\xf9\x01\xe3\xb1\x14\xd0@\xec\x7fEu9" } internal_nodes: { key: "Bsg=" value: "\xa6\xc3\xe9\x88\x1a8g\"\xd7\x14➊l\\\xf4\xbb\xe4\x0f\xd1\xdd\xea\xdc)\xba\xc3h\x10\x88\xc5\x05R" } internal_nodes: { key: "Bsw=" value: "*\xc1\xb2p\xc9s\xa3\x1d\xb9e\xdf\xf2xC\xed7uS\x90V\x05X\xc0\x0fo\x02\xb8\xcb|S\xc27" } internal_nodes: { key: "BtA=" value: "\xf9v\xa0>\xb7\x9e\x82\xb1\xa9\xcbI\x8bÖĨ!\x8e\x9fKY\x8d\x89\xb7\xb1S\x0c\n\x96\x7fZ\x1a\x12\xc1" } internal_nodes: { key: "BtQ=" value: "\x9d\x8b\xb6y\xd6Ad\x0cQ\xf6x\x840\xe1\xcb\xf8m\x98\xa7G\xa7\xa95f\x0c\xcd\xef+\x87\x89\"\x8a" } internal_nodes: { key: "Btg=" value: "@\x81\xf4o\xdb\x00Ũ™#\x01\x145!\xb0M\xd0\xe9\xf1\x126\xe7N܍\x94\x17\xba\xf5\x98\x13\xcaP" } internal_nodes: { key: "Btw=" value: "\xaf\x80\x0cEr\xf6\xe2\xb1Ŋ\xcd;!!vaP\x02\xec@L\xdd\xf8k8!x\x92\nf{\xa0" } internal_nodes: { key: "BuA=" value: "\xef,\xf7!|&\x04e\xc3\xd7iĄ\xbbl\xac\x99\xe9\x8d\xff(\x93K9\"\xa5-!\xe9\xf33>" } internal_nodes: { key: "BuQ=" value: "|R\x0c\x01@C\xbcG\xbbspr퀰ˇ't\xdd\xf0*\x95U\x08\xfa\xb1\xf7\x9c\xf2\x197\xbb" } internal_nodes: { key: "Bw4=" value: "\x99\xbc\xce}\xcd\xf2\xfe\xb9]\xecY:P7\x05~l\xfc\xab\x066 G\xa1y\x9c\x9a\xe4\xe1\r\x89\xb9" } internal_nodes: { key: "BwA=" value: "L\xce+\\zŪŋ\x9a\xedZ\\sba\\\x80;\xd5jRЇ\x1eL\xee\nF\xc4v\xe9\x9f\xf6" } internal_nodes: { key: "BwI=" value: "|2\x9eQ\xe3\xc9\xeb\xae^X\xb2\xb4\xd4\xfb\x07\x96\xd0\x13\xe9]\x01v}x\xa5t\xbbte\xaf\xbe\xca" } internal_nodes: { key: "BwQ=" value: "\xd8\xf7\xbc\x0e\xc8EG\x98*\xc6\x17\xde\x03\xa7\x97\nGk\xbd\x0f]\xf1\xad\xfa\x00\x8cŨ†\xd4H'\xd3" } internal_nodes: { key: "BwY=" value: "\xa1\xf7}\xf9\x16A\x9a\xd1l\x9b\xeb\x0e\xffܜLęŋē\xceb\xd8u\xbf\x95\x8b\xceM\x81\xdf\xf6a" } internal_nodes: { key: "Bwg=" value: "\xbf\xf6\"l-\x9d\xc3\x000\x03G\xd2\xe0\xf5M\xd5(\xdb\xc0\xed\xa4f^\x19W\xbd\xd0\xc4\xd8r\x83\xbf" } internal_nodes: { key: "Bwo=" value: "\x0e\x90\xbaÔ¯\xf6\x1e\xf9n\xb2\xb0\x13ZB\x88A\x1b|\x80\x0c\x1b\xb9n\xd3A\xcd\x14ΰ\xaf \x89" } internal_nodes: { key: "Bww=" value: "Ge\xea0\xb7\x9c\x00\xef\xa0Z\xb9\xb6^q\x11wyFir\x1blgHV,\xa3\xd7\xedg\xf6\x01" } internal_nodes: { key: "Bx4=" value: "\xf1\x93\xb8\xc7\x7f\x80C)\xae\xe6\x84\xfe\xb9\x15{\x0b\xdf\xee\x9cF%\x1cH\xae\xba\x9a\xa6g\xc4ZF0" } internal_nodes: { key: "BxA=" value: ":\xed\xc9\x1d\xefv\x1a\xc7\x06dĮšŌĢ\x92\xf6\xf5\x06\x98J_\xb0\x82HzF\xc2\x00\x0bX\x95C" } internal_nodes: { key: "BxI=" value: "\x98@x\x8f\xac\x89C\xa6\xc5\xcb\ri\xe7s\xe2_\x9a\x15bb\xedo\xfey\x9e\x7fj\x0e\xa8a\xae\xb3" } internal_nodes: { key: "BxQ=" value: "\xa8\xe5\x9bOGŎ\xb2c꒛--\x0b\x9e\x90\xf4u\x0bښΩxs\xfd\x82\x92\xaaĖ‹" } internal_nodes: { key: "BxY=" value: "\x8du\x02jV\x07\xd5Ņļ\r\xc5d\x8a\xd2\"-\x83\x93\xf75\xdbI\xe8SD\xb4u \x84\x16I\xfc" } internal_nodes: { key: "Bxg=" value: "\xbe}\x84\xad\xf62\xf1\xe1\x93Đ\xe9\xfc\xfeQ\xd5#\x9c\x9e\"i~U\xae\xe2\xafJ\xd1C\x9bP\xfa" } internal_nodes: { key: "Bxo=" value: "\x99\x16fg\x10'\xa1\x91\x83&S\xf9+a\x90\xe4\xbf\xdc\x02\xd67w\x04\xe4^Ě\x19\x89\x96Л" } internal_nodes: { key: "Bxw=" value: ";\x9f\xf7ĶŽ\x0c˚ÚĢ\xa2\x1f\x8f}h\xd2\xfb\xfesh\x83U\x97\xfe\x86\x8c\xb1\xee^\xd0\xee\n" } internal_nodes: { key: "By4=" value: "\x8b\x17\xe1O\x0fPI\xda4.\"[{\x1f\xc8\x0bĖŽ\x90\x97\x8du\xd7\xd90,\xde\xd8~\x83\xba\x84" } internal_nodes: { key: "ByA=" value: ")8\x8f\x8b2DK\xc9j6\xb1\xec\xb5H[\x9c\x87)\xf6z~1y\xc3\xc6K" } internal_nodes: { key: "Byw=" value: "}&\x17\x18\x16\x9f\x0f0S\xe2\xd7jyo\x86\x1a\x91\xa7\xf3\xa6\xbaw\x98xPT];\t\xbf\x03\xdd" } internal_nodes: { key: "Bz4=" value: "\x0c\xbb\xce\x19\xc5\xd4q\xfc\x03\x04\xf8\xc9gJ0\x17g\xfb\xda^\x01,\xbd~\n7I\x8d\xd61\xbc\x17" } internal_nodes: { key: "BzA=" value: "\xc3'Œ\xa1\xed\x95DV\xcf4\xa88\xbb\x151\x00\xb82\xa6\x0b\x87\xabd\x9e\xb61\xb6\x10\x96F\x90" } internal_nodes: { key: "BzI=" value: "\r\xad×\xa5\xb7\xc9L\t\xbd\xd9m\x0f\xce؅\xb0U䭐\x18l\xd1\xd8\xf1a\xb6\x15\xee\xd1\xd3" } internal_nodes: { key: "BzQ=" value: "\xd9_\xfc\xbch-\xab1\x95\xd8Øą\x01\xbd\xfe1T+;\xa0\x11rb\xb4\xca\x01|\x0e\xb3\x06l\xda" } internal_nodes: { key: "BzY=" value: "\xdd\\)BsD\xf3}\xcapƟ0\xfd\xbf\xe3\xadp9\xc8&\x7f\x98\xe6)\xf2kK\xe9ș\xba" } internal_nodes: { key: "Bzg=" value: "\xdc\x00;\xb5\x9b\xe8\x14X}\x84\xf7\xfc8;oFrE\x9fT\x00ČĄáŧƒŨ€\xb35ÚĢ" } internal_nodes: { key: "Bzo=" value: "=\x86{\x07\xeb\x05\xbee\x19[\\i\xeeT\x99\xe2c\x86\x00\x93\xe1\xfdqU\x84\x8a\x90\x00\x19\xe4\x10\xbc" } internal_nodes: { key: "Bzw=" value: "\xd5u\xac\xe5b\xa0&\xaa\x92\xd9\xf6~FO\x9c\xa2>c\x80\xe3>rI\x86\xe5\x91'l\xaf" } internal_node_count: 228 trillian-1.6.1/integration/format/testdata/dump_tree_output_1024000066400000000000000000007750361466362047600250160ustar00rootroot00000000000000prefix: "\x00\x00\x00\x00\x00\x00" depth: 8 leaves: { key: "CAA=" value: "\x8auO\x06ԁ..\x8e\xee\xef\xa4\x06\x17vĖŦ\x0c\x80j\xdd\x029\x98h`\x1el\x85y\xb3\xc2" } leaves: { key: "CAE=" value: "s\x82l\xc7;\xa0\x1f_\x08\xcc\xcc\x0e\x1a\xfbD\x84\xadŅŖS{Uh*V\xbc-\xe7\xb7i~\x81" } leaves: { key: "CAI=" value: "\xbb\x91zJ\xbb\xbbR\xc7\xe0>\xde\x1e\xe5\x9fC\x11\xe5J\x85\x0b\x00\x99\xd1^\xa2\xd8RB?;S5" } leaves: { key: "CAM=" value: " .Q\x9a\xc9\xc2\x04\xf4\xf5\xa8\xf7\xad8\xb4-\xa9\xb5F\x00Ėą}.\x15\x06O\x9c7t\x8e\x1c\x99" } internal_nodes: { key: "BgA=" value: "Ūˇ\x95KB\xcc\xee\xe5\xbd[$\x9e\xfc\x9c\xad\x0c]2\x8b\xbbg\x1bx\x85\x8cI\xe3+\xc4\x1e\x0ch" } internal_nodes: { key: "BwA=" value: "\xc6\xee@\x91R\xb3TI\x03\xf2\x86B-}c:\x9aÛĸ\xef\x0f/\xdb\xf1V\x8e-\r\xcf\x17?\x92" } internal_nodes: { key: "BwI=" value: "\xb0\x14\x04\xf0\xe4`\xf6\xb3\x85-\x94\n\x84$\xd5\x021B\xfc\x84\x89C%0\"\x85\xd0\xc4C7\xef\xb2" } internal_node_count: 3 prefix: "\x00\x00\x00\x00\x00\x00\x00" depth: 8 leaves: { key: "CA0=" value: "\x9e0}ë§ļ9q\xfd\xc6\xfeI\xcd\u0097\xab\xb0\xe0y.\xc6\x0c\xc5SpΊf+Eh\xce" } leaves: { key: "CA4=" value: "\xe3\xe5kq\xcc\xec\x9a\xe7Ņĩ>\x0e\x16Ũ\xc6j\x9fS\xe7\xb4\x15S\xeeT=\xc2U\x0c\x99\xed\xd1" } leaves: { key: "CA8=" value: "\xa7\x97\xc0\x16R\x94R^s\xf7F[\xa7\xbba\xb1\x9f\xbf\xa3bj0\xe84\xcdR\xd5\x08\xc6|^\x89" } leaves: { key: "CAA=" value: "\xe5\x918\x856\x85\xee]?.\xd6\xae{\x1bw\x82\xe1" } leaves: { key: "CAQ=" value: "\x8c\xa6\xb1\xbf놛\x94\xb0Ūļ\xc9\xcdp12Ò\xb4\x8f:Û˛3ejb\xaa]\x96\xb3\x18" } leaves: { key: "CAU=" value: "S\xc1\xd1n\x89\r1)\x86\xd9\xc3-\x10\x9fm@\x00\xf4\xa2F\x81\x0e\xe3\x95\xff\xcc\xf4{Y\xc8\"$" } leaves: { key: "CAY=" value: "\x91Čš.\nx/\xe3\xca-\x0c??;\xb1r\x8fViB\x9d\x9f\xa2\xc6\xd5\"\x8bŞ\xe41\x9b" } leaves: { key: "CAc=" value: "\r\xd76!L\x05o'^i\xff\xe6\xa1\xdaD\xd1q\xafƎ\xae\xdb\xeeIߕsR>\x05YI" } leaves: { key: "CAg=" value: "DH\x1au\xf5Éĩ\x0ep\x05\x8c\xd0\xe1\x95\xc9\xeeÚ˛\xff\xf7Z \x17\x7f\xfa\x9d\xc6(\x81\x9f\xe97" } leaves: { key: "CAk=" value: "\xc8\x07^\x7fL7\xe6\x1boQ\x85x\x7f\x00\xb8\xf1\xfes\t\xd9ę¸ĢL\xc5en$\xac-\x1b\xbf" } leaves: { key: "CAo=" value: "Ђa\x90\\\x1f\xa1h\xf5\x80\xcd\xe1i\xdb\xc68\xcfŪ\xfaq\x10O\xb8\xaaÅļ\xa7\x06h\xf3_" } leaves: { key: "CAs=" value: "\xd05\x1a\xe3FT\x03\x80~\xb6äƒŋ\x9fn#/u\xa7\xb5\xf7\xfa3HÔš\x97\x88\xbdJ\xf6_" } leaves: { key: "CAw=" value: "7V\xcf\xf2\xd8\x0c\x05\xe7\xf0 _BS\xecÕ›Đ‹U\x8eR\xf1\xc0\x0fK\xfe\x0fR\xf4\x87x\xe5" } leaves: { key: "CB0=" value: "\x81\xf5\xb0\x96\xe7;\xf6\x97\xdb\xd6\x7fm\xfb\x05\x80\xd6\xff-\xe5\xc5PßĻ\x82\x07`\xfca\xf77\xf0>" } leaves: { key: "CB4=" value: "\x92KAS\xbb~\x84\x17\xe2+\xdf\x03R\xbc\xbc\x89=\x8d\x85\x1e\x7f\x82\xe1\xe7\x1akh\x8aY\xd6\xdf\x02" } leaves: { key: "CB8=" value: "\x1d\xe6f\xde\xef\xef\xef\xc5C/)\xac2\x90\xe2\x08\xf1\x13\xbc\x0f\x93\x00M\x88/}\x1b\xa4\xad\x85\xac\x7f" } leaves: { key: "CBA=" value: "\xff\xa8nT\xc5\x1f\xabr\xbd\xfe\xa8'\x19\xe5k&\x12\xd3\xe9\xf8փ\xb5\xd2sCe#\x88G.K" } leaves: { key: "CBE=" value: "j\xed\xadGL\xa9{^\x13g\xf0B\x0b\xaf\xc1\xebsG\xce\x1c\xd5H\x11&\x9e3O\x81\xa4ܗ\xfd" } leaves: { key: "CBI=" value: "\x04CZ\xa9b\x01Į›\xc44F\xf6\xc3\x17\x02\x8e\xbe\xf1\xa2,@!L\xe6\xf3\xf3\xa69\xaf\"\xd6K" } leaves: { key: "CBM=" value: "z\xd4\x00\x15\xb1\x1a \x83nC\xfe\xdaDwq%\x95\xb0>h(\xfetn&\x95t\xae\x19O\xce\x0c" } leaves: { key: "CBQ=" value: "\x08\x85\xe85\xa8I\xe3\xcb\xfb\xa7\x08\xf0\x165G\xd3\xef`\xb8k\x14\xc6\x1c\xb2\xa8\xfd\xf0_y\xa2p\xd2" } leaves: { key: "CBU=" value: "kŨ¯\xfd\x92r3\xfe\xdewL\xd4Z4-\xd4Į”\xafx\x06\xef\xd5\xf4\x17\xed\x9eHP\x08\xc6\x1e" } leaves: { key: "CBY=" value: "~Z\xb8\xc0~K\x04\x80\x1b\xba1\x18\r\xcd\xc4g\xd4\xeeskj\xc2]|{ \x82ZK\xc8" } leaves: { key: "CBg=" value: "\x17\xc2Q\x93P\xe5\xbeE\xbb\x88\x1cF\xd3g\x10\x8a\xa7\x07o\x1c\x80\xfb\x86q\x16\xe4\xd1\xcb\x1e\xab*\x7f" } leaves: { key: "CBk=" value: "\xf9\xb1\x11Č´1\xa4\xa0\xeaz\xc6;\xa2\xc1p^\xd9\xf7\x0c\xf8\x95\x91\xd1\xed\xec\x84\xf5\xb4\xbd\x8c\xaa\xe9" } leaves: { key: "CBo=" value: "\xaf.\x9ca~~\xd2UJ\xd55\xf4 d\xd9w\xd3\xf3\x82\xe76\x88\xe0\xe8KÜŊ\xef\xf3i'\xbb" } leaves: { key: "CBs=" value: "Yf\x0c\xd2\x0cb\x0bL\xb9]\x02\x15\x0eL\xb4\xbd4\xb1\xea\xa7\xe7{`k\u009d7\x1d\xc9\x12\x1c&" } leaves: { key: "CBw=" value: "\xb8\xfd\x1cr\x10\xd1l.E\xb8\xa7\xef\xa1\xfc\x8d\xf4*Õą;,L\xbf\x16\xe8['\xee\xe5DY\xfa" } leaves: { key: "CC0=" value: "\xa9\xb8\t\x11|\xf2\xf2\xfbo׋\xe9\xfb\xdf\x11=\x8bey_1M\x19\xab\x8d\xa6\x12:\xd6\"ڄ" } leaves: { key: "CC4=" value: "\xb3\x01/\x86\xff\xf5\xac\xa4\xdc\x14\xfb]a\xe4\xe8\xf6J\xf1\xc2R\x1dn\x1e\xe0\x9e\x120I\xb4\xdep\x97" } leaves: { key: "CC8=" value: "\xe1\x80\x02\xf80ᎈ*n\x1f\x8c\x0fSLŃ\x8b9\xfe\xbc\xeb\x01m\xf2ؑ\xa8/\x16az" } leaves: { key: "CCA=" value: "\x15i\xb6[~=\x81\xa7\x180\xbb\x7fu\x08\xad\x95\x86\xa0\x9e\xffDTeIY\x96y\xcaO\x9fŨĒ" } leaves: { key: "CCE=" value: "\x81Uԉ$L\x8f\xeb\xd45{\xe5#\x8f!\xba\xb1A\x10\x96\x000Įž\\t\xb1\x07(\x12\xf4e" } leaves: { key: "CCI=" value: "\xa7\x92\xd7<0\xa9e\xe0\xa9\xebe\x82\x82ˎ\x0b#\x0f\x83c\xad\xa8\x12\x92\xa7\xb2\xe5>[\xe6<\xab" } leaves: { key: "CCM=" value: "\x8e\xb1y\x01\x91ʉ\x81⤗&>.\xfe\xa7K\xee9\xa2\xef\x18\xa6\x82\x92Wk\x80f\xbe\x8f\xc3" } leaves: { key: "CCQ=" value: "\xf7\xa8\x93\xf6B\xad\xd1\xf8\x7fw\x04X\x1f\xd8'\x1e-\xfdF@v\xb9@\x03p\x98AV\xec\x931\xb4" } leaves: { key: "CCU=" value: "\xb1\x7fC\x19\x159\x1b\xe3\x1f!\x99ذ\xf65\xb8\x1f4\x11-M\x83\x95Z\xe7\xadk\xefd\x1dÉŦ" } leaves: { key: "CCY=" value: "u\x86\x9a2\xc6\xedk@\x0ei\xcei\xf7\xb2[F\x1b\xb1\x1e\xdfYX\xf9No׋\x12\\8\x9f\x9c" } leaves: { key: "CCc=" value: "c-\x8bk\xac\x91\x82\xbb\\Qy\xd9Å\xac)\x13\x8e:\x03\x1c\x006\xc6\xcd\x1d\xc0NI\xc7\xedY" } leaves: { key: "CCg=" value: "cX\xe7\xd4\xd9u\xaepKsh\x00H\x07\x7f\xba2\xa4UK\xee\xa8\xfb\xebŨ“\x03\xe4\xffX\xc0\xee" } leaves: { key: "CCk=" value: "\x1d\xc2f]\x98\x0e\xed\x18\xc0\xde\xffb\xed\x05i%\xf2\x8d\x15]\xcc\x7f\xeb j)\xe7\x12Ëŗ\x06?" } leaves: { key: "CCo=" value: "\xb5\xbc`\"\xfe\xe9Q\xa2\xe3V\x94\xedRp\xe04\xe6\x85|#\xbb\x7f3\x16\x7fPk\x98F\x16\xa2]" } leaves: { key: "CCs=" value: "Y\xfbo\x80>}a8\x98Đĸ\x8f\x1c\xba3\x86\x8e7\x01\x11{\xb8\xd37~^\x9d\xd0" } leaves: { key: "CD0=" value: "\xc9_?\xd5\xc1X]c\xd8\xea\x0fÄ Q/K@ \xcc\x14A\xff\xa6\x96.\xe1\xc5\xe1\xdd\\\xa3," } leaves: { key: "CD4=" value: "X\xa6x\xff\x1d\xa2\xb5ŲĨ\xe8\xbd7\xaeP\xe4\xfd\x1f#\xd0t\x04\xb6@\x95\xdak\xb6\xb3\xe3Ūē\xfb" } leaves: { key: "CD8=" value: "xȈ\xa7\x9c\x86\xda&\xceE!f\x86'x\x08\x97\xeab,U\x0fÜ­\xc2\xfd8\xeb\xb4\xccs\xe5" } leaves: { key: "CDA=" value: "\xeb\x16\x83\xc5w\xe5I\xf0U\xa7\xc6\xc2\xe7\x87+b\xdboc\xb0\x01^\xe0V$\x95\\-\x83}*\x04" } leaves: { key: "CDE=" value: "\x90\xbe9\x92\xd0tTOuWД\x87}>z3\x08\xea\xbcrj\xfaY\xab\xc1\xf8eW\x12\x99\xdf" } leaves: { key: "CDI=" value: "\x18,Ύ\x9d\xda1\xd69\xa2m\x80\x16Fq\xb9\xc0Ķē\x80\xdf=Y\x19L\xb38\x1b\xb4I\xeb\xf4" } leaves: { key: "CDM=" value: "\x06*\xb2k\x11m^7\xf2*\xbaK@\xc9\xeaM\xa7\xcb/^âǝÆĒz\xa4KW=L^" } leaves: { key: "CDQ=" value: "\xee\xd3l\x02M\xc5m\x83\xf3\xa6(\x14\x8bIj\xbc\x94\x0e\xda\xf8\x93\xddD<'l1\xa5\x05\x9d\xdc%" } leaves: { key: "CDU=" value: "Ý(^n\xb7\x0eta\xb3\x99\x9eB\xb7]{\xa7Jl\x00hT\xd3\xe3(\xeeUO&\xfc\x90\xa2" } leaves: { key: "CDY=" value: "g䙃\x1c^\x07,Ķf\x1c\xaf\x0ez\x97/\x03\x90\xe8\xbe\x1c\xbc\xc9\ne\x1fwc\n\xa0\xa7" } leaves: { key: "CDc=" value: "P\xf2%\x151~vv\x90NWS\xa9\xbb#\xaf\x9bT\xcb\xcb\x05Ilk\xb8\x04\x19]D\xd3t\xf1" } leaves: { key: "CDg=" value: "\x8a\xd2ĐŽ\xdd;\xabp+\xff\xe0\x1ax\xe4\x15T\xa2\xcc\xeaה\x19\x10\x8f" } leaves: { key: "CDw=" value: "\"\xb0<>\xc6\xe7\xef\x02f\xa6\xd2Ōŋi,\xfb\x0e\xd0\x07\xc7\x04\xf9\xefo*\xbeE&p+\xcaT" } leaves: { key: "CE0=" value: "\\%\xe5\xb4\xf0\xf3\xbc?\xa4;\x8aV\xf1|\xdb\x18t8\xf3IRLr\xd8ĖŦ\xb1}\xd8\x10M\x0f" } leaves: { key: "CE4=" value: "\xfa5}k\x82e\x8cW\xb0\xff\xb40\xdfi\xac\xab\xf7\xb5q\xa8\xf1r\x11?P\x96}\xa8\x1c" } leaves: { key: "CE8=" value: "\x04\x96\x05\xa9\xe9\x17\xfd\xbe\x1d\x9f\x16\xf5\xf0o}huJ\xb3\x88T@\xf7e9\xc2p_cp\xc1\xf2" } leaves: { key: "CEA=" value: "}\x00\xb3\xa4h\xea\xa3j\xac\x1a\xff\xf4=SM\x91,\x95\xf2\xd1\x1a\n\xb8\xca\xdeK.9\xea'\xef>" } leaves: { key: "CEE=" value: "y\xbc\x18!\xb3xY<\x91-ԅ\x80H\xc4\x00Hj\xaa\x17t4\x07\xa4DF\x85*\xd7,\x18\xc4" } leaves: { key: "CEI=" value: "\xfb׏\xc5\xceley\xf3[\xc7\xe4\x8d\xf0͑ů\xc1ÃŽ\xfa\x91dZ\xadB(\xc4E\xd4\x10" } leaves: { key: "CEM=" value: "@\xf6\x93b\x9b\xe3$>N\xfb~\x88\xd17\x8a>\xd4b2\x9a\x1d\xa7\xa2\x8f\xd2)\xa2\x92\xa9\x104\x1c" } leaves: { key: "CEQ=" value: "\xae\xaeI\x84\x16\xa3y\xd7\x19\xe1d\x0f\x8f\x03P,\x97g\x1f8֗7ֆ\x92\xc5@3\xbc\xea\x82" } leaves: { key: "CEU=" value: "\xb4\xc2\xfeW:k+\xe4\xc6\x1bF\x82\xa0\xb1vk\x99v\x9b\xaaX\xfc~\x97\xfb\x9f~\xec0\x05:\x1e" } leaves: { key: "CEY=" value: "\xb2Ö°\x17\xb1{\x0eVNT\x831\xa2un\xf2\xd3k\xc1\xee\xe0\xe6\x1f\xc8b\xd50\x12\xef\xf9\n\xd5" } leaves: { key: "CEc=" value: "\xd8\xe9\xb9$\x86Ũ›\xc0\xda\x11@\xb4\x15\xf6\xd3}\x7fD\xf615\xf4\xf4^=~\x02ZĖ—\xe6p" } leaves: { key: "CEg=" value: "+\xb7R\x06&<+\xa6\xd9u\xb8'5'|\xf7\xc5OhD\xc1|Äē\xa4\x8c\xc0O\xd6/\xa3=" } leaves: { key: "CEk=" value: "-\xcb\"\xad\x1f\xa3 {\xff\x7f\xaa3.\xb4jPuy\xe7$u\xfcN\x90\xf8`\xa7\x9c\x82%G6" } leaves: { key: "CEo=" value: "w{\xa7\x1e-\xd7?'o&S\xaf\t\xc1J\xf7e\x9d\x08it4\xb8\x0c\xaf\x0cЉ\x16\x9f\xb4\x83" } leaves: { key: "CEs=" value: "\x80\xcdz\xef\x0c\xb4\xe0\xb3\xf3x\xd3\x1a\xc3n \x9d\xb2!yX\xf6W\xb5\xe79}\xac-\x91KЇ" } leaves: { key: "CEw=" value: "^\x003]f<}Q\xab\x8f.7\x82P\xeb\x0fHp\x8c\x03!\xf9B\xa0\xa1\xb1\xa9\x05\xf1~'\xef" } leaves: { key: "CF0=" value: "\xf6\xcbU5\xd8\xcf\xe0\x8d\x17\xd6H\xcc'o\xb0\xef\x10\x91\x01s\x93a\xc9Tj\xcd-3\"_\xe1L" } leaves: { key: "CF4=" value: "6\xf3\x972i\x80B\x88o\xf3\x15\xf8wՄu\xfet$\xa4\xdd<\x8b\x87\xd8\t2\xa2\xed\x93\xd7\x15" } leaves: { key: "CF8=" value: "K\x7f\x03鷋\x06\xb4ĐĻB\xf7\xca\xdfBa\xa3\x91[\xb8 \x03\xa0&\xf30\x87\xd3{\x82Xv" } leaves: { key: "CFA=" value: "\xab\x16p.aY\xa4j\xbc&\xbc\xfe'\xbce5J%Θ^Kw\xfdv\xd6!LT\xa5\xea`" } leaves: { key: "CFE=" value: "\xc7\xedN\xef!)\xfd\x87[\xbf1I2\xc4&e.\x81S\xcfD+-t-\xa0\x8fJ2s\xd3e" } leaves: { key: "CFI=" value: ">\xa5\xa2a\xf0\tA\x1f\xc8\xf5\xdc\xca\xf1\x13\xc0\xe2\xe5\xde\x05\x9c\x13\x7f'\xdd\x15\x83H&g\xb2\xfa[" } leaves: { key: "CFM=" value: "\x84\x9b\xf6\"\xc0`\x9a\xb0\xbfʇ\x82\xe9\xa0\xe7\x13\x87|FI\xc1\x81\xf3\x8d\xa6\x1e5\xe2\xc1\x92\xb9\x8c" } leaves: { key: "CFQ=" value: "\x80U\xc32\xdb`\xa6\x166*H\xe9(\x80\xa9\x9e\x98\x96\x0bf\xb5@Ę”Øƒ\x10\xfcx\nOo" } leaves: { key: "CFU=" value: "v\x93\x813\"\xb2qŅ\xba\x7fhix^\xab_\xdd.\xf3\xc0\xf5\x05\xed9\xc8\x10\xd3Ô´\"\xda" } leaves: { key: "CFY=" value: "΄Zz\xe5_\xa1\x99#\x80:\xb9\x15\xa2$\xe72\x04\xd9\xc9d\x92\xfble\xac!\xd8\xe2;\\B" } leaves: { key: "CFc=" value: "\xc1\xeb\xea\xb4\xce\xf5\x0e\xc5|\x15\xbb/\x96uu\x91\x0b\xd6\xe8a!\x15\x87qC\xcb\x0e\x036\xb1\xa4\xd3" } leaves: { key: "CFg=" value: "@\x15\x0eG\xc3\xea\x17\xf1!,O\x87\x01\xebe\xb8\x08\xe4\xac,@]\x82\x95Wi\xf6:\xf6\xe9I\xd9" } leaves: { key: "CFk=" value: "\x0eG%\x84\xf1\xd34\x06\xe1ꉠ\x9e\x12\x947\xa1\xa7\\\xb0U\x10\xba\xafu&\xbf\x05\x1c\xf2\xef\x85" } leaves: { key: "CFo=" value: "\x01\x06\\\xbcj\x82':\xc6\xc1\n\x17\xc97o\\\x005" } leaves: { key: "CFw=" value: "J\x1f\x06\x1b_\x03\xae\xd7-h\xd0\x17\x14\xd0c\xd9\x16\x14m\xd3\xc0Q\xa7nŅy\xf0\x1b\xb0\x92\x8f" } leaves: { key: "CG0=" value: "R\xccÎģ\x18nv\xa8\x8c\xd9\x14\xf6\x8a\xbf\xed\xf4:*\x0e[dO\xa3\x0e\xa5\"A*\xe8\xbe\xfe," } leaves: { key: "CG4=" value: "\xe7\xcd\x1bi\xc0ish\xe5\x050\x973M4:k\x0c\x89HB9\x17\xb4\xa0\x97\x10a\xf2#\xfa\xa0" } leaves: { key: "CG8=" value: "\xf3͞=\x11}*&C\x12HH\xbb\x8b\x15\xc9b\xf3{\x84I\xa4\x8b\xbb\xfe\x98\x7f9\x892\xd6l" } leaves: { key: "CGA=" value: "\x1a\x0c\xd1b\xb1\xcc;\xf9r6čģĩ\xee\xb0 \xb1[\x90\xcf\x0cP\xefO}J\x12Q\x93\x07?l" } leaves: { key: "CGE=" value: "\xfa\xffO\xf5\xe0\xd645\x07*\x965&j\xfa\x8a\xc8\xc8p\xa3\\\x86\x9d\x9d)\xbd\x01\x98ň\x15(" } leaves: { key: "CGI=" value: "4\x80`\x0e\xaa:\x18\xces\xa6\xe8\"\xfa\xbaŌ„\xeeV\xd1\xf8Ũ {\x96\xb04\xd6m\xd5L,\x0e" } leaves: { key: "CGM=" value: "T\xdf\x7f\x90\xc5ؓ\xf2%$4\xd7\xdd\xde\x06q\xd4\xf6\xaa\xfe\x01\xfb\xe5t)\x02=\xb5\x00\xf1\xf5\x85" } leaves: { key: "CGQ=" value: "O\xfb\xa7\xb3A\x16\xac\xdcB\xde\xe3\x94O\xcc^.o\xba\xfc\xfa6\xce\x1c\x8c\xc1\x83m\x16\x08\xa1\xdc\xca" } leaves: { key: "CGU=" value: "+\xa1\xda\xdcHdl\xe7\x1b``Y^\xacĪ€Õ\xbc\xe7\xa73\xeb\x89(\x84\x97\t\x99Îĩh" } leaves: { key: "CGY=" value: "\xf2\xde6΍\xb0\x9a\xd6ŅĻ\x14\x8fm\xb2uA\x1ah\xe6Ɯ.M&\xdd\x1a(&\xebx\x0b," } leaves: { key: "CGc=" value: "\x1c5ԇͤ\x82L\xb5\xe6\xb9V|Q\xa5\xdc4$\xdeyAVr\x89\xc08(\xba\xfb\xbf\\\xe4" } leaves: { key: "CGg=" value: "e\xea\xb6ČĻ\x07\xd0\n\x81U\x07\x0c#%\xf7\xd8\x14e\xac\t\x14O\x95\xb0@\xfdf\xe3\xb4\"\x03\x08" } leaves: { key: "CGk=" value: "\xe3{T\xc8\xfd\xbct\x19\x17)\xdd\xea8\xb5Xy\xaeqa\xd5l\xf8`C\x8b\xde\xd81\x14J\x06E" } leaves: { key: "CGo=" value: "[R\x11\x147ZHT\x8d\xdf\xc5\xea\xa8\xeb\x98\xe2ܐi\r\xf3\x15~\xea\x04\x93\x8b\xb2\x0f.\xee\x17" } leaves: { key: "CGs=" value: "\x94\x04\x1b9(\xba\x1cO\xe5\x01(\xe5:\xa3\x05\x1a,ʓ5\x07\x0e\x93\x17\x88\xc2Y\xd3t\xd4\xeb." } leaves: { key: "CGw=" value: "8\xe9\x16\xa8\x81\xb6bĪŽj\x08 \x02F\x8c\xbe\x8f\xa8\x90A\x06}O\xb9\xeb\xad:\xa4ČŠB\xa7" } leaves: { key: "CH0=" value: "E\x88\x00\xc4?E\xf1\xae\xc2\x1e\xd92\x02\xe4w)\xf9\xc1\x1cQ\xc0N\xc8\xc8Z\x96\xae\x0bR\xf1:L" } leaves: { key: "CH4=" value: "{\x16%{\x85Vʉ~\x03\x97鈞(\xb3*\x85C\x01\xbc\x1bV \x05\xa4>\xa1\x16\xf6z\r" } leaves: { key: "CH8=" value: "_\xcdRf@\xdf\xeaŲ–'+\x12\x83[\xbb\x02\xb1E)~\xa4S\x8eb\xc0\x80\x86\x8eq\xa6Aw" } leaves: { key: "CHA=" value: "\x0bÕ°^\xa6\x0fD\xf2\xeeMy\x14\x02\xe6!\x1fZt\xb2\xf8\x8bX\x91^J\xf9\xaauT\xab8\xc0" } leaves: { key: "CHE=" value: "\x9c\xc6\x11q\xebl\xa6j^{\x0c\x9f\x11E\xd2[\xc5S\x15\r\xe7;Üš`*\x1d\xb2\xaf5\xc2\xcd" } leaves: { key: "CHI=" value: "W\xee\x11\x8b]\xf3\x81\xda\xc7#E\ti|\xb1TÍ­O>\xe9\xea\xd7\xc9\xc9eu\x84H\x93\xae\x06" } leaves: { key: "CHM=" value: "\x17\xd2\xef\xb3\x15\xcf\xd1'\x863u u\xc7\xe9\x1b*8\xab\xc1\xee\xba\xe11\xb4\xb5\x8d\xb6\x92\x02,z" } leaves: { key: "CHQ=" value: "\x05\xe7\x0fG\xb8\x01)z.\x97\xab\x18m\x0b\xef\xfd!V\xa0\xd0r)\xb5v\x01\xba\xf5T\xb7$B\xef" } leaves: { key: "CHU=" value: "\x94;\x9d\x0fYQ\xe7\x9by\x9f\x90\xfe:\x19|\xceo\xb0\xd24\xb2X\x08\xc3\xe3\xc7_\x99_?sX" } leaves: { key: "CHY=" value: "J\xd3\xfc\x8fw>Yrja:\xc7\xdd\xd4\xd7^{\x1b-^\xf8\xa0\x8e[\x8am\"\x86f\xd8\t\xa2" } leaves: { key: "CHc=" value: "@lx\xb0\xef\x16\u008a\x1dy\xa4?~\x1e\xce\xf0/ax\xd4tu\xd6\xe4\xaaĶĸ\x007iI\x1f" } leaves: { key: "CHg=" value: "\xcb\x10\xdbʤ\xc1\xfbc\xb8x\x1b\x9b\x13\x80,Na\t\xc0\xb37^\x89\xa6@H\xb7\xeaIN\xe7k" } leaves: { key: "CHk=" value: "-\xac.G\x1c\xc7r.\xd0?o\xd5\xf0\x15K\xad\xe4\xf3\xb3\xa2\tz!\x16\x03\xcf\xdd\xe1jn)\x04" } leaves: { key: "CHo=" value: "\r\x93\xb6]\x17\xbaGo~\u009a\xa2 \x8c[\xf7.(F\x0c6\x89\x84\xc3\x00\x8e\xd3\xe6*\xe2\xdfI" } leaves: { key: "CHs=" value: "\xb5M/\xf8z\x8fL\x15\xc0\xa7\xed\xe6͘\xc8\xd9\xd6*ɘN\x06\x03\x9cl\xe0I*\x9e\xd92E" } leaves: { key: "CHw=" value: "\xb4\xba>\xda\"\x10\x9f\xab\xa2\xd6\xcf)\x9b\xdc\n\\\xeb\xd0jW\xde%u\xdd\xee/%" } leaves: { key: "CI4=" value: "ČĒq\xdcꚅ\x95\xf6\xa9Æ \xfa_\x99\xf0jL\xb8\xe3Ƹ\x8b\xa4\x108\xa4C#nz\x8a" } leaves: { key: "CI8=" value: "2H͉\xbdtWs\xd8\xeeVtĖĨ\xb7\xebC\xdc/\t\xb5C\xa3ßĩ{\xf0)Mz\xfc5" } leaves: { key: "CIA=" value: "\xa4\xe1Z\xe8K\xa5怔\xbc\x80\xe4p\xa5#5\xbd^\r-*,\x91Z\x01\xc2>qz-r\x19" } leaves: { key: "CIE=" value: "\x0b\xa2$Yqp';\xd0\u0084\xb9\x85U\xc0\xf2O5\xf0\xf9\x18\x80\xff\x07\r\x15\xc1\xd3ÅĻ\xfd^" } leaves: { key: "CII=" value: "\x8a\xa9\x12HL\xef\xdem\xe5\x13\xbb\x0e\x87\xf8 {E\xf1\xc8E\xa8e(\xcb\xc7Ej\x19r{?^" } leaves: { key: "CIM=" value: "Z\x1d\xa0?\xb7\x83\x00w8\xc1\x94K\x83\xae\xbb\xab=1\xd8Ôģ\xbe\x1d\xc0_\xb1\x08!va\xe4_" } leaves: { key: "CIQ=" value: "o)'k\xc4')؞\xc1\x10.\xa3h:\xfaYT5ě\xb3\xc55>)u\x11\x97[\xa9\xb5" } leaves: { key: "CIU=" value: "t\x0b\x15\xd8\xe0a\x91\x8a\x92éŊ‹;\x8d~k\x8b\x83~\xea\x7fx0Z:P\xf4\xe9f\xf8\x86$" } leaves: { key: "CIY=" value: "^\xd3i\\\xe8Q\xe7\xf4\x8e\xc7\x00ęŗ•ÆĄ\xee\xd9vUO/\x1c\x1ec\xae{\xa0e\x83\x19T" } leaves: { key: "CIc=" value: "\xa6\xef\xad\xf6\xea\xe6âļ’\x96~\x8c\x99]\xaeP?q\xa4\xe10]\xec8;\x96\xa3\xcc\xec\xe4\x9c8" } leaves: { key: "CIg=" value: "\xfe\xf7vĪ…\xe7S\xf2\xcb\x1e\x9b\x92 \x10\xff\xc5\x7f\xe1]]ÆŠVt\x8alÆš_M\xc8E" } leaves: { key: "CIk=" value: "\xec\xad_Km\x9aØŖh7\xf0Įŋ\x97\xb0VȂ\xf4sz\xdaC]\tZ'\xe1[\x84V\xbd" } leaves: { key: "CIo=" value: "\xbbqse\x81\x84\x97Ō\xbe<\xa8\xd4\xfa\x9f\x152^\x07\x18\x98\x04\x03\x0c\x87\xbdK\xb0\x03\xca0)\x11Ō¨\xba\x1a" } leaves: { key: "CK8=" value: "o\xc2XN\xa1\xaf\x92\xa8:\x88!2O\xed\xc7*\xda~\x0cL\x9fI\x85\x107Yp\x97\xde\x00\x82\x81" } leaves: { key: "CKA=" value: "\xd3҈\xb0\x14X*)\xcb)4\xfb\xdc\xf5\xc9!\x98\xf6\xb7\x94\xaf\x90\x06\xcds\x0c\xe1\x07\xb5\xf9\xc9\xee" } leaves: { key: "CKE=" value: "\x96r\xcfe)\x18\x0e\xdeF\xdb#\xcc~\xc1\xa1\x17\xe5b}\x08\xb3\xb0˃\xf1A<\xeb\xc3a\xe7l" } leaves: { key: "CKI=" value: "^\xbc&F\x04\xca\xc0Z\xbc\xb3\xa3fA\xbd\xfe+\t\x9d\x91\xcfDA\x11J\xb9\xcf\xe6\xb3?\x07\x8f\xad" } leaves: { key: "CKM=" value: "\x84\x9f,\x0c\xb4\xa0ÚēU\x11e\xf5ɧ\xbe\x8ex\xfa\x14\x0b\xeb\xec\xc6\xf3gT\x8c[\x88؟0" } leaves: { key: "CKQ=" value: "ReOk\xa6\x9d\xcbe\xaa\xe5q-ȟÛĸ\xdd\nj\xa8\xd0h\xe7\xfa*\x8b\x135ŗ\x15\xa9" } leaves: { key: "CKU=" value: "\xd6T\x885\x90L\x12Dk\x05\xd1|\x08\xa5\xd5?h\xf4\xe5\xbdr\xe4\xfe\xfcV\xbf.E}߁U" } leaves: { key: "CKY=" value: "\xf9m2RB\x9d\x97#\xf9z4\"\xe1\xca\xff\x80\x12F/\xa2\xb6\xf8\xb8\t0\xd1<ØŽ8\xa26" } leaves: { key: "CKc=" value: "Xl\xc5\xdc\xce%9\x18\t\xd07WŪˇ\xfem\xf0M\xd7[\xa1.Td\xf9\nO\x1f#ˑ\xd5" } leaves: { key: "CKg=" value: "I\xb22\r\xf0\x8f\xa3\x84vOB\x14\xd9(Smv\x89V\x0c]o\xf3\xf7D\xfb\x1cd\x1b\x13\r\xfa" } leaves: { key: "CKk=" value: "{kf\xe9\x11b͆\xacU\xd6\x0f\xb0z\x1c\x0bÕ¯4\xc8\x11\x9e\xa4\x8c\xf5>\x1e\xb0\xe9\xcd#\xe4" } leaves: { key: "CKo=" value: "\x90J?+\xd2%9\x84\xfa*\xc4Whwc\x8d\xfc\xc1\xf8\xc9E\x7f\xb3d>\x99$\xee\xfcL\xcb\xd5" } leaves: { key: "CKs=" value: "+Z!\x94\xea\x96t&\xfb\xfdM\xc7#rW\xdd#\xa8\xcf\x0c43P\xaaV-\x066\xc9߸\xe7" } leaves: { key: "CKw=" value: "\xbf\xb5k曗[\x93\xdd\xe2\xc3[\xed\xad'\xc0\x883\xdch\x1f\x93H\x0f\xf5RG\xe7t\x83\x05\xd0" } leaves: { key: "CL0=" value: "Rĸ\xf2\xd4sY\x01P\xf29g{~(\xae\xf4\x19\x822r\xbd\xafp\xa9\x05PN\xf6\xbd\xe2A" } leaves: { key: "CL4=" value: "\x157ÖŦ\xc06\x14_h5\xb6`bBɛ\x9a\x1b\x9e\r\xf4yČĒ\xb2\xe0Z,\x8d#\x83\xf4" } leaves: { key: "CL8=" value: "r\x126\x1b\x18ls\x04f}5Q\x7f\xe8ŨÔ—]B\xb2ÆŦ\x8c\xbdk^\xa1\xa0\x93\x01\xc6" } leaves: { key: "CLA=" value: "\xcb?\x83\x13Įœ\xd4\x04J\x82[\x84\xb6\x197\xb5\xec\xa2\x10!\x9a}Øŋ_/\xa8\xac>\r\xb60" } leaves: { key: "CLE=" value: "\x9e\r\x87R\x12\xff1\x9b/\xbc\xce\x15\x82\xaev/C4>\x9f\x12a\xabO\xf5\xf3\xddvJ\xe8G\xbc" } leaves: { key: "CLI=" value: "\xa8\xbaV}v\r\r\xe1\xc3FЃ\x19B\x80E\xa8\xc3\x11p\xb7\xaf\xef|\xba9\xaa˩ě\xa8" } leaves: { key: "CLM=" value: "\x93\x07\xf5\x02\x9d\xebQ\x92\x02`\x02\x8b\\U\xe0r\x8cu\xae\xf80\xfb\xa7\xc7\xc8\x13'\x9b\")\x90L" } leaves: { key: "CLQ=" value: "(\x90F\xc0G\x86\xee¸\xca1\xcar\xdf7,l9\xf0\x07BD\x98\x11j\xc8H*WB\xf9\x1d" } leaves: { key: "CLU=" value: "\\9\x92\xec\x1d\xc0\xec?\xc8\xfbUŲ”\xec\xe9\x19BS\xdd\xd7\xd0\x01w2y0d\xa6dX\xc1\xc8" } leaves: { key: "CLY=" value: "B0\xe0\x0b\xc0\xff\x8dIJ4ב_\xb3r\xddo9\xceb\x02\xdc32\xf7\x11\xe4\xf29Čē\x1e" } leaves: { key: "CLc=" value: "\x81\x1f\x12s\xe0(I\xfc\x1d\xeb\x1e\x0c\xe4\x92\x19\xedy\xf9\x97\x9b\xa2s\x8f\x9a\x14{\x88D\xb4\xab\xe4&" } leaves: { key: "CLg=" value: ",$\xb6(\xab\x95\xe0\xcbW3'E\x0elcOĮšØ„\xb0Re\xeb\x03\xf9\x19\xdc\xf5@\xf7b" } leaves: { key: "CLk=" value: "\x05\x0c\x8cd\xe9\x0e\x86Ņ™\xad\xc8m~m\xc1Īŗ\xe8۟\x94:H}\xe5\xc5\xe2\xc4\x13w\xf6\xd7" } leaves: { key: "CLo=" value: "S(\x0f\xda\xfa\xfd\x85`\xc6}?\xe9%\xe0\xe4\x05(Q\x88\xac\xf1\x8f*}ZĪĨM\x93;\x0e\xb1" } leaves: { key: "CLs=" value: "\xde.3=\x16\x85r\xaa\xfb\n\x1d\x91MsÈԞ\xcd\xd9\x7f\x98K\x1d\xac\x98\xbf$vv\xe0\xf0" } leaves: { key: "CLw=" value: "\xd2Z\x98l_\x81\xba\xea\xedD\x15\xe2\\\x83@\xfe\xee\xb4\x0e\x1b\xce\x17;\x91N\x0e#\xc1\xc4\xe6~\x05" } leaves: { key: "CM0=" value: "\xfd#\xa1\x05f*\xe9pŲž\xd3n,\xc2U\xd9\xde\xfb\xf9\xedP\xbd\xc2Ũ¸\xf0\xde\xf6\xeb\x9c\x07\xdc" } leaves: { key: "CM4=" value: "Y\xdf\xf6\xcd{\x86\x91М>BE\x12\xe4\xa8e\xb3\xa0\x15\x86\xdc@\x8a\x0b\xc5A\xbao\xf5n\xdcH" } leaves: { key: "CM8=" value: "\xba\xb8\xd31\xd4\xe5L\x03LtX\x90\xb1\x8dis\xefV\xa8\x7f\x82O{J\n\xb5D]\xa3x\xe0T" } leaves: { key: "CMA=" value: "ɀ\xbe\xaf\xa3\x99$\xf7\x0cR\x1eB\x86\xb8\x10\x7f\xb3\xbb\x87&A\xdc\xfa\xf5ğ%q\xb9SM\xc7" } leaves: { key: "CME=" value: "\x03\x05#\xdb\xcd\x04(\x80ɜ\xd4_`â‚ĸ\xf8\x9d\xcfcō†ž¯Wg\x0f1\xeeL\x83\xd2" } leaves: { key: "CMI=" value: "wgb\x1e\x9d\x80o\xee1\xb5l'f\xda\x17\xd7\t'%\xab\xaa\x15\xf2\xe4\x16[.\x14v1,\xda" } leaves: { key: "CMM=" value: "\xba\xe4\xe7)~\x0b\xa8[\xdb5<\x02\x86\xcc^\xbf\xb5\xd9\xdc1\x9b\x9e\xabYQ IL\xa9rr\x89" } leaves: { key: "CMQ=" value: "|\xee\xdbߨ)\xd0Ū™`\x8f&\xa6\xbe\t\xb7~\x00\xd3)1\x00\xc7\x05\xee\xc4\xf2\x07-\xd2k\x1c" } leaves: { key: "CMU=" value: "\xd4!V8\xa6&\x8bo#CK\x01.\x99e\xef9\x19e\x00\x9d*\xe3ͤ\x81\x9f\xea\x0f\xe9҃" } leaves: { key: "CMY=" value: "\xdcW\xe9\x8bu3^\xe2) yX\x96\xb2ßT\xffp\xe7\xf18\xf6\xc0O\x85IyC\x00um" } leaves: { key: "CMc=" value: "\xe8փ:\tW\x05\xa7\x86;H\xe73\xe5\x9c\xeb\xf5\xedn\xd0\xe4\x145Y\x01\xeec}\x1fL\xd1\xd0" } leaves: { key: "CMg=" value: "\x8f;\x80\x9e\xad`c>\x1b\xd7Ds\x8d\xa8ş\x9a0ČŠ\xee\xac\xe0\xe9\xdc\x10nJvQj\"" } leaves: { key: "CMk=" value: "\xee\x13\xdf\x14\xb4\xadw\xa0~\x95\xdf\\\x1atk.JQΗ\x87\xd3\xffK\xf1\xceX\x99:ĪĒ\xf6" } leaves: { key: "CMo=" value: "\xae\xf1N\x1c\xb7\xe4\x87\xf4\xa0\xc9\xf3)U\x81\xfd\xed\x87\x00\xaf\xba\xdb\x05\xfa\x9an.ߕ\xbdA\u0081" } leaves: { key: "CMs=" value: ".Q\xeb\x0f|a\xd8卧Q%\xe3\xf3g\x0chX\x91(\x98\xf6\x9f\x04(\x9fl#2x\xf7\xbc" } leaves: { key: "CMw=" value: "\\\x0e\xa7\xdfULP\xe9|b\xad\xfeM\xbd9\xd2\x1d\xb7\x06\x7f*\xa3ՃCS\xb0q#\x04_\xcf" } leaves: { key: "CN0=" value: "\x07\xac\x00 EZ\xf0\xa2\xf4\xe2\n!94d\xd9\xf4ZĐĨ\x87Jg\x88s\xe05\xe0\xe6D`\x1a" } leaves: { key: "CN4=" value: "\x8b7\x0b\xe1\xe9w\xec_\x9b\xa5\xbd\xd6\xef\xc2\x18\x0f\xda;\xf5\xc1\xba\x98\x92\x15++Q\x9d\x05\x80ž" } leaves: { key: "CN8=" value: "\xeb\\I\x8dO!Ĥ^\xdc(v\xd4\xef\xa1\xd6Ė—\x9d\xb8\x1b\x95XA\x1a\xe3DK\x13}P\x9e" } leaves: { key: "CNA=" value: "\x85~\xac\xa7=\xa5\xfc\xd1\"\x96i=uYk\x04w\xff\x18b\xaa\xed\xe7+j\x97\x16\xed(I\x12\x05" } leaves: { key: "CNE=" value: "\xb5\xd8O\xeb\x9b\xd6T\xc5\x1b\x14p\xd2ר(\xce6L\xcd\xe39c\xfa\n,\xfb+r-^2C" } leaves: { key: "CNI=" value: "q\xc5\x06\xef^m\n\x0e\x0f\x1e\x89\xd8D):X*q\xdf\x10U:3'>\x16\xbc\xf0\xed\x12_\xcf" } leaves: { key: "CNM=" value: "4^Íĩ0\xbfiŌē\x07P\x8bb\xf3\xb3\x8el\xfc\x18\x9fs\xa3\x89\xda\x1f\x81\x84\x00\x06\xa8\x8a4" } leaves: { key: "CNQ=" value: "\xbf_=I\x0c\xbd\x9e\x9c\x88k\xf6\xea\xcc8\xa7pG\x88\x823\xc7\xc9\xfaf1\x7f\xdc\xda\xf5\x9flV" } leaves: { key: "CNU=" value: "\x16dR\xe7\xc2\xc7'n\x01æĨ‘\xab\xb8^\t|\xf3\x93\x98\r¡5CL\xbd\xdc\xc5\xf9v\xd5" } leaves: { key: "CNY=" value: "\xc6'\xd2h\xad)MԖ\x9f\xfe{o\xfcŨ¸\xe7\xbb3\x1e\xbbU\xa4x\x9b1Y\xacP\x92\xdf#" } leaves: { key: "CNc=" value: "\\_\xc4\xfb\xbb\xbfT\xaf\x91\xaa\x9eiŪ—\x86&\xa3\xe2\xf6\x06\x94\xb5\x13\xcc\x19>\xe5o\x91\xd9GY" } leaves: { key: "CNg=" value: "\xb6\xdcu\x06\xfba\x0e" } leaves: { key: "COU=" value: "\x0c0\x0f\xe7C˞\x97\xf7\xf5\xd2g\xe4]\x92\x9e4k\x1b\xb0\xdf\xf1\xaa\xc4fY\x84\x1f\xfe\x89>\x93" } leaves: { key: "COY=" value: "5\xc1)=\xff\xc1\x1b\xaa\xd5,\xb5\xe3\xc2Ûĸ\n\xd9СH͟\x98\x8dX~\xf2ZxKmV" } leaves: { key: "COc=" value: "5\xa5\xf0`n\xb8\x9d\xc7߄\xec\xd9\x0e`\x14\xecĘ´c/\x0f\xf1@\x88\"O\x8a\xf9\x06PS\x02" } leaves: { key: "COg=" value: "\xc1*`B057\"S\x0f\x16KR\x06eX\xde\xfb@\x86\x92\x14()\x1e\x9a\x8e~B7\xa6j" } leaves: { key: "COk=" value: "\x9fh\xae\xce#\xe1K\x12xŅĄc\xa4?\xc6\xd1;\xbed\xfa\xdaz)Th\xd3\\\x99\xb8u\xea!" } leaves: { key: "COo=" value: "D\x08Χ~\xb2\x1f\x1fl\xfc\xbf\xcd\xd0\xd5Zx7}\xff)Y\x1a\x90\x83\x10,\xbf0\xa0\xa4\xcb\xd5" } leaves: { key: "COs=" value: "\xb9\xa2\xc43\x0f#\xf0\xe8\x93K\x83\x97Z\xb7\xff\xbf\xf9\x8c\n\x8e\x89L\x0f\xb1А\x87\x9c\x96k\x8c\xa2" } leaves: { key: "COw=" value: "\xf98߀V\x8c\xb0-\x8b\x8a\xd2d&{\x12A\xf4\xdct\xb3Wh\xf4\x88؁ßĻ\xd4\xd5\xd7D" } leaves: { key: "CP0=" value: "\xe4[\x83\x8d:\x82i1햑\xc64\xd5.\x0f\x88W$\x1d\x01\xa7)_\x92\x93\xb2\xed\xd1\xfew\xe1" } leaves: { key: "CP4=" value: "\xca0\x82\xffE\xea\xbd\xc7ko\xfd\xf3s`\r1\xdc0\x05\x93\x8b@\x14\xe8\xa7\xc1m_Si\xe1h" } leaves: { key: "CP8=" value: "\x94\xe5'|/.\x8e\x1aM\xe8(\x8f\x8c\xf3y#u5\xfd\xfaD>\xff\xc1\xe1\xf8$\t\x04>OJ" } leaves: { key: "CPA=" value: "\xf9o\xc4Q\x81\x1c\xc8\xf4\xe1l1\x15n\xcf#(\xb8\xec'\xff\xc3P\xdfS\xfb\xcd\xcbh\x92|\x9e2" } leaves: { key: "CPE=" value: "J\x94\xe3\x04>\xc9\x14\x92\x9f\xa4\x07\x1c\xd0\xd3\x04QW\x0f)\x8a:\x8c\xa2.m\xbb\x99xi\x16!\xe0" } leaves: { key: "CPI=" value: "\xa7\xf4\"\xdc\xd7M\x82\x02\xc0\xaf\x0e\x11^9\xeb\rp\xea\x82 \xbc\x01\xe1u\xac\xcc\xd3]\x1b\xf6\x95\x93" } leaves: { key: "CPM=" value: "h\xb5\xb3\x87\xbc\xad\x8a\xcf\xd3\x1b\x81\x03\x91\x90K\xa0z\x81<\t\xf3\xd13\x9d<\xc5F\x06<\x81\x06\xbe" } leaves: { key: "CPQ=" value: "\x81-\x0bu%Ê\x9d\xfb\xd2\xcb(\x1an\x9e\x0cq\xf4\x18\x9cG\x8eK\x8f\xb4p\x8bڟt\xa2\xe3" } leaves: { key: "CPU=" value: "\x1e\xd2ŨĻ\rSC\xbf\x10\xdfOĖŽ`\x83\x14\xef\x83r\xfb\xc0\x82y,\xfb\xe9\xf0\xc1bB\xb4\xae" } leaves: { key: "CPY=" value: "\xb0|v\xc3@\ts -\x90\xe6\x1c\xaf\xb5f2\x913\xd3Ty\x80\xab=\x187l\xc5\xc7WßŊ" } leaves: { key: "CPc=" value: "y\x1a\xb0\xc7\xf3\xd0\x06\xe8\xa5\x15\x0f\xe6\x89\xc5\xc4]\x92\x05\x8f\xe6\x98\x02^W\xf5\x95\xa0ĮEb\x9d" } leaves: { key: "CPg=" value: "A\x88wg\xb1\xc7Rp\xc4\x0e\x17(\xe9Z\xb5Î“Ō \x164\xb8 \xb1\xe0" } internal_nodes: { key: "AgA=" value: "\x8f\x05[\xe4\x9bG\x8fW\x12\xeb\xe2\xd8[\x89˔m\xccP\x87\x18_\x1d.\xb66S\xa5\x99\x81\x08\xce" } internal_nodes: { key: "AkA=" value: "\"3\x03\x1d[%\xa4\xec\x90\x1b\xd1\xe19\x8d\xda8\xe5oT\xdd\xd4y~\xfa\xb5tT[艊\xee" } internal_nodes: { key: "AoA=" value: "\xc0\xe3l-:\xcfj\x92ᯅ\xd0\x14\x98M\x0f\x9bĮŒWEX\x8d\xc2;\x0f\xd7b\xa4\xb5_\xb0" } internal_nodes: { key: "AsA=" value: "\x0b\x08\x8c\x91\xcd\x11\x0f\xefh\xbdB(\xf0\x1e\xf2V\xb1i\xf8\x1d\xae\xa4\xf3\xc0f\xa3\xb7\xf4\xd8)N\xf8" } internal_nodes: { key: "AwA=" value: "\\\x99\x84e\xee\xd0i2j\t\xcc>\x8bwC\xcb\x08\xa0\x9a\xbc\xb5\xe4\x14r\x03\xb7\xc5A\x9e\x9f" } internal_nodes: { key: "B0I=" value: "\xb1k\xb7n\xcbs\x14\"ĖŦL\x0c\x9f\x96\x87w9\xc7+\x97\xfae\\A\x02]/\xb7\x19NY\xaa" } internal_nodes: { key: "B0Q=" value: "\xea|6\x15q\x14*b\xc6\xc7\xf5\x18p\xba\x1e\xd1\xf3\x1b\x1au\x8c\x02-\xa0\x1eq\xc6\x1bn\xb8\xc7\xf9" } internal_nodes: { key: "B0Y=" value: "\n\x93\xca+u&:Go\x83\x05<\xf4\xfb\xe0V\xaf\x19\xe8\x94\xff\x03|\xee\xe1\x06\xc4:~s\x87\xc1" } internal_nodes: { key: "B0g=" value: "\x8c\x8f\xdb\xcb\xed\x11p\xb4ėžą\xce\x0e\x03\xff\x81\x87\r\xb3\\ads\x8dˁ\xe4\x0c\x83\x9a\xa8\xd7" } internal_nodes: { key: "B0o=" value: "\x9e\x87Ų’\xe8\xf8\xa8\x87\xee\xd72\xda\x17C-\xa8![J\xab\xf7\x89ɞ\x156סh+\xef\x1c" } internal_nodes: { key: "B0w=" value: "\xcfb\xfem7P&\xd4Z\x18gG\x92Z\x08G7\xc6}\x15H\xf1\x84fLOM\x83\x194\x80\xf3" } internal_nodes: { key: "B14=" value: "\xe2\xbe\x03\xde5\x01\xb8|\xa7\x8a\x9d\xa4\x1b%\x022P\x83l~\xb0\x1f\xb9\xa8\x95R3\x83m\xc8\x12]" } internal_nodes: { key: "B1A=" value: "/\xc0=\x0ew\xe5\xc1k\x8c\xb4\x9f\x94]|\x98\x11\x14\xaf\xef\xedҐ\xa7\x11\xad}\x7f\x80Km\xed\xf1" } internal_nodes: { key: "B1I=" value: "\xddA\xa6\xc7\x00r\x00ab\xd0\xf9\xc4s\xa4n\x96\xee\x1e|\x89#\x96a>M\xad\x88\xd1Ԋ\x1c\x19" } internal_nodes: { key: "B1Q=" value: " \xd7\xf7\x0f\xda\xc8}\xa9<\xb1\xa7\xe3\xc4\t\xcez\xf4ÖĒ\xf5\"\x99/\xb1-\x89\x8c\xf3\xa6/-\xf9" } internal_nodes: { key: "B1Y=" value: "\x8a\xa9\xd5\x02&\xebm=\x9a\x97\xd5\x07S\x00\xdf\x07\x10sR\xa7&K\x1c\xf5\x0e9\xa3\xd1\xee\x00\xf2\x98" } internal_nodes: { key: "B1g=" value: "8(\xcdG\"\xfe\xa4\x02\xf8\x19=\x9ax\xb4H7\xdaXKy5\x8a\xde\xffhL\x18\x19^\xe9\x83\xc7" } internal_nodes: { key: "B1o=" value: "\\|RΉ\xedV\xbd5(\x8e&\xc0p\xbd\xd0U\x1c\xb9\xfa\xd7\xd1cr\xa7Ķē\xbe\xbc\xeaD*" } internal_nodes: { key: "B1w=" value: ">j\x15A\x0c\xd5_\xa4\xf2\xe98\x83\xe2\xc2z4\xee\xc5\r^q\xee8\xac{Z\x18f.z\x82\x00" } internal_nodes: { key: "B24=" value: "B/{\xe7\xdezn+\xb6\xafb\xc8ۍ\x97\xf8x\x03\xd9u\x1f\xb4\x91\xa5Y·o@\x05\xe65" } internal_nodes: { key: "B2A=" value: "2\x9d\xf5w\xdd\"\xe4$KB\x9f\x8f\xdfB6\xe3H\x93\xefZ;z\xffK\x06H\xe4q\xed\xf4\x9e\xb3" } internal_nodes: { key: "B2I=" value: "\x9e\x11\x10\xf0\xbb\x8e\xed\xe6\x88\xebEbF;\x1d\xe5\x19.\xf1\x93\xd6y\xa0\x83\xc8^X>M{\xe7s" } internal_nodes: { key: "B2Q=" value: "\xf7\xb8\x88\x03\xa4U\xd6\"P\x8f;K9\xf1\x99\xa7\x14trs\xcaipL{\x94@\xe2-\x9c\xef\xc9" } internal_nodes: { key: "B2Y=" value: "\xe7\x155\xa7\x10\x17\t\xaf\xb0{\x86\x8cG\xfe\xb5\xd7U\x1c\xcc\x06\x8f\x15{\xeaÃļ\xb5z\xf6ix\x15" } internal_nodes: { key: "B2g=" value: "\x82\xbf\xaa\xc7\xf4T\xf16\xb6\x1c\xed\"\xa7\xc8'v\xa3\xda\xe7\x94\xe7\x97>\xa36\x1b\xa0CS\xb4x\x97" } internal_nodes: { key: "B2o=" value: "\xb3\x0c=\x1e\x1e\xd4n\x98^w\x85\xeaA\xd1`lx1]\xa1G\x1d\xe5F$dį–Ģ_\x0e\xea" } internal_nodes: { key: "B2w=" value: "\xba\xe4\"\x07\x9a\x02\x98\x9fj\xcd7\xff2\xac\xa7o\xf3\x96zn\xa5z<\xa1J\xc8Ũ“=AĶž" } internal_nodes: { key: "B34=" value: "Nd\xe1\x14C\x81\xa6\xea\x17ŲŽ\xabÍē\x99\xed\xf4\x90sE\xb8o\x16\xf5\xd7ܯGS\x00Ws" } internal_nodes: { key: "B3A=" value: "\xed\x95.\xa3\xfeŅ‘xŲ‹\xb3h\xf5U\xfeF\x02\xfc\xd0q\xe1Xf0\x9b\xb2\x1d\x93\\cY\xc3" } internal_nodes: { key: "B3I=" value: "Ęĸ\xf5\x98\xda\xeet\xc2\"\x1f\x0e1\xae\xe8I\x04\x82\xcd؟%\xe5UA|\xd9S\x96?5ߒ" } internal_nodes: { key: "B3Q=" value: "\x11\xf0\x162\x0b \x13\x1ao\xd6\x02\xe0\x0f8\xa1H\xbaI\xd3c\xa6\xca\xd7D\x88L\"\x03\x9dC>8" } internal_nodes: { key: "B3Y=" value: "\xfc\x9b\xe4\xbbSLoimP\xf6\x80\x9a\x08\xc8i(\xc0\x95\xb9\x92\xe3\x87\xeb\x94d0\x19\xe1T\xcc\xed" } internal_nodes: { key: "B3g=" value: "ߐ\xaf\xc0\x04\x1e\xf9$p#\xab>\x05\xb9\\&\x8d\x83\xdaP^\xc0\x98\xf4o\x06\x97\xe7\xc6\xe3\x9a[" } internal_nodes: { key: "B3o=" value: "#@\x1c*\x9c\xbe5(Ku\x8b\x85-v\xa5\xa7\xb4\\&w\x13\xa5x\xb0\x18cHN(;\xcdW" } internal_nodes: { key: "B3w=" value: "J:\x85\xbdC\xe8\x1d\x13\xd6\xd1\x1f\xbb\xe4\xdf\x00\x10\x844p\xa0\xa8\x83P\r-e\rW\xcf\x1e~\xa3" } internal_nodes: { key: "B44=" value: "\xb1\xe1b\xac~mQ\x1d\xe9\x05\xe9\xe8\xe8\xaf|\xb1p\x84\xfb\x14\r^U\xfb\xa7\x80(\x8a?\xe5\xdf:" } internal_nodes: { key: "B4A=" value: "\xb5B\x18\xb0\xdb\xfb\xe3\x1fP\x8d\xceF\x11\x81\xb0\xdf\xd4]\xa3VrŌ´&~\xbd\xb63\x96x\xc0~" } internal_nodes: { key: "B4I=" value: "\xba⌠\xd6/WC\xf9\xa4\x0c\x0bccn\xec\xad\xcd$͡Ɍ(\x03x\x1da\xc4\xecFd" } internal_nodes: { key: "B4Q=" value: "\xbdo\x0c\xeb\xcenf\x8aF\x10^l\xc1n\xf1\xb6g\x934\xc4\x0fY,>7\x83\x19Dw\x84\xe5\x16" } internal_nodes: { key: "B4Y=" value: "F\xad\xb4T^\x90\xbd\xff\x90d\x0b\xf8\x11\xee\xaf\xe9\x16\x88\xe3\xe5\r\xc9a\x03\xb4יe\xc7Xy\xa0" } internal_nodes: { key: "B4g=" value: "\xd8\x19\x9a\x88x\xf3K\xddv\x08Me\x15\xcc:\x07XN\xea\x83.\x8bF\xb12\xdcס0u]\xc9" } internal_nodes: { key: "B4o=" value: "\xea\xea6\xfb\xcb\xe6\x03\x99؇\xd2\xd2\x03o\x1e\x8e\xa7\xe6\x90)\xb8\x16\xe4\xfe\x1dY'\xbev\xb1C\xbc" } internal_nodes: { key: "B4w=" value: "D\xe3S\xb8\x85d\xcc-(Ī“\x14\xf0\x0e:m\xe9`=\xfe}\x83\xa9\xc1\xf6\xb1\x93V\x07=\xcb\xec" } internal_nodes: { key: "B54=" value: "s\xd6\xe8KT\x0cm\xaeTs*_MD\xb5\xd03D+\x9e\x87\xec\xaf\x03\xa4\xd6)\xa2Z\xe3\xbf\xd5" } internal_nodes: { key: "B5A=" value: "2\x169N\x03?d\\\x80T\"\xfb]\xdf\xf2\xdc\x10y\x15_\xe3Ū‘/5!:\xbd\x84\x8a\x0ca" } internal_nodes: { key: "B5I=" value: "\x87\xd8 \x9a\x8fN\x8a7;\x07k\x12\xb09\x19Ō›\x955\x1c\xcc6\xf4\xd2.\xe5jâĨŒ7J" } internal_nodes: { key: "B5Q=" value: "\x08\x93ؒ\x93\x8am\x9d\xd2\xde$\x82\x94\xc1\xfa\xb7\xd3;\xdb\xf5\xabaÉĸ5r\x8e\xf3\xebo.\\" } internal_nodes: { key: "B5Y=" value: "K3\xdc\xd4\x7f\xd9~-x\xee0bɈXe.\xa6\x8f5\x89Y\xa0\xdf\xc7j\x11\xbd\xc5!\xb5\xf9" } internal_nodes: { key: "B5g=" value: "\xfbÛ\xed]H\xa45\xde\x1dm\xc9\xf8ŅˆÜ\xa2\xf3\xb0\xbd\x18\x10\xb4\x1cL9y\xde\xd1\x1a\x1a" } internal_nodes: { key: "B5o=" value: "K%\x91s\x12\x12\x10\x95E]\"\xad\x04Z>d\xef\xa2\xe8\x9d\xe2\xd5\xda\xdb\x05)\x82\x1f\x1eV\xa0f" } internal_nodes: { key: "B5w=" value: "1\x89eR\xe6\xf2\xff\xc9\xdfn\x83~\xc0G\xb7\xf0v8\x85\x00ڕ\xf2\x03\x97sZ\x9a\xab\xb6\xe1\xf1" } internal_nodes: { key: "B64=" value: "\xad\xd3 \xcd\xcd\xee\"\xfeTW\x00\xc0\x8b\x9d\x1e\x82ds\xec\x1a\x8céĢ*\xe8@\xfc\xc1k\xfeA" } internal_nodes: { key: "B6A=" value: "B\x91\x87O\xdc\xde\x14]\x9b\xd2ƌ'\x1e[\xb5\\&\xc89^Cx1\x8e\xe7×Ĩ\x94i>\xc4" } internal_nodes: { key: "B6I=" value: "\xbeD\x1avNz\x8e4MA5\xfcR\xf9\x1fF\x8d\xc0C\xb3x\x01\xc2\x17S^\x95\x05\xa0\xa1YD" } internal_nodes: { key: "B6Q=" value: "\xb1\xc18\xfbs\xb6{@\x8d\xef\x87\xd4\xfcE\xaa\xa0E\xa7\r\xbf\xe2,rB\xb9\x90Y\x14Ä\x83\x87" } internal_nodes: { key: "B6Y=" value: "\x89\xcf\n\xb2\xbb\xaae\xb4\xd1\x06iZL\x00\xe9JT\xe04Oæēĸ\xc0\t\xbc]\xf9\xeab\x95\xed" } internal_nodes: { key: "B6g=" value: "\xfa6\xde\"\x16tT\x8d \xe1\xee7`\xc9\xee\x8dm^\xb0\xec\xc8\xf7\xd1?0\xa2\xbdqO" } internal_nodes: { key: "B6o=" value: "\xb4\x93\t\x9f\x81(\xe5,m\x05\xbf\xa6\x88\xf1\x0c\x10\x8d\x8b\x81G\x17\x1c\xb4\xb3N\"\xb6\x046\x92\x15\xc9" } internal_nodes: { key: "B6w=" value: ":\x17\x14\xa2\xa2\x05\x01\xfe4\xa0\x9e\x9d\x0b\xbb\xe37\xfe\xd3+\xdb\x0eu\x7f\xac6\xac|\xf6yb.\xef" } internal_nodes: { key: "B74=" value: "u\xf5T\x835%\xa6\xee\xb1\xd3\x02\xf1|SL\xcb\x07\xe3\x80\x055\x9b\xf6\x9d)\x81\xdf\x1d\x1eZ\xa7N" } internal_nodes: { key: "B7A=" value: "BB\x01`\x17\x11\x12\xad\x8c?\xba\x80RS\x96\xef\x9b\xd74\x1d\xbb\xb7\xbb\x02\xf5\xf4M\r_\xc2\x1b\xc8" } internal_nodes: { key: "B7I=" value: "M\xb4\xae\x9c\xff\xb6\x0f\x8fQ)\xe4\xf3\xdf\xcbh\xf2{\xafL\xfa׀\xed3G=\xf3aUs\xcfq" } internal_nodes: { key: "B7Q=" value: "\x8bC\x88)\xcc\xd4\x13=\x1c\xdb\xf7\xbdŌ„\xc3R\x84W\x84\xdb\x03\n\x9c\xa5\r\x06f\xe7\xbbr\x95Z" } internal_nodes: { key: "B7Y=" value: "\xbdB\xc4\x16\xff\xc4j\r#\xac\x105\x85c^\x02bs]\xf8^\xa2\xd6!n\xc6an\xbf\xdc\x0e$" } internal_nodes: { key: "B7g=" value: "`\x18Z\x8607\xdd_\x06\x97SF\x12dw,\x98R\xa4$\x98\xd1\x18\"\xb1<[r(v\x96\x98" } internal_nodes: { key: "B7o=" value: "\xf1\xee\xe5\xb8rzmcWvF\x8a#W\x89\xcf\xf5\x83\x17Y\xcb\x12ŌŧH^+e\xf6\x96\x0cp" } internal_nodes: { key: "B7w=" value: "RV\x14\xec^.פ\x85\xc1ThIC\xff\x10'\x8bXb0\xcf\xc8H\x11^7Bf\x87\xff\x08" } internal_nodes: { key: "B84=" value: "\xf5?},$\xab\x17FØŠ\xfd\x88;\xd0J\xc9s\x06\xe2\xd1\xec\x07\xdc\xf3\xf3w\x7fz`\x0b\x7fv" } internal_nodes: { key: "B8A=" value: "p\x03M\xbd\x87\xab\r\xe4ï\x8f\x0f\xac\x13U\xf3\x1a\x85\x95T\xd1R\xd2t\x95\xc4\xd1\xfc\x99X\xe1\x00" } internal_nodes: { key: "B8I=" value: "\xccL&\x94˲\xdbd\xbek^\xce-\xe3\xd1\x0e:\xb86\x1f\x8d\xcd\xde,\xed\x9c\x07,J{b\x1c" } internal_nodes: { key: "B8Q=" value: "\xed\xd7\xedu\x81|3\x85\x1b\xc1\x13P>4\x9b}a\x1c\x11$\x7f\x92F\x19a\x90~\x05\x92\x99\xb1\xdc" } internal_nodes: { key: "B8Y=" value: "Ô´\xe4R\xde\xe4\x88\xe8Ǝ\x83\r\xaa\x1a\xbe\x1f\xb2\x99!\x8e\xcai~f\x9b\xc3\x18\xe3L\xc2\r\x07\x97Į˜&C\xac\xefV" } internal_nodes: { key: "BBA=" value: "\xf8\xcd \xe2l\xf2\xa8\xa3ؚ\xda\x12͚\x12\xa0\xbaCb\xe4\x05\xde\xc1%O(\x81\x13\x19\xc0\xc9\x0b" } internal_nodes: { key: "BCA=" value: "RF\xff\x08\x15#\xf5^w\xa9\x8e8&t\xb8\x9c\x1a+\xb7l|Ξ\xb3W\x15r\xa6R\xb6\x07\xea" } internal_nodes: { key: "BDA=" value: "FT\xc2Ř\xf8v\"\x93\xe0;\xddjF0\xed\xcd[\x00\xb9zX\x90\xbc\x1d\xb3r~}\x03TY" } internal_nodes: { key: "BEA=" value: "QÛ {~\xf0g=\xa3\x9f\xbc\x96)\x85\x05t\xd6\xe9\xb0H'\x07ZD]x\x10\xbeE\x0c\xd2{" } internal_nodes: { key: "BFA=" value: "\x1b\x8f\x04\x99y\xc7\x0c\xbe\xfc\xael \xd4%tx\x0b<$\xb7\x1f\x90E\xfc\xf6!\x88k\xbc7\xbb\xb5" } internal_nodes: { key: "BGA=" value: "\x9c&\xf7\xfc\xa6\xd8830\x1bO\x06nc$\xd87\x88l\x0b\"\xe9\x10\xfb\xb98\n\x01\xf1\xf2\xea\x13" } internal_nodes: { key: "BHA=" value: "\x84\x14\x17\xa5\xa85\xedã€˛i\xbf?\xea\xacZx?s\xc5/\x14lIZ\"\xdd>0\xcbqC" } internal_nodes: { key: "BIA=" value: "\x96\x8c\xdaN\xdc8\xb3W\xc8j\xbdn{\x8cu\x04`\x92Xi\xe3\xc5m\xfbă$\xe1\xe6\xf5\xa4\xd5" } internal_nodes: { key: "BJA=" value: "\xc2\x0c\x92W\xd9#Kr\xf6\x03\x04\xb7\xe0\xdc\xee'\xabčēƒ\xf9\x99 o'\xe0\x0b\xb3\xba\xceJ\x07" } internal_nodes: { key: "BKA=" value: "\x1aSf\xeb\xbd\xfc~\x05\xc0\x9ctV\x8b<\"}\xa9CG\xe9\x97 k\x92w\x91^\xe9Ƥ~B" } internal_nodes: { key: "BLA=" value: "2\x8872˛\xe3*TxM\xbagw\xc3\xce K\xfb\xe6z8;\xb5\xae}z\x80\xd7U-\xc6" } internal_nodes: { key: "BMA=" value: "0m$\xe3\xd6ߊoЅ\xc7\x1f\xa1_h\x1eQF\x0f\xefQ9i\x95\xf7\x11+\x9bp~\x8a\x91" } internal_nodes: { key: "BNA=" value: "&\x1bj\xbc\x91\xa9\x0f7#\x1e\xde\x0f\x00\x81\x1d\xbfM\xba^C2\xe4\xb0\x15'\xaa\xba\x19R\x1a\x07\xed" } internal_nodes: { key: "BOA=" value: "\xad\xdc%j]\xbd\x8ep\xeb\x9e\xe0,%ZM(6\xfd\x0b\xd5(n'k\x8f\xa8\xa4\x8e\xd97\xcaJ" } internal_nodes: { key: "BPA=" value: "c\x80\x1b\xe4\xe8z\xa9\x8fNP\x88>\x0c\xeb\x97s\xe8\"\xaaI\x9f\xe5t\x05!P\x1dx\xf0g\xd6|" } internal_nodes: { key: "BQA=" value: "\x01d\xda\x05q\xfc\x8f-\x10J\xec\xc0r/\xf4\x91ar\xb0$i\x032\x99\x9eɸ\xea\x1d\xe8\xce+" } internal_nodes: { key: "BQg=" value: "\xc9$\xaeʛ\xf8\xffn\x0f\x8e\xa1I-H\x1aV\xc0g\xf8\xa1l$\x03`$\x08\xfafh\xfc\x80\x01" } internal_nodes: { key: "BRA=" value: "w\x08\xeb\xeb3\xf2\x1b\x97\xd5%\x19}b\x00Lc\x1e;W\xb7?K\xaa~N\x9f\xa1i\xbc\xb8\xd0C" } internal_nodes: { key: "BRg=" value: "\xe9\xde\\y\x17\xdd\xeeL2\x1a\x81U\xe3\xcbb\xf3\x1b&\xabl\x9b\xd3;\xef]Ũžq+\x12\xfbd" } internal_nodes: { key: "BSA=" value: "\xe9\xcd^Ή\x0f\xf3\x9e\x13dt\x08\x06\xfb\x1f\xb4\x83厃\xba\xc9\xf4c\x1a\xb2pUlZ\ru" } internal_nodes: { key: "BSg=" value: "\x05\x83\n\x06\xd0b\xfcKa\xa3\xe2\x99`\x7f\x1b)2\xf2\xd6\xdde\xb0\xfe\x07\x99]\x81\xbe\x9b\x1e\xa9 " } internal_nodes: { key: "BTA=" value: "z\xc4W\x0f\xf1\x87:\xe1t^\xf0\x0f\xd0\x1e,\xddT\xa8\xba`\xac\xb5\x9c\x16J0\xfbp\xc5y\x8f\x0f" } internal_nodes: { key: "BTg=" value: "\xf8\x85\x97\xe1\x1e\xaa\x00)4hR\xcaD\xc4&Ė‹@\xae\xe5\xe6k9.F\x1d\x04\xa1?\x82\r\xbf" } internal_nodes: { key: "BUA=" value: "\x02\xa1\xfds\xfc\xb0\xfe\xc2\xd5\"Piz\xf2\x1c\x7f\xe9z\xce\xca\x1c\x89\x11\x9d\xc2=$%\x9a+!\xa6" } internal_nodes: { key: "BUg=" value: "\xcb_\xbdC\x9f2\x8d\xf5P\xc30V\xff|\xf8\x16\xfc\x8em\x96>\x1ec@\xe9X\x8aŌŽ=\x11\xb0" } internal_nodes: { key: "BVA=" value: "\x85y\x9a\xcb3\x17\xee\x85s\x859_n\x87\xb3q-\x0e\x95:\xd6^m\x80\xa3\xbd^?Y\x90b\xf5" } internal_nodes: { key: "BVg=" value: "\xa6\x91q\x15B\xdbqo\xb6&\xbb\x08\xa2\xaf\t\xf6@\xee\xff/@\xc7\xfd\xf3\"\x16o\x94\x8e\xa2,v" } internal_nodes: { key: "BWA=" value: "F\xa6\xf9\xd1ËŦ\x83\x0e\xd1\x0e\xf7\xd7F\xa9\x9a\x1b\xb7\x89\xe0\x83\x19,\x80\x88\xccŲˇ\xfa\xa0\x02\"_" } internal_nodes: { key: "BWg=" value: "\x8c\xbd\xf9F\rĔ\x96ˡV\x1b-&\x03\x06\xd0\xfeA\x16_z\x11P\xc5\xf28\xc0\xbe\xed\xe8\xbe" } internal_nodes: { key: "BXA=" value: "6\x1c\xd9\x7f\xd6IV]\xbej\x95\xaf.\x81\x8d\x13HU\xedm\xad\x05\xfbM\x16\x93Aײ79\xc9" } internal_nodes: { key: "BXg=" value: "c\x9ea\x00\xf7\xe5\x88vG\r\xbc x\xf9\x87\xee\r\\'D[\xbd00\xb0\xd7\xf4\x19mL\x91\x97" } internal_nodes: { key: "BYA=" value: "?',\xe1\xeb\x1f\x83+\xcdX\x127\xd0,V\x1c;gx\x9f;\xbeB\x12\xe1\xe5\xe1\xc2\xf1{]\xe0" } internal_nodes: { key: "BYg=" value: "I\r\x83\xae\x07\x98\xf0A\x12\x96$&\xfcU\xb2\x04IR\xa5\xff\x9drL\xc8V\xf2(\x9d,]K\x02" } internal_nodes: { key: "BZA=" value: "\tr6b$,aXA\xd9:\x9d\xf9S\x18\x1f7Ҏ\x1d\xd8\xfeu\xec\xe2\xdcá̍\x1b\x16i" } internal_nodes: { key: "BZg=" value: "\xae|\xad]\xc9\xe4\xd4X\xa5\xa8\x08-W\xd45\x18\xeeY\x07(\xe88}\x11=H\xbb\x7fErÆˇ" } internal_nodes: { key: "BaA=" value: "T\xd4\xea\x8d\xfc8\x05B\xc3\x17\x854\x85\xf2\x86\xd7sE\x86\xf9\xbe\x16>\xe5I\xddJ\xe5\xbap\x89\x16" } internal_nodes: { key: "Bag=" value: "\x84\xe2P#\xa4{\xb3U\x00\x99$zi\r\x15K\x13G6B\xb4\xef\xd7\x0e\xaa\xb3\xac_\xae.\xc3s" } internal_nodes: { key: "BbA=" value: "\xf4ˁ\xb2\xc0.%U\xc3]\x94o\xd5k\xf6\x1c\xb4\x95\x96Su\x95\x9c\xf6\xc6N\xbf\xa3+-\x87\x7f" } internal_nodes: { key: "Bbg=" value: "\x86\x990\xa4\x1e\xe9؁\xe03@K|\x88\x04" } internal_nodes: { key: "Bcg=" value: "\xf7\x18\xed\x9bR:\xb9\xa8M\x8c\xaa)$\xb6\xaf\xa6\xf8\xd68MΚ!4\xb092-\xd1R\xb0+" } internal_nodes: { key: "BdA=" value: "\xcc\x05\xcc\xfb\xff\xef\xff\x1b\xd2\xf7\r.\xb8\xfa\xedk\x0eN\x11\x9c\x03\x92\x9c{ 4\x84\x04zÖš1" } internal_nodes: { key: "Bdg=" value: " \xe8\xc9\xc7O\xff-zwÃē\x19\xa6\xef\x0e\x0c\xe7\x97éģ–\xbe]\xf9J/\xab\xa1\xc1\x1b\xef\xf4" } internal_nodes: { key: "BeA=" value: "\x9f\xacf\xe5V\xa9\xc3^\xecV᛾\xc6\x1f0NA\x01\xd7Ug'\xd20\xeb\x9e\xfd:)}\x87" } internal_nodes: { key: "Beg=" value: "\\X\x8d\xa2\xbeŲŧ\xfd\x0b\xd7h\xf4\x14\xa1\"\xfd\xa5\xc0\x04B\xcf\xc3c{\xe6+^\xea\xb6\x1b\xc8\xcc" } internal_nodes: { key: "BfA=" value: "yJn\xa9Z\xb6h\x1d[\x94\xc9\x08\x1a΋>\x12E\xa3\xe2Gb]\x8f\x18i}\x1c\xdc" } internal_nodes: { key: "Bfg=" value: "\x84\xea*\x99pv*\xd4R\x95%\xf8\xf0\xec8\xa1\x9c\xab\x00a\x85FaÛŽ)\x13.@qw\xcb" } internal_nodes: { key: "BgA=" value: "t7w\xa2>\x8a\x9e\xf0]\x92\x83\x8b\xaa\xeb\x8c\xf9uQ#\xb3؞B\xb6\x802\xe3,\xfdJM\xa0" } internal_nodes: { key: "BgQ=" value: "\xe0\x1a\xb6Yw\xa8\x91\xc1\x14\x8c\xb4\x1ed\x8a\xa8\xcc\xf2P\xcb8\x04A\xd81\xb1BŌŋ" } internal_nodes: { key: "BhA=" value: "o!i\xbf+\xd4ꐀ\xb2\xf4J7\x9c\xf6\x80{\xfe\x94\x89\x1e\x90\xcbl\x06\xa3j\xa7%&\x96\x8b" } internal_nodes: { key: "BhQ=" value: "7\xd4aR~\x12\x07'\x13\xf3P\x9eiC\xe4\xdef\x01\x9b)Ėļ\xc0\xfbKG}v\x06\xb2\x08r" } internal_nodes: { key: "Bhg=" value: "\x14\x7f\xb8iExGvg\x97\xc4u\xc4\xde\xde/az:3=W\xd9O\xec\x02\x07pj\xe44\x1d" } internal_nodes: { key: "Bhw=" value: "u\xff\xefÅž?\x8c\xd6TyJ\xf1\x06\xdb\x02\xa8F[\x93\x81\xe0\x99\x91\xee\x1c\x86\x0b\x03\x0f\x9f\x8f\xaf" } internal_nodes: { key: "BiA=" value: "\xba\x82/\xdaX\x9b\xf2o\xf8\xdd\xf3\xf1S\xf0\x1f<\xbeËĢ\x10l\xdaN\xc0\\\x8c(\x9b\xe3\x18\xabA" } internal_nodes: { key: "BiQ=" value: "h\x99e\x9e?8}\xc9\r\xac\xf2 tc\xb3.L\xdc[\xf5\xc9Ѐ$\x03h\x9d=΂<." } internal_nodes: { key: "Big=" value: "7p\xf0j\x163\xbee\x9el\xd6/\x9f\x9e\xa3z\x8d(\x9a'U\xe5\xdc\xeb\xf7\xb7\xb6\xf0d\xee\xee\xef" } internal_nodes: { key: "Biw=" value: "\xa0\xb7\xa0\xa98WLRha\xefN\xc7\x12\xb1X\xb1V\xd3\xd8T\x90\xf1<įļ‚\xd9|\x01\xaf\x16" } internal_nodes: { key: "BjA=" value: "\xa8C/\xd9\xdb\x1b\xe8\x11\x8d\xa4\x1c\xb0\x83`\x92\xe8K\xabq|\x08\xf0q1\x81\x82_@\xdd&\x19k" } internal_nodes: { key: "BjQ=" value: ";8Ў\xde4\x03\x95\x0f\xb3U=\xf6\xf8*\xd0}Oo\xe1\xc7\xfct\r\x81;=\xdc\xe5U+t" } internal_nodes: { key: "Bjg=" value: "X\xac(\"\x03x\xd7KLO1\xe9dȋ\x14\x83,o\x87\x13\x8c\xb9o1\xd3,\x8d\x9c\x181Q" } internal_nodes: { key: "Bjw=" value: "\xbc׌\x95\"Íŋ\xaf\x85]\x08\xdb\x00\x93\x03\xe2PĘģ\\|.N\xd8x\xf6P&7\xd6É´" } internal_nodes: { key: "BkA=" value: "D\xdf\xf9W\x0cc\x1fOj\x0f\xdfok\xf3\x8f\x01S\xfc\xab\xf9\x93\xc0Q\xaa1t\x9cAÕ¤\x08\xa0" } internal_nodes: { key: "BkQ=" value: "3RNq\xdb3\x13=\x07\x89\x89wo^MFZ\x07X\x1d\x92Ņ…]\x9c\x12}\xfbB$\xda\xd4" } internal_nodes: { key: "Bkg=" value: "#\x81+\x9e\x196FGA\xaaS\xf8`Īģ\xb3܌vw\x05\x12\x04\x97\xc1\x9c\x86X\x18\x05\x8bT" } internal_nodes: { key: "Bkw=" value: "\x86\x84\xebc\xd1?x\xccW\xcf\xc5^ؤ\x07\xa0\x8e\x99\xc3\xc9T\x84Lj\x07\xad.\xbc\xc5\x02\x8f!" } internal_nodes: { key: "BlA=" value: "\x123\xa5+\xf8\x1c,\x9b\xca{Éĸ\x80\x9aQ\x9c\x1ffQ\x83\x93\x12\xd5\xc2d\xcb\xe3M\xc7\xd9!x" } internal_nodes: { key: "BlQ=" value: "\xfd\xc1\xef\x16\x00p$q\x97\x0e\xe1\xf6\n\xb0\xb9s\xd15\xe7\x02LM\x14\xc0F\x07\x92<;\xa8\xc1\xb2" } internal_nodes: { key: "Blg=" value: "\xa0\xc9Ԇ\x0b\x86\xea\xe0\xbe\xd2*\xb9/\xa1\x0bM)?\x03\xd9\xddH\x1b\x861\xac\x84\x0f!o\xa4\x15" } internal_nodes: { key: "Blw=" value: "J\xed\xe5$\x967K\x13+\xea\x18\xf8b\x0c\xdcrO\xa7`7\xff\x8b\xdd+\tŨœ\xf0\xe7uJe" } internal_nodes: { key: "BmA=" value: "\x81}\x0eN\x16]\x88\xd3\xc0|\x94\x8f\x10\x8c6\xc66@l\xe8qc/U\xad\xc0P\xacy\xf21\xca" } internal_nodes: { key: "BmQ=" value: "Ô´\xa4)\x14:cPXM\xe2V\xc7W\xeb\xee\xf7%\xbe1Uˏ\xf8\xf4\xedㄏo\x1c\x9b" } internal_nodes: { key: "Bmg=" value: "\xdbeIJT\x07V]`\xe8mG\x00\x18x\x96o\\\xec\xd9*\xff\xd3E\x98\xd8B\x9cD\x07@\x0b" } internal_nodes: { key: "Bmw=" value: "S\xf6\xbe\x10\x99A\x82$\x9e}\xe2\xf6*\xa4\x14\xf9\x8dQA\x16\x92\x8c;LEŌ‘1\x04k\x18\xa6" } internal_nodes: { key: "BnA=" value: "^\x81d\x00L|)\x0c\x9e\xc1G\x11\x92t\xadvz\xa3\xe0\x1fb\xed\x84\x14\xa4\n\xf5ܙ\x13\xdd\x1e" } internal_nodes: { key: "BnQ=" value: "|\xce\xeb$[\xbd\x002" } internal_nodes: { key: "BoA=" value: "?\x8f\xff1b}\x18%r\xf4\xba#\xa6Í K\x81ÆĒ\xda\xdd\x18\x0f\x05>\xe7\xcc-G\x00\xa9!" } internal_nodes: { key: "BoQ=" value: "\xe9\xe48\x88H\xe0~\xab\x94\xa9>\x19{\xb2\xa0\xb3s-\xc8\xea\xb8g7\"\xb7\xc6J\xaf\xd2\xf2c\xb7" } internal_nodes: { key: "Bog=" value: "\xe6\x1b!\xba\xb2\xff\x18u\xae\xa3\xb1\x99k]\xb45\xf4\x03\xda\xf3\xad)\xe7J\n\xbe\x87\xa4\xd6\x08{\xba" } internal_nodes: { key: "Bow=" value: "$\xc5u\xe1\x84\x01\x95\x85=Qt\xf9å ŗ\xf6\xfd\x89\xe7a-\xb6\x07:\xd6o\x91\xa8\xd1j\xbb{" } internal_nodes: { key: "BpA=" value: "\xfd\xbc\x04\x17ËŖ)F}\x93\x9fn;p\xd7?\x9dE\x14\x7f\x00>\xf0\xbe\xa20d\x87Z,Üģ" } internal_nodes: { key: "BpQ=" value: "\xeat&\x87>w\x86\x03\x01\x1a\u0097\xd4\x08\xab,\xde%0\xb1H\xba'\xcd5\r\x97\xf1kL=\x15" } internal_nodes: { key: "Bpg=" value: "0\x1a\xad\xdeF0\x17\xa1Âiy\xf6\n\xb4\xf8Ȓߤ\xa7\x8c\xa4\xfc\n\x14\xbe\xe9\xbb0:\x11" } internal_nodes: { key: "Bpw=" value: "\xe8mĶ™@\xcfE\xf4b\xf7\xbd\xde4\xf1\xdd\xe0\xef킐\x19\x11Ffzh\xd7u\x8b6\x98<" } internal_nodes: { key: "BqA=" value: "t\xb5\xb44\x93\xa2\xab6\xcc]\x00F^\x8e\xf0nΤw#o\xdf.\xfe yf\xf1Dk@\xc9" } internal_nodes: { key: "BqQ=" value: "\xacNTzZORʓ\x11.\xa6g\xcd\xf7\xa7\x9bFY\xb4x}\xf5\xd79\xa7&E\xc8\xdf}\x0f" } internal_nodes: { key: "Bqg=" value: "\xb6\x1b!_\x19\x82f\xce\x02\x8f[v\x15pE\xbc\xc6Z\x1co>8R\xeb\xa0|\xff\t\xffo\x9dT" } internal_nodes: { key: "Bqw=" value: "\xc0\x98v\xf2\x1b\xe1b\xb7*\xc1\xceh\x87|\xc6^\xff\xbe\x03҃\xa7\xb7\x05A\xa0\x9a\xf9\xeb\xd3R\x8c" } internal_nodes: { key: "BrA=" value: "\xea\xf1\xb5+g{\xe0Ú­ l\xa8\xc9S\x1f\x1b\xf7\x16\xfb\x17T\xd0cY\xaa+\x07\x0f\t\x1b\x12C" } internal_nodes: { key: "BrQ=" value: "1\x80įĒ’\xdbL\x18|IÕŠĐ…m\x1d\r\x8b/\x9c\xa9\xaaԒ\xc5r>\x1f;\x8f2\xbe" } internal_nodes: { key: "Brg=" value: "\x88;*\xb4Ãĩ`3C\xec\xd6\xee\xf2\xad\x17\xa0\x0bj\x99C\x82x\xcd^.\xa4\x06\x10̈́t\xa6" } internal_nodes: { key: "Brw=" value: "\xfe%\xa0OJ9/ɒrw#\xb2\x14\xc0\xd3hJ\x9d\xa6\x8a\x99\x93\x9a$\xc7P\x14\x838\xf4\xca" } internal_nodes: { key: "BsA=" value: "7\xc8FæŊ™Vp\x07l=0\xdc\x19?B\x91c\x92\xc7VI\x85F\xed%5$kH\xb4=" } internal_nodes: { key: "BsQ=" value: ".\xa9\x85\xf1\xb7Q>\xff\xd3\x06\x1b:\x06\x8a\xa6\xf0@\xebM\xbfF\xec\x81\xf6\x10\xff`\xd3m\x9c\xe3\xcf" } internal_nodes: { key: "Bsg=" value: "\x8d\xf4\xa0\xec&,\x8efԊ\x8d\xb9\x91\xd8\xda\xc4\xd9\xccgwnsE:\x81\xad\xf7\x8e\xad\xbd \x1f" } internal_nodes: { key: "Bsw=" value: "D\x1f=\xfa\x86*\x16%W>\xce\\+P\xe6a\xaa\x9fhBj*\x00:\xf4\xce&\x87$h\x1b\x98" } internal_nodes: { key: "BtA=" value: "\xeb{y\xb2\x08\xcfoi\x96\xb0v\\\xcf\x1d\xa3W\xacE\xf2\xdc'P\xab\xf2)\"\xc0Vw\xe33\xc9" } internal_nodes: { key: "BtQ=" value: "\x97TpB\xfd\x1aH\xb9$\xf8\xf2\n\xe7\x0b\xfc\xb7\xfd1\xa9z\xbd\x80\xbfm\xc8\xf6Z#\xee\x1d:\xa5" } internal_nodes: { key: "Btg=" value: "i\x1b\x86\x98k\xc9\x1fp\x0f\x16D\x03\x1e\x83Ķļ~8\x04[\xac8@\xae\xe4\xfe\x0e\xb2\nl\t\xe9" } internal_nodes: { key: "Btw=" value: "\xbd\xe0;\x17D\x8a\xbe\xf1 \xfe\xbc\x94\xf7t\n\xcf-\xfc\xcd\xf9\x04\x19\xacL\xfb\xa7\x83\xd4N\xbc\x85\x0f" } internal_nodes: { key: "BuA=" value: "^\xd4W\xed\xd8\xf5#\x9cÃŋj\xcd_n\xb7\x96\xaf\x041~Ah\xb9ˆU\xe8\xd7T\xf2\x05\xfc" } internal_nodes: { key: "BuQ=" value: "\x17of\x95S:8\xa0A\x0e\xba\x14b\xda\x02\xb9\xfa^" } internal_nodes: { key: "Bx4=" value: "Ɂ\xab\x98\xcf^u\xe6Z\x8a\xf1\x0c\xfa\x84\x98˟p\xb8\xc4?\x85] \"X|\x00\xb8\xb8It" } internal_nodes: { key: "BxA=" value: "&\xba\x8a}\xf7\xb8V\x03\xb86\xa8~\xf9-;L,x\xc8\xed\x87\xd3dQ\xbf\xee6\xaa\x9bM\x9a9" } internal_nodes: { key: "BxI=" value: "\xca\x04ڞ\na\xba\n\xbca\xf9\xf4!n&\xfbÔĸ\xb2\xf8\x9b!E\x8aZe\xe5\x1a\xf8\x7f\x83\x82" } internal_nodes: { key: "BxQ=" value: "\xf6N(x2]\xa7\xdc\xe7\xff\xaa\xdd\xeb\x9c\xe3{\x03k\x9b\xfa|\xe9\u009cS\xb7\x04\x1e\x03\xd6\xc0\x10" } internal_nodes: { key: "BxY=" value: "@\xe7<\x01\xbb\xbc\xa3\xd5J\x11^\x8an\xc8H8D\xe1|\xf9K\x84\xc3k\xafq\x17\x98\x98\x1a\xde\t" } internal_nodes: { key: "Bxg=" value: "\xba?3ô„ρ͙\xef\x8a\xcf\xe7 \x91\xa9߆\xda?'5\x82*\x91\x9e\xd5\xc7\xe8\x05\xae\xfe" } internal_nodes: { key: "Bxo=" value: "\xded\xd7>OĶ‘r9\x1a\x17\xf4\xeeVy\x15:S+\xb1\x91\x9b\xd2\xfc\x02\xccq\xca\xf4#Χ" } internal_nodes: { key: "Bxw=" value: "\x1e\xa0G\x1c\xa6\xee0\xabR\x14n\xdaE1\x8b#ju\xd3{Åŗ\xef\xef\x9fHud\xd0\x00\x85\\" } internal_nodes: { key: "By4=" value: "\xe4\xceF\xc67u\xab\t\xc5\x19ܛ\xb8[\x17#\xc7\xc9$X\x0b\xb3\xfa\xa4\xb1Ũ°Ķš\xb9\n?" } internal_nodes: { key: "ByA=" value: "\xf7\x18\xdcj\x8c\x19\x06I\xaa\xaf\x1c\xd8\xe8YW\x98XP\xcd!\xa5Ũ\xcd\xea\xbf\xfe\xaf\x97\x0c\xb6e" } internal_nodes: { key: "ByI=" value: "c\xafv\xf0o\x08\xac\xb0/S\xa7L\xadi\n\xa5N;!Bdc\xa2\x96\x12Nq\xa2\xe3\xdbj\x97" } internal_nodes: { key: "ByQ=" value: "\x83\x8f\x8a\x12\xb4W\xcd]\x9d\x8a\x92\xa5\x97\x85\x0b\x1d\xee:&\x98\xaa\xef\xd1: ÛŊ.\xf6\xa4^\x02" } internal_nodes: { key: "ByY=" value: "\x0cy\x05\x1daq\xdc\xc0Z\xc3\xd2 \x88\xa2\xbc\xdfh\x8d\x1c\xf8\x85\x986\x9d(\x18\x18&-}K\x04" } internal_nodes: { key: "Byg=" value: "\xa9\xbd&\xac\x88Ėą\xa1Įž\xfd\x85\xf4L[\xf7\xc0\xed\xec\xb1G\xe0\xe0\x937@[u\x83gp\xd3" } internal_nodes: { key: "Byo=" value: "\xddf\x91W(6D\x16d\x7f\x83\xd2\x18Z\xe1\x12\xbc#\xab\x0fdo\xfc5V\xc7r\x07\x9e;\xaf3" } internal_nodes: { key: "Byw=" value: "ˍ]\xe9o~\x85\xa8Ņš\x9fmJu\x100ڈ\x9d\x1efGb\x8d\x86\xc2\xf0\xc8\"\xfa\x92\xe1" } internal_nodes: { key: "Bz4=" value: "Q\x14Iv\xbe\xbat26\x14\xfa\xfc;\xa6e\x87!\xa7'\xcct\x02o\x83\xa4\xf7\xa4\xbb\xfa\xb2\x11\x9a" } internal_nodes: { key: "BzA=" value: "\xf3\x91CFTĶž3\x01\xa6\xdd>K\x19p\xe7]\xe1\x1d\xb4\x92\xaa\x7fg\xb4\xe4\xa0\xcf\xff,\xe9K" } internal_nodes: { key: "BzI=" value: "\xd8\xe2\xe7\x1f\xca\x0c\t\xc6[\xbc_=\x1cX\xfe\xd68>\x8aX\xad)\x82(QŞ\xc7\xf4\x8co\"" } internal_nodes: { key: "BzQ=" value: "C\xa9\xf4o߲\xa7\xce\x1c\xa3\x9fq]wDf\xf5u\x99\xf5\x99\xe5\x1d7d" } internal_node_count: 254 prefix: "\x00\x00\x00\x00\x00\x00\x01" depth: 8 leaves: { key: "CA0=" value: "\xd4K\xee\xf0q\xf4\x88\xdf\xdc\x1f\xc8\xd4G\xb2%\x0b\xdb\xc5\xf8\x83qxB\xf2\x08\n\xf0\x0e\xad:X\xdc" } leaves: { key: "CA4=" value: "\x94\x1a\\\xb3\\S\xf4\x91ԟ\xf1>5\xa9a~\x98R9\xfev#\xb7\xcd\x19\xb5}KnPmS" } leaves: { key: "CA8=" value: "\xa1Ņž\xc00XA\x89k\xcc\xca\x0e\xf9\xf3\xc2éĢ‘X\x82\xfd\x05\xab\x9f_\x9aw\xb5\xc1\xf1\x0f\xff" } leaves: { key: "CAA=" value: "\x1f\x14\xa6ŅĨ\x82j\x9c\xa9\x00\\\xf4\xa2\x14E\x13z\x9b\x92(q\x82\x06-\x87\x84\xf9Ôĩ#\x16\x0b" } leaves: { key: "CAE=" value: "\x80\xfe\xbaC\xae\xe8\xa2F\"!oN!\x9b\xfc]\xd5-O\x91Ôž\x9fL\xf81\xdc\xfe,\xea(\x80" } leaves: { key: "CAI=" value: "!\x89\xa1\x13#\xa20<\xe7\xad\xf3\xf8\xe9\x10)\x17\xbcĖ­\xa2\x8ar\xffj\x1e\xf0c^\xf3\x81\x0f\xdd" } leaves: { key: "CAM=" value: "\x0fI\xc3\xf1;y\xe5G \xf4\x1c\x9d\x97d\x91j×ē\xb0D8\xc0`\nیb\xef,V>[" } leaves: { key: "CAQ=" value: "\xe2\x01\xde\xc8k\x14\x0f+>\x0cM\xc9\xf8\x1b\x0bS\xa6\x7f#&2\x07\r\x92\xc2_\xd5C\xdb᠝" } leaves: { key: "CAU=" value: "n\x9a\x9b\x17\x17垔1\x96\xb1Z\xa0\x8c\xe7\x10\xbak\xbb\x89È\xd3\xf4=g\xa4\xf6\x83\xda\xc48" } leaves: { key: "CAY=" value: "\xe4\xf1\xb2=\xb0aH\xfa\x84\xad\xfcg\xb8P\xcb\x08\"!؀\xadIe\xc3(\x89R\xaa\xa5\x92\x1d " } leaves: { key: "CAc=" value: "\xff\xc3\xdfJ\xbeI\xde\x1fāχQ\xf0\x94\xa8hv\x9a\xb6\x94\x85r\x16\x93 &M\xad\xf0\xbd\x80#" } leaves: { key: "CAg=" value: "\"\x90I\xb9j\xcf#t\xee\x8c\xe4Αw2M\xfd\x8cK\xa9m\xb1)4\xc7A\xfaЅ\x80`\xec" } leaves: { key: "CAk=" value: "\xe384\xf6/$hD\x06\xe8\x1f\xbcpa\x05\xefS\x1f\xdc؞\x95K\xab\x13\x9880l\x04B\x83" } leaves: { key: "CAo=" value: "\xd6\xf1\xf4\x80$^\xe1Y\xf0æ‹Ĩ\x9fJ\x17\xea\x18\x0f\x97\xdb\xd17\xde:t\xd2\x16fPXˈ" } leaves: { key: "CAs=" value: "\x9f\xa2\x93\x91\x8bZ\xf2\x0e\xd7{\x14\x84\xde\xec`\xf9\xa0\xd6H\xeb\x87,\xb1\xc1\x96KH:\xd8V\xe8\x07" } leaves: { key: "CAw=" value: "\x0cG\xac%61\x96\x15\x13P4\xbb\x88\xb2\x8a\x92Ū‚\xa4.[c\xc0\xab\xc8\xd5\x00\x9e9\xb8\xac\xfc" } leaves: { key: "CB0=" value: "\xab\x8c\x1c\x0b\x1b\xebZ+\xa5\x17\x90\xf7\xa9\xab\xb2\xd29-\xa6ĮĒBp\xeak\xac\x04\xcf\\\x02\xd1y" } leaves: { key: "CB4=" value: "\x12\x1f\x96J%\x14\x1b\xabme)\xfa\xfcڃ\x93\xbd\xd2\xde\x1d&.\xc8\xda\xfa\xfd\x97d%\x12>\xb4" } leaves: { key: "CB8=" value: "v\x7f\xb7\xc3~?\x97\x13Ce\xe2-:2\x05\x97~\x8esB\xc4B\x15\xda\x0f\xd6Sh0\x962e" } leaves: { key: "CBA=" value: "?\x18\xa1\xc2Į‚\xf1\xd8+/\xe3\xf8\x06\x95\xd0g\x848\x08\x1e\x8c\x13\x1f\xc5\x14x\x88\x06)\x84\x19\xf9" } leaves: { key: "CBE=" value: "\xd0\xf1Ú­\x85\xd6\xf7\xeaIp{MDu*l\xa9\x16Øĸ\x7f\xdc\xd1Z+\xd2\x15 \xc6i\x85\x1a" } leaves: { key: "CBI=" value: "5\xf6\x17\xf6x#}\x82\xfc\xf7t\x95I\xed%\x93D\xb14\xb9\x9av<\xc7&\xf8]\x9c\xc4\xf5\xb4\x1d" } leaves: { key: "CBM=" value: "\xee=\xce\xfdH\x83\x1c\xfdLh3k-\x0c \x97\xeb2{+\x97;\xd5r\xf3\x1f\xfa\x0f\x9f@+\xfb" } leaves: { key: "CBQ=" value: ";\xad\x96\xc9K/\xdb\x0f\npc-e\x8b~v\x16\x8e\xc4\x00\xf4g\xb4!x\xab\xfd\xa2\xf1\x9a[\xd6" } leaves: { key: "CBU=" value: "\x88\xdb\xc0\xe8\xa1\xd1\xe0B\xe6.\x82.\x80\xed\x82Y\xbcԄM\x19\xd8\xf6\xe6\xc2\x06r\xd2\xfc%\x8e\xff" } leaves: { key: "CBY=" value: "\x16)\xb5\xb9\x1aT\xa2 \xfc\xf8\xdf#Kf\x8e$\xa8\x82\xbe\t\xad\x9c\xc0\xac\x8e\x1b\x9f9r\xb1\xca)" } leaves: { key: "CBc=" value: "\xd2Y\xfe\xb9An\xdb\x16\xdd\tQ\x94\xfaqo,]M>\\3q\xf4!\x97E%ĤdH\xd1" } leaves: { key: "CBg=" value: "\x1a\xd0RR'\x1a#\xdf\x14|ÉŠ\xe8\xc1\xb7\xb2ÎŽ\xdf\xc3bXr\x17q\x16^ZT\xdfz\xa1" } leaves: { key: "CBk=" value: "\xa4H\r!\"\x10jY\x16\xd5\xd1@\xcc\r\x83\xa6v\x17\xf5}\xba\xb6|Ũ—0.\xf7\xff\x17\xa1\xc2" } leaves: { key: "CBo=" value: "!\xba_>\x85\x174^;t\xb3\xfe]\xfc\x1du>9\x83\xc0\xe74\xff<\xe3!*\xfc\xd0\xdf\xf0\xf2" } leaves: { key: "CBs=" value: "\x15\x95\x82\xe2Øŧ\xf3\xf3q\xd4\xf4\xc8\xf3\xa6\x10\x13\xa1g^!\xaa\"j\xdev\x9ag\x9fONq\x1e" } leaves: { key: "CBw=" value: "\xdeb\xbd\xe3v\xc62\r\x8f-\x00J]\x9fCT'[\xc9u\xdf\x08%\x14\x0e\x03Kƒ\x00\xf2\t" } leaves: { key: "CC0=" value: "\x86\xac\xf1ĮD\x13\xe03\xa7ĘŖ\xf4\x93\x902\xba\x16\xed6!AS\x18i\x08\xf6\xcc\xc5\xee\x10\xbb" } leaves: { key: "CC4=" value: "\t\x80\x07\x0c\xba\xef\r\xea\xb55\x02e*\xd6䖗V\xf0\xb9\xe1co'\xd0\xc3\xebĐĄ\xa2d&" } leaves: { key: "CC8=" value: "OF\xa15\x84S錰\xa45\x19\xb265\x99\x87J\xc9\x1aג\xb6\x02>f[U\x82\xc5]\x80" } leaves: { key: "CCA=" value: "h\x87\x92иŲŊZ\x18\xbbM!Q\x95\xdeAķ…“Ŋ\x02p\x14m\\\x86I\xd2(1\x9d\\" } leaves: { key: "CCE=" value: "\x83\xb9\xcf3l\x8b\xcab\xcd\xcb'\x03\x11g\xa7\xa5\xa4S\xaf\x96ä¸Ĩ\xb8\xe1\x94\x10\xd9:`\x8b\xa9" } leaves: { key: "CCI=" value: "\xa1w\xff8\xccs;\tf؈\x16W\xab\t\xfdh\x8b\xdb>\xfb\xce\xfa\xe3d!\x04\xeb\xa5\xed\xb2\x0b" } leaves: { key: "CCM=" value: "J\xa3\x9d7V\xa8\xc8\xc0\xe53Ė—\xbd\x9dh\x01\xe5\xea\nt?\x97C\x80xKr\xa1<\x99\xf0\x97" } leaves: { key: "CCQ=" value: "\x02:N\xf0\xd0\x17\x96\xb9\x95H\x1d.\xfd\xf7:\x97\ra\x9b2ߖ\x1c\x9d\x1afuM\x88Xj\x92" } leaves: { key: "CCU=" value: "\xbe\x1c\xad\xf9z\x1a\x93\xe1\xefh\xee\x1d\xff\x11*\xa0h\xf6%\x97H.\x0cD\xe1fN\\\x19\x89\x85i" } leaves: { key: "CCY=" value: "u4\x8f\x04\xd7\xecH\xbf\x91\xf7Ũą\x1c\x8ey\x9d\x14\x07rĊR\xd3\xcc=wÄēQ\xf1\x08\x00" } leaves: { key: "CCc=" value: "\x13\x1c\xdc.\xf82ĖĨ_\xe3\x96X\xd9\xfeÍž\x90\x83<\xb1\x02\xf8\x1d\x12\xedT\x92\x08\xc9dĮ“" } leaves: { key: "CCg=" value: "y\x9a{\xa69\x1f[\xf9\x8aEb\xc4 Gx\x98&Q\x89ËĄ\xaey\x16:\t\xf6\x8a\x86\xa5\x18\xe9" } leaves: { key: "CCk=" value: "\x92\xf8so\x7f/\xb2\xbeéĩ•mO@(\xb2\xd4!r\xf1S\xe6\t\xf4\xc0\x7f|\x1e\x94\x10_=" } leaves: { key: "CCo=" value: "ĮĢ\xce>\x0f\xee\xb2W^\x01\xc2k\xa8\xe6\xd0,\xf5\x03\x15\xf1ėĻĨk\xc9\xc3\x1c\xbbQ|\x0ei" } leaves: { key: "CCs=" value: "\n.\r\xb8\x15I\xe5Ō˛\xaf\xeaG\x1e\x06\xf4*Z\xb2ΎҌ\x1b\xf4\x13\xed\xf2]\x01M\xaaa" } leaves: { key: "CCw=" value: "\xadÆĩBR\x0b\rAg\xd9,\x1e[\xceH|\x1e(4\xb3\x7fbo5\xa2\xc4\xdeE\xa1\x81\x0f(" } leaves: { key: "CD0=" value: "\xbb\xf7\x86\x92\x8aP\x11\xe6\xf6\xf1k\xc9\xdb\x10eð\xe0\xb7\x16Q\xc4\xf8\xb2\r\xf3A\xb3\x02\x89\xdb\xd6" } leaves: { key: "CD4=" value: "\xec\xf0y\xb6\xef\xc9s'\xf72\x95\x8b\x068\xb4\xdb&[\x9e\x12ƈJ\x93Db\x06Ო\xaaA" } leaves: { key: "CD8=" value: "]N>\x9f,\xa5\xdc\xf0\x9dB\xc7'\x8b\xa4\xecs\xfc\xf1\xf5\x0fT*\xe7\xc3\"\\\xd4ħ=rq" } leaves: { key: "CDA=" value: "͏\x80\x14\xba\xa2;V\x17×Ļ\x04s\x9b\xe0\xa3\x0f\xc7?\x08(\xb9}\x1fd\xfa1q\xb4\xf3\x88\x1b" } leaves: { key: "CDE=" value: "O\xdd]\x81\xf2\xe3\"\x02\x1c\x07\x9e}UE^\r\x00OK\xa0\xb9\xe3\xa4\xcdxJ\xdfn\x15\x11)\xcb" } leaves: { key: "CDI=" value: "\xbc\xf0\x0f\xb20\x03;j\xa2\xcd\x05\xa0:\xbd\xbe\xb9o5\x0e*8\xe9\x80|\x96P\xa3`\xc7\xc0 \xfb" } leaves: { key: "CDM=" value: "\xf9\xfa\x8d\xe8_\x8b\xaek\xed\x16Mf\r\xe5\x9e\xd8\xc0\xedD\xd7\xfc\xcd\xe6\xd5T\x81\x95\xafl\xba\xec\xfd" } leaves: { key: "CDQ=" value: "\xb0N\xa1\xcf\x02\xbe\x1a\xb6\xb0\x83\x17GW\xce\x04\xc6\xdf4ßĒĘĩ\xc5\xe2\x02\x105\xfe\xe2g\xf9\xab" } leaves: { key: "CDU=" value: "Վr>Ε\xcevʰ\xf1\xe3\xb2͘U[\r\xa7\xadw\x89\x87\xe0\xe0\x03\x10\x85\x88\xe5\xff\x9d" } leaves: { key: "CDY=" value: "\x14b\xfc\xe50'!w\xbb!\xf6\x16'i\xe1\xb0BՎ+G\xf6\xfc\xc9\xf21\x0c\xb1\xb0\x84\x95N" } leaves: { key: "CDc=" value: "\xf6xb\xfe\x12\xe4\xd3\x16\x8br\xdd(\xad'o[($`\x0e'Sq=B\xc7\x0b-\xd0\x07\xf1\xf5" } leaves: { key: "CDg=" value: "\xcf\xf80\xa7-^`\xfe\xd8@\xfb\xe8\x04\xb5z5\xe8~΃.\x96\x86;e\xe2R\x05\xd3X\x83\x12" } leaves: { key: "CDk=" value: "\x19\xe2c\x95MB\x12\t\xa9F1\xcaBp\xae\x86\x8c\t\xfb6ƃF\xe5K\xec\xea\xb36\xfbZ\xf7" } leaves: { key: "CDo=" value: "\x1e\x02n\x10;)\xe68#UR\x99\x825\xfe\xb4b\xd8=\t\x060\x17\xecM\xa2\x12\x037\xc2SQ" } leaves: { key: "CDs=" value: "\x18\xab\xff&\xf3?\"\x89\xc7Y\x90\xf3\xe6\xa4Xm\xb8\xd5\xc3\\\xc5\xf7\xe2c\xd7\x1f9\xa0\"\x18zS" } leaves: { key: "CDw=" value: "\xf2\x84\"\x02Z\x91_efv\xc0:\xfc1\xb5E\x11\"\x08,\xb1\xd0\xf3\xb2gqĮš/7\xaa\xdf" } leaves: { key: "CE0=" value: "|\xe29\x1c\"\xe8\x8d\\\x8b39e\xb8I\x0fNW\xcd6~j\x13b\xa0\x93P\"ᙓUV" } leaves: { key: "CE4=" value: "\xbf\xecF\x90\n\x88+\xf0\xb8?x/\x87\xdeAi\xca" } leaves: { key: "CEs=" value: "\xd4\x14\xc9\xf2}\xfa\xb9.\x19\xc3\x1e\xce\xed\xe8pėĢ„ÉĒJ2\xb4 Åļv\x16\xacAt*" } leaves: { key: "CEw=" value: "Ûˇ\xd4\x1e5k\x01OL\x00Zir:\xe48J\xa1\xf2\x1f\xb4}\xeb\xc8k\\\xd5\xc6KĖ\xd6" } leaves: { key: "CF0=" value: "\x97\xcea\xf7Z0c]f\xdf==Y\xd8\xf9\\\xb0_]\xba\xee\x9a\xf3.&Ut\xbf\xed\x1f\x10o" } leaves: { key: "CF4=" value: "#\"qЊ\xee\x90\xc0'9\xa3\x91]-\xda+\xe5\xefL\x98Îŋ~P\x8c\x1a\xe2\x06\x15Ôĩ\xf1" } leaves: { key: "CF8=" value: "\xc8\xc4q}\xbd\x8b7\x99t\x8c.\xb4\x98R$\x112\x89\xab\xb5\x82~\xad\xa8\xf0&F\x96\x95\xb0\t\xf3" } leaves: { key: "CFA=" value: "q\x01\xae\x0fb\xa6[c\xe1i\x17\x93\xdbO>i\xaeĖš\xb1\x98B\x92\xdc6y\x8b{\xb4V\x11\x11" } leaves: { key: "CFE=" value: "\x08\r}|Q\xb8PáŽļ\xfe\x12I<\xaeֈ\xe0\x06\xc6T\xf8\x02\x19\x02\xf2~\xf5\xe9+'\xf5" } leaves: { key: "CFI=" value: "\x14#\xfc\xe9>[\xc07\xf7\xad#p\x1f\xff\xf2L;\tW\x87F\x97\xec\xf6\xaaY\xa7G\x10\xb6\x17Q" } leaves: { key: "CFM=" value: "Øŋ\xfahw\xcb\xe2Wb\xa9\\\xb7F\x86\xb4\xc8N\xdf\xcfx\x8f1\xb1\xd0\xe86k\x8f\xbe\xce\xd6\xdf" } leaves: { key: "CFQ=" value: "ÍĻŨ§~\xces\xfb\xc4()\x9f\xa7\x97\xc9\xc6|4k\x8fe3\x9c\xab\xd2\\\xaa#\x07_\x1c\xe1" } leaves: { key: "CFU=" value: "\x1fG\xeb\xffwu\x01\x0c\xb4\x10\xc1\x86\xc9H>\x8eJ\xe1\x14\xc1\x0fz\xba\x0c|a\xe7\xd1&V\x80\xc8" } leaves: { key: "CFY=" value: "=)\xd9n\x95\xc0>\xd6\xe0\xfaz\xe1\xf1\x01z\xf7Åĸ\xa5R\x1fd|\xf2\xaes\x99j\xd3=dF" } leaves: { key: "CFc=" value: "9\xf9\xd4ÎŦho+X\xa3_#\xa7\x1a\xc7\x10\xf9J4\x15>4\xc3w\x8c\xdc\xfe\xb8\xa5M\xf5\x0b" } leaves: { key: "CFg=" value: "\xbdI\x03Sf\xdaS\x97U\xd7\xfc?^\xb9\xe1Jx\xbe\x06q\xdb\xeb\xf0M\xf8\x0c\xe5\xf6c\xa3\t\xc6" } leaves: { key: "CFk=" value: "\xdab\x0f'Z\x81\xa4AC\x1c\xff\x17\xdd\xc7C\xfe\xaa\xa2\t\x97\xdb3;\xa9\xa6\xc1\xfaJ\xf87-%" } leaves: { key: "CFo=" value: "0(\xc23\xc4\xf5ð?\x9f\xa3E\xed\xa8\x04~\xcf\xd0\x06\xf9z\x80\xcdJ\xa3\x9em}\x89\xa5\xf0k" } leaves: { key: "CFs=" value: "]\xde\xd4\x03\xcc\xccCN\"9\x1e\x0c~<ÃĢ\x02\xfc\x98\xaey\xfc\xb4 \xcb{\x92j\xb2\xfai\x1c" } leaves: { key: "CFw=" value: "e\xe9\xafd\x07`\xda\xc6&\x8e\xc1\x9e\x0cV\xed\x02\xffl\xd1\xd8\xd5>\x9c\x8c9\xa6\xda^ut]\xe6" } leaves: { key: "CG0=" value: "\xee\xf1\xcb\xf8\x9e\x93\xd4^\"\xc4\xdf\xc1\x1e#\xdf\xef\xc6;\xa3\x18\xdfiy\xa9\x13\xaf\xddFfn\xab\x0f" } leaves: { key: "CG4=" value: "t%^\x9d\xeec_8\x96s\x05o\xfb\t\xc8{\xae%*ŅĒ^ãšģ\x8a\x06q\xf4dy\x8b" } leaves: { key: "CG8=" value: "\x11\xe6\xf4\xb4\xb1\x98\xecz\xab\xaaqM\xa5q\x0c\xdf:\xee\x1f\xcb\x1ccRj\x9bC\xce\x12\xfff\xca%" } leaves: { key: "CGA=" value: "\xac\xb4r\x12j\x12\\[Q*d\\\xb3\xaem×ģ;poI\xfe^\x00\x02\xcf\xe2jn~\x98)" } leaves: { key: "CGE=" value: "\x97%9\xbc\xcfŪ™\x17\x106\xfc\xf4ß´\xc8\x07\xef\xcd\xe5\xd6\x04\xb8\x14s\x87\xf3\xfa\xd2+\xf0\r\xa6" } leaves: { key: "CGI=" value: "AY\x15P\xe8.\x0f\xa2+\xd8\" .\xe0\x9d\xeb\x0fx1\t\x9c\x19\xd0?\x10\x92i\xd0n\x08\x1a\xaa" } leaves: { key: "CGM=" value: "}p\x8b\xb8\xac\x15\x80W\xc0\x8ct\x1eX\n\xd4o\xd8>\x80\xd4\xde\xfd\x8e\xda\x16\x98\xa2\xda\xf9\xa2I\x14" } leaves: { key: "CGQ=" value: ">T\x90q\x14\x8a\r\xdf\x1aO.\xf3ЄZ\xa31B\xf8J+\xf9\xbd\x18\xbc\xc3\xf3Úˇ\xa8\xf2\xe8" } leaves: { key: "CGU=" value: "\xb0O\x0fC\xf0t\xae\x1d6\x06\xd7\xda\xc2\xfc\x87\xb3{A\xfdQ\xbd\xafu\xd60\x9a\x1a\xadv\x1f\xa0M" } leaves: { key: "CGY=" value: "9E1\xc9&I;\xe1\xbe\xf1\xed\x0f@\xad\x977\x9d(k\xd9\x15\x0f\xda\xe4\xe5U\xdc\x15OČŦ\xe4" } leaves: { key: "CGc=" value: "A=\xde\xdf|v-\xe1Q\xf5\xbd<*c\xa3\xf5\xe2v_pl\x08\x9a\xa9\xeb(\x98\xb8\xaf\xb1K\x9e" } leaves: { key: "CGg=" value: "\xaa\xe0\xa3\xef]p\xdb\x0b\x01\x91$z\xc4\x1e\x15[\x94\xb9)t\xa5\xd7Q.\x8d\xed\xa77\x0bQ\x93\x02" } leaves: { key: "CGk=" value: "8V6dSC\xebs\xdcQE\xf6\xec\xea\xf4z" } leaves: { key: "CGo=" value: "^\\\x9c\x19\xfeI\x8a\xdap\xbd\x1b=\xcct\xc7z\xa53f\xdfD[[\xc1M0\xb0\x10\xbe/\xc1e" } leaves: { key: "CGs=" value: "\x8f\xbe\xb43\xe7$\x1d\x02\xb0^\x93\x7f\xe8/\xe90\x83z.\xa5\x0c\xdc4zn\xe7\xd6\x1e.qkD" } leaves: { key: "CGw=" value: "\xc6\x07+\x8dĶ€m\x01\xa7\x85\xf5\xc5\x12\xf6\x8es\x11\x1dn\x15\x12\x0c@\xba\xf6\x87h\x14\x1b\xf7\x1c\xa3" } leaves: { key: "CH0=" value: "\xec\xce\xc6@U\xc7\x05S\x95\x0b\xb0\x1e9\xb6\xd4Z\xa0pWq\x81\xc0R\xfc\xc2#\xe9Ÿd\xb1Y" } leaves: { key: "CH4=" value: "[f\x99\xbb1\xfa\x1e\x8a\xbc\\v\x93/M\x1f\x84G\x17Uq\x89[C~\xbe\x82\x9e\xe2A\xf3\xfc|" } leaves: { key: "CH8=" value: "ßŗ\x0c\xf1\xca\xd1\xe1\xef\x8e\x7f\x9f\xe4\xe2\xee\xc7NnI\x7f\xa2S\xbfa\xaa=\xd6D\xd0@#\xa3}" } leaves: { key: "CHA=" value: "\x8b䋒\x91%\x02v\n\xd0v\xa9\x08\x11\x8f\x8f_\xf3I^M\xc4\xdcOƞ\x7f+\x8c\x8c\xbf3" } leaves: { key: "CHE=" value: "%f\xe3b;\xa5\x08\xf5\xc3R\xf7h\xeb\xca- \xcc(\xbaeg+!\x05\x95\x8e\xb5g\xcb\x17\x0c\x97" } leaves: { key: "CHI=" value: "i\x16O\x85\x84{O\xf4!\xe7\xc5o\xb0-\x08y\x80\xdb\xd5\xf5\x15\xf1\xbd?\x01^į ­\x1b\xfbZ" } leaves: { key: "CHM=" value: "\xbb\xc1\x88\xa9\xfeCB$\x96\x88i\x81\xe7pԃ\xeb\xd4\xdem\xa5\xf9\xef\x14+\x93y\xa4\xfc;\xc5u" } leaves: { key: "CHQ=" value: "^\xb8\xf8\xfa(\xa0Z\xffO\xc2W\xd5Wۗ\xc9\"\xd1\xe9a\xfb\xad@1\xa9r\xc1J:\xb2\xfa\x99" } leaves: { key: "CHU=" value: "R\xa3r\xf5M.VÜŦ\xd4\\\x9cQi\xc7\x1f0B\x1f\xd4uZ\xad\tĘ\xaa>uuy4" } leaves: { key: "CHY=" value: "\xf6\xd2fÅŖ>\x9c=\x90\xcb\x08\xf9\xaf\xd2\xf2\xd3\x07\xc8P_\x91\u0092G\x8bV\xd0Zꍀ\xe0" } leaves: { key: "CHc=" value: "Ar-\xbb\xf5\xa5\xe5\x1a\xf7\xb2\x8eV\xa5\x0e\xe6\x92ȁ\xb6<\xb4\xd2\x04\x96\x07\xab'\xb85| \x9a" } leaves: { key: "CHg=" value: "\x0fҏ\x07?a>=\x17\xc14U.\x08`%ՓN7\xa7\xeaR\x81-t<\xd1\xfe+@\x0e" } leaves: { key: "CHk=" value: "\xa9\xd5\xdd\x1d[;k\x910\x81\xa1\x1f\x99\x83\xc5\xc34\x9e\xe49~\xff\xd0e\xef" } leaves: { key: "CI0=" value: "ï7\xfe\xb3\xe3\xee\xd9`x\xf52\xf3\xe71@\x05\x17a\xee,\xdf\xe1ij0\x9c\xe5\x0c\xe4\x96\x03" } leaves: { key: "CI4=" value: "O\x15\x0c\xf6\xa0\x14\xbb\x12+\x9b\x96\xf8,to܌^O9\xff$Q!\xbf\xac\xa4\xe3\x05\x0b\xb5\xd5" } leaves: { key: "CI8=" value: "\xe3n\xebt\x0et\x03\xbe\xf6g:\xe4f\xb9͈poY\x81s\xc0:\x1f\x92\x1d\x10]\xd7\xc6mp" } leaves: { key: "CIA=" value: "\xf1\xfe\x043\xa5V\x9eOy\x80Iz\xe7\xd5Gn\xec$\x0cR\x1a\xc1d\x05\xf4c\xdd\xc4\x0bɇ{" } leaves: { key: "CIE=" value: "\xb9\x87\xd2\xed\x08\xe7\xcdUe\xce{\x91zo\x80\x91g\xf0׸\xab\xf7\x10b\x9c\xc5\x0c]\x07\xec\xf6\xbf" } leaves: { key: "CII=" value: "1\xa4\x9fm|\xad\x0f\xca\xf1>\x7f\x10\x9a\xaa\x94\x8deIn\xc5\xd2\x11\xd5\xcc\x0c\xdf\xc5d\xc9x\x01\xa7" } leaves: { key: "CIM=" value: "-S\"\xd1\xf2# \xd5\xc0\x90\x8fP\xd1\x15O\xa0\x941\xe4\xf0,b@M\x103Ōģt\x02SO" } leaves: { key: "CIQ=" value: "u\x89\x1ax7Ř;=\xc7G\x03\xac\xc7\xd0\xfe2\x87\xb6\x8b\xa6U\xe9\x11\x84\\\xe3\xf1\xd9%\t\xa0" } leaves: { key: "CIU=" value: "\xcbX\xf6\xa0 d;>\xc93ş\xc2AUb\x14\xa8,\xcb\xd1\xc5ÄŖ\xd6\xfc%\xf04\x8b\x88\x8e" } leaves: { key: "CIY=" value: "&aLQI\x15)'L\x92\xe9\xce)\xc4\xec\xeb\xa1\xe8\x18\x97\xd5mr\xee\xa8\x04\xa3\xa7\x13\x18\x1c1" } leaves: { key: "CIc=" value: "H\xf9Y\xf9xJ\xee\x17\xd2sīŠ¤&\xbe\x80֛\x80\x07\x14m\x05H\xa3+\xc2yy\x1a{\x12" } leaves: { key: "CIg=" value: "p\xe3\x84>q\xf9j\xa7\xf0\x1d1" } leaves: { key: "CKM=" value: "\x0b\x81`~G\x06\xd4>\xb4\x95\xac{\x85\x00\xfe\xcc\xedQ\x84\x1f\xf8ߥ\x98\x1b\n&Bl|\xd4." } leaves: { key: "CKQ=" value: "\xa7[\x0b\xa8\xec\x13\x08\xbci7\xf3\xab\xb8\xd5\xfc\xd0\x1cW\x19\xc4i\xafp\x06\xd3\xc6Hh\xb7Įƒv" } leaves: { key: "CKU=" value: "\\\xf2j\xfe\xc4i\xd0į‘n\x98\xd5YK$\x15:p$\x7f\xd8\xc8\xe0\x82R\xef\x85\x7f\xb3\x82\x06" } leaves: { key: "CKY=" value: "C\x01f 8j׀\xea3(\x90\xe4\xf2\xc7Of-\xf8m\x16\xdd\x0f\"\xe1\x7fa]y\xa6\xd1e" } leaves: { key: "CKc=" value: "\xaak6YX\x8f\x85#\x9f\xa0bČĨ\xdaD~\x16\xd7\xe7>\xe6'\xec{\xc34X\x06\xe8~}\x9d" } leaves: { key: "CKg=" value: "\xba G\xae\xd33\x9b@\xd48pLUD\xf5\xf4P\x82ΟÆĨ\x83080\x9bc\xb4\xa5\x8b\xe5" } leaves: { key: "CKk=" value: "\x0fl\xc0<\x83\xf7\xb0t[\x84\x92\xc6\xfe\xf4\x83\xe9k\xc9W\xbb\x9f\x11\xfe<\xdf\xd9z\xdf(h\xbcM" } leaves: { key: "CKo=" value: "[\x8d\x1f\xe3\xfb\xe2lt\x8dUf\xfaB.JE(9\xc0+UÔĩ\xaf}bR6J\xecŲą" } leaves: { key: "CKs=" value: " \x19\xbe\xe0\xf7\xdc4\xf8/(\xa4m\xe8\x94U\x1f\n\xfe\x99+\xae\xe0\tZ\xc0ևE\xb9\x98i\x07" } leaves: { key: "CKw=" value: "\xd3\x07|\xa7\x96\xfa\x89V^\xcb0\x94\x86\\aw\x9e\xee\xf0i\x97!\xadT\xbd\x1bV\x01y\x90-n" } leaves: { key: "CL0=" value: "\xb0m\x01\n.凂\xfd*\x81\xe8e,`\x80x\x8b\xd3\xe1\t\x15\x81\x18:]\x9a\xf9I9@X" } leaves: { key: "CL4=" value: "k@'@)\xdaA\x9b\xeeʤ\x85\xc2\xfa\xb3\xf7|\x93\x82\xb4\xbfo\x830O|\x99c&\xae\xa2\x9f" } leaves: { key: "CL8=" value: "r2!\xd5?\xac\xf9l\x99Ɛ\x81Ęļ\xb8\xb8P#\x08\xe3-\xbe\xdc\xc5\xc4`\xa0\x8c\x05\x81v\xd6" } leaves: { key: "CLA=" value: "#V\xdb\xdd\t\xb0Nh\xf3h\x97=\xc0\xf4\xee<į•ŊY\xd8Į›u\xcc9\xeeqj\xacf\x04" } leaves: { key: "CLE=" value: "\xb7%%\xbe:\x0f\x9aB\xf6\x98\xa6Ū…\xc2\xc0\xa3FZ\xb5\x05\xf19\x88XÜĸG^\xee\xcc\x17y" } leaves: { key: "CLI=" value: "]\x1e\xfd'z\x94\x9aU}\xd4\nS1Q\x10zG\xae\x13\x1b\xd8\xfd\xc1\x07b\xb9\xd3꩛\xeb\xa2" } leaves: { key: "CLM=" value: "S\x96n\xa5\xca$d\x9e\x88\x02\xe9\xc2BŅ’\x82Öŋ\x948\x96v\x94\x9c:\xff\x9c\x8f\xfa\xbe\x9b\xba" } leaves: { key: "CLQ=" value: "\xbaY\xebr\x00r\xf06cN\x15%\xf7,\x87I_\xb8\xf6pB\xe6\xe2j\xeaÜŋ\x84\x9b\xaa\xab\x91" } leaves: { key: "CLU=" value: "]\xbd\x06)\xe4\xb2\xfe\x11\xaeu\xef{\xa2!\xe8Úš\xcb8V\xfb\n\x8d\xab\xc60[\xe2\xc0\x86Ķ‹" } leaves: { key: "CLY=" value: "r\xa3\xbbU\x95\xb9\xc6\t\x01\x8fʛ\x92\xea\x0e1g\x05\x04\xb5\xda?\x88F\x89,\xc0\xef\x11\xbb\xb2R" } leaves: { key: "CLc=" value: "\xcbÅ E\xb4\xa8.@6*H2%\xc1\x18\xfa\xbb{A\x8f\xe5.\xf8\xa1ď\xc4'\x1c\xf7eh" } leaves: { key: "CLg=" value: "\xfe\xab\xcb\x0fw\xf2\x8e>P&\xf9\xf4\xa7\xc8\xf8\xbf\xf0\xa1\x89S\xbb\x84)\xc1\xb7\xd8l\x1bI7\x05b" } leaves: { key: "CLk=" value: "J\xa82sO\x0c\u0090\xab\xb9$\xc95\xc3t\xf2\xfcxm\x9aX\xfcT*\xe6N\x84\xd75cT\xc1" } leaves: { key: "CLo=" value: "\xcb\x19\x1e\xc2|N\xf9\x9e\xc1.\x1bw\xb8\xb3\xeeĮŋ|^\x13U\xf1\x0e\xdeLY\xfe,\xb2\x1f\xee\xc5" } leaves: { key: "CLs=" value: "\xa9|\x87\xef\xf7Îŧc\xb0\xc6\x1c\x93\xc6\xd1ÔĨ\x0cT\xefD\x85/Y\x8bV\xcf\x00\x9ed\xd0E\x1a" } leaves: { key: "CLw=" value: "\xef\xe2x6\xb6\x87\x97\x83V\x01\x0cD\xff˨S|B(4?\xb3\x87]h\x9dÄŊ\xa5r$\x13" } leaves: { key: "CM0=" value: "K \xccØŧ\x8c\xa0\xe5\xaa\xd8\xcb\xc3\xffu\xa3\x04G/\xb2\x03\x99\xb66*8J\x0f\xbc\xe4\xa0\xda\x1b" } leaves: { key: "CM4=" value: "\xa2/w#\xea\xe5\x137\xbb`\x18\x85`\xed\xa1\x98$\xbc\x1d\xee\xf3z\xe8Y\x9c\xda\x14\xd2.G\xa0\x87" } leaves: { key: "CM8=" value: "\xc4U\x84\xe4\x15\x17] \xb8WQ\\\x17\xf9XP\xf3\xfd\xbaF\x02\xa6\xb7\x81\x1fC~D\x9c}\xfa\xde" } leaves: { key: "CMA=" value: "\xa5\xdc\xf1\x9b\xe5*\xf6x\xd1\xfe\xd8in\x92\xd9UHI\x0c\x96i\xe0\"M\x15\x04=\x9bM|\xbf#" } leaves: { key: "CME=" value: "\xe0\xfc\xf4 \x14\xca\x07(n.k\x13\x9e\x08?\xe7+\x1a\x91\x9e\xe4\xe1\xca{\xe8A\xfb\xe6\x84\xe5\xc8F" } leaves: { key: "CMI=" value: "\xd8M\x12@\xa6\xb8Nv\x1b\x0e\x15,\x05Ψ*10\xd3\xc8f\x17\x8c\xba\xe8\x1dtΌ8\xcd6" } leaves: { key: "CMM=" value: "\x8aA\xf6\xd2$\xff8%W3`L\xcdW\x97ÔĢs)l\r^\xdcD\x18\x9c\x1a\xb1\x17Vj\xb4" } leaves: { key: "CMQ=" value: "\xbd\x95\n\x18v\\\x04\rd\xf7f\xd7,\xc7\xce/H\xe7\xfe\x87\xf5\xe0]\x87\xb2P\xb2ßŧ;.#" } leaves: { key: "CMU=" value: "m\xa9 ˧B\xcb\x1c\x19\x93\x825\xd1\xd0\x13\xf9FL\xe6\xf1\xe4\x9aqE\x8f\x8a\x18e\xddĖ“\x1c" } leaves: { key: "CMY=" value: "\x1dNC\xd34\xbad\xd3\xce\xd2\x1e\xe2T\x89\xf7\xec\xf6 uwy0\x12?O\xf6\x8d%\xb3v\xe4" } leaves: { key: "CMc=" value: "\xdb\x07o\x91\xb7\xaf4\xec)\xa3m\rk|\x93\xb1\xd9͈\x93\xc3g\x9d4o\x82\xda\xe4\x8b\xc6C\xf7" } leaves: { key: "CMg=" value: "\xceC#v{\x8c\xeb5X\xbb\x9aj\x97\x96\xf7\xe9\x08\x96E\x8b(\x07\xb6\x9c^X\x97\x10\x18\rB\x9f" } leaves: { key: "CMk=" value: "\x8b\xf3.;\xb3\\\xd7\xc1\x94\x99ÂĄ\x9d\x1e\x91\xfcU\xfe\x14\xc1H\xd1R'\xba5\t1\x94Ew\xa2" } leaves: { key: "CMo=" value: "\x7f\xe4\xc0\xf2iĪŽ\x83\xc6jÕĩE\x8a/\x1e\xbc\x9eŅŽ\xaa\\\xdbέëĨŸ+\xd6\x10X" } leaves: { key: "CMs=" value: "\x95T\xfaT\xc8Y\\\x13\xb9\xbe\xef\xde9-\x18\xfc\xcdW\x00\xf2\x9d\xd2DsM\x04_\x91&\xba\x83\xd0" } leaves: { key: "CMw=" value: "\x8e\xc1Ō‰\x07\x12=#\xeaO\xaeI*#\xc5\x0e\x01\x1feHb\xc9s\x10\xc1ˡ\xc60\xa1A\x97" } leaves: { key: "CN0=" value: "Ūŗ,\xc4PO\xb0(7\r\xfa\xd2\x7fu\xa0;\xe5\xdd\xf5\x0b+,\xe8\xa2A\xe4\x82\x18T\x9c\xe4\xe4" } leaves: { key: "CN4=" value: "&\x9c\xd8\x01\xe04\x97u\x8c\xa5eL\xcb_\xa0\xe7_\xa1w\xf1\xb0\xe1PWL\x9fE\xdf\"?k\x17" } leaves: { key: "CN8=" value: "~ܕ!\x11\x92e\x9e<\x89\xbe\xfcmY\xa00Y\xc3q\x1d\xddJ\x0b\xf5?PY\xfb\xdd" } leaves: { key: "CNA=" value: "\xf5\xf9\x86]\xbesw\x82Q\xc2K\x80\r\xc9#x\xac\xbe.Z6e}0df\x0f\xa1$\xd1-h" } leaves: { key: "CNE=" value: "ၐ\x05\xb9Zm؝L\x10\xaf~D\xf7\xa8Whs\x02\xf7\xe9\xd12\xc84F\x96]\x1bsT" } leaves: { key: "CNI=" value: ">\x85\xebۘ\x84\x0b\x01P\xb59\x9bif\x0fd\xab\xa5\x94&\xfd\x19\xdb\xf6\xd7\xd0\x7fÂĢ\x9d\xd3\xd5" } leaves: { key: "CNM=" value: "W\xf0\x99۞2\xba\xee;\xf0`?|f J\x87\x06\x7f\x8c\x02\xf0y\x19/s\xfe\xbe\xd4\xe7P\xd7" } leaves: { key: "CNQ=" value: "m\xf7\xfa\xdf\xc2\xe6\x7f\xe13\xe7{\x9cf\xe2\xb36=\u0085\xd7\xce\x1dQ\xa8\xcfd\xd4kj]r\xa6" } leaves: { key: "CNU=" value: "\x06|\x98/\xa0/,'*\x8a\x13T7\x02L{j\x10\xbe\xc3\x17z\xe2\x05Z\xc0\xd1E\xa3\xa88\xb6" } leaves: { key: "CNY=" value: "_)/J`%k>\x16\x8a%4\xac^!\xa0\xe8\x9cv\xb5\x9d\xe7\xf5j3\xf7\xde\xc3\xf0\x0c&e" } leaves: { key: "CNc=" value: "\xce\xe4\xd3>+e\xb8ä›ŧw`\x16j\x1e\xee\n-\xfd]=\x8c\xb9D.~=\xaa\xffQ\xf1\x95" } leaves: { key: "CNg=" value: "<\xd7䔱\x915\x89Ōž\xf2I\x08\x08t\xea\x02\x9f\x9cs\xad_(U\x8a\xb2k\x1c\xd1e\xeb\x14" } leaves: { key: "CNk=" value: "\x1c\xed^DĮŦ\xc0\xa6\xbdÆĨ\x05\xcdve\x88\xc4ן\xe1\xe6jtJ\xe4\xe0=ȁ\x1d!\x9c" } leaves: { key: "CNo=" value: "xÄĒb\xc6aR\xef\xd0\xc5M\xf3\xe9dD= Y8}I\x88BD.Z\xab\xe4\xc5%Q\x02" } leaves: { key: "CNs=" value: "\xe4%\xfdq\x9aG\x12\x15\x1c\xcbbc\xae\xe1+\xbaJ\x94PG\x94?\xa5į›ŊB\xfe\xf1\xd8#'" } leaves: { key: "CNw=" value: "\x96f`([\x9eN\xadbN\xae{˞L\x96\x1a\xe9\x81\x16qKk%\x96N\xd7u4.Nk" } leaves: { key: "CO0=" value: "\x99#G\x8b\xccH\xfb\x8bM=\xf2\xbf\x84wx\x96=\\\xceV9p7\xdb:ᖛyԐ\"" } leaves: { key: "CO4=" value: "-ę¯ŋ^s\xb9N=\xcf|\x91\xc2\xca\xf4v\xa4\xbd;\x9dvU\x93\x15\xe4;\x97Zv\xb2\xa3\x1c" } leaves: { key: "CO8=" value: "\xddAHU:G5\xa2\xc5o\x82\xdes\xb6\xfdQ\xa4\x0f\xeau5\x91\xa9N\x88\xfc.\xc9,\xb9\x1e\x83" } leaves: { key: "COA=" value: "0\xc6+\x99GJ\xd3\xd7\xc6'\t\xc3-@\xef\xe5\xc8\xceX\x9fP`\xec\xb5.\x89\xc9j60\xe9\xc2" } leaves: { key: "COE=" value: "9\x9bg\x99\x07\x98\xdf\xea՘`O\x1d\xe7\x14`\xa5\x18\xa5\xea;@k3\x04\x16\xa0\x18?Y\x8f\xd3" } leaves: { key: "COI=" value: "`XG\x91\xb5\xd3]\xb7\x17\xac$og \"\x0c\xbeB~v\xc1\xf1\xa1\xf6-U\\[M@F" } leaves: { key: "COM=" value: "\x1d2Jg\x03m\x01a\x7f_\xb3\xbd\xb8ČĒ\xc5\xc5\xe6\xef)\xf9\xce\x0e\x1d\x82b\xedXn\\c\xa4" } leaves: { key: "COQ=" value: "_T\ru\xa1\xd78\xc3s\xa6J1$m$z\x8c\xc4l\x12\xbcsr\x81Is\x19<\x83@\xc0\x9e" } leaves: { key: "COU=" value: "\x0c\x91\xd5U\xa7\xbc\x1b\xc1\x19\xe2\x13-\x19Ά`\x02\x11\x93\xa6w\xf7\xc0\xc0åϝ\xf8N\x12j\x0e" } leaves: { key: "COY=" value: "V\xc1H\xbe\xa7\x11\x1e\x94\x8a\x83\xd4\xd0\xde؁.\x0b\x05āŧž\x9fN\xee\xd3[\xb9\x15\x12e\x7f\x95" } leaves: { key: "COc=" value: "\x8d\xdc\xff9\xa4\x0fc\xb6\xfa\xcb%0\xef\xacm\xb6\xa7\xdb\x145p\n\x87ˇ\xa0\x1f\xc2C\xb3\xfcu" } leaves: { key: "COg=" value: "'(GD\xf9R\xac\xef\x05E7*\xe0\xe2X+lJ\xf5\xb6I\xddf:\x9aN\x06\xa6\x18\x8aI\x0c" } leaves: { key: "COk=" value: "\x8f\"I\x80\xe2\xa1\xf7\nt\xa6Ĩz9\x02\x19\t\xceJ~\xf8\xd7C5\xe3z0ŨŊ\x12S{" } leaves: { key: "COo=" value: "\xb0\x12\x1e\x82\x12jߏu\xa5\x07\xa6\xe2\x9e\xff及\xe7e~\x8b\xc1<\t\xcf\x05'\xd0\x1f\x8d\xce" } leaves: { key: "COs=" value: "<\x8e\x1a\x1a\xfcy6\xc6\xddd\x81/\x88PS\x07W!H5\x08]y(=\xff\x10S\x01\xfa\x10\xbb" } leaves: { key: "COw=" value: "(\xaal(\xb5\xc6\xf7809{oc\xb7\xa4\xb1Üąh\xce`n\xd04\xaf_\xba\xe6^Ũ2" } leaves: { key: "CP0=" value: "\xea\"B\x96M\xb0\xa6J\xbf\xe9\xa02\xed\xbc\x14O\xd3'1\xbb\xeb\x17\xf2qÎŋ)#\x99\xb4}\xa0" } leaves: { key: "CP4=" value: "\xcaM¡\x19\xc5B\xa2J\x1e\xa2\x16\x05\x99\xadØˇ\xe0nW\xeb\x1ex\xfc\x86y\x8d\x90\xc1\x0f\x12N" } leaves: { key: "CP8=" value: "\xdc\x0e\xbdI\x89\xd6\xd0\xe3\xfeI\x15\xf1ę†ļvO&7ėĄžËĨEjG\"\xdb\xdaA\x8c" } leaves: { key: "CPA=" value: "Ho\x81\x0f\xdfJ\xf5\x84h\xc9\xf6\xb4ÜŖ\x1a\x9a6\xdb`\xe1\x1dg\x93\xcaÍ´\xe4R\xfcPŌą" } leaves: { key: "CPE=" value: "4\xc1\\\xfa\x10z\x87\x8e\xe0\xe1\xd2éŧ\xc0\x82\x98ax\x01z\x83\xb8\xd7.q\xf7\xebsm\xc0\xfa" } leaves: { key: "CPI=" value: "!$5\xa0}\xcaze\xbc\x0c^U\x92\xbeX\x1a\xa9\x1c\xa6\x05\x7f|<\x0e\xc5\xe4P2\xbcgZ\xe9" } leaves: { key: "CPM=" value: "\x1biF5\x84\x15\xb8\n&'\x8680\x99\xd2\n\x17+\xa0\xd8Sl\xcf\xdd\xe2l\xcaRs\xcb\r\xd4" } leaves: { key: "CPQ=" value: "y\x12\xedM\xf2\xc3ׅ\xf3\xbb\x108rmp\x82B\xfb\x1a\x97\xe9N\xef<\xc6\xfe\x0c\xfa\x1bnD." } leaves: { key: "CPU=" value: "\r\xe3\xff\xf2\xc60xLdD\xd5\xf3\xe7\xf7\xf5qe\xabĪ>>\xa8\x85\xc9sOd'\xb8\x8f\xa2" } leaves: { key: "CPY=" value: "\x96i\xa8\xe2֐\xdf\x1a\xact\xf7\x10\xec\x91\x16\x1b \x13E*莎\n\x0c.H\xb8\x9c\xea\xcf\xe6" } leaves: { key: "CPc=" value: "2C\x98\x16N\x13\xd5^\xfd\xec\xe5\x0f\r\xc1\xb6\xddm'\x06\xc0\xfc\x83\xf0\xe8\x12\x8f\xd2\xef\xe7\x1b\";" } leaves: { key: "CPg=" value: "\xf4Г\xcd\xe9'\xc37\x90\xb3\xbch\x9e w\xd8W\x05\xed$J\rÕĒ\xa1\xf1\x13\x9b\xedQ\x80h" } leaves: { key: "CPk=" value: "\x81\xe4\xefx\x11\x94\xb6O\x90\x9fW6\xf6\xe7x\xa9\xc1\x1e\x95\xb1-b\x96\xcei\x17lPT\xefi\x10" } leaves: { key: "CPo=" value: "\xe3y\x81\x8a\xaa\x19\xe8-\x0b1\x96^\x85\x1f\xc0\xcb\xd1큂~@Q\x85u\xc8N\x11\xa1Ū—\xd3" } leaves: { key: "CPs=" value: "\xa4\x85\x89\xad\x11\xc1\xd0\xc1\x1f\x01t0\xd2=\xb7\xc9I\xf1\xdb`d69\xb0`\x90\xd28\xbfaZk\xbe" } internal_nodes: { key: "AQA=" value: "\xff\xffd\x12!9<8\xc3\xcf\x10\xab\xa8I\x08]\x81\xcbF\x13\xb8H\xf9\x9eoÎĢ\xbf\xad\xa6\x0c\x1c" } internal_nodes: { key: "AYA=" value: "\x11\x8by[́Q[\xb8\xdc\"\xedt\xde\xe4\xd2[\x8b^H&\x84$\xe43\x8f\xb5\xd2c.9\x93" } internal_nodes: { key: "AgA=" value: "\x07\x864j\xb2K`\xc0Q\xffKØ \xd0)\xb4\xd1b\x7f\xd8\xf1\xa7$\x05d\x9dq\xbc\xfc\xce\xd7\xc4" } internal_nodes: { key: "AkA=" value: "\xab)<\xc4o\x10{d0\xf8\xf8\xe1\xa1~\xb2J{^4D\x1bx{\xa8\xa4\x8a\xda\x1a\xe9Ґ\xf1" } internal_nodes: { key: "AoA=" value: "\xce\xc1\x8cz\xccA\xe7\xb7\xfcN\xe3\x90p\xe5Z\xfc\xf5^&\t\x18\x95\xbbܡ\xd0tQ\n\xf2\x83\xec" } internal_nodes: { key: "AsA=" value: "t\xde,\xeeb\x9b\xa5\x86%\xe8\xca\xff\xf21\x8a\xa1i\xc0\xa9â̘\xfd\x1fJM\xa3\xdd@Y\xb6O" } internal_nodes: { key: "AwA=" value: "y\xbe\xeb\x7f\x93ᄪ\x1b\xf0R\xcco\x89\xf2\xb4-\xd4\xd9\xce͉\xb9\xcf.@`zv$EI" } internal_nodes: { key: "AyA=" value: "Ihb\xe0?\xba\x98\x0b\x16\xedB\xe7\n%Cg\x97\x00\xbd\xf6\x13\tTy\x13\xc2\xd1].\x0b\xe6J" } internal_nodes: { key: "B+4=" value: "\xb3\xcf\x1cx\x08\xeeoysOR\xb4\xf0e\x1b\x9aw\xfaN\xc8\t\xb2\xc1\x03b\xcc\xf2q\xe6\xec҇" } internal_nodes: { key: "B+A=" value: "|\xfa\xf3\xe3\x9aszf\xf6Õ \xf6\xec\xfb\xc9\x0f\xea\xe5\x87*\x84\xb4,\x1e\xabZ,/\xf2s\\Q" } internal_nodes: { key: "B+I=" value: "\x1a\xe75\x95\x8d\x8b\x84\xb9\xa7\x15\xe1\xc3\xf22s#\xb1\xa8,\u0099Z\x8bm\x12\xa2P\xb8\xa1iŨŠ" } internal_nodes: { key: "B+Q=" value: ";\x9e\x1eŖ\xc0W;\xfd\x93\xf3\xdc\x108\x9f]}a\xde}(Đ¯\xe0\x91\xb7\xce\xf4ĐŽ\xbe\x17" } internal_nodes: { key: "B+Y=" value: "\xb5$=\x85\xaf\xa8uq\x87\x80|\xc9\xd89}1D\x03\xbf\n2y\x94j\x8e\xb2\x13\x1dw\x10\xf83" } internal_nodes: { key: "B+g=" value: "p\xa8\xb1Ũ\xbf\xbb⍰$HŅ &I\x06\xdezw!\xff\xafn\xa6\xaa<\x91\x86Z\x84\xfb" } internal_nodes: { key: "B+o=" value: "\xfe@\xed\xeb\x8d6\x86i\x8aiv\x12A\x7f\xc5O%7B퓱\xfd\x94\x8b\xf4\x7f\xa5\x98\xf2\xa1\xc1" } internal_nodes: { key: "B+w=" value: "fU\xff0d\xd3\xd4P\xf9\x82\xfb\x85\x1c(N\xf7\xecP\xdf|O\x9b2h\xfe\xd5\xeb\rŲˇ\x94\xe4" } internal_nodes: { key: "B/4=" value: "\x06B\xb49\xac\xf3\x90Qj\xb9\x17\r+N\x96\x8cL\x95\x94\xbdt\xcfM \x96-q\xab\x9f\x81\x8e\x91" } internal_nodes: { key: "B/A=" value: "9x\x13\x02\xe9T\xe8N}T\xbd\x02\xa0\x12\xf7\x1f\x1e\xb0\xddD\xb5\xa2o\xcfl%\xed~\xee\xd7>k" } internal_nodes: { key: "B/I=" value: "TP5\xba\xf9ĶŠ\xa2Vr\x1d\xbaN>4T\xd8~\xb2\xa6>#\xfed\xe7\x89\xd7_\xba~6\r" } internal_nodes: { key: "B/Q=" value: "\xafU\x93\xbb\x03{VŲ´\x1a\xf3\xfe\x05\xd2\xc6\xcaM\x14\x8155r\x11aq\xbd=\xea\xb2K\x16\xbf" } internal_nodes: { key: "B/Y=" value: "\xad\xc9k\xd3\xf6z\xc90\xda\xc0O\xacl\xb9\xaa'\x82@l\xc7Fl&߄\xd7f\xbdZ\xb5\xf0 " } internal_nodes: { key: "B/g=" value: "\xb2\x1f\xff\xa4\x1f\x03\x1a,LÕģ\xf2:4\x161\r\xca7\x08D\xf7\x03\x9d(o\x0bod\x8dh\xb5" } internal_nodes: { key: "B/o=" value: "=\x0b$\x1d\x8a0\xac\x14\x937\\T\x9b\xc4'qY\x1a-hic\xaaiVFq\x07\x96\x17$\x9e" } internal_nodes: { key: "B/w=" value: "\x84\x1bS\x9e\xfa\xccwh\x7f\x83[\xa7\x19\xb2y\xfa\x18Gc\xb4Z\x889/\x16\xd0@m\x976\xa0\x80" } internal_nodes: { key: "B04=" value: "\xb58\x01G\xbcJQÔ¤\t^ \xced\x93İ\x8ax\xedX\xf08o}\xd4G\x85\x99jx " } internal_nodes: { key: "B0A=" value: "\r\xb4\x07\xbe\xbc\xee\xf4\xde|\xd5b\x85\xf6Ĉ\\\x7f\x8e~\x17-47d\xa0*^\x82]x\xd7\x1a" } internal_nodes: { key: "B0I=" value: "\xb5\x86\xa0膀Cb*hY\xe7}\x1f\x1d\x19o\xca\x1c\x9d\x0e\x94%B\xb5\xcd\xd0ĖŦ\xb7\x89#" } internal_nodes: { key: "B0Q=" value: "\x84<\xcc\x0e\x8c\xce\xf2\xb7\xaa;\xebe\x1bA\x8e\xb0#7\xec1\xeb\xb7<\xa2'\x88\x8c\xb8\xfc\xab\xb8\x01" } internal_nodes: { key: "B0Y=" value: "\x00Y(q\xc4+p\xb2\xb2TU\t!1\n\xa4\xcd\x1e\xab\x82\xe5\xaaÍĒX\xf3\xad\x8a\xe06\xa8\xe0" } internal_nodes: { key: "B0g=" value: "\x06\xf9\xe0L~ \x08\x0e\x8b+\n\xb2/\xde\xfc\x89A^1\x1bv\xbd\xb8\x14\xff+ɀE[m\xd3" } internal_nodes: { key: "B0o=" value: "\xceH\x97\x1a\x9bV\x05\xe1\x19\xd5,MŃv\x9c\x1dI\x14\\\x18q\xb0\xcax\xf3\xa5\x03\xbcv\xa5\xfb" } internal_nodes: { key: "B0w=" value: "\xa0]܍vyŨĄ\x94\xa4#\xb6\xe0]\x8b|z\xee\x86;\xa1\x00;\x192!\x152z\x1f2C" } internal_nodes: { key: "B14=" value: "\xd2\x13>\x0c7]S\x81\xd8Įŗ\xbc\x81lh\x8b \x08R\x1f\xfb\x99\xa1Ûž\xc6\xe7\xe6L\xa4\xc0V" } internal_nodes: { key: "B1g=" value: "g\xe9x;\xef#\x8b\xedJ\xfa\xfbn>\x12K\x1aAؓ\x85\x01\xa2\x08}\xb2\xf8\xdc\xe5\xc2\xf28p" } internal_nodes: { key: "B1o=" value: "\xbe\x9f\xe5\xef?R\xb2\xf4[Be\x97^:\x91\xbf\x92\x19\x019)\x1dK\x93l\x14\xa3\xa7\xe6\x8e\x10\xf5" } internal_nodes: { key: "B1w=" value: "\x07\xeb\x07\x95\xdd\xfc\xc9Bt\xe8\xada{q\xc2C;\xc0\xcbÜą0\x7f\x0f\xca\xf6={U\x99\xe4h" } internal_nodes: { key: "B24=" value: "\x11\xc9+A\xaec\x8a)j\x82>\x1a\xdf\x1c{.\x1d3\xd7{βH\x0e\xe3\x14Hkzrt\xfb" } internal_nodes: { key: "B2A=" value: "'R(\x1d\xafU\t\xb4\x1b\xff\xeb\xfb\xac \xb7\xb3\xdd\xefr%w[X\xf44\xcd\xf8\x12\x1b\xd0s\x81" } internal_nodes: { key: "B2I=" value: "Ãĩ.\xe0b\xc91\xafEB>\xd9\xfd\xd7^\\}G7\xb0fZ\xc3\xe6\xdb_Kj\xb3\xe7.\t" } internal_nodes: { key: "B2Q=" value: "\xdc\xdd\xf7\xe9;\xb6\x05M\xea\x02l\xeai\x17\xb9\x0en:\xca:\x163\xb9\x18\xe54\xf9\xad\xb21\xd56" } internal_nodes: { key: "B2Y=" value: "\xdbN\x96\xc2MX8\xa6(\x84\x96ā\xa52\xf0ŨĨk\xe7\x9aГ\xb2\x80 \xb2ã›ĻZV" } internal_nodes: { key: "B2g=" value: "\xf6\xf6.\xdd\xe9J\x8b\x9e\"\xe7E\xf9\xf2\x83\xd1tÍ\x94U\x87Oq\x01\x85\xfa\xc7)\x97\x19S\x0f" } internal_nodes: { key: "B2o=" value: "\x17)\x83͕u\xfd\xa8&\xc5\x05\xc2۝\xea:\x93+\xb5Q\xb9\xa0=\x0e\xcf|\xc1\xe0Z\xf4\x95Y" } internal_nodes: { key: "B2w=" value: "\x86o\xb8\xb7\xe9\x1b\xdc\x1e\xaa8}\xa3\x0cBŲ#Jw\xb8\xac\xfb\xe4\x81t;?_\xcf\xd7\xfbb" } internal_nodes: { key: "B34=" value: "\xb9,h\x90`\x88\xf3)!\xaf\xe0Z\xd5 \x99!۟\x892\x1f\xe3\x8e\xe6\xaeE\xbe*\x04\xb4\x0c\x9a" } internal_nodes: { key: "B3A=" value: "\xe1\x87{\x1b\xb4_\xb4?\xee5\xf8\x929\x99\x1b\xa26l\x86\x193OM\xb5m\x84\x08w;\x18\xf2~" } internal_nodes: { key: "B3I=" value: "TØˇ2\xbe\x04\xb9\xa1\xdc?\xcfw\x7f=\x14\x87\x92\xb9\x8d/\x8e{O\xc3\x15#\x83\x1f\x82\x9a\xea\x05" } internal_nodes: { key: "B3Q=" value: "\\\x16\x0fb&\xe3\x07(\x858\x91n=\xf5\xf4\xd3\xe51G\x0f\xa8\x12P\x9c\x1a;\xf4D\x05,\x85\xe5" } internal_nodes: { key: "B3Y=" value: ")\x10\xf8\x17\xb7\x11\xc37\xac\xe4\x01f\x87\x95D\x18m\xe93\xadp\x7f\xcbK\xf1\xc0\xec\x87\x0b\xbf\x17\xa3" } internal_nodes: { key: "B3g=" value: "L\xf2\xb5Z\xe1\xb6ëǃ\xa6\xaf$\xc5L\xba\xc5Đ?\xc4\xdd&':͞\x0c6\x9bĐĻ\xf9" } internal_nodes: { key: "B3o=" value: "8o0\x87\xe6\xb5\xc3\xceT\x1b\x86\xec\xe2\xab\xc1Yw\xc1\x9a\x05\x03\xb8\x04\xb3\xc6\x01\x83\xa4\xbe\xbe\xf2a" } internal_nodes: { key: "B3w=" value: "Y\xb3\xd4#\x86W\x1a\x0f\xbb\xa3\x03\x12\x04\xb8\xe7Õŋ\x18Lt)\x98\xfdj6\xa1\xe0*U\x97\x10\x9f" } internal_nodes: { key: "B44=" value: "o*Q\xbb\xaa\\\xee\x9a7\xb1\xfcd/\x83@\xd5x\x15y\xceI\xe1\xcf&1\x14\xa9\x84zÉĄ\x1b" } internal_nodes: { key: "B4A=" value: "sG\xb4\x92\xb9\xcc\x03\xbc@\xa3\x91e\xf4\xceØ­\x11e\xdd\xec%\xe4TK/m\x17T\xfcbp\xa6" } internal_nodes: { key: "B4I=" value: "\xabo\xfa\xc9\x1d\x99\xa7\xc8æēš\xe0\x05_AK\xe3\xb2\xdbG\x87t\xe7\xff\x1c" } internal_nodes: { key: "B4w=" value: "\xdbn'#~\x1b\x84\xa6\x18\x88\x06\xff\xe9r#C\nE\x19B\xa0\x9d\xc6~\xa8\x07\x90:\x05\x0e\x9a\\" } internal_nodes: { key: "B54=" value: "\x11[\x1d\xb6\xa1\xa2a\x10;\xa7\xe4\xba`)\xc3\x1eQ\rЁ\xbb!\xb2\xbf\xa2\xe6\xb8\xfd\x92Å a" } internal_nodes: { key: "B5A=" value: "\xe0\xd2\x15\x03\x1f\x82Ee#\x1b\x9e\xb7?e\x94h\xaeJPN:=Ôˇ\x83\x95\xff\xd58\xba\xa1\xdb" } internal_nodes: { key: "B5I=" value: "\xd5\\\x18F\xd4\xc3\x15uR9\x08@r\x87\x8d\x97\x04\xe8\xc6\x07G\x14\xb8\xf6\xad\x9c\xa5\x1e\xe1\x87\xeb\xc2" } internal_nodes: { key: "B5Q=" value: "0vS\xfc:įŠn2\xe3\x94\xde\xdd^\xbe@\x1b\xe5\xc5ķ…œ…\xc0D\xb8\x11\x86IYy" } internal_nodes: { key: "B5Y=" value: "Y+\xa3\x8e\xc4\x1b\xd9\xcb\xc9\xcc\xea\xa5D\xa6\xf3\r\xb8#6S\xdb\x03h\xe9F\x0be\xba\x18\x0b\x12\x9b" } internal_nodes: { key: "B5g=" value: ".\x15\xa1\x84\x9c\xdb\xebH\x914\xbd\xf3r\xf3\x94=X\x14\xce\xf1\xa6\xdfa\xdb\x12\xd4\xc1Ū´\xf95\xc7" } internal_nodes: { key: "B5o=" value: "\xac\x1a\xa9\xaf\x01\\\x92\xba\x8e=\xc2R\xa9{\xff\xc89\xe4}\x82r͙\x1c\x13@\x85\xd4×Ŧ\x88\x00" } internal_nodes: { key: "B5w=" value: "\x13\x96\xf4qTNA\xa4e\xd66\xbazk\x97\xee`떂\xe5;y\x12J\xfd\x97\xc2s\xa9\x8a\xb6" } internal_nodes: { key: "B64=" value: "\x1e7\xe7\xbcW>\x12b/ČÃ´\x00\xac\xb7\xc5\xce\x1d&\xd8┊K\xb3XT\xa2ŒV" } internal_nodes: { key: "B6A=" value: "u\x0b\x07\xd7ourRF\xb9\xbd\xd2\x1e\x8e\xb7\x8c\xbe{J\xdbJ\xef\x01+\xab\xf0\xd9N\xd5\xfdy\x13" } internal_nodes: { key: "B6I=" value: "\xef\xadn\x17\xea|Fh\xaf\xc0\xa5\xf7\xf9$(@\x90DGVL\x05j\xbc\xa5=YC\x08\xac|\xa7" } internal_nodes: { key: "B6Q=" value: "#:ØŖ\xa9\x8f\x9aU\xc6k\xdeZ\xe1\x12y\x11P\xe5\xdb\xf5\x89\x08\xff\xf0\xe9\xad\x0c\xb8\x16}\xa8(" } internal_nodes: { key: "B6Y=" value: "n[\xa3u~ē1\x1a\xf9Jz\xe6\xf5β\x11<\xbd\xa6\x1c\xb3~\xceJ\r2F\x87\xcc\xe5\xf3" } internal_nodes: { key: "B6g=" value: "\xcbk\xd7\x11\xc20\xcb\xee\x1b\x89a\x10\x02\xd97X&\x1b|\x04c\xb6\x8f\xa4?i8\xb7\x19\xa7\x8a\"" } internal_nodes: { key: "B6o=" value: "\xdc\xf5\xbf->ԋ5ÂĒd\xe0%7\xf5\xad:\\8\xa5S\x1d\xe9Ōž)\xa3\x9d\x7f\xd9ĮŸ" } internal_nodes: { key: "B6w=" value: "Q\xf7\x9bN\x16\x92\xa6\xc7,\x9az\x15m/\xa3:\xefV\xaf\x04\n\x04\x80\xa0\xacŌ°I[\xb8K\xe6" } internal_nodes: { key: "B74=" value: "\x8c\xac\xa1\xb42d\xc11n\xe1\x88\xd29\x83&#\xc4\x17\x10\x92\x83r\xe3Ô­vO`.\x8fl\xd8" } internal_nodes: { key: "B7A=" value: "\x80\xf6\x88ڀ\x13\\Ú¯\xfbBÃ\xcc92\x98*DR\x7f\x89\xda\xe4\xd3Æĸ+\n\x13\xfa|" } internal_nodes: { key: "B7I=" value: "7a\x19\x0e\xc45h\x06\xec\xd4$J\xc1\x9a\xfc\x8b\xc19\x03\xcb\xddN\xb1l\x1dM\xe6)\xc5\xdaz\xf0" } internal_nodes: { key: "B7Q=" value: "\xb9\x8d\xe0\xd6˯x\x86\xc6\xc6\xcag\x88\x17\xf2o\x1fS\xfe\xaa`\xb4\x1f\x98o\xf9\xf2\x17\x0f.\xad " } internal_nodes: { key: "B7Y=" value: "\xc7Orn\xa2\xe6\x8d4\xa2\x16\x91\xea\x9a}\xa3eĒ\x9c\x88\x03P\xb6\xf1\x87\x16D\xdc_\xa2F;" } internal_nodes: { key: "B7g=" value: "\xf1\xcegM\x8f\xe1;\x89\xfc6\xcf#-M\xdc\xfb\x89\x00\xfa\xfbk\xc5E\xa0\x15\x97\x11M-d]S" } internal_nodes: { key: "B7o=" value: "#&\u0098\xf0\\\x97~\x82\x81\xfb\xcb/k\x05\x9e;\x0f\x03T\x11\xef\xc1\x8d\xfb\x99\xe9Z#}\xff\x19" } internal_nodes: { key: "B7w=" value: "i\x94~\r\x1a\xbf\xdbm\xb6\xe8:CL\xf0\xef|\xfd:Sq\xc3ˊ\xe7\x8b\xc0\t\x8d\xe2́\x9e" } internal_nodes: { key: "B84=" value: "\xa0\t\xf9\xe9\xbeL\xf6&\xf4~/n@\xff7Ė‘\xdd\xd4O\xe8W\x1d\xf9\xf9B\xd6\xda\x1f\x02\xcf)" } internal_nodes: { key: "B8A=" value: "İ\xee\xe3;\x0fq\x80\xa9\xd6l\x08\x91\x9f\x93\xc5\xe6\x0f\xdd\xc0EK\x05A CmA\xc5Z\x99\xd1" } internal_nodes: { key: "B8I=" value: "\xebTxFh\xf7\xb2G\xf3\xf3\x00\xb2\xa3\xc8\x17\xf0j?h\xe6:Q\xbf\xfe\xfd\x9a\xeeR\x87f\xebr" } internal_nodes: { key: "B8Q=" value: "\x91PW{<\x8a\x10h\xd11\xf3\xa6\xa0EP\xaa\xe5k\x05\xb8lQ\x05\x04r\x0b\xdfW\xbe\xf8&\x10" } internal_nodes: { key: "B8Y=" value: "Įš\x04\xa0r,\x06cĪ“\xbfY\x99q\xae\xdbU/\xf8\xa2w\x96\xae7\xa4@nW\x08\x17\xa09" } internal_nodes: { key: "B8g=" value: "\x98\xfcęĩĻ9\xf3\xae\x96\x10\xf0sSL\xce\xe9q\xfd\xe1\x8dv#\xc3\x1b\xdd\xc5'\x19f\x89\x90\xf1" } internal_nodes: { key: "B8o=" value: "%\xb1\x01\xf6\xa1!\x00\xb5\xaf^Z\x9c\x06\x83\x99;\xc2\xee_\xd0\xe4\xa4.\xf4\x88\x951\x10\xe1SiJ" } internal_nodes: { key: "B8w=" value: "2\xd5S\xfb\x08ėS!\xc1mU/r\xccm\xb1cK\xd7\xca\xfd2:\x8cQ3w\xceD\xce\xf7" } internal_nodes: { key: "B94=" value: "\xf9\xc3bX<={\x94\xce\xfe\xaeq\u009b/\x100P\x16\x9d\xe9\xd3R\x1bu\xbf\xfdysG}Y" } internal_nodes: { key: "B9A=" value: "\x99Ȩ\xe4\x11\x9b_\xf8\xa5\xc0\xef\xb8\xe2\xf3\xe1ʧ\x14i\xa1T<\xdeY\xc7Æ˜Ę•\xfa\x87\x8b" } internal_nodes: { key: "B9I=" value: "\x1c\x8djCjÚž\n\xb4,\xca'v\x1aR\x19B\x14FS~\xf8\x15$\xcc\x7f\x1e\x8f\xac\xdd\xd7\x0c" } internal_nodes: { key: "B9Q=" value: "\xa7\xfb\xdd\xe7\x08\xf7\xff\xa3\xef\x81?j^~4\xeaĶ‘\xbcR+_nTUf" } internal_nodes: { key: "B9o=" value: "\xb2\xbf\x90\x89`v\x87\x17m\xb6\x12\xce΂*\xfd\x156\xba\x0e\x7f\xc1\xca\x06%\xbb\xeaU\xd1\xc6\xd1o" } internal_nodes: { key: "B9w=" value: "\xac\x98R\x92[L\x05\x1b^1\x1b3wW4\x91|>\xdc\xcc\x7f\x82&\x88\xd81-px\xa99\xb2" } internal_nodes: { key: "BAA=" value: "%\"&\xeaLz%W\x9b\xdeM.\x11K\xadIs\xc7\xdf\xf4\xb3\x1bQ\xca\x1e(¨2\xf3\xb5U" } internal_nodes: { key: "BBA=" value: "T\n\xc3\xd7S;\x15\xf9\x88Ni\xff̀d\xb4t|b\xdc\x1c\xefv\xa0y\xf6\xca7\x8d[\x12\x1a" } internal_nodes: { key: "BCA=" value: "m\xa1IC\tT\xf4H\xab\xf1\xf4\xa4^?\xad\x9b\xf5\x10\x87\xa6\xfc%\x15{\xa3Öģv\xb4ĘŖh" } internal_nodes: { key: "BDA=" value: "$\xa3z7\xc6H\xe9\x02\xfc[\xe5\x14\xc4\xcb\xed4\xfe\x83'\x0b\x11\n\x07\xf1x\x12\x9e\x8a\xc9\xf0&~" } internal_nodes: { key: "BEA=" value: "\x8e\xad0\xdbuA)\xbe\xac\x93/*O\xbe\x06\x9d0&S\xa2T\xf2W\xac\x14\x91\xc8b\xa8\x8fC\x9b" } internal_nodes: { key: "BFA=" value: "R'?\xe4\x1d\xc0\xb4i7`{_h(\xf3\xcbHR\xa5\x90W\xd7M\xd7\xe6\xc1i\xbc:\xe0C\x9e" } internal_nodes: { key: "BGA=" value: "\xb8ԈG\x00E\xf9\xb6\xbeɈ\x00y\x15\x03\x9b\xc4w\xde\x12\x90&\xe0\x08u\xf6\x91l.\\\xf6\x1a" } internal_nodes: { key: "BHA=" value: "K4\xc3\"\xd0c\x93\x03\x98\xc5\x0b+\xf9/\x9b\x0eX\x0bY\t=\xdd^\xc4\xf4\x9c\x03ZJ4\x1aq" } internal_nodes: { key: "BIA=" value: "\r\xc6uz\x05i\xfa%}\xd3mC\xd1V\xe6z\xbd\x01B\x1b\xe0\x94}][\xabÖŖ\x82Â\xfe" } internal_nodes: { key: "BJA=" value: "\xd3\xdd\xc2Bv'\x17H\x9e\x18\xf2\x98c)scI\xbb|S\xbfL\xc1p\x9dPZȄ^\x9e\x91" } internal_nodes: { key: "BKA=" value: "\x88\x02\nn\xf6\x96r1\x83UbN\xa8\x82?\xb0Ͱ\x1b\xd6=\xdcee1MvP\x88\xc3\xdd0" } internal_nodes: { key: "BLA=" value: "\xf6\xf3\xa9\xd4\x01A\xef?\xa9\xb5O9\x81\xa4ÛŗŅĨ\xeb\x14\xabX^\xc6čCi-\xfcU\xb2" } internal_nodes: { key: "BMA=" value: "5\x90_lJ\xd0/\xa3q\xca\xf7\x95\xd2\x14A\xf2f\x84\xc0\xe8,b5\x9d\xeaߖ_\xce\xe0\xe8\\" } internal_nodes: { key: "BNA=" value: "x=2\x04\xdd6>W{\x00Y;UˍU\x0b\x0bV\xa4T\x92\x94\xd3\xc6\xe7\x8a\x03Z\x97<\xd8" } internal_nodes: { key: "BOA=" value: "C\xc0ͯF\xe66\xb3\xb8\x80\x8a\xb5A゘B&\xc5q\xdc\x02\x1a\xcb\xd4\xe0\xd7L\xe6$\xb8h" } internal_nodes: { key: "BPA=" value: "S#\x9c\x97\xackΚNR7\rjq6S#zQM\xaa\xca2c\xe8i\x86\xa3\xc99dx" } internal_nodes: { key: "BQA=" value: "\xc0\x8c\x1bmT+Z\xcdQ\xab]\x9e\xe5\x14\x10\x82Īą\x07-\xd9~F\x83Ų€\x16Z\xc9g\xe0\xd9" } internal_nodes: { key: "BQg=" value: "\x02\xc0\x94\xda7\x90\"cM\x01\xed\xf8\xc3\x02\xeaj\x198+\xc2Äē4\xd3\x7f\xa5\xe9Ō‚\xf4_\xdc" } internal_nodes: { key: "BRA=" value: "\x8e\x85s\x9c\x9fÃą\xeb\xb4`\xd2\xde\xc97\ntI\x1d\xef\x81\x1c\xa3\xa2\x02\x93\xbb\xb4it]\xb3n" } internal_nodes: { key: "BRg=" value: "G\x9a\x04\x1c/\xfb\xd8R\x87&\x82{\x8e\x9fD?y*\xb5\xb6Į‚\xa2\x1d\xc3\xd7\xdcV\x05{\x02 " } internal_nodes: { key: "BSA=" value: "\xd6\xd8\xef\xd0\xfb#\x94\xad\xbc\x07\xa6\x83h\xd3\xeb\x94@\x1c\xa1\xea5\xf0\xfaQ1R\x86\rĶŗg\xce" } internal_nodes: { key: "BSg=" value: "\xe3\x96%D\n\xd4i:\r\xe8|H\x7f\x8a\xac\xa3_MS$:Q%\xeb\x08\xfa$\x8d\rZ\xad)" } internal_nodes: { key: "BTA=" value: "\x06E\xe9\x15\x13Z\x0c\xc9}G\xc9!\x11ژ\x0f\xcey\xdd\xdb\xfd\xd3\x15'gL\x9a\xa6\xed\x01+\x02" } internal_nodes: { key: "BTg=" value: "\x18>\xdeU=t\xfa\x82[\\\x86\xfb\x0e\xbb\x89F\x0f\x18\x8a\xd8\xe2\xb0\\7f\x02\x85\xee\x90Y\x07\xb3" } internal_nodes: { key: "BUA=" value: "\xdfxt\xbe\x81\xdd.\x91\x0cL\xcc\xe2\xbb\r\xd3\xf3\x16g,+i.1\xf2\x1ee\x90\xae\xc3\xca\xc5," } internal_nodes: { key: "BUg=" value: "\xb2\xa3k\x04\x174\xe4\xe8\x9a=`Q@(\xf7ÃĄ\x97i\xac\xce\t\x9aA\x91\x9fN\x8f\xff\xd5Om" } internal_nodes: { key: "BVA=" value: "|\x7f\xbf>\x04\x10\xa2ۈN@U(\xa5\xa9\n\xcd\xcc\xe5q\xe6\xdc\xceWL\xec'\x86\x8bË \xfb" } internal_nodes: { key: "BVg=" value: "l\xd0`\x98N\n\x9aÆģ\x08\xd8\x15\x10cb\xafʗ\xe8\x04Lyv\x19W\x88\xa2\x99\t\x85\x86\x16" } internal_nodes: { key: "BWA=" value: "\xef\x1818ěK~Wo\x95\xd9`\xb65\xedL\xe1N\xf6w9{$\x03Ŝ2\xeb%\xe4\xe6" } internal_nodes: { key: "BWg=" value: "\xfa\xc5f\xda:\x92\x9fo\xaf%\x0fw\\(\xfe\xb6\xa3\x01\xee6\x90\xae\xfb\x87W\xff\x04%+7\xbdW" } internal_nodes: { key: "BXA=" value: "\xaa\x03\xac\xc8A7\x80\xee\xcd\xfe9wu\x88NKO\x05\xbf\x85^rCĪŦ\xc67>jx\x85\xcb" } internal_nodes: { key: "BXg=" value: "\xf0}\xe82X*\x12Y\xefÕĨË­N\xf1\xa8w\xdb\x1cR'\xd2\xc3G\xc7:\xd6Ū¨\xc6KY" } internal_nodes: { key: "BYA=" value: "\xe5Ua\xdb\x01\x96\xec\x0f1\x1d\xcd\x18p\x88\x08p\"\xb0\x8bא:dhL\xc5\xd6\xfc\xc0\xc52\xa6" } internal_nodes: { key: "BYg=" value: "\xe2\xb6aĐĄ\x82^\x0eK\xeeG\x897\x04i\xa6\x96\xb1\xbc\xddhQFS\xf9ŪĩC_ZŌ‡" } internal_nodes: { key: "BZA=" value: "\xa8\xa2\xfe\\\x9f\xa4\xe5o\xe1\xa5\xcer\xca#\x8b\xbc/\xad\xddr\xd2\xe2\xdcA\xc6\xf8\xc2s\"\xe2\x190" } internal_nodes: { key: "BZg=" value: "\x9a\x16\xc2_\x15+b\xb4\xc76Dvz\xac\x07:\xa9\xfa\x9f\xbf\x83\xdf\r\x84\x9e&C\xbd\xf1\xd3Ũˆ" } internal_nodes: { key: "BaA=" value: "\xae'\xbe6\xf4.X\xf0\xa3\x89AZ\x03,\xf4Es\xaaR\x1bk%Ns\\\xf0Bt\x02\xf2\xb13" } internal_nodes: { key: "Bag=" value: "MG:\xf0d\x19\xc2#&\xb1\xd38\xf4\xa1\x1c*\xebr\xe9\xf1\xeaad\x92\x85\x0b\xfd\xe0\x18\x91\x04\xa1" } internal_nodes: { key: "BbA=" value: "k\xdfx\xd5\xd0\x11\x1a\x0bD\xb8Gt\x8eh0-\x81\xba\xe9/VËŽ\xb3\x87n\x9e\x156\xb6\xf1\x19" } internal_nodes: { key: "Bbg=" value: "\x8c]x!\x01\xa1y\x1f\x9evB\x8b=\xd8\xe9\x87\xf3\xdbŨ\xc5\x1d\xee\xedB\xfd\x8a\xf2\xaa\x10\xfa\x82" } internal_nodes: { key: "BcA=" value: "\r\x14X96+i\xac\x9c#\xaeb\x94˃\xd0>N\xfa\xed\xa2\xcaB\x04؛4\\\x06\xe9\x0f\x80" } internal_nodes: { key: "Bcg=" value: "\xa6?\x93\n\xa3\xbc\xbev\x16F\xdc\xea:=b\xe8\xf6ȅ\x08\xce\xd2\x12G=\x88<\xf4\x93\xd1Ōŋ" } internal_nodes: { key: "BdA=" value: "\xa0ÃĒ-N\xf9Mbw\x80\xf8Lq'\x9e{1QL\xfb\xa9\xc2\xef%\x13K\xf8\xf6Ȇ:\x03" } internal_nodes: { key: "Bdg=" value: "\xc1\xc9<\x05\xfaM\xf7U$\xe6\x8d\xf1\xf2KF\xceI\x80N`t8\xfd\xfb豍V({\xb9\xee" } internal_nodes: { key: "BeA=" value: "ņ“Š’\xbe\xb5\xfd\t\x9e\x9f\xa3\x81\xadui\xdb\x1f\r\xb0\xac\xd9\x1aD;\xbb\x967\xc7\x0eY\x07}" } internal_nodes: { key: "Beg=" value: "pm0\xdb2\x03\x9ea\x94M]\x0f\xd5\xfe\xed\xa9ōŒ†›\x0e.d\x05\xee\x11\x92\x1a\xe3\x9bn\xbf" } internal_nodes: { key: "BfA=" value: "\xc2\x08O\xceaWpŪŽ\x91~ńG^!]\x16\x0b\xd1p=\xcd\x10\xe9\xae\n\xdf\x15\xea[\xb7" } internal_nodes: { key: "Bfg=" value: "\x9c5l\xa0\xbf,^X\xec\x04\xeb\x0c6\x06\xed\xaf\xe5\x97$\xf7\xeb\x7f\xbc[\xc7\xe4Y\x16N\x0ek&" } internal_nodes: { key: "BgA=" value: "!\xd0ʨ4K\xc6?\x94H]\x89wH\xfc\xe8\xe6Øš\xc8Q\t\xf0\xccwO+\x0f:2I\x9d" } internal_nodes: { key: "BgQ=" value: "\xc6\r;\xf9\xcb\xe8^\xc1`\xf4\xf1$\x0e\xd5\xe2Ū†î—\xa5\x9d\x05)\xbb\x95\x82\xc8=͂\xfc" } internal_nodes: { key: "Bgg=" value: "\x18E\xac\x02\xb0xؤ\xe9\xa7)#d\xaf\xc5%\x83İ\x93\x82~\xdbs;\xd9a\xac\xb0\xea\x03\"" } internal_nodes: { key: "Bgw=" value: "\x9f=Z\xe6\xc9\"ČŠi\x0c\x08O3gr\x00!\xb2\x8c\xb42\xdc\x1ezęŋ¤-.\x19\xb8\x01" } internal_nodes: { key: "BhA=" value: "\xcb\xfd_x\x057\xba\xbd~\x04\x9b\xc3⏛&\xff)ːQx\xe2\xe2\xd9D\xe2\xa6#R\xd1d" } internal_nodes: { key: "BhQ=" value: "\x1a\nD\x82\xfd0\x1c\xd3l\x05\xf9\x8e\xc1\x95\x1b>\xbf#\x96\x12\xcc\xf0\x84\xc4\xfd6\xd6E~\xd9\xd2\xf7" } internal_nodes: { key: "Bhg=" value: "~L\xff(\xa5\x90\xce\x7fV1Įŗ\x9d\xb8\xfc]`\x1a\xed\xfc\x05[H\x00z-%ʁn\xcd]" } internal_nodes: { key: "Bhw=" value: "1\xad\xbc\x0eq:\x94/:\xd8\x1fBYJ\xc9\xc9\x0f\xa38\xb3\xec\xdc\xf2M\xd5\xf3\xe3yxr\xf1W" } internal_nodes: { key: "BiA=" value: "]#\r\xa7\x18\xb8Īŧ\x05%\xc3\x03ɋ\x10uF\x93<\xef\x1cΟ?4c\xd3JȅÄļ" } internal_nodes: { key: "BiQ=" value: "d\xf1'FJu\x11\x08\x88y\x03\x18\xf8\xf3x\xeaO@\x10\xa72\x8a\xaaΓ\xe6yc\xfcG\x15N" } internal_nodes: { key: "Big=" value: "\xa9H\xe4\x9fo\xb1\x80C_\x02O\xc9\xdf\xf2$\xc3\x11\xafL\x95Ņ‹\x16K\x83\xa3\x13\x80y\xaeTH" } internal_nodes: { key: "Biw=" value: "\xc1\xc1\xfa\\\xe6\x1d\xd0ߔO\x1a__\xb5\xab\xcc\xe7\xc8\xe4\xe9\xeb\xb1\x16Z\xb6\xa4\x9e\x96d\xe4H\xaf" } internal_nodes: { key: "BjA=" value: "\xea5\x1dU\xb5G\xc0\xbc\x9e\xf4\xa4\xbay|\xa5\x9bq\x05\x01ßģo\xe8P=\x89\xc6\xf7\x16|<\xa2" } internal_nodes: { key: "BjQ=" value: "\xc9\xe4\xcf+\xc4X\x8e\x07\xbf)K\xeb\x0c\xde\"\xac/;\x95\xb5\xc6P0\xfd\xf6L\xf2\x10/%\x82N" } internal_nodes: { key: "Bjg=" value: "A-\xc2SÆ \x1a\x1c\xf5b\xa6\x9b\xdcA4\x11\xb2x\xb3\\\xcfiu\xf0)(\x02VU\x01\xff\r" } internal_nodes: { key: "Bjw=" value: "\xbe\x95\xe6e\xa37\xe5\xc5\xf9\xcfX\xc8\xe23\xa6֍5拄\xba\xbc\xf4\u0084\xf5\x8cu{>\x8d" } internal_nodes: { key: "BkA=" value: "n\x1c\xfd\x9e5W\xf4\x11\xa8\x0eu|Έ\xd9DZ\xa8\xdaA%\x99\xf5\xb2\x0c9\x80\x97k\xa1Y0" } internal_nodes: { key: "BkQ=" value: "\x8a\x15BF\xfect\x1c\xb2\xfdq\x97t\xcf_2\x10#\xecD\xe5\xe6\xf0\x9bxGÖĒ\xe9\xed\xb32" } internal_nodes: { key: "Bkg=" value: "\xa3\xf5ÛŽ\x86r\xbb\r_\x8e\x90\x8aŲ–\x07\xbc\xb5#d<\xcfŨ’I(\x03\x9f\xb9\x14c\x19\xea" } internal_nodes: { key: "Bkw=" value: "'Ȳ/Z\x11\x8c\xc0\x0b1\x98\xee~R\x85\x14ib\x07\t\xddÕ |\xa5K:\xdc\x1eN\x02\x95" } internal_nodes: { key: "BlA=" value: "ÂŦ\rA\xd1\x1e\xb1!\xf6\x0b\r\xd9\xc0$\xb5\x02\x13B\xbb\xd5|\x82\xf2J\x19\xf4M\xa3G?\xfb\x9a" } internal_nodes: { key: "BlQ=" value: "\xe2B+Ōŋ\xc6\xdbԚ\x820\x11\x8cAv\xa5\x02o%\xf0\xfa\xe8\xec\x1bE\x13q8\x07A\xcam" } internal_nodes: { key: "Blg=" value: "\"zĮ„\x15\xec7\x8d\x16햧\x80D\xcd\xc3\xf3\x86\xf8\xdfP\xa0\xe9\xae\xe1\xc2\xd7\xf4\x7f\x9b\x86)" } internal_nodes: { key: "Blw=" value: "M\x9e\x8e\x91\xfe\x7f$L\xfc\xc3\x16\x03ԏ͙1\x92\xff\x06\xb1\x0b\x01ߤ`W%+\xe2\x1d'" } internal_nodes: { key: "BmA=" value: "\xa2\xab\x15$g\xdd,\xcc&C!\x13\xf3\xe8\xf7,y\xb7\xa2\xeb\xf7\x94\xa7\xed\xcd<\x7f\x91B\xcd\xfc\xdd" } internal_nodes: { key: "BmQ=" value: "\x08op\xa0/4\xa6\x90\x1chTC\xca9\xff%\x93bɒo\xf7g\xe9u\x19\xfafKmÂē" } internal_nodes: { key: "Bmg=" value: "\xb0$7\xf61\xf7\x1d\xfb\x069\x85)\r\x8a\xa9\xb88Ū…d\xa2\x823\x15\xe3\x94\x0f\x95\xad\"\xa9\xa3" } internal_nodes: { key: "Bmw=" value: "a$\x01\x9eu\x13\u008f\xfd`\xfb\xc0+Nz\x9c\xa6\x0fR\x12\xe9\x1c\xa4\x0b\x97-àB\x9f@\x0b" } internal_nodes: { key: "BnA=" value: "\xe5\xe3\xa9N\xd8\\A\xba};ʝ\xf2\x16\xfbP4\x10\xe1Rx%\x8d\x0b\x16\nŅ‹\xae\xb2\"~" } internal_nodes: { key: "BnQ=" value: "\xd7\xe9YR\x1f=\x988T\x06#,\x8e[\xad\xd6:\x1e\x90\x14p?K\xe9\xd8В\xcdu\xee\xe0{" } internal_nodes: { key: "Bng=" value: "\xf9~\\\xd6\x13\xf1\xbfp\xda\xc4,\x19\xee\x91Z\xc1[7'\xb26z\x823\x7f\xfe\x16\x02^[\xc3\x19" } internal_nodes: { key: "Bnw=" value: "o\xa3\x996Jh-\xcb\xd1c\xf5=\xa6\xbd3\xeb\x1e\x9dp\"\x96\xb6ۗ\xaa\xad\xa5\xef{V/\x0c" } internal_nodes: { key: "BoA=" value: "\xde\x0c\xa5\xe9tU\x15\x94\x84\n\x9dҤT\x17\x1f\x87\xf8\xf5}\xb5}Oo\x8e\xdc\xeb\x8a\r%\x9e\xf1" } internal_nodes: { key: "BoQ=" value: "\x07\x07nD˗$Y\xb5\xf7\xb9d\x9d\xb5&Vl J\xdf5\xe4\xbc\xda\xd49\x07\xb6&F\xce\xcb" } internal_nodes: { key: "Bog=" value: "\xd5B\x0c\xb7\xd1Ô§5I\xa9.\x9a:1\xb1\xa6\xf2\x19E\xcd(\xb2q\xbe\x0b\xc1\xeeN\x18\xaeV\xf3" } internal_nodes: { key: "Bow=" value: "\x93\xa1\x8e\xc1\xef\xc1\xc5\x7f\x9e|}r\xe1\xe0 \xed\xd4\xeb;2zF\x85\x18\x9e\x12U\x14n9\x05q" } internal_nodes: { key: "BpA=" value: "8\xe4\xdc\x0f\xa9\xd5}n\x17\xd0bK\x07\\\xb5\xf0mU\x04B\x13\xa7<\xe4;\x16\xaf\x1f\x87|6\x16" } internal_nodes: { key: "BpQ=" value: "P\">\xad\xe7z۟\xc5%\x10\x0fAH\x85\xe7\xc4\x14\x80F\xaa%\x11}\xa2\xfdƌX\n\xbbl" } internal_nodes: { key: "Bpg=" value: "\x1an\xa7\x85\x11=d\n\xe1[?\x16\x8f\xf8\xd27\xfc\xdbm\xe4aĖz(\xd4\xf4\x0fxDE\x1c" } internal_nodes: { key: "Bpw=" value: "\x01\xbds\x9e\x14\xaa*\xa0\xfc\x7f\"\x91\xfa\xab\xe5\xf5v\xe1Ya,ĪŊ7/\xf6\x02\xa9\x9e\xb2A\x1a" } internal_nodes: { key: "BqA=" value: "\xf3\"h\xc4\x10\xb1\xa0\xbaG~\x80m\x8es\xd3\xe5B\xf9\xdf$\xefnG\xc6\x12\x00:\xb6\xed4\xecy" } internal_nodes: { key: "BqQ=" value: "\x80a\xe8\xf1\xb6s\\\xb8\x16\xce\xc0\x01\xfd\xfb\x9a\xcf>\xc7\xfd\x87\xc9mÅ´\xa9\x01d\x13x\x98\xb4;" } internal_nodes: { key: "Bqg=" value: "T⎋\xa6\xa8\xccSK\xa2E\xff\x97mZ\"8\xc0\xf8\x19\t\xfeR3-\xea\xb5;\x94\xd8\xfa\xa6" } internal_nodes: { key: "Bqw=" value: "\xda\xca\x01P\xceƊ\x94\x847>\xa2|-g\xa4\xf0\xe7\x07\xee\xa0\x00d\x8fi\x87`\xe27\x8e\xb61" } internal_nodes: { key: "BrA=" value: "\x96\x05\xd3\xf7r\xf2'\x8c\x80\n\xe9\xbd\x18\x14\xf7\x91hA\x08\x03+\x04\xcaÖŦ\x94\xb9\xa2\x05\xee\x12&" } internal_nodes: { key: "BrQ=" value: "\xb3o;\r\x04\x18\xe6\x8dG*\xa52 `\x1d\x01\xba{\xa7\xedq\x1e\xbc\xa7\xcdu\xa1Yj\x0b\xc9*" } internal_nodes: { key: "Brg=" value: "h\xbb\x9a.\xa2\x01\xf4ãģŧ\xe5\xdc\xd0\x0b\x04Ãĸԃ&\xc7Ћ\x9f$+\xe5\x15!V\n\x8e" } internal_nodes: { key: "Brw=" value: "\xf7H\th\x1d\xf8\xd0H\x90L\x13\x1e\xef\xa0\xcdßŦK\xe4tT5G\xf1J8\xd5˟\x83\x84\xd9" } internal_nodes: { key: "BsA=" value: "\x9f\x82D\xd2R~1\x8b\xc8\xf1\x10F}M3m0\xf4\xac\xffvfs{\xcb\xc1\x7fW\xf1\xc3\"\xad" } internal_nodes: { key: "BsQ=" value: "\xd4\xedڏw\xdau\x0f`}gK\xa2w\xab\xfe\xea\xed )\xa3\xf4\xd70\xb2Y\xf9u\xefA\x94\r" } internal_nodes: { key: "Bsg=" value: "\x9bP\x1a\x17\xb7\xf9\xffk[M\xaez]\xc0Bˋ׎SJ7\xeb\x10h\xd9XÅ˛Û‚S" } internal_nodes: { key: "Bsw=" value: "\xaa\xde\r4\x17\xd7\x1b*\xb4\x03\xfaYh(T\x87\x1ac9%n\xba\xf7V\x0fwB\xc8i\xf7ܧ" } internal_nodes: { key: "BtA=" value: "J\xe2\x95+\xb2*Ū¸\x93\xbe\x009f\xe3>\xfeK\x87$ K9\xa8\x1f\xbeO\xad6Ɔ\x8c\xd9" } internal_nodes: { key: "BtQ=" value: "K\x96\x9e\xde\xd2\x00\xb8\x11\x1c\x01\rn\xce$(u:2$G\x94\xe9\xedd_[\xaa\x0e\x90\xb3*v" } internal_nodes: { key: "Btg=" value: "\x8eŨŧ\xab\xdd[\xd4:\xd0\x03\"\xf2fÚŠf@#\xd80m;\xbf=\xd3\xea\x16\x14h\xe7\xb69" } internal_nodes: { key: "Btw=" value: "\xb5*\x7fãļ\x8d\x81!\xaf\x1d6Q\xb8'/M\xe8=v\xfb\xe4\xee\\\x87[=\xd5\xf1\xe9F\xb6" } internal_nodes: { key: "BuA=" value: "r\xd6-\xa7\x00_\xab\xfeSz\x1bf\x06B\xca\x12F\x11\x13\xfd\xb8\xf3+\xa4i3V}\xf0" } internal_nodes: { key: "BuQ=" value: "\xf0?\xbeC\xf8dwqk\xc6\x0c\xddj\x1a(\x07\xd6\xe8\xdcåļ¸\x85\xa5\xfa\x81\x95\xcc\\7\x1d\x94" } internal_nodes: { key: "Bug=" value: "\xf5 \xd1Íšd\xeb\x0f\xd2\xc4\xed,\x1c\x9b\xa3\xa7m\xb2>?{J\x1e\x89\xe97+~\xa1%:U" } internal_nodes: { key: "Buw=" value: "\xb9\x05I~u\xd41\xe9\xa7\x7f\xb1\x9f\xaa\x90\xea^\xa7Py\x9e\x18\x15\x85\x95G\xe4-\x8f\xff\xcf\x00\xc8" } internal_nodes: { key: "BvA=" value: "\xaf\xafih\xc6\xcf\xe5\t\x1eéŊļ\x1e\x8d\xd72\xd0\x16\x18\xcf@m\x8b\xeb\xa97\x9a\xbf\xc6\xfb\\\x7f" } internal_nodes: { key: "BvQ=" value: "\x9a*N\x17Ć/\x1fh2K\xa7\x08\x82_/\x82\xdf\xf5z&)R\xb6\x91\xbc\x89\xb0\xebU4+" } internal_nodes: { key: "Bvg=" value: "\"\xa9\xe3g\x06\xa1/t\xca\xc2x~Ah\x00\x8dz\xc9q\x03\xb14\x0b\x15\x9bF\x88\xd5Gz\x00^" } internal_nodes: { key: "Bvw=" value: "H,\xc1\xdb@N\x12K@\x9a|\xd1\xf2\xe9ek\x00\x12\xb3\xb3\x95\xae9\xc0!b\x0c\xe1H\xc3P/" } internal_nodes: { key: "Bw4=" value: "1\xab\xf8\x9b.\xf7\xb9\xa32t_y\xffG\x18\xa0\xd2=z\x16|Z.v\x03in\xe8\x14e\xb6\xf6" } internal_nodes: { key: "BwA=" value: "\xc8ld\xcd\x0e\x8b\xa8g\xecSM\x1erz\x08\xbbY~y\xb2o\"\xc2\xd0\xed*U\xc6\x04\xaf\x1d\x19" } internal_nodes: { key: "BwI=" value: "8M\xf39,\x08'\xdc,Ė‹\x1a\xc7\xf0\x7f\x1e\xa5I\x1c,aۏ\xd6We" } internal_nodes: { key: "BwY=" value: "\xf6R\x9f\xde^&\x00\x8f\xf9\x8d%\xe6\x02\xae\x95\x94<\xbe\xae\x9e'\xb4\xc1C\x84\\\xe5Y\xc0\xebT\x9a" } internal_nodes: { key: "Bwg=" value: "\xda<\xfd\xcf\x03\xaa\xf3\x91\"\x18\x18\xb1\x1f\xcc\xd0Į˛z\x90\xa5\r}G\x82\x97\x86ax\x9c\x8e\x84W" } internal_nodes: { key: "Bwo=" value: "\xca~\xe5\x8a[]\x15W\x1ao\x99\xa2B\xf9\xce1|\xb9\xb1\xe1U\xf5\xed\x04\xaeY^\x98\x1f\xc7?\x1b" } internal_nodes: { key: "Bww=" value: "\x89u\xd59\x1b\x8drLU\xb1\xfa\xad_\x8bh\x11\x1a\x96\xfd\x8aC\xad)n\xe4\xf3W\x8e\x80\xabjA" } internal_nodes: { key: "Bx4=" value: "i\x1a\x81q\xf8p\\\xd6\xd6\r\xaa\x9f\x04\x82\xc4\xf0\x07;\xa1s1\xc3(\xddM\x93\xb8 \x01\xf8-\x9e" } internal_nodes: { key: "BxA=" value: "fm\x1c\xb6\x04po\xb9\xa9\x84\x0e\xcc\xc1\x81L7\xe2{H/r\xbe\xcf\xf1R\x82\x80>w\xbeqt" } internal_nodes: { key: "BxI=" value: "\x1d\xc6(z\x14+E\xdbcFE\xab\x96k\x81\xbb\xa3\x81;\x8c\n\x85\xb5\x1cH)\xdc\xfe\x9a\xe8\xc7\xfb" } internal_nodes: { key: "BxQ=" value: "6\xcc\x05\x864\xc2|\x1e\xccZ\xa8?M\xf7\xe5JlI\x8f\xae\xbbu\x88L\x9b,G\xf9\xc1}\xdf4" } internal_nodes: { key: "BxY=" value: "\r\x03ho\xbc\xc9j\xccpl\x9c.\x03h\xa6}\xf0\xf2˙\xd3\xee%Ų”j\x85\xc4m'\xd7(" } internal_nodes: { key: "Bxg=" value: "\x17\xa5\x85\xc1\xb5\xf8\xd56^:H\x92\x8d@\x01\x0eh\x19%F\xbe\x92\x00\xba\xde&\xec\x88\xfa\xc6r\xb7" } internal_nodes: { key: "Bxo=" value: "\x9bu\x88V,\xdd@\x05˟$K\x18\x08\x89\xb4\x8d\xd6\xf5 O\x90\xff\"ĮŽÉŒÎŋ\x95\x10" } internal_nodes: { key: "Bxw=" value: "/6F\xee\xec\x13tp\xcd\x01\xea\xa8\x03:\xd9q\xee\x11\x8b?\xfd\xe5\xa7\xd6\xeb`؊P(((" } internal_nodes: { key: "By4=" value: "\xe9{\xf0R\xfa\xech\xae\x07\x861X\xcb\xcd\xf5\r\x93\xc1dbAw\x98\n\x15\xd6id\xe4\xfd\xbd\x11" } internal_nodes: { key: "ByA=" value: "\xbaqN\x0f\xfe\x08s\x1e/\x9dr˨2\xbb\xbd\x0e\x99:\x18\xcc2,\xa2F\t\x98D4\xd1ڜ" } internal_nodes: { key: "ByI=" value: "\x7f\xe5\xa6f#\xfb\xd5\xc1x,\xd5\xf0\x98T.\xbdI\xe1n\x8eJ1w\x96&#E7/d\xf8\x9a" } internal_nodes: { key: "ByQ=" value: "}JвdJ!\x04\x0e\xbc\xadI\x7f\xc1\x0c!\x0c\xf9\xccs\xe6|[A@?t\xd30\xcb|\xd6" } internal_nodes: { key: "ByY=" value: "{\xd0VQg\x82T\x08\x02x\xd1-<\xd1\x15\xd5[\xaa\xf9\x17\xf9|\xe2#r\x18\x85\x97\xfd\x06\x12\x0c" } internal_nodes: { key: "Byg=" value: "\xef\xb6oC\xe2\x10Q[\xdd\xd9R\xff\xd8Ed@_\xcbD\x8f^\xa8\xfc\xa4\xaa\x0e\x90W\xea,#\xcc" } internal_nodes: { key: "Byo=" value: "Q\x17OŨŖ\n.\xa2]\x08\x83ڂ\x82\xce:w\x00<\xf02z-\xa1`6\x1d\xf4r\xd7\xe1\x8a" } internal_nodes: { key: "Byw=" value: "\xb0\x1d\xb5#\x835N\xa4\x8d\x8e\x90\xf4;u\xc6\xfd\xa8\x17\xda\xfb\xee\xff>\xab\xa6\xeeÕŠ\xd8\xc6\r." } internal_nodes: { key: "Bz4=" value: "\x1btu\xbd\xc9\xcd\xf59W~\xf6ʛ\x82Qj\xdeG\xe1\xe0\xc1\xd7\x19\x8c\x98CO\x1c\xe51\x12#" } internal_nodes: { key: "BzA=" value: "\xc7\x1fQ\x86}\x97a\x98\xda\x18\xeaq\x97\x05\xb5\xf38;f]\n\xb1\x9b#\x8b\xed\x94;G\x8bc\xad" } internal_nodes: { key: "BzI=" value: "\xac\xfa\x8e\x04\rOL\x11\x0b\xe0\"\xd5i\xa1\x95h\xac\x10H[5\xa8F\xd0\xdet\x01\x17Qk.\xb5" } internal_nodes: { key: "BzQ=" value: "\x19\x81D\x175L\x81\xb7\xee\xceWS\xdaAƄ\xa5\xd8K{\xde\x17\x88\xf7\xf4\x14zB\xa0Y\x80G" } internal_nodes: { key: "BzY=" value: "Z\xfd\x82?S\x12f\xee\xa4\xd1\x118\xdbY\xb5\x9ef8\x16\x16\xfe\x08&py\x87U\xffQ\x02\xa2\r" } internal_nodes: { key: "Bzg=" value: "\xd5@\xec\xe4\x1eæŧ¤\x82\x80\xf4`\xd70d,j\x8b\xb0\u0080\x08iXvÜŋᔖ\x0e6" } internal_nodes: { key: "Bzo=" value: "a\x8b\x1b\x95x\xa6\xb34\x8b\xcd\xc3\xdc\r:\x04\x95i\xe5_\xea\x91;\x1f\xfdC\x11\x18$\x8cÎĸ\xf9" } internal_nodes: { key: "Bzw=" value: "âĢģ\x88d\x1c]B4\x7f\x88\xf0\x8d)d\x83\xbb\xf5\xc0$f\xfa\x9fkc+\xdd*\x9e\x15H\x9a" } internal_node_count: 254 prefix: "\x00\x00\x00\x00\x00\x00\x02" depth: 8 leaves: { key: "CA0=" value: "\xf7T\x9c\xf1T\x89\xa4\x84\x1f!\xc2\xf7j\xb4\x91N\x9dÚģ8\x1c)GŲ§A\xdbe\xb2\xe0%\xd6" } leaves: { key: "CA4=" value: "\xd5;|\x1eig=\xf8kO\x82\x9c\x95djc\x9a\x8b^\xfd\x8e.'\xb6\r\xbc\x84\xdc\xd5\xed:\xaf" } leaves: { key: "CA8=" value: "\x0b\x03\x9d\x86F\xb6\xddy\xf8P\x05\x1e\xfe\xce\xe4\xc6G\x16\x16h\x98\x8b\xc8\xe9\xf9!\xba\xdd\x12\\\xa4\xf4" } leaves: { key: "CAA=" value: "\xf1,'cI\xae\xbf\xccR=v\x9e\xe47\xa1\x01F\xbf\x8e\x00\x14k5\x9dh*\xce؜\xd5\xca+" } leaves: { key: "CAE=" value: "\x93\xfe\x1a\xdb~\x8bZ\xd5\xef\xf1\xf6\xeb*\x1d\x96X/\x95\xcd\xe2\xdfZkΏ\xe6\xa7(\xb3\xa4\x0c\xf2" } leaves: { key: "CAI=" value: "\x80\xc9B\xfe\xfbŲ†g\x95\x80\xa50G\xf1\"\xfbW?hB\xf2\x08\xe6\x17\xd5\x03\xec;\x85\xdf`\x04" } leaves: { key: "CAM=" value: "\\_\xf7\xe4_\x9dQ\xa9\xe7'TE\x01\xe1د6\xc1\xb1Ŋ \x85\x97\x97h.\xb9Z=k\x80" } leaves: { key: "CAQ=" value: "\x83\xf8\xb9\x19\xbb\x07\xe7\x14\x15\xf1\x0f\x89MFÆš\u0084\x1d\x91\x0eO7\xa2@9\x0f\xb3\x08\x16\xd5{" } leaves: { key: "CAU=" value: "\xf7\x8f\xe3\x02\x1a\xe8r(\x85\xab\xcb\"\x86\x84\xca9\x0f\xe3*p\xd9\xe7T\xf8q\xf8\x88G^\x9c\xb7\xd4" } leaves: { key: "CAY=" value: "/\xfaa\x98\xec\xd4\x0e?\x94\xf8\xdd\xcb_ÃĢ<\xcf?\x80J\x88\xf2\xa0\xadl\xfd\xfc\x17\xa9\x90g\xb7" } leaves: { key: "CAc=" value: "\xabq\xb2j\xac\xc0)\x8a \xfaËē\xa6C\x00\xcc\xe0\x03l\xa08\xb7\x9dh\x80\xf2\xf7\xca\xc1\x82)'" } leaves: { key: "CAg=" value: "\x0b\xb9\x8bq\x90\xdb\xf2s\xe4d\x88\xfb\x8e\x9fx\x98\xb7\x92Q\xca\x16\xb4S~Ne\xd3\x14\x12:6\xc3" } leaves: { key: "CAk=" value: "\xbe\x05(\xfe\\\x08\xc3+\xc6\xde\xc7ÔŦ\xff\xb9JO\xed\xc2P\xf6\x19i\x15\xbbe\xcfßŊ\xa1g\n" } leaves: { key: "CAo=" value: "4Y\x06\xc2U\x15\xa4i9d\xbe\x88\x1bGR\x9b\x90\xb8\xb7\x98\xaeM\xab\x17L\xf2\x98c\xcbA\xc4]" } leaves: { key: "CAs=" value: "\xa92\xa0\xa0\xfe\xd83\xa8\xf5\xdf.u6U\xda\"Se\x1fÛ~\x1e\xed\xfbT\xbay\x8f\xa1b\xa4" } leaves: { key: "CAw=" value: "\xbdk\xcb\xd6\x1dF4A\xf8\x99\xee\x12\xff\xec\xf9\r\xb4\xff?6&\xc2\xff+\xaa&\xc2ejSde" } leaves: { key: "CB0=" value: "F!\x97\xff\xef\xcb\x03K\x02\xebÜŊ\x90\x93\xb8dw\xee~\x8d`,ZL0\xe8Øļ:\x03 \x02" } leaves: { key: "CB4=" value: "\xa7>R.u}D\xd1h\xf0y\x88\xf9\x1d\xfb\xc3\xe8\xf5Ú´\xa2\xb9ŪĢ\xfdB\xa7\x04\x0bĖē=" } leaves: { key: "CB8=" value: "\x8b\xbc\x81j\x05I\x93I\xa2\xa0\x1f\xe7\xfdN72\xea\xe9\x00?\x08+\xb1\x89\x89\xc1O߃\x14\xdb!" } leaves: { key: "CBA=" value: "\xfd\x16\xdb1\xae*\x03\x82\x88\xf1\xfc\xb8\xa4\xe7\x8dG\xdeS\x9bU\xb2SN\xf0:\x02r=\xb8\xa5\x0bS" } leaves: { key: "CBE=" value: "\xe7f\x05BT$j\x90\x03t\xd7\x16\x0b\xe5\nU\x11\xb7\xf7J\xc6+\xd0?&\xa3\xd0\xf1\xa082c" } leaves: { key: "CBI=" value: "t\x8d\r\xe5\xc6 \xbdy*\x8e2QZ\x81\x8a\x12\xc5\xd9\xcf Nib\xacx\xde *D\xd3" } leaves: { key: "CBo=" value: "t\xe3\xe8\xba)\x87)\xa2\x89}\x0fV\xff\xf8\x14\xc8'T\x98\x04\xce\"{ˎ6\xbc\xea\x0cX\xb7\xe2" } leaves: { key: "CBs=" value: "\xda\x14\"\xba&\xa4\xe7\xe4\x18I8Z(\xbdKÛĒwK\xfaH\xf3\xd6O2\r\x0c\xd4\xf3\r\xb7\xf2" } leaves: { key: "CBw=" value: "\x04\x18\xec/5G\x1c\x1eb\xf3N\x90\x89\xf2&?C\xdcpJ[G\xd6%mE-nX\xe19\xee" } leaves: { key: "CC0=" value: "n\x8cMW \x9ej\x89\xec\x02\xa2%p\x85;\xe0=\x1a\x90\x03\x91\x0fn" } leaves: { key: "CCA=" value: "\xcd\xce\xf6u\x90gG\xf6q\xafh\xfc\xd1\xc9\xf2b5\x8e\x95c\n\xd6F\xaf#\x11ms\x08\x06\x90+" } leaves: { key: "CCE=" value: "\xd6d\xe9I\xe4\r\xead\xe32\xef\u009a\x80vS\xc9 \x17?\xbb#\xfd\xfdM\xb2\xb1EL\xd6e9" } leaves: { key: "CCI=" value: "|\xeazuD\xab\x10%\x8a\x13\xb7\xa5Z+f\xfa" } leaves: { key: "CCQ=" value: "\xf3\xf5\x0cP)Pk\x04\xc4^\x14C@@gS3\x98(\\ \x1a\x12\x8a\x8bH4\x9f\xe6y\x01\x04" } leaves: { key: "CCU=" value: "\x90q{\xeb\xccy\x9f\xebC\x10\xa2\xb1AI\xc5&'\x0ey\xceڎ\xe3%\x13\n\xbd\x82B8\x8d9" } leaves: { key: "CCY=" value: "brl\xabT\x88\x99\xc6\xee\x07\x1b\x07\x93ͤc8\xf2gzOyY\\\xfaGׯ\xc3,\xb5\xe2" } leaves: { key: "CCc=" value: "\xad\x99)\t.\xa0\"\x93z\xcd\xc6\x06A\x05qs\x1f_F\xea%\xa1\xfe\xb4\xc5{o\x0f\x8aB\x11\xc4" } leaves: { key: "CCg=" value: "t\xd6il\x15{\xfe86\xb4\x9b\xc0Ć\xfa\x1dL\xa5\xed[Yu)\xd7ĶŽ=\x8c\xc9uT\xea" } leaves: { key: "CCk=" value: "\xf5\xdd\x04Ō‰\xb1\x97\xe5\x0b\xa8\x82\x05\xca\xf9\xf9\xf7?F\x87\xed\xa0\x1eiŅ—'V\x89\xd8t\xc9Y" } leaves: { key: "CCo=" value: "\x16\xff\x9c\xd8=^\xe8\x06\xe8郅h\xd5j\x12_\x91\xfe\x14_Z\xfc\x8e\xa1\x17\xaau\xb5\xc0\x83\x0f" } leaves: { key: "CCs=" value: "\x0f\x7f\xc8\\Rr\xbd,\x1a\x89$zl\xd6\x12\xcd\x7f\x83\x9e\x13J:Į¯\x1fÅ­\xa5\xe7\xb3A\xd5" } leaves: { key: "CCw=" value: "\xb8\xbc\xba\xcf\xd6\x11(,\x0b}\xa9\xe1Gd\x00R\xf8^\xcd<\x96HR\xd2\xcaS<\x8d\x8fj\x8a\xf1" } leaves: { key: "CD0=" value: "\xde$\xf6\xbd/]\xecI\xac*\xc4.X\x81o\x04\x1fo\xe2K\x8d\xc0aB&a\xd4\"\xd5\xfd\x11\xcb" } leaves: { key: "CD4=" value: "7\x86H67\xbcW\x9f-B\xd6([\xee\xc9\x14\xa8\xff\xa7\x0e\xeb\x8f:\xbfs\xfc\xe9\x9e+\x15\xfa9" } leaves: { key: "CD8=" value: "\xcaj\x10\xd4g\xc0]\x1aOz\xea\xc8\x02b\xf3\xa36\xfb\x12[G\t\x89\x84\xed:\x9cf\xf4\xa1\xa3k" } leaves: { key: "CDA=" value: "\x0b\xff\xf4\xfe8\xfa\xc7>\x1d\xb0*\xa7ה Y*\x0f\xc2/N\xc1\x81\x94h/\xea\"\x86\xd3D\xd5" } leaves: { key: "CDE=" value: "\xe3\xea>'{\xa1~ŨŠ\xf1\x83K\xce\xcb\x07\xef\xc1x\xd0" } leaves: { key: "CDQ=" value: "\x12\xeeN\xe7\t\xec\r\xa6\xb6\x81Aet\xfe\xca\xe1\xb0\x19(\x7fJ'JC\xec{\x1b\x98#㖐" } leaves: { key: "CDU=" value: "\xc1\xa3\xd3\x04gZ\x1c\xdf\xf4p\xd7l\xf9<\xbbQ\x81ILL'J\xadr\xd8RqzX\x1e\x19\x9c" } leaves: { key: "CDY=" value: "\xa4\xcd\xfdiY)\x12$\xbf\xee\xa2\"\xa3\x12\xe5,k\x94O\xc7\xfbF\x83\xd4\xec\xc4&\xb9p\x8c\x87\xef" } leaves: { key: "CDc=" value: "Zy\xbf\x98\xbdc,\xfd2\xac\xceŌ“\xbbV?\xc8!\xff66$\xa9@\xf1āĨĸJ\xa4\x95\xd3" } leaves: { key: "CDg=" value: "\x8fJ\xaf\x1a\x1c\xadR\x0b\x84\x1cn\xe8\xd7\xe9\xe72'G!\xf5\x7f\xf6`\xc2@0\xb3;\x05\xd1\xe2E" } leaves: { key: "CDk=" value: "\x0c\x91\xd1\xcd\xe1\xcf\xe7%ÃŦL\x98c\x89\xd0\x18rY\x0eA^\xaca+\x04\x85\xba$6\x02\xc4e" } leaves: { key: "CDo=" value: "\x90Tp~uW\xfc|\x85\xa3p\x19\xbd_\xf0/\x01\x0f\xb7\x0f\xcd9T\x81\xbbNZ\xa1/g\x1d\xec" } leaves: { key: "CDs=" value: "17\xdft]\x8c\xd7\x07\x9a\x8b\xe6\xa2X\x94?ÄļMa\xfd\xbfØĸo\x81\xfa%`ÆĒߛ" } leaves: { key: "CDw=" value: "\x1a\xa8uÚ Dl\xad\xeeJ\xba!\xa0\xc1g\xd6\x11~\x87\xc1\xf2'L\xa5\xba\xca\xc1r7\xafd\xbf" } leaves: { key: "CE0=" value: "}\x87\xf5\xcbŋ\x1at3\xd2\x1fg\"/p\xb5M\xb9-\xf9\xb8\xeaf\x86\x03\xb5Úž\x1fO\xcb\xd9" } leaves: { key: "CE4=" value: "ÖŦe-\x88C\x1aK\x8cŨ”\x89\xcd\xcf\xda\xd8`\xbd\x11\x8e\xfdq\\l \xc3\x01wn\x9e8\x08" } leaves: { key: "CE8=" value: "\xb3\xfa\x88 \xc0\x99}\x8er\x87\x80xÅ´\xa3\x02\x97\xeb\x1aĶŊ\xee\xd6\na\xe5ZL/M\x7f\x18" } leaves: { key: "CEA=" value: "\xc2Ū…K\xf6Ҍ\xf2\xd9-\xfc@S\x1d\xa0u\x85\xda^*\xd1#\xf1\xee%\xc8o\xe6Đ¯\xcd\xd8" } leaves: { key: "CEE=" value: "\\T\x90\xacV[[Y\x9f\xe4R#(\xd0y\xed\xe4\xb4\xf9\x1a\x19T\x07\x00\xb6\xfc\x96\x85\xf1\xc7i\xcd" } leaves: { key: "CEI=" value: "\xbb\nC\x95w\xf6d\x11\x95.\x85\x14\xffN\xf7Ķ BR\xcd**\xdc\x14\xe9\x8e\x1c\x87\xf9\x84\xbd\xfd" } leaves: { key: "CEM=" value: "pG\x99\xceN4m\xb5\x968\xb6\xe4\xaf\xfa\x93\xef\xb1]\x1b\xb2\xb9D\x97\x81\xcc\x1aB\x13\xdb\x1d\xc0\xf4" } leaves: { key: "CEQ=" value: "\xe4Mh\xbd6Z\xf2\x1cu\xa3W\xaa\x13\xf5{G\xfcM\xb3ÕĻ\xa2\xf7e\xd1H\xeda܎&\xb3" } leaves: { key: "CEU=" value: "ˌ\xc4}\x83\xc0\\B\x85\x13\x06\x8c\xb6\x99\x81\xadl\xf4\xc5\xd3:\x01ŌŽv(h\x8eW]\x80-" } leaves: { key: "CEY=" value: "jZ)\x1f\xe3D\x9eGm\xa7&i]=\xdbv\xeb~\x12\x86\x1cU\x10\xf3#c&\x8aQ\xe9\xbd\xcb" } leaves: { key: "CEc=" value: "\xf6L\xcc<\xafn\xb6\x1fH\x8eiT\x19~\xc9G\xae)$]\xe8z\xd4\x0b\x1cf\xdf\xd4e\xfcFG" } leaves: { key: "CEg=" value: "t)B\xb1\xb6?'v\xad%\xb5_\x8a\xea\xda8\xe8\xe26\xfc\xf3\x0fM@Q\x12\xe0Lkm" } leaves: { key: "CEo=" value: ")\x0f2\x10\xf6\x98\x10\xafso#\x01\xceDL\x8f\xfad535\x10KYw\xe3\x1d\x98r\xf7\x15\x02" } leaves: { key: "CEs=" value: "\x86jŅē\xea,g\xb0\xa6\xfbh\x02z\xcb\xe2\xd5\xc4\x7f\xab\xe0\xf6D\x8f\xa8\xb4펚¸w\x808" } leaves: { key: "CEw=" value: "\x14\xe4N6\xf2\x11\xead\xcaXĪĄ\x11\xfd\xcaS\xf3\x8d\x1c$?\x91\xb4T\xc7\xea}6T\xabH\x10" } leaves: { key: "CF0=" value: "\x1d\xca(\xfa+\xa5\xe6E\x1f\x87yL\"W\x96\x84\x9b\x06\xd7\xe1fk2\xbf3Ķ–K;q\x8e\xb9" } leaves: { key: "CF4=" value: "\xe2\x1f\xf6\xb5\xb5\xd5\xfa\xeaʑ9\x00\xb3\xa5Ķ‹Y\x8eCCL\x1c\xc5~=`i\xe9\x9et\xab\xbd" } leaves: { key: "CF8=" value: "\x00\xe1\t\xcf\\#\xd6'U\x1e\xaf\xa6\xed\x82y\xb6.JIGn\x83Cxy\xf6\x85\xa0\xe01\x91P" } leaves: { key: "CFA=" value: "\xe2\xea!\xe1;c\xa7\x971\xe6M\x90lbn\x7f\xba\xd288\xfe\xfd\x8fH}\xe5P\x89\xc8\xfe\x1c\xc9" } leaves: { key: "CFE=" value: "\xcf\x1fu4O\xd4\xee=:3\xbdڈ\xfc\x92\x10\xbc\xea̓\x9e\x8a'U\x0ck*\xe1d\xba\x07\xf0" } leaves: { key: "CFI=" value: "ZЊ\xd4qBE\xe4\xc1K\x1c@\xd7$\x02jg\x9d\x00\xbb|1%\x91\x9c\xeb\xc1s\xa8@a\x80" } leaves: { key: "CFM=" value: "\xb3\xb0\"\xcc\xe9\xe9\x08\xc3\x05\x1dX\xb0\x03O\xb7\xad\x06Ш\xfe\"ZiA\xf1Hzt\x7f?\n\xd0" } leaves: { key: "CFQ=" value: "\x80\x1f&\xe2>6\x91\xb9\xb7G\x1e*\xdbv\x12O\x93\xf2AbŞ\x895\xb1=,\x10@A$\xf2" } leaves: { key: "CFU=" value: "p\xa1\xe8\\gŅ‹\x1ac\x13\xb2\x88\x06\xb9-\xf16M\xea8\x9ekdn.v\xa8&\x98\x0f\x9fl" } leaves: { key: "CFY=" value: "\x81ĘŊ]\xed\xe9D\x80H\xfd%\xa8\xe6\\\x06\xb7\xfbn&\x1f\xab\xefĶŽ\xb7X\xf7\x8c\xc0\x87[\xa1" } leaves: { key: "CFc=" value: "\xc5r\xef\x02\xbb\xfc\x0c\n\xb7H\x82\xf7\xfc\x82r1Q1o\"B\xea\x047\x9d\x1bJw\x84\xfc\xf1\xa1" } leaves: { key: "CFg=" value: "\xe9\xce~\x11\xc1\x12őa\x9d`\x81\xb9\xe6\xe78\xfebwx\x85\x885Ux\x14\xb6\x92\xadL\x89\xec" } leaves: { key: "CFk=" value: "\xa3\x95\xa6\xce\x12\xe6sd\x0c\x07\xb7\xbb (\xb4\x16\x19\xed\xba\x8c\xa4O\xec\xbe" } leaves: { key: "CGM=" value: "tv'\x18<\x98\xaaX\xdd\xe5\x86J\x97c\xde!\xd9✷\xa6\x92\xe2d\xe1j\xfd\x99\x12!\xa2h" } leaves: { key: "CGQ=" value: "g\x9c>\x83)n)/$\xe1\xd6\xfb\x89\x8a\x02\xa6\nZ\xf5\xc8)ᛋ\xfb\t\xaa\xb6\xb5\xc4/\x8d" } leaves: { key: "CGU=" value: "\x8aSͨ\x81\x06/\xf8\xcc\x00\xb1R}\x15^\xe50\xe0\xcfG\x9e\xce\x1bx\xeb\x85\xce\nQ6\xad\xc0" } leaves: { key: "CGY=" value: "\x1dI9\xb06d\xb6ofc)\xac]\xb63p\xbc\x9a\xdd\xeaU\x97&\x0btS\xc87\xc0\x93\xd4W" } leaves: { key: "CGc=" value: "p9t\xcd\xf3Q\xea\xe6uM6(Se\xaf\x17[\xb2\xf9Ҍ\x05\x9e\x88XD\xfd\xbe\xcdg\xe6\xb0" } leaves: { key: "CGg=" value: "\xcf\xd4Z\xa7\xac\x92\xb9\x1eaK\xc5Q8\xa3:\x81\xa7\xf1l\xcb?\xa2\x10\x12\x1b\xbed\x93V\xd6@^" } leaves: { key: "CGk=" value: "V$\xf5\xbc\xcd\xf2\xd2\xfe8\xc7 \xf9w\x014\x80i?y\xe5d\xdc,D\xff;~L\xe4y\xc4\x01" } leaves: { key: "CGo=" value: "R\x94\x04\x19zPsK#čŦ‰\x99\xae\xc1!\xc3<@\x16g\xf1\xcb8\xb1M}\x1a\xb1s8\xed" } leaves: { key: "CGs=" value: "g\xe7\x17\x0c㴔A@\xd3\xfb\xbf\x10\xde\"\xc7\xf6\xe3r\x15oe\xf7\x82~\x83b\xb8=m~{" } leaves: { key: "CGw=" value: "\xe6\t\xbc\xb5\xed\xf4\x15%\x92\x9ctB\x8c\x03ytvsn0\x07\x94\x97s;y\x0c\xdf/\xc8\x1a\xc4" } leaves: { key: "CH0=" value: "g\xa0\x81δ\xc7JC3\x8b\xbd=h5\x91\x8b\x1d\x81\x82\xee\x0e\xcc[u\xbb9\xdam%\x88\x16\x0b&\x7fF\x1c>\xb8\xcd\xf3\xf9\xa4\xdd\x1e2\xe2\xc5V\x8aQ" } leaves: { key: "CHA=" value: "\xd7~+M+\x06Ø !U\t\x08\x02\xa7X\xb5\xac\xf1p\x978PnY[4.XB\xbfL\x96" } leaves: { key: "CHE=" value: "T\xb1\xa0\xae\xaeX\xb5^Q\xa2\x92\xa6\"\"\x85\x93\x9a\xaf\xba\xee+{\xb8\xd0F\xb0ŌĢ\xbc×Ŋ\x9c" } leaves: { key: "CHI=" value: "\xfbb\xc32\xdbG`1Q\x97Z\xe2\xa3\xe7\x1e\xa3\t߲\x81\xb0u\x9bq]{\xceI\xa8\x1f\xf6\xe4" } leaves: { key: "CHM=" value: "5Y\x00P\x02\x97\x0b\xf3\xb6\xc6\xc0\x80\xbc\xb7,\xfe\xf7\x04\xf4\xb4\x14S\xf6k\x9c\xe4H\x19؊\xef\xb1" } leaves: { key: "CHQ=" value: "5\x89\xa1\x7f\x17\xfd\xb7E\xe4\t\t\x9bg\xfb\xe7N[\xa2\x06,*\x89\xbb\xaf\x1c\xcaãŦ¨\xbf\x93s" } leaves: { key: "CHU=" value: "2`\xa4\xea\xe2rШ\xf2\xb4wN\x96\xe7\xce\x17\xfeP\x1b\x8d\xd8\x02'\xfa\x86\xff\x1e\x8a8\x84\x95\x90" } leaves: { key: "CHY=" value: "\xcc!\xd6\xe6ŗ\xe3~z\xd3\xce\x05w;y\xfd\xa7T}\x90\xe0e>e3\xe0.\xbe\xbb\xac\xf0\xc1" } leaves: { key: "CHc=" value: "\x14\xecl\xc1❉\xc0\x83V\x82\x06\x12<įŗŠ\xa2\x97#\xd9]y\x03\xcd\x18\x88\xd1\xf8\xb4\x01]" } leaves: { key: "CHg=" value: "\xc8W7_äǁ\xfb?\xc3@\x8f\xbc\xe0\x05\xeb\xade\xb5\xcfB\xc8N\xbfH\x05\xc1\xa5K)\x0e\xb5" } leaves: { key: "CHk=" value: "\xd1w\x00\x8e\xeb\xa1\xfeR\xe2\x17g\xff\x04\\\"b$\x07r\x97=(Fc\"5J\x9e(a\xc4z" } leaves: { key: "CHo=" value: "\xc4%yÕ˛\xc2\x1bAX\xd1\x07\xcfmj\x1a'P\xcb\x0f\x7f;\xdf\x1a\xfa\x0eb\xa8\xd1|\x08L|" } leaves: { key: "CHs=" value: "\\\x1d\x81\xd3\t\xd7\xff\xec\xc6!\x03\x93Y\xf0`\x95}\xe9\xfb\xe0\xe1Y;\"s\xbf\xd7\xe0\xee\xe1v\xd2" } leaves: { key: "CHw=" value: "\x9bQ.\xb0\xffꄸ$\u009d\xf5zh`\xc2ie5\r\x0e#@Rv\x10\xb5\xfc\xdfY\x07\x85" } leaves: { key: "CI0=" value: "\xa3C\x90\x19\xbc\x08?\x89\xb4\xb9\xf4\x9f??\xf3\xd4Đĩ\xdd-S\xcb\xc1(\x89\x99\xafŪ„\xe0\xe9Z" } leaves: { key: "CI4=" value: "\x10\xe6p\x1dR\x08\x19\x19*\xf5x\xbd\xb7\xab\xe0\xbfW\x92$\xe6\x8b\xfdn\x1b\x9dUTA\x86{\xbb\x92" } leaves: { key: "CI8=" value: "h\xceA#\x80=\x9b\xc9\x0e\xad.\xcfrq\xc8\xdeZ\xf7^K \x04V\xc6VtqÖ \x1bz\xb0" } leaves: { key: "CIA=" value: "\xeb2v\xff\xab)Z\x08\x93X\xe0\x91\x9b&om\xc7E\xb6\x05\x87\xb6\x13M\xe1\\\xef\xe5?\n\x00\xe4" } leaves: { key: "CIE=" value: " 035\x84\xf6\xb6\x8e\x0ekqj\xd3\x1c\xe2\xc7\x12\x87\xf3\x8e\x18ÎĄ@\xfb\x9e\xb4\x9bI 7\xab" } leaves: { key: "CII=" value: "\xe3Ãĩ\xc8[\xc9W\x80&Ѐ\x90\xd7v\xf9<\x91É \xe2\xfe\xa6H\xa1\xc8\xd1\xcc\xec\n\x8d\xd7{" } leaves: { key: "CIM=" value: "6,\x1dG\x9a=\x166)\x03t\x917\x01\xae\x90\xee\x19'\xfd\x88\xfd\x99:\xbc\xe6\xcfN\x07v\x99`" } leaves: { key: "CIQ=" value: "O\xee\xde\xf5\x93ß­\x9cwo\xd7:" } leaves: { key: "CJ0=" value: "#\xf4\x0bn=,J\x9f\x80\xc2)\x9b\xe1\x84Ώ\x86I\x8c-T,\xcf+\x12\xc1\x8c\x9f\xad\xb8EZ" } leaves: { key: "CJ4=" value: "\xf1\xcd\x15l=7\xd8\xf7\xc9sj?#j\x12#rÖĻ\xfe\xab\xf3\xed\x89\xe3\x01p\xfa\x16Ȟ>" } leaves: { key: "CJ8=" value: "\x9d\xa2\x85va\xf5\xfe\xcb\xfb1\x92܋Ev\r/\xd6\x026\xe1\x17D>Ęĩ\xdac\x94\xd1\xd04" } leaves: { key: "CJA=" value: "\xf9\x11\x12\x9d`\x12\"\x87\xf3\xeb\xf1\x8b\xcc`\xfcx\xf0æž 5\xde\x13\x9c\xeei\x1e7\x9a\x00@\xcd" } leaves: { key: "CJE=" value: "Jx\xc1D:\x84DI1\x8b_\xb0{\xe4c\x0cDʀ\x95\x8c\xa1ns\x025\xfe*\x0b\xcb\xfe\xcb" } leaves: { key: "CJI=" value: "\xce\x08\xfe\xfe=\xc3\t!7\xdc0{\xaa\xaa\xaeH\x9d\xad\xfc\xe9\xea\x0bpM\xa2\r\xf6\xa2\xfa\x84\xf4\xb4" } leaves: { key: "CJM=" value: "{Iw`Y\xe8\x8a\x12\xe3\x84g\xae//h\x8b\x82\x98BQ8\xe0\xe4\x90\x19\x16[\xb7t\xfc{\xa5" } leaves: { key: "CJQ=" value: "ʍ\x02\x9f\x80\x98H\x8e\x8a\x8f\xcds\xbc\x91\xf3\x07\xc3\x0f0]\x15\x06đŋĄĨ\xa5\xf9\x10\xf4m]" } leaves: { key: "CJU=" value: "\x1c\xb9\x15Öē\xc5\xe5\xdb\x02\x11<>\x0e\x0b\x14\x99\"\x8cqc\x02\xc7\"\xb2\xb5\x98K\xf2\xe4I4\xa2" } leaves: { key: "CJY=" value: "\xb2\x94\x0bGU9\x92\x04<\xe6dYw^V\x93K\x07\xeb\r\xca\x0em\x1e$X94\x8a-X\x18" } leaves: { key: "CJc=" value: "6\xfb\xcb\x07\xa7o~\xcf\n\xc8[\xbfb\x9b\x9fQ6R\x9b\xaf\xcax90\x8d\xf6\xc50)r)=" } leaves: { key: "CJg=" value: "\x8a)\xb6T9\xa0;:rf:l\x1a\xb8LH\xb6XCQt-\xa61\x0bl\xb6\x0f\x83O\x9eD" } leaves: { key: "CJk=" value: "\xbd\r9\x00\xd4\xd7V)~.\x03\xd91\x84O\xfb\xb8\x9dK?\x87p\x87\x07W\xe3'.\xe0Lg\xe5" } leaves: { key: "CJo=" value: "\xcae,\x9a\xfd\x10<\xff\x9c\xe9Z\x00\x133\xc3ÚĻ\x99E\x08-\xbf\x9cl\xad\xf7\xba\xbb\x0b(L\xad" } leaves: { key: "CJs=" value: "k\x9f\xf2\x16\x01D\xaey\x96\xa4\x12\xd35\xa7h4\x8a#mv7\x94\xc1r\xb2^\xff\xbe\x92:ؘ" } leaves: { key: "CJw=" value: "\x0eLC\xf3'\xe7\xab^\xbd|\xa5\x11\xa3\t9U\x80\x80dq5c\xcfe\xb1;؈\xb4\xd30l" } leaves: { key: "CK0=" value: "5Z\xa8[\x8e͙l~^$0\xa6\xa0E\xd9>\x8eؚ\xac3D\x03|\xbd) \x91\x19\xb2\xbb" } leaves: { key: "CK4=" value: "Y\x9d\xa5\xaaԟ\xfci\x11J\xa6\xd7\xe6DQ\xec\xdf\x0c\xac\xdf\xfa\xa8\x9f\xf8\x93\xd7\xfeA\xf2a\xaf\xe4" } leaves: { key: "CK8=" value: "\xaafÉĄo\xd0\xe1\xd4\xc5~\xa4\x81\xc6s\xca\x0cu]a\x1eU\xc5UÛ¤\xf4\x98\xffhz\xfd$" } leaves: { key: "CKA=" value: "\x82X\xa3\xb2>\x18mMY\xd1^\x81\x00\x8cC\xbf\xcaZ\x06\x17\xc3\xde\x7f\xb0a\xbb\x8aP\x07\xdb7\xcf" } leaves: { key: "CKE=" value: "\xe2Cp\xdf)\x04Z\xfb\xe42\xaa\xe66\xb3\xd6p\x82\x91\xe9k'\xe7\x11\xb2\x88\xf3\x1d\xf0\x92?\xba\x0f" } leaves: { key: "CKI=" value: "$\x1e,\x0bg\"\x13\xa5Đ­\x0f\xde֝\xb0\x07\x08V(q\xa2\xf2V\x91\xf6K\xd8BkŌš?" } leaves: { key: "CKM=" value: ":w\x9d\xa3`@\xa0Eb\x15\xf5\xa0$\xd3\xf5\xe1R`\xdfU\x95\xf2\xa2\x06%L\x12\xecw\xce\x196" } leaves: { key: "CKQ=" value: "\xd07\x13\xc3X\xbc\xd8p\x13\xd7\xd97\xbdQd5\x88\xb7\xc1\x08\xef)kZ\xb7\xa8\xca\x18\x9ai=\"" } leaves: { key: "CKU=" value: "\xf2ȁh\x89Y1ne\xe3M\xce\xc4U\xcfŲ­\xa1g\xd8H\x16\xd9\x18\xb1e\x8f\xbc\x81>EL" } leaves: { key: "CKY=" value: "\xac\xab\xb6\xbc\xa8X~v\x0e˧\x81>\xe1\x0e<\x9d\xdd\xe0߲\xf1vs\xc8\xeb\xbd|\xed\x10\x9a/" } leaves: { key: "CKc=" value: "\xda.\xa0\x1d\x07&\x01\x90ŪŊWR\x1a\xfbE\xf7\xd9s\n\x9f\x7f\xb4Ãĸ\x10\x97H\x86\xf2i\xf7y" } leaves: { key: "CKg=" value: "p\x1c\xf8\ri\nņŠēĒMl\xf6\x98*MhVGy\x1bn0\xa7\x85'\x81\xce!\x99d\x8d" } leaves: { key: "CKk=" value: "\xed=H\x99\x98I\x96\x95@\xe4^~\xd7B\xa0k\xc1^\x88:\x8d\xc8\nh\xcd{\xd7^\xced\xcc\xcc" } leaves: { key: "CKo=" value: "\xff\xdcĶž\xc5\xe5\xb7\xc8!\x84KH!\x83\x15'\x9fk\xb8\x84\x14\x9c\xe0\xdf\x02;\xeb\xd23\xa0y\x01" } leaves: { key: "CKs=" value: "\xdc\x07ER\"\x18\x84\xf0\x0e`b\x01VJ&ÎŊ\x88\xe20\x1btW%\x0c5\x9a@\x8c6\x0fL" } leaves: { key: "CKw=" value: "\xe5ØJ\x1a\xf2\xbe\x0fr\xeb\x1c\xa4F\xd3\xed\x8d\xf2\xbd\xf5\x9b\xe6\xe5\xcc@QU>\x95i\xd5R\xee\x16B\x8d3=\x1f" } leaves: { key: "CMk=" value: "֊čēŠ\x15\xd0\x01\xac\xa9\x99'\x82\xcf\x14\x17r;\x1d\xb3%\xd9`\x8d\xd1m\xffiŪŦ\xf8\x99" } leaves: { key: "CMo=" value: "\x0fmC?>u\x06\x94\xa3$\xac\xf2\xe3\x1fu\x05$\x01\xf2S5\xdf\xc0\xb9\x1d\xd8|]\x87W\xe2\xed" } leaves: { key: "CMs=" value: "\xef\xbfҏQ\x85ܸfpŲēA8\x06\xa6\x13\xa5Ųŋu\xa7,\x80\x97/\xcf\x0f\x07\xa0\xa5m" } leaves: { key: "CMw=" value: "\xc5={\xb5\xc9\\}\xf9\xa3A~c$\xa4h\x87\xe9}\x1e֚\xe0\xa5d\xef\xc1K\xb2\x9a\x85\x8cx" } leaves: { key: "CN0=" value: "u\x02\xbaÉģL \x9a9\xc2\x18\xd6\x1c0\xee\xafH\xc4%b\xbe\x08Į§ÛĻv\x07U\xc4i\x04" } leaves: { key: "CN4=" value: "\x10*K5\x07\xfa\xe6\x18\x1fF\xa96\xe3}\xfa\x9a\xe7\x015\x0c\xa7\xb1;\xb9^G\x90Ķŧ\x98\xfb\xe5" } leaves: { key: "CN8=" value: "m\xe8\xf7\x94\x7fM\xc9\xeeJ\xfb7\xc091s<\x8e\xd03-\xea\xc6\xceYU\x19\x9e#\xbdL\xa5\xf9" } leaves: { key: "CNA=" value: "`\xa0\xca+\xc9C]\xd74\x9f\x93P$\\m!A\xf7\xf7U*\x97\xc7\xddu\xd9Rt\x9bĮ¨\xf3" } leaves: { key: "CNE=" value: "}\x90\xaf\x878\x8a\x14T\ty\xc2\xf5\xca\x05\x86\xd5wPl\xbc\xa5\xaevt\xcf@\xf8Z~\xab\x8e\xaf" } leaves: { key: "CNI=" value: "\xa2\xca]|y\xaf\xb9\xbb\x9e\x07\xab\xa6\n\x08\x1c>0O\xb8x+\x06M\xf2\xe0\xfb\xf7\xdb%\x7f\x92\xfc" } leaves: { key: "CNM=" value: "/\xff\xe4<\"\x97\xfe*&\x04\xe8\xd5\\%|\x129l(\x8c\xec\x17ČĄ\x9b\xcbFO\xb0]\x16\xa5" } leaves: { key: "CNQ=" value: "\xb5\x97\x91\x995\x88\xe8o\xf21\x8d\x87\x9b&Ũ\xba=\x80A\xf3\xc0\"\xd9\r\xb1\x8b\x9eG);$" } leaves: { key: "CNU=" value: "?%3\xb37\x1cm\x8d\r+.\x92\xf7\x15\xcaG\xea\xa0\xd9\x02\xe4\xb7\xe8\xb7\xf60\xae\x85\xa6\x96\xc0\xd4" } leaves: { key: "CNY=" value: "j\x83=\x93\x95\xcaW\xe0&\xd8$\xd4V\x9e\x99\xf1{\x80\xae\xf3\xb0\x8e~\xd7\xc6\xd0\xf6\x88\x94\x9c`\x98" } leaves: { key: "CNc=" value: "gV \x11\xb5ⲨC`o\xd0X(\x17do\x0c\x8cŌ§c\xe0\xc3&=\x8d\"=\xa5\xc0\xc8" } leaves: { key: "CNg=" value: "\x86q\x10\xaa\x11~\x15\x10r\xd8\x1dZ'\xbf\xb8\xd5:A\x87\x86[\x1f\xf1\xdb\x10\xcf\x07\xae\x04> \x8a" } leaves: { key: "CNk=" value: "\xc9\xfb\xbc{\x16\xb4Z3\x1a\x99HN\xf8e\x90âŊĢ\xf4\x19\xbe\x9cR\xe2\x7fI\xbe\x1f_\xdeH^" } leaves: { key: "CNo=" value: "\xc2\x14\xed\x03\xe9\xf5(,\x83\x8a\xe3\xf6$4Q\xf7\xdc[\xe3\xc8K\xb7\xaf\xf2G\xd1Ķē\x15\xf9\x0bk" } leaves: { key: "CNs=" value: "\x80Q\xca\x7f\x8f=c\xa0\xbdhHÛŗ\x88fS\xc8\x069\x8f\xa9-\xd1ŲĻ\x8c\xd5\xdf\x16\xecB\xcb" } leaves: { key: "CNw=" value: "\xe4&\xe4\xf0)\x1d\xa0\x185|\xa6ZL\xe8\xbc\x18\xbav\xfd\xcd\xe4\x1d\xbb\x9e\xda_ĪŠW\xe4c\xf9" } leaves: { key: "CO0=" value: "Fb1`\xdb\x15\xe145\xd0\xfbb8U3`\xf5v\x88\x08\n'aV\xba<~\xa6\x0c\xe0\xd5\xec" } leaves: { key: "CO4=" value: "\xaaS\xb5\xbd|\x11\x0f\x18\xd7\xf2\xa7^\xaf\xc1\x97B\xa4>\xcbX\xac\x14\xc0\xf9|Z\xf2f3\x1e\xd3\xec" } leaves: { key: "CO8=" value: "\x0ej\x04\x0c\xe7mUb\"\x1d|\x82\x8a\xfa^\x08{*\xf3\xf6op\x03X\xdc\xee\xf2ßĸH\xe1A" } leaves: { key: "COA=" value: "2\xb7\xbf\xc8Öŗ\x90:\xa0\x05\x94\xbdl\xe8\xa4{Ro\x1baСB\x88\xa3f\x82\x98\x8b\xb3\xf2\x0e" } leaves: { key: "COE=" value: "?K\x97rۘ\xe5>u>^OYŲž\x1bB\x9ag}[\x1d\xa28\x98\xb9\xf1Z\xe1\x81R\x15" } leaves: { key: "COI=" value: "#\x88{\x8b.F?\x06=\xeb\xbcJ\xc8Ԇ\x9eQ\x18\x1f\rh\xa7\xe2\xef|\xa1\xa4\x03\xde_M\r" } leaves: { key: "COM=" value: "s2yU\xa4\xddy\xcfY\xbc\x9c[\x84\xabh\x81\x90\xc1o_\x08-\x92\xa4jÕŊB\xd0\xee{\xe8" } leaves: { key: "COQ=" value: "\xef58\x08\xc53\xba\xa9\xa0?\xf1V\x80w\x14\xb6\x15:\xb3j3Īē~\x1f\x8c\x1a})(+\xff" } leaves: { key: "COU=" value: "\x06x;\xcc3\"\x98DcB\xb8H\x04\xf5<\x84xP\xe3g\x07}\x8e/&\x19ˑ\x90\xbd\x15\xd1" } leaves: { key: "COY=" value: "\x89)X\xb24M\xc0:\x08\xb4\xe5\x0c\xc4ŲŖ\xa7\x15\xd8\x14\x161X,\xb0*.\x8cV%\xee\xb1\x1e" } leaves: { key: "COc=" value: "\xf9\x89\x05.v\xfa\xd2\xcd5\x0e6n3!\x9e6\x0eBy\xf3\xfd\xd1\xf6\x96 ߛ\x0e\xf5\xf4X\x9b" } leaves: { key: "COg=" value: "\x83\xbcu[\xfa\x88\x02\x88\x81\xed\x17\xb4S\xcfr\x91\xb9_\xc18\xadm\xda2Ņ§â‡‚a\x1b\xa2" } leaves: { key: "COk=" value: "\xe1*\x10\xfd\xf8\x18LČĸI\x10\xf0\xe8\x08\xbbl\xe5\x18\xb6#\x11\x97{\xb6K \xd2D\xfe\x94\xe0\x1cȊ\x90\xaf\xcc\xea\xa2\xeaZH \x9d\xe0" } leaves: { key: "COs=" value: "\xb2\nr\x0b\xa2R\xba\x8bpc\xe1\x16\xe5\x13\xb93\x90\x08\xeb\x07\x89\xa6'\xf3\x92N\xd82{́\x91" } leaves: { key: "COw=" value: "\x11\x93&\x10Z\xebaʡE\xb4O1\xa7\x14|\xe9\xb3`v7\x82\xa5e\xccOF\x9f^\xec((" } leaves: { key: "CP0=" value: "{Jh\xf6~\xc1L\xe2\xc6^0\x04\xf7P\xd5\x18\xea\x7f9\x11\x84\\_t\xe2\xe4\x18Fi\x99{R" } leaves: { key: "CP4=" value: "{\x18ŪŠ\x95M?\xf8\xd9\x7f\xfb\x84\xcb\xd4\xf90\xbf%>w\xb2\x1e\xd1x<,\x80\x9dU \xeb\xa7" } leaves: { key: "CP8=" value: "\n@s+\x0c\x9ex\xf5-$mb\xb4\x1d 2D\xb7\x03!0\xfe\xee\xbd\x1f\xc7\xca\xce\x1dbGZ" } leaves: { key: "CPA=" value: "\xd8,\xefO_\x1bi\x1f\xb0\xa5\xaazŨ§\x01Y[\xae\xc0f\x1d]N2\xb6\xf4\xba\xa3@\x00\xb3\xdc" } leaves: { key: "CPE=" value: "\xbc\x0cA\xf9]\xdc\x7f\xaa.P\x8c\xf21g1\x91턜\xd7\xc5\xe7\x8cg\xe5|\xea\\:\xeewu" } leaves: { key: "CPI=" value: "\x02-w\xf5N\xe4\xc0h\xabBC\xdcih\xa0\xcf\n\x1f\xa5\xb1Fv4\xa6s|gS\r\x13;\x96" } leaves: { key: "CPM=" value: "\xddX\x99\xda4_>\x7f \xaa\xe1=i\xb9\xc5\xc3\xf3\xd1\xed\xa2\xec\xf8ĮĒ7\xe9\x83g-\xc4\xf4\xce" } leaves: { key: "CPQ=" value: "\xb3\xbe\x10p\xe81\x84\xc7m\xe17\xa0\xdb9\xc9+\xfd\x07} \x1a\xccf*\xa6\x1e\xb6CG[10" } leaves: { key: "CPU=" value: "\xe8EB\x918\x12\x83!a`\xd2\xef\x90\xdco\xee\xde8/\x96\xa9\x15\"#\xb8Y\xa4\xa4u\xdf\xc8\xc8" } leaves: { key: "CPY=" value: "\xe6\xc4\r\xba\xbcho>Qa*\x8f{g\x9c\x9e\xa9\xfeZ\xc3\xd3\ra\xb3I\x0b\xeb&\xf5\xb0\x1f<" } leaves: { key: "CPc=" value: "\x9d\x9f+\x17d\x9b\xbdd\x8c\xca՞D\xff\xefA\x0c\xef\xd7y\x13lZ\xdbfW\xfb\x14\xd2\xcd܋" } leaves: { key: "CPg=" value: "\xbf\xa1xb\x1a\xd0Ō™z\x9aK\xd9\xf8d\x12\x809į\xe5I\xac\x9e\x0eX\x7f\xa1x]\x13ی" } leaves: { key: "CPk=" value: "v\x82%\xfb{\xf3\xf5\x07\xad!Q\x9a>\xf6#\x93\xa6P\x80\x1ak\xa4\x80\x16\x99 \xef#t\xa9\x9b\x15" } leaves: { key: "CPo=" value: "C]\xe8\xea\x7fe\xc4\xf4mЊ\x85\x00%#H\x91\x9c\xac\xc8KK\x1c\x0c\x875\xb4\xb7\x10\x01S\x16" } leaves: { key: "CPs=" value: "v\x8157\xf9\x9bHR\x1c\xc4\xdb8_\xc2\xf7\x04\x06c\x8dl\x8b\x1f\x06\x1e\x93\xa4\x8f\xfb\x86\x87\xea\xa8" } leaves: { key: "CPw=" value: "AŨēň\x88\xda\x1cĶŗØž\x15\x04Đ­&\xa0\xfe\x03\xae?^f\xb1\xeb\xd7I\x13\xc7,a" } internal_nodes: { key: "A+A=" value: "˜h?\xe3\xd9-.co`\x99\x87\x1f\xd4kĶē)\x14\xe0E\x9d\xebwZ\x15\xd8m\x80i\xd1" } internal_nodes: { key: "A0A=" value: "\xdc\xfeu\xf2 \xac\xefĮŽQ\xd4:`\x9aS(\xf5\x9eBa\xbat\xc1\xe3\xb0|\xc0l\xd6c\x154" } internal_nodes: { key: "A2A=" value: "|\xc8\x08+Į\x9d@> \xdc`\x88\xd2\xe7ÆĩY姏\xbd\xdd]\xcc\x12\xbb\xb4\x89\r\xf5\xae" } internal_nodes: { key: "A4A=" value: "\xae\x7fc\x07V\xf7y\xb8\x7fq\xf2\xe9,\xeb\xc8e\xcf?\tV\xeb\xf3\xcfn\xc3&;c\x9e\x9f\xfa\xfd" } internal_nodes: { key: "A6A=" value: "\xd8^\xb7\xbf\x8f\x81g\xbdR\xbd\x1a\xd6\xc8\xd5\xfe\x19r\xa2\x14\xd0\xfc~^Y\xcd\xceO\")\xdf`\xde" } internal_nodes: { key: "A8A=" value: "\xa2ŪŽ~K\x11a\xec\xc6<{\xcc\x0e\"\x9a\xc2\x0c\xf8z\xe3Ė´\x9f\x023\x8e}\xfb\xc4}M\x19" } internal_nodes: { key: "AQA=" value: "\x81\x18w\xf0 ~\xbb\xc1\x1c\xda×°\xde\xc0\x00<\xeb\xaal6\x0ch\x02T\xf7\xc0c\x06\xa6\x994D" } internal_nodes: { key: "AYA=" value: "\x1e\xb4\x80\xd5X\x9fA0\xf9jd\x8f{\x19\x12h\x7f\xdcS\xa0 \xfbÆŧ\x9d\x9e;\x94ą\xd7\xf2" } internal_nodes: { key: "AgA=" value: "\x15.v9\xd2m\x80\xae\xe9P\xf1\xac\xe6\xe6\xe1h\xa1K\x83ÃĒk\xce\xfe\xba7W\xceu\xa2k\x87" } internal_nodes: { key: "AkA=" value: "\xa5~\x92\n2\x18\xcd\xc8\xde#\x96UV%\xba\xd89D\x1b\x98\xd9\r\x19\xe9C\xee\xd1B\xbfX\xfe}" } internal_nodes: { key: "AoA=" value: "I\xf8\x82\xe7{\xf0I\xf9 \xfb\x0e\x9a\xd3\xcb\x17\x0c\x8c\x98\x03\xb2\xff\x8cu\x9f\xb3\xd5R\xe9Ɩ:\x7f" } internal_nodes: { key: "AsA=" value: "l\x10\xd5Js\xb7c\xc20V\x8cQW܍E\x17\xd9M\x17 \xeb]\x8c\xbb\xdf\xfb\x7f\x92M\x039" } internal_nodes: { key: "AwA=" value: "\xa5\xe0Ėx\xe2R\x0eMuÆŖ\x98\xf5\x16P\xaf\x88F$\xe3;\x9fZ\xb8\xa5kK)g\xbd\x86" } internal_nodes: { key: "AyA=" value: "#;\xd0\xd8\x1a\xfa\xee\xfa4R\xe0\x02\xfax#x\xb3\xa4\xbd\x0cwA@=\x0b\x0e\xae_\x91*Ę­" } internal_nodes: { key: "B+4=" value: "#\xf2\xf8\x981w\xab\x84W\xa4c\xbdې\x00\xb1É­|\xe7y\x98sm\xd4#w(\xadF\xc05" } internal_nodes: { key: "B+A=" value: "\xd1a(\x9bZ\xda\x13@\xdb\x0f\x84\x91\xa3\xbeww\xa0w\x19@\xd9\xd3.\xe9\xdf+$k8\x0084" } internal_nodes: { key: "B+I=" value: "Nc\xec\xe9=\xb2\xaa\x9c6\xb5\x1a吕I\x90\x07t\\\x19#߀\xf8\xf1\xc4viË´ÎŊ" } internal_nodes: { key: "B+Q=" value: "\"\xb2Q)5t\x9f\xdd{\xfe\xf2ÚŠí€ģ\xfb\xefJ\x84\x1d\xfa\x08\xebg\x1f\xce/\xfc$\x9b<" } internal_nodes: { key: "B+Y=" value: "`ˋ\xbf\xbc⹸T'\xa0gei'a\xa9\xd3\xda\xe1q\xee\x016\x97\xc8\xe7`A\xd5=\x86" } internal_nodes: { key: "B+g=" value: "\xd1.?\x97\x8d\x90\xc0OX\xacbs\x18\x00\x82\x1c\xd5b=\xe3\x85P\x12\xaf\x03\xa9\x04Ԁ\x9fJI" } internal_nodes: { key: "B+o=" value: "0g1\xbcS\x93ÆŽ\x9a\xad\xacZv\x90\x8e\x86Ęą\xd2Sx_\xf82\xa1N\x1c\xd9F(\x15m" } internal_nodes: { key: "B+w=" value: "\xc9\xde\xfb\xdfb\x15\xb5a\xe8\xb6Į‘\t%\xa4\xf7C\xa4w\xbfG4\x1f\x97O\xccl=a*}\x19" } internal_nodes: { key: "B/4=" value: "'\xaa\xcb\x0b3\x07\xc9\xf1\x807\xe0\xd7\xeb{\r \xd5\x18֋\x97OL\xe4\xf09=H7\x08\xeb\xf5" } internal_nodes: { key: "B/A=" value: "\xf4\x02\x1d\x892\x9a$t5#\x12\x1dīŗš\xe8DC\x84\xb1\xb1\xc5\x0fn\x10i\x98=u\xba\xca\x1a" } internal_nodes: { key: "B/I=" value: "Á\xf5\x80\xe4\xca\x06\xed\x1eO\xb9\x9e\xa0\x19\xbdq\xbf\x91\xc9\r!\xd3P\x93\xf0`\x9c\xaeMW}:" } internal_nodes: { key: "B/Q=" value: "\xa2U\x05@\xbb\x81\xa1\x02go\xd8_\x8b\xea\xc0\x03D\xf8C\xac\xb9\xfaV@X9\x9fF\xa5t\x0cA" } internal_nodes: { key: "B/Y=" value: "\xb5V\x16Y\x83\xfa\xf1^\xc25M\xe9\xcd\x11\\\x11C5ÜŦ\x14TB\x80\xa1\xd1\xfe\x14\x06\xedn\xfa" } internal_nodes: { key: "B/g=" value: "\x06\x92x\x9d\x89^\xb6\xa0\xf2]RR\x9dÆĢK\xbf_\xab2\xe6\xa3\xf3\x86\xf6C\x16\xc5M\xd4(=" } internal_nodes: { key: "B/o=" value: "q\xb0\x08\x12\xa0!c\xa7\x8e \x02/\xe4\xb5\\CR3\x11\xbd\x96\xf4\xa12\x0f\xa15r\"u\xf1\xa0" } internal_nodes: { key: "B/w=" value: ",\xdar\x02\x17\x96\xd7r\xa5\x01\x10Įļa\x16\xc7\xc1\xe1+\xc8(t\x1b\x90\xfd\xed'\xec\xc7\xcexZ" } internal_nodes: { key: "B04=" value: "\tv?\x06\xf1\x8e\xaa\xd4Ho\xe0\x04W\xb7\x86\xcfՓ\xdbnG\x90D\x0e?\xaa\x16\xa0\xd0\xd9C'" } internal_nodes: { key: "B0A=" value: "\xfb[\xa6\u009e\xd5\xd4\xc3a\x85E\x81\xc44\x9f,΃\xbd?\xe7\x1e\x83o\x06\xd5LXm\xa9Y\x12" } internal_nodes: { key: "B0I=" value: "\xe8D\x97;\xb0\x1b\x9cK\x0b\xfc\xf2\xd3\x144\x8b\x0b\x15\xaf\xa5\xedR\xa6ÛŦ\xd6K:\x8c\xd8#bN" } internal_nodes: { key: "B0Q=" value: "\xf6@\x9cQ\xbc\xbc\xe0\xff\x83@\x1e\x12\x9f\xaa)\x0f\x98\xc3\xec\x17\x0b\x8e\xf5&#K\xc3ڀ\xb3\xd9\x04" } internal_nodes: { key: "B0Y=" value: "I\xdc=G\xcd\xd5\x1a\x06\x19(\xae`\xb6k\xf3\x94D\t\xa2\xfe\x08\x97&Į¤=\xe2ʂx\xed\x1d" } internal_nodes: { key: "B0g=" value: "c\x1f\xff\xa6\x0e\x18\xe3\xf3x8\x91!\xef\x14\xc4u7Ų¤\xae\x88l\xe8\x0bp\xd8\xf5\x0e\xe6\"d\xfd" } internal_nodes: { key: "B0o=" value: "V\x83 \xd7~\x80\x1cK\x04nDe\x98=\xbe\x945V;\x96Č 2M\x94p=BG\"\xae\xa3" } internal_nodes: { key: "B0w=" value: "\x05\x06\x03\x8f_\x8f(\xb4UnbXX6\xf8\x9c\xe3\x81\xfe\x9f\xb2r\xd2D" } internal_nodes: { key: "B1g=" value: "\xf5\x94\xc1E\x1d\xc1ZJp~\x83p\xa6\xe1\x9ck!t\xb2\x88!}\x15\r\x12vu护\x12>" } internal_nodes: { key: "B1o=" value: "\x9c\x9a\x80\x1a\x9b\xa8R\x9d\x96\xe8\x91S\t\xd6|\x92\x120\x04\xad\xb1\xc6\x11e\xad\xaa:\x90I%\xb7\xe2" } internal_nodes: { key: "B1w=" value: "\xd2\x19\xf3\xf7;\xe1\xdc[\xf9\xa0Mk\xe5օj\xdd\xdbT\x1di}\x85r\x88%\x04O\xe0\xc6\xe1\xa8" } internal_nodes: { key: "B24=" value: "]/tm\xf0\xba\x00\x9d\xfb\xe5\xf1\x06aq?\xf0^PF\xcd,\xa4K\x14e\xd9n\x82\xe5" } internal_nodes: { key: "B2A=" value: "(y\xf5\xb7d\xc2\xce\x11\xc5\x0f+w\xa0╝\xda9^\xf8x\xb9\x1c\xb4\x08z\xb6\x0c\x08\xa5\xe5q" } internal_nodes: { key: "B2I=" value: "\x12\x08\xf5\x1cB\x8a\xf4\xf1]v\x08\xa2P\x9aw*`t\xc7;S\xbaڂpO\xbaYg\xd7\xe2\xca" } internal_nodes: { key: "B2Q=" value: "\xa2\x96Nzm9\xaf\xd4=u\x8c\xa6\x98\xe8\xd2\x1f\xaf\x00\x9f\xda\xe93\x95\xc2\x01\x00M\x9a2\xa6*\xfe" } internal_nodes: { key: "B2Y=" value: "\x9b_@]\xac\x17\xf5\xbc9[[Ų¤Ö¯\xa5\x04vx\x12\xb4k\x85\x90l/Ԝ\x95\xec\xf7\xa9" } internal_nodes: { key: "B2g=" value: "\xfbP\x03ߗʧ\xcd\x14\xf4A\xad\xaa\x9a(*p^\xfa!\xcbd\x0b\xa0\x1b^\x7f\nZ\x1d\xef\x87" } internal_nodes: { key: "B2o=" value: "$z\xc1\xca\xc0Ê\xff\xf1\t\x9f\x8c\xff\xfc\x83\x83Z\x9a\xc6\xf3z\\\xf1Ū\tm!\xecu\x9d\xb3" } internal_nodes: { key: "B2w=" value: "WN\x9a\x0c\xa5/i\xe3\xa1)\x10\xe6*\xe9\xd3U\t4VD\x03\x95\xb1\xdbo?Y\xf4V\xf3=\xbf" } internal_nodes: { key: "B34=" value: "\xcay\xbeƅ\xe3\n\xb7\x87A\xbc\xc4\xddˍ\xc1\xb1\xb7\x19k\xba\u0082\xc2|k&\xe7\x11Xbg" } internal_nodes: { key: "B3A=" value: "w$@\xa9|p\x884\xc5\xe3\xaf'y\xa7\x86\xfe\xdeo\xe4\xeb\xdc\xd2\x02\xd1\xe3\x8f,\xa7\x95\xceځ" } internal_nodes: { key: "B3I=" value: "\x953\xc2\xc3\xe6\xa9V\x0ci\xc8N\xeb\xfe\xe7;\xe3vfv\x88e\x7fƌM\xa5#G\xdd-J\xe0" } internal_nodes: { key: "B3Q=" value: "I\xabJ3\xf9\x92_\x16\xc8f\x15\xc1=\r\xaa\x08~\xa4\xb2\xf8O\x93\x84u\xc4F\xd7\x19\xb2\xf8[`" } internal_nodes: { key: "B3Y=" value: "Ņ\xf5\xca\xff\x95\xe4\xb44\xd8\x0c\x94\xfdF\xdd\x02\x83\x878\xe8\xee9T\xed,\xe4\x1d\xc8!\x8b\xa0\xbf" } internal_nodes: { key: "B3g=" value: "ĐĨ\x1b\xfb\xf5\xab\xfdc\xd8\xd4\x04Ai\x05x\xd2O\x00\xea2\x95\xf2}\xf4\naa\x9bS\xab\xd0\xe9" } internal_nodes: { key: "B3o=" value: "}\xf9\xc49\xf6\xf9x_\xb2\xfb\x17\x08QBQuY\x8f\xb3\t\x16\xe0A\xa6U\xef/\xba{\xc3\x1b\x89" } internal_nodes: { key: "B3w=" value: "\x8e6j-@\xce\xdcH\xf0dR\xce\x00\x90s\xdd\xfa\xfc\x04\x9c\x9eA])S\rSHut>e" } internal_nodes: { key: "B44=" value: "\x12\xb4\xa5\xc4(q\x17:\xdef\xc4\x00z+F5\u008b\xcb\xe3\x0cj\xdf6E\x04\xcf\xfb +80" } internal_nodes: { key: "B4A=" value: "^\xe4~\xb3\xd0*\x0f\x9bBn6\x1b&\xfb>\x80\xc5aX\xaep\xa8/\x9a$\x92{\xec\xd6cmD" } internal_nodes: { key: "B4I=" value: "\xe7\x03N\xedx?!\x18E\x8a\x1e\xa3\xcf\\?\xca\xe5\xb9\xdcL\x1f;Yt\x1f\x92\x9e\xc2;z\xe2q" } internal_nodes: { key: "B4Q=" value: "\xbc\x8e\x98\xbag\x12\x1b\xa2\xa3G\x05\xbf\xa8\xd5\xfb\xd0A=\x02\xab-'B*\x87a4\x0euM\xd1\x17" } internal_nodes: { key: "B4Y=" value: "x\x13\xfa\x1bG|\xd24-\x00\xac\xc9\x14u\xfe|r\xe6c\xab\xddfY1\xd4\x18\xa3\x1495\xc7\xd9" } internal_nodes: { key: "B4g=" value: "\xa3\xa666\xf2\xaf\xcdf\x1d\xef>X\x91\xb2C\x0c\xc9+\xd8\xd3]\x98\xfc\x1c)\xe3\xf2\xf12J+\xc3" } internal_nodes: { key: "B4o=" value: "\xb5\x811\xb1~\xaf\x87\r\xc9\x19\x81\xb2\xcc\xdbMo\x1a\xf7\xe0\x93\xaf\x88\x8f\x90\x84M(\x9f\xeeg\x19\xe4" } internal_nodes: { key: "B4w=" value: "o\x88h\xff\x86\xbe\xfah\xa8\x8a\xf2\x15\xd3(\nP]\xb3J\xd2g\xcb|\x1f\x8f\xb6\xff\xd8\x17" } internal_nodes: { key: "B5o=" value: "\x0b\xae\xcd\xe2\"\xff\x9b\x08\x87\x9b;<;\x90\x1bc\xd1\xe9\xeb+_\xb10ld\xcdb\xffb\x89#Z" } internal_nodes: { key: "B5w=" value: "3@y4\x1b\x9c6H\x02Z\x92\xee\x0fFt\xedan\x7f\x0e\x06Ũ„\x97\xba/U\xfb\x8b.\xec\xdd" } internal_nodes: { key: "B64=" value: "ȧ\xed\x96\x11\x7fbB?ua\xc0]\xb5\x08\xa1#@ã˛\x07hc\xdep\x91Ęš'>\xfe" } internal_nodes: { key: "B6A=" value: "\x8b`\x97\xf9E\x15\xa1S;\xa8\r(O\xf9Ec!\xebJ0O48\xce\x05\xdf9#߇*6" } internal_nodes: { key: "B6I=" value: "QЃ\x07\x8e3\xef\x88C\xa3\x88\xfe\xe6Rt\u008f\xf9n\xa88\x18\x968u\xfd\x00p~\xa6\x0cA" } internal_nodes: { key: "B6Q=" value: "\xc1w4{\xd9=A\x15\xeb\xb0g\x0b\xb8y\\\x94P\x99\x19\x8a\x1ba\xf9?\x83\x82_\xec\x06<\x91\x98" } internal_nodes: { key: "B6Y=" value: "EŪ”\x1c\xfeMt\x88\xbao2\xbb\xc6t\xe81\xdd\xd5t\xe123N\xe0E$\xf2X\x83E\x9ap" } internal_nodes: { key: "B6g=" value: "\x16z#-,qS\xdecB\x7f\xe0\xfcɈĖĒ\xb3\x81\x95\x9eN\xb1\xc8\xdd\xec\x1a+\xa6Xe\xd2" } internal_nodes: { key: "B6o=" value: "@\xb28\x98\x8bx\xbef\xaa\xa9\xa1&Į…\xfc\xe8^\x84h#:b^\r\xa4\x8d\xc4q\xcfl\x9eJ" } internal_nodes: { key: "B6w=" value: "\x91\x13c\x80\xbfՎ\xe5\xb0\xecrs\xec\x0cL\x9e\xc3s(K\x9d\x81W\x16y7(s\xdb\xea\x07\n" } internal_nodes: { key: "B74=" value: "\x0e\xa1\xd0_\x0b\xf5\xa7r`\xff1H\x0e\x0b\xb3okM\xfb\x9d\x8d\x05\xa2\xf4\xf5\x10\x03^\xa4\xb2D\x97" } internal_nodes: { key: "B7A=" value: "\x01į°°\xd5\xffz)\xc7JqMŅĩ`q\xb2\xbc\xb6\xe8xB\x80\xe6\xaf/C\xb5\x0f\xc4\x13V" } internal_nodes: { key: "B7I=" value: "\x96\x95\x8fC\xc1\xe5\xd1\xfe\xc4T1,H\x84(\x82\xe9\x03\xda2\xdf\xd5?A\x8d\x92ĮžĪ–S\xb9" } internal_nodes: { key: "B7Q=" value: "4\xc7\x19\xa4\x10\xa5\x85\x85˕\xba\xddШ\x95\x97\xe7PI*\x99)A\xd1NWt\x92\x00\xa9,G" } internal_nodes: { key: "B7Y=" value: "\xa4\xa8\xdf\xd4Ų›c퟊-\xb9i\x0e\xaePŨĩ^\xfb\xa0Ķ’\xf3xxR\x02^7Wc" } internal_nodes: { key: "B7g=" value: "\xe04\x84\xb1\x9b\xfa\xc4\x16A\xd69?a\x0c\xde<>\xf0\x84m\x12'\xfdz\xcc7\xa58\x13\x96_\xbd" } internal_nodes: { key: "B7o=" value: "\x84\xcc\xf1\xc7\x01\xcd0\xfd4\xa0\xe9\x02N>n\x07\xe8\xac8\x8b%\xdd\xceR\x8b#S\x92\x87\rJx" } internal_nodes: { key: "B7w=" value: "\xf7\x8f\xae\x1a\xccfP)\xcf(^\x0f\x86-Qؙ8\xf3\x1b\xb4\xc6#f`Åģ4i\x8a\xe8\x14" } internal_nodes: { key: "B84=" value: "\xe8\x92\x1b\x9dn\x8c\xe5\x18&\x16\xc3ueĝ\xe7%\xa1N\x17\x1co\x97]k6\x8b\xbd\xcc8hB" } internal_nodes: { key: "B8A=" value: "\x9e_gßž\xber\x9aq\xccV\x1cm\x19\xbd\xd7ߍ\xf4\x8f$!U\x14\x18\xe1\x1dm\xad\xef\xee\xdc" } internal_nodes: { key: "B8I=" value: "\xaeF\xe7\x1e\xf5cS\x93\xc9\x00\xafԛˍ\xc5\xff*\x18\x96\xd3\x16\x16~\x98\x17\xbd\x08:\xd5<\xe3" } internal_nodes: { key: "B8Q=" value: "\xc4Ų–N\x1c\x16B*\xf2\"\xb5\xcd\x1cr\x1bC]\xba\xef\xf4\x92SN\xcd\xcd\xd94UŲĸ\xb2\xfb" } internal_nodes: { key: "B8Y=" value: "4\xa0o\xac\x932\x00\xf0J\xccr\x9dÂĩ\x9e'\xbaatA\xa3\x8f\xd3\x1cw\x12[\x81\xef+\xc88" } internal_nodes: { key: "B8g=" value: "\xe31!}\xa7H9\x1c\x0f\x85\x04<\x88'\xfd\xca\x18EŌŠ\xb6\xf3\xc9\xe76\xad=\xe9\x94\xc0b\xc5" } internal_nodes: { key: "B8o=" value: "Bgx\x9e\xe1}\x8b\xab\xa1x!\xd4\xc6\xe2\xb6Vį‚ž\xfb\x94\x90l\x02\x82\xc6\xd5G-N\xaa\xca" } internal_nodes: { key: "B8w=" value: "\xd5WRb\xb0\xea\x85\xd3\x1cFd\xf6\xb3\x13\x88\xc7o\xdc\x1eg\xc9ÍĢ\x13\xf6F\xe6\u009b-\xaa^" } internal_nodes: { key: "B94=" value: "\x82q\x88\xc2[\xcb\x1d\x91.\x1a\xbe\xab:\xa7S\x10$\x7f\xb9\x0fCo\x10\x811Ûˇ\xf0D\x0b\xaf\x81" } internal_nodes: { key: "B9A=" value: "J\xbbF\xb65\xca\x04=AeŨĸT!!\x9e\xfd\x13Y&o\xac\x81\xcc0\x96`z\xd0\xccY\xb5" } internal_nodes: { key: "B9I=" value: "9\xa8\xe7\xb1\xee\xa1\x02\xb1O\x00\xe6\xe8\xf2#\xe1O\x15U\x0c\x89\xf5$(3\xda/\x97\xc0pK\x0b\x97" } internal_nodes: { key: "B9Q=" value: "\xf9\x91\x15\xb5\xdeM\xe2xE\xd69\xc8\t\xcd\x02\x8f[\xe5\"\xd7r\xde\xc6CB#\xe1\xfb`\xc5\xfc<" } internal_nodes: { key: "B9Y=" value: "A\x8a\x02\xd0l\x1e\x98\x1b\xe1\xb8\xfa\xc5pc\x83\x12zk8E_\xff\x01\x0fM\xc8w?\x9f\xeb\xe1s" } internal_nodes: { key: "B9g=" value: "\xf47\xcc`\xeb:\xf69\x9bD\xcb\xf8zOe\xba\xab\x8c\x05Į„|2\x10ˍ\xb3^\n\x9b\x7f1" } internal_nodes: { key: "B9o=" value: "\xdf%\\\xf5\x1et\xb9\xee*\tr\xc3Į\x93\x15\xd8ëŊ§a\xa2pl\xeb`OUz\xfcB\x98" } internal_nodes: { key: "B9w=" value: "\x12\x02\xd3ÂĢq杰P\x8c\x16\xae-a\t\r\xb7\xc2@\x1aLj\xe8&:3h\xf8\x80\x99\xc1" } internal_nodes: { key: "BAA=" value: "\xfd\xe2\xf1\x98\xc3i8\xa2a\x07/X4I\x04w\xb0\x8coVH~\xea\xbdX\x96\x9a\x9e-/f\xb2" } internal_nodes: { key: "BBA=" value: "\xf7A_\xfd\xbe\x98S\x1c\x88\x97\xad\xf8foe#\xdf\x13\xec\xb6\xf7RŌ‡\x85\xdf\x00\xe4\x00\xe1\x1c\x9f" } internal_nodes: { key: "BCA=" value: "\xd5ŌĒ\x89\xb9\xecm\x10[\xb7\x99-ł\xc1\x11#Ō \x9a\xcb?L\xb4\xf9\x82\x06Z\x90\xd1*\x0b" } internal_nodes: { key: "BDA=" value: "\xd6D\xfa\xb5\xe5\xfb\xe8\x196\xb9\x08C\xd0\x03:9E\xc3v?\xefp\xcdQ\x83\x1fcW\r\x96\xe1d" } internal_nodes: { key: "BEA=" value: "\xcex\"\x00\x0eÚ\x8b6%\xcd\x0f8\x01C\x8a\xe4c\xccB'X\xe5$f\xf1@\xe61\xbdy;" } internal_nodes: { key: "BFA=" value: "d,1[˜I\xbfbh\xa2\xe1?\xf8\x19g\xc0\x90\x7f\xd5ËŊ`D\x81\xe5rh\\\x1fp\x1e" } internal_nodes: { key: "BGA=" value: "\xa1\x96\x10$\x93)\xb6\xc4J\x95t\x98Y\xcc\n4\xea\x9d]L\xfb\xb0\xd8ˈ\xec`\"Q\xd76r" } internal_nodes: { key: "BHA=" value: "\xf43\xf2\xa1`'\xc8?y\x80ĶŠX\xa8Q<\xe1\xe8~\x019FFc33\xcdN,\xf2\x7f\xd2" } internal_nodes: { key: "BIA=" value: "\xbb\x1e\x91\x86WTCYA\x04\x1b\xc8\xd11k\x96u\xa1Y-\xbd\x81\xf7?I\x05\xc41\xadz\x86a" } internal_nodes: { key: "BJA=" value: "?\x12\xd1j.\x0e\xde\xd7\x06]Xf\xc2\xe1\xc2\xc6\xce{Ũģ<\xca&\xcaҧ\x92\xeb\x06u+o" } internal_nodes: { key: "BKA=" value: "\xf1\xa3\x8cd@\x96\x8a\xdf\xf2\x95a1}\x8c \x05r\x18i\x97ȏ\xcc\x05\xca\xfb\x12\xa8/\x0e\xb8T" } internal_nodes: { key: "BLA=" value: "\x0e\xf0\x95|E_\x1f\xc2F\xae\xaf\xfa\xc3\xdd\xf9\xba\x90\xb8\x9fH@\x10ov\x92\xd8M\\H\x18\xcc\x13" } internal_nodes: { key: "BMA=" value: "\x9b\x1a\xbd\xdc\xc8K\x02\xa2T\x98\x8cÂŋ\xfb\xf00\xc8,8\x01\xf9+\x065L\x162&\x04\xc7\xc5e" } internal_nodes: { key: "BNA=" value: "~Ųļ\x89a\x8c~\x8b]\xbb\x1e8\xd6\xf1\xa0\xcbR\x7f@\x0f\x14\xd5n\x90\xa2\x13\xc72kw0\x07" } internal_nodes: { key: "BOA=" value: "\xd4v\xbf\xc2\xe8[Nl\xcau\x15x\xf6+\x9e\x16\xf2\xbaV\xc7\xf26\x13\xe2\xa9\x11\x86\x93Û´\xc2\x1b" } internal_nodes: { key: "BPA=" value: ":\xe4\xf2\xc8\x0c~VU\x0c\x19q\xa7fH\xc2~\x8a\xbb\xa0\x92\xfe\x88\xa1|DU7\x99\xb0\x87\xc06" } internal_nodes: { key: "BQA=" value: "\xcf%^Ք\xb4\xab%\xa0\xffx\x1fw\x12\x83\x7fO\x9e]\x10sU\xa9\x17,\x048qI\x87\xf2O" } internal_nodes: { key: "BQg=" value: "\x81\xeb@coPS7\x96\x1d{\xdc[\xf5\xb7\xfc\xcb\xf8\xc2*\x96\xee\x13\x10+ã̈\xd4*4=" } internal_nodes: { key: "BRA=" value: "؝\xbb%\x82\xaf\xdc8ȁ\x13\x88H\xe2\x19\x96\xdc_\x14-\x16\xc2\xc9\xd2\xfc\x81\rŨĻ\xc9]\x10" } internal_nodes: { key: "BRg=" value: "\x9buW\x82\xb6\xf1OA\x81\xd9\xfe\x16\x9e\xcfhcSˏA}X\xa0\x1d$\x89\x8f\xad`a\xaf+" } internal_nodes: { key: "BSA=" value: "\x83\x1f\x9e\xaa,\xa9\x0fX\x10\xfd.\xff\xd7\xc9Q\x9d\xc1\xc9\x19A*\xc6T\x05^\x97-u\x07w\x8c\xbc" } internal_nodes: { key: "BSg=" value: "\xad\x81%îŦž\xc83\xd9.K\xa2\xae\xd6nc\xb9\x08p\xfd!\xb6\x9cY\x88}cBFRҝ" } internal_nodes: { key: "BTA=" value: "B\x93\xa2П\xc1[\xef\xfb\xf7\x00\x01Ũ\xfc\x11\x9d\xa4\xd3n\x85_S\x02\xf9\xddɆ\xecKI\"" } internal_nodes: { key: "BTg=" value: "\x92\x9b\xf1\xa7S\x93\xae\x13R\xc3EK\x12\x93\xef\x9a\x1b\xc6\x0f+=\xc0tJ\x1c\x03P\xec\xc5\x06\x15\xd9" } internal_nodes: { key: "BUA=" value: "\x81tÅŗ\xda\xf4\x14\x11\x1ds\x88\xeb\x08QM\xe6\x16\xb3\xf2\x9d\x9f\x1c\xca\x1a\xe5\xc0.\x8ae\x83\xc3\x08" } internal_nodes: { key: "BUg=" value: "\xdej-X\xe1\x8c\xdbo\xecQ\x8b\x98\xb9\x06XćR\xfb\x7f\x08\xc0\x0b\xd0\x19V\x1d\xe5X\x82\x84\xc8" } internal_nodes: { key: "BVA=" value: "\xf2ˏ\xc7\xe8\xe4 \xfd\x1eb\x97B\xd5u\xcb]\x04\xc8_\x949\x01&GX(\xce\xe7\xde\x1fn\xc2" } internal_nodes: { key: "BVg=" value: "=\x9f\xd2W\xaa\xc6ce\xa7\x98c=OBe\x1f\xfb\xdc?:/\xdbq\xb5\xd3ֈ\xfb\xa8\x8f\x9a\xe4" } internal_nodes: { key: "BWA=" value: "kڗ@9j\x88\x80\xb9\xe6\xd2.\x07\xf7(F\xec\x01{\xc0\xd7~V\xb2--4\x8b\xc7\x18J\x07" } internal_nodes: { key: "BWg=" value: "\tĖģC\xecW#\xaa\xd0]QRa,(ĝQ\x18H\x15\xfd\x06\xfcC&#\xec.X^9" } internal_nodes: { key: "BXA=" value: "\x87\xe8?\x03\xb8\xecX\t\n\x1f\xc6~\xa0\x04\xdf[\xc34^b\x8f9\xc8]\x90B\x86\x9d\x1dE\x88\xe0" } internal_nodes: { key: "BXg=" value: "r\xb3\x99_Ĩ\x8b\x0fz\xb4\xc33\xf5\u0085s\x99\x03\xa3\xfe?\x02\x02\xf2\x9f\xc8e\xb1J\xa7[\xab" } internal_nodes: { key: "BYA=" value: "\x14\xfd9\x03<5\xf5\x84\xf3'q\xce\x1a4\xad:B\xb2\x881j\x0f]\xf3\xac\xe8\xb7\xe1\xb60\x7fW" } internal_nodes: { key: "BYg=" value: "Q\x13Ί\xa6\x92\xcbG\x1dB\x13@\xb4qM\xf9\xc4\r\rKELė\x8e\xbc\xbb\xeb\x1c\x02\xa3" } internal_nodes: { key: "BZA=" value: "\x05/k\xa0\xa7\xa8qD(\x08\x17$\xb7\xff\xfcX\xea~\xa1\xd5ί\x81\xb1!\x80\n\xbfp\x06\x12\x90" } internal_nodes: { key: "Blw=" value: "~\xc2d!\x075,4\xad\xb7\x1a\xeb\x18\x9f\xb7n\xabQ\xfd\x01R\xb0:h\xaa\x0b\xd7*\xb3\x94X\x8c" } internal_nodes: { key: "BmA=" value: "\xb5E\x91_\xe8\xee\x17S\x18\xeb%\xfc\xf0\x8b\xe3\x9b[Z\x9f)\xac\xec\xed\x835\x94\x8fgQ\xcb\xe6\xe8" } internal_nodes: { key: "BmQ=" value: "B\xec\xd5\xe3|\xc8\xf9\x86\xa2\xb2Su\xedВ\xe6w5\x16k\xb27p\x8e\xf5Nk\x89\t@Ëŧ" } internal_nodes: { key: "Bmg=" value: "\x96\xabN\xd8~\x81cQ\x0fW?\x15Äēé„Ģ\xef\x05n\x13\xda\xe80\xbd#\x85\xfd~\x0b\x1c\xbd" } internal_nodes: { key: "Bmw=" value: "\x97\xd3\xf9G\xbbM4\x84\xb4\x87\xf5\xcd\x14\x19Y\xf7\r,\x80Ā\xfc\xbajG\xa2%\xe2\x01\xca}$" } internal_nodes: { key: "BnA=" value: ".ŌēĮ´\xee\xd7rT\xb3\xca6\xa4\xbd]&<\x81 \x9a\x94\xe6\x16\xb9\x02\x16>\xe3<2\xa1\xdb" } internal_nodes: { key: "BnQ=" value: "SF\xfde\xb0\xcc\xc7C\xc0\x1e\x84\x88\xf4\xb8?u3\x9e\xb1\x14\x15#\x96`>\xd0\xf6ʒ\tB1" } internal_nodes: { key: "Bng=" value: "_\xf9{\xe1\x17\x93\xfa#\xe6m\xf2l\x98Īŗ8`\xf5e;\xf2\xf1hB\xba\x05\x7f\x19Ɵ\x17\x1b" } internal_nodes: { key: "Bnw=" value: "\xa3\xfe\\\xbf\x15Q\x19\x14\x0c) \xce\xd6\x14Îĸ\xad\x9e\xb48\x9f\xa0\xe8\x16\x9e\x92S\x0b\xe6M\x10u" } internal_nodes: { key: "BoA=" value: "\x1bl\x1e\xc2\xf6\x15\xf7\xecCL\xcaȉG\n4W\xc7\x17N\x16\xacm\x91\xb8\xf6\x101\xde|\xb5\xe7" } internal_nodes: { key: "BoQ=" value: "|\x81Y\xd8'Q<\xddI\xc5\x018^\xc27@s\xad\xfa\x06\xcbP\xf4\x9d\xb4\x9fͧ\xaa\xb1L\x83" } internal_nodes: { key: "Bog=" value: "\x90\x11,\x81\xf6\xc9A\xbc]\x9fj\xfa\x08\x06\xc9U\xb2p\x82H\x06.\x13\x82C\x89\xdc\xe1)`\xfbz" } internal_nodes: { key: "Bow=" value: "\xc6\\\xc6(\xe5\x07Y\xb4:\x90!\x0b\n;\xe4\x05\x8a\xa2\xb2\x80\x19\x17\xdb\x14l\xa1\t\xdan=iN" } internal_nodes: { key: "BpA=" value: "\xa3\xfa\xa5f\xed\xfeF\xebØĨ\x069\x10\x0b\x10L9D\xe0\xe2\xe5\x1d\x86[,\x08\x84eˁ\xaa\x13" } internal_nodes: { key: "BpQ=" value: "Z\xc3W/Ns\xa7\x87\x88%:\xed\xdb\xcc\x08\xe6><\xea\x04\x1d\x9f\x1c\n_\xb6\xaf\xa3~\xce\xd6\xd3" } internal_nodes: { key: "Bpg=" value: "!\x1a\xf14\x1cj:\xbb\xd4Ũ­\xec\xe3\xd4\xe0\x8e\xe4'p\xe4\xe5T}\xef\x9fhRɇ\xf7\xbd\x11" } internal_nodes: { key: "Bpw=" value: "\xe9\xc1|\xef\xfa\xfbTy)\xa9\xa8\xf1\xdc\xdef\xab\xdc\xf4\xb5qm\x1fu\x98u\x8a\x84\xf9\xd9R\x14\xa1" } internal_nodes: { key: "BqA=" value: "\x82S6\xdb\xdaČļ$M\xbd\xbaxG\x80v\xf4\\\x13Tn\x00r%!\x19\x8b5\xea\x8b'\xb14" } internal_nodes: { key: "BqQ=" value: "Äž\xfaQfR\xf4\xd5\xd3.\x11\x03\xc7\x15`\xcd\xe0OH\xfebԄ\xcd\xddS|\xc48\"\xe26" } internal_nodes: { key: "Bqg=" value: "z\x08\x1bb/k\x9f\x8b[\xcctpI\xb2\xcd)܅8D\xfd>|ɒUB\xccS\xac5\xef" } internal_nodes: { key: "Bqw=" value: "\x07\xce\xeb\xd2\xd4Zk\xbb!\xd5<\xf9\xfe\xf5XR\xe8\x8c\x1c\xfeG\xb0\xbe\xd7\x07\x82\x1e+\x10\xb6\xb4\x85" } internal_nodes: { key: "BrA=" value: ")\x81\x05\xf2\xe4\xc7)\x8dW\x0b76Z|\xee\x11e^\xed\x83\xe1h\xe2!\xd3R}ÅŽ\xd5B\x1c" } internal_nodes: { key: "BrQ=" value: "yHt\xce)\x8f \x88;\x1aG\xd0_\xe5\x07\x85I\xc3\xc2\x02\x81z\xfdZ\x82(\x91\x01\x87(\x04+7h\x8d\x07\x9f\n\x1f" } internal_nodes: { key: "BvQ=" value: "\xb2\xe5ט\x0e\"A\xe5\x1e\x91nG3\u0090\xb2\xf4A!" } internal_nodes: { key: "BwA=" value: "J\x0c\x8f1\xc4\xdf\xc7\xf9\xca\xd0\x04t\xa3\x10\x9e^\x93.ted!\xf7\xe0X\x1a\x99d\x1a\x9f\n\xfa" } internal_nodes: { key: "BwI=" value: "\xf7\xe0\xc5\x0b\xc6}0L\xec\xb0\xebÃēw\xc4P\xcaf\x86\xbc\x9b=\nA}\xa7\xf9\xbc\xeb\x7fp:" } internal_nodes: { key: "BwQ=" value: "\xcf\xe6\x1cV\xe3i$;>\xd3\x13w$y_\xb6\x91\xdefFb\xd3+=V\xc0\xdf\xeb\xbd\x07\xfe\xbb" } internal_nodes: { key: "BwY=" value: "\x8a\xc1y\x19\xabb\xe0\xe3.\x05\xef\xb9|\xab\xb9O\xa8oG}\x80\xf2t\x0b\x88\t\xb6\xc1A\xfa\xee\x97" } internal_nodes: { key: "Bwg=" value: "\x04\xdf\xf1\xe9\x87\xfdd\x0e\xc3;}iM\xd2\x03\xac\x15[\xf1\xb2\x8dbX\x0c\xdcc\xefu\x81\x00m\x8a" } internal_nodes: { key: "Bwo=" value: "T\x84\xb6\x89\xed~P\xc9y\x03\x08\x9f\x9cH\xc6\xc4\x10\x1b\xc9\xce\xc1$NP(\xf1\x98j\xae\xd8\x01\x7f" } internal_nodes: { key: "Bww=" value: "mpÖŊ\xbaTR\r\xbaJ\x17\x8f\x0cU\xdaD:\xa1X@S\x89\xe6K,\xee\xb4@\x00\x99~B" } internal_nodes: { key: "Bx4=" value: "g\xbf\xdf(\x97d\xa9dfrŨ˜\xe0\xeeAL4pqS'\xb9}[nY\x94\xc8\xe7\x15r\x17" } internal_nodes: { key: "BxA=" value: "@\xfa{\xb7\xe1\xf7\x80A\x94\x9c}ĶĨ>\xb9h\xeeEsrF!\x85Õĩ\x1c\x12\x1a\xba\xe1\xd5n" } internal_nodes: { key: "BxI=" value: "Y\xf6$Bi\xc4Æŋ8\xee\xa7Xl\xc9\x1eM\x16\x82Dn\xb5˜\xf6\\\x1e\xcc\xc4^dx9" } internal_nodes: { key: "BxQ=" value: "\x11r\xd50(j\xdeTxf\x9c\xe0U\xa5k`\xb5)R\x9b\xa1Ȍ\xa1%\xf8T\x95\x17\xe4iu" } internal_nodes: { key: "BxY=" value: "\x00.G\x9bw\xb7\xf3\xeb\xccY\x025k\x80#\xaf\xc0Fn\x8cČĩ|a\xfe\x9cr\x05x#\x12." } internal_nodes: { key: "Bxg=" value: "\x12M\x9eb\xed$\xa4'\xbe\x8ak\x85\xff\xd7\x03`\x84\xc1\x12\xfd\x02e\xc6l!\xddw\xc5a\x83\xdf\xe8" } internal_nodes: { key: "Bxo=" value: "\x8d\xacÎĢ*\xfb\xe5\xafr`\xa4&yk\x14A\x08\x02J{Y\xf0\xac\xa8Zf\xad\xfb\xc9KA\xac" } internal_nodes: { key: "Bxw=" value: "\xb0cI\xfdI\xd5*\xa5\tf4n\xae\xe2\xc3,k\xd42\x19\x08\xdat3K\xccF\xbd\x8c\xb6#\x97" } internal_nodes: { key: "By4=" value: "\x92\xa6\x9eJ-ĐŊ\xa6C\xef\xab\xf5m\x81\xac\xaf\x88\xacZ\x06\xe8!Ɖv\x8c\xc6\x01\x1f%\x90v" } internal_nodes: { key: "ByA=" value: "\xa47Tr\xa81Ų„\xa5s'\x0cz}\n\xfc\t\xbf\xd5zIs\x06\xb75Y\xdcZ\x02\xed\xc5W" } internal_nodes: { key: "ByI=" value: "Ņ”)Ņ–\xabIN\xacTØ­\x84dL\x8a\xed\x93\x12\x01ȕ\xf2\x86냜\xb9\x1fg\x9a\x90" } internal_nodes: { key: "ByQ=" value: "\xa4o/\xfa\xfc\xf0\xb5j\x19a41\xe9se\x0b\x1b\xd6ic\x92;\xa1\xa5\x05`\xd9\xe5\xf2\x105\xe3" } internal_nodes: { key: "ByY=" value: "\xbd\x06Ũ˛D\xf7\x1a\"\xc7ÂŽ\x9a\x94\x84\x96l\x80tqQvR\x9a\xde\x07\x16\xd9LGd\xec\xb0" } internal_nodes: { key: "Byg=" value: "\x8a?\xfc\xf8j\xb7c\xe1\x01\xb0\x82\x0c\x82\x96S\x88q\xbcD\xbc\xfc\xb8v1\xa0\x1c\xc7\x0e\xb2C*\x8e" } internal_nodes: { key: "Byo=" value: "\x1b\x1b\xf9\x89\xd7z\xba7w\xa5e[\x13\x9ao\x97TI\xe4&\x08\xb7\xdaQ|\xa7\x90c\xda\xdcYK" } internal_nodes: { key: "Byw=" value: "\x8cT\xc0\x9c\r\xfdS\xeb\xbf\xef\xcb\x1a\xf0\xf5\xab3;\x05B&U9\x0c\xa1\xc8\xf8\xc40\xb3\x0b\xae\xbe" } internal_nodes: { key: "Bz4=" value: "\xed\x9c0V}\xfe\xc5\xee\xec\t\xad\xc2\xfc\x8eG\x1a\xc4O,\x84\x05\xc9y\xb3\xb3F\xac\xf3\xc46#d" } internal_nodes: { key: "BzA=" value: "\xd7\xee\x7f;\xbdŨ–v\xd8Z0\x92\xe9W1'\xa3\x90q\xf6\xc5rXԃ\x99x\x1d3\x12\x90\x05" } internal_nodes: { key: "BzI=" value: "O\xef}'\xd7\xd8\xe7\xd4R\xe19\xad\xfcy?X1wx\xef4Ry\xa1\x8a\x8f\x00\xda2\x01\xee\xbd" } internal_nodes: { key: "BzQ=" value: "3\xe2\xc8!_$\x17j\xe9\x0f\x8aP\xaf\x8eŅĢ\xae\xablo\x95ߥ\xa5\xd5\xef\x97O8\xd7\x1b\xfa" } internal_nodes: { key: "BzY=" value: "\xbc:\xf8\xf9\x1e$\x15H*\xcaH\x18h\xef{\xae\x13\x84\x0fJ\xe0#\x90\xc7AaԘ,\xf6\xf1\x82" } internal_nodes: { key: "Bzg=" value: "\u009d\x98ä\xe8\xc5*\xdc:\xa9\xddA(\xb52\x83\x07\x86\xa6W\xc8H[(t\xed\xd9\x1by^\xe7" } internal_nodes: { key: "Bzo=" value: "\xb8W\xe8\xd1\x1d3\x03 \n\xc0ڍ\x9f\xeaH\xb8ÖļȲ4\xafS:\xe6\xdfj\\\x94\x0e#N" } internal_nodes: { key: "Bzw=" value: "6\x91\x8a\xfcg\xbd\x82[Ī›\\\x9b\xcfN\x8a\xa24\xde\xd4\x07\x02%\x0f\xa7\xf87[\xd4t\x18Õš" } internal_node_count: 254 prefix: "\x00\x00\x00\x00\x00\x00\x03" depth: 8 leaves: { key: "CA0=" value: "\x11L\xa1^\x1f\xe6\xeb\x9d<\xa0H\xd6O:t\x8a͍=14\xaa\xffu\xb0ne\x89D\x88#\x0e" } leaves: { key: "CA4=" value: "*I$+\xaf:}\x15\x13\x97\xb1s\xf4\x15\x14\xb7w\xa0\x97d+\x8e\x9f\xfe=Z\xa5\x9e\x92\xe0\xe7\x8f" } leaves: { key: "CA8=" value: ")\xb5Ņ\x0f\x1e\xf9\x8d\xbd\xa22Hg\n\xd4\xe5\x03\xd1c\x81\x87\xe1\xfc\xf57\xc58^\xe0\xda\xf5p" } leaves: { key: "CAA=" value: "\x0c\x13P\x17J\x9c\xe0\rT\xe1\x7f\xdb\x05eG\x91\xc5\x1f?\x12F\x87i\x11\x9a\x85\xa2\x15Ά\x84\xb8" } leaves: { key: "CAE=" value: "\xef\xe0\xf1O'\x1eqU\xc3L3ix\x12M\xc2a\x0c\x94\xda\x7f\xa4˓\xf0w\x88\xb3,4|Z" } leaves: { key: "CAI=" value: "_\x03(N\x84\x85\t\x87\x9f\xe3k&%\xb907\xd8J\xb3to\xfb\x18\xe0-\x7fs\x94\xab\xe4f1" } leaves: { key: "CAM=" value: "4\x8e\x99I\xb8b\xfb\xde\xde\x18\xfa\xafg=\xac\xe2\xe1\xacJ\xd66\"\xf2\xcc\xf5\xc1\x11[\xccz\xaet" } leaves: { key: "CAQ=" value: "*!o\x7f\x8f\xfc\xe8\x93J\xbb\x19\xc2P\xfbnW1\x8b\x0c,\xc6\x1fK\xbf\xc1t(:\xf4\xfc\xca\n" } leaves: { key: "CAU=" value: "\xeb\x16\xc0\xfc1\xdd\xd3\t\xb6Ɵ\xb0p\x8dr\xf6q\x91\xb9Ɯ\xfd\x15\xc05\xdaڟUy\t<" } leaves: { key: "CAY=" value: "Čą\x8bI\xff3\xd9\xdbq\x04\x8f\x9b\xb5\x8b\xe9i1D\xe562\x1f\x92_\xd3\xe9\xa7rTT\xac+" } leaves: { key: "CAc=" value: "\"\x9f\x80o\xb8gI\x03\xd8_\x94L))\xb9\xf2U\xddNX$;\x0c\xba\x87!O!B\x86\x83$" } leaves: { key: "CAg=" value: "\xe5\x08Y\xab+\xee\xd8G\x08\xc6$\xf0ja\xa8\xc1\x9f\x98=\xa5\xf6\xf7oX}\x7f\xbe\xa8\x82ėš–" } leaves: { key: "CAk=" value: "\xfd\x0f\x1f\xb8\x88r*I\xb7\xdeS;\xb4@\xea;\xcd\xe3]9E9\x14\xde@b\xf1ˁX)\xda" } leaves: { key: "CAo=" value: "\xd4\xebd\xddC\x18\x9e\xaf'\x18yȗ\x1b\x7f\xfe\x12.\x87R\xe6\xdfY\rhd?@\x08t\xc1\x86" } leaves: { key: "CAs=" value: "<,\x91%\xb4\x7f\xad\x92\xc5z\xf9\x0bgorV\xe1u\x96\x91\xa4\x19\x8e\r\xd2*ÚĒf\xadJ\xfe\x88\xfb\x03\xf6\x03\x8e" } leaves: { key: "CB4=" value: "\xf3\xaf(\xcc\xc97\x80\xc9\xf8\xc5\x14\xb6\xcdÆŖR\xd5|ƟQl\xcd#\xe7cR0\xd2vA\xc0" } leaves: { key: "CB8=" value: "b\xeb0^~\xe69\xe3\xce>\xc4}NJ\x00\x0bv\xfc\x98 \xec.\xa5ʆs\xea)\xe7s\xe8\xd0" } leaves: { key: "CBA=" value: "\x82\xbc* \xf7\xc0\x15!\xaa\xf3-j\x8cH\x8cu\x8d\xca\xe1$oÍŋy,\xebtԁ a " } leaves: { key: "CBE=" value: "\x0c\x00\xf4\x86,o\x80$\xdbtՕ\xe6Īž\x06*\xe2t\xd3\xf3;u\xa9\xc0\x8e\xa5\xec^\xcd|\"" } leaves: { key: "CBI=" value: "\xac\xaf\x05g7m\x8a\x82\x94\xf9\xbad\xffȡ\xd9\xe0\n\xae6\x8csA\x01>\x10\xb5A\xd3s\x1e\xc1" } leaves: { key: "CBM=" value: "\xa8\xafP\xd4\x1a[u\x80?\xa4\x87^T,\x10b\\\xc3\x1c\xa4\xf7HYAeQ\x0f\x04m\x9e\xd0W" } leaves: { key: "CBQ=" value: "\x8af4\xe2O\x98\xad\x9f\x8d\x1d~\xf4\x12\xff\x16F\xe0O\x1e\x9b\xc1u\xdf\xcb\x01\xf4\x18\xaa\xcc\xedR\xbd" } leaves: { key: "CBU=" value: "\n\x19\xd9\"CNR-)p\xbd\xb1\x94_e8p\xab\x044\xe2\xafh\r\x8f\xa4\x12\x05\xe3e\x86\xbc" } leaves: { key: "CBY=" value: "\xcdUm\x10]\xdd\n\xf8\xfd\x81\x8a2m\x03%[\\\x14_\x81\xf2w|\xec\xb2fU\xe1)Q\x19\xcb" } leaves: { key: "CBc=" value: "Bi\xd7\xfd\xber\xd6\xc1@\xces\xa5\xda\x17\x98\xa9\xa3W\xa6\xd5\xe0\xe9\x01\x98\xd0\x0e\xca\x0b\xaf\x91\x9a\xfc" } leaves: { key: "CBg=" value: "vV\x8d\x7f\x98\xcc\xe3\xc8\x07\xbb\xbf\t\xbeU\xb3\x0fČ `\x86\xea\x0b\xa4\xdd\xc8{\xc2\xf1ob\xae\r" } leaves: { key: "CBk=" value: "\xa1b\x0by\xbdŪ›\xa6\x0b\x0bT͘EF\xb5^\x98\x81Ų“\xe0\xefe\x0b\xf8\x16I\x15\x91\xb2\xdb" } leaves: { key: "CBo=" value: "j\xde\xdf\xc9\xd4\x0f\xa1+ \xaa\x1ee\n!nˁ\x8c2\xb1b.\xba\xc5\xebW\xb2w\xc9\x1dX\xf7" } leaves: { key: "CBs=" value: "ߊ\x82\x84-\x8f\x9b \xd5I\xc3o|\xe0\x05\x17g\xc1%)ß ^\xf5\x88\x92Qn\xa3\x19\xa2T" } leaves: { key: "CBw=" value: " \\\x7f\x84\xb9sT\xfdQ\x0b-\xech)\xbc\xd34\xeb\xb8\xc1\x93f\xd4uV\x0f\xcf\xf6\x8d\xbe\x0co" } leaves: { key: "CC0=" value: "\x9dO\x11\xc9W\xbb\x1f\x89\xe1<\x9d\xf5\xc9K\xf8\xfe\x86_\x94\xbe\xfa\xf5\xf3\xf0\xbd\xcdZ2\x1b\xba\xb5O" } leaves: { key: "CC4=" value: "\xf0'\xb0G\xe0\x16\xdb\xe6\xe5\n\x94Íą\xefL,kK\xe6lZG\x80\xfd\xc6\x144\x0bŅŖ\xad>" } leaves: { key: "CC8=" value: "\xc8nx\x8cl\xf1\xf3\xa0\xd0\xd1C{\xbe,\x94\t\xf8\x0f_\x8f\x8aO\x802b\xfb\x9d\x01\x97hVG" } leaves: { key: "CCA=" value: "\x08\xb4\xe8\x8c\xe2,\xac\xf2\x96q\x80\xa6\xc0\xb0\xd15\xfc\xda\xe6A\xb3\xa9\x08\xd0P\xf2\xd2\xe0\xdd\tk\xf9" } leaves: { key: "CCE=" value: "\x0cm\xa4V\xcdu\x10\x95LL\x96H\x01\x84\xd5\x0fks\x158\x17\x1e\x01*\x9fd\xb2y\x1e59\xaf" } leaves: { key: "CCI=" value: "\xb9b\xf0\xd5耚mwJ\xb0\x98\xc6r\xd6s\x96\x9a\xf3\xb7Uv\x8f\xf1W\x8ceVnG{y" } leaves: { key: "CCM=" value: "\xcaCœ\xde\xd3!\xf2^\x1b\xf9\xb6\x14\x1e\xfd\x80\"u\x08\xe1|\xa4?`\x8d\xafxl\xe1@\xbf\xb5" } leaves: { key: "CCQ=" value: "bh\xe1\x8a[\x95c{\x0c\xfe\x9d^\xef\x90\\\x13\xa2S?\x19\xa2(\x1f\xdcY(M\x08\xf1\x01\x97\x8e" } leaves: { key: "CCU=" value: "2PaIv\xd6B\x0b\xb5d\xa9\x1fį—Ē\x18\x80\xc0\x8bOU1M,\x96\xdeVZST=\xc5" } leaves: { key: "CCY=" value: "\x9e\x8f\x84\xb0\xd4;\xc6\xeb}e\xcf\xeaD\x90U0\xce\\b\xf6H>\xf0\x96\\,E@\xa1T0\xf8" } leaves: { key: "CCc=" value: "W3\xab\xb2\x19:9\x9f\x19\x91 \x90\x18!\x9cA\xab\x05P\xadc-\xa2\x15\xb8U\x81+<\xda}\xc2" } leaves: { key: "CCg=" value: "\x18\xf3\xcd\x11\xb4K\x10\xc2\xcdQ\x9c\x81\xb6\x14Q\xd7\xdd\x02YC\xa6\xbdP\xd8%\xd2\r\x0e\x0eW\x1dN" } leaves: { key: "CCk=" value: "Y\x81Pa\xcd(\x06\x07ˍ\x89\xb3\xfe\xf6z\xa9\x96n8ȓ4w\xea\x93\x1e\x13\x1d-\xf8,g" } leaves: { key: "CCo=" value: ".H\xf4Q\xc1\xbc\xedk$c;$*B\x8e\xf5\x15b9Ûˇ.\x15<\x0e\xe7\x98\xda$\xd4bG" } leaves: { key: "CCs=" value: "\x07-ËŦ=\xf4n\xa0W\x9d\xbc\xeb)\xb1Y\x83f\x15o\xd0\x19\x91%M3r\x00\xa0\x13}\xe9\x9f" } leaves: { key: "CCw=" value: "\xa6W\x98\xd6Ũ†\x0e֖q]\xfcė‰ļ\x0fa\x12\xdb?E?\x90\r\x82\xcd\xc2׃\xbc\x88\xb0" } leaves: { key: "CD0=" value: "\xea\x88xb}^\x05B{bH\xa5\x82)rU\r\x02b$\xda:H\"\xb7\x8cnj\xff¸\xd8" } leaves: { key: "CD4=" value: "\xc3\xc4,\x01R\x8c}R\xde+=\xe6\x14\x05o\xaf\x95\xb0\xc5\xf6\xeaŨŧ\xb0\xbf\x98\x89 \x9fJ3/" } leaves: { key: "CD8=" value: "\xd5\xc4'\n[\xac\xcbs\xbe\xc8\x04\xb2s\x84k\x90-\x177\x8cT\x0eM\x96\x1a^\xaf\x15!\x8f!\xea" } leaves: { key: "CDA=" value: "*\xac\x00\\؋\xadN5\xd5\x10\x12\xff\x8eh\xb1\xf9\xe6\xe7_\x03\xba\x1d\x0eNQ9\xa4\x16ÖĄ\x04" } leaves: { key: "CDE=" value: "\xc5\x06\xb9X\xa2\x82\x8c\x81\x91˯\x01\x17\xa1\x85\xd1m\x0ey\x91\xec\xe3\xdb\x13\xd3\xdd\x10\xe0\xb8'\x91\xac" } leaves: { key: "CDI=" value: "%%\x17K\x18\xb3\x97\xfc\xf7\x9f\x1e\x14r\x04q\xb7\xed\xf0T\x84\x80f\xe8\xa7\xe0\x88\xd53\xa3S\x8a\x02" } leaves: { key: "CDM=" value: "\x8e\x85\xb3\x96\x98'\xd7\r\xff\xf7\t\x08\x9b\xbbfe\xab\x90olל_\xa8\xd9\x00\xa0\xd3\xfa\x01^." } leaves: { key: "CDQ=" value: "\x1c\x04\xdb\xc0a\xc5E\xde\x1a\xa8\x18C\xe79%\xa2\xc0.\xe1\xfb'2\xdaX7C\xcfԇ5\xee\xe6" } leaves: { key: "CDU=" value: "!ht\x94\xe9\xc9Y\xf9\xc2\xa65\x0c\xf2,\xa4\xd6,O/;" } leaves: { key: "CE4=" value: "\x91\x8f\xf1\xfc\xdd$+c\x820뷉f\x8c\x1em\x8f\xc0\"W5\x90\xb0O\xed\xbdR\x03T\xf1\xe7" } leaves: { key: "CE8=" value: "B\xcd\x06\xb3\x9d\xf6c\x06\xe3CŌĨ\x85\xefE\x82Bߓ\xa9gS\x11\xfa|\xa4\xe7\x07p\xa1\x03+" } leaves: { key: "CEA=" value: "\xdd\xd4a\xe7\xdej\xdaͰ(\x19`\xe7\xdcâē™\x0239\x9eA\xe5g\xba1\xce\xcbp\xedc\x92" } leaves: { key: "CEE=" value: "\xe9\x90Ym'\x9e\xb0,\xdfDSu\x9a\r~\x0f\x1d\xa6-\xd6a\xe3}\xee\xbc0q\xd5H\xcb\xf5n" } leaves: { key: "CEI=" value: "K\x19\xe5w7\xc0S\x87\xff-\x9b\xdeD\xf0M\xf2I\xbeO\xd9\xc812r\x1e\xf8\xff\x06J\x04\xa8f" } leaves: { key: "CEM=" value: "Q\xb8G\x17\x9d\x93\x8a~E\x84o\x849\x96\"\x9a\xe4\tg?\xc7l\x8283ßĩb9\xcbG\x84" } leaves: { key: "CEQ=" value: "@C\x9d\xd2\xf2\xfa\x8d\x87\x9eATv\xa3\xbco\x1aO\xb0\xfa;t\xd7\n\xe9\xd7\xdbywd\xbcF<" } leaves: { key: "CEU=" value: "\x9c\x19:@\xb5\xbe,\x89[\xd5U\xb3\xbb4\x1e\xfd%E\xf3\x9e\x18W!\x14\x8b\x15I\x12;\x04\x1cY" } leaves: { key: "CEY=" value: "\x86Q>)\x8a\x00\x82\xc3\x08\x08\xe7\xd1rU\x88Us\xf3sq\x93\x87}" } leaves: { key: "CFc=" value: "\xdd\xe7\xd3\xd1\x18\xe5\x0e#\xf2\xa9\xf0\xd4\xde\x10:G\xc4PŨ¸A\xdb\xf0\x00\x82~!\xa7*\x98\x96\x02" } leaves: { key: "CFg=" value: "\xday(F(\xe7\x0bz\x12\xff_x\x00C\xe2\xe4\xac`\xfb\x9fvzz\x17K2\x11\x03\xe4\x0f\xe9\"" } leaves: { key: "CFk=" value: "\x81KX\x80:\xbf\xeaE\xdd\x0b\xd5\xfdl\xcc\xfef\x87\x1d\xb7\x86e\xb0\x0et\xa0xW\r\x10\x84\xeb\x92" } leaves: { key: "CFo=" value: "\x02\xdbKH^\x814lR`\x00X\x0c\xf0j@ÉĄ\x19\xa0\nĪļ5\x14\xf6(A\x06\x1b\x8c\xe9" } leaves: { key: "CFs=" value: "6Į\x8aPĐĢ\x92\xdf=\xb2\xfb\xf7IX\x84\xfc|\xfeY~\x93\xfc40\x16\xbb\x8d\xd5\xd6\xd8\xd4" } leaves: { key: "CFw=" value: "\x17~\x06B2\x01\xf0\x03$\xd0Ч\x13\xb9\xa6\"\xeb\xc8r\x96+\x02*\n\x84%\x9b\x93\xeb\xbd \x80" } leaves: { key: "CG0=" value: "\xb01\x99\x9fr\x00u\xb2A\x13^h=\x93%XY\"\xeaĪ”Û„\xed8'\xf7\xe6Ī€Ûƒ" } leaves: { key: "CG4=" value: "\n\x85z\x7f^t>\xfbdd\x7fR+\xfc\xcbQ\xa9UQ9\xf7\xf7\x8b\x16/\xc9\x7fq\xa2\xb9%\xc8" } leaves: { key: "CG8=" value: "\xcd\x06\x8a\xe8\xe4\x85\xd5r\xb7\x96ĪĄ\xd1\xf4\xba\x08E\x12Zw\x85\x89m\xf3\xc4\xd4u}q\xa2\xacJ" } leaves: { key: "CGA=" value: "\xd9>\xa4Õļp\x81\x15&\x97@\xec\xfb\x8a\xc2!\x1c\xf7T\xceIa\x0c\x90hv\t\xa6\xba\xc1E\x14" } leaves: { key: "CGE=" value: "\xe8\x8dp\xd6l\nh\xf7\xea\x94.\xe9z=\xb2\xc8.\x91\x1d\xbe\xc3&8\x9aaU\xee\x9a\x07\x13L\x1d" } leaves: { key: "CGI=" value: "\xb2\x1d\xef\x00\x9d+\x1e\xdci\xfcŲŧ5\xecJO\x88\xff\xca~\x124\xe5\x1a\xd8J\\\xedGm\\\xe1" } leaves: { key: "CGM=" value: "\x16\xfcZt\xc5$\xd8\xc9R\x01\xbc\xde\x02\xa0\xcfS&K\x1d\xd8f\xc3\xc5e\x82\x05\r(1x\x81n" } leaves: { key: "CGQ=" value: "\x90E\xfb@\x1av\xd5\x1c\xa4+\xf9$'3\xff\xd1|W\xd6\n:\x036(\\n\"r)\xa2aG" } leaves: { key: "CGU=" value: "=\xc2\xc2\xf7\r\\\xbf\xa2M\x8d\xed\xb4\xbf\xa8\xf8aF\x07\xad\xda\x07\xe6ֈG\x0b\x11\xa5_d\xe3\xfb" } leaves: { key: "CGY=" value: "\xf2\xed\x85qƌ\xb7\x1eQ5\x99Ņŧ^-\x17b\x89\xfd~\xd2\xec\xcb\x0c\xee\xbeG\xf7\xfa\xd8\x12\xa0" } leaves: { key: "CGc=" value: "\x89\x8eLH\xb4T\x8d\xae\"Q\x91x\x15 w\x1cw-\xcb3\xc7\xfd\xeb\x0b\x9bff\\\x16p\x0ba" } leaves: { key: "CGg=" value: "'\xe4xB\xda0\x8b\x14\x1e/\x98fl\xf8\x99\xbe,\x1b\xe1=J\xff\x9c\xa4<\xff\xfd\x00\xbd{\xda\xc8" } leaves: { key: "CGk=" value: "\x03}\xf5Įĩ1\xd6娍\xd3\xe4\x03Deyr\x8a9\x8b\x9d\x06Yc\xf0\xf7\x06\xfb(\x03˜" } leaves: { key: "CGo=" value: "\xc7W\xb4\xf0R\xa6\xb4.wR\x96'\x19\xa7@\xb0\xe366\x80\x1d\x01\xb2r\xd4\xfb\x8dT\xaa^\n\x0c" } leaves: { key: "CGs=" value: "\xc5\xe6\x1b\x8dwouAq\xd2J}$Dsv\xe0\x17\xbeIe\xac\xef'\x05\x1a!\x9a\xa1\xe3\xdf*" } leaves: { key: "CGw=" value: "}\xa5}l\x11îžļĮŧM\xab\x92\xab}\xbaz\xc5j\x07\xbdNQ\x9d)\xfd\x80\xf2_\xe6r\x0f" } leaves: { key: "CH0=" value: "k\x14ox\xf7\xc2J2V3\xb1\xca\xcb\\D\x12\x9el\xb9\x1e\xd6\xc1\xab\x06w$o\xba\xebf\x04F" } leaves: { key: "CH4=" value: "\xa32`\xee\\\x07\x10slaO\xb1u˰\xa3\xba\xbb)\x17Īŋq8\x95\x18\xeb4i\xacG\xd3" } leaves: { key: "CH8=" value: "Qm\x9a΀\x06t\xa3\xf1w]{\x01*\xfd\xc1\x88\x8f\xb6\xa8\n?\xe1J\xf3\x83\xcawZ\xd4F\\" } leaves: { key: "CHA=" value: "\xa3\xbf\xc9p\x93a\xb0\x9eF\x95E&0\x00\xab\xba'\x92O\x93\x1e\x92\x1b\xef\xae\x13\x95\xbe\x9c4\xf9(" } leaves: { key: "CHE=" value: "\xd7F\xe9^r\n\x8c\x1e^ĸ\xef\t}>\xe1b'\x18wT\xc3l2\x88j\xea\xb6\xf7\xf9\xfe\xd8" } leaves: { key: "CHI=" value: "T\xfff,K\x12\xbb\xac\xa6\x0eؙ\x8e\x12h4\x9fs_ AT\xfdR\x96\x89Di\x917\x9b\x1e" } leaves: { key: "CHM=" value: "n6\xbb0[yv@]\x87-\xf4{p[\xb2Y\x9b\x92\xaa\xbc\xa7\xe4\xbcst\xa2\xff^a\x91\xc9" } leaves: { key: "CHQ=" value: "%\x99\xa7\xf6-3\x0b\xe2u\xaf\x8e܋LS\xff\x01\x15?Χ\t\xa9ID\x8d\xb8\x96\xd9w7\xb0" } leaves: { key: "CHU=" value: "\xb7\xa3\xce\xefF\x15\xf1\xfc\x0b\x95\xc8y\xc8vf\x12\x84k\x18'B\xe4h\x8eÉ­\xc3fv\xcc\x08(" } leaves: { key: "CHY=" value: "\xca2jg\xc0\x84\xab\xdb\xe3\xf7O \xee\x9e=\x98ç{\xb6Z\x84'\xe1\x90A\x0cÚš\x96H\x8b" } leaves: { key: "CHc=" value: "\x8bĐŖ\xc1\xca\xfe\x10GrjY\xf2t\xed\x05|\xe4>\xa6H\t\xbbnVˁ4\xe7\x8b\x1f1#" } leaves: { key: "CHg=" value: "\x00\xfe\xb4\xf0\xf0\xf6\x11\xf0\xe3\xdbB\xa7\xcb&\xc2\xeeqF\xa9K\xef9w\xb6oeH\xca=p|u" } leaves: { key: "CHk=" value: "\xbf\xealÚ¤\x8c=[d\xe3)\xfb\xb7\xd1\x1b\x8c\x1b\xe9\xddA\"\xfa\x84\x9c^C\xe6b\xd97\x80\x93" } leaves: { key: "CHo=" value: "\xa6\xfc/\x9c\xa0 S\x86\xe3&[\x96oˀ\x97(m\x8dJ\xfaO#8\xcc:\xfd\xe8\x05q;\x92" } leaves: { key: "CHs=" value: ">ÆŠ\x91w.\x0eLK\x94\x88\x01\xec{\x0b\xf67\xf7\xbc|ׂ(\x004\x81\x90\xa9\x97ɑ\x9d" } leaves: { key: "CHw=" value: "j\x06(j\xab\xb2\xc7P\xec\xdd\x00\xed\x97\x01^\xafB\x1d\x13~)=Íž\x90\xdd\x18{\xa3י5" } leaves: { key: "CI0=" value: "Ed\r\x0e\x07\x12\x91\xa3\x92G\x97\x9a\xd0H\x94\xb8\x8d\x15\xceH3\x8b\x00&>\x9c\x06*C(\xc7K" } leaves: { key: "CI4=" value: "Q\xc3\xfd\x93c\xfc?\x179\xd8q\xe8\x02\xe2U\xbdWU\xe2\x14\r4\xf66\xe5\x01Ũ’\xb1m\x1d\xf4" } leaves: { key: "CI8=" value: "\x01k`\t\xb7\x0e\xe5j,\xbd\x8d\xed\xaf\x1ed\xae\xa1C.j`z\xf39=֜6\x9c/Lt" } leaves: { key: "CIA=" value: "h\x07\x81\x1e\x0ce\n\x9c\tc\x8b\xf2\xaa\xcbMTo\x8e\x12\x9a\x19\xbdV\xde\x0b\xe8U\"\x83H]\xf6" } leaves: { key: "CIE=" value: "\x97\x99\xbb>\x0e\xe2\xf4r\xc4 ty=<\xadI\xd9G\xfawN\xe7\xfc\xae\x88\xee]\xd64\xa3B\xbc" } leaves: { key: "CII=" value: "\xf2\xde\x1e՟$\x94ڂ\xbd\xd2\xcbP˟\xcbNF\xba\xcc\xccKÔĻ\xacG\x90\x00CԌ\xb8" } leaves: { key: "CIM=" value: "\xe9|\x0b\x8f\x16\x12`Vv\x98\xca \xa7q\x0e[\xb5\x91k[\x86\xa5\xc2\xc2\xc61\x81d\x15rd\xa5" } leaves: { key: "CIQ=" value: "|\xa3\xd6\xc7r\x87\xbde\x9f\xa0\xcbw^\x85\xe5GÃĸ\xa2\xfb+~\xed\x14\x82\xed\xe5\xd9#\x92\xde\xc1" } leaves: { key: "CIU=" value: "\x94\x00A8\xb2\\\xf4ßĢ\x88\n\xf7\xe4\xaf?\x90kŲ—QoV\x107}k\x1f\\\x9d\xc7|&" } leaves: { key: "CIY=" value: "?\xe2\xf4\xb0)\xa0\xa2O6\x03?\x12-\xe8Y\xde|\xdf\x13\xabO" } leaves: { key: "CIk=" value: "\xc4v\x1c\xba\xec\xfb\xc1\xa4\x11y \xc6\xc85 \x9b\xce\xd6(\x02\xb9=ĶŊ\xa5\xaa,\xe5\x99\x1f\tR" } leaves: { key: "CIo=" value: "{\x17\xbe\xeb]\xf2d\xd9\n+\xbf1L\xcc~\xde\"\t\xe8n\xe9\xe3DLi\xe0t\x0bp\xb1\xbc\xb1" } leaves: { key: "CIs=" value: "\x9e\x19.\xb9\x00âŋ™y\xf3\x87\x1f\x12?\xec\xb2\xf2\xa4>\x16`]{Ī›\x81\x0e\xe8\t\x97\u009e" } leaves: { key: "CIw=" value: "\x94#fVl\x8e\xecve\xba\x8b?.D\x1ab?\x99\xab\xd2\xe58\x15\xad\xa0\xb7\xb3\x17\xaf\xd5/1" } leaves: { key: "CJ0=" value: ";(\x11Sa\x0f\x84>\xeb\xb6\xd1\xf2\x1b\xe0\xcb\xfaK\x0fA.>\xee\x88\xe9\x123\xa0d\xf2\xa3\xde\xf3" } leaves: { key: "CJ4=" value: "\xd5[Y\x9c\n\x00\xf0\x81lp\xd4\xc5.\xbdЙ\xbcx\xda\xfcXĖĸ\xa7#\xaa\xbb\xdb%\x9e\xa1\x11" } leaves: { key: "CJ8=" value: "\xbeV\xf5\xa7\xb7\xfe\xb3\x03\x14:==\xb0*\x17\xb4N\xdf9*\x7fRK\x8f\x94\xe4åĸą\xeb\xbf*" } leaves: { key: "CJA=" value: "ÍŠ\xc7\xd4l\x13\xf4\xfaQĘļ\x9c\xd9ßĒz\r\xa3\x8e\xeb\x95\x0b\x92\xee\xd3I^eE7Gq" } leaves: { key: "CJE=" value: "n$B\xac\x12\xa2r\x99K\xdc\x16\x11\xe5T\xe9\x9aC\xa9\xd8|\xf7\xd0\xdf^xܯ\xd5a\xb6d\xc7" } leaves: { key: "CJI=" value: "9퍴\x87\x89,\x86\xc6\xda\xf5\xf3j\xfc\xd2\x1cn\x91\xf0\x94F\xa8\x1f\xbe\x0bS\xa4\x06\x87\xf1\x9d0\xa4" } leaves: { key: "CJg=" value: "Yc\x11q\x02\x1e\xc9^\x93à\xb0\xfd\x8ezfr\xa4\xe4NN\x9br\xeeW\x7f\xc9fxڈw" } leaves: { key: "CJk=" value: "\xbb\x9d\x9c@\xa32\xf3\x9d\xbffx\xc8\xf4\x06\xc8r\x08\xf9\x9f\x80\x17qm.\x97fL\xe9qu\xd1e" } leaves: { key: "CJo=" value: "))åļƒs\xa8\x82\x8f\x04c\x96\x92\x83\xbc4\xfd#\xa1rq\xbc\xae\xb9P@\x08\x82\xa8\xc1\xb4\x9e" } leaves: { key: "CJs=" value: "\xf07\x9e1\xfb\x8f\xe4æšĄ\xc0\xff\xa0i5Q\xb1cO\x9fA\tXR\xaa }yFG\x08x" } leaves: { key: "CJw=" value: "\xe9\xb3=\x100v2]\x80\x10\xba\x80\xaa[\xe14p\xb7pܐ\xf5\xf0\xe7:|Y\xbd\x1b\xd5ÖĨ" } leaves: { key: "CK0=" value: "Sâ§Ļ\x19\x0fh\xa8\x82\xb3\xa8$c\xdd\xdc\"\x8d*\x1d\xbbJ{\x00l\xce\xd2^jÉ­\xdc\xf2" } leaves: { key: "CK4=" value: "t\xa8\xb2\xfe\x1b\xb3|Yd\x18\x8a\xbe\xd2i\x99\x90\xce\r$s\xc6\xf3K\xb6\xf8\x12Z9\xa6\xc1\xbeZ" } leaves: { key: "CK8=" value: "\xa3<\xa9rȀc\x9b?b\xeb\xf7\xef\"\x88\xe3\xef\n\xfa'\xae#[:\x01\xb7W/\x9eA\xd3p" } leaves: { key: "CKA=" value: "\x91\xff\xf7\xcbJ\xc0\xba\xc1\xba\xcbZ\x9e\xb5\x16m_\xe3\xe6\x90\x11\xd5\x19\xb7\xbd\xceAx\xa9\xa9\x0f\xd23" } leaves: { key: "CKE=" value: "b\xcc+H\xba\xc4cE\xa9\xaf\xbb\xa6׹\xb6\xba&:O\xe1\xf4\xba\xfc\xe53\xa8$C\x1b\xf0\x0f\x1e" } leaves: { key: "CKI=" value: "\xc3\r.\xf7\xe4\xe7\xb4$\x98\xab\x82s`\xe2p\xfd\xae\x8aC\xaf3\xb4\xe3g\x9fq\x93\"v\xba\x18\x94" } leaves: { key: "CKM=" value: "\xc3\xf6r\x13P/Ht\xe0u\x84\xa5\xb1\xa5b\x06\xb4W\xa39Dȃ\xad\x9cg:\x01\xaf\xd9+r" } leaves: { key: "CKQ=" value: "x:\x8b\xfeA\xdcɛ\xa7\x04Ķ”n\xe3A\x9e\xfc\x8d\x1b!\xa6\xd0y\x7f\x0e\x8f\x15\x94\xbaVĮž" } leaves: { key: "CKU=" value: "s+u`\xf4P\x91\x9cyÅĢ{\xfa\xbeT\x1614\xed\x83(\x16\xae\xcdhu\x8bZ\xda'w\\" } leaves: { key: "CKY=" value: "\xbc\xb8\xab.6\xaf" } leaves: { key: "CLA=" value: "`\x07\x15\xd8?\xd7H\x02q\x0b՟\x84\xf8\x13W\xb1\xed#4=\x07\x95\x93\x0e\xfc\x95ΉQj\\" } leaves: { key: "CLE=" value: "\x8e\xed\xd2E\xf0U\xd9=\x8d\xb5\x14=%Vj\xe5\xdd?>:\x8c\x1b\xe5\x068\x88\xc0\n\x15\x94-\x8c" } leaves: { key: "CLI=" value: "\x82\xd5\xf2W.\xa1\xe5j\xef\x1e\xd0}d\xefbj\xb0Z\xb8(\x1e\x125+\x0ez\x1f\x1c!(\xbcj" } leaves: { key: "CLM=" value: "\xdaR\x8e\xd5\xe1\xc0\xd3\x117\x95d+\x86B\xe5\xa8L\x8c\"\x9aM\x07\x1f\x1e\xd6'\xb1)\xf0\xa5a\xd2" } leaves: { key: "CLQ=" value: "S\x9fG\xcb\x1a\x89\x8d\xf0\xbd\x8ex~`;iDP\xcd\xc5g\xb5(\x03\xeb:\xca\x18\x06F\x06|\xf3" } leaves: { key: "CLU=" value: "'\x86\"s\xc9\xdb9\xf2)\x85XiK\xc6%;\xfcŅĻqp\xebA\xfbXÛ¨\xef\xa6\xf2~\xb7" } leaves: { key: "CLY=" value: "\xea\x18\x98\xc1\xa6\xad\xd4-\x0eD\xb2╕u\xe9\x00\xb7kÃą\xb0\xfa\xe3\xed\xaf\xa3\xd4=oIK" } leaves: { key: "CLc=" value: "\x10l\x95\xa9\xef\x1d\xf0\xb3\xd7U\x02\xe8\x12\x87?\xba\xe4\x03\xef×Ēpu;\x81\xa8-܎Q\x84\xe7" } leaves: { key: "CLg=" value: "'\xfd89\x98w\x9f\x1a\x8e/\xf1O+\x1a1l\x88=\xcfu\x98 \xbb\rh\xc5\x07a\xb61\xa1\xe0" } leaves: { key: "CLk=" value: "k\x82\x0c\x05\xc6\xe2\x1ci\xd3\x01\xe7\xb5R\x01\xe1\xb5\xc7\xea\x16eC\xf9\xe6h\xda\xe8\x86y\xb3<\x15\r" } leaves: { key: "CLo=" value: "\xa81FR\x00\t\x97\xfeĪŊ\xe4\x87\xf2͒9\xcd9\xb9S\x95Ū­6\x97@\xf1KQ\xd3\xd3;" } leaves: { key: "CLs=" value: "\x1c[Uz\xa5\x9c\x0b\x1eŪĨ9\xc0\xac\xd7\xd4\x08US)X{\x91q×§-\xde\xc9\x19\x84\x7fJ" } leaves: { key: "CLw=" value: "V\x83&\xb8\xa3\xf8\xec]\xd0\xd1Y\x0f\xe8\ti\x97\xe1(G\xf4[?\x1a\x93\xf5\x14\xd3\xcan\x9b~)" } leaves: { key: "CM0=" value: "\x9e\xfbĶŽ\x0c\x84\x90\xa1\x14\xa7\xb1ʐ\xca?\x97k\x13\xa2W\xcf\xf0\x96\xd2\x1c~ßĻ\x1b\xa2\x08\xfd" } leaves: { key: "CM4=" value: "\x8f\xcciT\x91\xcc\xc4\xd6G_\xdf\r\x01\xa4\xa0N\xf5\xb4\xba.\x03s\x0efq\xcaP\xdaĪ‚\xad\x00" } leaves: { key: "CM8=" value: "\xc9\xf3Mf\xe2[\xf0\xb5\xb0l/X&\xaf&2Q\xd0I\x12,p-\xa2\xff\xb1\xba@A#/\xa7" } leaves: { key: "CMA=" value: "-\xdc;\xa5\x92.\xa2\xf5߈W\xb3>Öˇ\x88,E)5\x1d9\xe3_\xe0n\x1a\x88\xef\xdbZ4" } leaves: { key: "CME=" value: "=\xab\x083\x8e\xbap\x16\xb40?\xba\xe5\r\xd4\ru\x8a -ĘĻ|]l>\x18\x95\x89\x8aPe" } leaves: { key: "CMI=" value: "\xef\xc3\xd2B\x8b\x18+K\xf3\\\x98\x1a\x0c*#\x1amn\x8fŨĒ\xe9\xec(\x8a|3\xd1\x0b\xc3l\xdb" } leaves: { key: "CMM=" value: "c\xf3\xf2/G6G\x04\x00[r-$\xe8\x84J\xa9\xeb\xe0\xc0?\xbf.،Z)\xead1\xca*" } leaves: { key: "CMQ=" value: "\x03\xf0\xa6\xb7(\x8a\"\xf8@\x15\x9dw\xf9\x8c\x8c{}\xf4\x86\xfas\x02\x82\xf7\xbf\x9f\"\xb3{\xf0\x9d*" } leaves: { key: "CMU=" value: "nrŅ”c&\x14\x15\x0fp\x15\x1b\x8c\xb3\x91M\xcbM|\xb9\xbc\x13j\xd8V\x03\xfc\xae\x04d\xee\xef" } leaves: { key: "CMY=" value: "L\xd7\xda5(\xe4\xb9q\xff\xef\\!\xb0&\xe7\xa7\xf5\xf0^j*|%\xbcqi\xd7M)%\x1bM" } leaves: { key: "CMc=" value: "\xe3_uqa\x8c\x06\xa47]s\t?n\xf9\xf9:Tx\xd6~DÂŖ\xa8\x97\xd3\xee\x9e2\x81*" } leaves: { key: "CMg=" value: "\xf7\x17\xb4\x089\xf4\x9b\rŅ,\xd0%\x13J*69\xf3*\x1d\xe4\xe4:\xadU\xdcl\r\xf0\xa3T" } leaves: { key: "CMk=" value: "\xa2\x81)\xd1m\xc8ĖŊֈ\x9f~\x08\xf1\n\xb8\xbdS\xcbŪŖ>\x8e@\x85\xc3\x0e\x01\x840\x05\x92" } leaves: { key: "CMo=" value: "\x19\xe6\xf3\xa2\ta6e\xe3\x03Sn\xb4\xb1R\xb8Z\xea\x90\xf7\x9b\x93\x1b\x01X\x97\x02pD\xbc\xbe\x83" } leaves: { key: "CMs=" value: "\x11\xb3M{h\xeaw\xfd i\xcak\xbd\x91\x8cU-W\x0c\xe0\x84\x8d\x97\x06\xf4\x0b\x03\xc2c\xd5L\x80" } leaves: { key: "CMw=" value: "\xe0{_!Ņ€\xaa\xa9\xf5P7c\x18Jg{\x152\xa6FF\x9c\xfc|I\xa3\xac83\x08\xc5\xca" } leaves: { key: "CN0=" value: "\xc2q\xc9@\xfaP\xb6\xbe}\x95\x0bg\xee\"\x92ku\x03\xfd?E\xea\x9f3闌\x12\xecLv\x84" } leaves: { key: "CN4=" value: "\xad\x14\xfeD\xb2E)2m\xc5ŌŽ\x08\xac{s1^Ęą\x0eM\xad\x8d\xa6\xbe\xb4ۈ\x85\xcf^" } leaves: { key: "CN8=" value: "\xe3+\xe9ōģœĢ;\xce\xf8\xbcxo\x12\xa4Ũ‘\xff\xd0;\xc7\xd3\xd5\xc8\xc3C\xa2'\xc3d\x8eG" } leaves: { key: "CNA=" value: "\x89$(\xda(\r\xf6\xd8TV\x9f\x9d\x9bb\xe7\xd0\xda\xed\x07)\xffx={~\x12\x9c\xf1\xee\x1f\x81h" } leaves: { key: "CNE=" value: "\xc1\xf6\x15\x8c\x7f\xa8a\xb9u\x1fØą\xd0\xf3\xc0UЎ\xbbI\xcdv\xb7,N\x9f\x061_\x8e\x11U" } leaves: { key: "CNI=" value: "\x0btF\xfe^)\x01\x18\x8fx\x8b\x8e\xf4\n\x05e\x98\xa9 (\xf2p\x04PqK\xbd\xfb\xddĪŽ\x92" } leaves: { key: "CNM=" value: "\xe6oB$\x189\xbd\xf1\x81T\x8c\x82\x1f^Q\xa7/\xe5{\xea~e\x13Kys=\x01\r\x13W\xde" } leaves: { key: "CNQ=" value: "\x9bvw\x00M\xe0g1\xaaV\xc9O\x04\xf9\xc0\x8f\xdeG\x8a[\xfdi\r\x1e\xefW\xe2+\x92\x98\xe0O" } leaves: { key: "CNU=" value: "#ŲĢD\n\xa2>\x11\x84B\x95\x8f\xe8=\xd97\\\xbc(\x15{5\xcf\x1b)\x19d\xe09T\xae\xb2" } leaves: { key: "CNY=" value: "\x05+\x82\xd0)\x84B#\xf1\x8c*\x1e\xa9\xd8\xef\xebW\xc5+\xee\xa8C\xb5H^>\x9b\xba\x9e\x822\x8c" } leaves: { key: "CNc=" value: "\x03\xf2\xc0\x00\x93y\xe5\xca$\xda\xec\x08\xa8\x8e\xd5\xc7G\xcdKh3`\xccc2_5(e\xb6=\xd0" } leaves: { key: "CNg=" value: "Û°\x01d\x98\x02\xfd{-\x84\xf1\xd3&Z\xcb#\x18\xaf\xa2\xc2\xea\xafJz@2\xb7\xf6F|\x90S" } leaves: { key: "CNk=" value: "\x95\xd2h\x05\xb0\xa3\xa4N\xa3\x18<\xa0mv\xd2sKY;\x87\xdf\x187!X)\x07,SkÎŊ" } leaves: { key: "CNo=" value: "\xec\xa3JH}S\xd5fR\xe6\x81/\x1bÄĨz\xbe)6\xd2I\xd8\xca\xf6\xd5$\x89\x9bQS\xb9\xc7" } leaves: { key: "CNs=" value: "\x97\xf0\xf0,\xf9\x0fx\xdbk\xe2`%4\x83 j\xfd\x84\xea\xaa\xc3\xe8mb\xa5\xbb\xd0+&Đš." } leaves: { key: "CNw=" value: "\x0cW\xe0\x96C\x9c\xce]\xac!\xd0\x01\tc\x82\xd5z$\xe6W\xe0&l\xdfO%\x86\xc9\xd92F[" } leaves: { key: "CO0=" value: "S\x93枟M)|mÆ˛\x88e\xe9\nc\x8f\xe1u\x1dw\x03+\xe2\xe2\xa9\xf6\xad;\xc65D" } leaves: { key: "CO4=" value: "N\x1b\xbd\xca\x00\x87p\xc8\xe5\xa8\x1c|\xc0\x87\x04$\x93\x9bU\x1e'\x1f\x13I\xa8\x9f.äģą\xca=" } leaves: { key: "CO8=" value: "\x1b\xac\xbf\x82k\x83\xb5?\xd9\tu\xbe4\xb2܁Ԝu]Ų•K`\x98>\xacR3\x16\xd76" } leaves: { key: "COA=" value: "lד\xf3\xf0m^k\x05g\xaa\xfc\x1cf\xecVr\xe4\xe5\xf4\xb5\t\xff\x99\xfa\xc70^\xa9!\xc8d" } leaves: { key: "COE=" value: "\x19į´‹\x9e\xb9\x18\xec\x02I\x0c_\xbd+l>!\n\x134\t\xa4\xce7o\xe2\xd8\xf4\xedR\x0e\x01" } leaves: { key: "COI=" value: "\xc6\xfd\xc3^\xd6\xc7\xcd \x0cx=\x0e\xa4,\n\x99\xc5\xe6\x0b\x96\x88\xc83Ų•^\x88[\xf6\x98@y" } leaves: { key: "COM=" value: "CN\x8aM7\x97\x9d\xc1f3\xd2\xea\x19\x85\x02c\xa7\x1em\xac\xf8\xcd5\x17\x05C\xfcF\x0f/v\xf0" } leaves: { key: "COQ=" value: "\xde\x15\xd9\xf7\x99e@~\x9c\x18\xcf\u0085\x86\x9eYB\x8b@\x81\xe8\x99Ō‡\x03\xb1/Õŋ^\xea\x1f" } leaves: { key: "COU=" value: "=-\xcc0o\x87J\x997\x06\xcc\x1a\x89\xb1\xe6/yQ\xa7z\xaf1e\xc4\xea\xf7\x7f\xb0\x82+\xaa\xe7" } leaves: { key: "COY=" value: "\xa7n\x02\xa02z\xfd\x07\xb1\xf2&\x82qI\xf7\xc8I)l \r\xd5\xd0Yt\xa7\xdf\xf4`jHG" } leaves: { key: "COc=" value: "V\xf2\x8cČĻ\xed\xef]\xf3\x88\xf3\xe9\x94\xc9Ëž\xb8\x07\xe5\xa8o\x9b\x99\xf6\x8c\xb1ʓ\xa6\xc0\xc9R" } leaves: { key: "COg=" value: "\x91\xdb\xf3\xbcŲē\xea\xcfgF\xb7i\xc1\xcet:\\cÆ­z_\x9d\x8c\x8a\xc1z\xad\xad\xb5Օ" } leaves: { key: "COk=" value: "@+h\xc9\xe63\xc78GnQ\xc1\xf1͂g\xd0\x17\xe2\x9aG֌\x0bB)\x17\x11\x8f\xeb\x97T" } leaves: { key: "COo=" value: "c\xfb\xe8\x81\xd0\x08\x83\xea\xe0\xb9@*\x10\xd1xg\xa6\xa6m,\xb2\xa6\xea1\x8c\xd0\xc5\xebŨŒH\xb8" } leaves: { key: "COs=" value: "q\x91W%\xed\xc9\xc0\x12\x1cuk\xa6\xf4GT\xae\xf5܊xM\x95\xa7'yU\xfb\xe5\x91x \xc4" } leaves: { key: "COw=" value: "\xb8\xd9\xd5be\x84w\xdd\xdc\xe1;@\xd2.\xc5\x0c\x8f\x00h\xe5\x87\xfd\x92\x9c\xee\x869\x0b\x1c\xd6K\xe0" } leaves: { key: "CP0=" value: "\xed\xac\xc2N\xfe\xc0\xb2ŗj\x84\xea\x84Y\xeav\xd8Y\xa8c\x85\x82\xc6F\xc2\xe8\xa7q\x0cS\xa9\xc8" } leaves: { key: "CP4=" value: "\xe02\xc8\xeaÃ˛\xbed{M\x01a\x00\x9c\x98\r[w\xc9\xfeB\xa0\x19\xdf\xf6\xca\xe0\x0ck\x88\xbd\xb4" } leaves: { key: "CP8=" value: "\xbe\xe3\xfe\xe2\xeb6\x13\xda3\x19\xca_9pЏv1\xe5\xe8\x9d\"Îą\xb1e\x1d\xb5cF\xb2\x8f" } leaves: { key: "CPA=" value: "^f\x8c\x90W\x8d\x02\x99L\x89\xab\x05\x06_6\xfc'Z\xc8\xfc\x16\xc43\xf9\xee\x8c\xdbŪš\xa9$p" } leaves: { key: "CPE=" value: "\xaf8\x81\xa2\x08\xf1B6\xc1\xd8?\xa4\x01\x13\xb5Ap\x05Jv\xf6\xf0\xbf*3N\x90\xe1\xc8\xca\x1f\xcb" } leaves: { key: "CPI=" value: "\x14\xacj\x0e+\xd8:\xd88\x0b\xb4\x03\xd2\x196a\xa7\xb3\x9b\xfc\x10\x1d\x97F\xef\x0bv\xcd=\xf8aP" } leaves: { key: "CPM=" value: "\xf9O7<\xe8$\x9eM\x8aNv\xb4Y\xd8Đŧ+X\xe2\x963O\xaf\x1d{t\x86\xcan\rZ]" } leaves: { key: "CPQ=" value: "\x8c\xc2ښK\xabj\x9c\x8e\xa2VġH\xec\x9dT\xc5\xc01ÆŦ\xc1\x9aW\xd6\xfdĆ\x86\xc8\x18" } leaves: { key: "CPU=" value: "R\xbe\xdcl\xe9\xaf\x7f\xa5\xb0\xf7\xcd\x07L\x80\x97\x8b6\xfd\x06\xce\x0ft\xf3\xf0\x8d\x17fF\x82\xaaQv" } leaves: { key: "CPY=" value: "|\xb4\xc9\xfa\xc9Q\x82\x1e\x04\x93\xf8\x1clz~\x13n4\xcb\\ÃŦK]He\xf6j\x976\x19{" } leaves: { key: "CPc=" value: "\x98\xa5\x0bX΂}_\x9ar\xb3\xa8\x19\xe2\x13\x80\xb9\x9bR\xca\xcf\n\xdfnX\xae\x19N-\xd6*\x8f" } leaves: { key: "CPg=" value: "\x12\xc3@\xdd\xfa\x0f\xcbd\xf7\x89\x06\xbf\x0ew\x1eJ\x82\xab\xdb|\x12\xc1\xda-a\x02\xbe\x81E\x02\x87\xdc" } leaves: { key: "CPk=" value: "+\xc1\xb3\xd1n\xfe\xb8\x9d\x18\x12\x9e\x9f5\x1d\x81m\x1a\x1f\x06q\xae\x19j\x99\xa8[p\x99 \xdbx@" } leaves: { key: "CPo=" value: "S\xe7p\xf9\xb4\x13E/\xb8\xae\x07\x00\xf7y\xa9\xca\xf8m\x0b~) \xfdEk\xa7\x80\x1d\x96\x9e\xd6g" } leaves: { key: "CPs=" value: "B\x17,\xf1\xaa\x85\xd8P/?;,BoWXgg\x15\xc2Nh\x0c\x0fb\x1b*\x15Õ˛\xba\x81" } leaves: { key: "CPw=" value: " '\xf2Äē\x86\x7fL\xd4J\xa5+\x1e6\xfci\xb0\x07_\x86\x8f\x8cs\xa0\x1dS)\x86<\xb3\x07\xa8" } internal_nodes: { key: "A+A=" value: "\xf1\x8c\x1fj>\xe5\xc5ʕ\x12\x0e\xd1\x18\x88\xcc=\x7f\x06\xba\xeb\x99x\xe2G\x19Y\xb11\n3\x9d\x19" } internal_nodes: { key: "A0A=" value: "K\x1a\xbc\x93\xb8[20.Ō–`\x9d\x12\x1f\x90W\xa3Ȥ~\r>\t0\x02\xfd\x07\tL\x03y" } internal_nodes: { key: "A2A=" value: "\xde\xfc\x05\x94\tȨ\xc4\xe7\xbbLNzmS)\x96@ˤ\x80\x10\x8b\x8c\xd84_\xfe\x8c\x0cI\t" } internal_nodes: { key: "A4A=" value: "\x0e\xdb{<\x9d\xa2JxRV%\x1e\xa2\xaf\x04\x86\xa8\xb3\xc3\xf2\x8e2\xe0\x9a]$|\x87\x94hN\x05" } internal_nodes: { key: "A6A=" value: "\x80McU6=\xc0\xedIwU\x83\x81Q\xed*H\x04Ds\xa0\x16\xa1\x9c\x9c\xe9\xb0,_)\x82\x98" } internal_nodes: { key: "A8A=" value: "Æˇ\xb80\xa1\x95\x08\xae\xa5\x1cx$\xe8cJ\xe1tl\x9f\x12\xdd\xce\xc8D\xbf\x08@CדYD" } internal_nodes: { key: "AQA=" value: "\xb2\nE\x80\x9e\x88X\xb4\xe5\x14\xa7ic\x80\xd1b**.u\n\xfd \t\x90e\xae\xc4\xee\x89Q\xdc" } internal_nodes: { key: "AYA=" value: "/\xbds\xedUCv\xbehh2\"e\x90\xe1/\x90r\xaa\xce[\xe6\x1b\xdb\x17\xc9b^\x1b\xd0/(" } internal_nodes: { key: "AgA=" value: "A:\x85Z\x8aÄąqBÎē\t9\x02\x0fF\xe9\x91s\x84\x07\"\x08\x08ÅŽ\xfe\xea-\xf6\xe8R" } internal_nodes: { key: "AkA=" value: "W-\x9fEy\\\x93;ʆ\xdby6\xba\x8e\xd8vY6\x9a{LxUw9\xab\x8e\xc7qn\xa4" } internal_nodes: { key: "AoA=" value: "\xb1P\x17A\x02\xe2\xe40\xff\x16q\x9e\x1d\x91\xf6\xf0\x0e\x81\xf6\x9a\x81\xd0\r/\xac\x0e4]?3\x12r" } internal_nodes: { key: "AsA=" value: "\x88\xb8\xe0\x193\xcbÆŖ\xa6\xb9\xf6\x80\xbdYž\xfaŨ•;\xba\x1f7F\xd7\r\xb7\xbd\x12\xb4\xf8\x97" } internal_nodes: { key: "AwA=" value: "\xf9Χ\x1d\xf4?ZŲļ\x82\xfc\\g\xc9\xf2eyÄ­\x03\x0eI\rX\xc5\xdd\xd4\xf7\\K\xd0G" } internal_nodes: { key: "AyA=" value: "\xec{\"\x0c\"[U\x0b].R\x18\x83(\xa2\x1b~dU\x10\xa6\tyŲŖ\xa4-\xd4\x11\xb6\xb7\xbd" } internal_nodes: { key: "B+4=" value: "\xb3\xbbܸ\xfdE\xb0\xdf\xcbS'\xef:\xb6\xae|\x8cq$\xe1L\xc7\xcd\xde\xe4\xe0\xb2#\xcb\x02_\xa7" } internal_nodes: { key: "B+A=" value: "\xe4\xab\xe2ą]\xb5U\xdeSAB)\x0c~E}\rS\nÅš\x1b\x1cNX|$\xb0\xf3:n" } internal_nodes: { key: "B+I=" value: "\x0f\xf5\xcebĘŽ\xb1F\x02\xf7\xbd\x9c\x82d\xb2\xd3X\xf1\"ÖŽe\xd1`\x0f=h\xd2ŪŽz\x9c" } internal_nodes: { key: "B+Q=" value: "\xa87ߘ7]\xa2\x06\x8d\n\xd0\xc26\xab\x0c&\xbb\x15?_\r\xb0\xf8\x0b\\\x94N9\xfat*\xac" } internal_nodes: { key: "B+Y=" value: "A\x9a\xdb{\x97\xd7\x1a\xe6\x99\x03q\xb3O\xfdS\x16\xa4\x01\x08]\x8d^\xff\x885\x84\x05\xd3ÕĻŅŊ" } internal_nodes: { key: "B+g=" value: "\xee,R\x80l\xc6.\x88\xe1\x048EK\x897\x99Q\xaa0\xb0\x84Īą\x10\x9c\x05ߨnbZ\xc2" } internal_nodes: { key: "B+o=" value: "\xb4\xac\x01U\xadYP\xf8뤊\xb2|(\x05\x85\xb1\x0f\xa8v\x91\x7f\x06Rh\xbb\x03^\x7fr@\xd8" } internal_nodes: { key: "B+w=" value: "}\x89\xca\n\xeas\xd0>g\x14\xdd\x10\xa5\x12&\xab\xcf\xc6\xd0\xf4\x85vdI@\xc3\xe1\x9c\x7f\x1b%V" } internal_nodes: { key: "B/4=" value: "\xe7*\xb3/V3\"\x8c_\x9d\xb1C\xbf\xb5\xc2\x15\x86\xfa\xd1$\xf9\xff\xd1X\xdd\x0f\xf9'u\xf1j\x8a" } internal_nodes: { key: "B/A=" value: "vSy\x16l\xb3n\x0bq\x88\xf1!\xcf\x06\xb5\x944\xd2\x7f%X\xa1a\xa5\xcd" } internal_nodes: { key: "B/I=" value: "\x87#\xaa\xd0\x1d\x81\xf0\x10\xe4\xedzd\xf31\xc3K\xda\xca\xcbZH\xa9\xd0}\xcdh\xf1\x84\xf9\x1f\x12o" } internal_nodes: { key: "B/Q=" value: "\xd2a\x82\\\xaf\xc3\xf6贈_\xa8\xa9*\xcfx\xfd\x8eë•ŧx\xd7j\xfd\x94\x95\xfe\x88\xb2\xd6\xe6" } internal_nodes: { key: "B/Y=" value: "!\xbc\xfbm\xa4\xa5\x00|\xd1\x1e\xb9\x98\xd8ŨŦ\xe1`h\xeb{\xd82\x16L]\xc8\xcf\x14\x9e\xf1\xf4\x0e" } internal_nodes: { key: "B/g=" value: "V\xe7z\x9b\x8fLm\xa0\"q\xf2\x10\xf6\xb5ÆĄ\xc1\xc5\x1c\x83ډ\x87\x03\nPx\xd2s\x8e\x9cf" } internal_nodes: { key: "B/o=" value: "\xa0\xb3\x12\xc6\xcbRŅ’\xdd]\x8bi\xe1\xd1g\x8b\x8c06\xab\xa39\t\x06K_\xcd\xf18\x80Ķĸ" } internal_nodes: { key: "B/w=" value: "+\xd2B\x9f§\x0c\x1e\xd9h\x9di\x0e\xe6\x15\xc3\x08E\xa2\t\xe6\x9bH\xfa?'TS#O\x10\x01" } internal_nodes: { key: "B04=" value: "\xb6\xa0\xd7SŌ‹[\xfc\xe3\x7f\xf9V\x94\xa1t\xee\xa2|\x01\xe3\xa3T\x01\x7f\x92\xfbR\xe1\n\xd4\x1f\x8e" } internal_nodes: { key: "B0A=" value: "\xfe7\xb1\u009b\xb4\xf1\xa4\x13'\x136N3\xe3g\xf0ɟ/\xa1\x9c\xfb\xf9Ųŋ\xaeĶ•R*\xbd" } internal_nodes: { key: "B0I=" value: "\x9f\xfa\xb6\x87,<\xb8\xab\xab\xc4\xe91z sTj\xaf\x02\xf2}\x0cĖŋ\xfb\xde\xed\x86Îē\x96K" } internal_nodes: { key: "B0Q=" value: "\xc8x\x0c\x91w\xba$\x1b\xcc\xe3\x97\xd4\xe2R|R\x01%\xb3\x9emWV\x0f\x0e\x1d\xb4\x80J\x14{a" } internal_nodes: { key: "B0Y=" value: "\xc3\t\xbb\xeb\xe1\x16O\xa2\xa8\xd7\xf4\x99\x97(7IY\xa3\xf5\x0c\x0b\xa8`\x18\xff\xfa\x81\xbb\x80m9\x83" } internal_nodes: { key: "B0g=" value: "\xf6[3\x9e-\x08\x87!\xabd\xed\xbbܯ\xa7>\x9cp=g\xfe\xa8\x01\xc4E\xdaԍ\x99\x8d\x1d\xfa" } internal_nodes: { key: "B0o=" value: "\x8c\xfe;K\xe8iҰp\x11p\x9f\xd4\xf0\x82\xed\xe4#\xdd7\xc2\xfdH\xf8\xa4߇|\x99\x05O\x1a" } internal_nodes: { key: "B0w=" value: "S\x87\xaa,\xe9\x8d\xdcd8\xf4K֝\x87C\x17W\x9dK\xae\x9b\x01\x90\xba\x10k\x01\xa9\xc5Ŕ\xd9" } internal_nodes: { key: "B14=" value: "N\x12\xb2\x110\xa33'\xb4\xd6\xe0\x91<\xb9\xad\xf2\x1d_\xd1S\x8d\xed\xae\xc93o\xe0ɟ\xc49\xcd" } internal_nodes: { key: "B1A=" value: "\xdbrn\xb8\xfeaDO\xb2@\xfaO\x17\xf2qr\xe1rT\xb3\xbbČĒWd\xcb\xe7w\xeb\xb9a\x9f" } internal_nodes: { key: "B1I=" value: "D\xc5=$h\xb5\x7f\xb9\xebq\xa1\xf7\n\xf9\xb0\xd5\xec\x80T\x8ew\x0b\x82\xb6y\xa3\x1eå˜ŋ\xe2h" } internal_nodes: { key: "B1Q=" value: "\xe0u\xd5\x12\xa7\x05gßĒ\xc9\xd5B>@~tv\x03\xc0\xaa\xa3\x9aC\xf8mΊ\xff7Z\x1dE" } internal_nodes: { key: "B1Y=" value: "/\xe2L\xe3\xe59\xc9^\x02\x9cV\x9d\xc4Ywa\xb53\xbf\x16\xb7\xa4\\\x84\xdf\x12 \xf8\x99\x9b$\x11" } internal_nodes: { key: "B1g=" value: "\x19\xf9>\xb8\xa2\xbc\x01\x07\xda\x10\xbdB\x15\x87g\x1d\x90\x863YR\xfb[\x9a\x14\xcdY\x17U\xc33N" } internal_nodes: { key: "B1o=" value: "f\xbb\xbb\x8e\xed\xc0]`\xff7j/B\xce\xe1\xd6\x0c͜v\x96\xaaRp\xa9\x01Įēs\x10\x00f" } internal_nodes: { key: "B1w=" value: "1L\x01\xb8\x94\xe58\xf3\xb5n\xc4P\x9d\xbfiãĢ?\xba\xcd\r\xb9Wۑ=\xac\r\xc3\x01/" } internal_nodes: { key: "B24=" value: "~\xca#\x0e\x9cf\x15c\"\xddcL\x8a\xc9|M`\xbd4]\x88\xb6II\x03\xa4C\x91\xc2劚" } internal_nodes: { key: "B2A=" value: "Įˇ\xb4\xabGd\xe8\xb1\x05\x05J\x03\xc7#s\x82L/\xec\xec\\\x05\x17\xc8į“ŒIz\x9b\x8c\xed" } internal_nodes: { key: "B2I=" value: "\x07{\xc1\x10\x8a\xaaE\x9b\x9c\x15\x17d\x8e(\x0e`\xa2bi\x0f\x04\xed(u\x1c\xf1.\xf4\x7fTr\xa5" } internal_nodes: { key: "B2Q=" value: "8\xd8b|\x8d~\xd05\xaf\"\xa1n\xd0y.A=\xaaԕ \xb1\n\xfdN\xba,\xae_īĸ‘" } internal_nodes: { key: "B2Y=" value: "$3\x02\r\xcf\xf2\xacؕ\xf1\x06C\xf7\xdav\x9c\xc5D\r\x15;l\x13.&\xe8'\xa1\x1bd@c" } internal_nodes: { key: "B2g=" value: "ی\xe4p\x18\xe0\x8c\xef\xf4D\x13S\xc82m\xfb\x94\x9c\x0b\x94\xc1q1\xdbrH\x98\xbaz\x9bV\x0e" } internal_nodes: { key: "B2o=" value: "y\xe7\no[\xd6z;D\xaa\x03\x01O\xed\xd2\xf1^\xccy\xc3\x07pI\xe5\xb0\x17p\xd4\xdbk\xe7A" } internal_nodes: { key: "B2w=" value: "\xc8T\x8a\xbf\xcf.\xc8\xd1o\xfde\xd4'm\x85\x11\xe5Rp\x1dT[v\x9c\xac\xe3\n)N\x9b5\xfc" } internal_nodes: { key: "B34=" value: "\x8b$-\x85\x9c\xd6{\xc0d\x9b\x8c\x91\xb9\xf1\xf4\x18\x0b\x8e\xf5\x1f\x97\x8c\xf4ŅŦ\xc1Xv\xdaWnH" } internal_nodes: { key: "B3A=" value: "\x85\xab|\xa4\xb1\xc6\xddL;}\x88\xa1n{\xe1sa\x18\t\x7fG\xfa#\xd6\xccM\n\xc7BT" } internal_nodes: { key: "B44=" value: "ȇtG\x1f`ÉŋԐ\x02\x06\xb8\xc2\xf8q\xfam\xc1s2\x91\"\n\xb4\xca\xc8R\x7f\xf8\xd36" } internal_nodes: { key: "B4A=" value: "\xa8[\n;d\xaf\x87E%\xa8qÛŋ\xbb\xa0\xa3\xdayv\xb0\xf1\xee<\xa3z\xe8\xe1\xca\xc4ZJ\r" } internal_nodes: { key: "B4I=" value: "/Xy\x10Y0!\xd7\xe0\xb1\xfc\x8b\x7f\xae \xa1\xd1\xe8؈U\\\xc9\xfd\xe00)u\x0f\xef\xdd\xda" } internal_nodes: { key: "B4Q=" value: "1\xc3C\xa7v\xfc\xe3&.B\x81\xab\xad\xcd\xe5\xb6\xed_\\\xc5\xc7rA\xcb\x15ś\xafyG&\xf6" } internal_nodes: { key: "B4Y=" value: "\x8c\xd0R\xcb\x15\xcd͟q(&\xb3[d\x173V+(\x11R+\xa7\x12HE\xec\x18\x97\x0f\x93\xcf" } internal_nodes: { key: "B4g=" value: "\xf0N]\xd4\x18\xa2\x00OM\"\xab\xcd\x059:\x9cO/9*\xd7#\xf6.\xb5e\xd5X\x86\xdf?\xfa" } internal_nodes: { key: "B4o=" value: "M\xc3\rk\xebS\x1e\xf4\xa6\xfcu$:\xdb|\xbe\xc9\xdb\xeeH²\x86z\xc4\xdek\x00{ËĒ5" } internal_nodes: { key: "B4w=" value: ")G\xf0{y$u4WC\x8ck\xcb>\x10}\xc8\x0f\x98\xfcր\xa0\x9cu\xb69i\xfe<\xe2\x01" } internal_nodes: { key: "B54=" value: "'x\xd1bĐ­E\xc3\x06\xea\x13J\xff\xb2Zo\xb4{\xe8\x10D\xb2&jGx}@1[Z\xfe" } internal_nodes: { key: "B5A=" value: "\xe2\xaf%8g\x13I\xf7%\x87\xdf\xd8e\xa7\xddj\xe4n-\x00\x0co>\xc3\xf1\xbc|U2P\x1d\x1c" } internal_nodes: { key: "B5I=" value: "\xe3\xaf\x04iq\xab?\x817\x0e\xfc~\xa3L\xadN\xb2,\x17\x9e\x87\x1f\xf7\xfe\xfe\x14w\xd4y\xb4\xcd\x04" } internal_nodes: { key: "B5Q=" value: "u\xfc\x0cZ\x15\xc7VJl\x99\xde8\xadz\x9d\xe2\xdd\xe0\xf8\xac\x08\x99Ęŧ^\x0bۂ,\x13\x0b\x15" } internal_nodes: { key: "B5Y=" value: "\x9f$\xb6\x97\xe7\x95JX\x15\x89\xb4\x97\x95jU\x7f\x1d\x9c\xac\xf2\xf7CM\xd3]e\x89\xb2\xf3\xdf\xd7X" } internal_nodes: { key: "B5g=" value: ",\xf0\x0fx\x0c\xb2\xffbc5C/\x9b\xa4~\xfeHN蚌Z{\x8dFX\x91\xb1\x9f\x82jn" } internal_nodes: { key: "B5o=" value: "΍\xe5\x0b\x1fs\xee8\\\x16NÆ´\x14\xb1PIH\xd5EFR\\\x08\x8e\x81ŌŽ\xa8U\xe5\xe6" } internal_nodes: { key: "B5w=" value: "\xf0\xedV/\x9e0\xba\xa2-2\x07\xf4\x14`Tc\x9c\xda\x1f\x98p\x1bX\x90.m\xa24\xc4\xf4D?" } internal_nodes: { key: "B64=" value: "\xf6,w\x9cH\xccdl\x1d\xa3p\x8f\x16L\x06\x98\x9e3\xff[\xcfN\xb1\x16\xd4q\xd6b\xfdy\tb" } internal_nodes: { key: "B6A=" value: ",\xd7\xde\xc0\xee\r\x1c\xe5\xb7\xf8\x96\xef\xf5\xc8,\x97\r\xbe\xc8\xe8\xc0\x1a\xc2\xe8g-\xfaY\xb9\xd5X\xac" } internal_nodes: { key: "B6I=" value: "\xa8\x9b\x17\xf9\xc5p\xff\x88c\xd4@\xb2\xc2\xd2\xf3p\xe9\xbe%O\xfb\xd3n2\xc7R!\xf5\xc1D\x1bE" } internal_nodes: { key: "B6Q=" value: "\x92\xbe5Ũļ\x8a\xa7\x92=\xce\x14/\xcdk\x0b\x99W\xe0\xaf$@\xb7\xec˕Y\xc5\x01\xcb\xea<0" } internal_nodes: { key: "B6Y=" value: "C\x89f\x9dfU\x96\x97\xcc\xd33\xec4w\xea,G\xf2\xc8\\\x88\x0e\x07\xe6߄zK\x97/\xbc\xf9" } internal_nodes: { key: "B6g=" value: "\xc8u\x97\x98\x19\x9a\x1b\xdax\xc7>`\xc9\x17\xac\x87\xc36\x8e\x1b}#\x0b\x16J\xd0U\x10\x16\xac\x8dU" } internal_nodes: { key: "B6o=" value: "\x88^\xffy\x07}I\x1f4\x98eTH\xe7UC\xc0\x10:\xddԔ\xd3P\xad\xe2\x13hl\x0b\x14d" } internal_nodes: { key: "B6w=" value: "6\xc7\t2\xb0\xf5\x95\x19a\x81V\x9a\x85\xef\x0f){J\x98\x0c\xbc\xaa1|{\xcc\x16\xb6a\xd7?\xba" } internal_nodes: { key: "B74=" value: "}X\x9b뚾\xc0\xf8Y\xa4\x8a)\x96\xa0\xb8ÚĄF\x14{\x9eĮ¤\xd3|yV\x95\xa5E˛" } internal_nodes: { key: "B7A=" value: "\xfd\xe9V\xc8\"\xbby\x1a\x9a\x8f\x07a\xd5\xf62\x02e\t\xceY\x0e/\xedŲĸ\x8b\xaeŨˆ\xd5\xe6\xa5" } internal_nodes: { key: "B7I=" value: "\x9c\xa6c\x86'\xca(gXP9p\x97\xc4#a\x81\xae\rwZQ\xe2\x0bz\xef\xe8)\x1b\x8f\x7f(" } internal_nodes: { key: "B7Q=" value: "\x01\xb5E\x14\xa2B\x90dlğ<\x12\x96\x9e\xdf\xe9B\xea2\x7f\xce\xe7\xca\xc75ˀ\x0c\xa1Éž" } internal_nodes: { key: "B7Y=" value: "'\xa4^\x12\x1c\xd9\xc8\x02\x0c\x13%\x03\x12a鑲\xdbT\xffa\xb8\xe4~\x05\x8e\x00\xbd\xdc\x14\xf3\xdf" } internal_nodes: { key: "B7g=" value: "\xc9>qu\xaeÛ§\xa2/jd\xdc\"\x82\x1f\x7f\x00\xefD\xbe\xd5\xf4YI\xecbC7r|0\xb6" } internal_nodes: { key: "B7o=" value: "\x0c\x99`\xdeMF\x1fbO\xf28 \xaf\xb0\x8a\xdd˙xs\xb5\x92_Ķž\x81\x96` }\x80e" } internal_nodes: { key: "B7w=" value: "%\x9d\xb0\xb87\xbe\xc3\x1cU\xac\xf1\xc1\xbbo͆\xe8\xf2AT+\xec\x10\xa41\x19\xfbK\xe8\x83\xc1\xff" } internal_nodes: { key: "B84=" value: "\x01҉\xefpt\x88h\xb9\xa5\x19\xb5\xcc^n\\\x8b\x0fc{Q=\x06Ũ…hy]\xb1%Ū‹" } internal_nodes: { key: "B8A=" value: "\"?\xf0 \xef)(\x9a=\x14\x06g\x16|\x1e\x93WiÕĒQ\xbe9>́\x83b\xdcÕē\xde" } internal_nodes: { key: "B8I=" value: "B\x8aߍb\xbd\xfa\xc5?\x8c\xdai\x13\x1bp\xa7m=\xf7N\xc4L\xbd\x7f\x13\x87P4\xc9Z\xc0T" } internal_nodes: { key: "B8Q=" value: "$J\xa7\xc9\x03r$\xbf\xb6\x96,\xe4\xa7\rqlA\x01\x0cC\x8aÜž\x9c\x06T\xee\n\x8d\xccL\xb3" } internal_nodes: { key: "B8Y=" value: "\x13T\xed`\xbb\xb2y\xd6 \x82#\xf3a\xd5oy\x01\x0c\xc3S\x89\xa4\x8e\xb3Ī”\x8d\xdcT\x91\x9b\xc0" } internal_nodes: { key: "B8g=" value: "\x1e\x04B\x06\x17\xccM\x13\xf9\\K\xb1>\x80\xee\xadx\x0c\xb6\x10\xa0o\xf0\xd6\xec\x8ce\xfa\xf1Æ­\xe0" } internal_nodes: { key: "B8o=" value: ":]Z\xe3\xda!o\n\xf2\x8c\tK\\l\x1fp\x85ËŦ\x05X!\x9d\x1a\xf6ל\x88\xe3\xb5T$" } internal_nodes: { key: "B8w=" value: "\xbc\xfd\xd4\xcb#*^\xdd>\xab-Q\xaarjfjj\xc5W\xdc\x0c\xf1H\xf8\xc9UIB\xbfI\x02" } internal_nodes: { key: "B94=" value: "\xd2\xef\x02\xe8\xe4\xab\xfd\x9f\x1a\xb2\x89\xee+\xf3\x86\xdb\x1d\xe1ۖ\xbdg&\x08s\xaf\xd9\xef\x0f\xb4\x88\x03" } internal_nodes: { key: "B9A=" value: "%\"\xd9\x12t\xf6FD>;u\xa8\x91\xbbL\xd8Q\xd5\x1e\x89\x87R\xe5\x17Z\x92\x01\xf4\x06\x82\xdc\xef" } internal_nodes: { key: "B9I=" value: "\x89\xabz\x0f.0B\x99\xf7\xf8\x8b\x02%\x86P\xf4{(\xae\x91\x146X\x93\xae>_\xa3nߗ\xcf" } internal_nodes: { key: "B9Q=" value: "\x1fb\xfe$\x93\xcbd\xff\xe7QEX\xec\xdd\xe4\xcd|\x1fv\xd65\x99\xfdX\rY\x91Sd\x9c\u0082" } internal_nodes: { key: "B9Y=" value: "g\x90\x8f\xc1\xb0\x97A\xe1\\fk\x16Zi\xe2\xb6{Ō¤\xc0Ø˛\xa7\xb1\xc9\xd6\x1c\xa2`?\x10\x84" } internal_nodes: { key: "B9g=" value: "\x01\x81\xf7\xf1\x9c`%4\xe7d\xf4\x7f\xa6\xedØē\xd6(\x84\xd7;\nR\xc8z\x19(O\x85í\xe5" } internal_nodes: { key: "B9o=" value: "\xfe\xa9\x8aK\x9f\x88\xd6SF\x89\xe6*\x96\xce\x01\xd9\xc8\xe1\xb8e\x19×ģ\xeb\xe0S\xcc7\xd3UIc" } internal_nodes: { key: "B9w=" value: "a\xcc\xf9ظwV\xfb@X\x81\xbb\xf7q\x98\xe3F7\xad:<\xec\xe5$%x\x1f\xe7\x03\xd3\xc4\xd2" } internal_nodes: { key: "BAA=" value: "ÕĻ\xa5\xa5\xa6X\xc1R\xc3Ę´25\x184nU\xbaP\\~ĖĩX\xd1\x1d\x82#.\xaa\xe07" } internal_nodes: { key: "BBA=" value: "\xaf\xa6a/\xdc\r[\x16\x8a\xbb\xd3\tՖËŋ\x14\xed5\xaf]\xae\x92\xde\xcf\x02\x98\xaa\x06(\xd7;" } internal_nodes: { key: "BCA=" value: "\x94\xec@,YI\xf0'\xaab\x04\x05\xfbU\xc7U\rN\xa4\xee\xfd\xa9n\xc5={e^\xc27Ûŗ" } internal_nodes: { key: "BDA=" value: "-A\x08oq\x8bΉ9`\xd1\xeeߤEp\x13\x04\x9d@\xea\xb2\xca5\x98&\x96\xe8 \x96\x04\x97" } internal_nodes: { key: "BEA=" value: "\x0b#O\x04%w\x95\x8b\xa4\xf7\x86c\xe2\xf4؜\x10\x12\xaf\xfcXl\x83iT_\x9d\xebo\xd9L\xd2" } internal_nodes: { key: "BFA=" value: "\x98\x0e\x1e\xf2N(\xc6\x7fQ13\xdf\xda]\xad\xf4\xe6J#\xd6g\xf7\xd3(\xfb\xb8\xff\x9b\x8aH\x86M" } internal_nodes: { key: "BGA=" value: ";!uc\xbb#\x99\x8e7\xb8^\xb3\x92\x8di^E\x98>\xcd\xd4ȁ\x8a\x1a\xf3\xa3×ĩRJ\xf1" } internal_nodes: { key: "BHA=" value: "%\xb3\x8e\x87\x01x\xf6\r\xae\xe3\xf5\x8c\xe1]B\xcaM\xc3Jp\xa4zSw\xf3\x93\x1d\xbe\x1aNG\xb0" } internal_nodes: { key: "BIA=" value: "\x07\x95\xbc[\xab\x80L\x84\xa3\n\x1b\x81\x05\xab\xe4<\xf8sKiŪ \x14\x11\xd9m\xb5E\xda\xeae\x1d" } internal_nodes: { key: "BJA=" value: "'H\x0cd\x8aײM\x1c\x0e}\xfckܞ˪\x13\xf6ã‡Ŧ\x7f_\xedT\xeerܔ\x82\xa1" } internal_nodes: { key: "BKA=" value: "C\xa4\x91\xf75rJ\x831#\x87\xc0\xa7\x06\xb8&5\x15zŲĒ\xc1ËĻk\xba)\x0c\x13$2\t" } internal_nodes: { key: "BLA=" value: "ā/\x13:\x87d\x8c\x16\xcc>>\x93\xe1\x15\x1f\xc3\"P\x83Ú´Y|\xfd\xbb\xe7\xfcb\xf5'\x8c" } internal_nodes: { key: "BMA=" value: "X\xf1G\x1d\x88\x81<\xf9\x93\xaa/JF\x05\x97&\xb2\xde\xff_\xa7\xb2\xf0\x8e\x11\x8cE\xeb\x01=z\x1e" } internal_nodes: { key: "BNA=" value: "\xad\x0eŨž\xee\xea\r\xebD0\x8a\x0b\x1f\x1eŨŠ\x1a\xa4\xb5\xb1\x81\xd0xa\x9c\xf0\xfe\x99ex\x9d\x04" } internal_nodes: { key: "BOA=" value: "\x88AmN\xc4CWɎ\x0e\xc6\xfc\xfc\x16\xf2\xa3\xd7\xd0\xdfQ\xed\n\xc8\xf9\xad\x84\xe9E\xee'a\"" } internal_nodes: { key: "BPA=" value: "\xa4\x05|\x1bߐ\x13\x0cvm\x91~\xf6d\xcb\xf0.W\xabPS\xb9\x11\xc8H(\xd6\n\x01\x1c()" } internal_nodes: { key: "BQA=" value: "\xe37\"\x98\xe6Yd\xa9N\xed\xcbÚ˛\xc3\xc0)\x87s\xdd\x01\xff\x10^\x00m\x81\x98)<\x15\x8b\xb3" } internal_nodes: { key: "BQg=" value: "i\xd0B\xe1*\xe1H\x18\xb4\x8f\x18\xf7\xf2\x85Š\x9c\xdf\x1fp\x8d\xeb\xfa\xad\x12\xb7\xb7\xa4\xc2\xf9.\x15" } internal_nodes: { key: "BRA=" value: "\x01ZT\x18\xee\x1d\x86\xb8\xc7\xd2l~mk{\xfe\xbc\xe1\x11\x8c\x1a \xdf2\x00\xec_\xbf\xed\xe2\xd8\n" } internal_nodes: { key: "BRg=" value: "H\xd7,?O\xffE\x97\"*=\x7f\xf2'P\x1dA\xf1\x82\xa7\xf9\x8b\xdd@t|\xe9\x82-\x94*\xea" } internal_nodes: { key: "BSA=" value: "}\xfc\x87z\x8cZ\x84\x01\xc2xM\x81\xa3\xf3\x06\xca\x0e\x1d~\x84h\x99\xebL\xfe=\x98\xbe\xa2\xce\xf84" } internal_nodes: { key: "BSg=" value: "^\xbf\xf7\xdbu\xcf?sÛ¨T~\x9e]?9\x9b\xc0,\xa7\xaa\xcc\xe3\xf7\xa5p\xe1\xfeg\xfcp\x8b" } internal_nodes: { key: "BTA=" value: "S\xc2\r\x10\x1da5\xeeJjf\xc3'\xef}<;<\xac<\x8a\xa0B6Īļ\x1aoęŧˆ\xbd" } internal_nodes: { key: "BTg=" value: " F(&B\xab\x9d@t,\x82\xf0\xf7=\x99\xf9\nl\xb9\x83ڇ~\x03\xbb\xd4#\xb4\x16\xa2\xe2\x08" } internal_nodes: { key: "BUA=" value: "\x97\xe0\x86V\xd5]\xbb\xac\xd86\xdc\xc0\x12d&\x16\xc8ÃĄq84Np\xac\x05\xb3\x07\xae0\xb0\xb2" } internal_nodes: { key: "BUg=" value: "\x11\xbe\xb2-vuHL\x9b\x1a\xf1\xa2|\xcd\x0cI!\x08\xec\x84hW\r\xfc\x1f\x896_P\xceBf" } internal_nodes: { key: "BVA=" value: "\x16\x02\xd4\xec\xa9\xfd\t\xd7\x05H1\x1a\"N\x9dr^\xd50\xb3\xbe\x98\xae\x96!Q\xb8_\xa4\xb7\xb6\xfb" } internal_nodes: { key: "BVg=" value: "5W\x98P\xfe\x125'\xb9K0\xcf\x08\x17\x92\xdb\xfb\x04\xda^\xf6\x03K\x04B\xd9ifHZ\xb6\xfe" } internal_nodes: { key: "BWA=" value: "o\x1eؐ!\xe1\xcfį€Ģ\x8c\xe9E\x1c\x89\x1f\x80;\x05\xe0Z\xe0\x97N\x7f\xb8G,~I\x8eQ" } internal_nodes: { key: "BWg=" value: "\x83\x00\xc8\xef\xc5\xf8\xa9\x1f\x8d-\xb6>k\xf9\xde\x0e\x05\xa0\r[\xce71K\xedC\xa4\x87\x83\x9fZ\xd7" } internal_nodes: { key: "BXA=" value: "\xc7\x1eU0IV\x1aɧck1yo\x156\xc2\xf6\xd9\xd6CYR\xb3\xf0Y\xbd\x90\xb0\xed\xf53" } internal_nodes: { key: "BXg=" value: "\xd1BJcTwk\xb0\xe7y\x98\x14\xfc\xba)\xcf\xc9K\x1at\xf6D\xbf\xe7\x8d\xfa\xdd[\xf9x\xf1\xda" } internal_nodes: { key: "BYA=" value: "b\x86\xf3\x10\xf1\xc3A\xa7zX\x15\xbb\xfa\xcf\xe6t\xd2\x17Cw\xc1\xf9ÜĨ\xc26W\xf3Į—\xe0l" } internal_nodes: { key: "BYg=" value: "\x05B*\x01\xa0,+\xe6\xe1I\xe5\x1a\x1ck\xc7ak\x073\xc6V!\xaa\xd0\xcd\x10\x80\\\xd7,[\x9f" } internal_nodes: { key: "BZA=" value: "\x978\x8b\xcem\xbcA\x92\xa5',\xa3\xcd'@0\xc8`\x11\x81\xbf\x84$\x14͔\x9d\x89v\x18\xe8{" } internal_nodes: { key: "BZg=" value: "\x14\xc4ȕH\x9e\xcc\xfd\xd4Z`؛Ũ\xa5\xe9\x84\x1a\xf7\x16&\xee\xc3\x0cA\xf5g+bI\x11" } internal_nodes: { key: "BaA=" value: "^\xe3\x88QX\xc3(g\x9a\xd0\xc60;\x0c\x0eh@\x13ΏP\x8e\xf9\x86\xf3\x84\x1b\xb1!\xf0\xf1\x7f" } internal_nodes: { key: "Bag=" value: "Z-\xb2\x82)KM\xee\x99\xd6+\xe5\x94j\x00\x1aw\x19\xae\xa4\xc7DŅ­S}?8\xaa\x03\xbe<" } internal_nodes: { key: "BbA=" value: "/FY\xb4\xffnW\xac\x0f\x13|u,\xdd}\x9a\xe7\xcb\x01\xf8\xbc|\xd8\n\xcdR\xa4\xd5\xf7kl-" } internal_nodes: { key: "Bbg=" value: "o\xdf\xcb/ęŦ•m\xc5\xdf\xdf.\xbd\x17\xb4\xdf\x14\x15\x1d\x98bC\n\xa4]G\xd6sZE0\xe1" } internal_nodes: { key: "BcA=" value: "\x1bq.Z\x98N\xa4\xf7\xaa\xa9\xe2Ý<\xb2El>O\x17=\xc9\xc6\"\x8e9\xf9\x7fF\xa5\xd6\xf4" } internal_nodes: { key: "Bcg=" value: "\x1c\xeeq\xa0\x92-Ũ†\x9d\xb7\x95\xe9\xf8\xeb\xf1\"\x87Úģ\xa3܀\x88|\xea\xa4\xe1\xa6va\xe3D" } internal_nodes: { key: "BdA=" value: "\xc2\xe8\xd0\xecJ\xad\x1d\x88\x82\xd4`\xe2$\xf6\x9a\xd9ni9\xe3\x0c\x86p\xceb\xbc\x99\xbf\xeb~\xcb\xcd" } internal_nodes: { key: "Bdg=" value: "\xfc\x96\xc0c\xfek\x1e׊\xba\xa5\xaa5\xb34z\xc4\xd6\x07\xbc\x0e}kkK\xc8\xc0ÂžĪžl" } internal_nodes: { key: "BeA=" value: "\x01\x0f\x92\x91\x1f\x1bF\xe6\xfc\x81\x12\x95\x99HS4\xa0\xad\x1b\xa7\x91\x90r\x10z\xd26ey,\x95}" } internal_nodes: { key: "Beg=" value: "\x88\n\x1e9i\xcf|\x7f\xd9\xc0Qb\xf8\xe7\x1c\xfc\x0c\xf5\x94?&\xf0;\xae\x8aI\x0e\xfe\x1c\xde|\x96" } internal_nodes: { key: "BfA=" value: "\x8a\xb2\xeb:\xc0Ãˇ\x87bo\x0f\xb9PG\xf8i\x8e\xb9Üļ\xab\x87\x13^\x19\xea\x12\xa8\xdb}\xc90" } internal_nodes: { key: "Bfg=" value: "\xa3؄z\xd9<\x15\xd3L _\x10\x18l\xe9\xf6\x1a\x14g1L\xa1_\t'\xa1\xa7I\xdbv_\xdb" } internal_nodes: { key: "BgA=" value: "\xec\x86&v\xd5C*\x15\xb3\xec\t\x89_\xf1*\xc1\xf02Ō°\xbdk\x08\xb0x\xfc\xdfČĨ\xeaÜž" } internal_nodes: { key: "BgQ=" value: "g\r\x1b\xe9\x97}*\xaeN\xbf\xa8{!\x9aJsx\xd4\xc1j\xe2\x0e:\x88Z0e\xe1&\xdd\x16H" } internal_nodes: { key: "Bgg=" value: "b\xfd\xdeQ\xc1\xde8Ri\xbf\x13\xdf-l\xe0\xa0T\x7f\xb3\xfb!\xb5\xbabEVR=\xfc2\xe5\xeb" } internal_nodes: { key: "Bgw=" value: "S\x7fX\x84\t?ې\t{\xfd\xd1s|H~r\x00\x99X \xf0\xcanC\x99p+\x14X\x9f\x16" } internal_nodes: { key: "BhA=" value: "1\x01\x9d\x05N\x80΂\x99M\xe1\xda\x00y\x9c\xe7\xd9\x1eMN^\xae\xab\xb3\xdf\xec\xbfVv\xbf)\xb4" } internal_nodes: { key: "BhQ=" value: "\xa9\x06\xaf&\x96\x08\xeeJb*\x1f\xfd^\xbf\xeet\x8f\xb1G\x06\x7f_\xa5'\xb13M/\xde\xed\xf1\xb7" } internal_nodes: { key: "Bhg=" value: "2g&\xe2\xd2T\rY\x84\xb7\x94\xad_0\xbe\xb8\x84\xef\xef\xd0\xdb\xdcP\x18\x1b\xea\x01ZW\x94G\xa4" } internal_nodes: { key: "Bhw=" value: "JNv\xd8\x15\x8e\xe5/!\x06\x11D\xf4]\xe2[^~\xc68\xd7r]\x1cį…šDhR\r\x84" } internal_nodes: { key: "BiA=" value: "\xc6\xda>\x88\x98\xbc\x15b\xf0a\x98\xa3j\xab\xd0ČŠ\xe2\x9e\x18\x92\x94R\x8e\xd9\n\xbc)\xedOK6" } internal_nodes: { key: "BiQ=" value: "\x84\t\xb9\x15\x1d\xe2\xeb\xc9C\xc1\xc9\x05\xfe \x7fF\xb4M\xe5\xe1\xe2\x00cy\x10K\xfb\xcf@W:^" } internal_nodes: { key: "Big=" value: "\x8a\xe2>\xe5\xcbaÃŋ\x05X\xb8t\x94\xa7;\x12\xe76\x15i\x97o\xf3\xe2x\xa9\x9b\x01{\xa3\x12\xfd" } internal_nodes: { key: "Biw=" value: "\xe6\xca\xc3\xea\xd34\xfe\xb6w\xf9o\xe6\xbd\xf5\xff\x81l%kŲ¨U\x8eY~\x1a\x00\xb1\xe2>\xbd\x99" } internal_nodes: { key: "BjA=" value: "Лo\xafF\xc6UR\x87\xe1E\x8f[AZU{,\x07\xe8Vw\xf1\xe0\x93\x8a\xa3d\x9b\xffX\xce" } internal_nodes: { key: "BjQ=" value: "K\xb8\xa1K/\xaf\xad\x96\x8b\x9d;\xc9[\x9b\xad\x8bp\x9f\x9b.\xbaV\xba+\xa3\xdcW\x9d?^\x81\x07" } internal_nodes: { key: "Bjg=" value: "\x1b\x16GvG\xc3\x0b͚\x0eh\xa4\x92UL\xff֞lB\xae\x87\x04\xfb\x9e\x11|\xcdR\xf5R\x13" } internal_nodes: { key: "Bjw=" value: "\xba\xa3uËĸ\xff2\xddc'\xfaHD\x14\"w\x02\x0eK\xa3\xb9\xb2\x00\xa9}t\xb2Xzw\x9b\x9b" } internal_nodes: { key: "BkA=" value: "\xb9\xa5\x7f\x89O*\x0f\xa18\x1f\xf8E\xea\xab\x16\xd4Čŗ\x1d~\t\xfc\r\xe4)\xea\x0402" } internal_nodes: { key: "BkQ=" value: "S\xf3)\xe5oX\xd8J\xe1k\xc7gc\xab\xaa\xcae\xc6\x1f\x11RU\xdcz\xfe\xa7\xd1\x19-\xe1\xea;" } internal_nodes: { key: "Bkg=" value: "\xdd/Tײ\x02\xe5\x12\xe2\x1e\x18RG\xf5\xbaH\x93\t\x95U~\xabZ\n?N\x0fZ1\x8e\xb8\x88" } internal_nodes: { key: "Bkw=" value: "\x9cs\xef\x89N\x94/V\x1e(!5wc%\x00v\x9e\x1f$\xd1P/\x8a`=G\xad\xe4L@\xdf" } internal_nodes: { key: "BlA=" value: "\x08y\x88w\xad\xa35U\xf3s\x00 *&M\x0c\xb1\xed\xf0\x08\xa3\xad{m\x9fC(\xbe\xa4TVG" } internal_nodes: { key: "BlQ=" value: "\xfa)\x06\xdco\xf0b\x84x\x0bw\x9eD\xeeK0I\xae\x87C_x\x96\x16\x9d\xfcM\xfaď\xb5\xce" } internal_nodes: { key: "Blg=" value: "\x9a\xe39\xb1\xe5\x802q\x9d;^\xb6GP\x82NH@\xc6Z\x02G+\x80\xa5Z\xbe\xc5\xddn:\xc8" } internal_nodes: { key: "Blw=" value: "WD\xaf_w\x8b\x0e\x86\xa1FR\x9f\x7f\xd5\x19ČŖ\xf5b\x13\xa1\xf4\x1f\x82\xe4\xf9<\xc5\xcbėŊ‘" } internal_nodes: { key: "BmA=" value: "\x15\xd6l\x92\xd7$*q\x9f\xbf\xd6\x7f\x88\xb0\xc2`\x1d\xc2y\xc3\xfe\x82bÔĨ\xc4\xe9\r}i\x97!" } internal_nodes: { key: "BmQ=" value: "\x12[G\x15\xdf\xf1\xf8y\xbe]\x86\x88\xd1v\t&\x87đŦĩĄ\xbcD\x9aB\xf4\xaeԚ\xb7<\xaa" } internal_nodes: { key: "Bmg=" value: "\xea\x95\x17\xb1:\xe8Q\x9e\xbc\xe2~3\x7f$\x90\x15\x9e)\xd9ŪĒ\xd7\x1a5\x9evŲŽ\r\x0b\xa6\xfa" } internal_nodes: { key: "Bmw=" value: "|&\xa6KΊÛĩ)\xc9\x15\x95\xffÆąyâĩ™\x8f_O\xb4\x89\xc1\xd9\x15\"\xed\"f\x07" } internal_nodes: { key: "BnA=" value: "\xad\xef\xaf\xf2c\xe9Ɋ\xef\xc4,\xb2s^\xb4\xf5\xb1\xe9=S\xea\xfb\x13\xd0\x1a\x9cK\x8bo\xd3N\xf6" } internal_nodes: { key: "BnQ=" value: "O\xdf?\xf4\xa8W\xf1c\xc3\x1f\xd9[f\xfa\xf7O\x1f\xd4y\x03$\x83\xf8\x16 Į’\xb4\x17W#\x9f" } internal_nodes: { key: "Bng=" value: "\xb8\xf0=9\x0f\x8cq5_^\x05\x0b\x1d\xab1$F\x8a@_y~\xf5\x81\xc1\xb7Q\x83Ԙ\x15\xc3" } internal_nodes: { key: "Bnw=" value: "j\x83\x93p߅\xc5ɜ0t\xca\x10\\|\\8\xda\x11s\xad?\xd8'\xd7KE\x8aa\xed_(" } internal_nodes: { key: "BoA=" value: "4\xf3ÄŦ\xa4\xdb\x1f\xfe!u\xf7\xff\xfd\x8ac\xf7\xc0\xb5\xfdb\x08\xc0 \x8c\xba\xdaC\xdfr\x1b:\x14" } internal_nodes: { key: "BoQ=" value: "\x8f\xcau\x1al\x9e\xaa\xba\x80\x94~\x93!\xf3\x03\xb0BZ\x9a\x90Íŋ\xf2\xf1`!\xd6x\xf1\x03\xec)" } internal_nodes: { key: "Bog=" value: "wI\xbeNl\xb5z\x04\xe50\x99\\\xe7\x11\xf0]\xe8\xc0p*p\xbf떷\x91MЈ\xff;\xbd" } internal_nodes: { key: "Bow=" value: "\xe6\x7f0n\x1cr\x9c}5\x07*\xa8Q1;\xe5ڕ\xff?\xe2\x88\xd9\\\x81\xbd!\x88\x0f\x1b0S" } internal_nodes: { key: "BpA=" value: "\xde\x0c\x0ft-+\t2ę\xd6\x03\xd9u\x051\xd4JH\xb22\xca\x19\xbdo\xdbHÕž\x1fD\xe8" } internal_nodes: { key: "BpQ=" value: "x\xf9;\xa9.\xf0\xb0ZS\xd7L\x88\x08\xa1\xd6\x1a\x0f\x94\xb9\xc5j\x95\n\xc0\xc1l\xc1\xe8\x14\x1d\x8d\x8d" } internal_nodes: { key: "Bpg=" value: "\x87\x9cbesc\xee\x93V}\xd09\xcd\xec\xfd\x99Q\xe0y& \xb5\xc8\x0e\x9e\xe7\xa4\xccml\xe7\xe7" } internal_nodes: { key: "Bpw=" value: "\\-\x8e\xe24\xcb\xdf\xf35AĖĒ\xf2\xb6\xf5p\xb84=\xd3\xec\xea\xea\xfd\x9a\x086\xaa\xc4U\xe6\x0b" } internal_nodes: { key: "BqA=" value: "+\xc2]i\x9bEΛa\x07\x9e\xceȆk\xd9\x16\xa1\xb3i\xcf\xdd\xffr^&\xd3l\xe8r2\n" } internal_nodes: { key: "BqQ=" value: "\xb2;\xd1#$X\xec{-\x02\xa3a\xf9`~\x1f\x19ox\x12rp\x1a\xca\xe6tC:(\xf3\x10\x02" } internal_nodes: { key: "Bqg=" value: "\xcb\xc3F\xa0'wI\x8dx\xf5w\xbf\xd48\xda\xe8t\x03\x15\x83~\xee&`\xb9@\x1c \x0b[\xea\x81" } internal_nodes: { key: "Bqw=" value: "\xb2\x10\xb5\x01\xf4\xd2{\xb4P\x05[\xcag\x06UBËĻ/~ZB7l\xecB\xb3\xbe\xb2\x90\xf9:" } internal_nodes: { key: "BrA=" value: "\xe8'J\xab\xe9\xe5\xc4(\x8b\x99\x1fo2c\xc1\x0f\xf8\xea\xed\xe6\x95#\x1e\xe5\xc2#\\\x8d\x9c\x91\x0e\x1d" } internal_nodes: { key: "BrQ=" value: "!Ì \xc7\xfbFC~{8n\xeaF\xb9X\xa8j\xd6Lb.}\x86\xd0\xd0.\x1f:-\xb5Y" } internal_nodes: { key: "Brg=" value: "\x99\xb1%aČĄj\x8f\x97H\x9f\x1e/\xa0Y\xf22\xcc\xc67\x16\x07\x1c\x85\xfc\xfd\xa6[@@*+" } internal_nodes: { key: "Brw=" value: "\xf2\xc4]q\x00\xbc\x1dV\x1a\x80\xf8xF\x05\xebx\xfe\xf3q\xb4\x12(\x1e\x82S- ıl" } internal_nodes: { key: "BsA=" value: "\x80\xea\x983a\x00\xdcŨ†\xe8ʙ\x9e\x8b\xd5Z\xdb\x15\xa5\x12\x03y\xba\xe1f\xb1\xa8he\xefh\xa1" } internal_nodes: { key: "BsQ=" value: "X\xa5h\x1d\x97\xa9?\xfd\x8aYl!\x93\xe2s7\xd7\x18da\xf9\x01\xe3\xb1\x14\xd0@\xec\x7fEu9" } internal_nodes: { key: "Bsg=" value: "\xa6\xc3\xe9\x88\x1a8g\"\xd7\x14➊l\\\xf4\xbb\xe4\x0f\xd1\xdd\xea\xdc)\xba\xc3h\x10\x88\xc5\x05R" } internal_nodes: { key: "Bsw=" value: "*\xc1\xb2p\xc9s\xa3\x1d\xb9e\xdf\xf2xC\xed7uS\x90V\x05X\xc0\x0fo\x02\xb8\xcb|S\xc27" } internal_nodes: { key: "BtA=" value: "\xf9v\xa0>\xb7\x9e\x82\xb1\xa9\xcbI\x8bÖĨ!\x8e\x9fKY\x8d\x89\xb7\xb1S\x0c\n\x96\x7fZ\x1a\x12\xc1" } internal_nodes: { key: "BtQ=" value: "\x9d\x8b\xb6y\xd6Ad\x0cQ\xf6x\x840\xe1\xcb\xf8m\x98\xa7G\xa7\xa95f\x0c\xcd\xef+\x87\x89\"\x8a" } internal_nodes: { key: "Btg=" value: "@\x81\xf4o\xdb\x00Ũ™#\x01\x145!\xb0M\xd0\xe9\xf1\x126\xe7N܍\x94\x17\xba\xf5\x98\x13\xcaP" } internal_nodes: { key: "Btw=" value: "\xaf\x80\x0cEr\xf6\xe2\xb1Ŋ\xcd;!!vaP\x02\xec@L\xdd\xf8k8!x\x92\nf{\xa0" } internal_nodes: { key: "BuA=" value: "\xef,\xf7!|&\x04e\xc3\xd7iĄ\xbbl\xac\x99\xe9\x8d\xff(\x93K9\"\xa5-!\xe9\xf33>" } internal_nodes: { key: "BuQ=" value: "|R\x0c\x01@C\xbcG\xbbspr퀰ˇ't\xdd\xf0*\x95U\x08\xfa\xb1\xf7\x9c\xf2\x197\xbb" } internal_nodes: { key: "Bug=" value: "\xc7\xc2\x0c\xe4\xd4-cP\x9f\xf6\x1eO\xe7k\x11{\xadf\xa1\xf4R\x9cf\xec \xee\x89\xc1x\x9a\xf2" } internal_nodes: { key: "Buw=" value: "\x063s\xa0\xe8(\x198a)#\"\xd3V\x96I\xe1\x14\xac\x8ch^>?\x85\xfd\x8b\x84$\xa9\xd1z" } internal_nodes: { key: "BvA=" value: "dn\x8b\x18\xf8\x1a\xbebQ\x89\x9d֞ԋ\x86\xaf\xd7\x12\x83o\xd68I\xc7\xf8\x0c1\xf7\xf2\x92\xfe" } internal_nodes: { key: "BvQ=" value: "~\x0c\xf3\xee\xf5\xad\n\x80D\xf5\xa9I\xaf\xe98\xd6\xfc\x19\xbcX\xb0\xc36\xc9,b(F\xcf\x19\xb6\xb0" } internal_nodes: { key: "Bvg=" value: "\x04\x83*\x08\x0bZ3\xd3\xff\x1aN\x9a\xa7U\xb8(B\x81}\u0085\xe1\xb1\x15n\xa8A\nL\x04\xe4\x0b" } internal_nodes: { key: "Bvw=" value: "\x02T\"\xfdv\x98\x99\xf3\xb2p\x9d\xf1y\xae:U}\x81\xf99!\xce0\xc6\xf8\xa0\xbd\x98?t\x96\xa4" } internal_nodes: { key: "Bw4=" value: "\x99\xbc\xce}\xcd\xf2\xfe\xb9]\xecY:P7\x05~l\xfc\xab\x066 G\xa1y\x9c\x9a\xe4\xe1\r\x89\xb9" } internal_nodes: { key: "BwA=" value: "L\xce+\\zŪŋ\x9a\xedZ\\sba\\\x80;\xd5jRЇ\x1eL\xee\nF\xc4v\xe9\x9f\xf6" } internal_nodes: { key: "BwI=" value: "|2\x9eQ\xe3\xc9\xeb\xae^X\xb2\xb4\xd4\xfb\x07\x96\xd0\x13\xe9]\x01v}x\xa5t\xbbte\xaf\xbe\xca" } internal_nodes: { key: "BwQ=" value: "\xd8\xf7\xbc\x0e\xc8EG\x98*\xc6\x17\xde\x03\xa7\x97\nGk\xbd\x0f]\xf1\xad\xfa\x00\x8cŨ†\xd4H'\xd3" } internal_nodes: { key: "BwY=" value: "\xa1\xf7}\xf9\x16A\x9a\xd1l\x9b\xeb\x0e\xffܜLęŋē\xceb\xd8u\xbf\x95\x8b\xceM\x81\xdf\xf6a" } internal_nodes: { key: "Bwg=" value: "\xbf\xf6\"l-\x9d\xc3\x000\x03G\xd2\xe0\xf5M\xd5(\xdb\xc0\xed\xa4f^\x19W\xbd\xd0\xc4\xd8r\x83\xbf" } internal_nodes: { key: "Bwo=" value: "\x0e\x90\xbaÔ¯\xf6\x1e\xf9n\xb2\xb0\x13ZB\x88A\x1b|\x80\x0c\x1b\xb9n\xd3A\xcd\x14ΰ\xaf \x89" } internal_nodes: { key: "Bww=" value: "Ge\xea0\xb7\x9c\x00\xef\xa0Z\xb9\xb6^q\x11wyFir\x1blgHV,\xa3\xd7\xedg\xf6\x01" } internal_nodes: { key: "Bx4=" value: "\xf1\x93\xb8\xc7\x7f\x80C)\xae\xe6\x84\xfe\xb9\x15{\x0b\xdf\xee\x9cF%\x1cH\xae\xba\x9a\xa6g\xc4ZF0" } internal_nodes: { key: "BxA=" value: ":\xed\xc9\x1d\xefv\x1a\xc7\x06dĮšŌĢ\x92\xf6\xf5\x06\x98J_\xb0\x82HzF\xc2\x00\x0bX\x95C" } internal_nodes: { key: "BxI=" value: "\x98@x\x8f\xac\x89C\xa6\xc5\xcb\ri\xe7s\xe2_\x9a\x15bb\xedo\xfey\x9e\x7fj\x0e\xa8a\xae\xb3" } internal_nodes: { key: "BxQ=" value: "\xa8\xe5\x9bOGŎ\xb2c꒛--\x0b\x9e\x90\xf4u\x0bښΩxs\xfd\x82\x92\xaaĖ‹" } internal_nodes: { key: "BxY=" value: "\x8du\x02jV\x07\xd5Ņļ\r\xc5d\x8a\xd2\"-\x83\x93\xf75\xdbI\xe8SD\xb4u \x84\x16I\xfc" } internal_nodes: { key: "Bxg=" value: "\xbe}\x84\xad\xf62\xf1\xe1\x93Đ\xe9\xfc\xfeQ\xd5#\x9c\x9e\"i~U\xae\xe2\xafJ\xd1C\x9bP\xfa" } internal_nodes: { key: "Bxo=" value: "\x99\x16fg\x10'\xa1\x91\x83&S\xf9+a\x90\xe4\xbf\xdc\x02\xd67w\x04\xe4^Ě\x19\x89\x96Л" } internal_nodes: { key: "Bxw=" value: ";\x9f\xf7ĶŽ\x0c˚ÚĢ\xa2\x1f\x8f}h\xd2\xfb\xfesh\x83U\x97\xfe\x86\x8c\xb1\xee^\xd0\xee\n" } internal_nodes: { key: "By4=" value: "\x8b\x17\xe1O\x0fPI\xda4.\"[{\x1f\xc8\x0bĖŽ\x90\x97\x8du\xd7\xd90,\xde\xd8~\x83\xba\x84" } internal_nodes: { key: "ByA=" value: ")8\x8f\x8b2DK\xc9j6\xb1\xec\xb5H[\x9c\x87)\xf6z~1y\xc3\xc6K" } internal_nodes: { key: "Byw=" value: "}&\x17\x18\x16\x9f\x0f0S\xe2\xd7jyo\x86\x1a\x91\xa7\xf3\xa6\xbaw\x98xPT];\t\xbf\x03\xdd" } internal_nodes: { key: "Bz4=" value: "\x0c\xbb\xce\x19\xc5\xd4q\xfc\x03\x04\xf8\xc9gJ0\x17g\xfb\xda^\x01,\xbd~\n7I\x8d\xd61\xbc\x17" } internal_nodes: { key: "BzA=" value: "\xc3'Œ\xa1\xed\x95DV\xcf4\xa88\xbb\x151\x00\xb82\xa6\x0b\x87\xabd\x9e\xb61\xb6\x10\x96F\x90" } internal_nodes: { key: "BzI=" value: "\r\xad×\xa5\xb7\xc9L\t\xbd\xd9m\x0f\xce؅\xb0U䭐\x18l\xd1\xd8\xf1a\xb6\x15\xee\xd1\xd3" } internal_nodes: { key: "BzQ=" value: "\xd9_\xfc\xbch-\xab1\x95\xd8Øą\x01\xbd\xfe1T+;\xa0\x11rb\xb4\xca\x01|\x0e\xb3\x06l\xda" } internal_nodes: { key: "BzY=" value: "\xdd\\)BsD\xf3}\xcapƟ0\xfd\xbf\xe3\xadp9\xc8&\x7f\x98\xe6)\xf2kK\xe9ș\xba" } internal_nodes: { key: "Bzg=" value: "\xdc\x00;\xb5\x9b\xe8\x14X}\x84\xf7\xfc8;oFrE\x9fT\x00ČĄáŧƒŨ€\xb35ÚĢ" } internal_nodes: { key: "Bzo=" value: "=\x86{\x07\xeb\x05\xbee\x19[\\i\xeeT\x99\xe2c\x86\x00\x93\xe1\xfdqU\x84\x8a\x90\x00\x19\xe4\x10\xbc" } internal_nodes: { key: "Bzw=" value: "\xd5u\xac\xe5b\xa0&\xaa\x92\xd9\xf6~FO\x9c\xa2>c\x80\xe3>rI\x86\xe5\x91'l\xaf" } internal_node_count: 254 trillian-1.6.1/integration/format/testdata/dump_tree_output_871000066400000000000000000006575421466362047600247500ustar00rootroot00000000000000prefix: "\x00\x00\x00\x00\x00\x00" depth: 8 leaves: { key: "CAA=" value: "\x8auO\x06ԁ..\x8e\xee\xef\xa4\x06\x17vĖŦ\x0c\x80j\xdd\x029\x98h`\x1el\x85y\xb3\xc2" } leaves: { key: "CAE=" value: "s\x82l\xc7;\xa0\x1f_\x08\xcc\xcc\x0e\x1a\xfbD\x84\xadŅŖS{Uh*V\xbc-\xe7\xb7i~\x81" } leaves: { key: "CAI=" value: "\xbb\x91zJ\xbb\xbbR\xc7\xe0>\xde\x1e\xe5\x9fC\x11\xe5J\x85\x0b\x00\x99\xd1^\xa2\xd8RB?;S5" } internal_nodes: { key: "BwA=" value: "\xc6\xee@\x91R\xb3TI\x03\xf2\x86B-}c:\x9aÛĸ\xef\x0f/\xdb\xf1V\x8e-\r\xcf\x17?\x92" } internal_node_count: 1 prefix: "\x00\x00\x00\x00\x00\x00\x00" depth: 8 leaves: { key: "CA0=" value: "\x9e0}ë§ļ9q\xfd\xc6\xfeI\xcd\u0097\xab\xb0\xe0y.\xc6\x0c\xc5SpΊf+Eh\xce" } leaves: { key: "CA4=" value: "\xe3\xe5kq\xcc\xec\x9a\xe7Ņĩ>\x0e\x16Ũ\xc6j\x9fS\xe7\xb4\x15S\xeeT=\xc2U\x0c\x99\xed\xd1" } leaves: { key: "CA8=" value: "\xa7\x97\xc0\x16R\x94R^s\xf7F[\xa7\xbba\xb1\x9f\xbf\xa3bj0\xe84\xcdR\xd5\x08\xc6|^\x89" } leaves: { key: "CAA=" value: "\xe5\x918\x856\x85\xee]?.\xd6\xae{\x1bw\x82\xe1" } leaves: { key: "CAQ=" value: "\x8c\xa6\xb1\xbf놛\x94\xb0Ūļ\xc9\xcdp12Ò\xb4\x8f:Û˛3ejb\xaa]\x96\xb3\x18" } leaves: { key: "CAU=" value: "S\xc1\xd1n\x89\r1)\x86\xd9\xc3-\x10\x9fm@\x00\xf4\xa2F\x81\x0e\xe3\x95\xff\xcc\xf4{Y\xc8\"$" } leaves: { key: "CAY=" value: "\x91Čš.\nx/\xe3\xca-\x0c??;\xb1r\x8fViB\x9d\x9f\xa2\xc6\xd5\"\x8bŞ\xe41\x9b" } leaves: { key: "CAc=" value: "\r\xd76!L\x05o'^i\xff\xe6\xa1\xdaD\xd1q\xafƎ\xae\xdb\xeeIߕsR>\x05YI" } leaves: { key: "CAg=" value: "DH\x1au\xf5Éĩ\x0ep\x05\x8c\xd0\xe1\x95\xc9\xeeÚ˛\xff\xf7Z \x17\x7f\xfa\x9d\xc6(\x81\x9f\xe97" } leaves: { key: "CAk=" value: "\xc8\x07^\x7fL7\xe6\x1boQ\x85x\x7f\x00\xb8\xf1\xfes\t\xd9ę¸ĢL\xc5en$\xac-\x1b\xbf" } leaves: { key: "CAo=" value: "Ђa\x90\\\x1f\xa1h\xf5\x80\xcd\xe1i\xdb\xc68\xcfŪ\xfaq\x10O\xb8\xaaÅļ\xa7\x06h\xf3_" } leaves: { key: "CAs=" value: "\xd05\x1a\xe3FT\x03\x80~\xb6äƒŋ\x9fn#/u\xa7\xb5\xf7\xfa3HÔš\x97\x88\xbdJ\xf6_" } leaves: { key: "CAw=" value: "7V\xcf\xf2\xd8\x0c\x05\xe7\xf0 _BS\xecÕ›Đ‹U\x8eR\xf1\xc0\x0fK\xfe\x0fR\xf4\x87x\xe5" } leaves: { key: "CB0=" value: "\x81\xf5\xb0\x96\xe7;\xf6\x97\xdb\xd6\x7fm\xfb\x05\x80\xd6\xff-\xe5\xc5PßĻ\x82\x07`\xfca\xf77\xf0>" } leaves: { key: "CB4=" value: "\x92KAS\xbb~\x84\x17\xe2+\xdf\x03R\xbc\xbc\x89=\x8d\x85\x1e\x7f\x82\xe1\xe7\x1akh\x8aY\xd6\xdf\x02" } leaves: { key: "CB8=" value: "\x1d\xe6f\xde\xef\xef\xef\xc5C/)\xac2\x90\xe2\x08\xf1\x13\xbc\x0f\x93\x00M\x88/}\x1b\xa4\xad\x85\xac\x7f" } leaves: { key: "CBA=" value: "\xff\xa8nT\xc5\x1f\xabr\xbd\xfe\xa8'\x19\xe5k&\x12\xd3\xe9\xf8փ\xb5\xd2sCe#\x88G.K" } leaves: { key: "CBE=" value: "j\xed\xadGL\xa9{^\x13g\xf0B\x0b\xaf\xc1\xebsG\xce\x1c\xd5H\x11&\x9e3O\x81\xa4ܗ\xfd" } leaves: { key: "CBI=" value: "\x04CZ\xa9b\x01Į›\xc44F\xf6\xc3\x17\x02\x8e\xbe\xf1\xa2,@!L\xe6\xf3\xf3\xa69\xaf\"\xd6K" } leaves: { key: "CBM=" value: "z\xd4\x00\x15\xb1\x1a \x83nC\xfe\xdaDwq%\x95\xb0>h(\xfetn&\x95t\xae\x19O\xce\x0c" } leaves: { key: "CBQ=" value: "\x08\x85\xe85\xa8I\xe3\xcb\xfb\xa7\x08\xf0\x165G\xd3\xef`\xb8k\x14\xc6\x1c\xb2\xa8\xfd\xf0_y\xa2p\xd2" } leaves: { key: "CBU=" value: "kŨ¯\xfd\x92r3\xfe\xdewL\xd4Z4-\xd4Į”\xafx\x06\xef\xd5\xf4\x17\xed\x9eHP\x08\xc6\x1e" } leaves: { key: "CBY=" value: "~Z\xb8\xc0~K\x04\x80\x1b\xba1\x18\r\xcd\xc4g\xd4\xeeskj\xc2]|{ \x82ZK\xc8" } leaves: { key: "CBg=" value: "\x17\xc2Q\x93P\xe5\xbeE\xbb\x88\x1cF\xd3g\x10\x8a\xa7\x07o\x1c\x80\xfb\x86q\x16\xe4\xd1\xcb\x1e\xab*\x7f" } leaves: { key: "CBk=" value: "\xf9\xb1\x11Č´1\xa4\xa0\xeaz\xc6;\xa2\xc1p^\xd9\xf7\x0c\xf8\x95\x91\xd1\xed\xec\x84\xf5\xb4\xbd\x8c\xaa\xe9" } leaves: { key: "CBo=" value: "\xaf.\x9ca~~\xd2UJ\xd55\xf4 d\xd9w\xd3\xf3\x82\xe76\x88\xe0\xe8KÜŊ\xef\xf3i'\xbb" } leaves: { key: "CBs=" value: "Yf\x0c\xd2\x0cb\x0bL\xb9]\x02\x15\x0eL\xb4\xbd4\xb1\xea\xa7\xe7{`k\u009d7\x1d\xc9\x12\x1c&" } leaves: { key: "CBw=" value: "\xb8\xfd\x1cr\x10\xd1l.E\xb8\xa7\xef\xa1\xfc\x8d\xf4*Õą;,L\xbf\x16\xe8['\xee\xe5DY\xfa" } leaves: { key: "CC0=" value: "\xa9\xb8\t\x11|\xf2\xf2\xfbo׋\xe9\xfb\xdf\x11=\x8bey_1M\x19\xab\x8d\xa6\x12:\xd6\"ڄ" } leaves: { key: "CC4=" value: "\xb3\x01/\x86\xff\xf5\xac\xa4\xdc\x14\xfb]a\xe4\xe8\xf6J\xf1\xc2R\x1dn\x1e\xe0\x9e\x120I\xb4\xdep\x97" } leaves: { key: "CC8=" value: "\xe1\x80\x02\xf80ᎈ*n\x1f\x8c\x0fSLŃ\x8b9\xfe\xbc\xeb\x01m\xf2ؑ\xa8/\x16az" } leaves: { key: "CCA=" value: "\x15i\xb6[~=\x81\xa7\x180\xbb\x7fu\x08\xad\x95\x86\xa0\x9e\xffDTeIY\x96y\xcaO\x9fŨĒ" } leaves: { key: "CCE=" value: "\x81Uԉ$L\x8f\xeb\xd45{\xe5#\x8f!\xba\xb1A\x10\x96\x000Įž\\t\xb1\x07(\x12\xf4e" } leaves: { key: "CCI=" value: "\xa7\x92\xd7<0\xa9e\xe0\xa9\xebe\x82\x82ˎ\x0b#\x0f\x83c\xad\xa8\x12\x92\xa7\xb2\xe5>[\xe6<\xab" } leaves: { key: "CCM=" value: "\x8e\xb1y\x01\x91ʉ\x81⤗&>.\xfe\xa7K\xee9\xa2\xef\x18\xa6\x82\x92Wk\x80f\xbe\x8f\xc3" } leaves: { key: "CCQ=" value: "\xf7\xa8\x93\xf6B\xad\xd1\xf8\x7fw\x04X\x1f\xd8'\x1e-\xfdF@v\xb9@\x03p\x98AV\xec\x931\xb4" } leaves: { key: "CCU=" value: "\xb1\x7fC\x19\x159\x1b\xe3\x1f!\x99ذ\xf65\xb8\x1f4\x11-M\x83\x95Z\xe7\xadk\xefd\x1dÉŦ" } leaves: { key: "CCY=" value: "u\x86\x9a2\xc6\xedk@\x0ei\xcei\xf7\xb2[F\x1b\xb1\x1e\xdfYX\xf9No׋\x12\\8\x9f\x9c" } leaves: { key: "CCc=" value: "c-\x8bk\xac\x91\x82\xbb\\Qy\xd9Å\xac)\x13\x8e:\x03\x1c\x006\xc6\xcd\x1d\xc0NI\xc7\xedY" } leaves: { key: "CCg=" value: "cX\xe7\xd4\xd9u\xaepKsh\x00H\x07\x7f\xba2\xa4UK\xee\xa8\xfb\xebŨ“\x03\xe4\xffX\xc0\xee" } leaves: { key: "CCk=" value: "\x1d\xc2f]\x98\x0e\xed\x18\xc0\xde\xffb\xed\x05i%\xf2\x8d\x15]\xcc\x7f\xeb j)\xe7\x12Ëŗ\x06?" } leaves: { key: "CCo=" value: "\xb5\xbc`\"\xfe\xe9Q\xa2\xe3V\x94\xedRp\xe04\xe6\x85|#\xbb\x7f3\x16\x7fPk\x98F\x16\xa2]" } leaves: { key: "CCs=" value: "Y\xfbo\x80>}a8\x98Đĸ\x8f\x1c\xba3\x86\x8e7\x01\x11{\xb8\xd37~^\x9d\xd0" } leaves: { key: "CD0=" value: "\xc9_?\xd5\xc1X]c\xd8\xea\x0fÄ Q/K@ \xcc\x14A\xff\xa6\x96.\xe1\xc5\xe1\xdd\\\xa3," } leaves: { key: "CD4=" value: "X\xa6x\xff\x1d\xa2\xb5ŲĨ\xe8\xbd7\xaeP\xe4\xfd\x1f#\xd0t\x04\xb6@\x95\xdak\xb6\xb3\xe3Ūē\xfb" } leaves: { key: "CD8=" value: "xȈ\xa7\x9c\x86\xda&\xceE!f\x86'x\x08\x97\xeab,U\x0fÜ­\xc2\xfd8\xeb\xb4\xccs\xe5" } leaves: { key: "CDA=" value: "\xeb\x16\x83\xc5w\xe5I\xf0U\xa7\xc6\xc2\xe7\x87+b\xdboc\xb0\x01^\xe0V$\x95\\-\x83}*\x04" } leaves: { key: "CDE=" value: "\x90\xbe9\x92\xd0tTOuWД\x87}>z3\x08\xea\xbcrj\xfaY\xab\xc1\xf8eW\x12\x99\xdf" } leaves: { key: "CDI=" value: "\x18,Ύ\x9d\xda1\xd69\xa2m\x80\x16Fq\xb9\xc0Ķē\x80\xdf=Y\x19L\xb38\x1b\xb4I\xeb\xf4" } leaves: { key: "CDM=" value: "\x06*\xb2k\x11m^7\xf2*\xbaK@\xc9\xeaM\xa7\xcb/^âǝÆĒz\xa4KW=L^" } leaves: { key: "CDQ=" value: "\xee\xd3l\x02M\xc5m\x83\xf3\xa6(\x14\x8bIj\xbc\x94\x0e\xda\xf8\x93\xddD<'l1\xa5\x05\x9d\xdc%" } leaves: { key: "CDU=" value: "Ý(^n\xb7\x0eta\xb3\x99\x9eB\xb7]{\xa7Jl\x00hT\xd3\xe3(\xeeUO&\xfc\x90\xa2" } leaves: { key: "CDY=" value: "g䙃\x1c^\x07,Ķf\x1c\xaf\x0ez\x97/\x03\x90\xe8\xbe\x1c\xbc\xc9\ne\x1fwc\n\xa0\xa7" } leaves: { key: "CDc=" value: "P\xf2%\x151~vv\x90NWS\xa9\xbb#\xaf\x9bT\xcb\xcb\x05Ilk\xb8\x04\x19]D\xd3t\xf1" } leaves: { key: "CDg=" value: "\x8a\xd2ĐŽ\xdd;\xabp+\xff\xe0\x1ax\xe4\x15T\xa2\xcc\xeaה\x19\x10\x8f" } leaves: { key: "CDw=" value: "\"\xb0<>\xc6\xe7\xef\x02f\xa6\xd2Ōŋi,\xfb\x0e\xd0\x07\xc7\x04\xf9\xefo*\xbeE&p+\xcaT" } leaves: { key: "CE0=" value: "\\%\xe5\xb4\xf0\xf3\xbc?\xa4;\x8aV\xf1|\xdb\x18t8\xf3IRLr\xd8ĖŦ\xb1}\xd8\x10M\x0f" } leaves: { key: "CE4=" value: "\xfa5}k\x82e\x8cW\xb0\xff\xb40\xdfi\xac\xab\xf7\xb5q\xa8\xf1r\x11?P\x96}\xa8\x1c" } leaves: { key: "CE8=" value: "\x04\x96\x05\xa9\xe9\x17\xfd\xbe\x1d\x9f\x16\xf5\xf0o}huJ\xb3\x88T@\xf7e9\xc2p_cp\xc1\xf2" } leaves: { key: "CEA=" value: "}\x00\xb3\xa4h\xea\xa3j\xac\x1a\xff\xf4=SM\x91,\x95\xf2\xd1\x1a\n\xb8\xca\xdeK.9\xea'\xef>" } leaves: { key: "CEE=" value: "y\xbc\x18!\xb3xY<\x91-ԅ\x80H\xc4\x00Hj\xaa\x17t4\x07\xa4DF\x85*\xd7,\x18\xc4" } leaves: { key: "CEI=" value: "\xfb׏\xc5\xceley\xf3[\xc7\xe4\x8d\xf0͑ů\xc1ÃŽ\xfa\x91dZ\xadB(\xc4E\xd4\x10" } leaves: { key: "CEM=" value: "@\xf6\x93b\x9b\xe3$>N\xfb~\x88\xd17\x8a>\xd4b2\x9a\x1d\xa7\xa2\x8f\xd2)\xa2\x92\xa9\x104\x1c" } leaves: { key: "CEQ=" value: "\xae\xaeI\x84\x16\xa3y\xd7\x19\xe1d\x0f\x8f\x03P,\x97g\x1f8֗7ֆ\x92\xc5@3\xbc\xea\x82" } leaves: { key: "CEU=" value: "\xb4\xc2\xfeW:k+\xe4\xc6\x1bF\x82\xa0\xb1vk\x99v\x9b\xaaX\xfc~\x97\xfb\x9f~\xec0\x05:\x1e" } leaves: { key: "CEY=" value: "\xb2Ö°\x17\xb1{\x0eVNT\x831\xa2un\xf2\xd3k\xc1\xee\xe0\xe6\x1f\xc8b\xd50\x12\xef\xf9\n\xd5" } leaves: { key: "CEc=" value: "\xd8\xe9\xb9$\x86Ũ›\xc0\xda\x11@\xb4\x15\xf6\xd3}\x7fD\xf615\xf4\xf4^=~\x02ZĖ—\xe6p" } leaves: { key: "CEg=" value: "+\xb7R\x06&<+\xa6\xd9u\xb8'5'|\xf7\xc5OhD\xc1|Äē\xa4\x8c\xc0O\xd6/\xa3=" } leaves: { key: "CEk=" value: "-\xcb\"\xad\x1f\xa3 {\xff\x7f\xaa3.\xb4jPuy\xe7$u\xfcN\x90\xf8`\xa7\x9c\x82%G6" } leaves: { key: "CEo=" value: "w{\xa7\x1e-\xd7?'o&S\xaf\t\xc1J\xf7e\x9d\x08it4\xb8\x0c\xaf\x0cЉ\x16\x9f\xb4\x83" } leaves: { key: "CEs=" value: "\x80\xcdz\xef\x0c\xb4\xe0\xb3\xf3x\xd3\x1a\xc3n \x9d\xb2!yX\xf6W\xb5\xe79}\xac-\x91KЇ" } leaves: { key: "CEw=" value: "^\x003]f<}Q\xab\x8f.7\x82P\xeb\x0fHp\x8c\x03!\xf9B\xa0\xa1\xb1\xa9\x05\xf1~'\xef" } leaves: { key: "CF0=" value: "\xf6\xcbU5\xd8\xcf\xe0\x8d\x17\xd6H\xcc'o\xb0\xef\x10\x91\x01s\x93a\xc9Tj\xcd-3\"_\xe1L" } leaves: { key: "CF4=" value: "6\xf3\x972i\x80B\x88o\xf3\x15\xf8wՄu\xfet$\xa4\xdd<\x8b\x87\xd8\t2\xa2\xed\x93\xd7\x15" } leaves: { key: "CF8=" value: "K\x7f\x03鷋\x06\xb4ĐĻB\xf7\xca\xdfBa\xa3\x91[\xb8 \x03\xa0&\xf30\x87\xd3{\x82Xv" } leaves: { key: "CFA=" value: "\xab\x16p.aY\xa4j\xbc&\xbc\xfe'\xbce5J%Θ^Kw\xfdv\xd6!LT\xa5\xea`" } leaves: { key: "CFE=" value: "\xc7\xedN\xef!)\xfd\x87[\xbf1I2\xc4&e.\x81S\xcfD+-t-\xa0\x8fJ2s\xd3e" } leaves: { key: "CFI=" value: ">\xa5\xa2a\xf0\tA\x1f\xc8\xf5\xdc\xca\xf1\x13\xc0\xe2\xe5\xde\x05\x9c\x13\x7f'\xdd\x15\x83H&g\xb2\xfa[" } leaves: { key: "CFM=" value: "\x84\x9b\xf6\"\xc0`\x9a\xb0\xbfʇ\x82\xe9\xa0\xe7\x13\x87|FI\xc1\x81\xf3\x8d\xa6\x1e5\xe2\xc1\x92\xb9\x8c" } leaves: { key: "CFQ=" value: "\x80U\xc32\xdb`\xa6\x166*H\xe9(\x80\xa9\x9e\x98\x96\x0bf\xb5@Ę”Øƒ\x10\xfcx\nOo" } leaves: { key: "CFU=" value: "v\x93\x813\"\xb2qŅ\xba\x7fhix^\xab_\xdd.\xf3\xc0\xf5\x05\xed9\xc8\x10\xd3Ô´\"\xda" } leaves: { key: "CFY=" value: "΄Zz\xe5_\xa1\x99#\x80:\xb9\x15\xa2$\xe72\x04\xd9\xc9d\x92\xfble\xac!\xd8\xe2;\\B" } leaves: { key: "CFc=" value: "\xc1\xeb\xea\xb4\xce\xf5\x0e\xc5|\x15\xbb/\x96uu\x91\x0b\xd6\xe8a!\x15\x87qC\xcb\x0e\x036\xb1\xa4\xd3" } leaves: { key: "CFg=" value: "@\x15\x0eG\xc3\xea\x17\xf1!,O\x87\x01\xebe\xb8\x08\xe4\xac,@]\x82\x95Wi\xf6:\xf6\xe9I\xd9" } leaves: { key: "CFk=" value: "\x0eG%\x84\xf1\xd34\x06\xe1ꉠ\x9e\x12\x947\xa1\xa7\\\xb0U\x10\xba\xafu&\xbf\x05\x1c\xf2\xef\x85" } leaves: { key: "CFo=" value: "\x01\x06\\\xbcj\x82':\xc6\xc1\n\x17\xc97o\\\x005" } leaves: { key: "CFw=" value: "J\x1f\x06\x1b_\x03\xae\xd7-h\xd0\x17\x14\xd0c\xd9\x16\x14m\xd3\xc0Q\xa7nŅy\xf0\x1b\xb0\x92\x8f" } leaves: { key: "CG0=" value: "R\xccÎģ\x18nv\xa8\x8c\xd9\x14\xf6\x8a\xbf\xed\xf4:*\x0e[dO\xa3\x0e\xa5\"A*\xe8\xbe\xfe," } leaves: { key: "CG4=" value: "\xe7\xcd\x1bi\xc0ish\xe5\x050\x973M4:k\x0c\x89HB9\x17\xb4\xa0\x97\x10a\xf2#\xfa\xa0" } leaves: { key: "CG8=" value: "\xf3͞=\x11}*&C\x12HH\xbb\x8b\x15\xc9b\xf3{\x84I\xa4\x8b\xbb\xfe\x98\x7f9\x892\xd6l" } leaves: { key: "CGA=" value: "\x1a\x0c\xd1b\xb1\xcc;\xf9r6čģĩ\xee\xb0 \xb1[\x90\xcf\x0cP\xefO}J\x12Q\x93\x07?l" } leaves: { key: "CGE=" value: "\xfa\xffO\xf5\xe0\xd645\x07*\x965&j\xfa\x8a\xc8\xc8p\xa3\\\x86\x9d\x9d)\xbd\x01\x98ň\x15(" } leaves: { key: "CGI=" value: "4\x80`\x0e\xaa:\x18\xces\xa6\xe8\"\xfa\xbaŌ„\xeeV\xd1\xf8Ũ {\x96\xb04\xd6m\xd5L,\x0e" } leaves: { key: "CGM=" value: "T\xdf\x7f\x90\xc5ؓ\xf2%$4\xd7\xdd\xde\x06q\xd4\xf6\xaa\xfe\x01\xfb\xe5t)\x02=\xb5\x00\xf1\xf5\x85" } leaves: { key: "CGQ=" value: "O\xfb\xa7\xb3A\x16\xac\xdcB\xde\xe3\x94O\xcc^.o\xba\xfc\xfa6\xce\x1c\x8c\xc1\x83m\x16\x08\xa1\xdc\xca" } leaves: { key: "CGU=" value: "+\xa1\xda\xdcHdl\xe7\x1b``Y^\xacĪ€Õ\xbc\xe7\xa73\xeb\x89(\x84\x97\t\x99Îĩh" } leaves: { key: "CGY=" value: "\xf2\xde6΍\xb0\x9a\xd6ŅĻ\x14\x8fm\xb2uA\x1ah\xe6Ɯ.M&\xdd\x1a(&\xebx\x0b," } leaves: { key: "CGc=" value: "\x1c5ԇͤ\x82L\xb5\xe6\xb9V|Q\xa5\xdc4$\xdeyAVr\x89\xc08(\xba\xfb\xbf\\\xe4" } leaves: { key: "CGg=" value: "e\xea\xb6ČĻ\x07\xd0\n\x81U\x07\x0c#%\xf7\xd8\x14e\xac\t\x14O\x95\xb0@\xfdf\xe3\xb4\"\x03\x08" } leaves: { key: "CGk=" value: "\xe3{T\xc8\xfd\xbct\x19\x17)\xdd\xea8\xb5Xy\xaeqa\xd5l\xf8`C\x8b\xde\xd81\x14J\x06E" } leaves: { key: "CGo=" value: "[R\x11\x147ZHT\x8d\xdf\xc5\xea\xa8\xeb\x98\xe2ܐi\r\xf3\x15~\xea\x04\x93\x8b\xb2\x0f.\xee\x17" } leaves: { key: "CGs=" value: "\x94\x04\x1b9(\xba\x1cO\xe5\x01(\xe5:\xa3\x05\x1a,ʓ5\x07\x0e\x93\x17\x88\xc2Y\xd3t\xd4\xeb." } leaves: { key: "CGw=" value: "8\xe9\x16\xa8\x81\xb6bĪŽj\x08 \x02F\x8c\xbe\x8f\xa8\x90A\x06}O\xb9\xeb\xad:\xa4ČŠB\xa7" } leaves: { key: "CH0=" value: "E\x88\x00\xc4?E\xf1\xae\xc2\x1e\xd92\x02\xe4w)\xf9\xc1\x1cQ\xc0N\xc8\xc8Z\x96\xae\x0bR\xf1:L" } leaves: { key: "CH4=" value: "{\x16%{\x85Vʉ~\x03\x97鈞(\xb3*\x85C\x01\xbc\x1bV \x05\xa4>\xa1\x16\xf6z\r" } leaves: { key: "CH8=" value: "_\xcdRf@\xdf\xeaŲ–'+\x12\x83[\xbb\x02\xb1E)~\xa4S\x8eb\xc0\x80\x86\x8eq\xa6Aw" } leaves: { key: "CHA=" value: "\x0bÕ°^\xa6\x0fD\xf2\xeeMy\x14\x02\xe6!\x1fZt\xb2\xf8\x8bX\x91^J\xf9\xaauT\xab8\xc0" } leaves: { key: "CHE=" value: "\x9c\xc6\x11q\xebl\xa6j^{\x0c\x9f\x11E\xd2[\xc5S\x15\r\xe7;Üš`*\x1d\xb2\xaf5\xc2\xcd" } leaves: { key: "CHI=" value: "W\xee\x11\x8b]\xf3\x81\xda\xc7#E\ti|\xb1TÍ­O>\xe9\xea\xd7\xc9\xc9eu\x84H\x93\xae\x06" } leaves: { key: "CHM=" value: "\x17\xd2\xef\xb3\x15\xcf\xd1'\x863u u\xc7\xe9\x1b*8\xab\xc1\xee\xba\xe11\xb4\xb5\x8d\xb6\x92\x02,z" } leaves: { key: "CHQ=" value: "\x05\xe7\x0fG\xb8\x01)z.\x97\xab\x18m\x0b\xef\xfd!V\xa0\xd0r)\xb5v\x01\xba\xf5T\xb7$B\xef" } leaves: { key: "CHU=" value: "\x94;\x9d\x0fYQ\xe7\x9by\x9f\x90\xfe:\x19|\xceo\xb0\xd24\xb2X\x08\xc3\xe3\xc7_\x99_?sX" } leaves: { key: "CHY=" value: "J\xd3\xfc\x8fw>Yrja:\xc7\xdd\xd4\xd7^{\x1b-^\xf8\xa0\x8e[\x8am\"\x86f\xd8\t\xa2" } leaves: { key: "CHc=" value: "@lx\xb0\xef\x16\u008a\x1dy\xa4?~\x1e\xce\xf0/ax\xd4tu\xd6\xe4\xaaĶĸ\x007iI\x1f" } leaves: { key: "CHg=" value: "\xcb\x10\xdbʤ\xc1\xfbc\xb8x\x1b\x9b\x13\x80,Na\t\xc0\xb37^\x89\xa6@H\xb7\xeaIN\xe7k" } leaves: { key: "CHk=" value: "-\xac.G\x1c\xc7r.\xd0?o\xd5\xf0\x15K\xad\xe4\xf3\xb3\xa2\tz!\x16\x03\xcf\xdd\xe1jn)\x04" } leaves: { key: "CHo=" value: "\r\x93\xb6]\x17\xbaGo~\u009a\xa2 \x8c[\xf7.(F\x0c6\x89\x84\xc3\x00\x8e\xd3\xe6*\xe2\xdfI" } leaves: { key: "CHs=" value: "\xb5M/\xf8z\x8fL\x15\xc0\xa7\xed\xe6͘\xc8\xd9\xd6*ɘN\x06\x03\x9cl\xe0I*\x9e\xd92E" } leaves: { key: "CHw=" value: "\xb4\xba>\xda\"\x10\x9f\xab\xa2\xd6\xcf)\x9b\xdc\n\\\xeb\xd0jW\xde%u\xdd\xee/%" } leaves: { key: "CI4=" value: "ČĒq\xdcꚅ\x95\xf6\xa9Æ \xfa_\x99\xf0jL\xb8\xe3Ƹ\x8b\xa4\x108\xa4C#nz\x8a" } leaves: { key: "CI8=" value: "2H͉\xbdtWs\xd8\xeeVtĖĨ\xb7\xebC\xdc/\t\xb5C\xa3ßĩ{\xf0)Mz\xfc5" } leaves: { key: "CIA=" value: "\xa4\xe1Z\xe8K\xa5怔\xbc\x80\xe4p\xa5#5\xbd^\r-*,\x91Z\x01\xc2>qz-r\x19" } leaves: { key: "CIE=" value: "\x0b\xa2$Yqp';\xd0\u0084\xb9\x85U\xc0\xf2O5\xf0\xf9\x18\x80\xff\x07\r\x15\xc1\xd3ÅĻ\xfd^" } leaves: { key: "CII=" value: "\x8a\xa9\x12HL\xef\xdem\xe5\x13\xbb\x0e\x87\xf8 {E\xf1\xc8E\xa8e(\xcb\xc7Ej\x19r{?^" } leaves: { key: "CIM=" value: "Z\x1d\xa0?\xb7\x83\x00w8\xc1\x94K\x83\xae\xbb\xab=1\xd8Ôģ\xbe\x1d\xc0_\xb1\x08!va\xe4_" } leaves: { key: "CIQ=" value: "o)'k\xc4')؞\xc1\x10.\xa3h:\xfaYT5ě\xb3\xc55>)u\x11\x97[\xa9\xb5" } leaves: { key: "CIU=" value: "t\x0b\x15\xd8\xe0a\x91\x8a\x92éŊ‹;\x8d~k\x8b\x83~\xea\x7fx0Z:P\xf4\xe9f\xf8\x86$" } leaves: { key: "CIY=" value: "^\xd3i\\\xe8Q\xe7\xf4\x8e\xc7\x00ęŗ•ÆĄ\xee\xd9vUO/\x1c\x1ec\xae{\xa0e\x83\x19T" } leaves: { key: "CIc=" value: "\xa6\xef\xad\xf6\xea\xe6âļ’\x96~\x8c\x99]\xaeP?q\xa4\xe10]\xec8;\x96\xa3\xcc\xec\xe4\x9c8" } leaves: { key: "CIg=" value: "\xfe\xf7vĪ…\xe7S\xf2\xcb\x1e\x9b\x92 \x10\xff\xc5\x7f\xe1]]ÆŠVt\x8alÆš_M\xc8E" } leaves: { key: "CIk=" value: "\xec\xad_Km\x9aØŖh7\xf0Įŋ\x97\xb0VȂ\xf4sz\xdaC]\tZ'\xe1[\x84V\xbd" } leaves: { key: "CIo=" value: "\xbbqse\x81\x84\x97Ō\xbe<\xa8\xd4\xfa\x9f\x152^\x07\x18\x98\x04\x03\x0c\x87\xbdK\xb0\x03\xca0)\x11Ō¨\xba\x1a" } leaves: { key: "CK8=" value: "o\xc2XN\xa1\xaf\x92\xa8:\x88!2O\xed\xc7*\xda~\x0cL\x9fI\x85\x107Yp\x97\xde\x00\x82\x81" } leaves: { key: "CKA=" value: "\xd3҈\xb0\x14X*)\xcb)4\xfb\xdc\xf5\xc9!\x98\xf6\xb7\x94\xaf\x90\x06\xcds\x0c\xe1\x07\xb5\xf9\xc9\xee" } leaves: { key: "CKE=" value: "\x96r\xcfe)\x18\x0e\xdeF\xdb#\xcc~\xc1\xa1\x17\xe5b}\x08\xb3\xb0˃\xf1A<\xeb\xc3a\xe7l" } leaves: { key: "CKI=" value: "^\xbc&F\x04\xca\xc0Z\xbc\xb3\xa3fA\xbd\xfe+\t\x9d\x91\xcfDA\x11J\xb9\xcf\xe6\xb3?\x07\x8f\xad" } leaves: { key: "CKM=" value: "\x84\x9f,\x0c\xb4\xa0ÚēU\x11e\xf5ɧ\xbe\x8ex\xfa\x14\x0b\xeb\xec\xc6\xf3gT\x8c[\x88؟0" } leaves: { key: "CKQ=" value: "ReOk\xa6\x9d\xcbe\xaa\xe5q-ȟÛĸ\xdd\nj\xa8\xd0h\xe7\xfa*\x8b\x135ŗ\x15\xa9" } leaves: { key: "CKU=" value: "\xd6T\x885\x90L\x12Dk\x05\xd1|\x08\xa5\xd5?h\xf4\xe5\xbdr\xe4\xfe\xfcV\xbf.E}߁U" } leaves: { key: "CKY=" value: "\xf9m2RB\x9d\x97#\xf9z4\"\xe1\xca\xff\x80\x12F/\xa2\xb6\xf8\xb8\t0\xd1<ØŽ8\xa26" } leaves: { key: "CKc=" value: "Xl\xc5\xdc\xce%9\x18\t\xd07WŪˇ\xfem\xf0M\xd7[\xa1.Td\xf9\nO\x1f#ˑ\xd5" } leaves: { key: "CKg=" value: "I\xb22\r\xf0\x8f\xa3\x84vOB\x14\xd9(Smv\x89V\x0c]o\xf3\xf7D\xfb\x1cd\x1b\x13\r\xfa" } leaves: { key: "CKk=" value: "{kf\xe9\x11b͆\xacU\xd6\x0f\xb0z\x1c\x0bÕ¯4\xc8\x11\x9e\xa4\x8c\xf5>\x1e\xb0\xe9\xcd#\xe4" } leaves: { key: "CKo=" value: "\x90J?+\xd2%9\x84\xfa*\xc4Whwc\x8d\xfc\xc1\xf8\xc9E\x7f\xb3d>\x99$\xee\xfcL\xcb\xd5" } leaves: { key: "CKs=" value: "+Z!\x94\xea\x96t&\xfb\xfdM\xc7#rW\xdd#\xa8\xcf\x0c43P\xaaV-\x066\xc9߸\xe7" } leaves: { key: "CKw=" value: "\xbf\xb5k曗[\x93\xdd\xe2\xc3[\xed\xad'\xc0\x883\xdch\x1f\x93H\x0f\xf5RG\xe7t\x83\x05\xd0" } leaves: { key: "CL0=" value: "Rĸ\xf2\xd4sY\x01P\xf29g{~(\xae\xf4\x19\x822r\xbd\xafp\xa9\x05PN\xf6\xbd\xe2A" } leaves: { key: "CL4=" value: "\x157ÖŦ\xc06\x14_h5\xb6`bBɛ\x9a\x1b\x9e\r\xf4yČĒ\xb2\xe0Z,\x8d#\x83\xf4" } leaves: { key: "CL8=" value: "r\x126\x1b\x18ls\x04f}5Q\x7f\xe8ŨÔ—]B\xb2ÆŦ\x8c\xbdk^\xa1\xa0\x93\x01\xc6" } leaves: { key: "CLA=" value: "\xcb?\x83\x13Įœ\xd4\x04J\x82[\x84\xb6\x197\xb5\xec\xa2\x10!\x9a}Øŋ_/\xa8\xac>\r\xb60" } leaves: { key: "CLE=" value: "\x9e\r\x87R\x12\xff1\x9b/\xbc\xce\x15\x82\xaev/C4>\x9f\x12a\xabO\xf5\xf3\xddvJ\xe8G\xbc" } leaves: { key: "CLI=" value: "\xa8\xbaV}v\r\r\xe1\xc3FЃ\x19B\x80E\xa8\xc3\x11p\xb7\xaf\xef|\xba9\xaa˩ě\xa8" } leaves: { key: "CLM=" value: "\x93\x07\xf5\x02\x9d\xebQ\x92\x02`\x02\x8b\\U\xe0r\x8cu\xae\xf80\xfb\xa7\xc7\xc8\x13'\x9b\")\x90L" } leaves: { key: "CLQ=" value: "(\x90F\xc0G\x86\xee¸\xca1\xcar\xdf7,l9\xf0\x07BD\x98\x11j\xc8H*WB\xf9\x1d" } leaves: { key: "CLU=" value: "\\9\x92\xec\x1d\xc0\xec?\xc8\xfbUŲ”\xec\xe9\x19BS\xdd\xd7\xd0\x01w2y0d\xa6dX\xc1\xc8" } leaves: { key: "CLY=" value: "B0\xe0\x0b\xc0\xff\x8dIJ4ב_\xb3r\xddo9\xceb\x02\xdc32\xf7\x11\xe4\xf29Čē\x1e" } leaves: { key: "CLc=" value: "\x81\x1f\x12s\xe0(I\xfc\x1d\xeb\x1e\x0c\xe4\x92\x19\xedy\xf9\x97\x9b\xa2s\x8f\x9a\x14{\x88D\xb4\xab\xe4&" } leaves: { key: "CLg=" value: ",$\xb6(\xab\x95\xe0\xcbW3'E\x0elcOĮšØ„\xb0Re\xeb\x03\xf9\x19\xdc\xf5@\xf7b" } leaves: { key: "CLk=" value: "\x05\x0c\x8cd\xe9\x0e\x86Ņ™\xad\xc8m~m\xc1Īŗ\xe8۟\x94:H}\xe5\xc5\xe2\xc4\x13w\xf6\xd7" } leaves: { key: "CLo=" value: "S(\x0f\xda\xfa\xfd\x85`\xc6}?\xe9%\xe0\xe4\x05(Q\x88\xac\xf1\x8f*}ZĪĨM\x93;\x0e\xb1" } leaves: { key: "CLs=" value: "\xde.3=\x16\x85r\xaa\xfb\n\x1d\x91MsÈԞ\xcd\xd9\x7f\x98K\x1d\xac\x98\xbf$vv\xe0\xf0" } leaves: { key: "CLw=" value: "\xd2Z\x98l_\x81\xba\xea\xedD\x15\xe2\\\x83@\xfe\xee\xb4\x0e\x1b\xce\x17;\x91N\x0e#\xc1\xc4\xe6~\x05" } leaves: { key: "CM0=" value: "\xfd#\xa1\x05f*\xe9pŲž\xd3n,\xc2U\xd9\xde\xfb\xf9\xedP\xbd\xc2Ũ¸\xf0\xde\xf6\xeb\x9c\x07\xdc" } leaves: { key: "CM4=" value: "Y\xdf\xf6\xcd{\x86\x91М>BE\x12\xe4\xa8e\xb3\xa0\x15\x86\xdc@\x8a\x0b\xc5A\xbao\xf5n\xdcH" } leaves: { key: "CM8=" value: "\xba\xb8\xd31\xd4\xe5L\x03LtX\x90\xb1\x8dis\xefV\xa8\x7f\x82O{J\n\xb5D]\xa3x\xe0T" } leaves: { key: "CMA=" value: "ɀ\xbe\xaf\xa3\x99$\xf7\x0cR\x1eB\x86\xb8\x10\x7f\xb3\xbb\x87&A\xdc\xfa\xf5ğ%q\xb9SM\xc7" } leaves: { key: "CME=" value: "\x03\x05#\xdb\xcd\x04(\x80ɜ\xd4_`â‚ĸ\xf8\x9d\xcfcō†ž¯Wg\x0f1\xeeL\x83\xd2" } leaves: { key: "CMI=" value: "wgb\x1e\x9d\x80o\xee1\xb5l'f\xda\x17\xd7\t'%\xab\xaa\x15\xf2\xe4\x16[.\x14v1,\xda" } leaves: { key: "CMM=" value: "\xba\xe4\xe7)~\x0b\xa8[\xdb5<\x02\x86\xcc^\xbf\xb5\xd9\xdc1\x9b\x9e\xabYQ IL\xa9rr\x89" } leaves: { key: "CMQ=" value: "|\xee\xdbߨ)\xd0Ū™`\x8f&\xa6\xbe\t\xb7~\x00\xd3)1\x00\xc7\x05\xee\xc4\xf2\x07-\xd2k\x1c" } leaves: { key: "CMU=" value: "\xd4!V8\xa6&\x8bo#CK\x01.\x99e\xef9\x19e\x00\x9d*\xe3ͤ\x81\x9f\xea\x0f\xe9҃" } leaves: { key: "CMY=" value: "\xdcW\xe9\x8bu3^\xe2) yX\x96\xb2ßT\xffp\xe7\xf18\xf6\xc0O\x85IyC\x00um" } leaves: { key: "CMc=" value: "\xe8փ:\tW\x05\xa7\x86;H\xe73\xe5\x9c\xeb\xf5\xedn\xd0\xe4\x145Y\x01\xeec}\x1fL\xd1\xd0" } leaves: { key: "CMg=" value: "\x8f;\x80\x9e\xad`c>\x1b\xd7Ds\x8d\xa8ş\x9a0ČŠ\xee\xac\xe0\xe9\xdc\x10nJvQj\"" } leaves: { key: "CMk=" value: "\xee\x13\xdf\x14\xb4\xadw\xa0~\x95\xdf\\\x1atk.JQΗ\x87\xd3\xffK\xf1\xceX\x99:ĪĒ\xf6" } leaves: { key: "CMo=" value: "\xae\xf1N\x1c\xb7\xe4\x87\xf4\xa0\xc9\xf3)U\x81\xfd\xed\x87\x00\xaf\xba\xdb\x05\xfa\x9an.ߕ\xbdA\u0081" } leaves: { key: "CMs=" value: ".Q\xeb\x0f|a\xd8卧Q%\xe3\xf3g\x0chX\x91(\x98\xf6\x9f\x04(\x9fl#2x\xf7\xbc" } leaves: { key: "CMw=" value: "\\\x0e\xa7\xdfULP\xe9|b\xad\xfeM\xbd9\xd2\x1d\xb7\x06\x7f*\xa3ՃCS\xb0q#\x04_\xcf" } leaves: { key: "CN0=" value: "\x07\xac\x00 EZ\xf0\xa2\xf4\xe2\n!94d\xd9\xf4ZĐĨ\x87Jg\x88s\xe05\xe0\xe6D`\x1a" } leaves: { key: "CN4=" value: "\x8b7\x0b\xe1\xe9w\xec_\x9b\xa5\xbd\xd6\xef\xc2\x18\x0f\xda;\xf5\xc1\xba\x98\x92\x15++Q\x9d\x05\x80ž" } leaves: { key: "CN8=" value: "\xeb\\I\x8dO!Ĥ^\xdc(v\xd4\xef\xa1\xd6Ė—\x9d\xb8\x1b\x95XA\x1a\xe3DK\x13}P\x9e" } leaves: { key: "CNA=" value: "\x85~\xac\xa7=\xa5\xfc\xd1\"\x96i=uYk\x04w\xff\x18b\xaa\xed\xe7+j\x97\x16\xed(I\x12\x05" } leaves: { key: "CNE=" value: "\xb5\xd8O\xeb\x9b\xd6T\xc5\x1b\x14p\xd2ר(\xce6L\xcd\xe39c\xfa\n,\xfb+r-^2C" } leaves: { key: "CNI=" value: "q\xc5\x06\xef^m\n\x0e\x0f\x1e\x89\xd8D):X*q\xdf\x10U:3'>\x16\xbc\xf0\xed\x12_\xcf" } leaves: { key: "CNM=" value: "4^Íĩ0\xbfiŌē\x07P\x8bb\xf3\xb3\x8el\xfc\x18\x9fs\xa3\x89\xda\x1f\x81\x84\x00\x06\xa8\x8a4" } leaves: { key: "CNQ=" value: "\xbf_=I\x0c\xbd\x9e\x9c\x88k\xf6\xea\xcc8\xa7pG\x88\x823\xc7\xc9\xfaf1\x7f\xdc\xda\xf5\x9flV" } leaves: { key: "CNU=" value: "\x16dR\xe7\xc2\xc7'n\x01æĨ‘\xab\xb8^\t|\xf3\x93\x98\r¡5CL\xbd\xdc\xc5\xf9v\xd5" } leaves: { key: "CNY=" value: "\xc6'\xd2h\xad)MԖ\x9f\xfe{o\xfcŨ¸\xe7\xbb3\x1e\xbbU\xa4x\x9b1Y\xacP\x92\xdf#" } leaves: { key: "CNc=" value: "\\_\xc4\xfb\xbb\xbfT\xaf\x91\xaa\x9eiŪ—\x86&\xa3\xe2\xf6\x06\x94\xb5\x13\xcc\x19>\xe5o\x91\xd9GY" } leaves: { key: "CNg=" value: "\xb6\xdcu\x06\xfba\x0e" } leaves: { key: "COU=" value: "\x0c0\x0f\xe7C˞\x97\xf7\xf5\xd2g\xe4]\x92\x9e4k\x1b\xb0\xdf\xf1\xaa\xc4fY\x84\x1f\xfe\x89>\x93" } leaves: { key: "COY=" value: "5\xc1)=\xff\xc1\x1b\xaa\xd5,\xb5\xe3\xc2Ûĸ\n\xd9СH͟\x98\x8dX~\xf2ZxKmV" } leaves: { key: "COc=" value: "5\xa5\xf0`n\xb8\x9d\xc7߄\xec\xd9\x0e`\x14\xecĘ´c/\x0f\xf1@\x88\"O\x8a\xf9\x06PS\x02" } leaves: { key: "COg=" value: "\xc1*`B057\"S\x0f\x16KR\x06eX\xde\xfb@\x86\x92\x14()\x1e\x9a\x8e~B7\xa6j" } leaves: { key: "COk=" value: "\x9fh\xae\xce#\xe1K\x12xŅĄc\xa4?\xc6\xd1;\xbed\xfa\xdaz)Th\xd3\\\x99\xb8u\xea!" } leaves: { key: "COo=" value: "D\x08Χ~\xb2\x1f\x1fl\xfc\xbf\xcd\xd0\xd5Zx7}\xff)Y\x1a\x90\x83\x10,\xbf0\xa0\xa4\xcb\xd5" } leaves: { key: "COs=" value: "\xb9\xa2\xc43\x0f#\xf0\xe8\x93K\x83\x97Z\xb7\xff\xbf\xf9\x8c\n\x8e\x89L\x0f\xb1А\x87\x9c\x96k\x8c\xa2" } leaves: { key: "COw=" value: "\xf98߀V\x8c\xb0-\x8b\x8a\xd2d&{\x12A\xf4\xdct\xb3Wh\xf4\x88؁ßĻ\xd4\xd5\xd7D" } leaves: { key: "CP0=" value: "\xe4[\x83\x8d:\x82i1햑\xc64\xd5.\x0f\x88W$\x1d\x01\xa7)_\x92\x93\xb2\xed\xd1\xfew\xe1" } leaves: { key: "CP4=" value: "\xca0\x82\xffE\xea\xbd\xc7ko\xfd\xf3s`\r1\xdc0\x05\x93\x8b@\x14\xe8\xa7\xc1m_Si\xe1h" } leaves: { key: "CP8=" value: "\x94\xe5'|/.\x8e\x1aM\xe8(\x8f\x8c\xf3y#u5\xfd\xfaD>\xff\xc1\xe1\xf8$\t\x04>OJ" } leaves: { key: "CPA=" value: "\xf9o\xc4Q\x81\x1c\xc8\xf4\xe1l1\x15n\xcf#(\xb8\xec'\xff\xc3P\xdfS\xfb\xcd\xcbh\x92|\x9e2" } leaves: { key: "CPE=" value: "J\x94\xe3\x04>\xc9\x14\x92\x9f\xa4\x07\x1c\xd0\xd3\x04QW\x0f)\x8a:\x8c\xa2.m\xbb\x99xi\x16!\xe0" } leaves: { key: "CPI=" value: "\xa7\xf4\"\xdc\xd7M\x82\x02\xc0\xaf\x0e\x11^9\xeb\rp\xea\x82 \xbc\x01\xe1u\xac\xcc\xd3]\x1b\xf6\x95\x93" } leaves: { key: "CPM=" value: "h\xb5\xb3\x87\xbc\xad\x8a\xcf\xd3\x1b\x81\x03\x91\x90K\xa0z\x81<\t\xf3\xd13\x9d<\xc5F\x06<\x81\x06\xbe" } leaves: { key: "CPQ=" value: "\x81-\x0bu%Ê\x9d\xfb\xd2\xcb(\x1an\x9e\x0cq\xf4\x18\x9cG\x8eK\x8f\xb4p\x8bڟt\xa2\xe3" } leaves: { key: "CPU=" value: "\x1e\xd2ŨĻ\rSC\xbf\x10\xdfOĖŽ`\x83\x14\xef\x83r\xfb\xc0\x82y,\xfb\xe9\xf0\xc1bB\xb4\xae" } leaves: { key: "CPY=" value: "\xb0|v\xc3@\ts -\x90\xe6\x1c\xaf\xb5f2\x913\xd3Ty\x80\xab=\x187l\xc5\xc7WßŊ" } leaves: { key: "CPc=" value: "y\x1a\xb0\xc7\xf3\xd0\x06\xe8\xa5\x15\x0f\xe6\x89\xc5\xc4]\x92\x05\x8f\xe6\x98\x02^W\xf5\x95\xa0ĮEb\x9d" } leaves: { key: "CPg=" value: "A\x88wg\xb1\xc7Rp\xc4\x0e\x17(\xe9Z\xb5Î“Ō \x164\xb8 \xb1\xe0" } internal_nodes: { key: "AgA=" value: "\x8f\x05[\xe4\x9bG\x8fW\x12\xeb\xe2\xd8[\x89˔m\xccP\x87\x18_\x1d.\xb66S\xa5\x99\x81\x08\xce" } internal_nodes: { key: "AkA=" value: "\"3\x03\x1d[%\xa4\xec\x90\x1b\xd1\xe19\x8d\xda8\xe5oT\xdd\xd4y~\xfa\xb5tT[艊\xee" } internal_nodes: { key: "AoA=" value: "\xc0\xe3l-:\xcfj\x92ᯅ\xd0\x14\x98M\x0f\x9bĮŒWEX\x8d\xc2;\x0f\xd7b\xa4\xb5_\xb0" } internal_nodes: { key: "AsA=" value: "\x0b\x08\x8c\x91\xcd\x11\x0f\xefh\xbdB(\xf0\x1e\xf2V\xb1i\xf8\x1d\xae\xa4\xf3\xc0f\xa3\xb7\xf4\xd8)N\xf8" } internal_nodes: { key: "AwA=" value: "\\\x99\x84e\xee\xd0i2j\t\xcc>\x8bwC\xcb\x08\xa0\x9a\xbc\xb5\xe4\x14r\x03\xb7\xc5A\x9e\x9f" } internal_nodes: { key: "B0I=" value: "\xb1k\xb7n\xcbs\x14\"ĖŦL\x0c\x9f\x96\x87w9\xc7+\x97\xfae\\A\x02]/\xb7\x19NY\xaa" } internal_nodes: { key: "B0Q=" value: "\xea|6\x15q\x14*b\xc6\xc7\xf5\x18p\xba\x1e\xd1\xf3\x1b\x1au\x8c\x02-\xa0\x1eq\xc6\x1bn\xb8\xc7\xf9" } internal_nodes: { key: "B0Y=" value: "\n\x93\xca+u&:Go\x83\x05<\xf4\xfb\xe0V\xaf\x19\xe8\x94\xff\x03|\xee\xe1\x06\xc4:~s\x87\xc1" } internal_nodes: { key: "B0g=" value: "\x8c\x8f\xdb\xcb\xed\x11p\xb4ėžą\xce\x0e\x03\xff\x81\x87\r\xb3\\ads\x8dˁ\xe4\x0c\x83\x9a\xa8\xd7" } internal_nodes: { key: "B0o=" value: "\x9e\x87Ų’\xe8\xf8\xa8\x87\xee\xd72\xda\x17C-\xa8![J\xab\xf7\x89ɞ\x156סh+\xef\x1c" } internal_nodes: { key: "B0w=" value: "\xcfb\xfem7P&\xd4Z\x18gG\x92Z\x08G7\xc6}\x15H\xf1\x84fLOM\x83\x194\x80\xf3" } internal_nodes: { key: "B14=" value: "\xe2\xbe\x03\xde5\x01\xb8|\xa7\x8a\x9d\xa4\x1b%\x022P\x83l~\xb0\x1f\xb9\xa8\x95R3\x83m\xc8\x12]" } internal_nodes: { key: "B1A=" value: "/\xc0=\x0ew\xe5\xc1k\x8c\xb4\x9f\x94]|\x98\x11\x14\xaf\xef\xedҐ\xa7\x11\xad}\x7f\x80Km\xed\xf1" } internal_nodes: { key: "B1I=" value: "\xddA\xa6\xc7\x00r\x00ab\xd0\xf9\xc4s\xa4n\x96\xee\x1e|\x89#\x96a>M\xad\x88\xd1Ԋ\x1c\x19" } internal_nodes: { key: "B1Q=" value: " \xd7\xf7\x0f\xda\xc8}\xa9<\xb1\xa7\xe3\xc4\t\xcez\xf4ÖĒ\xf5\"\x99/\xb1-\x89\x8c\xf3\xa6/-\xf9" } internal_nodes: { key: "B1Y=" value: "\x8a\xa9\xd5\x02&\xebm=\x9a\x97\xd5\x07S\x00\xdf\x07\x10sR\xa7&K\x1c\xf5\x0e9\xa3\xd1\xee\x00\xf2\x98" } internal_nodes: { key: "B1g=" value: "8(\xcdG\"\xfe\xa4\x02\xf8\x19=\x9ax\xb4H7\xdaXKy5\x8a\xde\xffhL\x18\x19^\xe9\x83\xc7" } internal_nodes: { key: "B1o=" value: "\\|RΉ\xedV\xbd5(\x8e&\xc0p\xbd\xd0U\x1c\xb9\xfa\xd7\xd1cr\xa7Ķē\xbe\xbc\xeaD*" } internal_nodes: { key: "B1w=" value: ">j\x15A\x0c\xd5_\xa4\xf2\xe98\x83\xe2\xc2z4\xee\xc5\r^q\xee8\xac{Z\x18f.z\x82\x00" } internal_nodes: { key: "B24=" value: "B/{\xe7\xdezn+\xb6\xafb\xc8ۍ\x97\xf8x\x03\xd9u\x1f\xb4\x91\xa5Y·o@\x05\xe65" } internal_nodes: { key: "B2A=" value: "2\x9d\xf5w\xdd\"\xe4$KB\x9f\x8f\xdfB6\xe3H\x93\xefZ;z\xffK\x06H\xe4q\xed\xf4\x9e\xb3" } internal_nodes: { key: "B2I=" value: "\x9e\x11\x10\xf0\xbb\x8e\xed\xe6\x88\xebEbF;\x1d\xe5\x19.\xf1\x93\xd6y\xa0\x83\xc8^X>M{\xe7s" } internal_nodes: { key: "B2Q=" value: "\xf7\xb8\x88\x03\xa4U\xd6\"P\x8f;K9\xf1\x99\xa7\x14trs\xcaipL{\x94@\xe2-\x9c\xef\xc9" } internal_nodes: { key: "B2Y=" value: "\xe7\x155\xa7\x10\x17\t\xaf\xb0{\x86\x8cG\xfe\xb5\xd7U\x1c\xcc\x06\x8f\x15{\xeaÃļ\xb5z\xf6ix\x15" } internal_nodes: { key: "B2g=" value: "\x82\xbf\xaa\xc7\xf4T\xf16\xb6\x1c\xed\"\xa7\xc8'v\xa3\xda\xe7\x94\xe7\x97>\xa36\x1b\xa0CS\xb4x\x97" } internal_nodes: { key: "B2o=" value: "\xb3\x0c=\x1e\x1e\xd4n\x98^w\x85\xeaA\xd1`lx1]\xa1G\x1d\xe5F$dį–Ģ_\x0e\xea" } internal_nodes: { key: "B2w=" value: "\xba\xe4\"\x07\x9a\x02\x98\x9fj\xcd7\xff2\xac\xa7o\xf3\x96zn\xa5z<\xa1J\xc8Ũ“=AĶž" } internal_nodes: { key: "B34=" value: "Nd\xe1\x14C\x81\xa6\xea\x17ŲŽ\xabÍē\x99\xed\xf4\x90sE\xb8o\x16\xf5\xd7ܯGS\x00Ws" } internal_nodes: { key: "B3A=" value: "\xed\x95.\xa3\xfeŅ‘xŲ‹\xb3h\xf5U\xfeF\x02\xfc\xd0q\xe1Xf0\x9b\xb2\x1d\x93\\cY\xc3" } internal_nodes: { key: "B3I=" value: "Ęĸ\xf5\x98\xda\xeet\xc2\"\x1f\x0e1\xae\xe8I\x04\x82\xcd؟%\xe5UA|\xd9S\x96?5ߒ" } internal_nodes: { key: "B3Q=" value: "\x11\xf0\x162\x0b \x13\x1ao\xd6\x02\xe0\x0f8\xa1H\xbaI\xd3c\xa6\xca\xd7D\x88L\"\x03\x9dC>8" } internal_nodes: { key: "B3Y=" value: "\xfc\x9b\xe4\xbbSLoimP\xf6\x80\x9a\x08\xc8i(\xc0\x95\xb9\x92\xe3\x87\xeb\x94d0\x19\xe1T\xcc\xed" } internal_nodes: { key: "B3g=" value: "ߐ\xaf\xc0\x04\x1e\xf9$p#\xab>\x05\xb9\\&\x8d\x83\xdaP^\xc0\x98\xf4o\x06\x97\xe7\xc6\xe3\x9a[" } internal_nodes: { key: "B3o=" value: "#@\x1c*\x9c\xbe5(Ku\x8b\x85-v\xa5\xa7\xb4\\&w\x13\xa5x\xb0\x18cHN(;\xcdW" } internal_nodes: { key: "B3w=" value: "J:\x85\xbdC\xe8\x1d\x13\xd6\xd1\x1f\xbb\xe4\xdf\x00\x10\x844p\xa0\xa8\x83P\r-e\rW\xcf\x1e~\xa3" } internal_nodes: { key: "B44=" value: "\xb1\xe1b\xac~mQ\x1d\xe9\x05\xe9\xe8\xe8\xaf|\xb1p\x84\xfb\x14\r^U\xfb\xa7\x80(\x8a?\xe5\xdf:" } internal_nodes: { key: "B4A=" value: "\xb5B\x18\xb0\xdb\xfb\xe3\x1fP\x8d\xceF\x11\x81\xb0\xdf\xd4]\xa3VrŌ´&~\xbd\xb63\x96x\xc0~" } internal_nodes: { key: "B4I=" value: "\xba⌠\xd6/WC\xf9\xa4\x0c\x0bccn\xec\xad\xcd$͡Ɍ(\x03x\x1da\xc4\xecFd" } internal_nodes: { key: "B4Q=" value: "\xbdo\x0c\xeb\xcenf\x8aF\x10^l\xc1n\xf1\xb6g\x934\xc4\x0fY,>7\x83\x19Dw\x84\xe5\x16" } internal_nodes: { key: "B4Y=" value: "F\xad\xb4T^\x90\xbd\xff\x90d\x0b\xf8\x11\xee\xaf\xe9\x16\x88\xe3\xe5\r\xc9a\x03\xb4יe\xc7Xy\xa0" } internal_nodes: { key: "B4g=" value: "\xd8\x19\x9a\x88x\xf3K\xddv\x08Me\x15\xcc:\x07XN\xea\x83.\x8bF\xb12\xdcס0u]\xc9" } internal_nodes: { key: "B4o=" value: "\xea\xea6\xfb\xcb\xe6\x03\x99؇\xd2\xd2\x03o\x1e\x8e\xa7\xe6\x90)\xb8\x16\xe4\xfe\x1dY'\xbev\xb1C\xbc" } internal_nodes: { key: "B4w=" value: "D\xe3S\xb8\x85d\xcc-(Ī“\x14\xf0\x0e:m\xe9`=\xfe}\x83\xa9\xc1\xf6\xb1\x93V\x07=\xcb\xec" } internal_nodes: { key: "B54=" value: "s\xd6\xe8KT\x0cm\xaeTs*_MD\xb5\xd03D+\x9e\x87\xec\xaf\x03\xa4\xd6)\xa2Z\xe3\xbf\xd5" } internal_nodes: { key: "B5A=" value: "2\x169N\x03?d\\\x80T\"\xfb]\xdf\xf2\xdc\x10y\x15_\xe3Ū‘/5!:\xbd\x84\x8a\x0ca" } internal_nodes: { key: "B5I=" value: "\x87\xd8 \x9a\x8fN\x8a7;\x07k\x12\xb09\x19Ō›\x955\x1c\xcc6\xf4\xd2.\xe5jâĨŒ7J" } internal_nodes: { key: "B5Q=" value: "\x08\x93ؒ\x93\x8am\x9d\xd2\xde$\x82\x94\xc1\xfa\xb7\xd3;\xdb\xf5\xabaÉĸ5r\x8e\xf3\xebo.\\" } internal_nodes: { key: "B5Y=" value: "K3\xdc\xd4\x7f\xd9~-x\xee0bɈXe.\xa6\x8f5\x89Y\xa0\xdf\xc7j\x11\xbd\xc5!\xb5\xf9" } internal_nodes: { key: "B5g=" value: "\xfbÛ\xed]H\xa45\xde\x1dm\xc9\xf8ŅˆÜ\xa2\xf3\xb0\xbd\x18\x10\xb4\x1cL9y\xde\xd1\x1a\x1a" } internal_nodes: { key: "B5o=" value: "K%\x91s\x12\x12\x10\x95E]\"\xad\x04Z>d\xef\xa2\xe8\x9d\xe2\xd5\xda\xdb\x05)\x82\x1f\x1eV\xa0f" } internal_nodes: { key: "B5w=" value: "1\x89eR\xe6\xf2\xff\xc9\xdfn\x83~\xc0G\xb7\xf0v8\x85\x00ڕ\xf2\x03\x97sZ\x9a\xab\xb6\xe1\xf1" } internal_nodes: { key: "B64=" value: "\xad\xd3 \xcd\xcd\xee\"\xfeTW\x00\xc0\x8b\x9d\x1e\x82ds\xec\x1a\x8céĢ*\xe8@\xfc\xc1k\xfeA" } internal_nodes: { key: "B6A=" value: "B\x91\x87O\xdc\xde\x14]\x9b\xd2ƌ'\x1e[\xb5\\&\xc89^Cx1\x8e\xe7×Ĩ\x94i>\xc4" } internal_nodes: { key: "B6I=" value: "\xbeD\x1avNz\x8e4MA5\xfcR\xf9\x1fF\x8d\xc0C\xb3x\x01\xc2\x17S^\x95\x05\xa0\xa1YD" } internal_nodes: { key: "B6Q=" value: "\xb1\xc18\xfbs\xb6{@\x8d\xef\x87\xd4\xfcE\xaa\xa0E\xa7\r\xbf\xe2,rB\xb9\x90Y\x14Ä\x83\x87" } internal_nodes: { key: "B6Y=" value: "\x89\xcf\n\xb2\xbb\xaae\xb4\xd1\x06iZL\x00\xe9JT\xe04Oæēĸ\xc0\t\xbc]\xf9\xeab\x95\xed" } internal_nodes: { key: "B6g=" value: "\xfa6\xde\"\x16tT\x8d \xe1\xee7`\xc9\xee\x8dm^\xb0\xec\xc8\xf7\xd1?0\xa2\xbdqO" } internal_nodes: { key: "B6o=" value: "\xb4\x93\t\x9f\x81(\xe5,m\x05\xbf\xa6\x88\xf1\x0c\x10\x8d\x8b\x81G\x17\x1c\xb4\xb3N\"\xb6\x046\x92\x15\xc9" } internal_nodes: { key: "B6w=" value: ":\x17\x14\xa2\xa2\x05\x01\xfe4\xa0\x9e\x9d\x0b\xbb\xe37\xfe\xd3+\xdb\x0eu\x7f\xac6\xac|\xf6yb.\xef" } internal_nodes: { key: "B74=" value: "u\xf5T\x835%\xa6\xee\xb1\xd3\x02\xf1|SL\xcb\x07\xe3\x80\x055\x9b\xf6\x9d)\x81\xdf\x1d\x1eZ\xa7N" } internal_nodes: { key: "B7A=" value: "BB\x01`\x17\x11\x12\xad\x8c?\xba\x80RS\x96\xef\x9b\xd74\x1d\xbb\xb7\xbb\x02\xf5\xf4M\r_\xc2\x1b\xc8" } internal_nodes: { key: "B7I=" value: "M\xb4\xae\x9c\xff\xb6\x0f\x8fQ)\xe4\xf3\xdf\xcbh\xf2{\xafL\xfa׀\xed3G=\xf3aUs\xcfq" } internal_nodes: { key: "B7Q=" value: "\x8bC\x88)\xcc\xd4\x13=\x1c\xdb\xf7\xbdŌ„\xc3R\x84W\x84\xdb\x03\n\x9c\xa5\r\x06f\xe7\xbbr\x95Z" } internal_nodes: { key: "B7Y=" value: "\xbdB\xc4\x16\xff\xc4j\r#\xac\x105\x85c^\x02bs]\xf8^\xa2\xd6!n\xc6an\xbf\xdc\x0e$" } internal_nodes: { key: "B7g=" value: "`\x18Z\x8607\xdd_\x06\x97SF\x12dw,\x98R\xa4$\x98\xd1\x18\"\xb1<[r(v\x96\x98" } internal_nodes: { key: "B7o=" value: "\xf1\xee\xe5\xb8rzmcWvF\x8a#W\x89\xcf\xf5\x83\x17Y\xcb\x12ŌŧH^+e\xf6\x96\x0cp" } internal_nodes: { key: "B7w=" value: "RV\x14\xec^.פ\x85\xc1ThIC\xff\x10'\x8bXb0\xcf\xc8H\x11^7Bf\x87\xff\x08" } internal_nodes: { key: "B84=" value: "\xf5?},$\xab\x17FØŠ\xfd\x88;\xd0J\xc9s\x06\xe2\xd1\xec\x07\xdc\xf3\xf3w\x7fz`\x0b\x7fv" } internal_nodes: { key: "B8A=" value: "p\x03M\xbd\x87\xab\r\xe4ï\x8f\x0f\xac\x13U\xf3\x1a\x85\x95T\xd1R\xd2t\x95\xc4\xd1\xfc\x99X\xe1\x00" } internal_nodes: { key: "B8I=" value: "\xccL&\x94˲\xdbd\xbek^\xce-\xe3\xd1\x0e:\xb86\x1f\x8d\xcd\xde,\xed\x9c\x07,J{b\x1c" } internal_nodes: { key: "B8Q=" value: "\xed\xd7\xedu\x81|3\x85\x1b\xc1\x13P>4\x9b}a\x1c\x11$\x7f\x92F\x19a\x90~\x05\x92\x99\xb1\xdc" } internal_nodes: { key: "B8Y=" value: "Ô´\xe4R\xde\xe4\x88\xe8Ǝ\x83\r\xaa\x1a\xbe\x1f\xb2\x99!\x8e\xcai~f\x9b\xc3\x18\xe3L\xc2\r\x07\x97Į˜&C\xac\xefV" } internal_nodes: { key: "BBA=" value: "\xf8\xcd \xe2l\xf2\xa8\xa3ؚ\xda\x12͚\x12\xa0\xbaCb\xe4\x05\xde\xc1%O(\x81\x13\x19\xc0\xc9\x0b" } internal_nodes: { key: "BCA=" value: "RF\xff\x08\x15#\xf5^w\xa9\x8e8&t\xb8\x9c\x1a+\xb7l|Ξ\xb3W\x15r\xa6R\xb6\x07\xea" } internal_nodes: { key: "BDA=" value: "FT\xc2Ř\xf8v\"\x93\xe0;\xddjF0\xed\xcd[\x00\xb9zX\x90\xbc\x1d\xb3r~}\x03TY" } internal_nodes: { key: "BEA=" value: "QÛ {~\xf0g=\xa3\x9f\xbc\x96)\x85\x05t\xd6\xe9\xb0H'\x07ZD]x\x10\xbeE\x0c\xd2{" } internal_nodes: { key: "BFA=" value: "\x1b\x8f\x04\x99y\xc7\x0c\xbe\xfc\xael \xd4%tx\x0b<$\xb7\x1f\x90E\xfc\xf6!\x88k\xbc7\xbb\xb5" } internal_nodes: { key: "BGA=" value: "\x9c&\xf7\xfc\xa6\xd8830\x1bO\x06nc$\xd87\x88l\x0b\"\xe9\x10\xfb\xb98\n\x01\xf1\xf2\xea\x13" } internal_nodes: { key: "BHA=" value: "\x84\x14\x17\xa5\xa85\xedã€˛i\xbf?\xea\xacZx?s\xc5/\x14lIZ\"\xdd>0\xcbqC" } internal_nodes: { key: "BIA=" value: "\x96\x8c\xdaN\xdc8\xb3W\xc8j\xbdn{\x8cu\x04`\x92Xi\xe3\xc5m\xfbă$\xe1\xe6\xf5\xa4\xd5" } internal_nodes: { key: "BJA=" value: "\xc2\x0c\x92W\xd9#Kr\xf6\x03\x04\xb7\xe0\xdc\xee'\xabčēƒ\xf9\x99 o'\xe0\x0b\xb3\xba\xceJ\x07" } internal_nodes: { key: "BKA=" value: "\x1aSf\xeb\xbd\xfc~\x05\xc0\x9ctV\x8b<\"}\xa9CG\xe9\x97 k\x92w\x91^\xe9Ƥ~B" } internal_nodes: { key: "BLA=" value: "2\x8872˛\xe3*TxM\xbagw\xc3\xce K\xfb\xe6z8;\xb5\xae}z\x80\xd7U-\xc6" } internal_nodes: { key: "BMA=" value: "0m$\xe3\xd6ߊoЅ\xc7\x1f\xa1_h\x1eQF\x0f\xefQ9i\x95\xf7\x11+\x9bp~\x8a\x91" } internal_nodes: { key: "BNA=" value: "&\x1bj\xbc\x91\xa9\x0f7#\x1e\xde\x0f\x00\x81\x1d\xbfM\xba^C2\xe4\xb0\x15'\xaa\xba\x19R\x1a\x07\xed" } internal_nodes: { key: "BOA=" value: "\xad\xdc%j]\xbd\x8ep\xeb\x9e\xe0,%ZM(6\xfd\x0b\xd5(n'k\x8f\xa8\xa4\x8e\xd97\xcaJ" } internal_nodes: { key: "BPA=" value: "c\x80\x1b\xe4\xe8z\xa9\x8fNP\x88>\x0c\xeb\x97s\xe8\"\xaaI\x9f\xe5t\x05!P\x1dx\xf0g\xd6|" } internal_nodes: { key: "BQA=" value: "\x01d\xda\x05q\xfc\x8f-\x10J\xec\xc0r/\xf4\x91ar\xb0$i\x032\x99\x9eɸ\xea\x1d\xe8\xce+" } internal_nodes: { key: "BQg=" value: "\xc9$\xaeʛ\xf8\xffn\x0f\x8e\xa1I-H\x1aV\xc0g\xf8\xa1l$\x03`$\x08\xfafh\xfc\x80\x01" } internal_nodes: { key: "BRA=" value: "w\x08\xeb\xeb3\xf2\x1b\x97\xd5%\x19}b\x00Lc\x1e;W\xb7?K\xaa~N\x9f\xa1i\xbc\xb8\xd0C" } internal_nodes: { key: "BRg=" value: "\xe9\xde\\y\x17\xdd\xeeL2\x1a\x81U\xe3\xcbb\xf3\x1b&\xabl\x9b\xd3;\xef]Ũžq+\x12\xfbd" } internal_nodes: { key: "BSA=" value: "\xe9\xcd^Ή\x0f\xf3\x9e\x13dt\x08\x06\xfb\x1f\xb4\x83厃\xba\xc9\xf4c\x1a\xb2pUlZ\ru" } internal_nodes: { key: "BSg=" value: "\x05\x83\n\x06\xd0b\xfcKa\xa3\xe2\x99`\x7f\x1b)2\xf2\xd6\xdde\xb0\xfe\x07\x99]\x81\xbe\x9b\x1e\xa9 " } internal_nodes: { key: "BTA=" value: "z\xc4W\x0f\xf1\x87:\xe1t^\xf0\x0f\xd0\x1e,\xddT\xa8\xba`\xac\xb5\x9c\x16J0\xfbp\xc5y\x8f\x0f" } internal_nodes: { key: "BTg=" value: "\xf8\x85\x97\xe1\x1e\xaa\x00)4hR\xcaD\xc4&Ė‹@\xae\xe5\xe6k9.F\x1d\x04\xa1?\x82\r\xbf" } internal_nodes: { key: "BUA=" value: "\x02\xa1\xfds\xfc\xb0\xfe\xc2\xd5\"Piz\xf2\x1c\x7f\xe9z\xce\xca\x1c\x89\x11\x9d\xc2=$%\x9a+!\xa6" } internal_nodes: { key: "BUg=" value: "\xcb_\xbdC\x9f2\x8d\xf5P\xc30V\xff|\xf8\x16\xfc\x8em\x96>\x1ec@\xe9X\x8aŌŽ=\x11\xb0" } internal_nodes: { key: "BVA=" value: "\x85y\x9a\xcb3\x17\xee\x85s\x859_n\x87\xb3q-\x0e\x95:\xd6^m\x80\xa3\xbd^?Y\x90b\xf5" } internal_nodes: { key: "BVg=" value: "\xa6\x91q\x15B\xdbqo\xb6&\xbb\x08\xa2\xaf\t\xf6@\xee\xff/@\xc7\xfd\xf3\"\x16o\x94\x8e\xa2,v" } internal_nodes: { key: "BWA=" value: "F\xa6\xf9\xd1ËŦ\x83\x0e\xd1\x0e\xf7\xd7F\xa9\x9a\x1b\xb7\x89\xe0\x83\x19,\x80\x88\xccŲˇ\xfa\xa0\x02\"_" } internal_nodes: { key: "BWg=" value: "\x8c\xbd\xf9F\rĔ\x96ˡV\x1b-&\x03\x06\xd0\xfeA\x16_z\x11P\xc5\xf28\xc0\xbe\xed\xe8\xbe" } internal_nodes: { key: "BXA=" value: "6\x1c\xd9\x7f\xd6IV]\xbej\x95\xaf.\x81\x8d\x13HU\xedm\xad\x05\xfbM\x16\x93Aײ79\xc9" } internal_nodes: { key: "BXg=" value: "c\x9ea\x00\xf7\xe5\x88vG\r\xbc x\xf9\x87\xee\r\\'D[\xbd00\xb0\xd7\xf4\x19mL\x91\x97" } internal_nodes: { key: "BYA=" value: "?',\xe1\xeb\x1f\x83+\xcdX\x127\xd0,V\x1c;gx\x9f;\xbeB\x12\xe1\xe5\xe1\xc2\xf1{]\xe0" } internal_nodes: { key: "BYg=" value: "I\r\x83\xae\x07\x98\xf0A\x12\x96$&\xfcU\xb2\x04IR\xa5\xff\x9drL\xc8V\xf2(\x9d,]K\x02" } internal_nodes: { key: "BZA=" value: "\tr6b$,aXA\xd9:\x9d\xf9S\x18\x1f7Ҏ\x1d\xd8\xfeu\xec\xe2\xdcá̍\x1b\x16i" } internal_nodes: { key: "BZg=" value: "\xae|\xad]\xc9\xe4\xd4X\xa5\xa8\x08-W\xd45\x18\xeeY\x07(\xe88}\x11=H\xbb\x7fErÆˇ" } internal_nodes: { key: "BaA=" value: "T\xd4\xea\x8d\xfc8\x05B\xc3\x17\x854\x85\xf2\x86\xd7sE\x86\xf9\xbe\x16>\xe5I\xddJ\xe5\xbap\x89\x16" } internal_nodes: { key: "Bag=" value: "\x84\xe2P#\xa4{\xb3U\x00\x99$zi\r\x15K\x13G6B\xb4\xef\xd7\x0e\xaa\xb3\xac_\xae.\xc3s" } internal_nodes: { key: "BbA=" value: "\xf4ˁ\xb2\xc0.%U\xc3]\x94o\xd5k\xf6\x1c\xb4\x95\x96Su\x95\x9c\xf6\xc6N\xbf\xa3+-\x87\x7f" } internal_nodes: { key: "Bbg=" value: "\x86\x990\xa4\x1e\xe9؁\xe03@K|\x88\x04" } internal_nodes: { key: "Bcg=" value: "\xf7\x18\xed\x9bR:\xb9\xa8M\x8c\xaa)$\xb6\xaf\xa6\xf8\xd68MΚ!4\xb092-\xd1R\xb0+" } internal_nodes: { key: "BdA=" value: "\xcc\x05\xcc\xfb\xff\xef\xff\x1b\xd2\xf7\r.\xb8\xfa\xedk\x0eN\x11\x9c\x03\x92\x9c{ 4\x84\x04zÖš1" } internal_nodes: { key: "Bdg=" value: " \xe8\xc9\xc7O\xff-zwÃē\x19\xa6\xef\x0e\x0c\xe7\x97éģ–\xbe]\xf9J/\xab\xa1\xc1\x1b\xef\xf4" } internal_nodes: { key: "BeA=" value: "\x9f\xacf\xe5V\xa9\xc3^\xecV᛾\xc6\x1f0NA\x01\xd7Ug'\xd20\xeb\x9e\xfd:)}\x87" } internal_nodes: { key: "Beg=" value: "\\X\x8d\xa2\xbeŲŧ\xfd\x0b\xd7h\xf4\x14\xa1\"\xfd\xa5\xc0\x04B\xcf\xc3c{\xe6+^\xea\xb6\x1b\xc8\xcc" } internal_nodes: { key: "BfA=" value: "yJn\xa9Z\xb6h\x1d[\x94\xc9\x08\x1a΋>\x12E\xa3\xe2Gb]\x8f\x18i}\x1c\xdc" } internal_nodes: { key: "Bfg=" value: "\x84\xea*\x99pv*\xd4R\x95%\xf8\xf0\xec8\xa1\x9c\xab\x00a\x85FaÛŽ)\x13.@qw\xcb" } internal_nodes: { key: "BgA=" value: "t7w\xa2>\x8a\x9e\xf0]\x92\x83\x8b\xaa\xeb\x8c\xf9uQ#\xb3؞B\xb6\x802\xe3,\xfdJM\xa0" } internal_nodes: { key: "BgQ=" value: "\xe0\x1a\xb6Yw\xa8\x91\xc1\x14\x8c\xb4\x1ed\x8a\xa8\xcc\xf2P\xcb8\x04A\xd81\xb1BŌŋ" } internal_nodes: { key: "BhA=" value: "o!i\xbf+\xd4ꐀ\xb2\xf4J7\x9c\xf6\x80{\xfe\x94\x89\x1e\x90\xcbl\x06\xa3j\xa7%&\x96\x8b" } internal_nodes: { key: "BhQ=" value: "7\xd4aR~\x12\x07'\x13\xf3P\x9eiC\xe4\xdef\x01\x9b)Ėļ\xc0\xfbKG}v\x06\xb2\x08r" } internal_nodes: { key: "Bhg=" value: "\x14\x7f\xb8iExGvg\x97\xc4u\xc4\xde\xde/az:3=W\xd9O\xec\x02\x07pj\xe44\x1d" } internal_nodes: { key: "Bhw=" value: "u\xff\xefÅž?\x8c\xd6TyJ\xf1\x06\xdb\x02\xa8F[\x93\x81\xe0\x99\x91\xee\x1c\x86\x0b\x03\x0f\x9f\x8f\xaf" } internal_nodes: { key: "BiA=" value: "\xba\x82/\xdaX\x9b\xf2o\xf8\xdd\xf3\xf1S\xf0\x1f<\xbeËĢ\x10l\xdaN\xc0\\\x8c(\x9b\xe3\x18\xabA" } internal_nodes: { key: "BiQ=" value: "h\x99e\x9e?8}\xc9\r\xac\xf2 tc\xb3.L\xdc[\xf5\xc9Ѐ$\x03h\x9d=΂<." } internal_nodes: { key: "Big=" value: "7p\xf0j\x163\xbee\x9el\xd6/\x9f\x9e\xa3z\x8d(\x9a'U\xe5\xdc\xeb\xf7\xb7\xb6\xf0d\xee\xee\xef" } internal_nodes: { key: "Biw=" value: "\xa0\xb7\xa0\xa98WLRha\xefN\xc7\x12\xb1X\xb1V\xd3\xd8T\x90\xf1<įļ‚\xd9|\x01\xaf\x16" } internal_nodes: { key: "BjA=" value: "\xa8C/\xd9\xdb\x1b\xe8\x11\x8d\xa4\x1c\xb0\x83`\x92\xe8K\xabq|\x08\xf0q1\x81\x82_@\xdd&\x19k" } internal_nodes: { key: "BjQ=" value: ";8Ў\xde4\x03\x95\x0f\xb3U=\xf6\xf8*\xd0}Oo\xe1\xc7\xfct\r\x81;=\xdc\xe5U+t" } internal_nodes: { key: "Bjg=" value: "X\xac(\"\x03x\xd7KLO1\xe9dȋ\x14\x83,o\x87\x13\x8c\xb9o1\xd3,\x8d\x9c\x181Q" } internal_nodes: { key: "Bjw=" value: "\xbc׌\x95\"Íŋ\xaf\x85]\x08\xdb\x00\x93\x03\xe2PĘģ\\|.N\xd8x\xf6P&7\xd6É´" } internal_nodes: { key: "BkA=" value: "D\xdf\xf9W\x0cc\x1fOj\x0f\xdfok\xf3\x8f\x01S\xfc\xab\xf9\x93\xc0Q\xaa1t\x9cAÕ¤\x08\xa0" } internal_nodes: { key: "BkQ=" value: "3RNq\xdb3\x13=\x07\x89\x89wo^MFZ\x07X\x1d\x92Ņ…]\x9c\x12}\xfbB$\xda\xd4" } internal_nodes: { key: "Bkg=" value: "#\x81+\x9e\x196FGA\xaaS\xf8`Īģ\xb3܌vw\x05\x12\x04\x97\xc1\x9c\x86X\x18\x05\x8bT" } internal_nodes: { key: "Bkw=" value: "\x86\x84\xebc\xd1?x\xccW\xcf\xc5^ؤ\x07\xa0\x8e\x99\xc3\xc9T\x84Lj\x07\xad.\xbc\xc5\x02\x8f!" } internal_nodes: { key: "BlA=" value: "\x123\xa5+\xf8\x1c,\x9b\xca{Éĸ\x80\x9aQ\x9c\x1ffQ\x83\x93\x12\xd5\xc2d\xcb\xe3M\xc7\xd9!x" } internal_nodes: { key: "BlQ=" value: "\xfd\xc1\xef\x16\x00p$q\x97\x0e\xe1\xf6\n\xb0\xb9s\xd15\xe7\x02LM\x14\xc0F\x07\x92<;\xa8\xc1\xb2" } internal_nodes: { key: "Blg=" value: "\xa0\xc9Ԇ\x0b\x86\xea\xe0\xbe\xd2*\xb9/\xa1\x0bM)?\x03\xd9\xddH\x1b\x861\xac\x84\x0f!o\xa4\x15" } internal_nodes: { key: "Blw=" value: "J\xed\xe5$\x967K\x13+\xea\x18\xf8b\x0c\xdcrO\xa7`7\xff\x8b\xdd+\tŨœ\xf0\xe7uJe" } internal_nodes: { key: "BmA=" value: "\x81}\x0eN\x16]\x88\xd3\xc0|\x94\x8f\x10\x8c6\xc66@l\xe8qc/U\xad\xc0P\xacy\xf21\xca" } internal_nodes: { key: "BmQ=" value: "Ô´\xa4)\x14:cPXM\xe2V\xc7W\xeb\xee\xf7%\xbe1Uˏ\xf8\xf4\xedㄏo\x1c\x9b" } internal_nodes: { key: "Bmg=" value: "\xdbeIJT\x07V]`\xe8mG\x00\x18x\x96o\\\xec\xd9*\xff\xd3E\x98\xd8B\x9cD\x07@\x0b" } internal_nodes: { key: "Bmw=" value: "S\xf6\xbe\x10\x99A\x82$\x9e}\xe2\xf6*\xa4\x14\xf9\x8dQA\x16\x92\x8c;LEŌ‘1\x04k\x18\xa6" } internal_nodes: { key: "BnA=" value: "^\x81d\x00L|)\x0c\x9e\xc1G\x11\x92t\xadvz\xa3\xe0\x1fb\xed\x84\x14\xa4\n\xf5ܙ\x13\xdd\x1e" } internal_nodes: { key: "BnQ=" value: "|\xce\xeb$[\xbd\x002" } internal_nodes: { key: "BoA=" value: "?\x8f\xff1b}\x18%r\xf4\xba#\xa6Í K\x81ÆĒ\xda\xdd\x18\x0f\x05>\xe7\xcc-G\x00\xa9!" } internal_nodes: { key: "BoQ=" value: "\xe9\xe48\x88H\xe0~\xab\x94\xa9>\x19{\xb2\xa0\xb3s-\xc8\xea\xb8g7\"\xb7\xc6J\xaf\xd2\xf2c\xb7" } internal_nodes: { key: "Bog=" value: "\xe6\x1b!\xba\xb2\xff\x18u\xae\xa3\xb1\x99k]\xb45\xf4\x03\xda\xf3\xad)\xe7J\n\xbe\x87\xa4\xd6\x08{\xba" } internal_nodes: { key: "Bow=" value: "$\xc5u\xe1\x84\x01\x95\x85=Qt\xf9å ŗ\xf6\xfd\x89\xe7a-\xb6\x07:\xd6o\x91\xa8\xd1j\xbb{" } internal_nodes: { key: "BpA=" value: "\xfd\xbc\x04\x17ËŖ)F}\x93\x9fn;p\xd7?\x9dE\x14\x7f\x00>\xf0\xbe\xa20d\x87Z,Üģ" } internal_nodes: { key: "BpQ=" value: "\xeat&\x87>w\x86\x03\x01\x1a\u0097\xd4\x08\xab,\xde%0\xb1H\xba'\xcd5\r\x97\xf1kL=\x15" } internal_nodes: { key: "Bpg=" value: "0\x1a\xad\xdeF0\x17\xa1Âiy\xf6\n\xb4\xf8Ȓߤ\xa7\x8c\xa4\xfc\n\x14\xbe\xe9\xbb0:\x11" } internal_nodes: { key: "Bpw=" value: "\xe8mĶ™@\xcfE\xf4b\xf7\xbd\xde4\xf1\xdd\xe0\xef킐\x19\x11Ffzh\xd7u\x8b6\x98<" } internal_nodes: { key: "BqA=" value: "t\xb5\xb44\x93\xa2\xab6\xcc]\x00F^\x8e\xf0nΤw#o\xdf.\xfe yf\xf1Dk@\xc9" } internal_nodes: { key: "BqQ=" value: "\xacNTzZORʓ\x11.\xa6g\xcd\xf7\xa7\x9bFY\xb4x}\xf5\xd79\xa7&E\xc8\xdf}\x0f" } internal_nodes: { key: "Bqg=" value: "\xb6\x1b!_\x19\x82f\xce\x02\x8f[v\x15pE\xbc\xc6Z\x1co>8R\xeb\xa0|\xff\t\xffo\x9dT" } internal_nodes: { key: "Bqw=" value: "\xc0\x98v\xf2\x1b\xe1b\xb7*\xc1\xceh\x87|\xc6^\xff\xbe\x03҃\xa7\xb7\x05A\xa0\x9a\xf9\xeb\xd3R\x8c" } internal_nodes: { key: "BrA=" value: "\xea\xf1\xb5+g{\xe0Ú­ l\xa8\xc9S\x1f\x1b\xf7\x16\xfb\x17T\xd0cY\xaa+\x07\x0f\t\x1b\x12C" } internal_nodes: { key: "BrQ=" value: "1\x80įĒ’\xdbL\x18|IÕŠĐ…m\x1d\r\x8b/\x9c\xa9\xaaԒ\xc5r>\x1f;\x8f2\xbe" } internal_nodes: { key: "Brg=" value: "\x88;*\xb4Ãĩ`3C\xec\xd6\xee\xf2\xad\x17\xa0\x0bj\x99C\x82x\xcd^.\xa4\x06\x10̈́t\xa6" } internal_nodes: { key: "Brw=" value: "\xfe%\xa0OJ9/ɒrw#\xb2\x14\xc0\xd3hJ\x9d\xa6\x8a\x99\x93\x9a$\xc7P\x14\x838\xf4\xca" } internal_nodes: { key: "BsA=" value: "7\xc8FæŊ™Vp\x07l=0\xdc\x19?B\x91c\x92\xc7VI\x85F\xed%5$kH\xb4=" } internal_nodes: { key: "BsQ=" value: ".\xa9\x85\xf1\xb7Q>\xff\xd3\x06\x1b:\x06\x8a\xa6\xf0@\xebM\xbfF\xec\x81\xf6\x10\xff`\xd3m\x9c\xe3\xcf" } internal_nodes: { key: "Bsg=" value: "\x8d\xf4\xa0\xec&,\x8efԊ\x8d\xb9\x91\xd8\xda\xc4\xd9\xccgwnsE:\x81\xad\xf7\x8e\xad\xbd \x1f" } internal_nodes: { key: "Bsw=" value: "D\x1f=\xfa\x86*\x16%W>\xce\\+P\xe6a\xaa\x9fhBj*\x00:\xf4\xce&\x87$h\x1b\x98" } internal_nodes: { key: "BtA=" value: "\xeb{y\xb2\x08\xcfoi\x96\xb0v\\\xcf\x1d\xa3W\xacE\xf2\xdc'P\xab\xf2)\"\xc0Vw\xe33\xc9" } internal_nodes: { key: "BtQ=" value: "\x97TpB\xfd\x1aH\xb9$\xf8\xf2\n\xe7\x0b\xfc\xb7\xfd1\xa9z\xbd\x80\xbfm\xc8\xf6Z#\xee\x1d:\xa5" } internal_nodes: { key: "Btg=" value: "i\x1b\x86\x98k\xc9\x1fp\x0f\x16D\x03\x1e\x83Ķļ~8\x04[\xac8@\xae\xe4\xfe\x0e\xb2\nl\t\xe9" } internal_nodes: { key: "Btw=" value: "\xbd\xe0;\x17D\x8a\xbe\xf1 \xfe\xbc\x94\xf7t\n\xcf-\xfc\xcd\xf9\x04\x19\xacL\xfb\xa7\x83\xd4N\xbc\x85\x0f" } internal_nodes: { key: "BuA=" value: "^\xd4W\xed\xd8\xf5#\x9cÃŋj\xcd_n\xb7\x96\xaf\x041~Ah\xb9ˆU\xe8\xd7T\xf2\x05\xfc" } internal_nodes: { key: "BuQ=" value: "\x17of\x95S:8\xa0A\x0e\xba\x14b\xda\x02\xb9\xfa^" } internal_nodes: { key: "Bx4=" value: "Ɂ\xab\x98\xcf^u\xe6Z\x8a\xf1\x0c\xfa\x84\x98˟p\xb8\xc4?\x85] \"X|\x00\xb8\xb8It" } internal_nodes: { key: "BxA=" value: "&\xba\x8a}\xf7\xb8V\x03\xb86\xa8~\xf9-;L,x\xc8\xed\x87\xd3dQ\xbf\xee6\xaa\x9bM\x9a9" } internal_nodes: { key: "BxI=" value: "\xca\x04ڞ\na\xba\n\xbca\xf9\xf4!n&\xfbÔĸ\xb2\xf8\x9b!E\x8aZe\xe5\x1a\xf8\x7f\x83\x82" } internal_nodes: { key: "BxQ=" value: "\xf6N(x2]\xa7\xdc\xe7\xff\xaa\xdd\xeb\x9c\xe3{\x03k\x9b\xfa|\xe9\u009cS\xb7\x04\x1e\x03\xd6\xc0\x10" } internal_nodes: { key: "BxY=" value: "@\xe7<\x01\xbb\xbc\xa3\xd5J\x11^\x8an\xc8H8D\xe1|\xf9K\x84\xc3k\xafq\x17\x98\x98\x1a\xde\t" } internal_nodes: { key: "Bxg=" value: "\xba?3ô„ρ͙\xef\x8a\xcf\xe7 \x91\xa9߆\xda?'5\x82*\x91\x9e\xd5\xc7\xe8\x05\xae\xfe" } internal_nodes: { key: "Bxo=" value: "\xded\xd7>OĶ‘r9\x1a\x17\xf4\xeeVy\x15:S+\xb1\x91\x9b\xd2\xfc\x02\xccq\xca\xf4#Χ" } internal_nodes: { key: "Bxw=" value: "\x1e\xa0G\x1c\xa6\xee0\xabR\x14n\xdaE1\x8b#ju\xd3{Åŗ\xef\xef\x9fHud\xd0\x00\x85\\" } internal_nodes: { key: "By4=" value: "\xe4\xceF\xc67u\xab\t\xc5\x19ܛ\xb8[\x17#\xc7\xc9$X\x0b\xb3\xfa\xa4\xb1Ũ°Ķš\xb9\n?" } internal_nodes: { key: "ByA=" value: "\xf7\x18\xdcj\x8c\x19\x06I\xaa\xaf\x1c\xd8\xe8YW\x98XP\xcd!\xa5Ũ\xcd\xea\xbf\xfe\xaf\x97\x0c\xb6e" } internal_nodes: { key: "ByI=" value: "c\xafv\xf0o\x08\xac\xb0/S\xa7L\xadi\n\xa5N;!Bdc\xa2\x96\x12Nq\xa2\xe3\xdbj\x97" } internal_nodes: { key: "ByQ=" value: "\x83\x8f\x8a\x12\xb4W\xcd]\x9d\x8a\x92\xa5\x97\x85\x0b\x1d\xee:&\x98\xaa\xef\xd1: ÛŊ.\xf6\xa4^\x02" } internal_nodes: { key: "ByY=" value: "\x0cy\x05\x1daq\xdc\xc0Z\xc3\xd2 \x88\xa2\xbc\xdfh\x8d\x1c\xf8\x85\x986\x9d(\x18\x18&-}K\x04" } internal_nodes: { key: "Byg=" value: "\xa9\xbd&\xac\x88Ėą\xa1Įž\xfd\x85\xf4L[\xf7\xc0\xed\xec\xb1G\xe0\xe0\x937@[u\x83gp\xd3" } internal_nodes: { key: "Byo=" value: "\xddf\x91W(6D\x16d\x7f\x83\xd2\x18Z\xe1\x12\xbc#\xab\x0fdo\xfc5V\xc7r\x07\x9e;\xaf3" } internal_nodes: { key: "Byw=" value: "ˍ]\xe9o~\x85\xa8Ņš\x9fmJu\x100ڈ\x9d\x1efGb\x8d\x86\xc2\xf0\xc8\"\xfa\x92\xe1" } internal_nodes: { key: "Bz4=" value: "Q\x14Iv\xbe\xbat26\x14\xfa\xfc;\xa6e\x87!\xa7'\xcct\x02o\x83\xa4\xf7\xa4\xbb\xfa\xb2\x11\x9a" } internal_nodes: { key: "BzA=" value: "\xf3\x91CFTĶž3\x01\xa6\xdd>K\x19p\xe7]\xe1\x1d\xb4\x92\xaa\x7fg\xb4\xe4\xa0\xcf\xff,\xe9K" } internal_nodes: { key: "BzI=" value: "\xd8\xe2\xe7\x1f\xca\x0c\t\xc6[\xbc_=\x1cX\xfe\xd68>\x8aX\xad)\x82(QŞ\xc7\xf4\x8co\"" } internal_nodes: { key: "BzQ=" value: "C\xa9\xf4o߲\xa7\xce\x1c\xa3\x9fq]wDf\xf5u\x99\xf5\x99\xe5\x1d7d" } internal_node_count: 254 prefix: "\x00\x00\x00\x00\x00\x00\x01" depth: 8 leaves: { key: "CA0=" value: "\xd4K\xee\xf0q\xf4\x88\xdf\xdc\x1f\xc8\xd4G\xb2%\x0b\xdb\xc5\xf8\x83qxB\xf2\x08\n\xf0\x0e\xad:X\xdc" } leaves: { key: "CA4=" value: "\x94\x1a\\\xb3\\S\xf4\x91ԟ\xf1>5\xa9a~\x98R9\xfev#\xb7\xcd\x19\xb5}KnPmS" } leaves: { key: "CA8=" value: "\xa1Ņž\xc00XA\x89k\xcc\xca\x0e\xf9\xf3\xc2éĢ‘X\x82\xfd\x05\xab\x9f_\x9aw\xb5\xc1\xf1\x0f\xff" } leaves: { key: "CAA=" value: "\x1f\x14\xa6ŅĨ\x82j\x9c\xa9\x00\\\xf4\xa2\x14E\x13z\x9b\x92(q\x82\x06-\x87\x84\xf9Ôĩ#\x16\x0b" } leaves: { key: "CAE=" value: "\x80\xfe\xbaC\xae\xe8\xa2F\"!oN!\x9b\xfc]\xd5-O\x91Ôž\x9fL\xf81\xdc\xfe,\xea(\x80" } leaves: { key: "CAI=" value: "!\x89\xa1\x13#\xa20<\xe7\xad\xf3\xf8\xe9\x10)\x17\xbcĖ­\xa2\x8ar\xffj\x1e\xf0c^\xf3\x81\x0f\xdd" } leaves: { key: "CAM=" value: "\x0fI\xc3\xf1;y\xe5G \xf4\x1c\x9d\x97d\x91j×ē\xb0D8\xc0`\nیb\xef,V>[" } leaves: { key: "CAQ=" value: "\xe2\x01\xde\xc8k\x14\x0f+>\x0cM\xc9\xf8\x1b\x0bS\xa6\x7f#&2\x07\r\x92\xc2_\xd5C\xdb᠝" } leaves: { key: "CAU=" value: "n\x9a\x9b\x17\x17垔1\x96\xb1Z\xa0\x8c\xe7\x10\xbak\xbb\x89È\xd3\xf4=g\xa4\xf6\x83\xda\xc48" } leaves: { key: "CAY=" value: "\xe4\xf1\xb2=\xb0aH\xfa\x84\xad\xfcg\xb8P\xcb\x08\"!؀\xadIe\xc3(\x89R\xaa\xa5\x92\x1d " } leaves: { key: "CAc=" value: "\xff\xc3\xdfJ\xbeI\xde\x1fāχQ\xf0\x94\xa8hv\x9a\xb6\x94\x85r\x16\x93 &M\xad\xf0\xbd\x80#" } leaves: { key: "CAg=" value: "\"\x90I\xb9j\xcf#t\xee\x8c\xe4Αw2M\xfd\x8cK\xa9m\xb1)4\xc7A\xfaЅ\x80`\xec" } leaves: { key: "CAk=" value: "\xe384\xf6/$hD\x06\xe8\x1f\xbcpa\x05\xefS\x1f\xdc؞\x95K\xab\x13\x9880l\x04B\x83" } leaves: { key: "CAo=" value: "\xd6\xf1\xf4\x80$^\xe1Y\xf0æ‹Ĩ\x9fJ\x17\xea\x18\x0f\x97\xdb\xd17\xde:t\xd2\x16fPXˈ" } leaves: { key: "CAs=" value: "\x9f\xa2\x93\x91\x8bZ\xf2\x0e\xd7{\x14\x84\xde\xec`\xf9\xa0\xd6H\xeb\x87,\xb1\xc1\x96KH:\xd8V\xe8\x07" } leaves: { key: "CAw=" value: "\x0cG\xac%61\x96\x15\x13P4\xbb\x88\xb2\x8a\x92Ū‚\xa4.[c\xc0\xab\xc8\xd5\x00\x9e9\xb8\xac\xfc" } leaves: { key: "CB0=" value: "\xab\x8c\x1c\x0b\x1b\xebZ+\xa5\x17\x90\xf7\xa9\xab\xb2\xd29-\xa6ĮĒBp\xeak\xac\x04\xcf\\\x02\xd1y" } leaves: { key: "CB4=" value: "\x12\x1f\x96J%\x14\x1b\xabme)\xfa\xfcڃ\x93\xbd\xd2\xde\x1d&.\xc8\xda\xfa\xfd\x97d%\x12>\xb4" } leaves: { key: "CB8=" value: "v\x7f\xb7\xc3~?\x97\x13Ce\xe2-:2\x05\x97~\x8esB\xc4B\x15\xda\x0f\xd6Sh0\x962e" } leaves: { key: "CBA=" value: "?\x18\xa1\xc2Į‚\xf1\xd8+/\xe3\xf8\x06\x95\xd0g\x848\x08\x1e\x8c\x13\x1f\xc5\x14x\x88\x06)\x84\x19\xf9" } leaves: { key: "CBE=" value: "\xd0\xf1Ú­\x85\xd6\xf7\xeaIp{MDu*l\xa9\x16Øĸ\x7f\xdc\xd1Z+\xd2\x15 \xc6i\x85\x1a" } leaves: { key: "CBI=" value: "5\xf6\x17\xf6x#}\x82\xfc\xf7t\x95I\xed%\x93D\xb14\xb9\x9av<\xc7&\xf8]\x9c\xc4\xf5\xb4\x1d" } leaves: { key: "CBM=" value: "\xee=\xce\xfdH\x83\x1c\xfdLh3k-\x0c \x97\xeb2{+\x97;\xd5r\xf3\x1f\xfa\x0f\x9f@+\xfb" } leaves: { key: "CBQ=" value: ";\xad\x96\xc9K/\xdb\x0f\npc-e\x8b~v\x16\x8e\xc4\x00\xf4g\xb4!x\xab\xfd\xa2\xf1\x9a[\xd6" } leaves: { key: "CBU=" value: "\x88\xdb\xc0\xe8\xa1\xd1\xe0B\xe6.\x82.\x80\xed\x82Y\xbcԄM\x19\xd8\xf6\xe6\xc2\x06r\xd2\xfc%\x8e\xff" } leaves: { key: "CBY=" value: "\x16)\xb5\xb9\x1aT\xa2 \xfc\xf8\xdf#Kf\x8e$\xa8\x82\xbe\t\xad\x9c\xc0\xac\x8e\x1b\x9f9r\xb1\xca)" } leaves: { key: "CBc=" value: "\xd2Y\xfe\xb9An\xdb\x16\xdd\tQ\x94\xfaqo,]M>\\3q\xf4!\x97E%ĤdH\xd1" } leaves: { key: "CBg=" value: "\x1a\xd0RR'\x1a#\xdf\x14|ÉŠ\xe8\xc1\xb7\xb2ÎŽ\xdf\xc3bXr\x17q\x16^ZT\xdfz\xa1" } leaves: { key: "CBk=" value: "\xa4H\r!\"\x10jY\x16\xd5\xd1@\xcc\r\x83\xa6v\x17\xf5}\xba\xb6|Ũ—0.\xf7\xff\x17\xa1\xc2" } leaves: { key: "CBo=" value: "!\xba_>\x85\x174^;t\xb3\xfe]\xfc\x1du>9\x83\xc0\xe74\xff<\xe3!*\xfc\xd0\xdf\xf0\xf2" } leaves: { key: "CBs=" value: "\x15\x95\x82\xe2Øŧ\xf3\xf3q\xd4\xf4\xc8\xf3\xa6\x10\x13\xa1g^!\xaa\"j\xdev\x9ag\x9fONq\x1e" } leaves: { key: "CBw=" value: "\xdeb\xbd\xe3v\xc62\r\x8f-\x00J]\x9fCT'[\xc9u\xdf\x08%\x14\x0e\x03Kƒ\x00\xf2\t" } leaves: { key: "CC0=" value: "\x86\xac\xf1ĮD\x13\xe03\xa7ĘŖ\xf4\x93\x902\xba\x16\xed6!AS\x18i\x08\xf6\xcc\xc5\xee\x10\xbb" } leaves: { key: "CC4=" value: "\t\x80\x07\x0c\xba\xef\r\xea\xb55\x02e*\xd6䖗V\xf0\xb9\xe1co'\xd0\xc3\xebĐĄ\xa2d&" } leaves: { key: "CC8=" value: "OF\xa15\x84S錰\xa45\x19\xb265\x99\x87J\xc9\x1aג\xb6\x02>f[U\x82\xc5]\x80" } leaves: { key: "CCA=" value: "h\x87\x92иŲŊZ\x18\xbbM!Q\x95\xdeAķ…“Ŋ\x02p\x14m\\\x86I\xd2(1\x9d\\" } leaves: { key: "CCE=" value: "\x83\xb9\xcf3l\x8b\xcab\xcd\xcb'\x03\x11g\xa7\xa5\xa4S\xaf\x96ä¸Ĩ\xb8\xe1\x94\x10\xd9:`\x8b\xa9" } leaves: { key: "CCI=" value: "\xa1w\xff8\xccs;\tf؈\x16W\xab\t\xfdh\x8b\xdb>\xfb\xce\xfa\xe3d!\x04\xeb\xa5\xed\xb2\x0b" } leaves: { key: "CCM=" value: "J\xa3\x9d7V\xa8\xc8\xc0\xe53Ė—\xbd\x9dh\x01\xe5\xea\nt?\x97C\x80xKr\xa1<\x99\xf0\x97" } leaves: { key: "CCQ=" value: "\x02:N\xf0\xd0\x17\x96\xb9\x95H\x1d.\xfd\xf7:\x97\ra\x9b2ߖ\x1c\x9d\x1afuM\x88Xj\x92" } leaves: { key: "CCU=" value: "\xbe\x1c\xad\xf9z\x1a\x93\xe1\xefh\xee\x1d\xff\x11*\xa0h\xf6%\x97H.\x0cD\xe1fN\\\x19\x89\x85i" } leaves: { key: "CCY=" value: "u4\x8f\x04\xd7\xecH\xbf\x91\xf7Ũą\x1c\x8ey\x9d\x14\x07rĊR\xd3\xcc=wÄēQ\xf1\x08\x00" } leaves: { key: "CCc=" value: "\x13\x1c\xdc.\xf82ĖĨ_\xe3\x96X\xd9\xfeÍž\x90\x83<\xb1\x02\xf8\x1d\x12\xedT\x92\x08\xc9dĮ“" } leaves: { key: "CCg=" value: "y\x9a{\xa69\x1f[\xf9\x8aEb\xc4 Gx\x98&Q\x89ËĄ\xaey\x16:\t\xf6\x8a\x86\xa5\x18\xe9" } leaves: { key: "CCk=" value: "\x92\xf8so\x7f/\xb2\xbeéĩ•mO@(\xb2\xd4!r\xf1S\xe6\t\xf4\xc0\x7f|\x1e\x94\x10_=" } leaves: { key: "CCo=" value: "ĮĢ\xce>\x0f\xee\xb2W^\x01\xc2k\xa8\xe6\xd0,\xf5\x03\x15\xf1ėĻĨk\xc9\xc3\x1c\xbbQ|\x0ei" } leaves: { key: "CCs=" value: "\n.\r\xb8\x15I\xe5Ō˛\xaf\xeaG\x1e\x06\xf4*Z\xb2ΎҌ\x1b\xf4\x13\xed\xf2]\x01M\xaaa" } leaves: { key: "CCw=" value: "\xadÆĩBR\x0b\rAg\xd9,\x1e[\xceH|\x1e(4\xb3\x7fbo5\xa2\xc4\xdeE\xa1\x81\x0f(" } leaves: { key: "CD0=" value: "\xbb\xf7\x86\x92\x8aP\x11\xe6\xf6\xf1k\xc9\xdb\x10eð\xe0\xb7\x16Q\xc4\xf8\xb2\r\xf3A\xb3\x02\x89\xdb\xd6" } leaves: { key: "CD4=" value: "\xec\xf0y\xb6\xef\xc9s'\xf72\x95\x8b\x068\xb4\xdb&[\x9e\x12ƈJ\x93Db\x06Ო\xaaA" } leaves: { key: "CD8=" value: "]N>\x9f,\xa5\xdc\xf0\x9dB\xc7'\x8b\xa4\xecs\xfc\xf1\xf5\x0fT*\xe7\xc3\"\\\xd4ħ=rq" } leaves: { key: "CDA=" value: "͏\x80\x14\xba\xa2;V\x17×Ļ\x04s\x9b\xe0\xa3\x0f\xc7?\x08(\xb9}\x1fd\xfa1q\xb4\xf3\x88\x1b" } leaves: { key: "CDE=" value: "O\xdd]\x81\xf2\xe3\"\x02\x1c\x07\x9e}UE^\r\x00OK\xa0\xb9\xe3\xa4\xcdxJ\xdfn\x15\x11)\xcb" } leaves: { key: "CDI=" value: "\xbc\xf0\x0f\xb20\x03;j\xa2\xcd\x05\xa0:\xbd\xbe\xb9o5\x0e*8\xe9\x80|\x96P\xa3`\xc7\xc0 \xfb" } leaves: { key: "CDM=" value: "\xf9\xfa\x8d\xe8_\x8b\xaek\xed\x16Mf\r\xe5\x9e\xd8\xc0\xedD\xd7\xfc\xcd\xe6\xd5T\x81\x95\xafl\xba\xec\xfd" } leaves: { key: "CDQ=" value: "\xb0N\xa1\xcf\x02\xbe\x1a\xb6\xb0\x83\x17GW\xce\x04\xc6\xdf4ßĒĘĩ\xc5\xe2\x02\x105\xfe\xe2g\xf9\xab" } leaves: { key: "CDU=" value: "Վr>Ε\xcevʰ\xf1\xe3\xb2͘U[\r\xa7\xadw\x89\x87\xe0\xe0\x03\x10\x85\x88\xe5\xff\x9d" } leaves: { key: "CDY=" value: "\x14b\xfc\xe50'!w\xbb!\xf6\x16'i\xe1\xb0BՎ+G\xf6\xfc\xc9\xf21\x0c\xb1\xb0\x84\x95N" } leaves: { key: "CDc=" value: "\xf6xb\xfe\x12\xe4\xd3\x16\x8br\xdd(\xad'o[($`\x0e'Sq=B\xc7\x0b-\xd0\x07\xf1\xf5" } leaves: { key: "CDg=" value: "\xcf\xf80\xa7-^`\xfe\xd8@\xfb\xe8\x04\xb5z5\xe8~΃.\x96\x86;e\xe2R\x05\xd3X\x83\x12" } leaves: { key: "CDk=" value: "\x19\xe2c\x95MB\x12\t\xa9F1\xcaBp\xae\x86\x8c\t\xfb6ƃF\xe5K\xec\xea\xb36\xfbZ\xf7" } leaves: { key: "CDo=" value: "\x1e\x02n\x10;)\xe68#UR\x99\x825\xfe\xb4b\xd8=\t\x060\x17\xecM\xa2\x12\x037\xc2SQ" } leaves: { key: "CDs=" value: "\x18\xab\xff&\xf3?\"\x89\xc7Y\x90\xf3\xe6\xa4Xm\xb8\xd5\xc3\\\xc5\xf7\xe2c\xd7\x1f9\xa0\"\x18zS" } leaves: { key: "CDw=" value: "\xf2\x84\"\x02Z\x91_efv\xc0:\xfc1\xb5E\x11\"\x08,\xb1\xd0\xf3\xb2gqĮš/7\xaa\xdf" } leaves: { key: "CE0=" value: "|\xe29\x1c\"\xe8\x8d\\\x8b39e\xb8I\x0fNW\xcd6~j\x13b\xa0\x93P\"ᙓUV" } leaves: { key: "CE4=" value: "\xbf\xecF\x90\n\x88+\xf0\xb8?x/\x87\xdeAi\xca" } leaves: { key: "CEs=" value: "\xd4\x14\xc9\xf2}\xfa\xb9.\x19\xc3\x1e\xce\xed\xe8pėĢ„ÉĒJ2\xb4 Åļv\x16\xacAt*" } leaves: { key: "CEw=" value: "Ûˇ\xd4\x1e5k\x01OL\x00Zir:\xe48J\xa1\xf2\x1f\xb4}\xeb\xc8k\\\xd5\xc6KĖ\xd6" } leaves: { key: "CF0=" value: "\x97\xcea\xf7Z0c]f\xdf==Y\xd8\xf9\\\xb0_]\xba\xee\x9a\xf3.&Ut\xbf\xed\x1f\x10o" } leaves: { key: "CF4=" value: "#\"qЊ\xee\x90\xc0'9\xa3\x91]-\xda+\xe5\xefL\x98Îŋ~P\x8c\x1a\xe2\x06\x15Ôĩ\xf1" } leaves: { key: "CF8=" value: "\xc8\xc4q}\xbd\x8b7\x99t\x8c.\xb4\x98R$\x112\x89\xab\xb5\x82~\xad\xa8\xf0&F\x96\x95\xb0\t\xf3" } leaves: { key: "CFA=" value: "q\x01\xae\x0fb\xa6[c\xe1i\x17\x93\xdbO>i\xaeĖš\xb1\x98B\x92\xdc6y\x8b{\xb4V\x11\x11" } leaves: { key: "CFE=" value: "\x08\r}|Q\xb8PáŽļ\xfe\x12I<\xaeֈ\xe0\x06\xc6T\xf8\x02\x19\x02\xf2~\xf5\xe9+'\xf5" } leaves: { key: "CFI=" value: "\x14#\xfc\xe9>[\xc07\xf7\xad#p\x1f\xff\xf2L;\tW\x87F\x97\xec\xf6\xaaY\xa7G\x10\xb6\x17Q" } leaves: { key: "CFM=" value: "Øŋ\xfahw\xcb\xe2Wb\xa9\\\xb7F\x86\xb4\xc8N\xdf\xcfx\x8f1\xb1\xd0\xe86k\x8f\xbe\xce\xd6\xdf" } leaves: { key: "CFQ=" value: "ÍĻŨ§~\xces\xfb\xc4()\x9f\xa7\x97\xc9\xc6|4k\x8fe3\x9c\xab\xd2\\\xaa#\x07_\x1c\xe1" } leaves: { key: "CFU=" value: "\x1fG\xeb\xffwu\x01\x0c\xb4\x10\xc1\x86\xc9H>\x8eJ\xe1\x14\xc1\x0fz\xba\x0c|a\xe7\xd1&V\x80\xc8" } leaves: { key: "CFY=" value: "=)\xd9n\x95\xc0>\xd6\xe0\xfaz\xe1\xf1\x01z\xf7Åĸ\xa5R\x1fd|\xf2\xaes\x99j\xd3=dF" } leaves: { key: "CFc=" value: "9\xf9\xd4ÎŦho+X\xa3_#\xa7\x1a\xc7\x10\xf9J4\x15>4\xc3w\x8c\xdc\xfe\xb8\xa5M\xf5\x0b" } leaves: { key: "CFg=" value: "\xbdI\x03Sf\xdaS\x97U\xd7\xfc?^\xb9\xe1Jx\xbe\x06q\xdb\xeb\xf0M\xf8\x0c\xe5\xf6c\xa3\t\xc6" } leaves: { key: "CFk=" value: "\xdab\x0f'Z\x81\xa4AC\x1c\xff\x17\xdd\xc7C\xfe\xaa\xa2\t\x97\xdb3;\xa9\xa6\xc1\xfaJ\xf87-%" } leaves: { key: "CFo=" value: "0(\xc23\xc4\xf5ð?\x9f\xa3E\xed\xa8\x04~\xcf\xd0\x06\xf9z\x80\xcdJ\xa3\x9em}\x89\xa5\xf0k" } leaves: { key: "CFs=" value: "]\xde\xd4\x03\xcc\xccCN\"9\x1e\x0c~<ÃĢ\x02\xfc\x98\xaey\xfc\xb4 \xcb{\x92j\xb2\xfai\x1c" } leaves: { key: "CFw=" value: "e\xe9\xafd\x07`\xda\xc6&\x8e\xc1\x9e\x0cV\xed\x02\xffl\xd1\xd8\xd5>\x9c\x8c9\xa6\xda^ut]\xe6" } leaves: { key: "CG0=" value: "\xee\xf1\xcb\xf8\x9e\x93\xd4^\"\xc4\xdf\xc1\x1e#\xdf\xef\xc6;\xa3\x18\xdfiy\xa9\x13\xaf\xddFfn\xab\x0f" } leaves: { key: "CG4=" value: "t%^\x9d\xeec_8\x96s\x05o\xfb\t\xc8{\xae%*ŅĒ^ãšģ\x8a\x06q\xf4dy\x8b" } leaves: { key: "CG8=" value: "\x11\xe6\xf4\xb4\xb1\x98\xecz\xab\xaaqM\xa5q\x0c\xdf:\xee\x1f\xcb\x1ccRj\x9bC\xce\x12\xfff\xca%" } leaves: { key: "CGA=" value: "\xac\xb4r\x12j\x12\\[Q*d\\\xb3\xaem×ģ;poI\xfe^\x00\x02\xcf\xe2jn~\x98)" } leaves: { key: "CGE=" value: "\x97%9\xbc\xcfŪ™\x17\x106\xfc\xf4ß´\xc8\x07\xef\xcd\xe5\xd6\x04\xb8\x14s\x87\xf3\xfa\xd2+\xf0\r\xa6" } leaves: { key: "CGI=" value: "AY\x15P\xe8.\x0f\xa2+\xd8\" .\xe0\x9d\xeb\x0fx1\t\x9c\x19\xd0?\x10\x92i\xd0n\x08\x1a\xaa" } leaves: { key: "CGM=" value: "}p\x8b\xb8\xac\x15\x80W\xc0\x8ct\x1eX\n\xd4o\xd8>\x80\xd4\xde\xfd\x8e\xda\x16\x98\xa2\xda\xf9\xa2I\x14" } leaves: { key: "CGQ=" value: ">T\x90q\x14\x8a\r\xdf\x1aO.\xf3ЄZ\xa31B\xf8J+\xf9\xbd\x18\xbc\xc3\xf3Úˇ\xa8\xf2\xe8" } leaves: { key: "CGU=" value: "\xb0O\x0fC\xf0t\xae\x1d6\x06\xd7\xda\xc2\xfc\x87\xb3{A\xfdQ\xbd\xafu\xd60\x9a\x1a\xadv\x1f\xa0M" } leaves: { key: "CGY=" value: "9E1\xc9&I;\xe1\xbe\xf1\xed\x0f@\xad\x977\x9d(k\xd9\x15\x0f\xda\xe4\xe5U\xdc\x15OČŦ\xe4" } leaves: { key: "CGc=" value: "A=\xde\xdf|v-\xe1Q\xf5\xbd<*c\xa3\xf5\xe2v_pl\x08\x9a\xa9\xeb(\x98\xb8\xaf\xb1K\x9e" } leaves: { key: "CGg=" value: "\xaa\xe0\xa3\xef]p\xdb\x0b\x01\x91$z\xc4\x1e\x15[\x94\xb9)t\xa5\xd7Q.\x8d\xed\xa77\x0bQ\x93\x02" } leaves: { key: "CGk=" value: "8V6dSC\xebs\xdcQE\xf6\xec\xea\xf4z" } leaves: { key: "CGo=" value: "^\\\x9c\x19\xfeI\x8a\xdap\xbd\x1b=\xcct\xc7z\xa53f\xdfD[[\xc1M0\xb0\x10\xbe/\xc1e" } leaves: { key: "CGs=" value: "\x8f\xbe\xb43\xe7$\x1d\x02\xb0^\x93\x7f\xe8/\xe90\x83z.\xa5\x0c\xdc4zn\xe7\xd6\x1e.qkD" } leaves: { key: "CGw=" value: "\xc6\x07+\x8dĶ€m\x01\xa7\x85\xf5\xc5\x12\xf6\x8es\x11\x1dn\x15\x12\x0c@\xba\xf6\x87h\x14\x1b\xf7\x1c\xa3" } leaves: { key: "CH0=" value: "\xec\xce\xc6@U\xc7\x05S\x95\x0b\xb0\x1e9\xb6\xd4Z\xa0pWq\x81\xc0R\xfc\xc2#\xe9Ÿd\xb1Y" } leaves: { key: "CH4=" value: "[f\x99\xbb1\xfa\x1e\x8a\xbc\\v\x93/M\x1f\x84G\x17Uq\x89[C~\xbe\x82\x9e\xe2A\xf3\xfc|" } leaves: { key: "CH8=" value: "ßŗ\x0c\xf1\xca\xd1\xe1\xef\x8e\x7f\x9f\xe4\xe2\xee\xc7NnI\x7f\xa2S\xbfa\xaa=\xd6D\xd0@#\xa3}" } leaves: { key: "CHA=" value: "\x8b䋒\x91%\x02v\n\xd0v\xa9\x08\x11\x8f\x8f_\xf3I^M\xc4\xdcOƞ\x7f+\x8c\x8c\xbf3" } leaves: { key: "CHE=" value: "%f\xe3b;\xa5\x08\xf5\xc3R\xf7h\xeb\xca- \xcc(\xbaeg+!\x05\x95\x8e\xb5g\xcb\x17\x0c\x97" } leaves: { key: "CHI=" value: "i\x16O\x85\x84{O\xf4!\xe7\xc5o\xb0-\x08y\x80\xdb\xd5\xf5\x15\xf1\xbd?\x01^į ­\x1b\xfbZ" } leaves: { key: "CHM=" value: "\xbb\xc1\x88\xa9\xfeCB$\x96\x88i\x81\xe7pԃ\xeb\xd4\xdem\xa5\xf9\xef\x14+\x93y\xa4\xfc;\xc5u" } leaves: { key: "CHQ=" value: "^\xb8\xf8\xfa(\xa0Z\xffO\xc2W\xd5Wۗ\xc9\"\xd1\xe9a\xfb\xad@1\xa9r\xc1J:\xb2\xfa\x99" } leaves: { key: "CHU=" value: "R\xa3r\xf5M.VÜŦ\xd4\\\x9cQi\xc7\x1f0B\x1f\xd4uZ\xad\tĘ\xaa>uuy4" } leaves: { key: "CHY=" value: "\xf6\xd2fÅŖ>\x9c=\x90\xcb\x08\xf9\xaf\xd2\xf2\xd3\x07\xc8P_\x91\u0092G\x8bV\xd0Zꍀ\xe0" } leaves: { key: "CHc=" value: "Ar-\xbb\xf5\xa5\xe5\x1a\xf7\xb2\x8eV\xa5\x0e\xe6\x92ȁ\xb6<\xb4\xd2\x04\x96\x07\xab'\xb85| \x9a" } leaves: { key: "CHg=" value: "\x0fҏ\x07?a>=\x17\xc14U.\x08`%ՓN7\xa7\xeaR\x81-t<\xd1\xfe+@\x0e" } leaves: { key: "CHk=" value: "\xa9\xd5\xdd\x1d[;k\x910\x81\xa1\x1f\x99\x83\xc5\xc34\x9e\xe49~\xff\xd0e\xef" } leaves: { key: "CI0=" value: "ï7\xfe\xb3\xe3\xee\xd9`x\xf52\xf3\xe71@\x05\x17a\xee,\xdf\xe1ij0\x9c\xe5\x0c\xe4\x96\x03" } leaves: { key: "CI4=" value: "O\x15\x0c\xf6\xa0\x14\xbb\x12+\x9b\x96\xf8,to܌^O9\xff$Q!\xbf\xac\xa4\xe3\x05\x0b\xb5\xd5" } leaves: { key: "CI8=" value: "\xe3n\xebt\x0et\x03\xbe\xf6g:\xe4f\xb9͈poY\x81s\xc0:\x1f\x92\x1d\x10]\xd7\xc6mp" } leaves: { key: "CIA=" value: "\xf1\xfe\x043\xa5V\x9eOy\x80Iz\xe7\xd5Gn\xec$\x0cR\x1a\xc1d\x05\xf4c\xdd\xc4\x0bɇ{" } leaves: { key: "CIE=" value: "\xb9\x87\xd2\xed\x08\xe7\xcdUe\xce{\x91zo\x80\x91g\xf0׸\xab\xf7\x10b\x9c\xc5\x0c]\x07\xec\xf6\xbf" } leaves: { key: "CII=" value: "1\xa4\x9fm|\xad\x0f\xca\xf1>\x7f\x10\x9a\xaa\x94\x8deIn\xc5\xd2\x11\xd5\xcc\x0c\xdf\xc5d\xc9x\x01\xa7" } leaves: { key: "CIM=" value: "-S\"\xd1\xf2# \xd5\xc0\x90\x8fP\xd1\x15O\xa0\x941\xe4\xf0,b@M\x103Ōģt\x02SO" } leaves: { key: "CIQ=" value: "u\x89\x1ax7Ř;=\xc7G\x03\xac\xc7\xd0\xfe2\x87\xb6\x8b\xa6U\xe9\x11\x84\\\xe3\xf1\xd9%\t\xa0" } leaves: { key: "CIU=" value: "\xcbX\xf6\xa0 d;>\xc93ş\xc2AUb\x14\xa8,\xcb\xd1\xc5ÄŖ\xd6\xfc%\xf04\x8b\x88\x8e" } leaves: { key: "CIY=" value: "&aLQI\x15)'L\x92\xe9\xce)\xc4\xec\xeb\xa1\xe8\x18\x97\xd5mr\xee\xa8\x04\xa3\xa7\x13\x18\x1c1" } leaves: { key: "CIc=" value: "H\xf9Y\xf9xJ\xee\x17\xd2sīŠ¤&\xbe\x80֛\x80\x07\x14m\x05H\xa3+\xc2yy\x1a{\x12" } leaves: { key: "CIg=" value: "p\xe3\x84>q\xf9j\xa7\xf0\x1d1" } leaves: { key: "CKM=" value: "\x0b\x81`~G\x06\xd4>\xb4\x95\xac{\x85\x00\xfe\xcc\xedQ\x84\x1f\xf8ߥ\x98\x1b\n&Bl|\xd4." } leaves: { key: "CKQ=" value: "\xa7[\x0b\xa8\xec\x13\x08\xbci7\xf3\xab\xb8\xd5\xfc\xd0\x1cW\x19\xc4i\xafp\x06\xd3\xc6Hh\xb7Įƒv" } leaves: { key: "CKU=" value: "\\\xf2j\xfe\xc4i\xd0į‘n\x98\xd5YK$\x15:p$\x7f\xd8\xc8\xe0\x82R\xef\x85\x7f\xb3\x82\x06" } leaves: { key: "CKY=" value: "C\x01f 8j׀\xea3(\x90\xe4\xf2\xc7Of-\xf8m\x16\xdd\x0f\"\xe1\x7fa]y\xa6\xd1e" } leaves: { key: "CKc=" value: "\xaak6YX\x8f\x85#\x9f\xa0bČĨ\xdaD~\x16\xd7\xe7>\xe6'\xec{\xc34X\x06\xe8~}\x9d" } leaves: { key: "CKg=" value: "\xba G\xae\xd33\x9b@\xd48pLUD\xf5\xf4P\x82ΟÆĨ\x83080\x9bc\xb4\xa5\x8b\xe5" } leaves: { key: "CKk=" value: "\x0fl\xc0<\x83\xf7\xb0t[\x84\x92\xc6\xfe\xf4\x83\xe9k\xc9W\xbb\x9f\x11\xfe<\xdf\xd9z\xdf(h\xbcM" } leaves: { key: "CKo=" value: "[\x8d\x1f\xe3\xfb\xe2lt\x8dUf\xfaB.JE(9\xc0+UÔĩ\xaf}bR6J\xecŲą" } leaves: { key: "CKs=" value: " \x19\xbe\xe0\xf7\xdc4\xf8/(\xa4m\xe8\x94U\x1f\n\xfe\x99+\xae\xe0\tZ\xc0ևE\xb9\x98i\x07" } leaves: { key: "CKw=" value: "\xd3\x07|\xa7\x96\xfa\x89V^\xcb0\x94\x86\\aw\x9e\xee\xf0i\x97!\xadT\xbd\x1bV\x01y\x90-n" } leaves: { key: "CL0=" value: "\xb0m\x01\n.凂\xfd*\x81\xe8e,`\x80x\x8b\xd3\xe1\t\x15\x81\x18:]\x9a\xf9I9@X" } leaves: { key: "CL4=" value: "k@'@)\xdaA\x9b\xeeʤ\x85\xc2\xfa\xb3\xf7|\x93\x82\xb4\xbfo\x830O|\x99c&\xae\xa2\x9f" } leaves: { key: "CL8=" value: "r2!\xd5?\xac\xf9l\x99Ɛ\x81Ęļ\xb8\xb8P#\x08\xe3-\xbe\xdc\xc5\xc4`\xa0\x8c\x05\x81v\xd6" } leaves: { key: "CLA=" value: "#V\xdb\xdd\t\xb0Nh\xf3h\x97=\xc0\xf4\xee<į•ŊY\xd8Į›u\xcc9\xeeqj\xacf\x04" } leaves: { key: "CLE=" value: "\xb7%%\xbe:\x0f\x9aB\xf6\x98\xa6Ū…\xc2\xc0\xa3FZ\xb5\x05\xf19\x88XÜĸG^\xee\xcc\x17y" } leaves: { key: "CLI=" value: "]\x1e\xfd'z\x94\x9aU}\xd4\nS1Q\x10zG\xae\x13\x1b\xd8\xfd\xc1\x07b\xb9\xd3꩛\xeb\xa2" } leaves: { key: "CLM=" value: "S\x96n\xa5\xca$d\x9e\x88\x02\xe9\xc2BŅ’\x82Öŋ\x948\x96v\x94\x9c:\xff\x9c\x8f\xfa\xbe\x9b\xba" } leaves: { key: "CLQ=" value: "\xbaY\xebr\x00r\xf06cN\x15%\xf7,\x87I_\xb8\xf6pB\xe6\xe2j\xeaÜŋ\x84\x9b\xaa\xab\x91" } leaves: { key: "CLU=" value: "]\xbd\x06)\xe4\xb2\xfe\x11\xaeu\xef{\xa2!\xe8Úš\xcb8V\xfb\n\x8d\xab\xc60[\xe2\xc0\x86Ķ‹" } leaves: { key: "CLY=" value: "r\xa3\xbbU\x95\xb9\xc6\t\x01\x8fʛ\x92\xea\x0e1g\x05\x04\xb5\xda?\x88F\x89,\xc0\xef\x11\xbb\xb2R" } leaves: { key: "CLc=" value: "\xcbÅ E\xb4\xa8.@6*H2%\xc1\x18\xfa\xbb{A\x8f\xe5.\xf8\xa1ď\xc4'\x1c\xf7eh" } leaves: { key: "CLg=" value: "\xfe\xab\xcb\x0fw\xf2\x8e>P&\xf9\xf4\xa7\xc8\xf8\xbf\xf0\xa1\x89S\xbb\x84)\xc1\xb7\xd8l\x1bI7\x05b" } leaves: { key: "CLk=" value: "J\xa82sO\x0c\u0090\xab\xb9$\xc95\xc3t\xf2\xfcxm\x9aX\xfcT*\xe6N\x84\xd75cT\xc1" } leaves: { key: "CLo=" value: "\xcb\x19\x1e\xc2|N\xf9\x9e\xc1.\x1bw\xb8\xb3\xeeĮŋ|^\x13U\xf1\x0e\xdeLY\xfe,\xb2\x1f\xee\xc5" } leaves: { key: "CLs=" value: "\xa9|\x87\xef\xf7Îŧc\xb0\xc6\x1c\x93\xc6\xd1ÔĨ\x0cT\xefD\x85/Y\x8bV\xcf\x00\x9ed\xd0E\x1a" } leaves: { key: "CLw=" value: "\xef\xe2x6\xb6\x87\x97\x83V\x01\x0cD\xff˨S|B(4?\xb3\x87]h\x9dÄŊ\xa5r$\x13" } leaves: { key: "CM0=" value: "K \xccØŧ\x8c\xa0\xe5\xaa\xd8\xcb\xc3\xffu\xa3\x04G/\xb2\x03\x99\xb66*8J\x0f\xbc\xe4\xa0\xda\x1b" } leaves: { key: "CM4=" value: "\xa2/w#\xea\xe5\x137\xbb`\x18\x85`\xed\xa1\x98$\xbc\x1d\xee\xf3z\xe8Y\x9c\xda\x14\xd2.G\xa0\x87" } leaves: { key: "CM8=" value: "\xc4U\x84\xe4\x15\x17] \xb8WQ\\\x17\xf9XP\xf3\xfd\xbaF\x02\xa6\xb7\x81\x1fC~D\x9c}\xfa\xde" } leaves: { key: "CMA=" value: "\xa5\xdc\xf1\x9b\xe5*\xf6x\xd1\xfe\xd8in\x92\xd9UHI\x0c\x96i\xe0\"M\x15\x04=\x9bM|\xbf#" } leaves: { key: "CME=" value: "\xe0\xfc\xf4 \x14\xca\x07(n.k\x13\x9e\x08?\xe7+\x1a\x91\x9e\xe4\xe1\xca{\xe8A\xfb\xe6\x84\xe5\xc8F" } leaves: { key: "CMI=" value: "\xd8M\x12@\xa6\xb8Nv\x1b\x0e\x15,\x05Ψ*10\xd3\xc8f\x17\x8c\xba\xe8\x1dtΌ8\xcd6" } leaves: { key: "CMM=" value: "\x8aA\xf6\xd2$\xff8%W3`L\xcdW\x97ÔĢs)l\r^\xdcD\x18\x9c\x1a\xb1\x17Vj\xb4" } leaves: { key: "CMQ=" value: "\xbd\x95\n\x18v\\\x04\rd\xf7f\xd7,\xc7\xce/H\xe7\xfe\x87\xf5\xe0]\x87\xb2P\xb2ßŧ;.#" } leaves: { key: "CMU=" value: "m\xa9 ˧B\xcb\x1c\x19\x93\x825\xd1\xd0\x13\xf9FL\xe6\xf1\xe4\x9aqE\x8f\x8a\x18e\xddĖ“\x1c" } leaves: { key: "CMY=" value: "\x1dNC\xd34\xbad\xd3\xce\xd2\x1e\xe2T\x89\xf7\xec\xf6 uwy0\x12?O\xf6\x8d%\xb3v\xe4" } leaves: { key: "CMc=" value: "\xdb\x07o\x91\xb7\xaf4\xec)\xa3m\rk|\x93\xb1\xd9͈\x93\xc3g\x9d4o\x82\xda\xe4\x8b\xc6C\xf7" } leaves: { key: "CMg=" value: "\xceC#v{\x8c\xeb5X\xbb\x9aj\x97\x96\xf7\xe9\x08\x96E\x8b(\x07\xb6\x9c^X\x97\x10\x18\rB\x9f" } leaves: { key: "CMk=" value: "\x8b\xf3.;\xb3\\\xd7\xc1\x94\x99ÂĄ\x9d\x1e\x91\xfcU\xfe\x14\xc1H\xd1R'\xba5\t1\x94Ew\xa2" } leaves: { key: "CMo=" value: "\x7f\xe4\xc0\xf2iĪŽ\x83\xc6jÕĩE\x8a/\x1e\xbc\x9eŅŽ\xaa\\\xdbέëĨŸ+\xd6\x10X" } leaves: { key: "CMs=" value: "\x95T\xfaT\xc8Y\\\x13\xb9\xbe\xef\xde9-\x18\xfc\xcdW\x00\xf2\x9d\xd2DsM\x04_\x91&\xba\x83\xd0" } leaves: { key: "CMw=" value: "\x8e\xc1Ō‰\x07\x12=#\xeaO\xaeI*#\xc5\x0e\x01\x1feHb\xc9s\x10\xc1ˡ\xc60\xa1A\x97" } leaves: { key: "CN0=" value: "Ūŗ,\xc4PO\xb0(7\r\xfa\xd2\x7fu\xa0;\xe5\xdd\xf5\x0b+,\xe8\xa2A\xe4\x82\x18T\x9c\xe4\xe4" } leaves: { key: "CN4=" value: "&\x9c\xd8\x01\xe04\x97u\x8c\xa5eL\xcb_\xa0\xe7_\xa1w\xf1\xb0\xe1PWL\x9fE\xdf\"?k\x17" } leaves: { key: "CN8=" value: "~ܕ!\x11\x92e\x9e<\x89\xbe\xfcmY\xa00Y\xc3q\x1d\xddJ\x0b\xf5?PY\xfb\xdd" } leaves: { key: "CNA=" value: "\xf5\xf9\x86]\xbesw\x82Q\xc2K\x80\r\xc9#x\xac\xbe.Z6e}0df\x0f\xa1$\xd1-h" } leaves: { key: "CNE=" value: "ၐ\x05\xb9Zm؝L\x10\xaf~D\xf7\xa8Whs\x02\xf7\xe9\xd12\xc84F\x96]\x1bsT" } leaves: { key: "CNI=" value: ">\x85\xebۘ\x84\x0b\x01P\xb59\x9bif\x0fd\xab\xa5\x94&\xfd\x19\xdb\xf6\xd7\xd0\x7fÂĢ\x9d\xd3\xd5" } leaves: { key: "CNM=" value: "W\xf0\x99۞2\xba\xee;\xf0`?|f J\x87\x06\x7f\x8c\x02\xf0y\x19/s\xfe\xbe\xd4\xe7P\xd7" } leaves: { key: "CNQ=" value: "m\xf7\xfa\xdf\xc2\xe6\x7f\xe13\xe7{\x9cf\xe2\xb36=\u0085\xd7\xce\x1dQ\xa8\xcfd\xd4kj]r\xa6" } leaves: { key: "CNU=" value: "\x06|\x98/\xa0/,'*\x8a\x13T7\x02L{j\x10\xbe\xc3\x17z\xe2\x05Z\xc0\xd1E\xa3\xa88\xb6" } leaves: { key: "CNY=" value: "_)/J`%k>\x16\x8a%4\xac^!\xa0\xe8\x9cv\xb5\x9d\xe7\xf5j3\xf7\xde\xc3\xf0\x0c&e" } leaves: { key: "CNc=" value: "\xce\xe4\xd3>+e\xb8ä›ŧw`\x16j\x1e\xee\n-\xfd]=\x8c\xb9D.~=\xaa\xffQ\xf1\x95" } leaves: { key: "CNg=" value: "<\xd7䔱\x915\x89Ōž\xf2I\x08\x08t\xea\x02\x9f\x9cs\xad_(U\x8a\xb2k\x1c\xd1e\xeb\x14" } leaves: { key: "CNk=" value: "\x1c\xed^DĮŦ\xc0\xa6\xbdÆĨ\x05\xcdve\x88\xc4ן\xe1\xe6jtJ\xe4\xe0=ȁ\x1d!\x9c" } leaves: { key: "CNo=" value: "xÄĒb\xc6aR\xef\xd0\xc5M\xf3\xe9dD= Y8}I\x88BD.Z\xab\xe4\xc5%Q\x02" } leaves: { key: "CNs=" value: "\xe4%\xfdq\x9aG\x12\x15\x1c\xcbbc\xae\xe1+\xbaJ\x94PG\x94?\xa5į›ŊB\xfe\xf1\xd8#'" } leaves: { key: "CNw=" value: "\x96f`([\x9eN\xadbN\xae{˞L\x96\x1a\xe9\x81\x16qKk%\x96N\xd7u4.Nk" } leaves: { key: "CO0=" value: "\x99#G\x8b\xccH\xfb\x8bM=\xf2\xbf\x84wx\x96=\\\xceV9p7\xdb:ᖛyԐ\"" } leaves: { key: "CO4=" value: "-ę¯ŋ^s\xb9N=\xcf|\x91\xc2\xca\xf4v\xa4\xbd;\x9dvU\x93\x15\xe4;\x97Zv\xb2\xa3\x1c" } leaves: { key: "CO8=" value: "\xddAHU:G5\xa2\xc5o\x82\xdes\xb6\xfdQ\xa4\x0f\xeau5\x91\xa9N\x88\xfc.\xc9,\xb9\x1e\x83" } leaves: { key: "COA=" value: "0\xc6+\x99GJ\xd3\xd7\xc6'\t\xc3-@\xef\xe5\xc8\xceX\x9fP`\xec\xb5.\x89\xc9j60\xe9\xc2" } leaves: { key: "COE=" value: "9\x9bg\x99\x07\x98\xdf\xea՘`O\x1d\xe7\x14`\xa5\x18\xa5\xea;@k3\x04\x16\xa0\x18?Y\x8f\xd3" } leaves: { key: "COI=" value: "`XG\x91\xb5\xd3]\xb7\x17\xac$og \"\x0c\xbeB~v\xc1\xf1\xa1\xf6-U\\[M@F" } leaves: { key: "COM=" value: "\x1d2Jg\x03m\x01a\x7f_\xb3\xbd\xb8ČĒ\xc5\xc5\xe6\xef)\xf9\xce\x0e\x1d\x82b\xedXn\\c\xa4" } leaves: { key: "COQ=" value: "_T\ru\xa1\xd78\xc3s\xa6J1$m$z\x8c\xc4l\x12\xbcsr\x81Is\x19<\x83@\xc0\x9e" } leaves: { key: "COU=" value: "\x0c\x91\xd5U\xa7\xbc\x1b\xc1\x19\xe2\x13-\x19Ά`\x02\x11\x93\xa6w\xf7\xc0\xc0åϝ\xf8N\x12j\x0e" } leaves: { key: "COY=" value: "V\xc1H\xbe\xa7\x11\x1e\x94\x8a\x83\xd4\xd0\xde؁.\x0b\x05āŧž\x9fN\xee\xd3[\xb9\x15\x12e\x7f\x95" } leaves: { key: "COc=" value: "\x8d\xdc\xff9\xa4\x0fc\xb6\xfa\xcb%0\xef\xacm\xb6\xa7\xdb\x145p\n\x87ˇ\xa0\x1f\xc2C\xb3\xfcu" } leaves: { key: "COg=" value: "'(GD\xf9R\xac\xef\x05E7*\xe0\xe2X+lJ\xf5\xb6I\xddf:\x9aN\x06\xa6\x18\x8aI\x0c" } leaves: { key: "COk=" value: "\x8f\"I\x80\xe2\xa1\xf7\nt\xa6Ĩz9\x02\x19\t\xceJ~\xf8\xd7C5\xe3z0ŨŊ\x12S{" } leaves: { key: "COo=" value: "\xb0\x12\x1e\x82\x12jߏu\xa5\x07\xa6\xe2\x9e\xff及\xe7e~\x8b\xc1<\t\xcf\x05'\xd0\x1f\x8d\xce" } leaves: { key: "COs=" value: "<\x8e\x1a\x1a\xfcy6\xc6\xddd\x81/\x88PS\x07W!H5\x08]y(=\xff\x10S\x01\xfa\x10\xbb" } leaves: { key: "COw=" value: "(\xaal(\xb5\xc6\xf7809{oc\xb7\xa4\xb1Üąh\xce`n\xd04\xaf_\xba\xe6^Ũ2" } leaves: { key: "CP0=" value: "\xea\"B\x96M\xb0\xa6J\xbf\xe9\xa02\xed\xbc\x14O\xd3'1\xbb\xeb\x17\xf2qÎŋ)#\x99\xb4}\xa0" } leaves: { key: "CP4=" value: "\xcaM¡\x19\xc5B\xa2J\x1e\xa2\x16\x05\x99\xadØˇ\xe0nW\xeb\x1ex\xfc\x86y\x8d\x90\xc1\x0f\x12N" } leaves: { key: "CP8=" value: "\xdc\x0e\xbdI\x89\xd6\xd0\xe3\xfeI\x15\xf1ę†ļvO&7ėĄžËĨEjG\"\xdb\xdaA\x8c" } leaves: { key: "CPA=" value: "Ho\x81\x0f\xdfJ\xf5\x84h\xc9\xf6\xb4ÜŖ\x1a\x9a6\xdb`\xe1\x1dg\x93\xcaÍ´\xe4R\xfcPŌą" } leaves: { key: "CPE=" value: "4\xc1\\\xfa\x10z\x87\x8e\xe0\xe1\xd2éŧ\xc0\x82\x98ax\x01z\x83\xb8\xd7.q\xf7\xebsm\xc0\xfa" } leaves: { key: "CPI=" value: "!$5\xa0}\xcaze\xbc\x0c^U\x92\xbeX\x1a\xa9\x1c\xa6\x05\x7f|<\x0e\xc5\xe4P2\xbcgZ\xe9" } leaves: { key: "CPM=" value: "\x1biF5\x84\x15\xb8\n&'\x8680\x99\xd2\n\x17+\xa0\xd8Sl\xcf\xdd\xe2l\xcaRs\xcb\r\xd4" } leaves: { key: "CPQ=" value: "y\x12\xedM\xf2\xc3ׅ\xf3\xbb\x108rmp\x82B\xfb\x1a\x97\xe9N\xef<\xc6\xfe\x0c\xfa\x1bnD." } leaves: { key: "CPU=" value: "\r\xe3\xff\xf2\xc60xLdD\xd5\xf3\xe7\xf7\xf5qe\xabĪ>>\xa8\x85\xc9sOd'\xb8\x8f\xa2" } leaves: { key: "CPY=" value: "\x96i\xa8\xe2֐\xdf\x1a\xact\xf7\x10\xec\x91\x16\x1b \x13E*莎\n\x0c.H\xb8\x9c\xea\xcf\xe6" } leaves: { key: "CPc=" value: "2C\x98\x16N\x13\xd5^\xfd\xec\xe5\x0f\r\xc1\xb6\xddm'\x06\xc0\xfc\x83\xf0\xe8\x12\x8f\xd2\xef\xe7\x1b\";" } leaves: { key: "CPg=" value: "\xf4Г\xcd\xe9'\xc37\x90\xb3\xbch\x9e w\xd8W\x05\xed$J\rÕĒ\xa1\xf1\x13\x9b\xedQ\x80h" } leaves: { key: "CPk=" value: "\x81\xe4\xefx\x11\x94\xb6O\x90\x9fW6\xf6\xe7x\xa9\xc1\x1e\x95\xb1-b\x96\xcei\x17lPT\xefi\x10" } leaves: { key: "CPo=" value: "\xe3y\x81\x8a\xaa\x19\xe8-\x0b1\x96^\x85\x1f\xc0\xcb\xd1큂~@Q\x85u\xc8N\x11\xa1Ū—\xd3" } leaves: { key: "CPs=" value: "\xa4\x85\x89\xad\x11\xc1\xd0\xc1\x1f\x01t0\xd2=\xb7\xc9I\xf1\xdb`d69\xb0`\x90\xd28\xbfaZk\xbe" } internal_nodes: { key: "AQA=" value: "\xff\xffd\x12!9<8\xc3\xcf\x10\xab\xa8I\x08]\x81\xcbF\x13\xb8H\xf9\x9eoÎĢ\xbf\xad\xa6\x0c\x1c" } internal_nodes: { key: "AYA=" value: "\x11\x8by[́Q[\xb8\xdc\"\xedt\xde\xe4\xd2[\x8b^H&\x84$\xe43\x8f\xb5\xd2c.9\x93" } internal_nodes: { key: "AgA=" value: "\x07\x864j\xb2K`\xc0Q\xffKØ \xd0)\xb4\xd1b\x7f\xd8\xf1\xa7$\x05d\x9dq\xbc\xfc\xce\xd7\xc4" } internal_nodes: { key: "AkA=" value: "\xab)<\xc4o\x10{d0\xf8\xf8\xe1\xa1~\xb2J{^4D\x1bx{\xa8\xa4\x8a\xda\x1a\xe9Ґ\xf1" } internal_nodes: { key: "AoA=" value: "\xce\xc1\x8cz\xccA\xe7\xb7\xfcN\xe3\x90p\xe5Z\xfc\xf5^&\t\x18\x95\xbbܡ\xd0tQ\n\xf2\x83\xec" } internal_nodes: { key: "AsA=" value: "t\xde,\xeeb\x9b\xa5\x86%\xe8\xca\xff\xf21\x8a\xa1i\xc0\xa9â̘\xfd\x1fJM\xa3\xdd@Y\xb6O" } internal_nodes: { key: "AwA=" value: "y\xbe\xeb\x7f\x93ᄪ\x1b\xf0R\xcco\x89\xf2\xb4-\xd4\xd9\xce͉\xb9\xcf.@`zv$EI" } internal_nodes: { key: "AyA=" value: "Ihb\xe0?\xba\x98\x0b\x16\xedB\xe7\n%Cg\x97\x00\xbd\xf6\x13\tTy\x13\xc2\xd1].\x0b\xe6J" } internal_nodes: { key: "B+4=" value: "\xb3\xcf\x1cx\x08\xeeoysOR\xb4\xf0e\x1b\x9aw\xfaN\xc8\t\xb2\xc1\x03b\xcc\xf2q\xe6\xec҇" } internal_nodes: { key: "B+A=" value: "|\xfa\xf3\xe3\x9aszf\xf6Õ \xf6\xec\xfb\xc9\x0f\xea\xe5\x87*\x84\xb4,\x1e\xabZ,/\xf2s\\Q" } internal_nodes: { key: "B+I=" value: "\x1a\xe75\x95\x8d\x8b\x84\xb9\xa7\x15\xe1\xc3\xf22s#\xb1\xa8,\u0099Z\x8bm\x12\xa2P\xb8\xa1iŨŠ" } internal_nodes: { key: "B+Q=" value: ";\x9e\x1eŖ\xc0W;\xfd\x93\xf3\xdc\x108\x9f]}a\xde}(Đ¯\xe0\x91\xb7\xce\xf4ĐŽ\xbe\x17" } internal_nodes: { key: "B+Y=" value: "\xb5$=\x85\xaf\xa8uq\x87\x80|\xc9\xd89}1D\x03\xbf\n2y\x94j\x8e\xb2\x13\x1dw\x10\xf83" } internal_nodes: { key: "B+g=" value: "p\xa8\xb1Ũ\xbf\xbb⍰$HŅ &I\x06\xdezw!\xff\xafn\xa6\xaa<\x91\x86Z\x84\xfb" } internal_nodes: { key: "B+o=" value: "\xfe@\xed\xeb\x8d6\x86i\x8aiv\x12A\x7f\xc5O%7B퓱\xfd\x94\x8b\xf4\x7f\xa5\x98\xf2\xa1\xc1" } internal_nodes: { key: "B+w=" value: "fU\xff0d\xd3\xd4P\xf9\x82\xfb\x85\x1c(N\xf7\xecP\xdf|O\x9b2h\xfe\xd5\xeb\rŲˇ\x94\xe4" } internal_nodes: { key: "B/4=" value: "\x06B\xb49\xac\xf3\x90Qj\xb9\x17\r+N\x96\x8cL\x95\x94\xbdt\xcfM \x96-q\xab\x9f\x81\x8e\x91" } internal_nodes: { key: "B/A=" value: "9x\x13\x02\xe9T\xe8N}T\xbd\x02\xa0\x12\xf7\x1f\x1e\xb0\xddD\xb5\xa2o\xcfl%\xed~\xee\xd7>k" } internal_nodes: { key: "B/I=" value: "TP5\xba\xf9ĶŠ\xa2Vr\x1d\xbaN>4T\xd8~\xb2\xa6>#\xfed\xe7\x89\xd7_\xba~6\r" } internal_nodes: { key: "B/Q=" value: "\xafU\x93\xbb\x03{VŲ´\x1a\xf3\xfe\x05\xd2\xc6\xcaM\x14\x8155r\x11aq\xbd=\xea\xb2K\x16\xbf" } internal_nodes: { key: "B/Y=" value: "\xad\xc9k\xd3\xf6z\xc90\xda\xc0O\xacl\xb9\xaa'\x82@l\xc7Fl&߄\xd7f\xbdZ\xb5\xf0 " } internal_nodes: { key: "B/g=" value: "\xb2\x1f\xff\xa4\x1f\x03\x1a,LÕģ\xf2:4\x161\r\xca7\x08D\xf7\x03\x9d(o\x0bod\x8dh\xb5" } internal_nodes: { key: "B/o=" value: "=\x0b$\x1d\x8a0\xac\x14\x937\\T\x9b\xc4'qY\x1a-hic\xaaiVFq\x07\x96\x17$\x9e" } internal_nodes: { key: "B/w=" value: "\x84\x1bS\x9e\xfa\xccwh\x7f\x83[\xa7\x19\xb2y\xfa\x18Gc\xb4Z\x889/\x16\xd0@m\x976\xa0\x80" } internal_nodes: { key: "B04=" value: "\xb58\x01G\xbcJQÔ¤\t^ \xced\x93İ\x8ax\xedX\xf08o}\xd4G\x85\x99jx " } internal_nodes: { key: "B0A=" value: "\r\xb4\x07\xbe\xbc\xee\xf4\xde|\xd5b\x85\xf6Ĉ\\\x7f\x8e~\x17-47d\xa0*^\x82]x\xd7\x1a" } internal_nodes: { key: "B0I=" value: "\xb5\x86\xa0膀Cb*hY\xe7}\x1f\x1d\x19o\xca\x1c\x9d\x0e\x94%B\xb5\xcd\xd0ĖŦ\xb7\x89#" } internal_nodes: { key: "B0Q=" value: "\x84<\xcc\x0e\x8c\xce\xf2\xb7\xaa;\xebe\x1bA\x8e\xb0#7\xec1\xeb\xb7<\xa2'\x88\x8c\xb8\xfc\xab\xb8\x01" } internal_nodes: { key: "B0Y=" value: "\x00Y(q\xc4+p\xb2\xb2TU\t!1\n\xa4\xcd\x1e\xab\x82\xe5\xaaÍĒX\xf3\xad\x8a\xe06\xa8\xe0" } internal_nodes: { key: "B0g=" value: "\x06\xf9\xe0L~ \x08\x0e\x8b+\n\xb2/\xde\xfc\x89A^1\x1bv\xbd\xb8\x14\xff+ɀE[m\xd3" } internal_nodes: { key: "B0o=" value: "\xceH\x97\x1a\x9bV\x05\xe1\x19\xd5,MŃv\x9c\x1dI\x14\\\x18q\xb0\xcax\xf3\xa5\x03\xbcv\xa5\xfb" } internal_nodes: { key: "B0w=" value: "\xa0]܍vyŨĄ\x94\xa4#\xb6\xe0]\x8b|z\xee\x86;\xa1\x00;\x192!\x152z\x1f2C" } internal_nodes: { key: "B14=" value: "\xd2\x13>\x0c7]S\x81\xd8Įŗ\xbc\x81lh\x8b \x08R\x1f\xfb\x99\xa1Ûž\xc6\xe7\xe6L\xa4\xc0V" } internal_nodes: { key: "B1g=" value: "g\xe9x;\xef#\x8b\xedJ\xfa\xfbn>\x12K\x1aAؓ\x85\x01\xa2\x08}\xb2\xf8\xdc\xe5\xc2\xf28p" } internal_nodes: { key: "B1o=" value: "\xbe\x9f\xe5\xef?R\xb2\xf4[Be\x97^:\x91\xbf\x92\x19\x019)\x1dK\x93l\x14\xa3\xa7\xe6\x8e\x10\xf5" } internal_nodes: { key: "B1w=" value: "\x07\xeb\x07\x95\xdd\xfc\xc9Bt\xe8\xada{q\xc2C;\xc0\xcbÜą0\x7f\x0f\xca\xf6={U\x99\xe4h" } internal_nodes: { key: "B24=" value: "\x11\xc9+A\xaec\x8a)j\x82>\x1a\xdf\x1c{.\x1d3\xd7{βH\x0e\xe3\x14Hkzrt\xfb" } internal_nodes: { key: "B2A=" value: "'R(\x1d\xafU\t\xb4\x1b\xff\xeb\xfb\xac \xb7\xb3\xdd\xefr%w[X\xf44\xcd\xf8\x12\x1b\xd0s\x81" } internal_nodes: { key: "B2I=" value: "Ãĩ.\xe0b\xc91\xafEB>\xd9\xfd\xd7^\\}G7\xb0fZ\xc3\xe6\xdb_Kj\xb3\xe7.\t" } internal_nodes: { key: "B2Q=" value: "\xdc\xdd\xf7\xe9;\xb6\x05M\xea\x02l\xeai\x17\xb9\x0en:\xca:\x163\xb9\x18\xe54\xf9\xad\xb21\xd56" } internal_nodes: { key: "B2Y=" value: "\xdbN\x96\xc2MX8\xa6(\x84\x96ā\xa52\xf0ŨĨk\xe7\x9aГ\xb2\x80 \xb2ã›ĻZV" } internal_nodes: { key: "B2g=" value: "\xf6\xf6.\xdd\xe9J\x8b\x9e\"\xe7E\xf9\xf2\x83\xd1tÍ\x94U\x87Oq\x01\x85\xfa\xc7)\x97\x19S\x0f" } internal_nodes: { key: "B2o=" value: "\x17)\x83͕u\xfd\xa8&\xc5\x05\xc2۝\xea:\x93+\xb5Q\xb9\xa0=\x0e\xcf|\xc1\xe0Z\xf4\x95Y" } internal_nodes: { key: "B2w=" value: "\x86o\xb8\xb7\xe9\x1b\xdc\x1e\xaa8}\xa3\x0cBŲ#Jw\xb8\xac\xfb\xe4\x81t;?_\xcf\xd7\xfbb" } internal_nodes: { key: "B34=" value: "\xb9,h\x90`\x88\xf3)!\xaf\xe0Z\xd5 \x99!۟\x892\x1f\xe3\x8e\xe6\xaeE\xbe*\x04\xb4\x0c\x9a" } internal_nodes: { key: "B3A=" value: "\xe1\x87{\x1b\xb4_\xb4?\xee5\xf8\x929\x99\x1b\xa26l\x86\x193OM\xb5m\x84\x08w;\x18\xf2~" } internal_nodes: { key: "B3I=" value: "TØˇ2\xbe\x04\xb9\xa1\xdc?\xcfw\x7f=\x14\x87\x92\xb9\x8d/\x8e{O\xc3\x15#\x83\x1f\x82\x9a\xea\x05" } internal_nodes: { key: "B3Q=" value: "\\\x16\x0fb&\xe3\x07(\x858\x91n=\xf5\xf4\xd3\xe51G\x0f\xa8\x12P\x9c\x1a;\xf4D\x05,\x85\xe5" } internal_nodes: { key: "B3Y=" value: ")\x10\xf8\x17\xb7\x11\xc37\xac\xe4\x01f\x87\x95D\x18m\xe93\xadp\x7f\xcbK\xf1\xc0\xec\x87\x0b\xbf\x17\xa3" } internal_nodes: { key: "B3g=" value: "L\xf2\xb5Z\xe1\xb6ëǃ\xa6\xaf$\xc5L\xba\xc5Đ?\xc4\xdd&':͞\x0c6\x9bĐĻ\xf9" } internal_nodes: { key: "B3o=" value: "8o0\x87\xe6\xb5\xc3\xceT\x1b\x86\xec\xe2\xab\xc1Yw\xc1\x9a\x05\x03\xb8\x04\xb3\xc6\x01\x83\xa4\xbe\xbe\xf2a" } internal_nodes: { key: "B3w=" value: "Y\xb3\xd4#\x86W\x1a\x0f\xbb\xa3\x03\x12\x04\xb8\xe7Õŋ\x18Lt)\x98\xfdj6\xa1\xe0*U\x97\x10\x9f" } internal_nodes: { key: "B44=" value: "o*Q\xbb\xaa\\\xee\x9a7\xb1\xfcd/\x83@\xd5x\x15y\xceI\xe1\xcf&1\x14\xa9\x84zÉĄ\x1b" } internal_nodes: { key: "B4A=" value: "sG\xb4\x92\xb9\xcc\x03\xbc@\xa3\x91e\xf4\xceØ­\x11e\xdd\xec%\xe4TK/m\x17T\xfcbp\xa6" } internal_nodes: { key: "B4I=" value: "\xabo\xfa\xc9\x1d\x99\xa7\xc8æēš\xe0\x05_AK\xe3\xb2\xdbG\x87t\xe7\xff\x1c" } internal_nodes: { key: "B4w=" value: "\xdbn'#~\x1b\x84\xa6\x18\x88\x06\xff\xe9r#C\nE\x19B\xa0\x9d\xc6~\xa8\x07\x90:\x05\x0e\x9a\\" } internal_nodes: { key: "B54=" value: "\x11[\x1d\xb6\xa1\xa2a\x10;\xa7\xe4\xba`)\xc3\x1eQ\rЁ\xbb!\xb2\xbf\xa2\xe6\xb8\xfd\x92Å a" } internal_nodes: { key: "B5A=" value: "\xe0\xd2\x15\x03\x1f\x82Ee#\x1b\x9e\xb7?e\x94h\xaeJPN:=Ôˇ\x83\x95\xff\xd58\xba\xa1\xdb" } internal_nodes: { key: "B5I=" value: "\xd5\\\x18F\xd4\xc3\x15uR9\x08@r\x87\x8d\x97\x04\xe8\xc6\x07G\x14\xb8\xf6\xad\x9c\xa5\x1e\xe1\x87\xeb\xc2" } internal_nodes: { key: "B5Q=" value: "0vS\xfc:įŠn2\xe3\x94\xde\xdd^\xbe@\x1b\xe5\xc5ķ…œ…\xc0D\xb8\x11\x86IYy" } internal_nodes: { key: "B5Y=" value: "Y+\xa3\x8e\xc4\x1b\xd9\xcb\xc9\xcc\xea\xa5D\xa6\xf3\r\xb8#6S\xdb\x03h\xe9F\x0be\xba\x18\x0b\x12\x9b" } internal_nodes: { key: "B5g=" value: ".\x15\xa1\x84\x9c\xdb\xebH\x914\xbd\xf3r\xf3\x94=X\x14\xce\xf1\xa6\xdfa\xdb\x12\xd4\xc1Ū´\xf95\xc7" } internal_nodes: { key: "B5o=" value: "\xac\x1a\xa9\xaf\x01\\\x92\xba\x8e=\xc2R\xa9{\xff\xc89\xe4}\x82r͙\x1c\x13@\x85\xd4×Ŧ\x88\x00" } internal_nodes: { key: "B5w=" value: "\x13\x96\xf4qTNA\xa4e\xd66\xbazk\x97\xee`떂\xe5;y\x12J\xfd\x97\xc2s\xa9\x8a\xb6" } internal_nodes: { key: "B64=" value: "\x1e7\xe7\xbcW>\x12b/ČÃ´\x00\xac\xb7\xc5\xce\x1d&\xd8┊K\xb3XT\xa2ŒV" } internal_nodes: { key: "B6A=" value: "u\x0b\x07\xd7ourRF\xb9\xbd\xd2\x1e\x8e\xb7\x8c\xbe{J\xdbJ\xef\x01+\xab\xf0\xd9N\xd5\xfdy\x13" } internal_nodes: { key: "B6I=" value: "\xef\xadn\x17\xea|Fh\xaf\xc0\xa5\xf7\xf9$(@\x90DGVL\x05j\xbc\xa5=YC\x08\xac|\xa7" } internal_nodes: { key: "B6Q=" value: "#:ØŖ\xa9\x8f\x9aU\xc6k\xdeZ\xe1\x12y\x11P\xe5\xdb\xf5\x89\x08\xff\xf0\xe9\xad\x0c\xb8\x16}\xa8(" } internal_nodes: { key: "B6Y=" value: "n[\xa3u~ē1\x1a\xf9Jz\xe6\xf5β\x11<\xbd\xa6\x1c\xb3~\xceJ\r2F\x87\xcc\xe5\xf3" } internal_nodes: { key: "B6g=" value: "\xcbk\xd7\x11\xc20\xcb\xee\x1b\x89a\x10\x02\xd97X&\x1b|\x04c\xb6\x8f\xa4?i8\xb7\x19\xa7\x8a\"" } internal_nodes: { key: "B6o=" value: "\xdc\xf5\xbf->ԋ5ÂĒd\xe0%7\xf5\xad:\\8\xa5S\x1d\xe9Ōž)\xa3\x9d\x7f\xd9ĮŸ" } internal_nodes: { key: "B6w=" value: "Q\xf7\x9bN\x16\x92\xa6\xc7,\x9az\x15m/\xa3:\xefV\xaf\x04\n\x04\x80\xa0\xacŌ°I[\xb8K\xe6" } internal_nodes: { key: "B74=" value: "\x8c\xac\xa1\xb42d\xc11n\xe1\x88\xd29\x83&#\xc4\x17\x10\x92\x83r\xe3Ô­vO`.\x8fl\xd8" } internal_nodes: { key: "B7A=" value: "\x80\xf6\x88ڀ\x13\\Ú¯\xfbBÃ\xcc92\x98*DR\x7f\x89\xda\xe4\xd3Æĸ+\n\x13\xfa|" } internal_nodes: { key: "B7I=" value: "7a\x19\x0e\xc45h\x06\xec\xd4$J\xc1\x9a\xfc\x8b\xc19\x03\xcb\xddN\xb1l\x1dM\xe6)\xc5\xdaz\xf0" } internal_nodes: { key: "B7Q=" value: "\xb9\x8d\xe0\xd6˯x\x86\xc6\xc6\xcag\x88\x17\xf2o\x1fS\xfe\xaa`\xb4\x1f\x98o\xf9\xf2\x17\x0f.\xad " } internal_nodes: { key: "B7Y=" value: "\xc7Orn\xa2\xe6\x8d4\xa2\x16\x91\xea\x9a}\xa3eĒ\x9c\x88\x03P\xb6\xf1\x87\x16D\xdc_\xa2F;" } internal_nodes: { key: "B7g=" value: "\xf1\xcegM\x8f\xe1;\x89\xfc6\xcf#-M\xdc\xfb\x89\x00\xfa\xfbk\xc5E\xa0\x15\x97\x11M-d]S" } internal_nodes: { key: "B7o=" value: "#&\u0098\xf0\\\x97~\x82\x81\xfb\xcb/k\x05\x9e;\x0f\x03T\x11\xef\xc1\x8d\xfb\x99\xe9Z#}\xff\x19" } internal_nodes: { key: "B7w=" value: "i\x94~\r\x1a\xbf\xdbm\xb6\xe8:CL\xf0\xef|\xfd:Sq\xc3ˊ\xe7\x8b\xc0\t\x8d\xe2́\x9e" } internal_nodes: { key: "B84=" value: "\xa0\t\xf9\xe9\xbeL\xf6&\xf4~/n@\xff7Ė‘\xdd\xd4O\xe8W\x1d\xf9\xf9B\xd6\xda\x1f\x02\xcf)" } internal_nodes: { key: "B8A=" value: "İ\xee\xe3;\x0fq\x80\xa9\xd6l\x08\x91\x9f\x93\xc5\xe6\x0f\xdd\xc0EK\x05A CmA\xc5Z\x99\xd1" } internal_nodes: { key: "B8I=" value: "\xebTxFh\xf7\xb2G\xf3\xf3\x00\xb2\xa3\xc8\x17\xf0j?h\xe6:Q\xbf\xfe\xfd\x9a\xeeR\x87f\xebr" } internal_nodes: { key: "B8Q=" value: "\x91PW{<\x8a\x10h\xd11\xf3\xa6\xa0EP\xaa\xe5k\x05\xb8lQ\x05\x04r\x0b\xdfW\xbe\xf8&\x10" } internal_nodes: { key: "B8Y=" value: "Įš\x04\xa0r,\x06cĪ“\xbfY\x99q\xae\xdbU/\xf8\xa2w\x96\xae7\xa4@nW\x08\x17\xa09" } internal_nodes: { key: "B8g=" value: "\x98\xfcęĩĻ9\xf3\xae\x96\x10\xf0sSL\xce\xe9q\xfd\xe1\x8dv#\xc3\x1b\xdd\xc5'\x19f\x89\x90\xf1" } internal_nodes: { key: "B8o=" value: "%\xb1\x01\xf6\xa1!\x00\xb5\xaf^Z\x9c\x06\x83\x99;\xc2\xee_\xd0\xe4\xa4.\xf4\x88\x951\x10\xe1SiJ" } internal_nodes: { key: "B8w=" value: "2\xd5S\xfb\x08ėS!\xc1mU/r\xccm\xb1cK\xd7\xca\xfd2:\x8cQ3w\xceD\xce\xf7" } internal_nodes: { key: "B94=" value: "\xf9\xc3bX<={\x94\xce\xfe\xaeq\u009b/\x100P\x16\x9d\xe9\xd3R\x1bu\xbf\xfdysG}Y" } internal_nodes: { key: "B9A=" value: "\x99Ȩ\xe4\x11\x9b_\xf8\xa5\xc0\xef\xb8\xe2\xf3\xe1ʧ\x14i\xa1T<\xdeY\xc7Æ˜Ę•\xfa\x87\x8b" } internal_nodes: { key: "B9I=" value: "\x1c\x8djCjÚž\n\xb4,\xca'v\x1aR\x19B\x14FS~\xf8\x15$\xcc\x7f\x1e\x8f\xac\xdd\xd7\x0c" } internal_nodes: { key: "B9Q=" value: "\xa7\xfb\xdd\xe7\x08\xf7\xff\xa3\xef\x81?j^~4\xeaĶ‘\xbcR+_nTUf" } internal_nodes: { key: "B9o=" value: "\xb2\xbf\x90\x89`v\x87\x17m\xb6\x12\xce΂*\xfd\x156\xba\x0e\x7f\xc1\xca\x06%\xbb\xeaU\xd1\xc6\xd1o" } internal_nodes: { key: "B9w=" value: "\xac\x98R\x92[L\x05\x1b^1\x1b3wW4\x91|>\xdc\xcc\x7f\x82&\x88\xd81-px\xa99\xb2" } internal_nodes: { key: "BAA=" value: "%\"&\xeaLz%W\x9b\xdeM.\x11K\xadIs\xc7\xdf\xf4\xb3\x1bQ\xca\x1e(¨2\xf3\xb5U" } internal_nodes: { key: "BBA=" value: "T\n\xc3\xd7S;\x15\xf9\x88Ni\xff̀d\xb4t|b\xdc\x1c\xefv\xa0y\xf6\xca7\x8d[\x12\x1a" } internal_nodes: { key: "BCA=" value: "m\xa1IC\tT\xf4H\xab\xf1\xf4\xa4^?\xad\x9b\xf5\x10\x87\xa6\xfc%\x15{\xa3Öģv\xb4ĘŖh" } internal_nodes: { key: "BDA=" value: "$\xa3z7\xc6H\xe9\x02\xfc[\xe5\x14\xc4\xcb\xed4\xfe\x83'\x0b\x11\n\x07\xf1x\x12\x9e\x8a\xc9\xf0&~" } internal_nodes: { key: "BEA=" value: "\x8e\xad0\xdbuA)\xbe\xac\x93/*O\xbe\x06\x9d0&S\xa2T\xf2W\xac\x14\x91\xc8b\xa8\x8fC\x9b" } internal_nodes: { key: "BFA=" value: "R'?\xe4\x1d\xc0\xb4i7`{_h(\xf3\xcbHR\xa5\x90W\xd7M\xd7\xe6\xc1i\xbc:\xe0C\x9e" } internal_nodes: { key: "BGA=" value: "\xb8ԈG\x00E\xf9\xb6\xbeɈ\x00y\x15\x03\x9b\xc4w\xde\x12\x90&\xe0\x08u\xf6\x91l.\\\xf6\x1a" } internal_nodes: { key: "BHA=" value: "K4\xc3\"\xd0c\x93\x03\x98\xc5\x0b+\xf9/\x9b\x0eX\x0bY\t=\xdd^\xc4\xf4\x9c\x03ZJ4\x1aq" } internal_nodes: { key: "BIA=" value: "\r\xc6uz\x05i\xfa%}\xd3mC\xd1V\xe6z\xbd\x01B\x1b\xe0\x94}][\xabÖŖ\x82Â\xfe" } internal_nodes: { key: "BJA=" value: "\xd3\xdd\xc2Bv'\x17H\x9e\x18\xf2\x98c)scI\xbb|S\xbfL\xc1p\x9dPZȄ^\x9e\x91" } internal_nodes: { key: "BKA=" value: "\x88\x02\nn\xf6\x96r1\x83UbN\xa8\x82?\xb0Ͱ\x1b\xd6=\xdcee1MvP\x88\xc3\xdd0" } internal_nodes: { key: "BLA=" value: "\xf6\xf3\xa9\xd4\x01A\xef?\xa9\xb5O9\x81\xa4ÛŗŅĨ\xeb\x14\xabX^\xc6čCi-\xfcU\xb2" } internal_nodes: { key: "BMA=" value: "5\x90_lJ\xd0/\xa3q\xca\xf7\x95\xd2\x14A\xf2f\x84\xc0\xe8,b5\x9d\xeaߖ_\xce\xe0\xe8\\" } internal_nodes: { key: "BNA=" value: "x=2\x04\xdd6>W{\x00Y;UˍU\x0b\x0bV\xa4T\x92\x94\xd3\xc6\xe7\x8a\x03Z\x97<\xd8" } internal_nodes: { key: "BOA=" value: "C\xc0ͯF\xe66\xb3\xb8\x80\x8a\xb5A゘B&\xc5q\xdc\x02\x1a\xcb\xd4\xe0\xd7L\xe6$\xb8h" } internal_nodes: { key: "BPA=" value: "S#\x9c\x97\xackΚNR7\rjq6S#zQM\xaa\xca2c\xe8i\x86\xa3\xc99dx" } internal_nodes: { key: "BQA=" value: "\xc0\x8c\x1bmT+Z\xcdQ\xab]\x9e\xe5\x14\x10\x82Īą\x07-\xd9~F\x83Ų€\x16Z\xc9g\xe0\xd9" } internal_nodes: { key: "BQg=" value: "\x02\xc0\x94\xda7\x90\"cM\x01\xed\xf8\xc3\x02\xeaj\x198+\xc2Äē4\xd3\x7f\xa5\xe9Ō‚\xf4_\xdc" } internal_nodes: { key: "BRA=" value: "\x8e\x85s\x9c\x9fÃą\xeb\xb4`\xd2\xde\xc97\ntI\x1d\xef\x81\x1c\xa3\xa2\x02\x93\xbb\xb4it]\xb3n" } internal_nodes: { key: "BRg=" value: "G\x9a\x04\x1c/\xfb\xd8R\x87&\x82{\x8e\x9fD?y*\xb5\xb6Į‚\xa2\x1d\xc3\xd7\xdcV\x05{\x02 " } internal_nodes: { key: "BSA=" value: "\xd6\xd8\xef\xd0\xfb#\x94\xad\xbc\x07\xa6\x83h\xd3\xeb\x94@\x1c\xa1\xea5\xf0\xfaQ1R\x86\rĶŗg\xce" } internal_nodes: { key: "BSg=" value: "\xe3\x96%D\n\xd4i:\r\xe8|H\x7f\x8a\xac\xa3_MS$:Q%\xeb\x08\xfa$\x8d\rZ\xad)" } internal_nodes: { key: "BTA=" value: "\x06E\xe9\x15\x13Z\x0c\xc9}G\xc9!\x11ژ\x0f\xcey\xdd\xdb\xfd\xd3\x15'gL\x9a\xa6\xed\x01+\x02" } internal_nodes: { key: "BTg=" value: "\x18>\xdeU=t\xfa\x82[\\\x86\xfb\x0e\xbb\x89F\x0f\x18\x8a\xd8\xe2\xb0\\7f\x02\x85\xee\x90Y\x07\xb3" } internal_nodes: { key: "BUA=" value: "\xdfxt\xbe\x81\xdd.\x91\x0cL\xcc\xe2\xbb\r\xd3\xf3\x16g,+i.1\xf2\x1ee\x90\xae\xc3\xca\xc5," } internal_nodes: { key: "BUg=" value: "\xb2\xa3k\x04\x174\xe4\xe8\x9a=`Q@(\xf7ÃĄ\x97i\xac\xce\t\x9aA\x91\x9fN\x8f\xff\xd5Om" } internal_nodes: { key: "BVA=" value: "|\x7f\xbf>\x04\x10\xa2ۈN@U(\xa5\xa9\n\xcd\xcc\xe5q\xe6\xdc\xceWL\xec'\x86\x8bË \xfb" } internal_nodes: { key: "BVg=" value: "l\xd0`\x98N\n\x9aÆģ\x08\xd8\x15\x10cb\xafʗ\xe8\x04Lyv\x19W\x88\xa2\x99\t\x85\x86\x16" } internal_nodes: { key: "BWA=" value: "\xef\x1818ěK~Wo\x95\xd9`\xb65\xedL\xe1N\xf6w9{$\x03Ŝ2\xeb%\xe4\xe6" } internal_nodes: { key: "BWg=" value: "\xfa\xc5f\xda:\x92\x9fo\xaf%\x0fw\\(\xfe\xb6\xa3\x01\xee6\x90\xae\xfb\x87W\xff\x04%+7\xbdW" } internal_nodes: { key: "BXA=" value: "\xaa\x03\xac\xc8A7\x80\xee\xcd\xfe9wu\x88NKO\x05\xbf\x85^rCĪŦ\xc67>jx\x85\xcb" } internal_nodes: { key: "BXg=" value: "\xf0}\xe82X*\x12Y\xefÕĨË­N\xf1\xa8w\xdb\x1cR'\xd2\xc3G\xc7:\xd6Ū¨\xc6KY" } internal_nodes: { key: "BYA=" value: "\xe5Ua\xdb\x01\x96\xec\x0f1\x1d\xcd\x18p\x88\x08p\"\xb0\x8bא:dhL\xc5\xd6\xfc\xc0\xc52\xa6" } internal_nodes: { key: "BYg=" value: "\xe2\xb6aĐĄ\x82^\x0eK\xeeG\x897\x04i\xa6\x96\xb1\xbc\xddhQFS\xf9ŪĩC_ZŌ‡" } internal_nodes: { key: "BZA=" value: "\xa8\xa2\xfe\\\x9f\xa4\xe5o\xe1\xa5\xcer\xca#\x8b\xbc/\xad\xddr\xd2\xe2\xdcA\xc6\xf8\xc2s\"\xe2\x190" } internal_nodes: { key: "BZg=" value: "\x9a\x16\xc2_\x15+b\xb4\xc76Dvz\xac\x07:\xa9\xfa\x9f\xbf\x83\xdf\r\x84\x9e&C\xbd\xf1\xd3Ũˆ" } internal_nodes: { key: "BaA=" value: "\xae'\xbe6\xf4.X\xf0\xa3\x89AZ\x03,\xf4Es\xaaR\x1bk%Ns\\\xf0Bt\x02\xf2\xb13" } internal_nodes: { key: "Bag=" value: "MG:\xf0d\x19\xc2#&\xb1\xd38\xf4\xa1\x1c*\xebr\xe9\xf1\xeaad\x92\x85\x0b\xfd\xe0\x18\x91\x04\xa1" } internal_nodes: { key: "BbA=" value: "k\xdfx\xd5\xd0\x11\x1a\x0bD\xb8Gt\x8eh0-\x81\xba\xe9/VËŽ\xb3\x87n\x9e\x156\xb6\xf1\x19" } internal_nodes: { key: "Bbg=" value: "\x8c]x!\x01\xa1y\x1f\x9evB\x8b=\xd8\xe9\x87\xf3\xdbŨ\xc5\x1d\xee\xedB\xfd\x8a\xf2\xaa\x10\xfa\x82" } internal_nodes: { key: "BcA=" value: "\r\x14X96+i\xac\x9c#\xaeb\x94˃\xd0>N\xfa\xed\xa2\xcaB\x04؛4\\\x06\xe9\x0f\x80" } internal_nodes: { key: "Bcg=" value: "\xa6?\x93\n\xa3\xbc\xbev\x16F\xdc\xea:=b\xe8\xf6ȅ\x08\xce\xd2\x12G=\x88<\xf4\x93\xd1Ōŋ" } internal_nodes: { key: "BdA=" value: "\xa0ÃĒ-N\xf9Mbw\x80\xf8Lq'\x9e{1QL\xfb\xa9\xc2\xef%\x13K\xf8\xf6Ȇ:\x03" } internal_nodes: { key: "Bdg=" value: "\xc1\xc9<\x05\xfaM\xf7U$\xe6\x8d\xf1\xf2KF\xceI\x80N`t8\xfd\xfb豍V({\xb9\xee" } internal_nodes: { key: "BeA=" value: "ņ“Š’\xbe\xb5\xfd\t\x9e\x9f\xa3\x81\xadui\xdb\x1f\r\xb0\xac\xd9\x1aD;\xbb\x967\xc7\x0eY\x07}" } internal_nodes: { key: "Beg=" value: "pm0\xdb2\x03\x9ea\x94M]\x0f\xd5\xfe\xed\xa9ōŒ†›\x0e.d\x05\xee\x11\x92\x1a\xe3\x9bn\xbf" } internal_nodes: { key: "BfA=" value: "\xc2\x08O\xceaWpŪŽ\x91~ńG^!]\x16\x0b\xd1p=\xcd\x10\xe9\xae\n\xdf\x15\xea[\xb7" } internal_nodes: { key: "Bfg=" value: "\x9c5l\xa0\xbf,^X\xec\x04\xeb\x0c6\x06\xed\xaf\xe5\x97$\xf7\xeb\x7f\xbc[\xc7\xe4Y\x16N\x0ek&" } internal_nodes: { key: "BgA=" value: "!\xd0ʨ4K\xc6?\x94H]\x89wH\xfc\xe8\xe6Øš\xc8Q\t\xf0\xccwO+\x0f:2I\x9d" } internal_nodes: { key: "BgQ=" value: "\xc6\r;\xf9\xcb\xe8^\xc1`\xf4\xf1$\x0e\xd5\xe2Ū†î—\xa5\x9d\x05)\xbb\x95\x82\xc8=͂\xfc" } internal_nodes: { key: "Bgg=" value: "\x18E\xac\x02\xb0xؤ\xe9\xa7)#d\xaf\xc5%\x83İ\x93\x82~\xdbs;\xd9a\xac\xb0\xea\x03\"" } internal_nodes: { key: "Bgw=" value: "\x9f=Z\xe6\xc9\"ČŠi\x0c\x08O3gr\x00!\xb2\x8c\xb42\xdc\x1ezęŋ¤-.\x19\xb8\x01" } internal_nodes: { key: "BhA=" value: "\xcb\xfd_x\x057\xba\xbd~\x04\x9b\xc3⏛&\xff)ːQx\xe2\xe2\xd9D\xe2\xa6#R\xd1d" } internal_nodes: { key: "BhQ=" value: "\x1a\nD\x82\xfd0\x1c\xd3l\x05\xf9\x8e\xc1\x95\x1b>\xbf#\x96\x12\xcc\xf0\x84\xc4\xfd6\xd6E~\xd9\xd2\xf7" } internal_nodes: { key: "Bhg=" value: "~L\xff(\xa5\x90\xce\x7fV1Įŗ\x9d\xb8\xfc]`\x1a\xed\xfc\x05[H\x00z-%ʁn\xcd]" } internal_nodes: { key: "Bhw=" value: "1\xad\xbc\x0eq:\x94/:\xd8\x1fBYJ\xc9\xc9\x0f\xa38\xb3\xec\xdc\xf2M\xd5\xf3\xe3yxr\xf1W" } internal_nodes: { key: "BiA=" value: "]#\r\xa7\x18\xb8Īŧ\x05%\xc3\x03ɋ\x10uF\x93<\xef\x1cΟ?4c\xd3JȅÄļ" } internal_nodes: { key: "BiQ=" value: "d\xf1'FJu\x11\x08\x88y\x03\x18\xf8\xf3x\xeaO@\x10\xa72\x8a\xaaΓ\xe6yc\xfcG\x15N" } internal_nodes: { key: "Big=" value: "\xa9H\xe4\x9fo\xb1\x80C_\x02O\xc9\xdf\xf2$\xc3\x11\xafL\x95Ņ‹\x16K\x83\xa3\x13\x80y\xaeTH" } internal_nodes: { key: "Biw=" value: "\xc1\xc1\xfa\\\xe6\x1d\xd0ߔO\x1a__\xb5\xab\xcc\xe7\xc8\xe4\xe9\xeb\xb1\x16Z\xb6\xa4\x9e\x96d\xe4H\xaf" } internal_nodes: { key: "BjA=" value: "\xea5\x1dU\xb5G\xc0\xbc\x9e\xf4\xa4\xbay|\xa5\x9bq\x05\x01ßģo\xe8P=\x89\xc6\xf7\x16|<\xa2" } internal_nodes: { key: "BjQ=" value: "\xc9\xe4\xcf+\xc4X\x8e\x07\xbf)K\xeb\x0c\xde\"\xac/;\x95\xb5\xc6P0\xfd\xf6L\xf2\x10/%\x82N" } internal_nodes: { key: "Bjg=" value: "A-\xc2SÆ \x1a\x1c\xf5b\xa6\x9b\xdcA4\x11\xb2x\xb3\\\xcfiu\xf0)(\x02VU\x01\xff\r" } internal_nodes: { key: "Bjw=" value: "\xbe\x95\xe6e\xa37\xe5\xc5\xf9\xcfX\xc8\xe23\xa6֍5拄\xba\xbc\xf4\u0084\xf5\x8cu{>\x8d" } internal_nodes: { key: "BkA=" value: "n\x1c\xfd\x9e5W\xf4\x11\xa8\x0eu|Έ\xd9DZ\xa8\xdaA%\x99\xf5\xb2\x0c9\x80\x97k\xa1Y0" } internal_nodes: { key: "BkQ=" value: "\x8a\x15BF\xfect\x1c\xb2\xfdq\x97t\xcf_2\x10#\xecD\xe5\xe6\xf0\x9bxGÖĒ\xe9\xed\xb32" } internal_nodes: { key: "Bkg=" value: "\xa3\xf5ÛŽ\x86r\xbb\r_\x8e\x90\x8aŲ–\x07\xbc\xb5#d<\xcfŨ’I(\x03\x9f\xb9\x14c\x19\xea" } internal_nodes: { key: "Bkw=" value: "'Ȳ/Z\x11\x8c\xc0\x0b1\x98\xee~R\x85\x14ib\x07\t\xddÕ |\xa5K:\xdc\x1eN\x02\x95" } internal_nodes: { key: "BlA=" value: "ÂŦ\rA\xd1\x1e\xb1!\xf6\x0b\r\xd9\xc0$\xb5\x02\x13B\xbb\xd5|\x82\xf2J\x19\xf4M\xa3G?\xfb\x9a" } internal_nodes: { key: "BlQ=" value: "\xe2B+Ōŋ\xc6\xdbԚ\x820\x11\x8cAv\xa5\x02o%\xf0\xfa\xe8\xec\x1bE\x13q8\x07A\xcam" } internal_nodes: { key: "Blg=" value: "\"zĮ„\x15\xec7\x8d\x16햧\x80D\xcd\xc3\xf3\x86\xf8\xdfP\xa0\xe9\xae\xe1\xc2\xd7\xf4\x7f\x9b\x86)" } internal_nodes: { key: "Blw=" value: "M\x9e\x8e\x91\xfe\x7f$L\xfc\xc3\x16\x03ԏ͙1\x92\xff\x06\xb1\x0b\x01ߤ`W%+\xe2\x1d'" } internal_nodes: { key: "BmA=" value: "\xa2\xab\x15$g\xdd,\xcc&C!\x13\xf3\xe8\xf7,y\xb7\xa2\xeb\xf7\x94\xa7\xed\xcd<\x7f\x91B\xcd\xfc\xdd" } internal_nodes: { key: "BmQ=" value: "\x08op\xa0/4\xa6\x90\x1chTC\xca9\xff%\x93bɒo\xf7g\xe9u\x19\xfafKmÂē" } internal_nodes: { key: "Bmg=" value: "\xb0$7\xf61\xf7\x1d\xfb\x069\x85)\r\x8a\xa9\xb88Ū…d\xa2\x823\x15\xe3\x94\x0f\x95\xad\"\xa9\xa3" } internal_nodes: { key: "Bmw=" value: "a$\x01\x9eu\x13\u008f\xfd`\xfb\xc0+Nz\x9c\xa6\x0fR\x12\xe9\x1c\xa4\x0b\x97-àB\x9f@\x0b" } internal_nodes: { key: "BnA=" value: "\xe5\xe3\xa9N\xd8\\A\xba};ʝ\xf2\x16\xfbP4\x10\xe1Rx%\x8d\x0b\x16\nŅ‹\xae\xb2\"~" } internal_nodes: { key: "BnQ=" value: "\xd7\xe9YR\x1f=\x988T\x06#,\x8e[\xad\xd6:\x1e\x90\x14p?K\xe9\xd8В\xcdu\xee\xe0{" } internal_nodes: { key: "Bng=" value: "\xf9~\\\xd6\x13\xf1\xbfp\xda\xc4,\x19\xee\x91Z\xc1[7'\xb26z\x823\x7f\xfe\x16\x02^[\xc3\x19" } internal_nodes: { key: "Bnw=" value: "o\xa3\x996Jh-\xcb\xd1c\xf5=\xa6\xbd3\xeb\x1e\x9dp\"\x96\xb6ۗ\xaa\xad\xa5\xef{V/\x0c" } internal_nodes: { key: "BoA=" value: "\xde\x0c\xa5\xe9tU\x15\x94\x84\n\x9dҤT\x17\x1f\x87\xf8\xf5}\xb5}Oo\x8e\xdc\xeb\x8a\r%\x9e\xf1" } internal_nodes: { key: "BoQ=" value: "\x07\x07nD˗$Y\xb5\xf7\xb9d\x9d\xb5&Vl J\xdf5\xe4\xbc\xda\xd49\x07\xb6&F\xce\xcb" } internal_nodes: { key: "Bog=" value: "\xd5B\x0c\xb7\xd1Ô§5I\xa9.\x9a:1\xb1\xa6\xf2\x19E\xcd(\xb2q\xbe\x0b\xc1\xeeN\x18\xaeV\xf3" } internal_nodes: { key: "Bow=" value: "\x93\xa1\x8e\xc1\xef\xc1\xc5\x7f\x9e|}r\xe1\xe0 \xed\xd4\xeb;2zF\x85\x18\x9e\x12U\x14n9\x05q" } internal_nodes: { key: "BpA=" value: "8\xe4\xdc\x0f\xa9\xd5}n\x17\xd0bK\x07\\\xb5\xf0mU\x04B\x13\xa7<\xe4;\x16\xaf\x1f\x87|6\x16" } internal_nodes: { key: "BpQ=" value: "P\">\xad\xe7z۟\xc5%\x10\x0fAH\x85\xe7\xc4\x14\x80F\xaa%\x11}\xa2\xfdƌX\n\xbbl" } internal_nodes: { key: "Bpg=" value: "\x1an\xa7\x85\x11=d\n\xe1[?\x16\x8f\xf8\xd27\xfc\xdbm\xe4aĖz(\xd4\xf4\x0fxDE\x1c" } internal_nodes: { key: "Bpw=" value: "\x01\xbds\x9e\x14\xaa*\xa0\xfc\x7f\"\x91\xfa\xab\xe5\xf5v\xe1Ya,ĪŊ7/\xf6\x02\xa9\x9e\xb2A\x1a" } internal_nodes: { key: "BqA=" value: "\xf3\"h\xc4\x10\xb1\xa0\xbaG~\x80m\x8es\xd3\xe5B\xf9\xdf$\xefnG\xc6\x12\x00:\xb6\xed4\xecy" } internal_nodes: { key: "BqQ=" value: "\x80a\xe8\xf1\xb6s\\\xb8\x16\xce\xc0\x01\xfd\xfb\x9a\xcf>\xc7\xfd\x87\xc9mÅ´\xa9\x01d\x13x\x98\xb4;" } internal_nodes: { key: "Bqg=" value: "T⎋\xa6\xa8\xccSK\xa2E\xff\x97mZ\"8\xc0\xf8\x19\t\xfeR3-\xea\xb5;\x94\xd8\xfa\xa6" } internal_nodes: { key: "Bqw=" value: "\xda\xca\x01P\xceƊ\x94\x847>\xa2|-g\xa4\xf0\xe7\x07\xee\xa0\x00d\x8fi\x87`\xe27\x8e\xb61" } internal_nodes: { key: "BrA=" value: "\x96\x05\xd3\xf7r\xf2'\x8c\x80\n\xe9\xbd\x18\x14\xf7\x91hA\x08\x03+\x04\xcaÖŦ\x94\xb9\xa2\x05\xee\x12&" } internal_nodes: { key: "BrQ=" value: "\xb3o;\r\x04\x18\xe6\x8dG*\xa52 `\x1d\x01\xba{\xa7\xedq\x1e\xbc\xa7\xcdu\xa1Yj\x0b\xc9*" } internal_nodes: { key: "Brg=" value: "h\xbb\x9a.\xa2\x01\xf4ãģŧ\xe5\xdc\xd0\x0b\x04Ãĸԃ&\xc7Ћ\x9f$+\xe5\x15!V\n\x8e" } internal_nodes: { key: "Brw=" value: "\xf7H\th\x1d\xf8\xd0H\x90L\x13\x1e\xef\xa0\xcdßŦK\xe4tT5G\xf1J8\xd5˟\x83\x84\xd9" } internal_nodes: { key: "BsA=" value: "\x9f\x82D\xd2R~1\x8b\xc8\xf1\x10F}M3m0\xf4\xac\xffvfs{\xcb\xc1\x7fW\xf1\xc3\"\xad" } internal_nodes: { key: "BsQ=" value: "\xd4\xedڏw\xdau\x0f`}gK\xa2w\xab\xfe\xea\xed )\xa3\xf4\xd70\xb2Y\xf9u\xefA\x94\r" } internal_nodes: { key: "Bsg=" value: "\x9bP\x1a\x17\xb7\xf9\xffk[M\xaez]\xc0Bˋ׎SJ7\xeb\x10h\xd9XÅ˛Û‚S" } internal_nodes: { key: "Bsw=" value: "\xaa\xde\r4\x17\xd7\x1b*\xb4\x03\xfaYh(T\x87\x1ac9%n\xba\xf7V\x0fwB\xc8i\xf7ܧ" } internal_nodes: { key: "BtA=" value: "J\xe2\x95+\xb2*Ū¸\x93\xbe\x009f\xe3>\xfeK\x87$ K9\xa8\x1f\xbeO\xad6Ɔ\x8c\xd9" } internal_nodes: { key: "BtQ=" value: "K\x96\x9e\xde\xd2\x00\xb8\x11\x1c\x01\rn\xce$(u:2$G\x94\xe9\xedd_[\xaa\x0e\x90\xb3*v" } internal_nodes: { key: "Btg=" value: "\x8eŨŧ\xab\xdd[\xd4:\xd0\x03\"\xf2fÚŠf@#\xd80m;\xbf=\xd3\xea\x16\x14h\xe7\xb69" } internal_nodes: { key: "Btw=" value: "\xb5*\x7fãļ\x8d\x81!\xaf\x1d6Q\xb8'/M\xe8=v\xfb\xe4\xee\\\x87[=\xd5\xf1\xe9F\xb6" } internal_nodes: { key: "BuA=" value: "r\xd6-\xa7\x00_\xab\xfeSz\x1bf\x06B\xca\x12F\x11\x13\xfd\xb8\xf3+\xa4i3V}\xf0" } internal_nodes: { key: "BuQ=" value: "\xf0?\xbeC\xf8dwqk\xc6\x0c\xddj\x1a(\x07\xd6\xe8\xdcåļ¸\x85\xa5\xfa\x81\x95\xcc\\7\x1d\x94" } internal_nodes: { key: "Bug=" value: "\xf5 \xd1Íšd\xeb\x0f\xd2\xc4\xed,\x1c\x9b\xa3\xa7m\xb2>?{J\x1e\x89\xe97+~\xa1%:U" } internal_nodes: { key: "Buw=" value: "\xb9\x05I~u\xd41\xe9\xa7\x7f\xb1\x9f\xaa\x90\xea^\xa7Py\x9e\x18\x15\x85\x95G\xe4-\x8f\xff\xcf\x00\xc8" } internal_nodes: { key: "BvA=" value: "\xaf\xafih\xc6\xcf\xe5\t\x1eéŊļ\x1e\x8d\xd72\xd0\x16\x18\xcf@m\x8b\xeb\xa97\x9a\xbf\xc6\xfb\\\x7f" } internal_nodes: { key: "BvQ=" value: "\x9a*N\x17Ć/\x1fh2K\xa7\x08\x82_/\x82\xdf\xf5z&)R\xb6\x91\xbc\x89\xb0\xebU4+" } internal_nodes: { key: "Bvg=" value: "\"\xa9\xe3g\x06\xa1/t\xca\xc2x~Ah\x00\x8dz\xc9q\x03\xb14\x0b\x15\x9bF\x88\xd5Gz\x00^" } internal_nodes: { key: "Bvw=" value: "H,\xc1\xdb@N\x12K@\x9a|\xd1\xf2\xe9ek\x00\x12\xb3\xb3\x95\xae9\xc0!b\x0c\xe1H\xc3P/" } internal_nodes: { key: "Bw4=" value: "1\xab\xf8\x9b.\xf7\xb9\xa32t_y\xffG\x18\xa0\xd2=z\x16|Z.v\x03in\xe8\x14e\xb6\xf6" } internal_nodes: { key: "BwA=" value: "\xc8ld\xcd\x0e\x8b\xa8g\xecSM\x1erz\x08\xbbY~y\xb2o\"\xc2\xd0\xed*U\xc6\x04\xaf\x1d\x19" } internal_nodes: { key: "BwI=" value: "8M\xf39,\x08'\xdc,Ė‹\x1a\xc7\xf0\x7f\x1e\xa5I\x1c,aۏ\xd6We" } internal_nodes: { key: "BwY=" value: "\xf6R\x9f\xde^&\x00\x8f\xf9\x8d%\xe6\x02\xae\x95\x94<\xbe\xae\x9e'\xb4\xc1C\x84\\\xe5Y\xc0\xebT\x9a" } internal_nodes: { key: "Bwg=" value: "\xda<\xfd\xcf\x03\xaa\xf3\x91\"\x18\x18\xb1\x1f\xcc\xd0Į˛z\x90\xa5\r}G\x82\x97\x86ax\x9c\x8e\x84W" } internal_nodes: { key: "Bwo=" value: "\xca~\xe5\x8a[]\x15W\x1ao\x99\xa2B\xf9\xce1|\xb9\xb1\xe1U\xf5\xed\x04\xaeY^\x98\x1f\xc7?\x1b" } internal_nodes: { key: "Bww=" value: "\x89u\xd59\x1b\x8drLU\xb1\xfa\xad_\x8bh\x11\x1a\x96\xfd\x8aC\xad)n\xe4\xf3W\x8e\x80\xabjA" } internal_nodes: { key: "Bx4=" value: "i\x1a\x81q\xf8p\\\xd6\xd6\r\xaa\x9f\x04\x82\xc4\xf0\x07;\xa1s1\xc3(\xddM\x93\xb8 \x01\xf8-\x9e" } internal_nodes: { key: "BxA=" value: "fm\x1c\xb6\x04po\xb9\xa9\x84\x0e\xcc\xc1\x81L7\xe2{H/r\xbe\xcf\xf1R\x82\x80>w\xbeqt" } internal_nodes: { key: "BxI=" value: "\x1d\xc6(z\x14+E\xdbcFE\xab\x96k\x81\xbb\xa3\x81;\x8c\n\x85\xb5\x1cH)\xdc\xfe\x9a\xe8\xc7\xfb" } internal_nodes: { key: "BxQ=" value: "6\xcc\x05\x864\xc2|\x1e\xccZ\xa8?M\xf7\xe5JlI\x8f\xae\xbbu\x88L\x9b,G\xf9\xc1}\xdf4" } internal_nodes: { key: "BxY=" value: "\r\x03ho\xbc\xc9j\xccpl\x9c.\x03h\xa6}\xf0\xf2˙\xd3\xee%Ų”j\x85\xc4m'\xd7(" } internal_nodes: { key: "Bxg=" value: "\x17\xa5\x85\xc1\xb5\xf8\xd56^:H\x92\x8d@\x01\x0eh\x19%F\xbe\x92\x00\xba\xde&\xec\x88\xfa\xc6r\xb7" } internal_nodes: { key: "Bxo=" value: "\x9bu\x88V,\xdd@\x05˟$K\x18\x08\x89\xb4\x8d\xd6\xf5 O\x90\xff\"ĮŽÉŒÎŋ\x95\x10" } internal_nodes: { key: "Bxw=" value: "/6F\xee\xec\x13tp\xcd\x01\xea\xa8\x03:\xd9q\xee\x11\x8b?\xfd\xe5\xa7\xd6\xeb`؊P(((" } internal_nodes: { key: "By4=" value: "\xe9{\xf0R\xfa\xech\xae\x07\x861X\xcb\xcd\xf5\r\x93\xc1dbAw\x98\n\x15\xd6id\xe4\xfd\xbd\x11" } internal_nodes: { key: "ByA=" value: "\xbaqN\x0f\xfe\x08s\x1e/\x9dr˨2\xbb\xbd\x0e\x99:\x18\xcc2,\xa2F\t\x98D4\xd1ڜ" } internal_nodes: { key: "ByI=" value: "\x7f\xe5\xa6f#\xfb\xd5\xc1x,\xd5\xf0\x98T.\xbdI\xe1n\x8eJ1w\x96&#E7/d\xf8\x9a" } internal_nodes: { key: "ByQ=" value: "}JвdJ!\x04\x0e\xbc\xadI\x7f\xc1\x0c!\x0c\xf9\xccs\xe6|[A@?t\xd30\xcb|\xd6" } internal_nodes: { key: "ByY=" value: "{\xd0VQg\x82T\x08\x02x\xd1-<\xd1\x15\xd5[\xaa\xf9\x17\xf9|\xe2#r\x18\x85\x97\xfd\x06\x12\x0c" } internal_nodes: { key: "Byg=" value: "\xef\xb6oC\xe2\x10Q[\xdd\xd9R\xff\xd8Ed@_\xcbD\x8f^\xa8\xfc\xa4\xaa\x0e\x90W\xea,#\xcc" } internal_nodes: { key: "Byo=" value: "Q\x17OŨŖ\n.\xa2]\x08\x83ڂ\x82\xce:w\x00<\xf02z-\xa1`6\x1d\xf4r\xd7\xe1\x8a" } internal_nodes: { key: "Byw=" value: "\xb0\x1d\xb5#\x835N\xa4\x8d\x8e\x90\xf4;u\xc6\xfd\xa8\x17\xda\xfb\xee\xff>\xab\xa6\xeeÕŠ\xd8\xc6\r." } internal_nodes: { key: "Bz4=" value: "\x1btu\xbd\xc9\xcd\xf59W~\xf6ʛ\x82Qj\xdeG\xe1\xe0\xc1\xd7\x19\x8c\x98CO\x1c\xe51\x12#" } internal_nodes: { key: "BzA=" value: "\xc7\x1fQ\x86}\x97a\x98\xda\x18\xeaq\x97\x05\xb5\xf38;f]\n\xb1\x9b#\x8b\xed\x94;G\x8bc\xad" } internal_nodes: { key: "BzI=" value: "\xac\xfa\x8e\x04\rOL\x11\x0b\xe0\"\xd5i\xa1\x95h\xac\x10H[5\xa8F\xd0\xdet\x01\x17Qk.\xb5" } internal_nodes: { key: "BzQ=" value: "\x19\x81D\x175L\x81\xb7\xee\xceWS\xdaAƄ\xa5\xd8K{\xde\x17\x88\xf7\xf4\x14zB\xa0Y\x80G" } internal_nodes: { key: "BzY=" value: "Z\xfd\x82?S\x12f\xee\xa4\xd1\x118\xdbY\xb5\x9ef8\x16\x16\xfe\x08&py\x87U\xffQ\x02\xa2\r" } internal_nodes: { key: "Bzg=" value: "\xd5@\xec\xe4\x1eæŧ¤\x82\x80\xf4`\xd70d,j\x8b\xb0\u0080\x08iXvÜŋᔖ\x0e6" } internal_nodes: { key: "Bzo=" value: "a\x8b\x1b\x95x\xa6\xb34\x8b\xcd\xc3\xdc\r:\x04\x95i\xe5_\xea\x91;\x1f\xfdC\x11\x18$\x8cÎĸ\xf9" } internal_nodes: { key: "Bzw=" value: "âĢģ\x88d\x1c]B4\x7f\x88\xf0\x8d)d\x83\xbb\xf5\xc0$f\xfa\x9fkc+\xdd*\x9e\x15H\x9a" } internal_node_count: 254 prefix: "\x00\x00\x00\x00\x00\x00\x02" depth: 8 leaves: { key: "CA0=" value: "\xf7T\x9c\xf1T\x89\xa4\x84\x1f!\xc2\xf7j\xb4\x91N\x9dÚģ8\x1c)GŲ§A\xdbe\xb2\xe0%\xd6" } leaves: { key: "CA4=" value: "\xd5;|\x1eig=\xf8kO\x82\x9c\x95djc\x9a\x8b^\xfd\x8e.'\xb6\r\xbc\x84\xdc\xd5\xed:\xaf" } leaves: { key: "CA8=" value: "\x0b\x03\x9d\x86F\xb6\xddy\xf8P\x05\x1e\xfe\xce\xe4\xc6G\x16\x16h\x98\x8b\xc8\xe9\xf9!\xba\xdd\x12\\\xa4\xf4" } leaves: { key: "CAA=" value: "\xf1,'cI\xae\xbf\xccR=v\x9e\xe47\xa1\x01F\xbf\x8e\x00\x14k5\x9dh*\xce؜\xd5\xca+" } leaves: { key: "CAE=" value: "\x93\xfe\x1a\xdb~\x8bZ\xd5\xef\xf1\xf6\xeb*\x1d\x96X/\x95\xcd\xe2\xdfZkΏ\xe6\xa7(\xb3\xa4\x0c\xf2" } leaves: { key: "CAI=" value: "\x80\xc9B\xfe\xfbŲ†g\x95\x80\xa50G\xf1\"\xfbW?hB\xf2\x08\xe6\x17\xd5\x03\xec;\x85\xdf`\x04" } leaves: { key: "CAM=" value: "\\_\xf7\xe4_\x9dQ\xa9\xe7'TE\x01\xe1د6\xc1\xb1Ŋ \x85\x97\x97h.\xb9Z=k\x80" } leaves: { key: "CAQ=" value: "\x83\xf8\xb9\x19\xbb\x07\xe7\x14\x15\xf1\x0f\x89MFÆš\u0084\x1d\x91\x0eO7\xa2@9\x0f\xb3\x08\x16\xd5{" } leaves: { key: "CAU=" value: "\xf7\x8f\xe3\x02\x1a\xe8r(\x85\xab\xcb\"\x86\x84\xca9\x0f\xe3*p\xd9\xe7T\xf8q\xf8\x88G^\x9c\xb7\xd4" } leaves: { key: "CAY=" value: "/\xfaa\x98\xec\xd4\x0e?\x94\xf8\xdd\xcb_ÃĢ<\xcf?\x80J\x88\xf2\xa0\xadl\xfd\xfc\x17\xa9\x90g\xb7" } leaves: { key: "CAc=" value: "\xabq\xb2j\xac\xc0)\x8a \xfaËē\xa6C\x00\xcc\xe0\x03l\xa08\xb7\x9dh\x80\xf2\xf7\xca\xc1\x82)'" } leaves: { key: "CAg=" value: "\x0b\xb9\x8bq\x90\xdb\xf2s\xe4d\x88\xfb\x8e\x9fx\x98\xb7\x92Q\xca\x16\xb4S~Ne\xd3\x14\x12:6\xc3" } leaves: { key: "CAk=" value: "\xbe\x05(\xfe\\\x08\xc3+\xc6\xde\xc7ÔŦ\xff\xb9JO\xed\xc2P\xf6\x19i\x15\xbbe\xcfßŊ\xa1g\n" } leaves: { key: "CAo=" value: "4Y\x06\xc2U\x15\xa4i9d\xbe\x88\x1bGR\x9b\x90\xb8\xb7\x98\xaeM\xab\x17L\xf2\x98c\xcbA\xc4]" } leaves: { key: "CAs=" value: "\xa92\xa0\xa0\xfe\xd83\xa8\xf5\xdf.u6U\xda\"Se\x1fÛ~\x1e\xed\xfbT\xbay\x8f\xa1b\xa4" } leaves: { key: "CAw=" value: "\xbdk\xcb\xd6\x1dF4A\xf8\x99\xee\x12\xff\xec\xf9\r\xb4\xff?6&\xc2\xff+\xaa&\xc2ejSde" } leaves: { key: "CB0=" value: "F!\x97\xff\xef\xcb\x03K\x02\xebÜŊ\x90\x93\xb8dw\xee~\x8d`,ZL0\xe8Øļ:\x03 \x02" } leaves: { key: "CB4=" value: "\xa7>R.u}D\xd1h\xf0y\x88\xf9\x1d\xfb\xc3\xe8\xf5Ú´\xa2\xb9ŪĢ\xfdB\xa7\x04\x0bĖē=" } leaves: { key: "CB8=" value: "\x8b\xbc\x81j\x05I\x93I\xa2\xa0\x1f\xe7\xfdN72\xea\xe9\x00?\x08+\xb1\x89\x89\xc1O߃\x14\xdb!" } leaves: { key: "CBA=" value: "\xfd\x16\xdb1\xae*\x03\x82\x88\xf1\xfc\xb8\xa4\xe7\x8dG\xdeS\x9bU\xb2SN\xf0:\x02r=\xb8\xa5\x0bS" } leaves: { key: "CBE=" value: "\xe7f\x05BT$j\x90\x03t\xd7\x16\x0b\xe5\nU\x11\xb7\xf7J\xc6+\xd0?&\xa3\xd0\xf1\xa082c" } leaves: { key: "CBI=" value: "t\x8d\r\xe5\xc6 \xbdy*\x8e2QZ\x81\x8a\x12\xc5\xd9\xcf Nib\xacx\xde *D\xd3" } leaves: { key: "CBo=" value: "t\xe3\xe8\xba)\x87)\xa2\x89}\x0fV\xff\xf8\x14\xc8'T\x98\x04\xce\"{ˎ6\xbc\xea\x0cX\xb7\xe2" } leaves: { key: "CBs=" value: "\xda\x14\"\xba&\xa4\xe7\xe4\x18I8Z(\xbdKÛĒwK\xfaH\xf3\xd6O2\r\x0c\xd4\xf3\r\xb7\xf2" } leaves: { key: "CBw=" value: "\x04\x18\xec/5G\x1c\x1eb\xf3N\x90\x89\xf2&?C\xdcpJ[G\xd6%mE-nX\xe19\xee" } leaves: { key: "CC0=" value: "n\x8cMW \x9ej\x89\xec\x02\xa2%p\x85;\xe0=\x1a\x90\x03\x91\x0fn" } leaves: { key: "CCA=" value: "\xcd\xce\xf6u\x90gG\xf6q\xafh\xfc\xd1\xc9\xf2b5\x8e\x95c\n\xd6F\xaf#\x11ms\x08\x06\x90+" } leaves: { key: "CCE=" value: "\xd6d\xe9I\xe4\r\xead\xe32\xef\u009a\x80vS\xc9 \x17?\xbb#\xfd\xfdM\xb2\xb1EL\xd6e9" } leaves: { key: "CCI=" value: "|\xeazuD\xab\x10%\x8a\x13\xb7\xa5Z+f\xfa" } leaves: { key: "CCQ=" value: "\xf3\xf5\x0cP)Pk\x04\xc4^\x14C@@gS3\x98(\\ \x1a\x12\x8a\x8bH4\x9f\xe6y\x01\x04" } leaves: { key: "CCU=" value: "\x90q{\xeb\xccy\x9f\xebC\x10\xa2\xb1AI\xc5&'\x0ey\xceڎ\xe3%\x13\n\xbd\x82B8\x8d9" } leaves: { key: "CCY=" value: "brl\xabT\x88\x99\xc6\xee\x07\x1b\x07\x93ͤc8\xf2gzOyY\\\xfaGׯ\xc3,\xb5\xe2" } leaves: { key: "CCc=" value: "\xad\x99)\t.\xa0\"\x93z\xcd\xc6\x06A\x05qs\x1f_F\xea%\xa1\xfe\xb4\xc5{o\x0f\x8aB\x11\xc4" } leaves: { key: "CCg=" value: "t\xd6il\x15{\xfe86\xb4\x9b\xc0Ć\xfa\x1dL\xa5\xed[Yu)\xd7ĶŽ=\x8c\xc9uT\xea" } leaves: { key: "CCk=" value: "\xf5\xdd\x04Ō‰\xb1\x97\xe5\x0b\xa8\x82\x05\xca\xf9\xf9\xf7?F\x87\xed\xa0\x1eiŅ—'V\x89\xd8t\xc9Y" } leaves: { key: "CCo=" value: "\x16\xff\x9c\xd8=^\xe8\x06\xe8郅h\xd5j\x12_\x91\xfe\x14_Z\xfc\x8e\xa1\x17\xaau\xb5\xc0\x83\x0f" } leaves: { key: "CCs=" value: "\x0f\x7f\xc8\\Rr\xbd,\x1a\x89$zl\xd6\x12\xcd\x7f\x83\x9e\x13J:Į¯\x1fÅ­\xa5\xe7\xb3A\xd5" } leaves: { key: "CCw=" value: "\xb8\xbc\xba\xcf\xd6\x11(,\x0b}\xa9\xe1Gd\x00R\xf8^\xcd<\x96HR\xd2\xcaS<\x8d\x8fj\x8a\xf1" } leaves: { key: "CD0=" value: "\xde$\xf6\xbd/]\xecI\xac*\xc4.X\x81o\x04\x1fo\xe2K\x8d\xc0aB&a\xd4\"\xd5\xfd\x11\xcb" } leaves: { key: "CD4=" value: "7\x86H67\xbcW\x9f-B\xd6([\xee\xc9\x14\xa8\xff\xa7\x0e\xeb\x8f:\xbfs\xfc\xe9\x9e+\x15\xfa9" } leaves: { key: "CD8=" value: "\xcaj\x10\xd4g\xc0]\x1aOz\xea\xc8\x02b\xf3\xa36\xfb\x12[G\t\x89\x84\xed:\x9cf\xf4\xa1\xa3k" } leaves: { key: "CDA=" value: "\x0b\xff\xf4\xfe8\xfa\xc7>\x1d\xb0*\xa7ה Y*\x0f\xc2/N\xc1\x81\x94h/\xea\"\x86\xd3D\xd5" } leaves: { key: "CDE=" value: "\xe3\xea>'{\xa1~ŨŠ\xf1\x83K\xce\xcb\x07\xef\xc1x\xd0" } leaves: { key: "CDQ=" value: "\x12\xeeN\xe7\t\xec\r\xa6\xb6\x81Aet\xfe\xca\xe1\xb0\x19(\x7fJ'JC\xec{\x1b\x98#㖐" } leaves: { key: "CDU=" value: "\xc1\xa3\xd3\x04gZ\x1c\xdf\xf4p\xd7l\xf9<\xbbQ\x81ILL'J\xadr\xd8RqzX\x1e\x19\x9c" } leaves: { key: "CDY=" value: "\xa4\xcd\xfdiY)\x12$\xbf\xee\xa2\"\xa3\x12\xe5,k\x94O\xc7\xfbF\x83\xd4\xec\xc4&\xb9p\x8c\x87\xef" } leaves: { key: "CDc=" value: "Zy\xbf\x98\xbdc,\xfd2\xac\xceŌ“\xbbV?\xc8!\xff66$\xa9@\xf1āĨĸJ\xa4\x95\xd3" } leaves: { key: "CDg=" value: "\x8fJ\xaf\x1a\x1c\xadR\x0b\x84\x1cn\xe8\xd7\xe9\xe72'G!\xf5\x7f\xf6`\xc2@0\xb3;\x05\xd1\xe2E" } leaves: { key: "CDk=" value: "\x0c\x91\xd1\xcd\xe1\xcf\xe7%ÃŦL\x98c\x89\xd0\x18rY\x0eA^\xaca+\x04\x85\xba$6\x02\xc4e" } leaves: { key: "CDo=" value: "\x90Tp~uW\xfc|\x85\xa3p\x19\xbd_\xf0/\x01\x0f\xb7\x0f\xcd9T\x81\xbbNZ\xa1/g\x1d\xec" } leaves: { key: "CDs=" value: "17\xdft]\x8c\xd7\x07\x9a\x8b\xe6\xa2X\x94?ÄļMa\xfd\xbfØĸo\x81\xfa%`ÆĒߛ" } leaves: { key: "CDw=" value: "\x1a\xa8uÚ Dl\xad\xeeJ\xba!\xa0\xc1g\xd6\x11~\x87\xc1\xf2'L\xa5\xba\xca\xc1r7\xafd\xbf" } leaves: { key: "CE0=" value: "}\x87\xf5\xcbŋ\x1at3\xd2\x1fg\"/p\xb5M\xb9-\xf9\xb8\xeaf\x86\x03\xb5Úž\x1fO\xcb\xd9" } leaves: { key: "CE4=" value: "ÖŦe-\x88C\x1aK\x8cŨ”\x89\xcd\xcf\xda\xd8`\xbd\x11\x8e\xfdq\\l \xc3\x01wn\x9e8\x08" } leaves: { key: "CE8=" value: "\xb3\xfa\x88 \xc0\x99}\x8er\x87\x80xÅ´\xa3\x02\x97\xeb\x1aĶŊ\xee\xd6\na\xe5ZL/M\x7f\x18" } leaves: { key: "CEA=" value: "\xc2Ū…K\xf6Ҍ\xf2\xd9-\xfc@S\x1d\xa0u\x85\xda^*\xd1#\xf1\xee%\xc8o\xe6Đ¯\xcd\xd8" } leaves: { key: "CEE=" value: "\\T\x90\xacV[[Y\x9f\xe4R#(\xd0y\xed\xe4\xb4\xf9\x1a\x19T\x07\x00\xb6\xfc\x96\x85\xf1\xc7i\xcd" } leaves: { key: "CEI=" value: "\xbb\nC\x95w\xf6d\x11\x95.\x85\x14\xffN\xf7Ķ BR\xcd**\xdc\x14\xe9\x8e\x1c\x87\xf9\x84\xbd\xfd" } leaves: { key: "CEM=" value: "pG\x99\xceN4m\xb5\x968\xb6\xe4\xaf\xfa\x93\xef\xb1]\x1b\xb2\xb9D\x97\x81\xcc\x1aB\x13\xdb\x1d\xc0\xf4" } leaves: { key: "CEQ=" value: "\xe4Mh\xbd6Z\xf2\x1cu\xa3W\xaa\x13\xf5{G\xfcM\xb3ÕĻ\xa2\xf7e\xd1H\xeda܎&\xb3" } leaves: { key: "CEU=" value: "ˌ\xc4}\x83\xc0\\B\x85\x13\x06\x8c\xb6\x99\x81\xadl\xf4\xc5\xd3:\x01ŌŽv(h\x8eW]\x80-" } leaves: { key: "CEY=" value: "jZ)\x1f\xe3D\x9eGm\xa7&i]=\xdbv\xeb~\x12\x86\x1cU\x10\xf3#c&\x8aQ\xe9\xbd\xcb" } leaves: { key: "CEc=" value: "\xf6L\xcc<\xafn\xb6\x1fH\x8eiT\x19~\xc9G\xae)$]\xe8z\xd4\x0b\x1cf\xdf\xd4e\xfcFG" } leaves: { key: "CEg=" value: "t)B\xb1\xb6?'v\xad%\xb5_\x8a\xea\xda8\xe8\xe26\xfc\xf3\x0fM@Q\x12\xe0Lkm" } leaves: { key: "CEo=" value: ")\x0f2\x10\xf6\x98\x10\xafso#\x01\xceDL\x8f\xfad535\x10KYw\xe3\x1d\x98r\xf7\x15\x02" } leaves: { key: "CEs=" value: "\x86jŅē\xea,g\xb0\xa6\xfbh\x02z\xcb\xe2\xd5\xc4\x7f\xab\xe0\xf6D\x8f\xa8\xb4펚¸w\x808" } leaves: { key: "CEw=" value: "\x14\xe4N6\xf2\x11\xead\xcaXĪĄ\x11\xfd\xcaS\xf3\x8d\x1c$?\x91\xb4T\xc7\xea}6T\xabH\x10" } leaves: { key: "CF0=" value: "\x1d\xca(\xfa+\xa5\xe6E\x1f\x87yL\"W\x96\x84\x9b\x06\xd7\xe1fk2\xbf3Ķ–K;q\x8e\xb9" } leaves: { key: "CF4=" value: "\xe2\x1f\xf6\xb5\xb5\xd5\xfa\xeaʑ9\x00\xb3\xa5Ķ‹Y\x8eCCL\x1c\xc5~=`i\xe9\x9et\xab\xbd" } leaves: { key: "CF8=" value: "\x00\xe1\t\xcf\\#\xd6'U\x1e\xaf\xa6\xed\x82y\xb6.JIGn\x83Cxy\xf6\x85\xa0\xe01\x91P" } leaves: { key: "CFA=" value: "\xe2\xea!\xe1;c\xa7\x971\xe6M\x90lbn\x7f\xba\xd288\xfe\xfd\x8fH}\xe5P\x89\xc8\xfe\x1c\xc9" } leaves: { key: "CFE=" value: "\xcf\x1fu4O\xd4\xee=:3\xbdڈ\xfc\x92\x10\xbc\xea̓\x9e\x8a'U\x0ck*\xe1d\xba\x07\xf0" } leaves: { key: "CFI=" value: "ZЊ\xd4qBE\xe4\xc1K\x1c@\xd7$\x02jg\x9d\x00\xbb|1%\x91\x9c\xeb\xc1s\xa8@a\x80" } leaves: { key: "CFM=" value: "\xb3\xb0\"\xcc\xe9\xe9\x08\xc3\x05\x1dX\xb0\x03O\xb7\xad\x06Ш\xfe\"ZiA\xf1Hzt\x7f?\n\xd0" } leaves: { key: "CFQ=" value: "\x80\x1f&\xe2>6\x91\xb9\xb7G\x1e*\xdbv\x12O\x93\xf2AbŞ\x895\xb1=,\x10@A$\xf2" } leaves: { key: "CFU=" value: "p\xa1\xe8\\gŅ‹\x1ac\x13\xb2\x88\x06\xb9-\xf16M\xea8\x9ekdn.v\xa8&\x98\x0f\x9fl" } leaves: { key: "CFY=" value: "\x81ĘŊ]\xed\xe9D\x80H\xfd%\xa8\xe6\\\x06\xb7\xfbn&\x1f\xab\xefĶŽ\xb7X\xf7\x8c\xc0\x87[\xa1" } leaves: { key: "CFc=" value: "\xc5r\xef\x02\xbb\xfc\x0c\n\xb7H\x82\xf7\xfc\x82r1Q1o\"B\xea\x047\x9d\x1bJw\x84\xfc\xf1\xa1" } leaves: { key: "CFg=" value: "\xe9\xce~\x11\xc1\x12őa\x9d`\x81\xb9\xe6\xe78\xfebwx\x85\x885Ux\x14\xb6\x92\xadL\x89\xec" } leaves: { key: "CFk=" value: "\xa3\x95\xa6\xce\x12\xe6sd\x0c\x07\xb7\xbb (\xb4\x16\x19\xed\xba\x8c\xa4O\xec\xbe" } leaves: { key: "CGM=" value: "tv'\x18<\x98\xaaX\xdd\xe5\x86J\x97c\xde!\xd9✷\xa6\x92\xe2d\xe1j\xfd\x99\x12!\xa2h" } leaves: { key: "CGQ=" value: "g\x9c>\x83)n)/$\xe1\xd6\xfb\x89\x8a\x02\xa6\nZ\xf5\xc8)ᛋ\xfb\t\xaa\xb6\xb5\xc4/\x8d" } leaves: { key: "CGU=" value: "\x8aSͨ\x81\x06/\xf8\xcc\x00\xb1R}\x15^\xe50\xe0\xcfG\x9e\xce\x1bx\xeb\x85\xce\nQ6\xad\xc0" } leaves: { key: "CGY=" value: "\x1dI9\xb06d\xb6ofc)\xac]\xb63p\xbc\x9a\xdd\xeaU\x97&\x0btS\xc87\xc0\x93\xd4W" } leaves: { key: "CGc=" value: "p9t\xcd\xf3Q\xea\xe6uM6(Se\xaf\x17[\xb2\xf9Ҍ\x05\x9e\x88XD\xfd\xbe\xcdg\xe6\xb0" } leaves: { key: "CGg=" value: "\xcf\xd4Z\xa7\xac\x92\xb9\x1eaK\xc5Q8\xa3:\x81\xa7\xf1l\xcb?\xa2\x10\x12\x1b\xbed\x93V\xd6@^" } leaves: { key: "CGk=" value: "V$\xf5\xbc\xcd\xf2\xd2\xfe8\xc7 \xf9w\x014\x80i?y\xe5d\xdc,D\xff;~L\xe4y\xc4\x01" } leaves: { key: "CGo=" value: "R\x94\x04\x19zPsK#čŦ‰\x99\xae\xc1!\xc3<@\x16g\xf1\xcb8\xb1M}\x1a\xb1s8\xed" } leaves: { key: "CGs=" value: "g\xe7\x17\x0c㴔A@\xd3\xfb\xbf\x10\xde\"\xc7\xf6\xe3r\x15oe\xf7\x82~\x83b\xb8=m~{" } leaves: { key: "CGw=" value: "\xe6\t\xbc\xb5\xed\xf4\x15%\x92\x9ctB\x8c\x03ytvsn0\x07\x94\x97s;y\x0c\xdf/\xc8\x1a\xc4" } leaves: { key: "CH0=" value: "g\xa0\x81δ\xc7JC3\x8b\xbd=h5\x91\x8b\x1d\x81\x82\xee\x0e\xcc[u\xbb9\xdam%\x88\x16\x0b&\x7fF\x1c>\xb8\xcd\xf3\xf9\xa4\xdd\x1e2\xe2\xc5V\x8aQ" } leaves: { key: "CHA=" value: "\xd7~+M+\x06Ø !U\t\x08\x02\xa7X\xb5\xac\xf1p\x978PnY[4.XB\xbfL\x96" } leaves: { key: "CHE=" value: "T\xb1\xa0\xae\xaeX\xb5^Q\xa2\x92\xa6\"\"\x85\x93\x9a\xaf\xba\xee+{\xb8\xd0F\xb0ŌĢ\xbc×Ŋ\x9c" } leaves: { key: "CHI=" value: "\xfbb\xc32\xdbG`1Q\x97Z\xe2\xa3\xe7\x1e\xa3\t߲\x81\xb0u\x9bq]{\xceI\xa8\x1f\xf6\xe4" } leaves: { key: "CHM=" value: "5Y\x00P\x02\x97\x0b\xf3\xb6\xc6\xc0\x80\xbc\xb7,\xfe\xf7\x04\xf4\xb4\x14S\xf6k\x9c\xe4H\x19؊\xef\xb1" } leaves: { key: "CHQ=" value: "5\x89\xa1\x7f\x17\xfd\xb7E\xe4\t\t\x9bg\xfb\xe7N[\xa2\x06,*\x89\xbb\xaf\x1c\xcaãŦ¨\xbf\x93s" } leaves: { key: "CHU=" value: "2`\xa4\xea\xe2rШ\xf2\xb4wN\x96\xe7\xce\x17\xfeP\x1b\x8d\xd8\x02'\xfa\x86\xff\x1e\x8a8\x84\x95\x90" } leaves: { key: "CHY=" value: "\xcc!\xd6\xe6ŗ\xe3~z\xd3\xce\x05w;y\xfd\xa7T}\x90\xe0e>e3\xe0.\xbe\xbb\xac\xf0\xc1" } leaves: { key: "CHc=" value: "\x14\xecl\xc1❉\xc0\x83V\x82\x06\x12<įŗŠ\xa2\x97#\xd9]y\x03\xcd\x18\x88\xd1\xf8\xb4\x01]" } leaves: { key: "CHg=" value: "\xc8W7_äǁ\xfb?\xc3@\x8f\xbc\xe0\x05\xeb\xade\xb5\xcfB\xc8N\xbfH\x05\xc1\xa5K)\x0e\xb5" } leaves: { key: "CHk=" value: "\xd1w\x00\x8e\xeb\xa1\xfeR\xe2\x17g\xff\x04\\\"b$\x07r\x97=(Fc\"5J\x9e(a\xc4z" } leaves: { key: "CHo=" value: "\xc4%yÕ˛\xc2\x1bAX\xd1\x07\xcfmj\x1a'P\xcb\x0f\x7f;\xdf\x1a\xfa\x0eb\xa8\xd1|\x08L|" } leaves: { key: "CHs=" value: "\\\x1d\x81\xd3\t\xd7\xff\xec\xc6!\x03\x93Y\xf0`\x95}\xe9\xfb\xe0\xe1Y;\"s\xbf\xd7\xe0\xee\xe1v\xd2" } leaves: { key: "CHw=" value: "\x9bQ.\xb0\xffꄸ$\u009d\xf5zh`\xc2ie5\r\x0e#@Rv\x10\xb5\xfc\xdfY\x07\x85" } leaves: { key: "CI0=" value: "\xa3C\x90\x19\xbc\x08?\x89\xb4\xb9\xf4\x9f??\xf3\xd4Đĩ\xdd-S\xcb\xc1(\x89\x99\xafŪ„\xe0\xe9Z" } leaves: { key: "CI4=" value: "\x10\xe6p\x1dR\x08\x19\x19*\xf5x\xbd\xb7\xab\xe0\xbfW\x92$\xe6\x8b\xfdn\x1b\x9dUTA\x86{\xbb\x92" } leaves: { key: "CI8=" value: "h\xceA#\x80=\x9b\xc9\x0e\xad.\xcfrq\xc8\xdeZ\xf7^K \x04V\xc6VtqÖ \x1bz\xb0" } leaves: { key: "CIA=" value: "\xeb2v\xff\xab)Z\x08\x93X\xe0\x91\x9b&om\xc7E\xb6\x05\x87\xb6\x13M\xe1\\\xef\xe5?\n\x00\xe4" } leaves: { key: "CIE=" value: " 035\x84\xf6\xb6\x8e\x0ekqj\xd3\x1c\xe2\xc7\x12\x87\xf3\x8e\x18ÎĄ@\xfb\x9e\xb4\x9bI 7\xab" } leaves: { key: "CII=" value: "\xe3Ãĩ\xc8[\xc9W\x80&Ѐ\x90\xd7v\xf9<\x91É \xe2\xfe\xa6H\xa1\xc8\xd1\xcc\xec\n\x8d\xd7{" } leaves: { key: "CIM=" value: "6,\x1dG\x9a=\x166)\x03t\x917\x01\xae\x90\xee\x19'\xfd\x88\xfd\x99:\xbc\xe6\xcfN\x07v\x99`" } leaves: { key: "CIQ=" value: "O\xee\xde\xf5\x93ß­\x9cwo\xd7:" } leaves: { key: "CJ0=" value: "#\xf4\x0bn=,J\x9f\x80\xc2)\x9b\xe1\x84Ώ\x86I\x8c-T,\xcf+\x12\xc1\x8c\x9f\xad\xb8EZ" } leaves: { key: "CJ4=" value: "\xf1\xcd\x15l=7\xd8\xf7\xc9sj?#j\x12#rÖĻ\xfe\xab\xf3\xed\x89\xe3\x01p\xfa\x16Ȟ>" } leaves: { key: "CJ8=" value: "\x9d\xa2\x85va\xf5\xfe\xcb\xfb1\x92܋Ev\r/\xd6\x026\xe1\x17D>Ęĩ\xdac\x94\xd1\xd04" } leaves: { key: "CJA=" value: "\xf9\x11\x12\x9d`\x12\"\x87\xf3\xeb\xf1\x8b\xcc`\xfcx\xf0æž 5\xde\x13\x9c\xeei\x1e7\x9a\x00@\xcd" } leaves: { key: "CJE=" value: "Jx\xc1D:\x84DI1\x8b_\xb0{\xe4c\x0cDʀ\x95\x8c\xa1ns\x025\xfe*\x0b\xcb\xfe\xcb" } leaves: { key: "CJI=" value: "\xce\x08\xfe\xfe=\xc3\t!7\xdc0{\xaa\xaa\xaeH\x9d\xad\xfc\xe9\xea\x0bpM\xa2\r\xf6\xa2\xfa\x84\xf4\xb4" } leaves: { key: "CJM=" value: "{Iw`Y\xe8\x8a\x12\xe3\x84g\xae//h\x8b\x82\x98BQ8\xe0\xe4\x90\x19\x16[\xb7t\xfc{\xa5" } leaves: { key: "CJQ=" value: "ʍ\x02\x9f\x80\x98H\x8e\x8a\x8f\xcds\xbc\x91\xf3\x07\xc3\x0f0]\x15\x06đŋĄĨ\xa5\xf9\x10\xf4m]" } leaves: { key: "CJU=" value: "\x1c\xb9\x15Öē\xc5\xe5\xdb\x02\x11<>\x0e\x0b\x14\x99\"\x8cqc\x02\xc7\"\xb2\xb5\x98K\xf2\xe4I4\xa2" } leaves: { key: "CJY=" value: "\xb2\x94\x0bGU9\x92\x04<\xe6dYw^V\x93K\x07\xeb\r\xca\x0em\x1e$X94\x8a-X\x18" } leaves: { key: "CJc=" value: "6\xfb\xcb\x07\xa7o~\xcf\n\xc8[\xbfb\x9b\x9fQ6R\x9b\xaf\xcax90\x8d\xf6\xc50)r)=" } leaves: { key: "CJg=" value: "\x8a)\xb6T9\xa0;:rf:l\x1a\xb8LH\xb6XCQt-\xa61\x0bl\xb6\x0f\x83O\x9eD" } leaves: { key: "CJk=" value: "\xbd\r9\x00\xd4\xd7V)~.\x03\xd91\x84O\xfb\xb8\x9dK?\x87p\x87\x07W\xe3'.\xe0Lg\xe5" } leaves: { key: "CJo=" value: "\xcae,\x9a\xfd\x10<\xff\x9c\xe9Z\x00\x133\xc3ÚĻ\x99E\x08-\xbf\x9cl\xad\xf7\xba\xbb\x0b(L\xad" } leaves: { key: "CJs=" value: "k\x9f\xf2\x16\x01D\xaey\x96\xa4\x12\xd35\xa7h4\x8a#mv7\x94\xc1r\xb2^\xff\xbe\x92:ؘ" } leaves: { key: "CJw=" value: "\x0eLC\xf3'\xe7\xab^\xbd|\xa5\x11\xa3\t9U\x80\x80dq5c\xcfe\xb1;؈\xb4\xd30l" } leaves: { key: "CK0=" value: "5Z\xa8[\x8e͙l~^$0\xa6\xa0E\xd9>\x8eؚ\xac3D\x03|\xbd) \x91\x19\xb2\xbb" } leaves: { key: "CK4=" value: "Y\x9d\xa5\xaaԟ\xfci\x11J\xa6\xd7\xe6DQ\xec\xdf\x0c\xac\xdf\xfa\xa8\x9f\xf8\x93\xd7\xfeA\xf2a\xaf\xe4" } leaves: { key: "CK8=" value: "\xaafÉĄo\xd0\xe1\xd4\xc5~\xa4\x81\xc6s\xca\x0cu]a\x1eU\xc5UÛ¤\xf4\x98\xffhz\xfd$" } leaves: { key: "CKA=" value: "\x82X\xa3\xb2>\x18mMY\xd1^\x81\x00\x8cC\xbf\xcaZ\x06\x17\xc3\xde\x7f\xb0a\xbb\x8aP\x07\xdb7\xcf" } leaves: { key: "CKE=" value: "\xe2Cp\xdf)\x04Z\xfb\xe42\xaa\xe66\xb3\xd6p\x82\x91\xe9k'\xe7\x11\xb2\x88\xf3\x1d\xf0\x92?\xba\x0f" } leaves: { key: "CKI=" value: "$\x1e,\x0bg\"\x13\xa5Đ­\x0f\xde֝\xb0\x07\x08V(q\xa2\xf2V\x91\xf6K\xd8BkŌš?" } leaves: { key: "CKM=" value: ":w\x9d\xa3`@\xa0Eb\x15\xf5\xa0$\xd3\xf5\xe1R`\xdfU\x95\xf2\xa2\x06%L\x12\xecw\xce\x196" } leaves: { key: "CKQ=" value: "\xd07\x13\xc3X\xbc\xd8p\x13\xd7\xd97\xbdQd5\x88\xb7\xc1\x08\xef)kZ\xb7\xa8\xca\x18\x9ai=\"" } leaves: { key: "CKU=" value: "\xf2ȁh\x89Y1ne\xe3M\xce\xc4U\xcfŲ­\xa1g\xd8H\x16\xd9\x18\xb1e\x8f\xbc\x81>EL" } leaves: { key: "CKY=" value: "\xac\xab\xb6\xbc\xa8X~v\x0e˧\x81>\xe1\x0e<\x9d\xdd\xe0߲\xf1vs\xc8\xeb\xbd|\xed\x10\x9a/" } leaves: { key: "CKc=" value: "\xda.\xa0\x1d\x07&\x01\x90ŪŊWR\x1a\xfbE\xf7\xd9s\n\x9f\x7f\xb4Ãĸ\x10\x97H\x86\xf2i\xf7y" } leaves: { key: "CKg=" value: "p\x1c\xf8\ri\nņŠēĒMl\xf6\x98*MhVGy\x1bn0\xa7\x85'\x81\xce!\x99d\x8d" } leaves: { key: "CKk=" value: "\xed=H\x99\x98I\x96\x95@\xe4^~\xd7B\xa0k\xc1^\x88:\x8d\xc8\nh\xcd{\xd7^\xced\xcc\xcc" } leaves: { key: "CKo=" value: "\xff\xdcĶž\xc5\xe5\xb7\xc8!\x84KH!\x83\x15'\x9fk\xb8\x84\x14\x9c\xe0\xdf\x02;\xeb\xd23\xa0y\x01" } leaves: { key: "CKs=" value: "\xdc\x07ER\"\x18\x84\xf0\x0e`b\x01VJ&ÎŊ\x88\xe20\x1btW%\x0c5\x9a@\x8c6\x0fL" } leaves: { key: "CKw=" value: "\xe5ØJ\x1a\xf2\xbe\x0fr\xeb\x1c\xa4F\xd3\xed\x8d\xf2\xbd\xf5\x9b\xe6\xe5\xcc@QU>\x95i\xd5R\xee\x16B\x8d3=\x1f" } leaves: { key: "CMk=" value: "֊čēŠ\x15\xd0\x01\xac\xa9\x99'\x82\xcf\x14\x17r;\x1d\xb3%\xd9`\x8d\xd1m\xffiŪŦ\xf8\x99" } leaves: { key: "CMo=" value: "\x0fmC?>u\x06\x94\xa3$\xac\xf2\xe3\x1fu\x05$\x01\xf2S5\xdf\xc0\xb9\x1d\xd8|]\x87W\xe2\xed" } leaves: { key: "CMs=" value: "\xef\xbfҏQ\x85ܸfpŲēA8\x06\xa6\x13\xa5Ųŋu\xa7,\x80\x97/\xcf\x0f\x07\xa0\xa5m" } leaves: { key: "CMw=" value: "\xc5={\xb5\xc9\\}\xf9\xa3A~c$\xa4h\x87\xe9}\x1e֚\xe0\xa5d\xef\xc1K\xb2\x9a\x85\x8cx" } leaves: { key: "CN0=" value: "u\x02\xbaÉģL \x9a9\xc2\x18\xd6\x1c0\xee\xafH\xc4%b\xbe\x08Į§ÛĻv\x07U\xc4i\x04" } leaves: { key: "CN4=" value: "\x10*K5\x07\xfa\xe6\x18\x1fF\xa96\xe3}\xfa\x9a\xe7\x015\x0c\xa7\xb1;\xb9^G\x90Ķŧ\x98\xfb\xe5" } leaves: { key: "CN8=" value: "m\xe8\xf7\x94\x7fM\xc9\xeeJ\xfb7\xc091s<\x8e\xd03-\xea\xc6\xceYU\x19\x9e#\xbdL\xa5\xf9" } leaves: { key: "CNA=" value: "`\xa0\xca+\xc9C]\xd74\x9f\x93P$\\m!A\xf7\xf7U*\x97\xc7\xddu\xd9Rt\x9bĮ¨\xf3" } leaves: { key: "CNE=" value: "}\x90\xaf\x878\x8a\x14T\ty\xc2\xf5\xca\x05\x86\xd5wPl\xbc\xa5\xaevt\xcf@\xf8Z~\xab\x8e\xaf" } leaves: { key: "CNI=" value: "\xa2\xca]|y\xaf\xb9\xbb\x9e\x07\xab\xa6\n\x08\x1c>0O\xb8x+\x06M\xf2\xe0\xfb\xf7\xdb%\x7f\x92\xfc" } leaves: { key: "CNM=" value: "/\xff\xe4<\"\x97\xfe*&\x04\xe8\xd5\\%|\x129l(\x8c\xec\x17ČĄ\x9b\xcbFO\xb0]\x16\xa5" } leaves: { key: "CNQ=" value: "\xb5\x97\x91\x995\x88\xe8o\xf21\x8d\x87\x9b&Ũ\xba=\x80A\xf3\xc0\"\xd9\r\xb1\x8b\x9eG);$" } leaves: { key: "CNU=" value: "?%3\xb37\x1cm\x8d\r+.\x92\xf7\x15\xcaG\xea\xa0\xd9\x02\xe4\xb7\xe8\xb7\xf60\xae\x85\xa6\x96\xc0\xd4" } leaves: { key: "CNY=" value: "j\x83=\x93\x95\xcaW\xe0&\xd8$\xd4V\x9e\x99\xf1{\x80\xae\xf3\xb0\x8e~\xd7\xc6\xd0\xf6\x88\x94\x9c`\x98" } leaves: { key: "CNc=" value: "gV \x11\xb5ⲨC`o\xd0X(\x17do\x0c\x8cŌ§c\xe0\xc3&=\x8d\"=\xa5\xc0\xc8" } leaves: { key: "CNg=" value: "\x86q\x10\xaa\x11~\x15\x10r\xd8\x1dZ'\xbf\xb8\xd5:A\x87\x86[\x1f\xf1\xdb\x10\xcf\x07\xae\x04> \x8a" } leaves: { key: "CNk=" value: "\xc9\xfb\xbc{\x16\xb4Z3\x1a\x99HN\xf8e\x90âŊĢ\xf4\x19\xbe\x9cR\xe2\x7fI\xbe\x1f_\xdeH^" } leaves: { key: "CNo=" value: "\xc2\x14\xed\x03\xe9\xf5(,\x83\x8a\xe3\xf6$4Q\xf7\xdc[\xe3\xc8K\xb7\xaf\xf2G\xd1Ķē\x15\xf9\x0bk" } leaves: { key: "CNs=" value: "\x80Q\xca\x7f\x8f=c\xa0\xbdhHÛŗ\x88fS\xc8\x069\x8f\xa9-\xd1ŲĻ\x8c\xd5\xdf\x16\xecB\xcb" } leaves: { key: "CNw=" value: "\xe4&\xe4\xf0)\x1d\xa0\x185|\xa6ZL\xe8\xbc\x18\xbav\xfd\xcd\xe4\x1d\xbb\x9e\xda_ĪŠW\xe4c\xf9" } leaves: { key: "CO0=" value: "Fb1`\xdb\x15\xe145\xd0\xfbb8U3`\xf5v\x88\x08\n'aV\xba<~\xa6\x0c\xe0\xd5\xec" } leaves: { key: "CO4=" value: "\xaaS\xb5\xbd|\x11\x0f\x18\xd7\xf2\xa7^\xaf\xc1\x97B\xa4>\xcbX\xac\x14\xc0\xf9|Z\xf2f3\x1e\xd3\xec" } leaves: { key: "CO8=" value: "\x0ej\x04\x0c\xe7mUb\"\x1d|\x82\x8a\xfa^\x08{*\xf3\xf6op\x03X\xdc\xee\xf2ßĸH\xe1A" } leaves: { key: "COA=" value: "2\xb7\xbf\xc8Öŗ\x90:\xa0\x05\x94\xbdl\xe8\xa4{Ro\x1baСB\x88\xa3f\x82\x98\x8b\xb3\xf2\x0e" } leaves: { key: "COE=" value: "?K\x97rۘ\xe5>u>^OYŲž\x1bB\x9ag}[\x1d\xa28\x98\xb9\xf1Z\xe1\x81R\x15" } leaves: { key: "COI=" value: "#\x88{\x8b.F?\x06=\xeb\xbcJ\xc8Ԇ\x9eQ\x18\x1f\rh\xa7\xe2\xef|\xa1\xa4\x03\xde_M\r" } leaves: { key: "COM=" value: "s2yU\xa4\xddy\xcfY\xbc\x9c[\x84\xabh\x81\x90\xc1o_\x08-\x92\xa4jÕŊB\xd0\xee{\xe8" } leaves: { key: "COQ=" value: "\xef58\x08\xc53\xba\xa9\xa0?\xf1V\x80w\x14\xb6\x15:\xb3j3Īē~\x1f\x8c\x1a})(+\xff" } leaves: { key: "COU=" value: "\x06x;\xcc3\"\x98DcB\xb8H\x04\xf5<\x84xP\xe3g\x07}\x8e/&\x19ˑ\x90\xbd\x15\xd1" } leaves: { key: "COY=" value: "\x89)X\xb24M\xc0:\x08\xb4\xe5\x0c\xc4ŲŖ\xa7\x15\xd8\x14\x161X,\xb0*.\x8cV%\xee\xb1\x1e" } leaves: { key: "COc=" value: "\xf9\x89\x05.v\xfa\xd2\xcd5\x0e6n3!\x9e6\x0eBy\xf3\xfd\xd1\xf6\x96 ߛ\x0e\xf5\xf4X\x9b" } leaves: { key: "COg=" value: "\x83\xbcu[\xfa\x88\x02\x88\x81\xed\x17\xb4S\xcfr\x91\xb9_\xc18\xadm\xda2Ņ§â‡‚a\x1b\xa2" } leaves: { key: "COk=" value: "\xe1*\x10\xfd\xf8\x18LČĸI\x10\xf0\xe8\x08\xbbl\xe5\x18\xb6#\x11\x97{\xb6K \xd2D\xfe\x94\xe0\x1cȊ\x90\xaf\xcc\xea\xa2\xeaZH \x9d\xe0" } leaves: { key: "COs=" value: "\xb2\nr\x0b\xa2R\xba\x8bpc\xe1\x16\xe5\x13\xb93\x90\x08\xeb\x07\x89\xa6'\xf3\x92N\xd82{́\x91" } leaves: { key: "COw=" value: "\x11\x93&\x10Z\xebaʡE\xb4O1\xa7\x14|\xe9\xb3`v7\x82\xa5e\xccOF\x9f^\xec((" } leaves: { key: "CP0=" value: "{Jh\xf6~\xc1L\xe2\xc6^0\x04\xf7P\xd5\x18\xea\x7f9\x11\x84\\_t\xe2\xe4\x18Fi\x99{R" } leaves: { key: "CP4=" value: "{\x18ŪŠ\x95M?\xf8\xd9\x7f\xfb\x84\xcb\xd4\xf90\xbf%>w\xb2\x1e\xd1x<,\x80\x9dU \xeb\xa7" } leaves: { key: "CP8=" value: "\n@s+\x0c\x9ex\xf5-$mb\xb4\x1d 2D\xb7\x03!0\xfe\xee\xbd\x1f\xc7\xca\xce\x1dbGZ" } leaves: { key: "CPA=" value: "\xd8,\xefO_\x1bi\x1f\xb0\xa5\xaazŨ§\x01Y[\xae\xc0f\x1d]N2\xb6\xf4\xba\xa3@\x00\xb3\xdc" } leaves: { key: "CPE=" value: "\xbc\x0cA\xf9]\xdc\x7f\xaa.P\x8c\xf21g1\x91턜\xd7\xc5\xe7\x8cg\xe5|\xea\\:\xeewu" } leaves: { key: "CPI=" value: "\x02-w\xf5N\xe4\xc0h\xabBC\xdcih\xa0\xcf\n\x1f\xa5\xb1Fv4\xa6s|gS\r\x13;\x96" } leaves: { key: "CPM=" value: "\xddX\x99\xda4_>\x7f \xaa\xe1=i\xb9\xc5\xc3\xf3\xd1\xed\xa2\xec\xf8ĮĒ7\xe9\x83g-\xc4\xf4\xce" } leaves: { key: "CPQ=" value: "\xb3\xbe\x10p\xe81\x84\xc7m\xe17\xa0\xdb9\xc9+\xfd\x07} \x1a\xccf*\xa6\x1e\xb6CG[10" } leaves: { key: "CPU=" value: "\xe8EB\x918\x12\x83!a`\xd2\xef\x90\xdco\xee\xde8/\x96\xa9\x15\"#\xb8Y\xa4\xa4u\xdf\xc8\xc8" } leaves: { key: "CPY=" value: "\xe6\xc4\r\xba\xbcho>Qa*\x8f{g\x9c\x9e\xa9\xfeZ\xc3\xd3\ra\xb3I\x0b\xeb&\xf5\xb0\x1f<" } leaves: { key: "CPc=" value: "\x9d\x9f+\x17d\x9b\xbdd\x8c\xca՞D\xff\xefA\x0c\xef\xd7y\x13lZ\xdbfW\xfb\x14\xd2\xcd܋" } leaves: { key: "CPg=" value: "\xbf\xa1xb\x1a\xd0Ō™z\x9aK\xd9\xf8d\x12\x809į\xe5I\xac\x9e\x0eX\x7f\xa1x]\x13ی" } leaves: { key: "CPk=" value: "v\x82%\xfb{\xf3\xf5\x07\xad!Q\x9a>\xf6#\x93\xa6P\x80\x1ak\xa4\x80\x16\x99 \xef#t\xa9\x9b\x15" } leaves: { key: "CPo=" value: "C]\xe8\xea\x7fe\xc4\xf4mЊ\x85\x00%#H\x91\x9c\xac\xc8KK\x1c\x0c\x875\xb4\xb7\x10\x01S\x16" } leaves: { key: "CPs=" value: "v\x8157\xf9\x9bHR\x1c\xc4\xdb8_\xc2\xf7\x04\x06c\x8dl\x8b\x1f\x06\x1e\x93\xa4\x8f\xfb\x86\x87\xea\xa8" } leaves: { key: "CPw=" value: "AŨēň\x88\xda\x1cĶŗØž\x15\x04Đ­&\xa0\xfe\x03\xae?^f\xb1\xeb\xd7I\x13\xc7,a" } internal_nodes: { key: "A+A=" value: "˜h?\xe3\xd9-.co`\x99\x87\x1f\xd4kĶē)\x14\xe0E\x9d\xebwZ\x15\xd8m\x80i\xd1" } internal_nodes: { key: "A0A=" value: "\xdc\xfeu\xf2 \xac\xefĮŽQ\xd4:`\x9aS(\xf5\x9eBa\xbat\xc1\xe3\xb0|\xc0l\xd6c\x154" } internal_nodes: { key: "A2A=" value: "|\xc8\x08+Į\x9d@> \xdc`\x88\xd2\xe7ÆĩY姏\xbd\xdd]\xcc\x12\xbb\xb4\x89\r\xf5\xae" } internal_nodes: { key: "A4A=" value: "\xae\x7fc\x07V\xf7y\xb8\x7fq\xf2\xe9,\xeb\xc8e\xcf?\tV\xeb\xf3\xcfn\xc3&;c\x9e\x9f\xfa\xfd" } internal_nodes: { key: "A6A=" value: "\xd8^\xb7\xbf\x8f\x81g\xbdR\xbd\x1a\xd6\xc8\xd5\xfe\x19r\xa2\x14\xd0\xfc~^Y\xcd\xceO\")\xdf`\xde" } internal_nodes: { key: "A8A=" value: "\xa2ŪŽ~K\x11a\xec\xc6<{\xcc\x0e\"\x9a\xc2\x0c\xf8z\xe3Ė´\x9f\x023\x8e}\xfb\xc4}M\x19" } internal_nodes: { key: "AQA=" value: "\x81\x18w\xf0 ~\xbb\xc1\x1c\xda×°\xde\xc0\x00<\xeb\xaal6\x0ch\x02T\xf7\xc0c\x06\xa6\x994D" } internal_nodes: { key: "AYA=" value: "\x1e\xb4\x80\xd5X\x9fA0\xf9jd\x8f{\x19\x12h\x7f\xdcS\xa0 \xfbÆŧ\x9d\x9e;\x94ą\xd7\xf2" } internal_nodes: { key: "AgA=" value: "\x15.v9\xd2m\x80\xae\xe9P\xf1\xac\xe6\xe6\xe1h\xa1K\x83ÃĒk\xce\xfe\xba7W\xceu\xa2k\x87" } internal_nodes: { key: "AkA=" value: "\xa5~\x92\n2\x18\xcd\xc8\xde#\x96UV%\xba\xd89D\x1b\x98\xd9\r\x19\xe9C\xee\xd1B\xbfX\xfe}" } internal_nodes: { key: "AoA=" value: "I\xf8\x82\xe7{\xf0I\xf9 \xfb\x0e\x9a\xd3\xcb\x17\x0c\x8c\x98\x03\xb2\xff\x8cu\x9f\xb3\xd5R\xe9Ɩ:\x7f" } internal_nodes: { key: "AsA=" value: "l\x10\xd5Js\xb7c\xc20V\x8cQW܍E\x17\xd9M\x17 \xeb]\x8c\xbb\xdf\xfb\x7f\x92M\x039" } internal_nodes: { key: "AwA=" value: "\xa5\xe0Ėx\xe2R\x0eMuÆŖ\x98\xf5\x16P\xaf\x88F$\xe3;\x9fZ\xb8\xa5kK)g\xbd\x86" } internal_nodes: { key: "AyA=" value: "#;\xd0\xd8\x1a\xfa\xee\xfa4R\xe0\x02\xfax#x\xb3\xa4\xbd\x0cwA@=\x0b\x0e\xae_\x91*Ę­" } internal_nodes: { key: "B+4=" value: "#\xf2\xf8\x981w\xab\x84W\xa4c\xbdې\x00\xb1É­|\xe7y\x98sm\xd4#w(\xadF\xc05" } internal_nodes: { key: "B+A=" value: "\xd1a(\x9bZ\xda\x13@\xdb\x0f\x84\x91\xa3\xbeww\xa0w\x19@\xd9\xd3.\xe9\xdf+$k8\x0084" } internal_nodes: { key: "B+I=" value: "Nc\xec\xe9=\xb2\xaa\x9c6\xb5\x1a吕I\x90\x07t\\\x19#߀\xf8\xf1\xc4viË´ÎŊ" } internal_nodes: { key: "B+Q=" value: "\"\xb2Q)5t\x9f\xdd{\xfe\xf2ÚŠí€ģ\xfb\xefJ\x84\x1d\xfa\x08\xebg\x1f\xce/\xfc$\x9b<" } internal_nodes: { key: "B+Y=" value: "`ˋ\xbf\xbc⹸T'\xa0gei'a\xa9\xd3\xda\xe1q\xee\x016\x97\xc8\xe7`A\xd5=\x86" } internal_nodes: { key: "B+g=" value: "\xd1.?\x97\x8d\x90\xc0OX\xacbs\x18\x00\x82\x1c\xd5b=\xe3\x85P\x12\xaf\x03\xa9\x04Ԁ\x9fJI" } internal_nodes: { key: "B+o=" value: "0g1\xbcS\x93ÆŽ\x9a\xad\xacZv\x90\x8e\x86Ęą\xd2Sx_\xf82\xa1N\x1c\xd9F(\x15m" } internal_nodes: { key: "B+w=" value: "\xc9\xde\xfb\xdfb\x15\xb5a\xe8\xb6Į‘\t%\xa4\xf7C\xa4w\xbfG4\x1f\x97O\xccl=a*}\x19" } internal_nodes: { key: "B/4=" value: "'\xaa\xcb\x0b3\x07\xc9\xf1\x807\xe0\xd7\xeb{\r \xd5\x18֋\x97OL\xe4\xf09=H7\x08\xeb\xf5" } internal_nodes: { key: "B/A=" value: "\xf4\x02\x1d\x892\x9a$t5#\x12\x1dīŗš\xe8DC\x84\xb1\xb1\xc5\x0fn\x10i\x98=u\xba\xca\x1a" } internal_nodes: { key: "B/I=" value: "Á\xf5\x80\xe4\xca\x06\xed\x1eO\xb9\x9e\xa0\x19\xbdq\xbf\x91\xc9\r!\xd3P\x93\xf0`\x9c\xaeMW}:" } internal_nodes: { key: "B/Q=" value: "\xa2U\x05@\xbb\x81\xa1\x02go\xd8_\x8b\xea\xc0\x03D\xf8C\xac\xb9\xfaV@X9\x9fF\xa5t\x0cA" } internal_nodes: { key: "B/Y=" value: "\xb5V\x16Y\x83\xfa\xf1^\xc25M\xe9\xcd\x11\\\x11C5ÜŦ\x14TB\x80\xa1\xd1\xfe\x14\x06\xedn\xfa" } internal_nodes: { key: "B/g=" value: "\x06\x92x\x9d\x89^\xb6\xa0\xf2]RR\x9dÆĢK\xbf_\xab2\xe6\xa3\xf3\x86\xf6C\x16\xc5M\xd4(=" } internal_nodes: { key: "B/o=" value: "q\xb0\x08\x12\xa0!c\xa7\x8e \x02/\xe4\xb5\\CR3\x11\xbd\x96\xf4\xa12\x0f\xa15r\"u\xf1\xa0" } internal_nodes: { key: "B/w=" value: ",\xdar\x02\x17\x96\xd7r\xa5\x01\x10Įļa\x16\xc7\xc1\xe1+\xc8(t\x1b\x90\xfd\xed'\xec\xc7\xcexZ" } internal_nodes: { key: "B04=" value: "\tv?\x06\xf1\x8e\xaa\xd4Ho\xe0\x04W\xb7\x86\xcfՓ\xdbnG\x90D\x0e?\xaa\x16\xa0\xd0\xd9C'" } internal_nodes: { key: "B0A=" value: "\xfb[\xa6\u009e\xd5\xd4\xc3a\x85E\x81\xc44\x9f,΃\xbd?\xe7\x1e\x83o\x06\xd5LXm\xa9Y\x12" } internal_nodes: { key: "B0I=" value: "\xe8D\x97;\xb0\x1b\x9cK\x0b\xfc\xf2\xd3\x144\x8b\x0b\x15\xaf\xa5\xedR\xa6ÛŦ\xd6K:\x8c\xd8#bN" } internal_nodes: { key: "B0Q=" value: "\xf6@\x9cQ\xbc\xbc\xe0\xff\x83@\x1e\x12\x9f\xaa)\x0f\x98\xc3\xec\x17\x0b\x8e\xf5&#K\xc3ڀ\xb3\xd9\x04" } internal_nodes: { key: "B0Y=" value: "I\xdc=G\xcd\xd5\x1a\x06\x19(\xae`\xb6k\xf3\x94D\t\xa2\xfe\x08\x97&Į¤=\xe2ʂx\xed\x1d" } internal_nodes: { key: "B0g=" value: "c\x1f\xff\xa6\x0e\x18\xe3\xf3x8\x91!\xef\x14\xc4u7Ų¤\xae\x88l\xe8\x0bp\xd8\xf5\x0e\xe6\"d\xfd" } internal_nodes: { key: "B0o=" value: "V\x83 \xd7~\x80\x1cK\x04nDe\x98=\xbe\x945V;\x96Č 2M\x94p=BG\"\xae\xa3" } internal_nodes: { key: "B0w=" value: "\x05\x06\x03\x8f_\x8f(\xb4UnbXX6\xf8\x9c\xe3\x81\xfe\x9f\xb2r\xd2D" } internal_nodes: { key: "B1g=" value: "\xf5\x94\xc1E\x1d\xc1ZJp~\x83p\xa6\xe1\x9ck!t\xb2\x88!}\x15\r\x12vu护\x12>" } internal_nodes: { key: "B1o=" value: "\x9c\x9a\x80\x1a\x9b\xa8R\x9d\x96\xe8\x91S\t\xd6|\x92\x120\x04\xad\xb1\xc6\x11e\xad\xaa:\x90I%\xb7\xe2" } internal_nodes: { key: "B1w=" value: "\xd2\x19\xf3\xf7;\xe1\xdc[\xf9\xa0Mk\xe5օj\xdd\xdbT\x1di}\x85r\x88%\x04O\xe0\xc6\xe1\xa8" } internal_nodes: { key: "B24=" value: "]/tm\xf0\xba\x00\x9d\xfb\xe5\xf1\x06aq?\xf0^PF\xcd,\xa4K\x14e\xd9n\x82\xe5" } internal_nodes: { key: "B2A=" value: "(y\xf5\xb7d\xc2\xce\x11\xc5\x0f+w\xa0╝\xda9^\xf8x\xb9\x1c\xb4\x08z\xb6\x0c\x08\xa5\xe5q" } internal_nodes: { key: "B2I=" value: "\x12\x08\xf5\x1cB\x8a\xf4\xf1]v\x08\xa2P\x9aw*`t\xc7;S\xbaڂpO\xbaYg\xd7\xe2\xca" } internal_nodes: { key: "B2Q=" value: "\xa2\x96Nzm9\xaf\xd4=u\x8c\xa6\x98\xe8\xd2\x1f\xaf\x00\x9f\xda\xe93\x95\xc2\x01\x00M\x9a2\xa6*\xfe" } internal_nodes: { key: "B2Y=" value: "\x9b_@]\xac\x17\xf5\xbc9[[Ų¤Ö¯\xa5\x04vx\x12\xb4k\x85\x90l/Ԝ\x95\xec\xf7\xa9" } internal_nodes: { key: "B2g=" value: "\xfbP\x03ߗʧ\xcd\x14\xf4A\xad\xaa\x9a(*p^\xfa!\xcbd\x0b\xa0\x1b^\x7f\nZ\x1d\xef\x87" } internal_nodes: { key: "B2o=" value: "$z\xc1\xca\xc0Ê\xff\xf1\t\x9f\x8c\xff\xfc\x83\x83Z\x9a\xc6\xf3z\\\xf1Ū\tm!\xecu\x9d\xb3" } internal_nodes: { key: "B2w=" value: "WN\x9a\x0c\xa5/i\xe3\xa1)\x10\xe6*\xe9\xd3U\t4VD\x03\x95\xb1\xdbo?Y\xf4V\xf3=\xbf" } internal_nodes: { key: "B34=" value: "\xcay\xbeƅ\xe3\n\xb7\x87A\xbc\xc4\xddˍ\xc1\xb1\xb7\x19k\xba\u0082\xc2|k&\xe7\x11Xbg" } internal_nodes: { key: "B3A=" value: "w$@\xa9|p\x884\xc5\xe3\xaf'y\xa7\x86\xfe\xdeo\xe4\xeb\xdc\xd2\x02\xd1\xe3\x8f,\xa7\x95\xceځ" } internal_nodes: { key: "B3I=" value: "\x953\xc2\xc3\xe6\xa9V\x0ci\xc8N\xeb\xfe\xe7;\xe3vfv\x88e\x7fƌM\xa5#G\xdd-J\xe0" } internal_nodes: { key: "B3Q=" value: "I\xabJ3\xf9\x92_\x16\xc8f\x15\xc1=\r\xaa\x08~\xa4\xb2\xf8O\x93\x84u\xc4F\xd7\x19\xb2\xf8[`" } internal_nodes: { key: "B3Y=" value: "Ņ\xf5\xca\xff\x95\xe4\xb44\xd8\x0c\x94\xfdF\xdd\x02\x83\x878\xe8\xee9T\xed,\xe4\x1d\xc8!\x8b\xa0\xbf" } internal_nodes: { key: "B3g=" value: "ĐĨ\x1b\xfb\xf5\xab\xfdc\xd8\xd4\x04Ai\x05x\xd2O\x00\xea2\x95\xf2}\xf4\naa\x9bS\xab\xd0\xe9" } internal_nodes: { key: "B3o=" value: "}\xf9\xc49\xf6\xf9x_\xb2\xfb\x17\x08QBQuY\x8f\xb3\t\x16\xe0A\xa6U\xef/\xba{\xc3\x1b\x89" } internal_nodes: { key: "B3w=" value: "\x8e6j-@\xce\xdcH\xf0dR\xce\x00\x90s\xdd\xfa\xfc\x04\x9c\x9eA])S\rSHut>e" } internal_nodes: { key: "B44=" value: "\x12\xb4\xa5\xc4(q\x17:\xdef\xc4\x00z+F5\u008b\xcb\xe3\x0cj\xdf6E\x04\xcf\xfb +80" } internal_nodes: { key: "B4A=" value: "^\xe4~\xb3\xd0*\x0f\x9bBn6\x1b&\xfb>\x80\xc5aX\xaep\xa8/\x9a$\x92{\xec\xd6cmD" } internal_nodes: { key: "B4I=" value: "\xe7\x03N\xedx?!\x18E\x8a\x1e\xa3\xcf\\?\xca\xe5\xb9\xdcL\x1f;Yt\x1f\x92\x9e\xc2;z\xe2q" } internal_nodes: { key: "B4Q=" value: "\xbc\x8e\x98\xbag\x12\x1b\xa2\xa3G\x05\xbf\xa8\xd5\xfb\xd0A=\x02\xab-'B*\x87a4\x0euM\xd1\x17" } internal_nodes: { key: "B4Y=" value: "x\x13\xfa\x1bG|\xd24-\x00\xac\xc9\x14u\xfe|r\xe6c\xab\xddfY1\xd4\x18\xa3\x1495\xc7\xd9" } internal_nodes: { key: "B4g=" value: "\xa3\xa666\xf2\xaf\xcdf\x1d\xef>X\x91\xb2C\x0c\xc9+\xd8\xd3]\x98\xfc\x1c)\xe3\xf2\xf12J+\xc3" } internal_nodes: { key: "B4o=" value: "\xb5\x811\xb1~\xaf\x87\r\xc9\x19\x81\xb2\xcc\xdbMo\x1a\xf7\xe0\x93\xaf\x88\x8f\x90\x84M(\x9f\xeeg\x19\xe4" } internal_nodes: { key: "B4w=" value: "o\x88h\xff\x86\xbe\xfah\xa8\x8a\xf2\x15\xd3(\nP]\xb3J\xd2g\xcb|\x1f\x8f\xb6\xff\xd8\x17" } internal_nodes: { key: "B5o=" value: "\x0b\xae\xcd\xe2\"\xff\x9b\x08\x87\x9b;<;\x90\x1bc\xd1\xe9\xeb+_\xb10ld\xcdb\xffb\x89#Z" } internal_nodes: { key: "B5w=" value: "3@y4\x1b\x9c6H\x02Z\x92\xee\x0fFt\xedan\x7f\x0e\x06Ũ„\x97\xba/U\xfb\x8b.\xec\xdd" } internal_nodes: { key: "B64=" value: "ȧ\xed\x96\x11\x7fbB?ua\xc0]\xb5\x08\xa1#@ã˛\x07hc\xdep\x91Ęš'>\xfe" } internal_nodes: { key: "B6A=" value: "\x8b`\x97\xf9E\x15\xa1S;\xa8\r(O\xf9Ec!\xebJ0O48\xce\x05\xdf9#߇*6" } internal_nodes: { key: "B6I=" value: "QЃ\x07\x8e3\xef\x88C\xa3\x88\xfe\xe6Rt\u008f\xf9n\xa88\x18\x968u\xfd\x00p~\xa6\x0cA" } internal_nodes: { key: "B6Q=" value: "\xc1w4{\xd9=A\x15\xeb\xb0g\x0b\xb8y\\\x94P\x99\x19\x8a\x1ba\xf9?\x83\x82_\xec\x06<\x91\x98" } internal_nodes: { key: "B6Y=" value: "EŪ”\x1c\xfeMt\x88\xbao2\xbb\xc6t\xe81\xdd\xd5t\xe123N\xe0E$\xf2X\x83E\x9ap" } internal_nodes: { key: "B6g=" value: "\x16z#-,qS\xdecB\x7f\xe0\xfcɈĖĒ\xb3\x81\x95\x9eN\xb1\xc8\xdd\xec\x1a+\xa6Xe\xd2" } internal_nodes: { key: "B6o=" value: "@\xb28\x98\x8bx\xbef\xaa\xa9\xa1&Į…\xfc\xe8^\x84h#:b^\r\xa4\x8d\xc4q\xcfl\x9eJ" } internal_nodes: { key: "B6w=" value: "\x91\x13c\x80\xbfՎ\xe5\xb0\xecrs\xec\x0cL\x9e\xc3s(K\x9d\x81W\x16y7(s\xdb\xea\x07\n" } internal_nodes: { key: "B74=" value: "\x0e\xa1\xd0_\x0b\xf5\xa7r`\xff1H\x0e\x0b\xb3okM\xfb\x9d\x8d\x05\xa2\xf4\xf5\x10\x03^\xa4\xb2D\x97" } internal_nodes: { key: "B7A=" value: "\x01į°°\xd5\xffz)\xc7JqMŅĩ`q\xb2\xbc\xb6\xe8xB\x80\xe6\xaf/C\xb5\x0f\xc4\x13V" } internal_nodes: { key: "B7I=" value: "\x96\x95\x8fC\xc1\xe5\xd1\xfe\xc4T1,H\x84(\x82\xe9\x03\xda2\xdf\xd5?A\x8d\x92ĮžĪ–S\xb9" } internal_nodes: { key: "B7Q=" value: "4\xc7\x19\xa4\x10\xa5\x85\x85˕\xba\xddШ\x95\x97\xe7PI*\x99)A\xd1NWt\x92\x00\xa9,G" } internal_nodes: { key: "B7Y=" value: "\xa4\xa8\xdf\xd4Ų›c퟊-\xb9i\x0e\xaePŨĩ^\xfb\xa0Ķ’\xf3xxR\x02^7Wc" } internal_nodes: { key: "B7g=" value: "\xe04\x84\xb1\x9b\xfa\xc4\x16A\xd69?a\x0c\xde<>\xf0\x84m\x12'\xfdz\xcc7\xa58\x13\x96_\xbd" } internal_nodes: { key: "B7o=" value: "\x84\xcc\xf1\xc7\x01\xcd0\xfd4\xa0\xe9\x02N>n\x07\xe8\xac8\x8b%\xdd\xceR\x8b#S\x92\x87\rJx" } internal_nodes: { key: "B7w=" value: "\xf7\x8f\xae\x1a\xccfP)\xcf(^\x0f\x86-Qؙ8\xf3\x1b\xb4\xc6#f`Åģ4i\x8a\xe8\x14" } internal_nodes: { key: "B84=" value: "\xe8\x92\x1b\x9dn\x8c\xe5\x18&\x16\xc3ueĝ\xe7%\xa1N\x17\x1co\x97]k6\x8b\xbd\xcc8hB" } internal_nodes: { key: "B8A=" value: "\x9e_gßž\xber\x9aq\xccV\x1cm\x19\xbd\xd7ߍ\xf4\x8f$!U\x14\x18\xe1\x1dm\xad\xef\xee\xdc" } internal_nodes: { key: "B8I=" value: "\xaeF\xe7\x1e\xf5cS\x93\xc9\x00\xafԛˍ\xc5\xff*\x18\x96\xd3\x16\x16~\x98\x17\xbd\x08:\xd5<\xe3" } internal_nodes: { key: "B8Q=" value: "\xc4Ų–N\x1c\x16B*\xf2\"\xb5\xcd\x1cr\x1bC]\xba\xef\xf4\x92SN\xcd\xcd\xd94UŲĸ\xb2\xfb" } internal_nodes: { key: "B8Y=" value: "4\xa0o\xac\x932\x00\xf0J\xccr\x9dÂĩ\x9e'\xbaatA\xa3\x8f\xd3\x1cw\x12[\x81\xef+\xc88" } internal_nodes: { key: "B8g=" value: "\xe31!}\xa7H9\x1c\x0f\x85\x04<\x88'\xfd\xca\x18EŌŠ\xb6\xf3\xc9\xe76\xad=\xe9\x94\xc0b\xc5" } internal_nodes: { key: "B8o=" value: "Bgx\x9e\xe1}\x8b\xab\xa1x!\xd4\xc6\xe2\xb6Vį‚ž\xfb\x94\x90l\x02\x82\xc6\xd5G-N\xaa\xca" } internal_nodes: { key: "B8w=" value: "\xd5WRb\xb0\xea\x85\xd3\x1cFd\xf6\xb3\x13\x88\xc7o\xdc\x1eg\xc9ÍĢ\x13\xf6F\xe6\u009b-\xaa^" } internal_nodes: { key: "B94=" value: "\x82q\x88\xc2[\xcb\x1d\x91.\x1a\xbe\xab:\xa7S\x10$\x7f\xb9\x0fCo\x10\x811Ûˇ\xf0D\x0b\xaf\x81" } internal_nodes: { key: "B9A=" value: "J\xbbF\xb65\xca\x04=AeŨĸT!!\x9e\xfd\x13Y&o\xac\x81\xcc0\x96`z\xd0\xccY\xb5" } internal_nodes: { key: "B9I=" value: "9\xa8\xe7\xb1\xee\xa1\x02\xb1O\x00\xe6\xe8\xf2#\xe1O\x15U\x0c\x89\xf5$(3\xda/\x97\xc0pK\x0b\x97" } internal_nodes: { key: "B9Q=" value: "\xf9\x91\x15\xb5\xdeM\xe2xE\xd69\xc8\t\xcd\x02\x8f[\xe5\"\xd7r\xde\xc6CB#\xe1\xfb`\xc5\xfc<" } internal_nodes: { key: "B9Y=" value: "A\x8a\x02\xd0l\x1e\x98\x1b\xe1\xb8\xfa\xc5pc\x83\x12zk8E_\xff\x01\x0fM\xc8w?\x9f\xeb\xe1s" } internal_nodes: { key: "B9g=" value: "\xf47\xcc`\xeb:\xf69\x9bD\xcb\xf8zOe\xba\xab\x8c\x05Į„|2\x10ˍ\xb3^\n\x9b\x7f1" } internal_nodes: { key: "B9o=" value: "\xdf%\\\xf5\x1et\xb9\xee*\tr\xc3Į\x93\x15\xd8ëŊ§a\xa2pl\xeb`OUz\xfcB\x98" } internal_nodes: { key: "B9w=" value: "\x12\x02\xd3ÂĢq杰P\x8c\x16\xae-a\t\r\xb7\xc2@\x1aLj\xe8&:3h\xf8\x80\x99\xc1" } internal_nodes: { key: "BAA=" value: "\xfd\xe2\xf1\x98\xc3i8\xa2a\x07/X4I\x04w\xb0\x8coVH~\xea\xbdX\x96\x9a\x9e-/f\xb2" } internal_nodes: { key: "BBA=" value: "\xf7A_\xfd\xbe\x98S\x1c\x88\x97\xad\xf8foe#\xdf\x13\xec\xb6\xf7RŌ‡\x85\xdf\x00\xe4\x00\xe1\x1c\x9f" } internal_nodes: { key: "BCA=" value: "\xd5ŌĒ\x89\xb9\xecm\x10[\xb7\x99-ł\xc1\x11#Ō \x9a\xcb?L\xb4\xf9\x82\x06Z\x90\xd1*\x0b" } internal_nodes: { key: "BDA=" value: "\xd6D\xfa\xb5\xe5\xfb\xe8\x196\xb9\x08C\xd0\x03:9E\xc3v?\xefp\xcdQ\x83\x1fcW\r\x96\xe1d" } internal_nodes: { key: "BEA=" value: "\xcex\"\x00\x0eÚ\x8b6%\xcd\x0f8\x01C\x8a\xe4c\xccB'X\xe5$f\xf1@\xe61\xbdy;" } internal_nodes: { key: "BFA=" value: "d,1[˜I\xbfbh\xa2\xe1?\xf8\x19g\xc0\x90\x7f\xd5ËŊ`D\x81\xe5rh\\\x1fp\x1e" } internal_nodes: { key: "BGA=" value: "\xa1\x96\x10$\x93)\xb6\xc4J\x95t\x98Y\xcc\n4\xea\x9d]L\xfb\xb0\xd8ˈ\xec`\"Q\xd76r" } internal_nodes: { key: "BHA=" value: "\xf43\xf2\xa1`'\xc8?y\x80ĶŠX\xa8Q<\xe1\xe8~\x019FFc33\xcdN,\xf2\x7f\xd2" } internal_nodes: { key: "BIA=" value: "\xbb\x1e\x91\x86WTCYA\x04\x1b\xc8\xd11k\x96u\xa1Y-\xbd\x81\xf7?I\x05\xc41\xadz\x86a" } internal_nodes: { key: "BJA=" value: "?\x12\xd1j.\x0e\xde\xd7\x06]Xf\xc2\xe1\xc2\xc6\xce{Ũģ<\xca&\xcaҧ\x92\xeb\x06u+o" } internal_nodes: { key: "BKA=" value: "\xf1\xa3\x8cd@\x96\x8a\xdf\xf2\x95a1}\x8c \x05r\x18i\x97ȏ\xcc\x05\xca\xfb\x12\xa8/\x0e\xb8T" } internal_nodes: { key: "BLA=" value: "\x0e\xf0\x95|E_\x1f\xc2F\xae\xaf\xfa\xc3\xdd\xf9\xba\x90\xb8\x9fH@\x10ov\x92\xd8M\\H\x18\xcc\x13" } internal_nodes: { key: "BMA=" value: "\x9b\x1a\xbd\xdc\xc8K\x02\xa2T\x98\x8cÂŋ\xfb\xf00\xc8,8\x01\xf9+\x065L\x162&\x04\xc7\xc5e" } internal_nodes: { key: "BNA=" value: "~Ųļ\x89a\x8c~\x8b]\xbb\x1e8\xd6\xf1\xa0\xcbR\x7f@\x0f\x14\xd5n\x90\xa2\x13\xc72kw0\x07" } internal_nodes: { key: "BOA=" value: "\xd4v\xbf\xc2\xe8[Nl\xcau\x15x\xf6+\x9e\x16\xf2\xbaV\xc7\xf26\x13\xe2\xa9\x11\x86\x93Û´\xc2\x1b" } internal_nodes: { key: "BPA=" value: ":\xe4\xf2\xc8\x0c~VU\x0c\x19q\xa7fH\xc2~\x8a\xbb\xa0\x92\xfe\x88\xa1|DU7\x99\xb0\x87\xc06" } internal_nodes: { key: "BQA=" value: "\xcf%^Ք\xb4\xab%\xa0\xffx\x1fw\x12\x83\x7fO\x9e]\x10sU\xa9\x17,\x048qI\x87\xf2O" } internal_nodes: { key: "BQg=" value: "\x81\xeb@coPS7\x96\x1d{\xdc[\xf5\xb7\xfc\xcb\xf8\xc2*\x96\xee\x13\x10+ã̈\xd4*4=" } internal_nodes: { key: "BRA=" value: "؝\xbb%\x82\xaf\xdc8ȁ\x13\x88H\xe2\x19\x96\xdc_\x14-\x16\xc2\xc9\xd2\xfc\x81\rŨĻ\xc9]\x10" } internal_nodes: { key: "BRg=" value: "\x9buW\x82\xb6\xf1OA\x81\xd9\xfe\x16\x9e\xcfhcSˏA}X\xa0\x1d$\x89\x8f\xad`a\xaf+" } internal_nodes: { key: "BSA=" value: "\x83\x1f\x9e\xaa,\xa9\x0fX\x10\xfd.\xff\xd7\xc9Q\x9d\xc1\xc9\x19A*\xc6T\x05^\x97-u\x07w\x8c\xbc" } internal_nodes: { key: "BSg=" value: "\xad\x81%îŦž\xc83\xd9.K\xa2\xae\xd6nc\xb9\x08p\xfd!\xb6\x9cY\x88}cBFRҝ" } internal_nodes: { key: "BTA=" value: "B\x93\xa2П\xc1[\xef\xfb\xf7\x00\x01Ũ\xfc\x11\x9d\xa4\xd3n\x85_S\x02\xf9\xddɆ\xecKI\"" } internal_nodes: { key: "BTg=" value: "\x92\x9b\xf1\xa7S\x93\xae\x13R\xc3EK\x12\x93\xef\x9a\x1b\xc6\x0f+=\xc0tJ\x1c\x03P\xec\xc5\x06\x15\xd9" } internal_nodes: { key: "BUA=" value: "\x81tÅŗ\xda\xf4\x14\x11\x1ds\x88\xeb\x08QM\xe6\x16\xb3\xf2\x9d\x9f\x1c\xca\x1a\xe5\xc0.\x8ae\x83\xc3\x08" } internal_nodes: { key: "BUg=" value: "\xdej-X\xe1\x8c\xdbo\xecQ\x8b\x98\xb9\x06XćR\xfb\x7f\x08\xc0\x0b\xd0\x19V\x1d\xe5X\x82\x84\xc8" } internal_nodes: { key: "BVA=" value: "\xf2ˏ\xc7\xe8\xe4 \xfd\x1eb\x97B\xd5u\xcb]\x04\xc8_\x949\x01&GX(\xce\xe7\xde\x1fn\xc2" } internal_nodes: { key: "BVg=" value: "=\x9f\xd2W\xaa\xc6ce\xa7\x98c=OBe\x1f\xfb\xdc?:/\xdbq\xb5\xd3ֈ\xfb\xa8\x8f\x9a\xe4" } internal_nodes: { key: "BWA=" value: "kڗ@9j\x88\x80\xb9\xe6\xd2.\x07\xf7(F\xec\x01{\xc0\xd7~V\xb2--4\x8b\xc7\x18J\x07" } internal_nodes: { key: "BWg=" value: "\tĖģC\xecW#\xaa\xd0]QRa,(ĝQ\x18H\x15\xfd\x06\xfcC&#\xec.X^9" } internal_nodes: { key: "BXA=" value: "\x87\xe8?\x03\xb8\xecX\t\n\x1f\xc6~\xa0\x04\xdf[\xc34^b\x8f9\xc8]\x90B\x86\x9d\x1dE\x88\xe0" } internal_nodes: { key: "BXg=" value: "r\xb3\x99_Ĩ\x8b\x0fz\xb4\xc33\xf5\u0085s\x99\x03\xa3\xfe?\x02\x02\xf2\x9f\xc8e\xb1J\xa7[\xab" } internal_nodes: { key: "BYA=" value: "\x14\xfd9\x03<5\xf5\x84\xf3'q\xce\x1a4\xad:B\xb2\x881j\x0f]\xf3\xac\xe8\xb7\xe1\xb60\x7fW" } internal_nodes: { key: "BYg=" value: "Q\x13Ί\xa6\x92\xcbG\x1dB\x13@\xb4qM\xf9\xc4\r\rKELė\x8e\xbc\xbb\xeb\x1c\x02\xa3" } internal_nodes: { key: "BZA=" value: "\x05/k\xa0\xa7\xa8qD(\x08\x17$\xb7\xff\xfcX\xea~\xa1\xd5ί\x81\xb1!\x80\n\xbfp\x06\x12\x90" } internal_nodes: { key: "Blw=" value: "~\xc2d!\x075,4\xad\xb7\x1a\xeb\x18\x9f\xb7n\xabQ\xfd\x01R\xb0:h\xaa\x0b\xd7*\xb3\x94X\x8c" } internal_nodes: { key: "BmA=" value: "\xb5E\x91_\xe8\xee\x17S\x18\xeb%\xfc\xf0\x8b\xe3\x9b[Z\x9f)\xac\xec\xed\x835\x94\x8fgQ\xcb\xe6\xe8" } internal_nodes: { key: "BmQ=" value: "B\xec\xd5\xe3|\xc8\xf9\x86\xa2\xb2Su\xedВ\xe6w5\x16k\xb27p\x8e\xf5Nk\x89\t@Ëŧ" } internal_nodes: { key: "Bmg=" value: "\x96\xabN\xd8~\x81cQ\x0fW?\x15Äēé„Ģ\xef\x05n\x13\xda\xe80\xbd#\x85\xfd~\x0b\x1c\xbd" } internal_nodes: { key: "Bmw=" value: "\x97\xd3\xf9G\xbbM4\x84\xb4\x87\xf5\xcd\x14\x19Y\xf7\r,\x80Ā\xfc\xbajG\xa2%\xe2\x01\xca}$" } internal_nodes: { key: "BnA=" value: ".ŌēĮ´\xee\xd7rT\xb3\xca6\xa4\xbd]&<\x81 \x9a\x94\xe6\x16\xb9\x02\x16>\xe3<2\xa1\xdb" } internal_nodes: { key: "BnQ=" value: "SF\xfde\xb0\xcc\xc7C\xc0\x1e\x84\x88\xf4\xb8?u3\x9e\xb1\x14\x15#\x96`>\xd0\xf6ʒ\tB1" } internal_nodes: { key: "Bng=" value: "_\xf9{\xe1\x17\x93\xfa#\xe6m\xf2l\x98Īŗ8`\xf5e;\xf2\xf1hB\xba\x05\x7f\x19Ɵ\x17\x1b" } internal_nodes: { key: "Bnw=" value: "\xa3\xfe\\\xbf\x15Q\x19\x14\x0c) \xce\xd6\x14Îĸ\xad\x9e\xb48\x9f\xa0\xe8\x16\x9e\x92S\x0b\xe6M\x10u" } internal_nodes: { key: "BoA=" value: "\x1bl\x1e\xc2\xf6\x15\xf7\xecCL\xcaȉG\n4W\xc7\x17N\x16\xacm\x91\xb8\xf6\x101\xde|\xb5\xe7" } internal_nodes: { key: "BoQ=" value: "|\x81Y\xd8'Q<\xddI\xc5\x018^\xc27@s\xad\xfa\x06\xcbP\xf4\x9d\xb4\x9fͧ\xaa\xb1L\x83" } internal_nodes: { key: "Bog=" value: "\x90\x11,\x81\xf6\xc9A\xbc]\x9fj\xfa\x08\x06\xc9U\xb2p\x82H\x06.\x13\x82C\x89\xdc\xe1)`\xfbz" } internal_nodes: { key: "Bow=" value: "\xc6\\\xc6(\xe5\x07Y\xb4:\x90!\x0b\n;\xe4\x05\x8a\xa2\xb2\x80\x19\x17\xdb\x14l\xa1\t\xdan=iN" } internal_nodes: { key: "BpA=" value: "\xa3\xfa\xa5f\xed\xfeF\xebØĨ\x069\x10\x0b\x10L9D\xe0\xe2\xe5\x1d\x86[,\x08\x84eˁ\xaa\x13" } internal_nodes: { key: "BpQ=" value: "Z\xc3W/Ns\xa7\x87\x88%:\xed\xdb\xcc\x08\xe6><\xea\x04\x1d\x9f\x1c\n_\xb6\xaf\xa3~\xce\xd6\xd3" } internal_nodes: { key: "Bpg=" value: "!\x1a\xf14\x1cj:\xbb\xd4Ũ­\xec\xe3\xd4\xe0\x8e\xe4'p\xe4\xe5T}\xef\x9fhRɇ\xf7\xbd\x11" } internal_nodes: { key: "Bpw=" value: "\xe9\xc1|\xef\xfa\xfbTy)\xa9\xa8\xf1\xdc\xdef\xab\xdc\xf4\xb5qm\x1fu\x98u\x8a\x84\xf9\xd9R\x14\xa1" } internal_nodes: { key: "BqA=" value: "\x82S6\xdb\xdaČļ$M\xbd\xbaxG\x80v\xf4\\\x13Tn\x00r%!\x19\x8b5\xea\x8b'\xb14" } internal_nodes: { key: "BqQ=" value: "Äž\xfaQfR\xf4\xd5\xd3.\x11\x03\xc7\x15`\xcd\xe0OH\xfebԄ\xcd\xddS|\xc48\"\xe26" } internal_nodes: { key: "Bqg=" value: "z\x08\x1bb/k\x9f\x8b[\xcctpI\xb2\xcd)܅8D\xfd>|ɒUB\xccS\xac5\xef" } internal_nodes: { key: "Bqw=" value: "\x07\xce\xeb\xd2\xd4Zk\xbb!\xd5<\xf9\xfe\xf5XR\xe8\x8c\x1c\xfeG\xb0\xbe\xd7\x07\x82\x1e+\x10\xb6\xb4\x85" } internal_nodes: { key: "BrA=" value: ")\x81\x05\xf2\xe4\xc7)\x8dW\x0b76Z|\xee\x11e^\xed\x83\xe1h\xe2!\xd3R}ÅŽ\xd5B\x1c" } internal_nodes: { key: "BrQ=" value: "yHt\xce)\x8f \x88;\x1aG\xd0_\xe5\x07\x85I\xc3\xc2\x02\x81z\xfdZ\x82(\x91\x01\x87(\x04+7h\x8d\x07\x9f\n\x1f" } internal_nodes: { key: "BvQ=" value: "\xb2\xe5ט\x0e\"A\xe5\x1e\x91nG3\u0090\xb2\xf4A!" } internal_nodes: { key: "BwA=" value: "J\x0c\x8f1\xc4\xdf\xc7\xf9\xca\xd0\x04t\xa3\x10\x9e^\x93.ted!\xf7\xe0X\x1a\x99d\x1a\x9f\n\xfa" } internal_nodes: { key: "BwI=" value: "\xf7\xe0\xc5\x0b\xc6}0L\xec\xb0\xebÃēw\xc4P\xcaf\x86\xbc\x9b=\nA}\xa7\xf9\xbc\xeb\x7fp:" } internal_nodes: { key: "BwQ=" value: "\xcf\xe6\x1cV\xe3i$;>\xd3\x13w$y_\xb6\x91\xdefFb\xd3+=V\xc0\xdf\xeb\xbd\x07\xfe\xbb" } internal_nodes: { key: "BwY=" value: "\x8a\xc1y\x19\xabb\xe0\xe3.\x05\xef\xb9|\xab\xb9O\xa8oG}\x80\xf2t\x0b\x88\t\xb6\xc1A\xfa\xee\x97" } internal_nodes: { key: "Bwg=" value: "\x04\xdf\xf1\xe9\x87\xfdd\x0e\xc3;}iM\xd2\x03\xac\x15[\xf1\xb2\x8dbX\x0c\xdcc\xefu\x81\x00m\x8a" } internal_nodes: { key: "Bwo=" value: "T\x84\xb6\x89\xed~P\xc9y\x03\x08\x9f\x9cH\xc6\xc4\x10\x1b\xc9\xce\xc1$NP(\xf1\x98j\xae\xd8\x01\x7f" } internal_nodes: { key: "Bww=" value: "mpÖŊ\xbaTR\r\xbaJ\x17\x8f\x0cU\xdaD:\xa1X@S\x89\xe6K,\xee\xb4@\x00\x99~B" } internal_nodes: { key: "Bx4=" value: "g\xbf\xdf(\x97d\xa9dfrŨ˜\xe0\xeeAL4pqS'\xb9}[nY\x94\xc8\xe7\x15r\x17" } internal_nodes: { key: "BxA=" value: "@\xfa{\xb7\xe1\xf7\x80A\x94\x9c}ĶĨ>\xb9h\xeeEsrF!\x85Õĩ\x1c\x12\x1a\xba\xe1\xd5n" } internal_nodes: { key: "BxI=" value: "Y\xf6$Bi\xc4Æŋ8\xee\xa7Xl\xc9\x1eM\x16\x82Dn\xb5˜\xf6\\\x1e\xcc\xc4^dx9" } internal_nodes: { key: "BxQ=" value: "\x11r\xd50(j\xdeTxf\x9c\xe0U\xa5k`\xb5)R\x9b\xa1Ȍ\xa1%\xf8T\x95\x17\xe4iu" } internal_nodes: { key: "BxY=" value: "\x00.G\x9bw\xb7\xf3\xeb\xccY\x025k\x80#\xaf\xc0Fn\x8cČĩ|a\xfe\x9cr\x05x#\x12." } internal_nodes: { key: "Bxg=" value: "\x12M\x9eb\xed$\xa4'\xbe\x8ak\x85\xff\xd7\x03`\x84\xc1\x12\xfd\x02e\xc6l!\xddw\xc5a\x83\xdf\xe8" } internal_nodes: { key: "Bxo=" value: "\x8d\xacÎĢ*\xfb\xe5\xafr`\xa4&yk\x14A\x08\x02J{Y\xf0\xac\xa8Zf\xad\xfb\xc9KA\xac" } internal_nodes: { key: "Bxw=" value: "\xb0cI\xfdI\xd5*\xa5\tf4n\xae\xe2\xc3,k\xd42\x19\x08\xdat3K\xccF\xbd\x8c\xb6#\x97" } internal_nodes: { key: "By4=" value: "\x92\xa6\x9eJ-ĐŊ\xa6C\xef\xab\xf5m\x81\xac\xaf\x88\xacZ\x06\xe8!Ɖv\x8c\xc6\x01\x1f%\x90v" } internal_nodes: { key: "ByA=" value: "\xa47Tr\xa81Ų„\xa5s'\x0cz}\n\xfc\t\xbf\xd5zIs\x06\xb75Y\xdcZ\x02\xed\xc5W" } internal_nodes: { key: "ByI=" value: "Ņ”)Ņ–\xabIN\xacTØ­\x84dL\x8a\xed\x93\x12\x01ȕ\xf2\x86냜\xb9\x1fg\x9a\x90" } internal_nodes: { key: "ByQ=" value: "\xa4o/\xfa\xfc\xf0\xb5j\x19a41\xe9se\x0b\x1b\xd6ic\x92;\xa1\xa5\x05`\xd9\xe5\xf2\x105\xe3" } internal_nodes: { key: "ByY=" value: "\xbd\x06Ũ˛D\xf7\x1a\"\xc7ÂŽ\x9a\x94\x84\x96l\x80tqQvR\x9a\xde\x07\x16\xd9LGd\xec\xb0" } internal_nodes: { key: "Byg=" value: "\x8a?\xfc\xf8j\xb7c\xe1\x01\xb0\x82\x0c\x82\x96S\x88q\xbcD\xbc\xfc\xb8v1\xa0\x1c\xc7\x0e\xb2C*\x8e" } internal_nodes: { key: "Byo=" value: "\x1b\x1b\xf9\x89\xd7z\xba7w\xa5e[\x13\x9ao\x97TI\xe4&\x08\xb7\xdaQ|\xa7\x90c\xda\xdcYK" } internal_nodes: { key: "Byw=" value: "\x8cT\xc0\x9c\r\xfdS\xeb\xbf\xef\xcb\x1a\xf0\xf5\xab3;\x05B&U9\x0c\xa1\xc8\xf8\xc40\xb3\x0b\xae\xbe" } internal_nodes: { key: "Bz4=" value: "\xed\x9c0V}\xfe\xc5\xee\xec\t\xad\xc2\xfc\x8eG\x1a\xc4O,\x84\x05\xc9y\xb3\xb3F\xac\xf3\xc46#d" } internal_nodes: { key: "BzA=" value: "\xd7\xee\x7f;\xbdŨ–v\xd8Z0\x92\xe9W1'\xa3\x90q\xf6\xc5rXԃ\x99x\x1d3\x12\x90\x05" } internal_nodes: { key: "BzI=" value: "O\xef}'\xd7\xd8\xe7\xd4R\xe19\xad\xfcy?X1wx\xef4Ry\xa1\x8a\x8f\x00\xda2\x01\xee\xbd" } internal_nodes: { key: "BzQ=" value: "3\xe2\xc8!_$\x17j\xe9\x0f\x8aP\xaf\x8eŅĢ\xae\xablo\x95ߥ\xa5\xd5\xef\x97O8\xd7\x1b\xfa" } internal_nodes: { key: "BzY=" value: "\xbc:\xf8\xf9\x1e$\x15H*\xcaH\x18h\xef{\xae\x13\x84\x0fJ\xe0#\x90\xc7AaԘ,\xf6\xf1\x82" } internal_nodes: { key: "Bzg=" value: "\u009d\x98ä\xe8\xc5*\xdc:\xa9\xddA(\xb52\x83\x07\x86\xa6W\xc8H[(t\xed\xd9\x1by^\xe7" } internal_nodes: { key: "Bzo=" value: "\xb8W\xe8\xd1\x1d3\x03 \n\xc0ڍ\x9f\xeaH\xb8ÖļȲ4\xafS:\xe6\xdfj\\\x94\x0e#N" } internal_nodes: { key: "Bzw=" value: "6\x91\x8a\xfcg\xbd\x82[Ī›\\\x9b\xcfN\x8a\xa24\xde\xd4\x07\x02%\x0f\xa7\xf87[\xd4t\x18Õš" } internal_node_count: 254 prefix: "\x00\x00\x00\x00\x00\x00\x03" depth: 8 leaves: { key: "CA0=" value: "\x11L\xa1^\x1f\xe6\xeb\x9d<\xa0H\xd6O:t\x8a͍=14\xaa\xffu\xb0ne\x89D\x88#\x0e" } leaves: { key: "CA4=" value: "*I$+\xaf:}\x15\x13\x97\xb1s\xf4\x15\x14\xb7w\xa0\x97d+\x8e\x9f\xfe=Z\xa5\x9e\x92\xe0\xe7\x8f" } leaves: { key: "CA8=" value: ")\xb5Ņ\x0f\x1e\xf9\x8d\xbd\xa22Hg\n\xd4\xe5\x03\xd1c\x81\x87\xe1\xfc\xf57\xc58^\xe0\xda\xf5p" } leaves: { key: "CAA=" value: "\x0c\x13P\x17J\x9c\xe0\rT\xe1\x7f\xdb\x05eG\x91\xc5\x1f?\x12F\x87i\x11\x9a\x85\xa2\x15Ά\x84\xb8" } leaves: { key: "CAE=" value: "\xef\xe0\xf1O'\x1eqU\xc3L3ix\x12M\xc2a\x0c\x94\xda\x7f\xa4˓\xf0w\x88\xb3,4|Z" } leaves: { key: "CAI=" value: "_\x03(N\x84\x85\t\x87\x9f\xe3k&%\xb907\xd8J\xb3to\xfb\x18\xe0-\x7fs\x94\xab\xe4f1" } leaves: { key: "CAM=" value: "4\x8e\x99I\xb8b\xfb\xde\xde\x18\xfa\xafg=\xac\xe2\xe1\xacJ\xd66\"\xf2\xcc\xf5\xc1\x11[\xccz\xaet" } leaves: { key: "CAQ=" value: "*!o\x7f\x8f\xfc\xe8\x93J\xbb\x19\xc2P\xfbnW1\x8b\x0c,\xc6\x1fK\xbf\xc1t(:\xf4\xfc\xca\n" } leaves: { key: "CAU=" value: "\xeb\x16\xc0\xfc1\xdd\xd3\t\xb6Ɵ\xb0p\x8dr\xf6q\x91\xb9Ɯ\xfd\x15\xc05\xdaڟUy\t<" } leaves: { key: "CAY=" value: "Čą\x8bI\xff3\xd9\xdbq\x04\x8f\x9b\xb5\x8b\xe9i1D\xe562\x1f\x92_\xd3\xe9\xa7rTT\xac+" } leaves: { key: "CAc=" value: "\"\x9f\x80o\xb8gI\x03\xd8_\x94L))\xb9\xf2U\xddNX$;\x0c\xba\x87!O!B\x86\x83$" } leaves: { key: "CAg=" value: "\xe5\x08Y\xab+\xee\xd8G\x08\xc6$\xf0ja\xa8\xc1\x9f\x98=\xa5\xf6\xf7oX}\x7f\xbe\xa8\x82ėš–" } leaves: { key: "CAk=" value: "\xfd\x0f\x1f\xb8\x88r*I\xb7\xdeS;\xb4@\xea;\xcd\xe3]9E9\x14\xde@b\xf1ˁX)\xda" } leaves: { key: "CAo=" value: "\xd4\xebd\xddC\x18\x9e\xaf'\x18yȗ\x1b\x7f\xfe\x12.\x87R\xe6\xdfY\rhd?@\x08t\xc1\x86" } leaves: { key: "CAs=" value: "<,\x91%\xb4\x7f\xad\x92\xc5z\xf9\x0bgorV\xe1u\x96\x91\xa4\x19\x8e\r\xd2*ÚĒf\xadJ\xfe\x88\xfb\x03\xf6\x03\x8e" } leaves: { key: "CB4=" value: "\xf3\xaf(\xcc\xc97\x80\xc9\xf8\xc5\x14\xb6\xcdÆŖR\xd5|ƟQl\xcd#\xe7cR0\xd2vA\xc0" } leaves: { key: "CB8=" value: "b\xeb0^~\xe69\xe3\xce>\xc4}NJ\x00\x0bv\xfc\x98 \xec.\xa5ʆs\xea)\xe7s\xe8\xd0" } leaves: { key: "CBA=" value: "\x82\xbc* \xf7\xc0\x15!\xaa\xf3-j\x8cH\x8cu\x8d\xca\xe1$oÍŋy,\xebtԁ a " } leaves: { key: "CBE=" value: "\x0c\x00\xf4\x86,o\x80$\xdbtՕ\xe6Īž\x06*\xe2t\xd3\xf3;u\xa9\xc0\x8e\xa5\xec^\xcd|\"" } leaves: { key: "CBI=" value: "\xac\xaf\x05g7m\x8a\x82\x94\xf9\xbad\xffȡ\xd9\xe0\n\xae6\x8csA\x01>\x10\xb5A\xd3s\x1e\xc1" } leaves: { key: "CBM=" value: "\xa8\xafP\xd4\x1a[u\x80?\xa4\x87^T,\x10b\\\xc3\x1c\xa4\xf7HYAeQ\x0f\x04m\x9e\xd0W" } leaves: { key: "CBQ=" value: "\x8af4\xe2O\x98\xad\x9f\x8d\x1d~\xf4\x12\xff\x16F\xe0O\x1e\x9b\xc1u\xdf\xcb\x01\xf4\x18\xaa\xcc\xedR\xbd" } leaves: { key: "CBU=" value: "\n\x19\xd9\"CNR-)p\xbd\xb1\x94_e8p\xab\x044\xe2\xafh\r\x8f\xa4\x12\x05\xe3e\x86\xbc" } leaves: { key: "CBY=" value: "\xcdUm\x10]\xdd\n\xf8\xfd\x81\x8a2m\x03%[\\\x14_\x81\xf2w|\xec\xb2fU\xe1)Q\x19\xcb" } leaves: { key: "CBc=" value: "Bi\xd7\xfd\xber\xd6\xc1@\xces\xa5\xda\x17\x98\xa9\xa3W\xa6\xd5\xe0\xe9\x01\x98\xd0\x0e\xca\x0b\xaf\x91\x9a\xfc" } leaves: { key: "CBg=" value: "vV\x8d\x7f\x98\xcc\xe3\xc8\x07\xbb\xbf\t\xbeU\xb3\x0fČ `\x86\xea\x0b\xa4\xdd\xc8{\xc2\xf1ob\xae\r" } leaves: { key: "CBk=" value: "\xa1b\x0by\xbdŪ›\xa6\x0b\x0bT͘EF\xb5^\x98\x81Ų“\xe0\xefe\x0b\xf8\x16I\x15\x91\xb2\xdb" } leaves: { key: "CBo=" value: "j\xde\xdf\xc9\xd4\x0f\xa1+ \xaa\x1ee\n!nˁ\x8c2\xb1b.\xba\xc5\xebW\xb2w\xc9\x1dX\xf7" } leaves: { key: "CBs=" value: "ߊ\x82\x84-\x8f\x9b \xd5I\xc3o|\xe0\x05\x17g\xc1%)ß ^\xf5\x88\x92Qn\xa3\x19\xa2T" } leaves: { key: "CBw=" value: " \\\x7f\x84\xb9sT\xfdQ\x0b-\xech)\xbc\xd34\xeb\xb8\xc1\x93f\xd4uV\x0f\xcf\xf6\x8d\xbe\x0co" } leaves: { key: "CC0=" value: "\x9dO\x11\xc9W\xbb\x1f\x89\xe1<\x9d\xf5\xc9K\xf8\xfe\x86_\x94\xbe\xfa\xf5\xf3\xf0\xbd\xcdZ2\x1b\xba\xb5O" } leaves: { key: "CC4=" value: "\xf0'\xb0G\xe0\x16\xdb\xe6\xe5\n\x94Íą\xefL,kK\xe6lZG\x80\xfd\xc6\x144\x0bŅŖ\xad>" } leaves: { key: "CC8=" value: "\xc8nx\x8cl\xf1\xf3\xa0\xd0\xd1C{\xbe,\x94\t\xf8\x0f_\x8f\x8aO\x802b\xfb\x9d\x01\x97hVG" } leaves: { key: "CCA=" value: "\x08\xb4\xe8\x8c\xe2,\xac\xf2\x96q\x80\xa6\xc0\xb0\xd15\xfc\xda\xe6A\xb3\xa9\x08\xd0P\xf2\xd2\xe0\xdd\tk\xf9" } leaves: { key: "CCE=" value: "\x0cm\xa4V\xcdu\x10\x95LL\x96H\x01\x84\xd5\x0fks\x158\x17\x1e\x01*\x9fd\xb2y\x1e59\xaf" } leaves: { key: "CCI=" value: "\xb9b\xf0\xd5耚mwJ\xb0\x98\xc6r\xd6s\x96\x9a\xf3\xb7Uv\x8f\xf1W\x8ceVnG{y" } leaves: { key: "CCM=" value: "\xcaCœ\xde\xd3!\xf2^\x1b\xf9\xb6\x14\x1e\xfd\x80\"u\x08\xe1|\xa4?`\x8d\xafxl\xe1@\xbf\xb5" } leaves: { key: "CCQ=" value: "bh\xe1\x8a[\x95c{\x0c\xfe\x9d^\xef\x90\\\x13\xa2S?\x19\xa2(\x1f\xdcY(M\x08\xf1\x01\x97\x8e" } leaves: { key: "CCU=" value: "2PaIv\xd6B\x0b\xb5d\xa9\x1fį—Ē\x18\x80\xc0\x8bOU1M,\x96\xdeVZST=\xc5" } leaves: { key: "CCY=" value: "\x9e\x8f\x84\xb0\xd4;\xc6\xeb}e\xcf\xeaD\x90U0\xce\\b\xf6H>\xf0\x96\\,E@\xa1T0\xf8" } leaves: { key: "CCc=" value: "W3\xab\xb2\x19:9\x9f\x19\x91 \x90\x18!\x9cA\xab\x05P\xadc-\xa2\x15\xb8U\x81+<\xda}\xc2" } leaves: { key: "CCg=" value: "\x18\xf3\xcd\x11\xb4K\x10\xc2\xcdQ\x9c\x81\xb6\x14Q\xd7\xdd\x02YC\xa6\xbdP\xd8%\xd2\r\x0e\x0eW\x1dN" } leaves: { key: "CCk=" value: "Y\x81Pa\xcd(\x06\x07ˍ\x89\xb3\xfe\xf6z\xa9\x96n8ȓ4w\xea\x93\x1e\x13\x1d-\xf8,g" } leaves: { key: "CCo=" value: ".H\xf4Q\xc1\xbc\xedk$c;$*B\x8e\xf5\x15b9Ûˇ.\x15<\x0e\xe7\x98\xda$\xd4bG" } leaves: { key: "CCs=" value: "\x07-ËŦ=\xf4n\xa0W\x9d\xbc\xeb)\xb1Y\x83f\x15o\xd0\x19\x91%M3r\x00\xa0\x13}\xe9\x9f" } leaves: { key: "CCw=" value: "\xa6W\x98\xd6Ũ†\x0e֖q]\xfcė‰ļ\x0fa\x12\xdb?E?\x90\r\x82\xcd\xc2׃\xbc\x88\xb0" } leaves: { key: "CD0=" value: "\xea\x88xb}^\x05B{bH\xa5\x82)rU\r\x02b$\xda:H\"\xb7\x8cnj\xff¸\xd8" } leaves: { key: "CD4=" value: "\xc3\xc4,\x01R\x8c}R\xde+=\xe6\x14\x05o\xaf\x95\xb0\xc5\xf6\xeaŨŧ\xb0\xbf\x98\x89 \x9fJ3/" } leaves: { key: "CD8=" value: "\xd5\xc4'\n[\xac\xcbs\xbe\xc8\x04\xb2s\x84k\x90-\x177\x8cT\x0eM\x96\x1a^\xaf\x15!\x8f!\xea" } leaves: { key: "CDA=" value: "*\xac\x00\\؋\xadN5\xd5\x10\x12\xff\x8eh\xb1\xf9\xe6\xe7_\x03\xba\x1d\x0eNQ9\xa4\x16ÖĄ\x04" } leaves: { key: "CDE=" value: "\xc5\x06\xb9X\xa2\x82\x8c\x81\x91˯\x01\x17\xa1\x85\xd1m\x0ey\x91\xec\xe3\xdb\x13\xd3\xdd\x10\xe0\xb8'\x91\xac" } leaves: { key: "CDI=" value: "%%\x17K\x18\xb3\x97\xfc\xf7\x9f\x1e\x14r\x04q\xb7\xed\xf0T\x84\x80f\xe8\xa7\xe0\x88\xd53\xa3S\x8a\x02" } leaves: { key: "CDM=" value: "\x8e\x85\xb3\x96\x98'\xd7\r\xff\xf7\t\x08\x9b\xbbfe\xab\x90olל_\xa8\xd9\x00\xa0\xd3\xfa\x01^." } leaves: { key: "CDQ=" value: "\x1c\x04\xdb\xc0a\xc5E\xde\x1a\xa8\x18C\xe79%\xa2\xc0.\xe1\xfb'2\xdaX7C\xcfԇ5\xee\xe6" } leaves: { key: "CDU=" value: "!ht\x94\xe9\xc9Y\xf9\xc2\xa65\x0c\xf2,\xa4\xd6,O/;" } leaves: { key: "CE4=" value: "\x91\x8f\xf1\xfc\xdd$+c\x820뷉f\x8c\x1em\x8f\xc0\"W5\x90\xb0O\xed\xbdR\x03T\xf1\xe7" } leaves: { key: "CE8=" value: "B\xcd\x06\xb3\x9d\xf6c\x06\xe3CŌĨ\x85\xefE\x82Bߓ\xa9gS\x11\xfa|\xa4\xe7\x07p\xa1\x03+" } leaves: { key: "CEA=" value: "\xdd\xd4a\xe7\xdej\xdaͰ(\x19`\xe7\xdcâē™\x0239\x9eA\xe5g\xba1\xce\xcbp\xedc\x92" } leaves: { key: "CEE=" value: "\xe9\x90Ym'\x9e\xb0,\xdfDSu\x9a\r~\x0f\x1d\xa6-\xd6a\xe3}\xee\xbc0q\xd5H\xcb\xf5n" } leaves: { key: "CEI=" value: "K\x19\xe5w7\xc0S\x87\xff-\x9b\xdeD\xf0M\xf2I\xbeO\xd9\xc812r\x1e\xf8\xff\x06J\x04\xa8f" } leaves: { key: "CEM=" value: "Q\xb8G\x17\x9d\x93\x8a~E\x84o\x849\x96\"\x9a\xe4\tg?\xc7l\x8283ßĩb9\xcbG\x84" } leaves: { key: "CEQ=" value: "@C\x9d\xd2\xf2\xfa\x8d\x87\x9eATv\xa3\xbco\x1aO\xb0\xfa;t\xd7\n\xe9\xd7\xdbywd\xbcF<" } leaves: { key: "CEU=" value: "\x9c\x19:@\xb5\xbe,\x89[\xd5U\xb3\xbb4\x1e\xfd%E\xf3\x9e\x18W!\x14\x8b\x15I\x12;\x04\x1cY" } leaves: { key: "CEY=" value: "\x86Q>)\x8a\x00\x82\xc3\x08\x08\xe7\xd1rU\x88Us\xf3sq\x93\x87}" } leaves: { key: "CFc=" value: "\xdd\xe7\xd3\xd1\x18\xe5\x0e#\xf2\xa9\xf0\xd4\xde\x10:G\xc4PŨ¸A\xdb\xf0\x00\x82~!\xa7*\x98\x96\x02" } leaves: { key: "CFg=" value: "\xday(F(\xe7\x0bz\x12\xff_x\x00C\xe2\xe4\xac`\xfb\x9fvzz\x17K2\x11\x03\xe4\x0f\xe9\"" } leaves: { key: "CFk=" value: "\x81KX\x80:\xbf\xeaE\xdd\x0b\xd5\xfdl\xcc\xfef\x87\x1d\xb7\x86e\xb0\x0et\xa0xW\r\x10\x84\xeb\x92" } leaves: { key: "CFo=" value: "\x02\xdbKH^\x814lR`\x00X\x0c\xf0j@ÉĄ\x19\xa0\nĪļ5\x14\xf6(A\x06\x1b\x8c\xe9" } leaves: { key: "CFs=" value: "6Į\x8aPĐĢ\x92\xdf=\xb2\xfb\xf7IX\x84\xfc|\xfeY~\x93\xfc40\x16\xbb\x8d\xd5\xd6\xd8\xd4" } leaves: { key: "CFw=" value: "\x17~\x06B2\x01\xf0\x03$\xd0Ч\x13\xb9\xa6\"\xeb\xc8r\x96+\x02*\n\x84%\x9b\x93\xeb\xbd \x80" } leaves: { key: "CGA=" value: "\xd9>\xa4Õļp\x81\x15&\x97@\xec\xfb\x8a\xc2!\x1c\xf7T\xceIa\x0c\x90hv\t\xa6\xba\xc1E\x14" } leaves: { key: "CGE=" value: "\xe8\x8dp\xd6l\nh\xf7\xea\x94.\xe9z=\xb2\xc8.\x91\x1d\xbe\xc3&8\x9aaU\xee\x9a\x07\x13L\x1d" } leaves: { key: "CGI=" value: "\xb2\x1d\xef\x00\x9d+\x1e\xdci\xfcŲŧ5\xecJO\x88\xff\xca~\x124\xe5\x1a\xd8J\\\xedGm\\\xe1" } leaves: { key: "CGM=" value: "\x16\xfcZt\xc5$\xd8\xc9R\x01\xbc\xde\x02\xa0\xcfS&K\x1d\xd8f\xc3\xc5e\x82\x05\r(1x\x81n" } leaves: { key: "CGQ=" value: "\x90E\xfb@\x1av\xd5\x1c\xa4+\xf9$'3\xff\xd1|W\xd6\n:\x036(\\n\"r)\xa2aG" } leaves: { key: "CGU=" value: "=\xc2\xc2\xf7\r\\\xbf\xa2M\x8d\xed\xb4\xbf\xa8\xf8aF\x07\xad\xda\x07\xe6ֈG\x0b\x11\xa5_d\xe3\xfb" } leaves: { key: "CGY=" value: "\xf2\xed\x85qƌ\xb7\x1eQ5\x99Ņŧ^-\x17b\x89\xfd~\xd2\xec\xcb\x0c\xee\xbeG\xf7\xfa\xd8\x12\xa0" } internal_nodes: { key: "A0A=" value: "K\x1a\xbc\x93\xb8[20.Ō–`\x9d\x12\x1f\x90W\xa3Ȥ~\r>\t0\x02\xfd\x07\tL\x03y" } internal_nodes: { key: "AgA=" value: "A:\x85Z\x8aÄąqBÎē\t9\x02\x0fF\xe9\x91s\x84\x07\"\x08\x08ÅŽ\xfe\xea-\xf6\xe8R" } internal_nodes: { key: "AwA=" value: "\xf9Χ\x1d\xf4?ZŲļ\x82\xfc\\g\xc9\xf2eyÄ­\x03\x0eI\rX\xc5\xdd\xd4\xf7\\K\xd0G" } internal_nodes: { key: "AyA=" value: "\xec{\"\x0c\"[U\x0b].R\x18\x83(\xa2\x1b~dU\x10\xa6\tyŲŖ\xa4-\xd4\x11\xb6\xb7\xbd" } internal_nodes: { key: "B04=" value: "\xb6\xa0\xd7SŌ‹[\xfc\xe3\x7f\xf9V\x94\xa1t\xee\xa2|\x01\xe3\xa3T\x01\x7f\x92\xfbR\xe1\n\xd4\x1f\x8e" } internal_nodes: { key: "B0A=" value: "\xfe7\xb1\u009b\xb4\xf1\xa4\x13'\x136N3\xe3g\xf0ɟ/\xa1\x9c\xfb\xf9Ųŋ\xaeĶ•R*\xbd" } internal_nodes: { key: "B0I=" value: "\x9f\xfa\xb6\x87,<\xb8\xab\xab\xc4\xe91z sTj\xaf\x02\xf2}\x0cĖŋ\xfb\xde\xed\x86Îē\x96K" } internal_nodes: { key: "B0Q=" value: "\xc8x\x0c\x91w\xba$\x1b\xcc\xe3\x97\xd4\xe2R|R\x01%\xb3\x9emWV\x0f\x0e\x1d\xb4\x80J\x14{a" } internal_nodes: { key: "B0Y=" value: "\xc3\t\xbb\xeb\xe1\x16O\xa2\xa8\xd7\xf4\x99\x97(7IY\xa3\xf5\x0c\x0b\xa8`\x18\xff\xfa\x81\xbb\x80m9\x83" } internal_nodes: { key: "B0g=" value: "\xf6[3\x9e-\x08\x87!\xabd\xed\xbbܯ\xa7>\x9cp=g\xfe\xa8\x01\xc4E\xdaԍ\x99\x8d\x1d\xfa" } internal_nodes: { key: "B0o=" value: "\x8c\xfe;K\xe8iҰp\x11p\x9f\xd4\xf0\x82\xed\xe4#\xdd7\xc2\xfdH\xf8\xa4߇|\x99\x05O\x1a" } internal_nodes: { key: "B0w=" value: "S\x87\xaa,\xe9\x8d\xdcd8\xf4K֝\x87C\x17W\x9dK\xae\x9b\x01\x90\xba\x10k\x01\xa9\xc5Ŕ\xd9" } internal_nodes: { key: "B14=" value: "N\x12\xb2\x110\xa33'\xb4\xd6\xe0\x91<\xb9\xad\xf2\x1d_\xd1S\x8d\xed\xae\xc93o\xe0ɟ\xc49\xcd" } internal_nodes: { key: "B1A=" value: "\xdbrn\xb8\xfeaDO\xb2@\xfaO\x17\xf2qr\xe1rT\xb3\xbbČĒWd\xcb\xe7w\xeb\xb9a\x9f" } internal_nodes: { key: "B1I=" value: "D\xc5=$h\xb5\x7f\xb9\xebq\xa1\xf7\n\xf9\xb0\xd5\xec\x80T\x8ew\x0b\x82\xb6y\xa3\x1eå˜ŋ\xe2h" } internal_nodes: { key: "B1Q=" value: "\xe0u\xd5\x12\xa7\x05gßĒ\xc9\xd5B>@~tv\x03\xc0\xaa\xa3\x9aC\xf8mΊ\xff7Z\x1dE" } internal_nodes: { key: "B1Y=" value: "/\xe2L\xe3\xe59\xc9^\x02\x9cV\x9d\xc4Ywa\xb53\xbf\x16\xb7\xa4\\\x84\xdf\x12 \xf8\x99\x9b$\x11" } internal_nodes: { key: "B1g=" value: "\x19\xf9>\xb8\xa2\xbc\x01\x07\xda\x10\xbdB\x15\x87g\x1d\x90\x863YR\xfb[\x9a\x14\xcdY\x17U\xc33N" } internal_nodes: { key: "B1o=" value: "f\xbb\xbb\x8e\xed\xc0]`\xff7j/B\xce\xe1\xd6\x0c͜v\x96\xaaRp\xa9\x01Įēs\x10\x00f" } internal_nodes: { key: "B1w=" value: "1L\x01\xb8\x94\xe58\xf3\xb5n\xc4P\x9d\xbfiãĢ?\xba\xcd\r\xb9Wۑ=\xac\r\xc3\x01/" } internal_nodes: { key: "B2A=" value: "Įˇ\xb4\xabGd\xe8\xb1\x05\x05J\x03\xc7#s\x82L/\xec\xec\\\x05\x17\xc8į“ŒIz\x9b\x8c\xed" } internal_nodes: { key: "B2I=" value: "\x07{\xc1\x10\x8a\xaaE\x9b\x9c\x15\x17d\x8e(\x0e`\xa2bi\x0f\x04\xed(u\x1c\xf1.\xf4\x7fTr\xa5" } internal_nodes: { key: "B2Q=" value: "8\xd8b|\x8d~\xd05\xaf\"\xa1n\xd0y.A=\xaaԕ \xb1\n\xfdN\xba,\xae_īĸ‘" } internal_nodes: { key: "BAA=" value: "ÕĻ\xa5\xa5\xa6X\xc1R\xc3Ę´25\x184nU\xbaP\\~ĖĩX\xd1\x1d\x82#.\xaa\xe07" } internal_nodes: { key: "BBA=" value: "\xaf\xa6a/\xdc\r[\x16\x8a\xbb\xd3\tՖËŋ\x14\xed5\xaf]\xae\x92\xde\xcf\x02\x98\xaa\x06(\xd7;" } internal_nodes: { key: "BCA=" value: "\x94\xec@,YI\xf0'\xaab\x04\x05\xfbU\xc7U\rN\xa4\xee\xfd\xa9n\xc5={e^\xc27Ûŗ" } internal_nodes: { key: "BDA=" value: "-A\x08oq\x8bΉ9`\xd1\xeeߤEp\x13\x04\x9d@\xea\xb2\xca5\x98&\x96\xe8 \x96\x04\x97" } internal_nodes: { key: "BEA=" value: "\x0b#O\x04%w\x95\x8b\xa4\xf7\x86c\xe2\xf4؜\x10\x12\xaf\xfcXl\x83iT_\x9d\xebo\xd9L\xd2" } internal_nodes: { key: "BFA=" value: "\x98\x0e\x1e\xf2N(\xc6\x7fQ13\xdf\xda]\xad\xf4\xe6J#\xd6g\xf7\xd3(\xfb\xb8\xff\x9b\x8aH\x86M" } internal_nodes: { key: "BQA=" value: "\xe37\"\x98\xe6Yd\xa9N\xed\xcbÚ˛\xc3\xc0)\x87s\xdd\x01\xff\x10^\x00m\x81\x98)<\x15\x8b\xb3" } internal_nodes: { key: "BQg=" value: "i\xd0B\xe1*\xe1H\x18\xb4\x8f\x18\xf7\xf2\x85Š\x9c\xdf\x1fp\x8d\xeb\xfa\xad\x12\xb7\xb7\xa4\xc2\xf9.\x15" } internal_nodes: { key: "BRA=" value: "\x01ZT\x18\xee\x1d\x86\xb8\xc7\xd2l~mk{\xfe\xbc\xe1\x11\x8c\x1a \xdf2\x00\xec_\xbf\xed\xe2\xd8\n" } internal_nodes: { key: "BRg=" value: "H\xd7,?O\xffE\x97\"*=\x7f\xf2'P\x1dA\xf1\x82\xa7\xf9\x8b\xdd@t|\xe9\x82-\x94*\xea" } internal_nodes: { key: "BSA=" value: "}\xfc\x87z\x8cZ\x84\x01\xc2xM\x81\xa3\xf3\x06\xca\x0e\x1d~\x84h\x99\xebL\xfe=\x98\xbe\xa2\xce\xf84" } internal_nodes: { key: "BSg=" value: "^\xbf\xf7\xdbu\xcf?sÛ¨T~\x9e]?9\x9b\xc0,\xa7\xaa\xcc\xe3\xf7\xa5p\xe1\xfeg\xfcp\x8b" } internal_nodes: { key: "BTA=" value: "S\xc2\r\x10\x1da5\xeeJjf\xc3'\xef}<;<\xac<\x8a\xa0B6Īļ\x1aoęŧˆ\xbd" } internal_nodes: { key: "BTg=" value: " F(&B\xab\x9d@t,\x82\xf0\xf7=\x99\xf9\nl\xb9\x83ڇ~\x03\xbb\xd4#\xb4\x16\xa2\xe2\x08" } internal_nodes: { key: "BUA=" value: "\x97\xe0\x86V\xd5]\xbb\xac\xd86\xdc\xc0\x12d&\x16\xc8ÃĄq84Np\xac\x05\xb3\x07\xae0\xb0\xb2" } internal_nodes: { key: "BUg=" value: "\x11\xbe\xb2-vuHL\x9b\x1a\xf1\xa2|\xcd\x0cI!\x08\xec\x84hW\r\xfc\x1f\x896_P\xceBf" } internal_nodes: { key: "BVA=" value: "\x16\x02\xd4\xec\xa9\xfd\t\xd7\x05H1\x1a\"N\x9dr^\xd50\xb3\xbe\x98\xae\x96!Q\xb8_\xa4\xb7\xb6\xfb" } internal_nodes: { key: "BVg=" value: "5W\x98P\xfe\x125'\xb9K0\xcf\x08\x17\x92\xdb\xfb\x04\xda^\xf6\x03K\x04B\xd9ifHZ\xb6\xfe" } internal_nodes: { key: "BgA=" value: "\xec\x86&v\xd5C*\x15\xb3\xec\t\x89_\xf1*\xc1\xf02Ō°\xbdk\x08\xb0x\xfc\xdfČĨ\xeaÜž" } internal_nodes: { key: "BgQ=" value: "g\r\x1b\xe9\x97}*\xaeN\xbf\xa8{!\x9aJsx\xd4\xc1j\xe2\x0e:\x88Z0e\xe1&\xdd\x16H" } internal_nodes: { key: "Bgg=" value: "b\xfd\xdeQ\xc1\xde8Ri\xbf\x13\xdf-l\xe0\xa0T\x7f\xb3\xfb!\xb5\xbabEVR=\xfc2\xe5\xeb" } internal_nodes: { key: "Bgw=" value: "S\x7fX\x84\t?ې\t{\xfd\xd1s|H~r\x00\x99X \xf0\xcanC\x99p+\x14X\x9f\x16" } internal_nodes: { key: "BhA=" value: "1\x01\x9d\x05N\x80΂\x99M\xe1\xda\x00y\x9c\xe7\xd9\x1eMN^\xae\xab\xb3\xdf\xec\xbfVv\xbf)\xb4" } internal_nodes: { key: "BhQ=" value: "\xa9\x06\xaf&\x96\x08\xeeJb*\x1f\xfd^\xbf\xeet\x8f\xb1G\x06\x7f_\xa5'\xb13M/\xde\xed\xf1\xb7" } internal_nodes: { key: "Bhg=" value: "2g&\xe2\xd2T\rY\x84\xb7\x94\xad_0\xbe\xb8\x84\xef\xef\xd0\xdb\xdcP\x18\x1b\xea\x01ZW\x94G\xa4" } internal_nodes: { key: "Bhw=" value: "JNv\xd8\x15\x8e\xe5/!\x06\x11D\xf4]\xe2[^~\xc68\xd7r]\x1cį…šDhR\r\x84" } internal_nodes: { key: "BiA=" value: "\xc6\xda>\x88\x98\xbc\x15b\xf0a\x98\xa3j\xab\xd0ČŠ\xe2\x9e\x18\x92\x94R\x8e\xd9\n\xbc)\xedOK6" } internal_nodes: { key: "BiQ=" value: "\x84\t\xb9\x15\x1d\xe2\xeb\xc9C\xc1\xc9\x05\xfe \x7fF\xb4M\xe5\xe1\xe2\x00cy\x10K\xfb\xcf@W:^" } internal_nodes: { key: "Big=" value: "\x8a\xe2>\xe5\xcbaÃŋ\x05X\xb8t\x94\xa7;\x12\xe76\x15i\x97o\xf3\xe2x\xa9\x9b\x01{\xa3\x12\xfd" } internal_nodes: { key: "Biw=" value: "\xe6\xca\xc3\xea\xd34\xfe\xb6w\xf9o\xe6\xbd\xf5\xff\x81l%kŲ¨U\x8eY~\x1a\x00\xb1\xe2>\xbd\x99" } internal_nodes: { key: "BjA=" value: "Лo\xafF\xc6UR\x87\xe1E\x8f[AZU{,\x07\xe8Vw\xf1\xe0\x93\x8a\xa3d\x9b\xffX\xce" } internal_nodes: { key: "BjQ=" value: "K\xb8\xa1K/\xaf\xad\x96\x8b\x9d;\xc9[\x9b\xad\x8bp\x9f\x9b.\xbaV\xba+\xa3\xdcW\x9d?^\x81\x07" } internal_nodes: { key: "Bjg=" value: "\x1b\x16GvG\xc3\x0b͚\x0eh\xa4\x92UL\xff֞lB\xae\x87\x04\xfb\x9e\x11|\xcdR\xf5R\x13" } internal_nodes: { key: "Bjw=" value: "\xba\xa3uËĸ\xff2\xddc'\xfaHD\x14\"w\x02\x0eK\xa3\xb9\xb2\x00\xa9}t\xb2Xzw\x9b\x9b" } internal_nodes: { key: "BkA=" value: "\xb9\xa5\x7f\x89O*\x0f\xa18\x1f\xf8E\xea\xab\x16\xd4Čŗ\x1d~\t\xfc\r\xe4)\xea\x0402" } internal_nodes: { key: "BkQ=" value: "S\xf3)\xe5oX\xd8J\xe1k\xc7gc\xab\xaa\xcae\xc6\x1f\x11RU\xdcz\xfe\xa7\xd1\x19-\xe1\xea;" } internal_nodes: { key: "Bkg=" value: "\xdd/Tײ\x02\xe5\x12\xe2\x1e\x18RG\xf5\xbaH\x93\t\x95U~\xabZ\n?N\x0fZ1\x8e\xb8\x88" } internal_nodes: { key: "Bkw=" value: "\x9cs\xef\x89N\x94/V\x1e(!5wc%\x00v\x9e\x1f$\xd1P/\x8a`=G\xad\xe4L@\xdf" } internal_nodes: { key: "BlA=" value: "\x08y\x88w\xad\xa35U\xf3s\x00 *&M\x0c\xb1\xed\xf0\x08\xa3\xad{m\x9fC(\xbe\xa4TVG" } internal_nodes: { key: "BlQ=" value: "\xfa)\x06\xdco\xf0b\x84x\x0bw\x9eD\xeeK0I\xae\x87C_x\x96\x16\x9d\xfcM\xfaď\xb5\xce" } internal_nodes: { key: "Blg=" value: "\x9a\xe39\xb1\xe5\x802q\x9d;^\xb6GP\x82NH@\xc6Z\x02G+\x80\xa5Z\xbe\xc5\xddn:\xc8" } internal_nodes: { key: "Blw=" value: "WD\xaf_w\x8b\x0e\x86\xa1FR\x9f\x7f\xd5\x19ČŖ\xf5b\x13\xa1\xf4\x1f\x82\xe4\xf9<\xc5\xcbėŊ‘" } internal_nodes: { key: "BmA=" value: "\x15\xd6l\x92\xd7$*q\x9f\xbf\xd6\x7f\x88\xb0\xc2`\x1d\xc2y\xc3\xfe\x82bÔĨ\xc4\xe9\r}i\x97!" } internal_nodes: { key: "Bw4=" value: "\x99\xbc\xce}\xcd\xf2\xfe\xb9]\xecY:P7\x05~l\xfc\xab\x066 G\xa1y\x9c\x9a\xe4\xe1\r\x89\xb9" } internal_nodes: { key: "BwA=" value: "L\xce+\\zŪŋ\x9a\xedZ\\sba\\\x80;\xd5jRЇ\x1eL\xee\nF\xc4v\xe9\x9f\xf6" } internal_nodes: { key: "BwI=" value: "|2\x9eQ\xe3\xc9\xeb\xae^X\xb2\xb4\xd4\xfb\x07\x96\xd0\x13\xe9]\x01v}x\xa5t\xbbte\xaf\xbe\xca" } internal_nodes: { key: "BwQ=" value: "\xd8\xf7\xbc\x0e\xc8EG\x98*\xc6\x17\xde\x03\xa7\x97\nGk\xbd\x0f]\xf1\xad\xfa\x00\x8cŨ†\xd4H'\xd3" } internal_nodes: { key: "BwY=" value: "\xa1\xf7}\xf9\x16A\x9a\xd1l\x9b\xeb\x0e\xffܜLęŋē\xceb\xd8u\xbf\x95\x8b\xceM\x81\xdf\xf6a" } internal_nodes: { key: "Bwg=" value: "\xbf\xf6\"l-\x9d\xc3\x000\x03G\xd2\xe0\xf5M\xd5(\xdb\xc0\xed\xa4f^\x19W\xbd\xd0\xc4\xd8r\x83\xbf" } internal_nodes: { key: "Bwo=" value: "\x0e\x90\xbaÔ¯\xf6\x1e\xf9n\xb2\xb0\x13ZB\x88A\x1b|\x80\x0c\x1b\xb9n\xd3A\xcd\x14ΰ\xaf \x89" } internal_nodes: { key: "Bww=" value: "Ge\xea0\xb7\x9c\x00\xef\xa0Z\xb9\xb6^q\x11wyFir\x1blgHV,\xa3\xd7\xedg\xf6\x01" } internal_nodes: { key: "Bx4=" value: "\xf1\x93\xb8\xc7\x7f\x80C)\xae\xe6\x84\xfe\xb9\x15{\x0b\xdf\xee\x9cF%\x1cH\xae\xba\x9a\xa6g\xc4ZF0" } internal_nodes: { key: "BxA=" value: ":\xed\xc9\x1d\xefv\x1a\xc7\x06dĮšŌĢ\x92\xf6\xf5\x06\x98J_\xb0\x82HzF\xc2\x00\x0bX\x95C" } internal_nodes: { key: "BxI=" value: "\x98@x\x8f\xac\x89C\xa6\xc5\xcb\ri\xe7s\xe2_\x9a\x15bb\xedo\xfey\x9e\x7fj\x0e\xa8a\xae\xb3" } internal_nodes: { key: "BxQ=" value: "\xa8\xe5\x9bOGŎ\xb2c꒛--\x0b\x9e\x90\xf4u\x0bښΩxs\xfd\x82\x92\xaaĖ‹" } internal_nodes: { key: "BxY=" value: "\x8du\x02jV\x07\xd5Ņļ\r\xc5d\x8a\xd2\"-\x83\x93\xf75\xdbI\xe8SD\xb4u \x84\x16I\xfc" } internal_nodes: { key: "Bxg=" value: "\xbe}\x84\xad\xf62\xf1\xe1\x93Đ\xe9\xfc\xfeQ\xd5#\x9c\x9e\"i~U\xae\xe2\xafJ\xd1C\x9bP\xfa" } internal_nodes: { key: "Bxo=" value: "\x99\x16fg\x10'\xa1\x91\x83&S\xf9+a\x90\xe4\xbf\xdc\x02\xd67w\x04\xe4^Ě\x19\x89\x96Л" } internal_nodes: { key: "Bxw=" value: ";\x9f\xf7ĶŽ\x0c˚ÚĢ\xa2\x1f\x8f}h\xd2\xfb\xfesh\x83U\x97\xfe\x86\x8c\xb1\xee^\xd0\xee\n" } internal_nodes: { key: "By4=" value: "\x8b\x17\xe1O\x0fPI\xda4.\"[{\x1f\xc8\x0bĖŽ\x90\x97\x8du\xd7\xd90,\xde\xd8~\x83\xba\x84" } internal_nodes: { key: "ByA=" value: ")8\x8f\x8b2DK\xc9j6\xb1\xec\xb5H[\x9c\x87)\xf6z~1y\xc3\xc6K" } internal_nodes: { key: "Byw=" value: "}&\x17\x18\x16\x9f\x0f0S\xe2\xd7jyo\x86\x1a\x91\xa7\xf3\xa6\xbaw\x98xPT];\t\xbf\x03\xdd" } internal_nodes: { key: "Bz4=" value: "\x0c\xbb\xce\x19\xc5\xd4q\xfc\x03\x04\xf8\xc9gJ0\x17g\xfb\xda^\x01,\xbd~\n7I\x8d\xd61\xbc\x17" } internal_nodes: { key: "BzA=" value: "\xc3'Œ\xa1\xed\x95DV\xcf4\xa88\xbb\x151\x00\xb82\xa6\x0b\x87\xabd\x9e\xb61\xb6\x10\x96F\x90" } internal_nodes: { key: "BzI=" value: "\r\xad×\xa5\xb7\xc9L\t\xbd\xd9m\x0f\xce؅\xb0U䭐\x18l\xd1\xd8\xf1a\xb6\x15\xee\xd1\xd3" } internal_nodes: { key: "BzQ=" value: "\xd9_\xfc\xbch-\xab1\x95\xd8Øą\x01\xbd\xfe1T+;\xa0\x11rb\xb4\xca\x01|\x0e\xb3\x06l\xda" } internal_nodes: { key: "BzY=" value: "\xdd\\)BsD\xf3}\xcapƟ0\xfd\xbf\xe3\xadp9\xc8&\x7f\x98\xe6)\xf2kK\xe9ș\xba" } internal_nodes: { key: "Bzg=" value: "\xdc\x00;\xb5\x9b\xe8\x14X}\x84\xf7\xfc8;oFrE\x9fT\x00ČĄáŧƒŨ€\xb35ÚĢ" } internal_nodes: { key: "Bzo=" value: "=\x86{\x07\xeb\x05\xbee\x19[\\i\xeeT\x99\xe2c\x86\x00\x93\xe1\xfdqU\x84\x8a\x90\x00\x19\xe4\x10\xbc" } internal_nodes: { key: "Bzw=" value: "\xd5u\xac\xe5b\xa0&\xaa\x92\xd9\xf6~FO\x9c\xa2>c\x80\xe3>rI\x86\xe5\x91'l\xaf" } internal_node_count: 98 trillian-1.6.1/integration/format/testdata/dump_tree_output_96000066400000000000000000000574111466362047600246540ustar00rootroot00000000000000prefix: "\x00\x00\x00\x00\x00\x00\x00" depth: 8 leaves: { key: "CA0=" value: "\x9e0}ë§ļ9q\xfd\xc6\xfeI\xcd\u0097\xab\xb0\xe0y.\xc6\x0c\xc5SpΊf+Eh\xce" } leaves: { key: "CA4=" value: "\xe3\xe5kq\xcc\xec\x9a\xe7Ņĩ>\x0e\x16Ũ\xc6j\x9fS\xe7\xb4\x15S\xeeT=\xc2U\x0c\x99\xed\xd1" } leaves: { key: "CA8=" value: "\xa7\x97\xc0\x16R\x94R^s\xf7F[\xa7\xbba\xb1\x9f\xbf\xa3bj0\xe84\xcdR\xd5\x08\xc6|^\x89" } leaves: { key: "CAA=" value: "\xe5\x918\x856\x85\xee]?.\xd6\xae{\x1bw\x82\xe1" } leaves: { key: "CAQ=" value: "\x8c\xa6\xb1\xbf놛\x94\xb0Ūļ\xc9\xcdp12Ò\xb4\x8f:Û˛3ejb\xaa]\x96\xb3\x18" } leaves: { key: "CAU=" value: "S\xc1\xd1n\x89\r1)\x86\xd9\xc3-\x10\x9fm@\x00\xf4\xa2F\x81\x0e\xe3\x95\xff\xcc\xf4{Y\xc8\"$" } leaves: { key: "CAY=" value: "\x91Čš.\nx/\xe3\xca-\x0c??;\xb1r\x8fViB\x9d\x9f\xa2\xc6\xd5\"\x8bŞ\xe41\x9b" } leaves: { key: "CAc=" value: "\r\xd76!L\x05o'^i\xff\xe6\xa1\xdaD\xd1q\xafƎ\xae\xdb\xeeIߕsR>\x05YI" } leaves: { key: "CAg=" value: "DH\x1au\xf5Éĩ\x0ep\x05\x8c\xd0\xe1\x95\xc9\xeeÚ˛\xff\xf7Z \x17\x7f\xfa\x9d\xc6(\x81\x9f\xe97" } leaves: { key: "CAk=" value: "\xc8\x07^\x7fL7\xe6\x1boQ\x85x\x7f\x00\xb8\xf1\xfes\t\xd9ę¸ĢL\xc5en$\xac-\x1b\xbf" } leaves: { key: "CAo=" value: "Ђa\x90\\\x1f\xa1h\xf5\x80\xcd\xe1i\xdb\xc68\xcfŪ\xfaq\x10O\xb8\xaaÅļ\xa7\x06h\xf3_" } leaves: { key: "CAs=" value: "\xd05\x1a\xe3FT\x03\x80~\xb6äƒŋ\x9fn#/u\xa7\xb5\xf7\xfa3HÔš\x97\x88\xbdJ\xf6_" } leaves: { key: "CAw=" value: "7V\xcf\xf2\xd8\x0c\x05\xe7\xf0 _BS\xecÕ›Đ‹U\x8eR\xf1\xc0\x0fK\xfe\x0fR\xf4\x87x\xe5" } leaves: { key: "CB0=" value: "\x81\xf5\xb0\x96\xe7;\xf6\x97\xdb\xd6\x7fm\xfb\x05\x80\xd6\xff-\xe5\xc5PßĻ\x82\x07`\xfca\xf77\xf0>" } leaves: { key: "CB4=" value: "\x92KAS\xbb~\x84\x17\xe2+\xdf\x03R\xbc\xbc\x89=\x8d\x85\x1e\x7f\x82\xe1\xe7\x1akh\x8aY\xd6\xdf\x02" } leaves: { key: "CB8=" value: "\x1d\xe6f\xde\xef\xef\xef\xc5C/)\xac2\x90\xe2\x08\xf1\x13\xbc\x0f\x93\x00M\x88/}\x1b\xa4\xad\x85\xac\x7f" } leaves: { key: "CBA=" value: "\xff\xa8nT\xc5\x1f\xabr\xbd\xfe\xa8'\x19\xe5k&\x12\xd3\xe9\xf8փ\xb5\xd2sCe#\x88G.K" } leaves: { key: "CBE=" value: "j\xed\xadGL\xa9{^\x13g\xf0B\x0b\xaf\xc1\xebsG\xce\x1c\xd5H\x11&\x9e3O\x81\xa4ܗ\xfd" } leaves: { key: "CBI=" value: "\x04CZ\xa9b\x01Į›\xc44F\xf6\xc3\x17\x02\x8e\xbe\xf1\xa2,@!L\xe6\xf3\xf3\xa69\xaf\"\xd6K" } leaves: { key: "CBM=" value: "z\xd4\x00\x15\xb1\x1a \x83nC\xfe\xdaDwq%\x95\xb0>h(\xfetn&\x95t\xae\x19O\xce\x0c" } leaves: { key: "CBQ=" value: "\x08\x85\xe85\xa8I\xe3\xcb\xfb\xa7\x08\xf0\x165G\xd3\xef`\xb8k\x14\xc6\x1c\xb2\xa8\xfd\xf0_y\xa2p\xd2" } leaves: { key: "CBU=" value: "kŨ¯\xfd\x92r3\xfe\xdewL\xd4Z4-\xd4Į”\xafx\x06\xef\xd5\xf4\x17\xed\x9eHP\x08\xc6\x1e" } leaves: { key: "CBY=" value: "~Z\xb8\xc0~K\x04\x80\x1b\xba1\x18\r\xcd\xc4g\xd4\xeeskj\xc2]|{ \x82ZK\xc8" } leaves: { key: "CBg=" value: "\x17\xc2Q\x93P\xe5\xbeE\xbb\x88\x1cF\xd3g\x10\x8a\xa7\x07o\x1c\x80\xfb\x86q\x16\xe4\xd1\xcb\x1e\xab*\x7f" } leaves: { key: "CBk=" value: "\xf9\xb1\x11Č´1\xa4\xa0\xeaz\xc6;\xa2\xc1p^\xd9\xf7\x0c\xf8\x95\x91\xd1\xed\xec\x84\xf5\xb4\xbd\x8c\xaa\xe9" } leaves: { key: "CBo=" value: "\xaf.\x9ca~~\xd2UJ\xd55\xf4 d\xd9w\xd3\xf3\x82\xe76\x88\xe0\xe8KÜŊ\xef\xf3i'\xbb" } leaves: { key: "CBs=" value: "Yf\x0c\xd2\x0cb\x0bL\xb9]\x02\x15\x0eL\xb4\xbd4\xb1\xea\xa7\xe7{`k\u009d7\x1d\xc9\x12\x1c&" } leaves: { key: "CBw=" value: "\xb8\xfd\x1cr\x10\xd1l.E\xb8\xa7\xef\xa1\xfc\x8d\xf4*Õą;,L\xbf\x16\xe8['\xee\xe5DY\xfa" } leaves: { key: "CC0=" value: "\xa9\xb8\t\x11|\xf2\xf2\xfbo׋\xe9\xfb\xdf\x11=\x8bey_1M\x19\xab\x8d\xa6\x12:\xd6\"ڄ" } leaves: { key: "CC4=" value: "\xb3\x01/\x86\xff\xf5\xac\xa4\xdc\x14\xfb]a\xe4\xe8\xf6J\xf1\xc2R\x1dn\x1e\xe0\x9e\x120I\xb4\xdep\x97" } leaves: { key: "CC8=" value: "\xe1\x80\x02\xf80ᎈ*n\x1f\x8c\x0fSLŃ\x8b9\xfe\xbc\xeb\x01m\xf2ؑ\xa8/\x16az" } leaves: { key: "CCA=" value: "\x15i\xb6[~=\x81\xa7\x180\xbb\x7fu\x08\xad\x95\x86\xa0\x9e\xffDTeIY\x96y\xcaO\x9fŨĒ" } leaves: { key: "CCE=" value: "\x81Uԉ$L\x8f\xeb\xd45{\xe5#\x8f!\xba\xb1A\x10\x96\x000Įž\\t\xb1\x07(\x12\xf4e" } leaves: { key: "CCI=" value: "\xa7\x92\xd7<0\xa9e\xe0\xa9\xebe\x82\x82ˎ\x0b#\x0f\x83c\xad\xa8\x12\x92\xa7\xb2\xe5>[\xe6<\xab" } leaves: { key: "CCM=" value: "\x8e\xb1y\x01\x91ʉ\x81⤗&>.\xfe\xa7K\xee9\xa2\xef\x18\xa6\x82\x92Wk\x80f\xbe\x8f\xc3" } leaves: { key: "CCQ=" value: "\xf7\xa8\x93\xf6B\xad\xd1\xf8\x7fw\x04X\x1f\xd8'\x1e-\xfdF@v\xb9@\x03p\x98AV\xec\x931\xb4" } leaves: { key: "CCU=" value: "\xb1\x7fC\x19\x159\x1b\xe3\x1f!\x99ذ\xf65\xb8\x1f4\x11-M\x83\x95Z\xe7\xadk\xefd\x1dÉŦ" } leaves: { key: "CCY=" value: "u\x86\x9a2\xc6\xedk@\x0ei\xcei\xf7\xb2[F\x1b\xb1\x1e\xdfYX\xf9No׋\x12\\8\x9f\x9c" } leaves: { key: "CCc=" value: "c-\x8bk\xac\x91\x82\xbb\\Qy\xd9Å\xac)\x13\x8e:\x03\x1c\x006\xc6\xcd\x1d\xc0NI\xc7\xedY" } leaves: { key: "CCg=" value: "cX\xe7\xd4\xd9u\xaepKsh\x00H\x07\x7f\xba2\xa4UK\xee\xa8\xfb\xebŨ“\x03\xe4\xffX\xc0\xee" } leaves: { key: "CCk=" value: "\x1d\xc2f]\x98\x0e\xed\x18\xc0\xde\xffb\xed\x05i%\xf2\x8d\x15]\xcc\x7f\xeb j)\xe7\x12Ëŗ\x06?" } leaves: { key: "CCo=" value: "\xb5\xbc`\"\xfe\xe9Q\xa2\xe3V\x94\xedRp\xe04\xe6\x85|#\xbb\x7f3\x16\x7fPk\x98F\x16\xa2]" } leaves: { key: "CCs=" value: "Y\xfbo\x80>}a8\x98Đĸ\x8f\x1c\xba3\x86\x8e7\x01\x11{\xb8\xd37~^\x9d\xd0" } leaves: { key: "CD0=" value: "\xc9_?\xd5\xc1X]c\xd8\xea\x0fÄ Q/K@ \xcc\x14A\xff\xa6\x96.\xe1\xc5\xe1\xdd\\\xa3," } leaves: { key: "CD4=" value: "X\xa6x\xff\x1d\xa2\xb5ŲĨ\xe8\xbd7\xaeP\xe4\xfd\x1f#\xd0t\x04\xb6@\x95\xdak\xb6\xb3\xe3Ūē\xfb" } leaves: { key: "CD8=" value: "xȈ\xa7\x9c\x86\xda&\xceE!f\x86'x\x08\x97\xeab,U\x0fÜ­\xc2\xfd8\xeb\xb4\xccs\xe5" } leaves: { key: "CDA=" value: "\xeb\x16\x83\xc5w\xe5I\xf0U\xa7\xc6\xc2\xe7\x87+b\xdboc\xb0\x01^\xe0V$\x95\\-\x83}*\x04" } leaves: { key: "CDE=" value: "\x90\xbe9\x92\xd0tTOuWД\x87}>z3\x08\xea\xbcrj\xfaY\xab\xc1\xf8eW\x12\x99\xdf" } leaves: { key: "CDI=" value: "\x18,Ύ\x9d\xda1\xd69\xa2m\x80\x16Fq\xb9\xc0Ķē\x80\xdf=Y\x19L\xb38\x1b\xb4I\xeb\xf4" } leaves: { key: "CDM=" value: "\x06*\xb2k\x11m^7\xf2*\xbaK@\xc9\xeaM\xa7\xcb/^âǝÆĒz\xa4KW=L^" } leaves: { key: "CDQ=" value: "\xee\xd3l\x02M\xc5m\x83\xf3\xa6(\x14\x8bIj\xbc\x94\x0e\xda\xf8\x93\xddD<'l1\xa5\x05\x9d\xdc%" } leaves: { key: "CDU=" value: "Ý(^n\xb7\x0eta\xb3\x99\x9eB\xb7]{\xa7Jl\x00hT\xd3\xe3(\xeeUO&\xfc\x90\xa2" } leaves: { key: "CDY=" value: "g䙃\x1c^\x07,Ķf\x1c\xaf\x0ez\x97/\x03\x90\xe8\xbe\x1c\xbc\xc9\ne\x1fwc\n\xa0\xa7" } leaves: { key: "CDc=" value: "P\xf2%\x151~vv\x90NWS\xa9\xbb#\xaf\x9bT\xcb\xcb\x05Ilk\xb8\x04\x19]D\xd3t\xf1" } leaves: { key: "CDg=" value: "\x8a\xd2ĐŽ\xdd;\xabp+\xff\xe0\x1ax\xe4\x15T\xa2\xcc\xeaה\x19\x10\x8f" } leaves: { key: "CDw=" value: "\"\xb0<>\xc6\xe7\xef\x02f\xa6\xd2Ōŋi,\xfb\x0e\xd0\x07\xc7\x04\xf9\xefo*\xbeE&p+\xcaT" } leaves: { key: "CE0=" value: "\\%\xe5\xb4\xf0\xf3\xbc?\xa4;\x8aV\xf1|\xdb\x18t8\xf3IRLr\xd8ĖŦ\xb1}\xd8\x10M\x0f" } leaves: { key: "CE4=" value: "\xfa5}k\x82e\x8cW\xb0\xff\xb40\xdfi\xac\xab\xf7\xb5q\xa8\xf1r\x11?P\x96}\xa8\x1c" } leaves: { key: "CE8=" value: "\x04\x96\x05\xa9\xe9\x17\xfd\xbe\x1d\x9f\x16\xf5\xf0o}huJ\xb3\x88T@\xf7e9\xc2p_cp\xc1\xf2" } leaves: { key: "CEA=" value: "}\x00\xb3\xa4h\xea\xa3j\xac\x1a\xff\xf4=SM\x91,\x95\xf2\xd1\x1a\n\xb8\xca\xdeK.9\xea'\xef>" } leaves: { key: "CEE=" value: "y\xbc\x18!\xb3xY<\x91-ԅ\x80H\xc4\x00Hj\xaa\x17t4\x07\xa4DF\x85*\xd7,\x18\xc4" } leaves: { key: "CEI=" value: "\xfb׏\xc5\xceley\xf3[\xc7\xe4\x8d\xf0͑ů\xc1ÃŽ\xfa\x91dZ\xadB(\xc4E\xd4\x10" } leaves: { key: "CEM=" value: "@\xf6\x93b\x9b\xe3$>N\xfb~\x88\xd17\x8a>\xd4b2\x9a\x1d\xa7\xa2\x8f\xd2)\xa2\x92\xa9\x104\x1c" } leaves: { key: "CEQ=" value: "\xae\xaeI\x84\x16\xa3y\xd7\x19\xe1d\x0f\x8f\x03P,\x97g\x1f8֗7ֆ\x92\xc5@3\xbc\xea\x82" } leaves: { key: "CEU=" value: "\xb4\xc2\xfeW:k+\xe4\xc6\x1bF\x82\xa0\xb1vk\x99v\x9b\xaaX\xfc~\x97\xfb\x9f~\xec0\x05:\x1e" } leaves: { key: "CEY=" value: "\xb2Ö°\x17\xb1{\x0eVNT\x831\xa2un\xf2\xd3k\xc1\xee\xe0\xe6\x1f\xc8b\xd50\x12\xef\xf9\n\xd5" } leaves: { key: "CEc=" value: "\xd8\xe9\xb9$\x86Ũ›\xc0\xda\x11@\xb4\x15\xf6\xd3}\x7fD\xf615\xf4\xf4^=~\x02ZĖ—\xe6p" } leaves: { key: "CEg=" value: "+\xb7R\x06&<+\xa6\xd9u\xb8'5'|\xf7\xc5OhD\xc1|Äē\xa4\x8c\xc0O\xd6/\xa3=" } leaves: { key: "CEk=" value: "-\xcb\"\xad\x1f\xa3 {\xff\x7f\xaa3.\xb4jPuy\xe7$u\xfcN\x90\xf8`\xa7\x9c\x82%G6" } leaves: { key: "CEo=" value: "w{\xa7\x1e-\xd7?'o&S\xaf\t\xc1J\xf7e\x9d\x08it4\xb8\x0c\xaf\x0cЉ\x16\x9f\xb4\x83" } leaves: { key: "CEs=" value: "\x80\xcdz\xef\x0c\xb4\xe0\xb3\xf3x\xd3\x1a\xc3n \x9d\xb2!yX\xf6W\xb5\xe79}\xac-\x91KЇ" } leaves: { key: "CEw=" value: "^\x003]f<}Q\xab\x8f.7\x82P\xeb\x0fHp\x8c\x03!\xf9B\xa0\xa1\xb1\xa9\x05\xf1~'\xef" } leaves: { key: "CF0=" value: "\xf6\xcbU5\xd8\xcf\xe0\x8d\x17\xd6H\xcc'o\xb0\xef\x10\x91\x01s\x93a\xc9Tj\xcd-3\"_\xe1L" } leaves: { key: "CF4=" value: "6\xf3\x972i\x80B\x88o\xf3\x15\xf8wՄu\xfet$\xa4\xdd<\x8b\x87\xd8\t2\xa2\xed\x93\xd7\x15" } leaves: { key: "CF8=" value: "K\x7f\x03鷋\x06\xb4ĐĻB\xf7\xca\xdfBa\xa3\x91[\xb8 \x03\xa0&\xf30\x87\xd3{\x82Xv" } leaves: { key: "CFA=" value: "\xab\x16p.aY\xa4j\xbc&\xbc\xfe'\xbce5J%Θ^Kw\xfdv\xd6!LT\xa5\xea`" } leaves: { key: "CFE=" value: "\xc7\xedN\xef!)\xfd\x87[\xbf1I2\xc4&e.\x81S\xcfD+-t-\xa0\x8fJ2s\xd3e" } leaves: { key: "CFI=" value: ">\xa5\xa2a\xf0\tA\x1f\xc8\xf5\xdc\xca\xf1\x13\xc0\xe2\xe5\xde\x05\x9c\x13\x7f'\xdd\x15\x83H&g\xb2\xfa[" } leaves: { key: "CFM=" value: "\x84\x9b\xf6\"\xc0`\x9a\xb0\xbfʇ\x82\xe9\xa0\xe7\x13\x87|FI\xc1\x81\xf3\x8d\xa6\x1e5\xe2\xc1\x92\xb9\x8c" } leaves: { key: "CFQ=" value: "\x80U\xc32\xdb`\xa6\x166*H\xe9(\x80\xa9\x9e\x98\x96\x0bf\xb5@Ę”Øƒ\x10\xfcx\nOo" } leaves: { key: "CFU=" value: "v\x93\x813\"\xb2qŅ\xba\x7fhix^\xab_\xdd.\xf3\xc0\xf5\x05\xed9\xc8\x10\xd3Ô´\"\xda" } leaves: { key: "CFY=" value: "΄Zz\xe5_\xa1\x99#\x80:\xb9\x15\xa2$\xe72\x04\xd9\xc9d\x92\xfble\xac!\xd8\xe2;\\B" } leaves: { key: "CFc=" value: "\xc1\xeb\xea\xb4\xce\xf5\x0e\xc5|\x15\xbb/\x96uu\x91\x0b\xd6\xe8a!\x15\x87qC\xcb\x0e\x036\xb1\xa4\xd3" } leaves: { key: "CFg=" value: "@\x15\x0eG\xc3\xea\x17\xf1!,O\x87\x01\xebe\xb8\x08\xe4\xac,@]\x82\x95Wi\xf6:\xf6\xe9I\xd9" } leaves: { key: "CFk=" value: "\x0eG%\x84\xf1\xd34\x06\xe1ꉠ\x9e\x12\x947\xa1\xa7\\\xb0U\x10\xba\xafu&\xbf\x05\x1c\xf2\xef\x85" } leaves: { key: "CFo=" value: "\x01\x06\\\xbcj\x82':\xc6\xc1\n\x17\xc97o\\\x005" } leaves: { key: "CFw=" value: "J\x1f\x06\x1b_\x03\xae\xd7-h\xd0\x17\x14\xd0c\xd9\x16\x14m\xd3\xc0Q\xa7nŅy\xf0\x1b\xb0\x92\x8f" } internal_nodes: { key: "A0A=" value: "\xa2\x9b|=$\xce/\x86+\x0b\"\x92n\xcc\xc3\\/b7Lm\xdb\xd5\x19{\xfeKo\xe8\xbdtF" } internal_nodes: { key: "AgA=" value: "\x8f\x05[\xe4\x9bG\x8fW\x12\xeb\xe2\xd8[\x89˔m\xccP\x87\x18_\x1d.\xb66S\xa5\x99\x81\x08\xce" } internal_nodes: { key: "AwA=" value: "\\\x99\x84e\xee\xd0i2j\t\xcc>\x8bwC\xcb\x08\xa0\x9a\xbc\xb5\xe4\x14r\x03\xb7\xc5A\x9e\x9f" } internal_nodes: { key: "B0I=" value: "\xb1k\xb7n\xcbs\x14\"ĖŦL\x0c\x9f\x96\x87w9\xc7+\x97\xfae\\A\x02]/\xb7\x19NY\xaa" } internal_nodes: { key: "B0Q=" value: "\xea|6\x15q\x14*b\xc6\xc7\xf5\x18p\xba\x1e\xd1\xf3\x1b\x1au\x8c\x02-\xa0\x1eq\xc6\x1bn\xb8\xc7\xf9" } internal_nodes: { key: "B0Y=" value: "\n\x93\xca+u&:Go\x83\x05<\xf4\xfb\xe0V\xaf\x19\xe8\x94\xff\x03|\xee\xe1\x06\xc4:~s\x87\xc1" } internal_nodes: { key: "B0g=" value: "\x8c\x8f\xdb\xcb\xed\x11p\xb4ėžą\xce\x0e\x03\xff\x81\x87\r\xb3\\ads\x8dˁ\xe4\x0c\x83\x9a\xa8\xd7" } internal_nodes: { key: "B0o=" value: "\x9e\x87Ų’\xe8\xf8\xa8\x87\xee\xd72\xda\x17C-\xa8![J\xab\xf7\x89ɞ\x156סh+\xef\x1c" } internal_nodes: { key: "B0w=" value: "\xcfb\xfem7P&\xd4Z\x18gG\x92Z\x08G7\xc6}\x15H\xf1\x84fLOM\x83\x194\x80\xf3" } internal_nodes: { key: "B14=" value: "\xe2\xbe\x03\xde5\x01\xb8|\xa7\x8a\x9d\xa4\x1b%\x022P\x83l~\xb0\x1f\xb9\xa8\x95R3\x83m\xc8\x12]" } internal_nodes: { key: "B1A=" value: "/\xc0=\x0ew\xe5\xc1k\x8c\xb4\x9f\x94]|\x98\x11\x14\xaf\xef\xedҐ\xa7\x11\xad}\x7f\x80Km\xed\xf1" } internal_nodes: { key: "B1I=" value: "\xddA\xa6\xc7\x00r\x00ab\xd0\xf9\xc4s\xa4n\x96\xee\x1e|\x89#\x96a>M\xad\x88\xd1Ԋ\x1c\x19" } internal_nodes: { key: "B1Q=" value: " \xd7\xf7\x0f\xda\xc8}\xa9<\xb1\xa7\xe3\xc4\t\xcez\xf4ÖĒ\xf5\"\x99/\xb1-\x89\x8c\xf3\xa6/-\xf9" } internal_nodes: { key: "B1Y=" value: "\x8a\xa9\xd5\x02&\xebm=\x9a\x97\xd5\x07S\x00\xdf\x07\x10sR\xa7&K\x1c\xf5\x0e9\xa3\xd1\xee\x00\xf2\x98" } internal_nodes: { key: "B1g=" value: "8(\xcdG\"\xfe\xa4\x02\xf8\x19=\x9ax\xb4H7\xdaXKy5\x8a\xde\xffhL\x18\x19^\xe9\x83\xc7" } internal_nodes: { key: "B1o=" value: "\\|RΉ\xedV\xbd5(\x8e&\xc0p\xbd\xd0U\x1c\xb9\xfa\xd7\xd1cr\xa7Ķē\xbe\xbc\xeaD*" } internal_nodes: { key: "B1w=" value: ">j\x15A\x0c\xd5_\xa4\xf2\xe98\x83\xe2\xc2z4\xee\xc5\r^q\xee8\xac{Z\x18f.z\x82\x00" } internal_nodes: { key: "BAA=" value: "\xb7\x93l\xe4\r\x8aW\x93e\xeb\xcfi>i~f\x9b\xc3\x18\xe3L\xc2\r\x07\x97Į˜&C\xac\xefV" } internal_nodes: { key: "BBA=" value: "\xf8\xcd \xe2l\xf2\xa8\xa3ؚ\xda\x12͚\x12\xa0\xbaCb\xe4\x05\xde\xc1%O(\x81\x13\x19\xc0\xc9\x0b" } internal_nodes: { key: "BCA=" value: "RF\xff\x08\x15#\xf5^w\xa9\x8e8&t\xb8\x9c\x1a+\xb7l|Ξ\xb3W\x15r\xa6R\xb6\x07\xea" } internal_nodes: { key: "BDA=" value: "FT\xc2Ř\xf8v\"\x93\xe0;\xddjF0\xed\xcd[\x00\xb9zX\x90\xbc\x1d\xb3r~}\x03TY" } internal_nodes: { key: "BEA=" value: "QÛ {~\xf0g=\xa3\x9f\xbc\x96)\x85\x05t\xd6\xe9\xb0H'\x07ZD]x\x10\xbeE\x0c\xd2{" } internal_nodes: { key: "BFA=" value: "\x1b\x8f\x04\x99y\xc7\x0c\xbe\xfc\xael \xd4%tx\x0b<$\xb7\x1f\x90E\xfc\xf6!\x88k\xbc7\xbb\xb5" } internal_nodes: { key: "BQA=" value: "\x01d\xda\x05q\xfc\x8f-\x10J\xec\xc0r/\xf4\x91ar\xb0$i\x032\x99\x9eɸ\xea\x1d\xe8\xce+" } internal_nodes: { key: "BQg=" value: "\xc9$\xaeʛ\xf8\xffn\x0f\x8e\xa1I-H\x1aV\xc0g\xf8\xa1l$\x03`$\x08\xfafh\xfc\x80\x01" } internal_nodes: { key: "BRA=" value: "w\x08\xeb\xeb3\xf2\x1b\x97\xd5%\x19}b\x00Lc\x1e;W\xb7?K\xaa~N\x9f\xa1i\xbc\xb8\xd0C" } internal_nodes: { key: "BRg=" value: "\xe9\xde\\y\x17\xdd\xeeL2\x1a\x81U\xe3\xcbb\xf3\x1b&\xabl\x9b\xd3;\xef]Ũžq+\x12\xfbd" } internal_nodes: { key: "BSA=" value: "\xe9\xcd^Ή\x0f\xf3\x9e\x13dt\x08\x06\xfb\x1f\xb4\x83厃\xba\xc9\xf4c\x1a\xb2pUlZ\ru" } internal_nodes: { key: "BSg=" value: "\x05\x83\n\x06\xd0b\xfcKa\xa3\xe2\x99`\x7f\x1b)2\xf2\xd6\xdde\xb0\xfe\x07\x99]\x81\xbe\x9b\x1e\xa9 " } internal_nodes: { key: "BTA=" value: "z\xc4W\x0f\xf1\x87:\xe1t^\xf0\x0f\xd0\x1e,\xddT\xa8\xba`\xac\xb5\x9c\x16J0\xfbp\xc5y\x8f\x0f" } internal_nodes: { key: "BTg=" value: "\xf8\x85\x97\xe1\x1e\xaa\x00)4hR\xcaD\xc4&Ė‹@\xae\xe5\xe6k9.F\x1d\x04\xa1?\x82\r\xbf" } internal_nodes: { key: "BUA=" value: "\x02\xa1\xfds\xfc\xb0\xfe\xc2\xd5\"Piz\xf2\x1c\x7f\xe9z\xce\xca\x1c\x89\x11\x9d\xc2=$%\x9a+!\xa6" } internal_nodes: { key: "BUg=" value: "\xcb_\xbdC\x9f2\x8d\xf5P\xc30V\xff|\xf8\x16\xfc\x8em\x96>\x1ec@\xe9X\x8aŌŽ=\x11\xb0" } internal_nodes: { key: "BVA=" value: "\x85y\x9a\xcb3\x17\xee\x85s\x859_n\x87\xb3q-\x0e\x95:\xd6^m\x80\xa3\xbd^?Y\x90b\xf5" } internal_nodes: { key: "BVg=" value: "\xa6\x91q\x15B\xdbqo\xb6&\xbb\x08\xa2\xaf\t\xf6@\xee\xff/@\xc7\xfd\xf3\"\x16o\x94\x8e\xa2,v" } internal_nodes: { key: "BgA=" value: "t7w\xa2>\x8a\x9e\xf0]\x92\x83\x8b\xaa\xeb\x8c\xf9uQ#\xb3؞B\xb6\x802\xe3,\xfdJM\xa0" } internal_nodes: { key: "BgQ=" value: "\xe0\x1a\xb6Yw\xa8\x91\xc1\x14\x8c\xb4\x1ed\x8a\xa8\xcc\xf2P\xcb8\x04A\xd81\xb1BŌŋ" } internal_nodes: { key: "BhA=" value: "o!i\xbf+\xd4ꐀ\xb2\xf4J7\x9c\xf6\x80{\xfe\x94\x89\x1e\x90\xcbl\x06\xa3j\xa7%&\x96\x8b" } internal_nodes: { key: "BhQ=" value: "7\xd4aR~\x12\x07'\x13\xf3P\x9eiC\xe4\xdef\x01\x9b)Ėļ\xc0\xfbKG}v\x06\xb2\x08r" } internal_nodes: { key: "Bhg=" value: "\x14\x7f\xb8iExGvg\x97\xc4u\xc4\xde\xde/az:3=W\xd9O\xec\x02\x07pj\xe44\x1d" } internal_nodes: { key: "Bhw=" value: "u\xff\xefÅž?\x8c\xd6TyJ\xf1\x06\xdb\x02\xa8F[\x93\x81\xe0\x99\x91\xee\x1c\x86\x0b\x03\x0f\x9f\x8f\xaf" } internal_nodes: { key: "BiA=" value: "\xba\x82/\xdaX\x9b\xf2o\xf8\xdd\xf3\xf1S\xf0\x1f<\xbeËĢ\x10l\xdaN\xc0\\\x8c(\x9b\xe3\x18\xabA" } internal_nodes: { key: "BiQ=" value: "h\x99e\x9e?8}\xc9\r\xac\xf2 tc\xb3.L\xdc[\xf5\xc9Ѐ$\x03h\x9d=΂<." } internal_nodes: { key: "Big=" value: "7p\xf0j\x163\xbee\x9el\xd6/\x9f\x9e\xa3z\x8d(\x9a'U\xe5\xdc\xeb\xf7\xb7\xb6\xf0d\xee\xee\xef" } internal_nodes: { key: "Biw=" value: "\xa0\xb7\xa0\xa98WLRha\xefN\xc7\x12\xb1X\xb1V\xd3\xd8T\x90\xf1<įļ‚\xd9|\x01\xaf\x16" } internal_nodes: { key: "BjA=" value: "\xa8C/\xd9\xdb\x1b\xe8\x11\x8d\xa4\x1c\xb0\x83`\x92\xe8K\xabq|\x08\xf0q1\x81\x82_@\xdd&\x19k" } internal_nodes: { key: "BjQ=" value: ";8Ў\xde4\x03\x95\x0f\xb3U=\xf6\xf8*\xd0}Oo\xe1\xc7\xfct\r\x81;=\xdc\xe5U+t" } internal_nodes: { key: "Bjg=" value: "X\xac(\"\x03x\xd7KLO1\xe9dȋ\x14\x83,o\x87\x13\x8c\xb9o1\xd3,\x8d\x9c\x181Q" } internal_nodes: { key: "Bjw=" value: "\xbc׌\x95\"Íŋ\xaf\x85]\x08\xdb\x00\x93\x03\xe2PĘģ\\|.N\xd8x\xf6P&7\xd6É´" } internal_nodes: { key: "BkA=" value: "D\xdf\xf9W\x0cc\x1fOj\x0f\xdfok\xf3\x8f\x01S\xfc\xab\xf9\x93\xc0Q\xaa1t\x9cAÕ¤\x08\xa0" } internal_nodes: { key: "BkQ=" value: "3RNq\xdb3\x13=\x07\x89\x89wo^MFZ\x07X\x1d\x92Ņ…]\x9c\x12}\xfbB$\xda\xd4" } internal_nodes: { key: "Bkg=" value: "#\x81+\x9e\x196FGA\xaaS\xf8`Īģ\xb3܌vw\x05\x12\x04\x97\xc1\x9c\x86X\x18\x05\x8bT" } internal_nodes: { key: "Bkw=" value: "\x86\x84\xebc\xd1?x\xccW\xcf\xc5^ؤ\x07\xa0\x8e\x99\xc3\xc9T\x84Lj\x07\xad.\xbc\xc5\x02\x8f!" } internal_nodes: { key: "BlA=" value: "\x123\xa5+\xf8\x1c,\x9b\xca{Éĸ\x80\x9aQ\x9c\x1ffQ\x83\x93\x12\xd5\xc2d\xcb\xe3M\xc7\xd9!x" } internal_nodes: { key: "BlQ=" value: "\xfd\xc1\xef\x16\x00p$q\x97\x0e\xe1\xf6\n\xb0\xb9s\xd15\xe7\x02LM\x14\xc0F\x07\x92<;\xa8\xc1\xb2" } internal_nodes: { key: "Blg=" value: "\xa0\xc9Ԇ\x0b\x86\xea\xe0\xbe\xd2*\xb9/\xa1\x0bM)?\x03\xd9\xddH\x1b\x861\xac\x84\x0f!o\xa4\x15" } internal_nodes: { key: "Blw=" value: "J\xed\xe5$\x967K\x13+\xea\x18\xf8b\x0c\xdcrO\xa7`7\xff\x8b\xdd+\tŨœ\xf0\xe7uJe" } internal_nodes: { key: "Bw4=" value: "q@Âē\xc24\xea\xc9\xe6d\x9a\xf9\xf7\x8b\xbdi\x02\xf0\x03p\xc0;\xeb[\t\x9c\xe9rO\x81\xe9'" } internal_nodes: { key: "BwA=" value: "\x81ŪŽ\xc1\x08O)\xb4\xbe\xc8\x0bJ\x9fŌĩ\xac\xeb\xbc\xc2o\xc4E\x86*\x16\xb2\x9b\xa8\x8eÎŗ\x01" } internal_nodes: { key: "BwI=" value: "\x8c\x0bTLC\x9a\x92\xb5\x07\xbd\x98\xf8\xeas\xad\xa9oz\xb1\xd9\x11k2\x8c\xaaaNR\xd7X\x0e\xba" } internal_nodes: { key: "BwQ=" value: "\xbbL\x107X\x9b\xa7Z\x91\xbblÕ°\xd1b\xf3\x87\xfc\xe7\x01\xa3\x9d\x8a\x7f\x85gr\x03TT\x92x" } internal_nodes: { key: "BwY=" value: "Ȑ~\xa1|\x02z\x00^\xb2\x96+\xb8\x90\xd7/\xb0c\xab\xf5\xbd\xeaA\\\x9f\x99`\xbfg\x03\xda\xd1" } internal_nodes: { key: "Bwg=" value: "S`}\x91\xf63\xdcA\xb2\x10)m\xe3zr\"\x94\xc5$\x95\xed\xc0\x13\x8f1\xab\xa1\xfcn\xe5\xcbQ" } internal_nodes: { key: "Bwo=" value: "h\xbe\xcdNy\x03\xb2\xe9hC\xc6\xe0\xfa\x13(\x92\xbb2\xbf\x14XD%%\xf7UyMS\xe0\xcb\xe5" } internal_nodes: { key: "Bww=" value: "億P\"EkE\x03\u0096J5\xecC\x1e\"G\xe6\x9b\xf1\xfe\x15\xfaQ\x91\x98>\x02\xb9\xfa^" } internal_nodes: { key: "Bx4=" value: "Ɂ\xab\x98\xcf^u\xe6Z\x8a\xf1\x0c\xfa\x84\x98˟p\xb8\xc4?\x85] \"X|\x00\xb8\xb8It" } internal_nodes: { key: "BxA=" value: "&\xba\x8a}\xf7\xb8V\x03\xb86\xa8~\xf9-;L,x\xc8\xed\x87\xd3dQ\xbf\xee6\xaa\x9bM\x9a9" } internal_nodes: { key: "BxI=" value: "\xca\x04ڞ\na\xba\n\xbca\xf9\xf4!n&\xfbÔĸ\xb2\xf8\x9b!E\x8aZe\xe5\x1a\xf8\x7f\x83\x82" } internal_nodes: { key: "BxQ=" value: "\xf6N(x2]\xa7\xdc\xe7\xff\xaa\xdd\xeb\x9c\xe3{\x03k\x9b\xfa|\xe9\u009cS\xb7\x04\x1e\x03\xd6\xc0\x10" } internal_nodes: { key: "BxY=" value: "@\xe7<\x01\xbb\xbc\xa3\xd5J\x11^\x8an\xc8H8D\xe1|\xf9K\x84\xc3k\xafq\x17\x98\x98\x1a\xde\t" } internal_nodes: { key: "Bxg=" value: "\xba?3ô„ρ͙\xef\x8a\xcf\xe7 \x91\xa9߆\xda?'5\x82*\x91\x9e\xd5\xc7\xe8\x05\xae\xfe" } internal_nodes: { key: "Bxo=" value: "\xded\xd7>OĶ‘r9\x1a\x17\xf4\xeeVy\x15:S+\xb1\x91\x9b\xd2\xfc\x02\xccq\xca\xf4#Χ" } internal_nodes: { key: "Bxw=" value: "\x1e\xa0G\x1c\xa6\xee0\xabR\x14n\xdaE1\x8b#ju\xd3{Åŗ\xef\xef\x9fHud\xd0\x00\x85\\" } internal_nodes: { key: "By4=" value: "\xe4\xceF\xc67u\xab\t\xc5\x19ܛ\xb8[\x17#\xc7\xc9$X\x0b\xb3\xfa\xa4\xb1Ũ°Ķš\xb9\n?" } internal_nodes: { key: "ByA=" value: "\xf7\x18\xdcj\x8c\x19\x06I\xaa\xaf\x1c\xd8\xe8YW\x98XP\xcd!\xa5Ũ\xcd\xea\xbf\xfe\xaf\x97\x0c\xb6e" } internal_nodes: { key: "ByI=" value: "c\xafv\xf0o\x08\xac\xb0/S\xa7L\xadi\n\xa5N;!Bdc\xa2\x96\x12Nq\xa2\xe3\xdbj\x97" } internal_nodes: { key: "ByQ=" value: "\x83\x8f\x8a\x12\xb4W\xcd]\x9d\x8a\x92\xa5\x97\x85\x0b\x1d\xee:&\x98\xaa\xef\xd1: ÛŊ.\xf6\xa4^\x02" } internal_nodes: { key: "ByY=" value: "\x0cy\x05\x1daq\xdc\xc0Z\xc3\xd2 \x88\xa2\xbc\xdfh\x8d\x1c\xf8\x85\x986\x9d(\x18\x18&-}K\x04" } internal_nodes: { key: "Byg=" value: "\xa9\xbd&\xac\x88Ėą\xa1Įž\xfd\x85\xf4L[\xf7\xc0\xed\xec\xb1G\xe0\xe0\x937@[u\x83gp\xd3" } internal_nodes: { key: "Byo=" value: "\xddf\x91W(6D\x16d\x7f\x83\xd2\x18Z\xe1\x12\xbc#\xab\x0fdo\xfc5V\xc7r\x07\x9e;\xaf3" } internal_nodes: { key: "Byw=" value: "ˍ]\xe9o~\x85\xa8Ņš\x9fmJu\x100ڈ\x9d\x1efGb\x8d\x86\xc2\xf0\xc8\"\xfa\x92\xe1" } internal_nodes: { key: "Bz4=" value: "Q\x14Iv\xbe\xbat26\x14\xfa\xfc;\xa6e\x87!\xa7'\xcct\x02o\x83\xa4\xf7\xa4\xbb\xfa\xb2\x11\x9a" } internal_nodes: { key: "BzA=" value: "\xf3\x91CFTĶž3\x01\xa6\xdd>K\x19p\xe7]\xe1\x1d\xb4\x92\xaa\x7fg\xb4\xe4\xa0\xcf\xff,\xe9K" } internal_nodes: { key: "BzI=" value: "\xd8\xe2\xe7\x1f\xca\x0c\t\xc6[\xbc_=\x1cX\xfe\xd68>\x8aX\xad)\x82(QŞ\xc7\xf4\x8co\"" } internal_nodes: { key: "BzQ=" value: "C\xa9\xf4o߲\xa7\xce\x1c\xa3\x9fq]wDf\xf5u\x99\xf5\x99\xe5\x1d7d" } internal_node_count: 94 trillian-1.6.1/integration/functions.sh000077500000000000000000000213651466362047600202540ustar00rootroot00000000000000# Functions for setting up Trillian integration tests if [[ -z "${TMPDIR}" ]]; then TMPDIR=/tmp fi readonly TMPDIR declare -a RPC_SERVER_PIDS declare -a LOG_SIGNER_PIDS declare -a TO_KILL declare -a TO_DELETE HTTP_SERVER_1='' RPC_SERVER_1='' RPC_SERVERS='' ETCD_OPTS='' ETCD_PID='' ETCD_DB_DIR='' readonly TRILLIAN_PATH=$(go list -f '{{.Dir}}' github.com/google/trillian) # run_test runs the given test with additional output messages. run_test() { local name=$1 shift echo "=== RUN ${name}" "$@" rc=$? if [ $rc -ne 0 ]; then echo "--- FAIL: ${name}" else echo "--- PASS: ${name}" fi return $rc } # wait_for_server_startup pauses until there is a response on the given port. wait_for_server_startup() { # The server will 404 the request as there's no handler for it. This error doesn't matter # as the test will fail if the server is really not up. local port=$1 set +e wget -q --spider --retry-connrefused --waitretry=2 -t 20 localhost:${port} # Wait a bit more to give it a chance to become actually available, e.g. if CI # environment is slow. sleep 5 wget -q --spider -t 1 localhost:${port} local rc=$? set -e # wget emits rc=8 for server issuing an error response (e.g. 404) if [ ${rc} != 0 -a ${rc} != 8 ]; then echo "Failed to get response on localhost:${port}" exit 1 fi } # pick_unused_port selects an apparently unused port. pick_unused_port() { local avoid=${1:-0} local base=6962 local port for (( port = "${base}" ; port <= 61000 ; port++ )); do if [[ $port == $avoid ]]; then continue fi if ! lsof -i :$port > /dev/null; then echo $port break fi done } # kill_pid tries to kill the given pid(s), first softly then more aggressively. kill_pid() { local pids=$@ set +e local count=0 while kill -INT ${pids} > /dev/null 2>&1; do sleep 1 ((count++)) local im_still_alive="" for pid in ${pids}; do if ps -p ${pid} > /dev/null ; then # https://www.youtube.com/watch?time_continue=1&v=VuLktUzq23c im_still_alive+=" ${pid}" fi done pids="${im_still_alive}" if [ -z "${pids}" ]; then # all gone! break fi if [ $count -gt 5 ]; then echo "Now do kill -KILL ${pids}" kill -KILL ${pids} break fi echo "Retry kill -INT ${pids}" done set -e } # log_prep_test prepares a set of running processes for a Trillian log test. # Parameters: # - number of log servers to run # - number of log signers to run # Env: # - If TEST_MYSQL_URI is set, uses that for the server --mysql_uri flag. # Populates: # - HTTP_SERVER_1 : first HTTP server # - RPC_SERVER_1 : first RPC server # - RPC_SERVERS : RPC target, either comma-separated list of RPC addresses or etcd service # - RPC_SERVER_PIDS : bash array of RPC server pids # - LOG_SIGNER_PIDS : bash array of signer pids # - ETCD_OPTS : common option to configure etcd location # If the ETCD_DIR var points to a valid etcd, also populates: # - ETCD_PID : etcd pid # - ETCD_DB_DIR : location of etcd database # If WITH_PKCS11 is set, also populates: # - SOFTHSM_CONF : location of the SoftHSM configuration file # log_prep_test() { set -x # Default to one of each. local rpc_server_count=${1:-1} local log_signer_count=${2:-1} # Wipe the test database if [[ "${TEST_MYSQL_URI}" != "" ]]; then yes | bash "${TRILLIAN_PATH}/scripts/resetdb.sh" elif [[ "${TEST_COCKROACHDB_URI}" != "" ]]; then yes | bash "${TRILLIAN_PATH}/scripts/resetcrdb.sh" fi local logserver_opts='' local logsigner_opts='' local has_etcd=0 if [[ "${TEST_MYSQL_URI}" != "" ]]; then logserver_opts+=" --mysql_uri=${TEST_MYSQL_URI}" logsigner_opts+=" --mysql_uri=${TEST_MYSQL_URI}" elif [[ "${TEST_COCKROACHDB_URI}" != "" ]]; then logserver_opts+="--quota_system=crdb --storage_system=crdb --crdb_uri=${TEST_COCKROACHDB_URI}" logsigner_opts+="--quota_system=crdb --storage_system=crdb --crdb_uri=${TEST_COCKROACHDB_URI}" fi # Start a local etcd instance (if configured). if [[ -x "${ETCD_DIR}/etcd" ]]; then has_etcd=1 local etcd_port=2379 local etcd_server="localhost:${etcd_port}" echo "Starting local etcd server on ${etcd_server}" ${ETCD_DIR}/etcd & ETCD_PID=$! ETCD_OPTS="--etcd_servers=${etcd_server}" ETCD_DB_DIR=default.etcd wait_for_server_startup ${etcd_port} logserver_opts="${logserver_opts} --etcd_http_service=trillian-logserver-http --etcd_service=trillian-logserver --quota_system=etcd" logsigner_opts="${logsigner_opts} --etcd_http_service=trillian-logsigner-http --quota_system=etcd" else if [[ ${log_signer_count} > 1 ]]; then echo "*** Warning: running multiple signers with no etcd instance ***" fi logsigner_opts="${logsigner_opts} --force_master" fi if [[ "${WITH_PKCS11}" == "true" ]]; then export SOFTHSM_CONF=${TMPDIR}/softhsm.conf local pkcs11_opts="--pkcs11_module_path ${PKCS11_MODULE:-/usr/lib/softhsm/libsofthsm.so}" fi # Start a set of Log RPC servers. for ((i=0; i < rpc_server_count; i++)); do port=$(pick_unused_port) RPC_SERVERS="${RPC_SERVERS},localhost:${port}" http=$(pick_unused_port ${port}) echo "Starting Log RPC server on localhost:${port}, HTTP on localhost:${http}" go run ${GOFLAGS} github.com/google/trillian/cmd/trillian_log_server \ ${ETCD_OPTS} ${pkcs11_opts} ${logserver_opts} \ --rpc_endpoint="localhost:${port}" \ --http_endpoint="localhost:${http}" \ ${LOGGING_OPTS} \ & pid=$! RPC_SERVER_PIDS+=(${pid}) wait_for_server_startup ${port} # Use the first Log server as the Admin server (any would do) if [[ $i -eq 0 ]]; then HTTP_SERVER_1="localhost:${http}" RPC_SERVER_1="localhost:${port}" fi done RPC_SERVERS="${RPC_SERVERS:1}" # Setup etcd quotas, if applicable if [[ ${has_etcd} -eq 1 ]]; then setup_etcd_quotas "${RPC_SERVER_1}" fi # Start a set of signers. for ((i=0; i < log_signer_count; i++)); do port=$(pick_unused_port) http=$(pick_unused_port ${port}) echo "Starting Log signer, HTTP on localhost:${http}" go run ${GOFLAGS} github.com/google/trillian/cmd/trillian_log_signer \ ${ETCD_OPTS} ${pkcs11_opts} ${logsigner_opts} \ --sequencer_interval="1s" \ --batch_size=500 \ --rpc_endpoint="localhost:${port}" \ --http_endpoint="localhost:${http}" \ --num_sequencers 2 \ ${LOGGING_OPTS} \ & pid=$! LOG_SIGNER_PIDS+=(${pid}) wait_for_server_startup ${http} done if [[ ! -z "${ETCD_OPTS}" ]]; then RPC_SERVERS="trillian-logserver" echo "Registered log servers @${RPC_SERVERS}/" ETCDCTL_API=3 etcdctl get ${RPC_SERVERS}/ --prefix echo "Registered HTTP endpoints" ETCDCTL_API=3 etcdctl get trillian-logserver-http/ --prefix ETCDCTL_API=3 etcdctl get trillian-logsigner-http/ --prefix fi } # log_stop_tests closes down a set of running processes for a log test. # Assumes the following variables are set: # - LOG_SIGNER_PIDS : bash array of signer pids # - RPC_SERVER_PIDS : bash array of RPC server pids # - ETCD_PID : etcd pid log_stop_test() { local pids echo "Stopping Log signers (pids ${LOG_SIGNER_PIDS[@]})" pids+=" ${LOG_SIGNER_PIDS[@]}" echo "Stopping Log RPC servers (pids ${RPC_SERVER_PIDS[@]})" pids+=" ${RPC_SERVER_PIDS[@]}" if [[ "${ETCD_PID}" != "" ]]; then echo "Stopping local etcd server (pid ${ETCD_PID})" pids+=" ${ETCD_PID}" fi kill_pid ${pids} } # setup_etcd_quotas creates the etcd quota configurations used by tests. # # Parameters: # - server : GRPC endpoint for the quota API (eg, logserver grpc port) # # Outputs: # DeleteConfig and CreateConfig responses. # # Returns: # 0 if success, non-zero otherwise. setup_etcd_quotas() { local server="$1" local name='quotas/global/write/config' # Remove the config before creating. It's OK if it doesn't exist. local delete_output=$(grpcurl -plaintext -d "name: ${name}" ${server} quotapb.Quota.DeleteConfig ) printf 'quotapb.Quota.DeleteConfig %s: %s\n' "${name}" "${delete_output}" local create_output=$(grpcurl -plaintext -d @ ${server} quotapb.Quota.CreateConfig < /dev/null } # on_exit will clean up anything in ${TO_KILL} and ${TO_DELETE}. on_exit() { local pids= for pid in "${TO_KILL[@]}"; do echo "Killing ${pid} on exit" pids+=" ${pid}" done kill_pid "${pids}" local file="" for file in "${TO_DELETE[@]}"; do echo "Deleting ${file} on exit" rm -rf ${file} done } trap on_exit EXIT trillian-1.6.1/integration/integration_test.sh000077500000000000000000000002771466362047600216250ustar00rootroot00000000000000#!/bin/bash set -e INTEGRATION_DIR="$( cd "$( dirname "$0" )" && pwd )" . "${INTEGRATION_DIR}"/functions.sh run_test "Log integration test" "${INTEGRATION_DIR}/log_integration_test.sh" "$@"trillian-1.6.1/integration/log.go000066400000000000000000000441501466362047600170120ustar00rootroot00000000000000// 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 integration import ( "bytes" "context" "errors" "fmt" "math/rand" "time" "github.com/google/trillian" "github.com/google/trillian/client/backoff" "github.com/google/trillian/types" "github.com/transparency-dev/merkle/proof" "github.com/transparency-dev/merkle/rfc6962" inmemory "github.com/transparency-dev/merkle/testonly" "k8s.io/klog/v2" ) // TestParameters bundles up all the settings for a test run type TestParameters struct { TreeID int64 CheckLogEmpty bool QueueLeaves bool AwaitSequencing bool StartLeaf int64 LeafCount int64 UniqueLeaves int64 QueueBatchSize int SequencerBatchSize int ReadBatchSize int64 SequencingWaitTotal time.Duration SequencingPollWait time.Duration RPCRequestDeadline time.Duration CustomLeafPrefix string } // DefaultTestParameters builds a TestParameters object for a normal // test of the given log. func DefaultTestParameters(treeID int64) TestParameters { return TestParameters{ TreeID: treeID, CheckLogEmpty: true, QueueLeaves: true, AwaitSequencing: true, StartLeaf: 0, LeafCount: 1000, UniqueLeaves: 1000, QueueBatchSize: 50, SequencerBatchSize: 100, ReadBatchSize: 50, SequencingWaitTotal: 10 * time.Second * 60, SequencingPollWait: time.Second * 5, RPCRequestDeadline: time.Second * 30, CustomLeafPrefix: "", } } type consistencyProofParams struct { size1 int64 size2 int64 } // inclusionProofTestIndices are the 0 based leaf indices to probe inclusion proofs at. var inclusionProofTestIndices = []int64{5, 27, 31, 80, 91} // consistencyProofTestParams are the intervals // to test proofs at var consistencyProofTestParams = []consistencyProofParams{{1, 2}, {2, 3}, {1, 3}, {2, 4}} // consistencyProofBadTestParams are the intervals to probe for consistency proofs, none of // these should succeed. Zero is not a valid tree size, nor is -1. 10000000 is outside the // range we'll reasonably queue (multiple of batch size). var consistencyProofBadTestParams = []consistencyProofParams{{0, 0}, {-1, 0}, {10000000, 10000000}} // RunLogIntegration runs a log integration test using the given client and test // parameters. func RunLogIntegration(client trillian.TrillianLogClient, params TestParameters) error { // Step 1 - Optionally check log starts empty then optionally queue leaves on server if params.CheckLogEmpty { klog.Infof("Checking log is empty before starting test") resp, err := getLatestSignedLogRoot(client, params) if err != nil { return fmt.Errorf("failed to get latest log root: %v %v", resp, err) } var root types.LogRootV1 if err := root.UnmarshalBinary(resp.SignedLogRoot.GetLogRoot()); err != nil { return fmt.Errorf("could not read current log root: %v", err) } if root.TreeSize > 0 { return fmt.Errorf("expected an empty log but got tree head response: %v", resp) } } preEntries := genEntries(params) if params.QueueLeaves { klog.Infof("Queueing %d leaves to log server ...", params.LeafCount) if err := queueLeaves(client, params, preEntries); err != nil { return fmt.Errorf("failed to queue leaves: %v", err) } } // Step 2 - Wait for queue to drain when server sequences, give up if it doesn't happen (optional) if params.AwaitSequencing { klog.Infof("Waiting for log to sequence ...") if err := waitForSequencing(params.TreeID, client, params); err != nil { return fmt.Errorf("leaves were not sequenced: %v", err) } } // Step 3 - Use get entries to read back what was written, check leaves are correct klog.Infof("Reading back leaves from log ...") entries, err := readEntries(params.TreeID, client, params) if err != nil { return fmt.Errorf("could not read back log entries: %v", err) } if err := verifyEntries(preEntries, entries); err != nil { return fmt.Errorf("written and read entries mismatch: %v", err) } // Step 4 - Cross validation between log and memory tree root hashes klog.Infof("Checking log STH with our constructed in-memory tree ...") tree := buildMerkleTree(entries, params) if err := checkLogRootHashMatches(tree, client, params); err != nil { return fmt.Errorf("log consistency check failed: %v", err) } // Now that the basic tree has passed validation we can start testing proofs // Step 5 - Test some inclusion proofs klog.Info("Testing inclusion proofs") // Ensure log doesn't serve a proof for a leaf index outside the tree size if err := checkInclusionProofLeafOutOfRange(params.TreeID, client, params); err != nil { return fmt.Errorf("log served out of range proof (index): %v", err) } // Ensure that log doesn't serve a proof for a valid index at a size outside the tree if err := checkInclusionProofTreeSizeOutOfRange(params.TreeID, client, params); err != nil { return fmt.Errorf("log served out of range proof (tree size): %v", err) } // Probe the log at several leaf indices each with a range of tree sizes for _, testIndex := range inclusionProofTestIndices { if err := checkInclusionProofsAtIndex(testIndex, params.TreeID, tree, client, params); err != nil { return fmt.Errorf("log inclusion index: %d proof checks failed: %v", testIndex, err) } } // TODO(al): test some inclusion proofs by Merkle hash too. // Step 6 - Test some consistency proofs klog.Info("Testing consistency proofs") // Make some consistency proof requests that we know should not succeed for _, consistParams := range consistencyProofBadTestParams { if err := checkConsistencyProof(consistParams, params.TreeID, tree, client, params, int64(params.QueueBatchSize)); err == nil { return fmt.Errorf("log consistency for %v: unexpected proof returned", consistParams) } } // Probe the log between some tree sizes we know are included and check the results against // the in memory tree. Request proofs at both STH and non STH sizes unless batch size is one, // when these would be equivalent requests. for _, consistParams := range consistencyProofTestParams { if err := checkConsistencyProof(consistParams, params.TreeID, tree, client, params, int64(params.QueueBatchSize)); err != nil { return fmt.Errorf("log consistency for %v: proof checks failed: %v", consistParams, err) } // Only do this if the batch size changes when halved if params.QueueBatchSize > 1 { if err := checkConsistencyProof(consistParams, params.TreeID, tree, client, params, int64(params.QueueBatchSize/2)); err != nil { return fmt.Errorf("log consistency for %v: proof checks failed (Non STH size): %v", consistParams, err) } } } return nil } func genEntries(params TestParameters) []*trillian.LogLeaf { if params.UniqueLeaves == 0 { params.UniqueLeaves = params.LeafCount } uniqueLeaves := make([]*trillian.LogLeaf, 0, params.UniqueLeaves) for i := int64(0); i < params.UniqueLeaves; i++ { index := params.StartLeaf + i leaf := &trillian.LogLeaf{ LeafValue: []byte(fmt.Sprintf("%sLeaf %d", params.CustomLeafPrefix, index)), ExtraData: []byte(fmt.Sprintf("%sExtra %d", params.CustomLeafPrefix, index)), } uniqueLeaves = append(uniqueLeaves, leaf) } // Shuffle the leaves to see if that breaks things, but record the rand seed // so we can reproduce failures. seed := time.Now().UnixNano() perm := rand.New(rand.NewSource(seed)).Perm(int(params.LeafCount)) klog.Infof("Generating %d leaves, %d unique, using permutation seed %d", params.LeafCount, params.UniqueLeaves, seed) leaves := make([]*trillian.LogLeaf, 0, params.LeafCount) for l := int64(0); l < params.LeafCount; l++ { leaves = append(leaves, uniqueLeaves[int64(perm[l])%params.UniqueLeaves]) } return leaves } func queueLeaves(client trillian.TrillianLogClient, params TestParameters, entries []*trillian.LogLeaf) error { klog.Infof("Queueing %d leaves...", len(entries)) for _, leaf := range entries { ctx, cancel := getRPCDeadlineContext(params) b := &backoff.Backoff{ Min: 100 * time.Millisecond, Max: 10 * time.Second, Factor: 2, Jitter: true, } err := b.Retry(ctx, func() error { _, err := client.QueueLeaf(ctx, &trillian.QueueLeafRequest{ LogId: params.TreeID, Leaf: leaf, }) return err }) cancel() if err != nil { return err } } return nil } func waitForSequencing(treeID int64, client trillian.TrillianLogClient, params TestParameters) error { endTime := time.Now().Add(params.SequencingWaitTotal) klog.Infof("Waiting for sequencing until: %v", endTime) for endTime.After(time.Now()) { req := trillian.GetLatestSignedLogRootRequest{LogId: treeID} ctx, cancel := getRPCDeadlineContext(params) resp, err := client.GetLatestSignedLogRoot(ctx, &req) cancel() if err != nil { return err } var root types.LogRootV1 if err := root.UnmarshalBinary(resp.SignedLogRoot.GetLogRoot()); err != nil { return err } klog.Infof("Leaf count: %d", root.TreeSize) if root.TreeSize >= uint64(params.LeafCount+params.StartLeaf) { return nil } klog.Infof("Leaves sequenced: %d. Still waiting ...", root.TreeSize) time.Sleep(params.SequencingPollWait) } return errors.New("wait time expired") } func readEntries(logID int64, client trillian.TrillianLogClient, params TestParameters) ([]*trillian.LogLeaf, error) { if start := params.StartLeaf; start != 0 { return nil, fmt.Errorf("non-zero StartLeaf is not supported: %d", start) } leaves := make([]*trillian.LogLeaf, 0, params.LeafCount) for index, end := params.StartLeaf, params.StartLeaf+params.LeafCount; index < end; { count := end - index if max := params.ReadBatchSize; count > max { count = max } klog.Infof("Reading %d leaves from %d ...", count, index) req := &trillian.GetLeavesByRangeRequest{LogId: logID, StartIndex: index, Count: count} ctx, cancel := getRPCDeadlineContext(params) response, err := client.GetLeavesByRange(ctx, req) cancel() if err != nil { return nil, err } // Check we got the right number of leaves. if got, want := int64(len(response.Leaves)), count; got != want { return nil, fmt.Errorf("expected %d leaves, got %d", want, got) } leaves = append(leaves, response.Leaves...) index += int64(len(response.Leaves)) } return leaves, nil } func verifyEntries(written, read []*trillian.LogLeaf) error { counts := make(map[string]int, len(written)) for _, e := range read { counts[string(e.LeafValue)]++ // Check that the MerkleLeafHash field is computed correctly. hash := rfc6962.DefaultHasher.HashLeaf(e.LeafValue) if got, want := e.MerkleLeafHash, hash; !bytes.Equal(got, want) { return fmt.Errorf("leaf %d hash mismatch: got %x want %x", e.LeafIndex, got, want) } // Ensure that the ExtraData in the leaf made it through the roundtrip. // This was set up when we queued the leaves. if got, want := e.ExtraData, bytes.Replace(e.LeafValue, []byte("Leaf"), []byte("Extra"), 1); !bytes.Equal(got, want) { return fmt.Errorf("leaf %d ExtraData: got %x, want %xv", e.LeafIndex, got, want) } } for _, e := range written { counts[string(e.LeafValue)]-- if counts[string(e.LeafValue)] == 0 { delete(counts, string(e.LeafValue)) } } if len(counts) != 0 { return fmt.Errorf("entry leaf values don't match: diff (-expected +got)\n%v", counts) } return nil } func checkLogRootHashMatches(tree *inmemory.Tree, client trillian.TrillianLogClient, params TestParameters) error { // Check the STH against the hash we got from our tree resp, err := getLatestSignedLogRoot(client, params) if err != nil { return err } var root types.LogRootV1 if err := root.UnmarshalBinary(resp.SignedLogRoot.GetLogRoot()); err != nil { return err } // Hash must not be empty and must match the one we built ourselves if got, want := root.RootHash, tree.Hash(); !bytes.Equal(got, want) { return fmt.Errorf("root hash mismatch expected got: %x want: %x", got, want) } return nil } // checkInclusionProofLeafOutOfRange requests an inclusion proof beyond the current tree size. This // should fail func checkInclusionProofLeafOutOfRange(logID int64, client trillian.TrillianLogClient, params TestParameters) error { // Test is a leaf index bigger than the current tree size ctx, cancel := getRPCDeadlineContext(params) proof, err := client.GetInclusionProof(ctx, &trillian.GetInclusionProofRequest{ LogId: logID, LeafIndex: params.LeafCount + 1, TreeSize: int64(params.LeafCount), }) cancel() if err == nil { return fmt.Errorf("log returned proof for leaf index outside tree: %d v %d: %v", params.LeafCount+1, params.LeafCount, proof) } return nil } // checkInclusionProofTreeSizeOutOfRange requests an inclusion proof for a leaf within the tree size at // a tree size larger than the current tree size. This should succeed but with an STH for the current // tree and an empty proof, because it is a result of skew. func checkInclusionProofTreeSizeOutOfRange(logID int64, client trillian.TrillianLogClient, params TestParameters) error { // Test is an in range leaf index for a tree size that doesn't exist ctx, cancel := getRPCDeadlineContext(params) req := &trillian.GetInclusionProofRequest{ LogId: logID, LeafIndex: int64(params.SequencerBatchSize), TreeSize: params.LeafCount + int64(params.SequencerBatchSize), } proof, err := client.GetInclusionProof(ctx, req) cancel() if err != nil { return fmt.Errorf("log returned error for tree size outside tree: %d v %d: %v", params.LeafCount, req.TreeSize, err) } var root types.LogRootV1 if err := root.UnmarshalBinary(proof.SignedLogRoot.LogRoot); err != nil { return fmt.Errorf("could not read current log root: %v", err) } if proof.Proof != nil { return fmt.Errorf("log returned proof for tree size outside tree: %d v %d: %v", params.LeafCount, req.TreeSize, proof) } if int64(root.TreeSize) >= req.TreeSize { return fmt.Errorf("log returned bad root for tree size outside tree: %d v %d: %v", params.LeafCount, req.TreeSize, proof) } return nil } // checkInclusionProofsAtIndex obtains and checks proofs at tree sizes from zero up to 2 x the sequencing // batch size (or number of leaves queued if less). The log should only serve proofs for indices in a tree // at least as big as the index where STHs where the index is a multiple of the sequencer batch size. All // proofs returned should match ones computed by the alternate Merkle Tree implementation, which differs // from what the log uses. func checkInclusionProofsAtIndex(index int64, logID int64, tree *inmemory.Tree, client trillian.TrillianLogClient, params TestParameters) error { for treeSize := int64(0); treeSize < min(params.LeafCount, int64(2*params.SequencerBatchSize)); treeSize++ { ctx, cancel := getRPCDeadlineContext(params) resp, err := client.GetInclusionProof(ctx, &trillian.GetInclusionProofRequest{ LogId: logID, LeafIndex: index, TreeSize: treeSize, }) cancel() // If the index is larger than the tree size we cannot have a valid proof shouldHaveProof := index < treeSize if got, want := err == nil, shouldHaveProof; got != want { return fmt.Errorf("GetInclusionProof(index: %d, treeSize %d): %v, want nil: %v", index, treeSize, err, want) } if !shouldHaveProof { continue } // Verify inclusion proof. root := tree.HashAt(uint64(treeSize)) merkleLeafHash := tree.LeafHash(uint64(index)) if err := proof.VerifyInclusion(rfc6962.DefaultHasher, uint64(index), uint64(treeSize), merkleLeafHash, resp.Proof.Hashes, root); err != nil { return err } } return nil } func checkConsistencyProof(consistParams consistencyProofParams, treeID int64, tree *inmemory.Tree, client trillian.TrillianLogClient, params TestParameters, batchSize int64) error { // We expect the proof request to succeed ctx, cancel := getRPCDeadlineContext(params) req := &trillian.GetConsistencyProofRequest{ LogId: treeID, FirstTreeSize: consistParams.size1 * int64(batchSize), SecondTreeSize: consistParams.size2 * int64(batchSize), } resp, err := client.GetConsistencyProof(ctx, req) cancel() if err != nil { return fmt.Errorf("GetConsistencyProof(%v) = %v %v", consistParams, err, resp) } if resp.SignedLogRoot == nil || resp.SignedLogRoot.LogRoot == nil { return fmt.Errorf("received invalid response: %v", resp) } var root types.LogRootV1 if err := root.UnmarshalBinary(resp.SignedLogRoot.LogRoot); err != nil { return fmt.Errorf("could not read current log root: %v", err) } if req.SecondTreeSize > int64(root.TreeSize) { return fmt.Errorf("requested tree size %d > available tree size %d", req.SecondTreeSize, root.TreeSize) } root1 := tree.HashAt(uint64(req.FirstTreeSize)) root2 := tree.HashAt(uint64(req.SecondTreeSize)) return proof.VerifyConsistency(rfc6962.DefaultHasher, uint64(req.FirstTreeSize), uint64(req.SecondTreeSize), resp.Proof.Hashes, root1, root2) } // buildMerkleTree returns an in-memory Merkle tree built on the given leaves. func buildMerkleTree(leaves []*trillian.LogLeaf, params TestParameters) *inmemory.Tree { merkleTree := inmemory.New(rfc6962.DefaultHasher) for _, leaf := range leaves { merkleTree.AppendData(leaf.LeafValue) } return merkleTree } func getLatestSignedLogRoot(client trillian.TrillianLogClient, params TestParameters) (*trillian.GetLatestSignedLogRootResponse, error) { req := trillian.GetLatestSignedLogRootRequest{LogId: params.TreeID} ctx, cancel := getRPCDeadlineContext(params) resp, err := client.GetLatestSignedLogRoot(ctx, &req) cancel() return resp, err } // getRPCDeadlineTime calculates the future time an RPC should expire based on our config func getRPCDeadlineContext(params TestParameters) (context.Context, context.CancelFunc) { return context.WithDeadline(context.Background(), time.Now().Add(params.RPCRequestDeadline)) } func min(a, b int64) int64 { if a < b { return a } return b } trillian-1.6.1/integration/log_integration_test.go000066400000000000000000000127211466362047600224530ustar00rootroot00000000000000// 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 integration import ( "context" "flag" "testing" "time" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "github.com/google/trillian" "github.com/google/trillian/client" "github.com/google/trillian/extension" "github.com/google/trillian/quota" "github.com/google/trillian/storage/memory" "github.com/google/trillian/storage/testdb" "github.com/google/trillian/testonly/integration" stestonly "github.com/google/trillian/storage/testonly" ) var ( treeIDFlag = flag.Int64("treeid", -1, "The tree id to use") serverFlag = flag.String("log_rpc_server", "localhost:8092", "Server address:port") queueLeavesFlag = flag.Bool("queue_leaves", true, "If true queues leaves, false just reads from the log") awaitSequencingFlag = flag.Bool("await_sequencing", true, "If true then waits until log size is at least num_leaves") checkLogEmptyFlag = flag.Bool("check_log_empty", true, "If true ensures log is empty before queuing anything") startLeafFlag = flag.Int64("start_leaf", 0, "The first leaf index to use") numLeavesFlag = flag.Int64("num_leaves", 1000, "The number of leaves to submit and read back") queueBatchSizeFlag = flag.Int("queue_batch_size", 50, "Batch size when queueing leaves") sequencerBatchSizeFlag = flag.Int("sequencing_batch_size", 100, "Batch size for server sequencer") readBatchSizeFlag = flag.Int64("read_batch_size", 50, "Batch size when getting leaves by index") waitForSequencingFlag = flag.Duration("wait_for_sequencing", time.Second*60, "How long to wait for leaves to be sequenced") waitBetweenQueueChecksFlag = flag.Duration("queue_poll_wait", time.Second*5, "How frequently to check the queue while waiting") rpcRequestDeadlineFlag = flag.Duration("rpc_deadline", time.Second*10, "Deadline to use for all RPC requests") customLeafPrefixFlag = flag.String("custom_leaf_prefix", "", "Prefix string added to all queued leaves") ) func TestLiveLogIntegration(t *testing.T) { flag.Parse() if *treeIDFlag == -1 { t.Skip("Log integration test skipped as no tree ID provided") } // Initialize and connect to log server params := TestParameters{ TreeID: *treeIDFlag, CheckLogEmpty: *checkLogEmptyFlag, QueueLeaves: *queueLeavesFlag, AwaitSequencing: *awaitSequencingFlag, StartLeaf: *startLeafFlag, LeafCount: *numLeavesFlag, QueueBatchSize: *queueBatchSizeFlag, SequencerBatchSize: *sequencerBatchSizeFlag, ReadBatchSize: *readBatchSizeFlag, SequencingWaitTotal: *waitForSequencingFlag, SequencingPollWait: *waitBetweenQueueChecksFlag, RPCRequestDeadline: *rpcRequestDeadlineFlag, CustomLeafPrefix: *customLeafPrefixFlag, } if params.StartLeaf < 0 || params.LeafCount <= 0 { t.Fatalf("Start leaf index must be >= 0 (%d) and number of leaves must be > 0 (%d)", params.StartLeaf, params.LeafCount) } ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() // TODO: Other options apart from insecure connections conn, err := grpc.DialContext(ctx, *serverFlag, grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { t.Fatalf("Failed to connect to log server: %v", err) } defer func() { if err := conn.Close(); err != nil { t.Errorf("Close(): %v", err) } }() lc := trillian.NewTrillianLogClient(conn) if err := RunLogIntegration(lc, params); err != nil { t.Fatalf("Test failed: %v", err) } } func TestInProcessLogIntegration(t *testing.T) { testdb.SkipIfNoMySQL(t) ctx := context.Background() const numSequencers = 2 env, err := integration.NewLogEnvWithGRPCOptions(ctx, numSequencers, nil, nil) if err != nil { t.Fatal(err) } defer env.Close() tree, err := client.CreateAndInitTree(ctx, &trillian.CreateTreeRequest{ Tree: stestonly.LogTree, }, env.Admin, env.Log) if err != nil { t.Fatalf("Failed to create log: %v", err) } params := DefaultTestParameters(tree.TreeId) if err := RunLogIntegration(env.Log, params); err != nil { t.Fatalf("Test failed: %v", err) } } func TestInProcessLogIntegrationDuplicateLeaves(t *testing.T) { ctx := context.Background() const numSequencers = 2 ts := memory.NewTreeStorage() ms := memory.NewLogStorage(ts, nil) reggie := extension.Registry{ AdminStorage: memory.NewAdminStorage(ts), LogStorage: ms, QuotaManager: quota.Noop(), } env, err := integration.NewLogEnvWithRegistry(ctx, numSequencers, reggie) if err != nil { t.Fatal(err) } defer env.Close() tree, err := client.CreateAndInitTree(ctx, &trillian.CreateTreeRequest{ Tree: stestonly.LogTree, }, env.Admin, env.Log) if err != nil { t.Fatalf("Failed to create log: %v", err) } params := DefaultTestParameters(tree.TreeId) params.UniqueLeaves = 10 if err := RunLogIntegration(env.Log, params); err != nil { t.Fatalf("Test failed: %v", err) } } trillian-1.6.1/integration/log_integration_test.sh000077500000000000000000000023541466362047600224640ustar00rootroot00000000000000#!/bin/bash set -e INTEGRATION_DIR="$( cd "$( dirname "$0" )" && pwd )" . "${INTEGRATION_DIR}"/functions.sh TRILLIAN_SERVER="$1" TEST_STARTED_TRILLIAN_SERVER=false if [ -z "${TRILLIAN_SERVER}" ]; then echo "Launching core Trillian log components" log_prep_test 1 1 # Cleanup for the Trillian components TO_DELETE="${TO_DELETE} ${ETCD_DB_DIR}" TO_KILL+=(${LOG_SIGNER_PIDS[@]}) TO_KILL+=(${RPC_SERVER_PIDS[@]}) TO_KILL+=(${ETCD_PID}) TRILLIAN_SERVER="${RPC_SERVER_1}" TEST_STARTED_TRILLIAN_SERVER=true fi echo "Provision log" TEST_TREE_ID=$(go run github.com/google/trillian/cmd/createtree \ --admin_server="${TRILLIAN_SERVER}" \ ${KEY_ARGS}) echo "Created tree ${TEST_TREE_ID}" echo "Running test" pushd "${INTEGRATION_DIR}" set +e go test \ -run ".*LiveLog.*" \ -timeout=${GO_TEST_TIMEOUT:-5m} \ ./ \ --log_rpc_server="${TRILLIAN_SERVER}" \ --treeid ${TEST_TREE_ID} RESULT=$? set -e popd if ${TEST_STARTED_TRILLIAN_SERVER}; then log_stop_test TO_KILL=() if [ $RESULT != 0 ]; then sleep 1 echo "Server log:" echo "--------------------" cat "${TMPDIR}"/trillian_log_server.INFO echo "Signer log:" echo "--------------------" cat "${TMPDIR}"/trillian_log_signer.INFO fi fi exit $RESULT trillian-1.6.1/integration/log_md_test.sh000077500000000000000000000013531466362047600205370ustar00rootroot00000000000000#!/bin/bash set -e INTEGRATION_DIR="$( cd "$( dirname "$0" )" && pwd )" . "${INTEGRATION_DIR}"/functions.sh echo "Launching core Trillian log components" log_prep_test 1 1 TO_DELETE="${TO_DELETE} ${ETCD_DB_DIR}" TO_KILL+=(${LOG_SIGNER_PIDS[@]}) TO_KILL+=(${RPC_SERVER_PIDS[@]}) TO_KILL+=(${ETCD_PID}) TRILLIAN_SERVER="${RPC_SERVER_1}" metrics_port=$(pick_unused_port ${port}) echo "Running test against ephemeral tree, metrics on http://localhost:${metrics_port}/metrics" go run ${GOFLAGS} github.com/google/trillian/testonly/mdm/mdmtest \ --rpc_server="${TRILLIAN_SERVER}" \ --metrics_endpoint="localhost:${metrics_port}" \ --checkers=10 \ --new_leaf_chance=90 \ --logtostderr RESULT=$? set -e log_stop_test TO_KILL=() exit $RESULT trillian-1.6.1/integration/quota/000077500000000000000000000000001466362047600170275ustar00rootroot00000000000000trillian-1.6.1/integration/quota/doc.go000066400000000000000000000013571466362047600201310ustar00rootroot00000000000000// 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 quota contains quota-related integration tests. package quota // Keeping this file avoids "no buildable Go source files" errors. trillian-1.6.1/integration/quota/quota_test.go000066400000000000000000000156301466362047600215530ustar00rootroot00000000000000// 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 quota import ( "context" "crypto" "errors" "fmt" "hash" "net" "testing" "time" "github.com/google/trillian" "github.com/google/trillian/extension" "github.com/google/trillian/quota/etcd/etcdqm" "github.com/google/trillian/quota/etcd/quotaapi" "github.com/google/trillian/quota/etcd/quotapb" "github.com/google/trillian/quota/mysqlqm" "github.com/google/trillian/server" "github.com/google/trillian/server/admin" "github.com/google/trillian/server/interceptor" "github.com/google/trillian/storage/mysql" "github.com/google/trillian/storage/testdb" "github.com/google/trillian/storage/testonly" "github.com/google/trillian/testonly/integration" "github.com/google/trillian/testonly/integration/etcd" "github.com/google/trillian/util/clock" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/status" "k8s.io/klog/v2" grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware" ) func TestEtcdRateLimiting(t *testing.T) { testdb.SkipIfNoMySQL(t) ctx := context.Background() // TODO(jaosorior): Make this configurable for Cockroach or MySQL registry, done, err := integration.NewRegistryForTests(ctx, testdb.DriverMySQL) if err != nil { t.Fatalf("NewRegistryForTests() returned err = %v", err) } defer done(ctx) _, etcdClient, cleanup, err := etcd.StartEtcd() if err != nil { t.Fatalf("StartEtcd() returned err = %v", err) } defer cleanup() registry.QuotaManager = etcdqm.New(etcdClient) s, err := newTestServer(registry) if err != nil { t.Fatalf("newTestServer() returned err = %v", err) } defer s.close() quotapb.RegisterQuotaServer(s.server, quotaapi.NewServer(etcdClient)) quotaClient := quotapb.NewQuotaClient(s.conn) go s.serve() const maxTokens = 100 if _, err := quotaClient.CreateConfig(ctx, "apb.CreateConfigRequest{ Name: "quotas/global/write/config", Config: "apb.Config{ State: quotapb.Config_ENABLED, MaxTokens: maxTokens, ReplenishmentStrategy: "apb.Config_TimeBased{ TimeBased: "apb.TimeBasedStrategy{ TokensToReplenish: maxTokens, ReplenishIntervalSeconds: 1000, }, }, }, }); err != nil { t.Fatalf("CreateConfig() returned err = %v", err) } if err := runRateLimitingTest(ctx, s, maxTokens); err != nil { t.Error(err) } } func TestMySQLRateLimiting(t *testing.T) { testdb.SkipIfNoMySQL(t) ctx := context.Background() db, done, err := testdb.NewTrillianDB(ctx, testdb.DriverMySQL) if err != nil { t.Fatalf("GetTestDB() returned err = %v", err) } defer done(ctx) const maxUnsequenced = 20 qm := &mysqlqm.QuotaManager{DB: db, MaxUnsequencedRows: maxUnsequenced} registry := extension.Registry{ AdminStorage: mysql.NewAdminStorage(db), LogStorage: mysql.NewLogStorage(db, nil), QuotaManager: qm, } s, err := newTestServer(registry) if err != nil { t.Fatalf("newTestServer() returned err = %v", err) } defer s.close() go s.serve() if err := runRateLimitingTest(ctx, s, maxUnsequenced); err != nil { t.Error(err) } } func runRateLimitingTest(ctx context.Context, s *testServer, numTokens int) error { tree, err := s.admin.CreateTree(ctx, &trillian.CreateTreeRequest{Tree: testonly.LogTree}) if err != nil { return fmt.Errorf("CreateTree() returned err = %v", err) } // InitLog costs 1 token numTokens-- _, err = s.log.InitLog(ctx, &trillian.InitLogRequest{LogId: tree.TreeId}) if err != nil { return fmt.Errorf("InitLog() returned err = %v", err) } hasher := crypto.SHA256.New() lw := &leafWriter{client: s.log, hash: hasher, treeID: tree.TreeId} // Requests where leaves < numTokens should work for i := 0; i < numTokens; i++ { if err := lw.queueLeaf(ctx); err != nil { return fmt.Errorf("queueLeaf(@%d) returned err = %v", i, err) } } // Some point after now requests should start to fail stop := false timeout := time.After(1 * time.Second) for !stop { select { case <-timeout: return errors.New("timed out before rate limiting kicked in") default: err := lw.queueLeaf(ctx) if err == nil { continue // Rate liming hasn't kicked in yet } if s, ok := status.FromError(err); !ok || s.Code() != codes.ResourceExhausted { return fmt.Errorf("queueLeaf() returned err = %v", err) } stop = true } } return nil } type leafWriter struct { client trillian.TrillianLogClient hash hash.Hash treeID int64 leafID int } func (w *leafWriter) queueLeaf(ctx context.Context) error { value := []byte(fmt.Sprintf("leaf-%v", w.leafID)) w.leafID++ w.hash.Reset() if _, err := w.hash.Write(value); err != nil { return err } h := w.hash.Sum(nil) _, err := w.client.QueueLeaf(ctx, &trillian.QueueLeafRequest{ LogId: w.treeID, Leaf: &trillian.LogLeaf{ MerkleLeafHash: h, LeafValue: value, }, }) return err } type testServer struct { lis net.Listener server *grpc.Server conn *grpc.ClientConn admin trillian.TrillianAdminClient log trillian.TrillianLogClient } func (s *testServer) close() { if s.conn != nil { _ = s.conn.Close() } if s.server != nil { s.server.GracefulStop() } if s.lis != nil { _ = s.lis.Close() } } func (s *testServer) serve() { if err := s.server.Serve(s.lis); err != nil { klog.Errorf("Serve(): %v", err) } } // newTestServer returns a new testServer configured for integration tests. // Callers must defer-call s.close() to make sure resources aren't being leaked and must start the // server via s.serve(). func newTestServer(registry extension.Registry) (*testServer, error) { s := &testServer{} ti := interceptor.New(registry.AdminStorage, registry.QuotaManager, false /* quotaDryRun */, registry.MetricFactory) s.server = grpc.NewServer( grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer( interceptor.ErrorWrapper, ti.UnaryInterceptor, )), ) trillian.RegisterTrillianAdminServer(s.server, admin.New(registry, nil /* allowedTreeTypes */)) trillian.RegisterTrillianLogServer(s.server, server.NewTrillianLogRPCServer(registry, clock.System)) var err error s.lis, err = net.Listen("tcp", "127.0.0.1:0") if err != nil { s.close() return nil, err } s.conn, err = grpc.Dial(s.lis.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { s.close() return nil, err } s.admin = trillian.NewTrillianAdminClient(s.conn) s.log = trillian.NewTrillianLogClient(s.conn) return s, nil } trillian-1.6.1/integration/run_integration_test.sh000077500000000000000000000017721466362047600225120ustar00rootroot00000000000000#!/bin/bash # # run_integration_test.sh is a wrapper around the log_prep and run_test # functions. It's intended to be used by the Travis CT/Trillian integration test # only, and will go away when we migrate off of Travis. # # TODO(al): Make the script go away. set -e INTEGRATION_DIR="$( cd "$( dirname "$0" )" && pwd )" . "${INTEGRATION_DIR}"/functions.sh echo "Launching core Trillian log components" log_prep_test 1 1 # Cleanup for the Trillian components TO_DELETE="${TO_DELETE} ${ETCD_DB_DIR}" TO_KILL+=(${LOG_SIGNER_PIDS[@]}) TO_KILL+=(${RPC_SERVER_PIDS[@]}) TO_KILL+=(${ETCD_PID}) export TRILLIAN_LOG_SERVERS="${RPC_SERVERS}" export TRILLIAN_LOG_SERVER_1="${RPC_SERVER_1}" echo "Running test" run_test "$1" "$2" RESULT=$? log_stop_test TO_KILL=() if [ $RESULT != 0 ]; then sleep 1 echo "Server log:" echo "--------------------" cat "${TMPDIR}"/trillian_log_server.INFO echo "Signer log:" echo "--------------------" cat "${TMPDIR}"/trillian_log_signer.INFO fi exit $RESULT trillian-1.6.1/integration/storagetest/000077500000000000000000000000001466362047600202425ustar00rootroot00000000000000trillian-1.6.1/integration/storagetest/doc.go000066400000000000000000000013131466362047600213340ustar00rootroot00000000000000// Copyright 2022 Trillian Authors. 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 storagetest contains tests and helpers for storage implementations. package storagetest trillian-1.6.1/integration/storagetest/loghelpers.go000066400000000000000000000050611466362047600227370ustar00rootroot00000000000000// Copyright 2020 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 storagetest import ( "context" "crypto/sha256" "fmt" "testing" "time" "github.com/google/trillian" "github.com/google/trillian/storage" "github.com/google/trillian/types" ) // runLogTX is a helps avoid copying out "if err != nil { blah }" all over the place func runLogTX(s storage.LogStorage, tree *trillian.Tree, t *testing.T, f storage.LogTXFunc) { t.Helper() if err := s.ReadWriteTransaction(context.Background(), tree, f); err != nil { t.Fatalf("Failed to run log tx: %v", err) } } // createTestLeaves creates some test leaves with predictable data func createTestLeaves(n, startSeq int64) []*trillian.LogLeaf { var leaves []*trillian.LogLeaf for l := int64(0); l < n; l++ { lv := fmt.Sprintf("Leaf %d", l+startSeq) leafHash := sha256.Sum256([]byte(lv)) leaf := &trillian.LogLeaf{ LeafIdentityHash: leafHash[:], MerkleLeafHash: leafHash[:], LeafValue: []byte(lv), ExtraData: []byte(fmt.Sprintf("Extra %d", l)), LeafIndex: int64(startSeq + l), } leaves = append(leaves, leaf) } return leaves } func mustSignAndStoreLogRoot(ctx context.Context, t *testing.T, l storage.LogStorage, tree *trillian.Tree, r *types.LogRootV1) { t.Helper() logRoot, err := r.MarshalBinary() if err != nil { t.Fatalf("error marshaling new LogRootV1: %v", err) } root := &trillian.SignedLogRoot{LogRoot: logRoot} if err := l.ReadWriteTransaction(ctx, tree, func(ctx context.Context, tx storage.LogTreeTX) error { return tx.StoreSignedLogRoot(ctx, root) }); err != nil { t.Fatalf("ReadWriteTransaction() = %v", err) } } func dequeueLeavesInTx(ctx context.Context, ls storage.LogStorage, tree *trillian.Tree, t time.Time, limit int) ([]*trillian.LogLeaf, error) { var ret []*trillian.LogLeaf if err := ls.ReadWriteTransaction(ctx, tree, func(ctx context.Context, tx storage.LogTreeTX) error { var err error ret, err = tx.DequeueLeaves(ctx, limit, t) return err }); err != nil { return nil, err } return ret, nil } trillian-1.6.1/integration/storagetest/logtests.go000066400000000000000000000533341466362047600224450ustar00rootroot00000000000000// Copyright 2020 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 storagetest import ( "bytes" "context" "crypto/sha256" "fmt" "reflect" "strings" "testing" "time" "github.com/google/go-cmp/cmp" "github.com/google/trillian" "github.com/google/trillian/storage" "github.com/google/trillian/types" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/testing/protocmp" "google.golang.org/protobuf/types/known/timestamppb" storageto "github.com/google/trillian/storage/testonly" ) // LogStorageFactory creates LogStorage and AdminStorage for a test to use. type LogStorageFactory = func(ctx context.Context, t *testing.T) (storage.LogStorage, storage.AdminStorage) // LogStorageTest executes a test using the given storage implementations. type LogStorageTest = func(ctx context.Context, t *testing.T, s storage.LogStorage, as storage.AdminStorage) // RunLogStorageTests runs all the log storage tests against the provided log storage implementation. func RunLogStorageTests(t *testing.T, storageFactory LogStorageFactory) { ctx := context.Background() for name, f := range logTestFunctions(t, &logTests{}) { s, as := storageFactory(ctx, t) t.Run(name, func(t *testing.T) { f(ctx, t, s, as) }) } } func logTestFunctions(t *testing.T, x interface{}) map[string]LogStorageTest { tests := make(map[string]LogStorageTest) xv := reflect.ValueOf(x) for _, name := range testFunctions(x) { m := xv.MethodByName(name) if !m.IsValid() { t.Fatalf("storagetest: function %v is not valid", name) } i := m.Interface() f, ok := i.(LogStorageTest) if !ok { // Method exists but has the wrong type signature. t.Fatalf("storagetest: function %v has unexpected signature %T, %v", name, m.Interface(), m) } nickname := strings.TrimPrefix(name, "Test") tests[nickname] = f } return tests } // logTests is a suite of tests to run against the storage.LogTest interface. type logTests struct{} func (*logTests) TestCheckDatabaseAccessible(ctx context.Context, t *testing.T, s storage.LogStorage, as storage.AdminStorage) { if err := s.CheckDatabaseAccessible(ctx); err != nil { t.Errorf("CheckDatabaseAccessible() = %v, want = nil", err) } } func (*logTests) TestSnapshot(ctx context.Context, t *testing.T, s storage.LogStorage, as storage.AdminStorage) { frozenLog := mustCreateTree(ctx, t, as, storageto.LogTree) mustSignAndStoreLogRoot(ctx, t, s, frozenLog, &types.LogRootV1{}) if _, err := storage.UpdateTree(ctx, as, frozenLog.TreeId, func(tree *trillian.Tree) { tree.TreeState = trillian.TreeState_FROZEN }); err != nil { t.Fatalf("Error updating frozen tree: %v", err) } activeLog := mustCreateTree(ctx, t, as, storageto.LogTree) mustSignAndStoreLogRoot(ctx, t, s, activeLog, &types.LogRootV1{}) tests := []struct { desc string tree *trillian.Tree wantErr bool }{ { desc: "unknownSnapshot", tree: logTree(-1), wantErr: true, }, { desc: "activeLogSnapshot", tree: activeLog, }, { desc: "frozenSnapshot", tree: frozenLog, }, } for _, test := range tests { t.Run(test.desc, func(t *testing.T) { tx, err := s.SnapshotForTree(ctx, test.tree) if err == storage.ErrTreeNeedsInit { defer func() { if err := tx.Close(); err != nil { t.Errorf("Close(): %v", err) } }() } if hasErr := err != nil; hasErr != test.wantErr { t.Fatalf("err: %v, wantErr = %v", err, test.wantErr) } else if hasErr { return } defer func() { _ = tx.Close() }() _, err = tx.LatestSignedLogRoot(ctx) if err != nil { t.Errorf("LatestSignedLogRoot() returned err: %v", err) } if err := tx.Commit(ctx); err != nil { t.Errorf("Commit() returned err: %v", err) } }) } } func (*logTests) TestReadWriteTransaction(ctx context.Context, t *testing.T, s storage.LogStorage, as storage.AdminStorage) { activeLog := mustCreateTree(ctx, t, as, storageto.LogTree) tests := []struct { desc string doBefore func() wantNeedsInit bool wantErr bool wantLogRoot []byte }{ { desc: "uninitializedBegin", doBefore: func() {}, wantNeedsInit: true, }, { desc: "activeLogBegin", doBefore: func() { mustSignAndStoreLogRoot(ctx, t, s, activeLog, &types.LogRootV1{RootHash: []byte{0}}) }, wantLogRoot: func() []byte { b, err := (&types.LogRootV1{RootHash: []byte{0}}).MarshalBinary() if err != nil { panic(err) } return b }(), }, } for _, test := range tests { t.Run(test.desc, func(t *testing.T) { test.doBefore() err := s.ReadWriteTransaction(ctx, activeLog, func(ctx context.Context, tx storage.LogTreeTX) error { root, err := tx.LatestSignedLogRoot(ctx) if err != nil && !(err == storage.ErrTreeNeedsInit && test.wantNeedsInit) { t.Fatalf("%v: LatestSignedLogRoot() returned err = %v", test.desc, err) } if got, want := root.GetLogRoot(), test.wantLogRoot; !bytes.Equal(got, want) { var logRoot types.LogRootV1 if err := logRoot.UnmarshalBinary(got); err != nil { t.Error(err) } t.Errorf("%v: LogRoot: \n%x, want \n%x \nUnpacked: %#v", test.desc, got, want, logRoot) } return nil }) if hasErr := err != nil; hasErr != test.wantErr { t.Fatalf("%v: err = %q, wantErr = %v", test.desc, err, test.wantErr) } else if hasErr { return } }) } } func logTree(logID int64) *trillian.Tree { return &trillian.Tree{ TreeId: logID, TreeType: trillian.TreeType_LOG, } } // AddSequencedLeaves tests. --------------------------------------------------- type addSequencedLeavesTest struct { t *testing.T s storage.LogStorage tree *trillian.Tree } func initAddSequencedLeavesTest(ctx context.Context, t *testing.T, s storage.LogStorage, as storage.AdminStorage) addSequencedLeavesTest { tree := mustCreateTree(ctx, t, as, storageto.PreorderedLogTree) mustSignAndStoreLogRoot(ctx, t, s, tree, &types.LogRootV1{}) return addSequencedLeavesTest{t, s, tree} } func (t *addSequencedLeavesTest) addSequencedLeaves(leaves []*trillian.LogLeaf) { ctx := context.TODO() // Time we will queue all leaves at. fakeQueueTime := time.Date(2016, 11, 10, 15, 16, 27, 0, time.UTC) queued, err := t.s.AddSequencedLeaves(ctx, t.tree, leaves, fakeQueueTime) if err != nil { t.t.Fatalf("Failed to add sequenced leaves: %v", err) } if got, want := len(queued), len(leaves); got != want { t.t.Errorf("AddSequencedLeaves(): %v queued leaves, want %v", got, want) } // TODO(pavelkalinnikov): Verify returned status for each leaf. } func (t *addSequencedLeavesTest) verifySequencedLeaves(start, count int64, exp []*trillian.LogLeaf) { var stored []*trillian.LogLeaf runLogTX(t.s, t.tree, t.t, func(ctx context.Context, tx storage.LogTreeTX) error { var err error t.t.Logf("GetLeavesByRange(%v, %v)", start, count) stored, err = tx.GetLeavesByRange(ctx, start, count) if err != nil { t.t.Fatalf("Failed to read sequenced leaves: %v", err) } return nil }) if got, want := len(stored), len(exp); got != want { t.t.Fatalf("Unexpected number of leaves: got %d, want %d", got, want) } for i, leaf := range stored { if got, want := leaf.LeafIndex, exp[i].LeafIndex; got != want { t.t.Fatalf("Leaf #%d: LeafIndex=%v, want %v", i, got, want) } if got, want := leaf.LeafIdentityHash, exp[i].LeafIdentityHash; !bytes.Equal(got, want) { t.t.Fatalf("Leaf #%d: LeafIdentityHash=%v, want %v", i, got, want) } } } func (*logTests) TestAddSequencedLeavesUnordered(ctx context.Context, t *testing.T, s storage.LogStorage, as storage.AdminStorage) { const chunk = 5 const count = chunk * 5 const extraCount = 16 leaves := createTestLeaves(count, 0) aslt := initAddSequencedLeavesTest(ctx, t, s, as) for _, idx := range []int{1, 0, 4, 2} { aslt.addSequencedLeaves(leaves[chunk*idx : chunk*(idx+1)]) } aslt.verifySequencedLeaves(0, count+extraCount, leaves[:chunk*3]) aslt.verifySequencedLeaves(chunk*4, chunk+extraCount, leaves[chunk*4:count]) aslt.addSequencedLeaves(leaves[chunk*3 : chunk*4]) aslt.verifySequencedLeaves(0, count+extraCount, leaves) } func (*logTests) TestAddSequencedLeavesWithDuplicates(ctx context.Context, t *testing.T, s storage.LogStorage, as storage.AdminStorage) { leaves := createTestLeaves(6, 0) aslt := initAddSequencedLeavesTest(ctx, t, s, as) aslt.addSequencedLeaves(leaves[:3]) aslt.verifySequencedLeaves(0, 3, leaves[:3]) aslt.addSequencedLeaves(leaves[2:]) // Full dup. aslt.verifySequencedLeaves(0, 6, leaves) dupLeaves := createTestLeaves(4, 6) dupLeaves[0].LeafIdentityHash = leaves[0].LeafIdentityHash // Hash dup. dupLeaves[2].LeafIndex = 2 // Index dup. leafHash := sha256.Sum256([]byte("foobar")) dupLeaves[2].LeafIdentityHash = leafHash[:] // TODO: Remove when spannertest has transaction support. aslt.addSequencedLeaves(dupLeaves) aslt.verifySequencedLeaves(6, 4, nil) aslt.verifySequencedLeaves(7, 4, dupLeaves[1:2]) aslt.verifySequencedLeaves(8, 4, nil) aslt.verifySequencedLeaves(9, 4, dupLeaves[3:4]) dupLeaves = createTestLeaves(4, 6) aslt.addSequencedLeaves(dupLeaves) aslt.verifySequencedLeaves(6, 4, dupLeaves) } // Time we'll request for guard cutoff in tests that don't test this (should include all above) var fakeDequeueCutoffTime = time.Date(2016, 11, 10, 15, 16, 30, 0, time.UTC) func (*logTests) TestDequeueLeavesNoneQueued(ctx context.Context, t *testing.T, s storage.LogStorage, as storage.AdminStorage) { tree := mustCreateTree(ctx, t, as, storageto.LogTree) runLogTX(s, tree, t, func(ctx context.Context, tx storage.LogTreeTX) error { leaves, err := tx.DequeueLeaves(ctx, 999, fakeDequeueCutoffTime) if err != nil { t.Fatalf("Didn't expect an error on dequeue with no work to be done: %v", err) } if len(leaves) > 0 { t.Fatalf("Expected nothing to be dequeued but we got %d leaves", len(leaves)) } return nil }) } // GetLeavesByRange tests. ----------------------------------------------------- type getLeavesByRangeTest struct { start, count int64 want []int64 wantErr bool } func testGetLeavesByRangeImpl(ctx context.Context, t *testing.T, s storage.LogStorage, as storage.AdminStorage, create *trillian.Tree, tests []getLeavesByRangeTest) { tree := mustCreateTree(ctx, t, as, create) // Note: GetLeavesByRange loads the root internally to get the tree size. mustSignAndStoreLogRoot(ctx, t, s, tree, &types.LogRootV1{TreeSize: 14}) // Create leaves [0]..[19] but drop leaf [5] and set the tree size to 14. for i := int64(0); i < 20; i++ { if i == 5 { continue } data := []byte{byte(i)} someExtraData := []byte("Some extra data") identityHash := sha256.Sum256(data) createFakeLeaf(ctx, s, tree, identityHash[:], identityHash[:], data, someExtraData, i, t) } for _, test := range tests { runLogTX(s, tree, t, func(ctx context.Context, tx storage.LogTreeTX) error { leaves, err := tx.GetLeavesByRange(ctx, test.start, test.count) if err != nil { if !test.wantErr { t.Errorf("GetLeavesByRange(%d, +%d)=_,%v; want _,nil", test.start, test.count, err) } return nil } if test.wantErr { t.Errorf("GetLeavesByRange(%d, +%d)=_,nil; want _,non-nil", test.start, test.count) } got := make([]int64, len(leaves)) for i, leaf := range leaves { got[i] = leaf.LeafIndex } if !reflect.DeepEqual(got, test.want) { t.Errorf("GetLeavesByRange(%d, +%d)=%+v; want %+v", test.start, test.count, got, test.want) } return nil }) } } func (*logTests) TestGetLeavesByRangeFromLog(ctx context.Context, t *testing.T, s storage.LogStorage, as storage.AdminStorage) { tests := []getLeavesByRangeTest{ {start: 0, count: 1, want: []int64{0}}, {start: 0, count: 2, want: []int64{0, 1}}, {start: 1, count: 3, want: []int64{1, 2, 3}}, {start: 10, count: 7, want: []int64{10, 11, 12, 13}}, {start: 13, count: 1, want: []int64{13}}, {start: 14, count: 4, wantErr: true}, // Starts right after tree size. {start: 19, count: 2, wantErr: true}, // Starts further away. {start: 3, count: 5, wantErr: true}, // Hits non-contiguous leaves. {start: 5, count: 5, wantErr: true}, // Starts from a missing leaf. {start: 1, count: 0, wantErr: true}, // Empty range. {start: -1, count: 1, wantErr: true}, // Negative start. {start: 1, count: -1, wantErr: true}, // Negative count. {start: 100, count: 30, wantErr: true}, // Starts after all stored leaves. } testGetLeavesByRangeImpl(ctx, t, s, as, storageto.LogTree, tests) } func (*logTests) TestGetLeavesByRangeFromPreorderedLog(ctx context.Context, t *testing.T, s storage.LogStorage, as storage.AdminStorage) { tests := []getLeavesByRangeTest{ {start: 0, count: 1, want: []int64{0}}, {start: 0, count: 2, want: []int64{0, 1}}, {start: 1, count: 3, want: []int64{1, 2, 3}}, {start: 10, count: 7, want: []int64{10, 11, 12, 13, 14, 15, 16}}, {start: 13, count: 1, want: []int64{13}}, // Starts right after tree size. {start: 14, count: 4, want: []int64{14, 15, 16, 17}}, {start: 19, count: 2, want: []int64{19}}, // Starts further away. {start: 3, count: 5, wantErr: true}, // Hits non-contiguous leaves. {start: 5, count: 5, wantErr: true}, // Starts from a missing leaf. {start: 1, count: 0, wantErr: true}, // Empty range. {start: -1, count: 1, wantErr: true}, // Negative start. {start: 1, count: -1, wantErr: true}, // Negative count. {start: 100, count: 30, want: []int64{}}, // Starts after all stored leaves. } testGetLeavesByRangeImpl(ctx, t, s, as, storageto.PreorderedLogTree, tests) } // Time we will queue all leaves at var fakeQueueTime = time.Date(2016, 11, 10, 15, 16, 27, 0, time.UTC) func createFakeLeaf(ctx context.Context, s storage.LogStorage, tree *trillian.Tree, rawHash, hash, data, extraData []byte, seq int64, t *testing.T) *trillian.LogLeaf { t.Helper() leaf := &trillian.LogLeaf{ MerkleLeafHash: hash, LeafValue: data, ExtraData: extraData, LeafIndex: seq, LeafIdentityHash: rawHash, } q, err := s.AddSequencedLeaves(ctx, tree, []*trillian.LogLeaf{leaf}, fakeQueueTime) if err != nil { t.Fatalf("Failed to create test leaves: %v", err) } return q[0].Leaf } func (*logTests) TestDequeueLeaves(ctx context.Context, t *testing.T, s storage.LogStorage, as storage.AdminStorage) { const leavesToInsert = 5 tree := mustCreateTree(ctx, t, as, storageto.LogTree) mustSignAndStoreLogRoot(ctx, t, s, tree, &types.LogRootV1{}) leaves := createTestLeaves(leavesToInsert, 20) if _, err := s.QueueLeaves(ctx, tree, leaves, fakeDequeueCutoffTime); err != nil { t.Fatalf("Failed to queue leaves: %v", err) } // Now try to dequeue them // Some dequeue implementations probabalistically dequeue and require retrying until timeout. cctx, cancel := context.WithTimeout(ctx, 5*time.Second) // Retry until timeout defer cancel() leaves2 := dequeueAndSequence(cctx, t, s, tree, fakeDequeueCutoffTime, leavesToInsert, 0) if got, want := len(leaves2), leavesToInsert; got != want { t.Fatalf("Got %d leaves want %d", got, want) } // If we dequeue again then we should now get nothing if err := s.ReadWriteTransaction(ctx, tree, func(ctx context.Context, tx storage.LogTreeTX) error { leaves, err := tx.DequeueLeaves(ctx, 99, fakeDequeueCutoffTime) if err != nil { t.Fatalf("Failed to dequeue leaves (second time): %v", err) } if got, want := len(leaves), 0; got != want { t.Fatalf("Got %d leaves want %d", got, want) } return nil }, ); err != nil { t.Fatalf("Could not dequeue the expected number of leaves: %v", err) } } // dequeueAndSequence repeatedly dequeues in a single transaction until limit is reached or a timeout occurs. // Then, it sequences the leaves with UpdateSequencedLeaves. func dequeueAndSequence(ctx context.Context, t *testing.T, ls storage.LogStorage, tree *trillian.Tree, ts time.Time, limit int, startIndex int64) []*trillian.LogLeaf { t.Helper() // We'll retry a few times if we get nothing back since we're now dependent // on the underlying queue delivering unsequenced entries. var ret []*trillian.LogLeaf err := ls.ReadWriteTransaction(ctx, tree, func(ctx context.Context, tx storage.LogTreeTX) error { ret = make([]*trillian.LogLeaf, 0, limit) i := 0 start := time.Now() for rem := limit; rem > 0; { i++ got, err := tx.DequeueLeaves(ctx, rem, ts) if err != nil { return err } ret = append(ret, got...) rem -= len(got) } t.Logf("DequeueLeaves took %v tries and %v to dequeue %d leaves", i, time.Since(start), len(ret)) ensureAllLeavesDistinct(t, ret) ensureLeavesHaveQueueTimestamp(t, ret, ts) iTimestamp := timestamppb.Now() for i, l := range ret { l.IntegrateTimestamp = iTimestamp l.LeafIndex = int64(i) + startIndex } if err := tx.UpdateSequencedLeaves(ctx, ret); err != nil { return fmt.Errorf("UpdateSequencedLeaves(): %v", err) } return nil }) if err != nil { t.Fatalf("dequeueLeaves(limit: %v): %v", limit, err) } return ret } func ensureAllLeavesDistinct(t *testing.T, leaves []*trillian.LogLeaf) { t.Helper() set := make(map[string]bool) for _, l := range leaves { k := string(l.LeafIdentityHash) if _, ok := set[k]; ok { t.Fatalf("Unexpectedly got a duplicate leaf hash: %x", l.LeafIdentityHash) } set[k] = true } } func ensureLeavesHaveQueueTimestamp(t *testing.T, leaves []*trillian.LogLeaf, want time.Time) { t.Helper() for _, leaf := range leaves { gotQTimestamp := leaf.QueueTimestamp.AsTime() if got, want := gotQTimestamp.UnixNano(), want.UnixNano(); got != want { t.Errorf("Got leaf with QueueTimestampNanos = %v, want %v: %v", got, want, leaf) } } } func (*logTests) TestDequeueLeavesTwoBatches(ctx context.Context, t *testing.T, s storage.LogStorage, as storage.AdminStorage) { fakeDequeueCutoffTime := time.Date(2016, 11, 10, 15, 16, 30, 0, time.UTC) const leavesToInsert = 5 tree := mustCreateTree(ctx, t, as, storageto.LogTree) mustSignAndStoreLogRoot(ctx, t, s, tree, &types.LogRootV1{}) leavesToDequeue1 := 3 leavesToDequeue2 := 2 leaves := createTestLeaves(leavesToInsert, 20) if _, err := s.QueueLeaves(ctx, tree, leaves, fakeDequeueCutoffTime); err != nil { t.Fatalf("Failed to queue leaves: %v", err) } cctx, cancel := context.WithTimeout(ctx, 5*time.Second) // Retry until timeout defer cancel() leaves2 := dequeueAndSequence(cctx, t, s, tree, fakeDequeueCutoffTime, leavesToDequeue1, 0) if len(leaves2) != leavesToDequeue1 { t.Fatalf("Dequeued %d leaves but expected to get %d", len(leaves2), leavesToDequeue1) } mustSignAndStoreLogRoot(ctx, t, s, tree, &types.LogRootV1{ TreeSize: uint64(leavesToDequeue1), TimestampNanos: 1, }) leaves3 := dequeueAndSequence(cctx, t, s, tree, fakeDequeueCutoffTime, leavesToDequeue2, int64(leavesToDequeue1)) if len(leaves3) != leavesToDequeue2 { t.Fatalf("Dequeued %d leaves but expected to get %d", len(leaves2), leavesToDequeue2) } // Plus the union of the leaf batches should all have distinct hashes ensureAllLeavesDistinct(t, append(leaves2, leaves3...)) // If we dequeue again then we should now get nothing runLogTX(s, tree, t, func(ctx context.Context, tx4 storage.LogTreeTX) error { leaves5, err := tx4.DequeueLeaves(ctx, 99, fakeDequeueCutoffTime) if err != nil { t.Fatalf("Failed to dequeue leaves (second time): %v", err) } if len(leaves5) != 0 { t.Fatalf("Dequeued %d leaves but expected to get none", len(leaves5)) } return nil }) } func (*logTests) TestAddSequencedLeavesAndDequeueLeaves(ctx context.Context, t *testing.T, s storage.LogStorage, as storage.AdminStorage) { tree := mustCreateTree(ctx, t, as, storageto.PreorderedLogTree) mustSignAndStoreLogRoot(ctx, t, s, tree, &types.LogRootV1{}) leaves := createTestLeaves(3, 0) leaves[0].ExtraData = nil now := time.Now() res, err := s.AddSequencedLeaves(ctx, tree, leaves, now) if err != nil { t.Errorf("AddSequencedLeaves(_, %v, %+v, %v): %v", tree.TreeId, leaves, now, err) } if got, want := len(res), len(leaves); got != want { t.Fatalf("AddSequencedLeaves returned %v results, want %v", got, want) } for i, r := range res { if got, want := status.FromProto(r.Status).Code(), codes.OK; got != want { t.Errorf("res[%d]=%v, want %v", i, got, want) } } qts := timestamppb.New(now) its := timestamppb.New(time.Unix(0, 0)) partial := make([]*trillian.LogLeaf, 0, len(leaves)) for _, leaf := range leaves { partial = append(partial, &trillian.LogLeaf{ LeafIdentityHash: leaf.LeafIdentityHash, MerkleLeafHash: leaf.MerkleLeafHash, LeafValue: leaf.LeafValue, ExtraData: leaf.ExtraData, LeafIndex: leaf.LeafIndex, QueueTimestamp: qts, IntegrateTimestamp: its, }) } // Check that the first sequenced entry is returned. dequeued, err := dequeueLeavesInTx(ctx, s, tree, now, 1) if err != nil { t.Fatalf("dequeueLeaves(): %v", err) } if diff := cmp.Diff(dequeued, partial[:1], protocmp.Transform()); diff != "" { t.Errorf("dequeueLeaves() diff: %v", diff) } // Fake a signing run that signs 1 entry. mustSignAndStoreLogRoot(ctx, t, s, tree, &types.LogRootV1{ TimestampNanos: uint64(time.Now().UnixNano()), TreeSize: 1, RootHash: []byte("roothash"), }) // Check that the 2nd and 3rd sequenced entries are returned. dequeued, err = dequeueLeavesInTx(ctx, s, tree, now, 10) if err != nil { t.Fatalf("dequeueLeaves(): %v", err) } if diff := cmp.Diff(dequeued, partial[1:3], protocmp.Transform()); diff != "" { t.Errorf("dequeueLeaves() diff: %v", diff) } } trillian-1.6.1/integration/storagetest/testrunner.go000066400000000000000000000017111466362047600230020ustar00rootroot00000000000000// 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 storagetest import ( "reflect" "strings" ) func testFunctions(x interface{}) []string { const prefix = "Test" xt := reflect.TypeOf(x) tests := []string{} for i := 0; i < xt.NumMethod(); i++ { methodName := xt.Method(i).Name if !strings.HasPrefix(methodName, prefix) { continue } tests = append(tests, methodName) } return tests } trillian-1.6.1/integration/storagetest/treehelpers.go000066400000000000000000000020361466362047600231140ustar00rootroot00000000000000// 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 storagetest import ( "context" "testing" "github.com/google/trillian" "github.com/google/trillian/storage" ) // mustCreateTree creates the specified tree using AdminStorage. func mustCreateTree(ctx context.Context, t *testing.T, s storage.AdminStorage, tree *trillian.Tree) *trillian.Tree { t.Helper() tree, err := storage.CreateTree(ctx, s, tree) if err != nil { t.Fatalf("storage.CreateTree(): %v", err) } return tree } trillian-1.6.1/log/000077500000000000000000000000001466362047600141345ustar00rootroot00000000000000trillian-1.6.1/log/gen.go000066400000000000000000000014441466362047600152370ustar00rootroot00000000000000// 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 log //go:generate mockgen -write_package_comment=false -self_package github.com/google/trillian/log -package log -destination mock_operation.go github.com/google/trillian/log Operation trillian-1.6.1/log/mock_operation.go000066400000000000000000000027061466362047600175010ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/google/trillian/log (interfaces: Operation) package log import ( context "context" reflect "reflect" gomock "github.com/golang/mock/gomock" ) // MockOperation is a mock of Operation interface. type MockOperation struct { ctrl *gomock.Controller recorder *MockOperationMockRecorder } // MockOperationMockRecorder is the mock recorder for MockOperation. type MockOperationMockRecorder struct { mock *MockOperation } // NewMockOperation creates a new mock instance. func NewMockOperation(ctrl *gomock.Controller) *MockOperation { mock := &MockOperation{ctrl: ctrl} mock.recorder = &MockOperationMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockOperation) EXPECT() *MockOperationMockRecorder { return m.recorder } // ExecutePass mocks base method. func (m *MockOperation) ExecutePass(arg0 context.Context, arg1 int64, arg2 *OperationInfo) (int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ExecutePass", arg0, arg1, arg2) ret0, _ := ret[0].(int) ret1, _ := ret[1].(error) return ret0, ret1 } // ExecutePass indicates an expected call of ExecutePass. func (mr *MockOperationMockRecorder) ExecutePass(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecutePass", reflect.TypeOf((*MockOperation)(nil).ExecutePass), arg0, arg1, arg2) } trillian-1.6.1/log/operation_manager.go000066400000000000000000000370651466362047600201700ustar00rootroot00000000000000// 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 log holds the code that is specific to Trillian logs core operation, // particularly the code for sequencing. package log import ( "context" "fmt" "reflect" "sort" "strconv" "sync" "time" "github.com/google/trillian/extension" "github.com/google/trillian/monitoring" "github.com/google/trillian/storage" "github.com/google/trillian/util/clock" "github.com/google/trillian/util/election" "golang.org/x/sync/semaphore" "k8s.io/klog/v2" ) var ( // DefaultTimeout is the default timeout on a single log operation run. DefaultTimeout = 60 * time.Second once sync.Once knownLogs monitoring.Gauge resignations monitoring.Counter isMaster monitoring.Gauge signingRuns monitoring.Counter failedSigningRuns monitoring.Counter entriesAdded monitoring.Counter batchesAdded monitoring.Counter ) func createMetrics(mf monitoring.MetricFactory) { if mf == nil { mf = monitoring.InertMetricFactory{} } knownLogs = mf.NewGauge("known_logs", "Set to 1 for known logs (whether this instance is master or not)", logIDLabel) resignations = mf.NewCounter("master_resignations", "Number of mastership resignations", logIDLabel) isMaster = mf.NewGauge("is_master", "Whether this instance is master (0/1)", logIDLabel) signingRuns = mf.NewCounter("signing_runs", "Number of times a signing run has succeeded", logIDLabel) failedSigningRuns = mf.NewCounter("failed_signing_runs", "Number of times a signing run has failed", logIDLabel) // entriesAdded is the total number of entries that have been added to the // log during the lifetime of a signer. This allows an operator to determine // that the queue is empty for a particular log; if signing runs are succeeding // but nothing is being processed then this counter will stop increasing. entriesAdded = mf.NewCounter("entries_added", "Number of entries added to the log", logIDLabel) // batchesAdded is the number of times a signing run caused entries to be // integrated into the log. The value batchesAdded / signingRuns is an // indication of how often the signer runs but does no work. The value of // entriesAdded / batchesAdded is average batch size. These can be used for // tuning sequencing or evaluating performance. batchesAdded = mf.NewCounter("batches_added", "Number of times a non zero number of entries was added", logIDLabel) } // Operation defines a task that operates on a log. Examples are scheduling, signing, // consistency checking or cleanup. type Operation interface { // ExecutePass performs a single pass of processing on a single log. It returns // a count of items processed (for logging) and an error. ExecutePass(ctx context.Context, logID int64, info *OperationInfo) (int, error) } // OperationInfo bundles up information needed for running a set of Operations. type OperationInfo struct { // Registry provides access to Trillian storage. Registry extension.Registry // The following parameters are passed to individual Operations. // BatchSize is the batch size to be passed to tasks run by this manager. BatchSize int // TimeSource should be used by the Operation to allow mocking for tests. TimeSource clock.TimeSource // The following parameters govern the overall scheduling of Operations // by a OperationManager. // Election-related configuration. Copied for each log. ElectionConfig election.RunnerConfig // RunInterval is the time between starting batches of processing. If a // batch takes longer than this interval to complete, the next batch // will start immediately. RunInterval time.Duration // NumWorkers is the number of worker goroutines to run in parallel. NumWorkers int // Timeout sets an optional timeout on each operation run. // If unset, default to the value of DefaultTimeout. Timeout time.Duration } // OperationManager controls scheduling activities for logs. type OperationManager struct { info OperationInfo // logOperation is the task that gets run for active logs. logOperation Operation // runnerWG groups all goroutines with election Runners. runnerWG sync.WaitGroup // runnerCancels contains cancel function for each logID election Runner. runnerCancels map[string]context.CancelFunc // pendingResignations delivers resignation requests from election Runners. pendingResignations chan election.Resignation tracker *election.MasterTracker // Cache of logID => name. Names are assumed not to change during runtime. logNames map[int64]string // A recent list of active logs that this instance is master for. lastHeld []int64 // idsMutex guards logNames and lastHeld fields. idsMutex sync.Mutex } // NewOperationManager creates a new OperationManager instance. func NewOperationManager(info OperationInfo, logOperation Operation) *OperationManager { once.Do(func() { createMetrics(info.Registry.MetricFactory) }) if info.Timeout == 0 { info.Timeout = DefaultTimeout } tracker := election.NewMasterTracker(nil, func(id string, v bool) { val := 0.0 if v { val = 1.0 } isMaster.Set(val, id) }) return &OperationManager{ info: info, logOperation: logOperation, runnerCancels: make(map[string]context.CancelFunc), pendingResignations: make(chan election.Resignation, 100), tracker: tracker, logNames: make(map[int64]string), } } // logName maps a logID to a human-readable name, caching results along the way. // The human-readable name may non-unique so should only be used for diagnostics. func (o *OperationManager) logName(ctx context.Context, logID int64) string { o.idsMutex.Lock() defer o.idsMutex.Unlock() if name, ok := o.logNames[logID]; ok { return name } tree, err := storage.GetTree(ctx, o.info.Registry.AdminStorage, logID) if err != nil { klog.Errorf("%v: failed to get log info: %v", logID, err) return "" } name := tree.DisplayName if name == "" { name = fmt.Sprintf("", logID) } o.logNames[logID] = name return o.logNames[logID] } func (o *OperationManager) heldInfo(ctx context.Context, logIDs []int64) string { names := make([]string, 0, len(logIDs)) for _, logID := range logIDs { names = append(names, o.logName(ctx, logID)) } sort.Strings(names) result := "master for:" for _, name := range names { result += " " + name } return result } // masterFor returns the list of log IDs among allIDs that this instance is // master for. Note that the instance may hold mastership for logs that are not // listed in allIDs, but such logs are skipped. func (o *OperationManager) masterFor(ctx context.Context, allIDs []int64) ([]int64, error) { if o.info.Registry.ElectionFactory == nil { return allIDs, nil } allStringIDs := make([]string, 0, len(allIDs)) for _, id := range allIDs { s := strconv.FormatInt(id, 10) allStringIDs = append(allStringIDs, s) } // Synchronize the set of log IDs with those we are tracking mastership for. for _, logID := range allStringIDs { knownLogs.Set(1, logID) if o.runnerCancels[logID] == nil { o.tracker.Set(logID, false) // Initialise tracking for this ID. o.runnerCancels[logID] = o.runElectionWithRestarts(ctx, logID) } } held := o.tracker.Held() heldIDs := make([]int64, 0, len(allIDs)) sort.Strings(allStringIDs) for _, s := range held { // Skip the log if it is not present in allIDs. if i := sort.SearchStrings(allStringIDs, s); i >= len(allStringIDs) || allStringIDs[i] != s { continue } id, err := strconv.ParseInt(s, 10, 64) if err != nil { return nil, fmt.Errorf("failed to parse logID %v as int64", s) } heldIDs = append(heldIDs, id) } return heldIDs, nil } // runElectionWithRestarts runs the election/resignation loop for the given log // indefinitely, until the returned CancelFunc is invoked. Any failure during // the loop leads to a restart of the loop with a few seconds delay. // // TODO(pavelkalinnikov): Restart the whole log operation rather than just the // election, and have a metric for restarts. func (o *OperationManager) runElectionWithRestarts(ctx context.Context, logID string) context.CancelFunc { klog.Infof("create master election goroutine for %v", logID) cctx, cancel := context.WithCancel(ctx) run := func(ctx context.Context) { e, err := o.info.Registry.ElectionFactory.NewElection(ctx, logID) if err != nil { klog.Errorf("failed to create election for %v: %v", logID, err) return } // Warning: NewRunner can attempt to modify the config. Make a separate // copy of the config for each log, to avoid data races. config := o.info.ElectionConfig // TODO(pavelkalinnikov): Passing the cancel function is not needed here. r := election.NewRunner(logID, &config, o.tracker, cancel, e) r.Run(ctx, o.pendingResignations) } o.runnerWG.Add(1) go func(ctx context.Context) { defer o.runnerWG.Done() // Continue only while the context is active. for ctx.Err() == nil { run(ctx) // Sleep before restarts, to not spam the log with errors. // TODO(pavelkalinnikov): Make the interval configurable. const pause = time.Duration(5 * time.Second) if err := clock.SleepSource(ctx, pause, o.info.TimeSource); err != nil { break // The context has been canceled during the sleep. } } }(cctx) return cancel } // updateHeldIDs updates the process status with the number/list of logs that // the instance holds mastership for. func (o *OperationManager) updateHeldIDs(ctx context.Context, logIDs, activeIDs []int64) { heldInfo := o.heldInfo(ctx, logIDs) msg := fmt.Sprintf("Acting as master for %d / %d active logs: %s", len(logIDs), len(activeIDs), heldInfo) o.idsMutex.Lock() defer o.idsMutex.Unlock() if !reflect.DeepEqual(logIDs, o.lastHeld) { o.lastHeld = make([]int64, len(logIDs)) copy(o.lastHeld, logIDs) klog.Info(msg) if o.info.Registry.SetProcessStatus != nil { o.info.Registry.SetProcessStatus(heldInfo) } } else { klog.V(1).Info(msg) } } func (o *OperationManager) getLogsAndExecutePass(ctx context.Context) error { runCtx, cancel := context.WithTimeout(ctx, o.info.Timeout) defer cancel() activeIDs, err := o.info.Registry.LogStorage.GetActiveLogIDs(ctx) if err != nil { return fmt.Errorf("failed to list active log IDs: %v", err) } // Find the logs we are master for, skipping those logs that are not active, // e.g. deleted or FROZEN ones. // TODO(pavelkalinnikov): Resign mastership for the inactive logs. logIDs, err := o.masterFor(ctx, activeIDs) if err != nil { return fmt.Errorf("failed to determine log IDs we're master for: %v", err) } o.updateHeldIDs(ctx, logIDs, activeIDs) executePassForAll(runCtx, &o.info, o.logOperation, logIDs) return nil } // OperationSingle performs a single pass of the manager. // // TODO(pavelkalinnikov): Deprecate this because it doesn't clean up any state, // and is used only for testing. func (o *OperationManager) OperationSingle(ctx context.Context) { if err := o.getLogsAndExecutePass(ctx); err != nil { klog.Errorf("failed to perform operation: %v", err) } } // OperationLoop starts the manager working. It continues until told to exit. // TODO(Martin2112): No mechanism for error reporting etc., this is OK for v1 but needs work func (o *OperationManager) OperationLoop(ctx context.Context) { klog.Infof("Log operation manager starting") // Outer loop, runs until terminated. for { if err := o.operateOnce(ctx); err != nil { klog.Infof("Log operation manager shutting down") break } } // Terminate all the election Runners. for logID, cancel := range o.runnerCancels { if cancel != nil { klog.V(1).Infof("cancel election runner for %s", logID) cancel() } } // Drain any remaining resignations which might have triggered. close(o.pendingResignations) for r := range o.pendingResignations { resignations.Inc(r.ID) r.Execute(ctx) } klog.Infof("wait for termination of election runners...") o.runnerWG.Wait() klog.Infof("wait for termination of election runners...done") } // operateOnce runs a single round of operation for each of the active logs // that this instance is master for. Returns an error only if the context is // canceled, i.e. the operation is being shut down. func (o *OperationManager) operateOnce(ctx context.Context) error { // TODO(alcutter): want a child context with deadline here? start := o.info.TimeSource.Now() if err := o.getLogsAndExecutePass(ctx); err != nil { // Suppress the error if ctx is done (ctx.Err != nil) as we're exiting. if ctx.Err() != nil { klog.Errorf("failed to execute operation on logs: %v", err) } } klog.V(1).Infof("Log operation manager pass complete") // Process any pending resignations while there's no activity. doneResigning := false for !doneResigning { select { case r := <-o.pendingResignations: resignations.Inc(r.ID) r.Execute(ctx) default: doneResigning = true } } // See if it's time to quit. select { case <-ctx.Done(): return ctx.Err() default: } // Wait for the configured time before going for another pass. duration := o.info.TimeSource.Now().Sub(start) wait := o.info.RunInterval - duration if wait > 0 { klog.V(1).Infof("Processing started at %v for %v; wait %v before next run", start, duration, wait) if err := clock.SleepContext(ctx, wait); err != nil { return err } } else { klog.V(1).Infof("Processing started at %v for %v; start next run immediately", start, duration) } return nil } // executePassForAll runs ExecutePass of the given operation for each of the // passed-in logs, allowing up to a configurable number of parallel operations. func executePassForAll(ctx context.Context, info *OperationInfo, op Operation, logIDs []int64) { startBatch := info.TimeSource.Now() numWorkers := info.NumWorkers if numWorkers <= 0 { klog.Warning("Running executor with NumWorkers <= 0, assuming 1") numWorkers = 1 } klog.V(1).Infof("Running executor with %d worker(s)", numWorkers) sem := semaphore.NewWeighted(int64(numWorkers)) var wg sync.WaitGroup for _, logID := range logIDs { if err := sem.Acquire(ctx, 1); err != nil { break // Terminate because the context is canceled. } wg.Add(1) go func(logID int64) { defer wg.Done() defer sem.Release(1) if err := executePass(ctx, info, op, logID); err != nil { klog.Errorf("ExecutePass(%v) failed: %v", logID, err) } }(logID) } // Wait for the workers to consume all of the logIDs. wg.Wait() d := clock.SecondsSince(info.TimeSource, startBatch) klog.V(1).Infof("Group run completed in %.2f seconds", d) } // executePass runs ExecutePass of the given operation for the passed-in log. func executePass(ctx context.Context, info *OperationInfo, op Operation, logID int64) error { label := strconv.FormatInt(logID, 10) start := info.TimeSource.Now() count, err := op.ExecutePass(ctx, logID, info) if err != nil { failedSigningRuns.Inc(label) return err } // This indicates signing activity is proceeding on the logID. signingRuns.Inc(label) if count > 0 { d := clock.SecondsSince(info.TimeSource, start) klog.Infof("%v: processed %d items in %.2f seconds (%.2f qps)", logID, count, d, float64(count)/d) entriesAdded.Add(float64(count), label) batchesAdded.Inc(label) } else { klog.V(1).Infof("%v: no items to process", logID) } return nil } trillian-1.6.1/log/operation_manager_test.go000066400000000000000000000356101466362047600212210ustar00rootroot00000000000000// 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 log import ( "context" "errors" "fmt" "reflect" "strconv" "sync/atomic" "testing" "time" "github.com/golang/mock/gomock" "github.com/google/trillian" "github.com/google/trillian/extension" "github.com/google/trillian/monitoring/testonly" "github.com/google/trillian/storage" "github.com/google/trillian/util/clock" "github.com/google/trillian/util/election" "github.com/google/trillian/util/election2" eto "github.com/google/trillian/util/election2/testonly" ) const ( logIDThatFailsGetTreeOp = int64(98) logIDWithNoDisplayName = int64(99) ) func defaultOperationInfo(registry extension.Registry) OperationInfo { return OperationInfo{ Registry: registry, BatchSize: 50, NumWorkers: 1, RunInterval: time.Second, TimeSource: fakeTimeSource, } } func TestOperationManagerGetLogsFails(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() fakeStorage := storage.NewMockLogStorage(ctrl) fakeStorage.EXPECT().GetActiveLogIDs(gomock.Any()).Return(nil, errors.New("getactivelogs")) registry := extension.Registry{ LogStorage: fakeStorage, } mockLogOp := NewMockOperation(ctrl) ctx := context.Background() info := defaultOperationInfo(registry) lom := NewOperationManager(info, mockLogOp) lom.OperationSingle(ctx) } type logOpInfoMatcher struct { BatchSize int } func (l logOpInfoMatcher) Matches(x interface{}) bool { o, ok := x.(*OperationInfo) if !ok { return false } return o.BatchSize == l.BatchSize } func (l logOpInfoMatcher) String() string { return fmt.Sprintf("has batchSize %d", l.BatchSize) } // Set up some log IDs in mock storage. // The following IDs have special behaviour: // // logIDThatFailsGetTreeOp: fail the GetTree() operation // logIDWithNoDisplayName: return a tree with no DisplayName func setupLogIDs(ctrl *gomock.Controller, logNames map[int64]string) (*storage.MockLogStorage, *storage.MockAdminStorage) { ids := make([]int64, 0, len(logNames)) for id := range logNames { ids = append(ids, id) } fakeStorage := storage.NewMockLogStorage(ctrl) fakeStorage.EXPECT().GetActiveLogIDs(gomock.Any()).AnyTimes().Return(ids, nil) mockAdmin := storage.NewMockAdminStorage(ctrl) mockAdminTx := storage.NewMockReadOnlyAdminTX(ctrl) for id, name := range logNames { switch id { case logIDThatFailsGetTreeOp: mockAdminTx.EXPECT().GetTree(gomock.Any(), id).AnyTimes().Return(nil, errors.New("failedGetTree")) case logIDWithNoDisplayName: mockAdminTx.EXPECT().GetTree(gomock.Any(), id).AnyTimes().Return(&trillian.Tree{TreeId: id}, nil) default: mockAdminTx.EXPECT().GetTree(gomock.Any(), id).AnyTimes().Return(&trillian.Tree{ TreeId: id, DisplayName: name, }, nil) } } mockAdminTx.EXPECT().Commit().AnyTimes().Return(nil) mockAdminTx.EXPECT().Close().AnyTimes().Return(nil) mockAdmin.EXPECT().Snapshot(gomock.Any()).AnyTimes().Return(mockAdminTx, nil) return fakeStorage, mockAdmin } func TestOperationManagerPassesIDs(t *testing.T) { ctx := context.Background() logID1 := int64(451) logID2 := int64(145) ctrl := gomock.NewController(t) defer ctrl.Finish() fakeStorage, mockAdmin := setupLogIDs(ctrl, map[int64]string{logID1: "LogID1", logID2: "LogID2"}) registry := extension.Registry{ LogStorage: fakeStorage, AdminStorage: mockAdmin, } mockLogOp := NewMockOperation(ctrl) infoMatcher := logOpInfoMatcher{50} mockLogOp.EXPECT().ExecutePass(gomock.Any(), logID1, infoMatcher).Return(1, nil) mockLogOp.EXPECT().ExecutePass(gomock.Any(), logID2, infoMatcher).Return(0, nil) info := defaultOperationInfo(registry) lom := NewOperationManager(info, mockLogOp) lom.OperationSingle(ctx) } func TestOperationManagerExecutePassError(t *testing.T) { ctx := context.Background() logID1 := int64(451) logID2 := int64(145) logID1Label := strconv.FormatInt(logID1, 10) logID2Label := strconv.FormatInt(logID2, 10) ctrl := gomock.NewController(t) defer ctrl.Finish() fakeStorage, mockAdmin := setupLogIDs(ctrl, map[int64]string{logID1: "LogID1", logID2: "LogID2"}) registry := extension.Registry{ LogStorage: fakeStorage, AdminStorage: mockAdmin, } mockLogOp := NewMockOperation(ctrl) infoMatcher := logOpInfoMatcher{50} mockLogOp.EXPECT().ExecutePass(gomock.Any(), logID1, infoMatcher).Return(1, nil) mockLogOp.EXPECT().ExecutePass(gomock.Any(), logID2, infoMatcher).Return(0, errors.New("test error")) info := defaultOperationInfo(registry) lom := NewOperationManager(info, mockLogOp) // initialises counters // Do not run this test in parallel with any other that affects the signingRuns or failedSigningRuns counters. log1SigningRuns := testonly.NewCounterSnapshot(signingRuns, logID1Label) log1FailedSigningRuns := testonly.NewCounterSnapshot(failedSigningRuns, logID1Label) log2SigningRuns := testonly.NewCounterSnapshot(signingRuns, logID2Label) log2FailedSigningRuns := testonly.NewCounterSnapshot(failedSigningRuns, logID2Label) lom.OperationSingle(ctx) // Check that logID1 has 1 successful signing run, 0 failed. if want, got := 1, int(log1SigningRuns.Delta()); want != got { t.Errorf("want signingRuns[logID1] == %d, got %d", want, got) } if want, got := 0, int(log1FailedSigningRuns.Delta()); want != got { t.Errorf("want signingRuns[logID2] == %d, got %d", want, got) } // Check that logID2 has 0 successful signing runs, 1 failed. if want, got := 0, int(log2SigningRuns.Delta()); want != got { t.Errorf("want failedSigningRuns[logID1] == %d, got %d", want, got) } if want, got := 1, int(log2FailedSigningRuns.Delta()); want != got { t.Errorf("want failedSigningRuns[logID2] == %d, got %d", want, got) } } func TestOperationManagerOperationLoopPassesIDs(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() logID1 := int64(451) logID2 := int64(145) var logCount int64 ctrl := gomock.NewController(t) defer ctrl.Finish() fakeStorage, mockAdmin := setupLogIDs(ctrl, map[int64]string{451: "LogID1", 145: "LogID2"}) registry := extension.Registry{ LogStorage: fakeStorage, AdminStorage: mockAdmin, } mockLogOp := NewMockOperation(ctrl) infoMatcher := logOpInfoMatcher{50} mockLogOp.EXPECT().ExecutePass(gomock.Any(), logID1, infoMatcher).Do(func(_ context.Context, _ int64, _ *OperationInfo) { if atomic.AddInt64(&logCount, 1) == 2 { cancel() } }).Return(1, nil) mockLogOp.EXPECT().ExecutePass(gomock.Any(), logID2, infoMatcher).Do(func(_ context.Context, _ int64, _ *OperationInfo) { if atomic.AddInt64(&logCount, 1) == 2 { cancel() } }).Return(0, nil) info := defaultOperationInfo(registry) lom := NewOperationManager(info, mockLogOp) lom.OperationLoop(ctx) } // TestOperationManagerOperationLoopExitOnContext is a regression test for a // deadlock condition wherein a masterelection queues up a Resignation (due to // having been master for too long) during a long-running sequencing operation. // Meanwhile, the context for the operation loop is cancelled, which causes the // operation loop to immediately exit upon the sequencing run finishing, // bypassing acting on the resignation, causing the election running to hang // forever. func TestOperationManagerOperationLoopExitOnContext(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) logID1 := int64(451) ctrl := gomock.NewController(t) defer ctrl.Finish() fakeStorage, mockAdmin := setupLogIDs(ctrl, map[int64]string{451: "LogID1"}) registry := extension.Registry{ LogStorage: fakeStorage, AdminStorage: mockAdmin, ElectionFactory: &alwaysMasterFactory{}, } fakeTime := clock.NewFake(time.Now()) d := election.MinMasterHoldInterval info := defaultOperationInfo(registry) info.RunInterval = time.Second info.TimeSource = fakeTime info.ElectionConfig.TimeSource = fakeTime // We'll cause the master election to "quickly" resign here: info.ElectionConfig.MasterHoldInterval = d info.ElectionConfig.MasterHoldJitter = 0 mockLogOp := NewMockOperation(ctrl) infoMatcher := logOpInfoMatcher{50} mockLogOp.EXPECT().ExecutePass(gomock.Any(), logID1, infoMatcher).Do(func(_ context.Context, _ int64, _ *OperationInfo) { // Wind the clock on so that we queue up a resignation while we're "working" on a signing run fakeTime.Set(fakeTime.Now().Add(d * 3)) // Give some slack for it to take effect... time.Sleep(time.Second) // ...and then cancel the OperationLoop context cancel() }).Return(1, nil) lom := NewOperationManager(info, mockLogOp) // Prompt a signing run to happen once we're running the operation loop: go func() { time.Sleep(time.Second) fakeTime.Set(fakeTime.Now().Add(info.RunInterval * 2)) }() t.Logf("Entering operationLoop") lom.OperationLoop(ctx) t.Logf("Exited operationLoop") } func TestOperationManagerOperationLoopExecutePassError(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() logID1 := int64(451) logID2 := int64(145) logID1Label := strconv.FormatInt(logID1, 10) logID2Label := strconv.FormatInt(logID2, 10) var logCount int64 ctrl := gomock.NewController(t) defer ctrl.Finish() fakeStorage, mockAdmin := setupLogIDs(ctrl, map[int64]string{451: "LogID1", 145: "LogID2"}) registry := extension.Registry{ LogStorage: fakeStorage, AdminStorage: mockAdmin, } mockLogOp := NewMockOperation(ctrl) infoMatcher := logOpInfoMatcher{50} mockLogOp.EXPECT().ExecutePass(gomock.Any(), logID1, infoMatcher).Do(func(_ context.Context, _ int64, _ *OperationInfo) { if atomic.AddInt64(&logCount, 1) == 2 { cancel() } }).Return(1, nil) mockLogOp.EXPECT().ExecutePass(gomock.Any(), logID2, infoMatcher).Do(func(_ context.Context, _ int64, _ *OperationInfo) { if atomic.AddInt64(&logCount, 1) == 2 { cancel() } }).Return(0, errors.New("test error")) info := defaultOperationInfo(registry) lom := NewOperationManager(info, mockLogOp) // initialises counters // Do not run this test in parallel with any other that affects the signingRuns or failedSigningRuns counters. log1SigningRuns := testonly.NewCounterSnapshot(signingRuns, logID1Label) log1FailedSigningRuns := testonly.NewCounterSnapshot(failedSigningRuns, logID1Label) log2SigningRuns := testonly.NewCounterSnapshot(signingRuns, logID2Label) log2FailedSigningRuns := testonly.NewCounterSnapshot(failedSigningRuns, logID2Label) lom.OperationLoop(ctx) // Check that logID1 has 1 successful signing run, 0 failed. if want, got := 1, int(log1SigningRuns.Delta()); want != got { t.Errorf("want signingRuns[logID1] == %d, got %d", want, got) } if want, got := 0, int(log1FailedSigningRuns.Delta()); want != got { t.Errorf("want signingRuns[logID2] == %d, got %d", want, got) } // Check that logID2 has 0 successful signing runs, 1 failed. if want, got := 0, int(log2SigningRuns.Delta()); want != got { t.Errorf("want failedSigningRuns[logID1] == %d, got %d", want, got) } if want, got := 1, int(log2FailedSigningRuns.Delta()); want != got { t.Errorf("want failedSigningRuns[logID2] == %d, got %d", want, got) } } func TestHeldInfo(t *testing.T) { ctx := context.Background() ctrl := gomock.NewController(t) defer ctrl.Finish() fakeStorage, mockAdmin := setupLogIDs(ctrl, map[int64]string{ 1: "one", 2: "two", logIDThatFailsGetTreeOp: "fail", logIDWithNoDisplayName: "", }) registry := extension.Registry{LogStorage: fakeStorage, AdminStorage: mockAdmin} mockLogOp := NewMockOperation(ctrl) info := defaultOperationInfo(registry) lom := NewOperationManager(info, mockLogOp) tests := []struct { in []int64 want string }{ {in: []int64{}, want: "master for:"}, {in: []int64{1, 2}, want: "master for: one two"}, {in: []int64{2, 1}, want: "master for: one two"}, {in: []int64{2, 1, 2}, want: "master for: one two two"}, {in: []int64{2, 1, logIDWithNoDisplayName}, want: fmt.Sprintf("master for: one two", logIDWithNoDisplayName)}, {in: []int64{2, 1, logIDThatFailsGetTreeOp, 1}, want: "master for: one one two"}, } for _, test := range tests { got := lom.heldInfo(ctx, test.in) if got != test.want { t.Errorf("lom.HeldInfo(%+v)=%q; want %q", test.in, got, test.want) } } } func TestMasterFor(t *testing.T) { ctx := context.Background() firstIDs := []int64{1, 2, 3, 4} allIDs := []int64{1, 2, 3, 4, 5, 6} tests := []struct { desc string factory election2.Factory want1 []int64 want2 []int64 }{ {desc: "no-factory", factory: nil, want1: firstIDs, want2: allIDs}, {desc: "noop-factory", factory: election2.NoopFactory{}, want1: firstIDs, want2: allIDs}, {desc: "master-for-even", factory: masterForEvenFactory{}, want1: []int64{2, 4}, want2: []int64{2, 4, 6}}, {desc: "failure-factory", factory: failureFactory{}, want1: []int64{}, want2: []int64{}}, } for _, test := range tests { t.Run(test.desc, func(t *testing.T) { testCtx, cancel := context.WithCancel(ctx) defer cancel() registry := extension.Registry{ElectionFactory: test.factory} info := OperationInfo{ Registry: registry, TimeSource: clock.System, } lom := NewOperationManager(info, nil) // Check mastership twice, to give the election threads a chance to get started and report. if _, err := lom.masterFor(testCtx, firstIDs); err != nil { t.Error(err) } time.Sleep(100 * time.Millisecond) logIDs, err := lom.masterFor(testCtx, firstIDs) if !reflect.DeepEqual(logIDs, test.want1) { t.Fatalf("masterFor(factory=%T)=%v,%v; want %v,_", test.factory, logIDs, err, test.want1) } // Now add extra IDs and re-check. if _, err := lom.masterFor(testCtx, allIDs); err != nil { t.Error(err) } time.Sleep(100 * time.Millisecond) logIDs, err = lom.masterFor(testCtx, allIDs) if !reflect.DeepEqual(logIDs, test.want2) { t.Fatalf("masterFor(factory=%T)=%v,%v; want %v,_", test.factory, logIDs, err, test.want2) } }) } } type alwaysMasterFactory struct{} func (m alwaysMasterFactory) NewElection(ctx context.Context, treeID string) (election2.Election, error) { d := eto.NewDecorator(eto.NewElection()) return d, nil } type masterForEvenFactory struct{} func (m masterForEvenFactory) NewElection(ctx context.Context, treeID string) (election2.Election, error) { id, err := strconv.ParseInt(treeID, 10, 64) if err != nil { return nil, err } isMaster := (id % 2) == 0 d := eto.NewDecorator(eto.NewElection()) d.BlockAwait(!isMaster) return d, nil } type failureFactory struct{} func (ff failureFactory) NewElection(ctx context.Context, treeID string) (election2.Election, error) { return nil, errors.New("injected failure") } trillian-1.6.1/log/sequencer.go000066400000000000000000000435531466362047600164670ustar00rootroot00000000000000// 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 log import ( "bytes" "context" "flag" "fmt" "strconv" "sync" "time" "github.com/google/trillian" "github.com/google/trillian/monitoring" "github.com/google/trillian/quota" "github.com/google/trillian/storage" "github.com/google/trillian/storage/tree" "github.com/google/trillian/types" "github.com/google/trillian/util/clock" "github.com/transparency-dev/merkle/compact" "github.com/transparency-dev/merkle/rfc6962" "google.golang.org/protobuf/types/known/timestamppb" "k8s.io/klog/v2" ) const logIDLabel = "logid" var ( sequencerOnce sync.Once seqBatches monitoring.Counter seqTreeSize monitoring.Gauge seqLatency monitoring.Histogram seqDequeueLatency monitoring.Histogram seqGetRootLatency monitoring.Histogram seqInitTreeLatency monitoring.Histogram seqWriteTreeLatency monitoring.Histogram seqUpdateLeavesLatency monitoring.Histogram seqSetNodesLatency monitoring.Histogram seqStoreRootLatency monitoring.Histogram seqCounter monitoring.Counter seqMergeDelay monitoring.Histogram seqTimestamp monitoring.Gauge // QuotaIncreaseFactor is the multiplier used for the number of tokens added back to // sequencing-based quotas. The resulting PutTokens call is equivalent to // "PutTokens(_, numLeaves * QuotaIncreaseFactor, _)". // A factor >1 adds resilience to token leakage, on the risk of a system that's overly // optimistic in face of true token shortages. The higher the factor, the higher the quota // "optimism" is. A factor that's too high (say, >1.5) is likely a sign that the quota // configuration should be changed instead. // A factor <1 WILL lead to token shortages, therefore it'll be normalized to 1. QuotaIncreaseFactor = 1.1 ) // TODO(https://github.com/google/trillian/issues/2786): Remove this flag in the next release. var _ = flag.String("tree_ids_with_no_ephemeral_nodes", "*", "[Deprecated] Comma-separated list of tree IDs for which storing the ephemeral nodes is disabled, or * to disable it for all trees") func quotaIncreaseFactor() float64 { if QuotaIncreaseFactor < 1 { QuotaIncreaseFactor = 1 return 1 } return QuotaIncreaseFactor } // InitMetrics sets up some metrics for this package. Must be called before calling IntegrateBatch. // Can be called more than once, but only the first call has any effect. // TODO(pavelkalinnikov): Create all metrics in this package together. func InitMetrics(mf monitoring.MetricFactory) { sequencerOnce.Do(func() { if mf == nil { mf = monitoring.InertMetricFactory{} } quota.InitMetrics(mf) seqBatches = mf.NewCounter("sequencer_batches", "Number of sequencer batch operations", logIDLabel) seqTreeSize = mf.NewGauge("sequencer_tree_size", "Tree size of last SLR signed", logIDLabel) seqTimestamp = mf.NewGauge("sequencer_tree_timestamp", "Time of last SLR signed in ms since epoch", logIDLabel) seqLatency = mf.NewHistogram("sequencer_latency", "Latency of sequencer batch operation in seconds", logIDLabel) seqDequeueLatency = mf.NewHistogram("sequencer_latency_dequeue", "Latency of dequeue-leaves part of sequencer batch operation in seconds", logIDLabel) seqGetRootLatency = mf.NewHistogram("sequencer_latency_get_root", "Latency of get-root part of sequencer batch operation in seconds", logIDLabel) seqInitTreeLatency = mf.NewHistogram("sequencer_latency_init_tree", "Latency of init-tree part of sequencer batch operation in seconds", logIDLabel) seqWriteTreeLatency = mf.NewHistogram("sequencer_latency_write_tree", "Latency of write-tree part of sequencer batch operation in seconds", logIDLabel) seqUpdateLeavesLatency = mf.NewHistogram("sequencer_latency_update_leaves", "Latency of update-leaves part of sequencer batch operation in seconds", logIDLabel) seqSetNodesLatency = mf.NewHistogram("sequencer_latency_set_nodes", "Latency of set-nodes part of sequencer batch operation in seconds", logIDLabel) seqStoreRootLatency = mf.NewHistogram("sequencer_latency_store_root", "Latency of store-root part of sequencer batch operation in seconds", logIDLabel) seqCounter = mf.NewCounter("sequencer_sequenced", "Number of leaves sequenced", logIDLabel) seqMergeDelay = mf.NewHistogram("sequencer_merge_delay", "Delay between queuing and integration of leaves", logIDLabel) }) } // initCompactRangeFromStorage builds a compact range that matches the latest // data in the database. Ensures that the root hash matches the passed in root. func initCompactRangeFromStorage(ctx context.Context, root *types.LogRootV1, tx storage.LogTreeTX) (*compact.Range, error) { fact := compact.RangeFactory{Hash: rfc6962.DefaultHasher.HashChildren} if root.TreeSize == 0 { return fact.NewEmptyRange(0), nil } ids := compact.RangeNodes(0, root.TreeSize, nil) nodes, err := tx.GetMerkleNodes(ctx, ids) if err != nil { return nil, fmt.Errorf("failed to read tree nodes: %v", err) } if got, want := len(nodes), len(ids); got != want { return nil, fmt.Errorf("failed to get %d nodes, got %d", want, got) } hashes := make([][]byte, len(nodes)) for i, node := range nodes { hashes[i] = node.Hash } cr, err := fact.NewRange(0, root.TreeSize, hashes) if err != nil { return nil, fmt.Errorf("failed to create compact.Range: %v", err) } hash, err := cr.GetRootHash(nil) if err != nil { return nil, fmt.Errorf("failed to compute the root hash: %v", err) } // Note: Tree size != 0 at this point, so we don't consider the empty hash. if want := root.RootHash; !bytes.Equal(hash, want) { return nil, fmt.Errorf("root hash mismatch: got %x, want %x", hash, want) } return cr, nil } func buildNodesFromNodeMap(nodeMap map[compact.NodeID][]byte) []tree.Node { nodes := make([]tree.Node, 0, len(nodeMap)) for id, hash := range nodeMap { nodes = append(nodes, tree.Node{ID: id, Hash: hash}) } return nodes } func prepareLeaves(leaves []*trillian.LogLeaf, begin uint64, label string, timeSource clock.TimeSource) error { now := timeSource.Now() integrateAt := timestamppb.New(now) if err := integrateAt.CheckValid(); err != nil { return fmt.Errorf("got invalid integrate timestamp: %w", err) } for i, leaf := range leaves { // The leaf should already have the correct index before it's integrated. if got, want := leaf.LeafIndex, begin+uint64(i); got < 0 || got != int64(want) { return fmt.Errorf("got invalid leaf index: %v, want: %v", got, want) } leaf.IntegrateTimestamp = integrateAt // Old leaves might not have a QueueTimestamp, only calculate the merge // delay if this one does. if leaf.QueueTimestamp != nil && leaf.QueueTimestamp.Seconds != 0 { if err := leaf.QueueTimestamp.CheckValid(); err != nil { return fmt.Errorf("got invalid queue timestamp: %w", err) } queueTS := leaf.QueueTimestamp.AsTime() mergeDelay := now.Sub(queueTS) seqMergeDelay.Observe(mergeDelay.Seconds(), label) } } return nil } // updateCompactRange adds the passed in leaves to the compact range. Returns a // map of all updated tree nodes, and the new root hash. func updateCompactRange(cr *compact.Range, leaves []*trillian.LogLeaf, label string) (map[compact.NodeID][]byte, []byte, error) { nodeMap := make(map[compact.NodeID][]byte) store := func(id compact.NodeID, hash []byte) { nodeMap[id] = hash } // Update the tree state by integrating the leaves one by one. for _, leaf := range leaves { idx := leaf.LeafIndex if size := cr.End(); idx < 0 || idx != int64(size) { return nil, nil, fmt.Errorf("leaf index mismatch: got %d, want %d", idx, size) } // Store all the new internal nodes, including the added leaf. if err := cr.Append(leaf.MerkleLeafHash, store); err != nil { return nil, nil, err } } // Note: Ephemeral nodes are not stored. hash, err := cr.GetRootHash(nil) if err != nil { return nil, nil, err } return nodeMap, hash, nil } // sequencingTask provides sequenced LogLeaf entries, and updates storage // according to their ordering if needed. type sequencingTask interface { // fetch returns a batch of sequenced entries obtained from storage, sized up // to the specified limit. The returned leaves have consecutive LeafIndex // values starting from the current tree size. fetch(ctx context.Context, limit int, cutoff time.Time) ([]*trillian.LogLeaf, error) // update makes sequencing persisted in storage, if not yet. update(ctx context.Context, leaves []*trillian.LogLeaf) error } type sequencingTaskData struct { label string treeSize uint64 timeSource clock.TimeSource tx storage.LogTreeTX } // logSequencingTask is a sequencingTask implementation for "normal" Log mode, // which assigns consecutive sequence numbers to leaves as they are read from // the pending unsequenced entries. type logSequencingTask sequencingTaskData func (s *logSequencingTask) fetch(ctx context.Context, limit int, cutoff time.Time) ([]*trillian.LogLeaf, error) { start := s.timeSource.Now() // Recent leaves inside the guard window will not be available for sequencing. leaves, err := s.tx.DequeueLeaves(ctx, limit, cutoff) if err != nil { return nil, fmt.Errorf("%v: Sequencer failed to dequeue leaves: %v", s.label, err) } seqDequeueLatency.Observe(clock.SecondsSince(s.timeSource, start), s.label) // Assign leaf sequence numbers. for i, leaf := range leaves { leaf.LeafIndex = int64(s.treeSize + uint64(i)) if got := leaf.LeafIndex; got < 0 { return nil, fmt.Errorf("%v: leaf index overflow: %d", s.label, got) } } return leaves, nil } func (s *logSequencingTask) update(ctx context.Context, leaves []*trillian.LogLeaf) error { start := s.timeSource.Now() // Write the new sequence numbers to the leaves in the DB. if err := s.tx.UpdateSequencedLeaves(ctx, leaves); err != nil { return fmt.Errorf("%v: Sequencer failed to update sequenced leaves: %v", s.label, err) } seqUpdateLeavesLatency.Observe(clock.SecondsSince(s.timeSource, start), s.label) return nil } // preorderedLogSequencingTask is a sequencingTask implementation for // Pre-ordered Log mode. It reads sequenced entries past the tree size which // are already in the storage. type preorderedLogSequencingTask sequencingTaskData func (s *preorderedLogSequencingTask) fetch(ctx context.Context, limit int, cutoff time.Time) ([]*trillian.LogLeaf, error) { start := s.timeSource.Now() leaves, err := s.tx.DequeueLeaves(ctx, limit, cutoff) if err != nil { return nil, fmt.Errorf("%v: Sequencer failed to load sequenced leaves: %v", s.label, err) } seqDequeueLatency.Observe(clock.SecondsSince(s.timeSource, start), s.label) return leaves, nil } func (s *preorderedLogSequencingTask) update(ctx context.Context, leaves []*trillian.LogLeaf) error { // TODO(pavelkalinnikov): Update integration timestamps. return nil } // IntegrateBatch wraps up all the operations needed to take a batch of queued // or sequenced leaves and integrate them into the tree. func IntegrateBatch(ctx context.Context, tree *trillian.Tree, limit int, guardWindow, maxRootDurationInterval time.Duration, ts clock.TimeSource, ls storage.LogStorage, qm quota.Manager) (int, error) { start := ts.Now() label := strconv.FormatInt(tree.TreeId, 10) numLeaves := 0 var newLogRoot *types.LogRootV1 var newSLR *trillian.SignedLogRoot err := ls.ReadWriteTransaction(ctx, tree, func(ctx context.Context, tx storage.LogTreeTX) error { stageStart := ts.Now() defer seqBatches.Inc(label) defer func() { seqLatency.Observe(clock.SecondsSince(ts, start), label) }() // Get the latest known root from storage sth, err := tx.LatestSignedLogRoot(ctx) if err != nil || sth == nil { return fmt.Errorf("%v: Sequencer failed to get latest root: %v", tree.TreeId, err) } // There is no trust boundary between the signer and the // database, so we skip signature verification. // TODO(gbelvin): Add signature checking as a santity check. var currentRoot types.LogRootV1 if err := currentRoot.UnmarshalBinary(sth.LogRoot); err != nil { return fmt.Errorf("%v: Sequencer failed to unmarshal latest root: %v", tree.TreeId, err) } seqGetRootLatency.Observe(clock.SecondsSince(ts, stageStart), label) seqTreeSize.Set(float64(currentRoot.TreeSize), label) if currentRoot.RootHash == nil { klog.Warningf("%v: Fresh log - no previous TreeHeads exist.", tree.TreeId) return storage.ErrTreeNeedsInit } taskData := &sequencingTaskData{ label: label, treeSize: currentRoot.TreeSize, timeSource: ts, tx: tx, } var st sequencingTask switch tree.TreeType { case trillian.TreeType_LOG: st = (*logSequencingTask)(taskData) case trillian.TreeType_PREORDERED_LOG: st = (*preorderedLogSequencingTask)(taskData) default: return fmt.Errorf("IntegrateBatch not supported for TreeType %v", tree.TreeType) } sequencedLeaves, err := st.fetch(ctx, limit, start.Add(-guardWindow)) if err != nil { return fmt.Errorf("%v: Sequencer failed to load sequenced batch: %v", tree.TreeId, err) } numLeaves = len(sequencedLeaves) // We need to create a signed root if entries were added or the latest root // is too old. if numLeaves == 0 { nowNanos := ts.Now().UnixNano() interval := time.Duration(nowNanos - int64(currentRoot.TimestampNanos)) if maxRootDurationInterval == 0 || interval < maxRootDurationInterval { // We have nothing to integrate into the tree. klog.V(1).Infof("%v: No leaves sequenced in this signing operation", tree.TreeId) return nil } klog.Infof("%v: Force new root generation as %v since last root", tree.TreeId, interval) } stageStart = ts.Now() cr, err := initCompactRangeFromStorage(ctx, ¤tRoot, tx) if err != nil { return fmt.Errorf("%v: compact range init failed: %v", tree.TreeId, err) } seqInitTreeLatency.Observe(clock.SecondsSince(ts, stageStart), label) stageStart = ts.Now() // We've done all the reads, can now do the updates in the same // transaction. Collate node updates. if err := prepareLeaves(sequencedLeaves, cr.End(), label, ts); err != nil { return err } nodeMap, newRoot, err := updateCompactRange(cr, sequencedLeaves, label) if err != nil { return err } seqWriteTreeLatency.Observe(clock.SecondsSince(ts, stageStart), label) // Store the sequenced batch. if err := st.update(ctx, sequencedLeaves); err != nil { return err } stageStart = ts.Now() // Build objects for the nodes to be updated. Because we deduped via the map // each node can only be created / updated once in each tree revision and // they cannot conflict when we do the storage update. targetNodes := buildNodesFromNodeMap(nodeMap) // Now insert or update the nodes affected by the above, at the new tree // version. if err := tx.SetMerkleNodes(ctx, targetNodes); err != nil { return fmt.Errorf("%v: Sequencer failed to set Merkle nodes: %v", tree.TreeId, err) } seqSetNodesLatency.Observe(clock.SecondsSince(ts, stageStart), label) stageStart = ts.Now() // Create the log root ready for signing. if cr.End() == 0 { // Override the nil root hash returned by the compact range. newRoot = rfc6962.DefaultHasher.EmptyRoot() } newLogRoot = &types.LogRootV1{ RootHash: newRoot, TimestampNanos: uint64(ts.Now().UnixNano()), TreeSize: cr.End(), } seqTreeSize.Set(float64(newLogRoot.TreeSize), label) seqTimestamp.Set(float64(time.Duration(newLogRoot.TimestampNanos)*time.Nanosecond/ time.Millisecond), label) if newLogRoot.TimestampNanos <= currentRoot.TimestampNanos { return fmt.Errorf("%v: refusing to sign root with timestamp earlier than previous root (%d <= %d)", tree.TreeId, newLogRoot.TimestampNanos, currentRoot.TimestampNanos) } logRoot, err := newLogRoot.MarshalBinary() if err != nil { return fmt.Errorf("%v: signer failed to marshal root: %v", tree.TreeId, err) } newSLR := &trillian.SignedLogRoot{LogRoot: logRoot} if err := tx.StoreSignedLogRoot(ctx, newSLR); err != nil { return fmt.Errorf("%v: failed to write updated tree root: %v", tree.TreeId, err) } seqStoreRootLatency.Observe(clock.SecondsSince(ts, stageStart), label) return nil }) if err != nil { return 0, err } // Let quota.Manager know about newly-sequenced entries. replenishQuota(ctx, numLeaves, tree.TreeId, qm) seqCounter.Add(float64(numLeaves), label) if newSLR != nil { klog.Infof("%v: sequenced %v leaves, size %v", tree.TreeId, numLeaves, newLogRoot.TreeSize) } return numLeaves, nil } // replenishQuota replenishes all quotas, such as {Tree/Global, Read/Write}, // that are possibly influenced by sequencing numLeaves entries for the passed // in tree ID. Implementations are tasked with filtering quotas that shouldn't // be replenished. // // TODO(codingllama): Consider adding a source-aware replenish method (e.g., // qm.Replenish(ctx, tokens, specs, quota.SequencerSource)), so there's no // ambiguity as to where the tokens come from. func replenishQuota(ctx context.Context, numLeaves int, treeID int64, qm quota.Manager) { if numLeaves > 0 { tokens := int(float64(numLeaves) * quotaIncreaseFactor()) specs := []quota.Spec{ {Group: quota.Tree, Kind: quota.Read, TreeID: treeID}, {Group: quota.Tree, Kind: quota.Write, TreeID: treeID}, {Group: quota.Global, Kind: quota.Read}, {Group: quota.Global, Kind: quota.Write}, } klog.V(2).Infof("%v: replenishing %d tokens (numLeaves = %d)", treeID, tokens, numLeaves) err := qm.PutTokens(ctx, tokens, specs) if err != nil { klog.Warningf("%v: failed to replenish %d tokens: %v", treeID, tokens, err) } quota.Metrics.IncReplenished(tokens, specs, err == nil) } } trillian-1.6.1/log/sequencer_manager.go000066400000000000000000000044261466362047600201550ustar00rootroot00000000000000// 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 log import ( "context" "fmt" "time" "github.com/google/trillian" "github.com/google/trillian/extension" "github.com/google/trillian/trees" "k8s.io/klog/v2" ) // SequencerManager provides sequencing operations for a collection of Logs. type SequencerManager struct { guardWindow time.Duration registry extension.Registry } var seqOpts = trees.NewGetOpts(trees.SequenceLog, trillian.TreeType_LOG, trillian.TreeType_PREORDERED_LOG) // NewSequencerManager creates a new SequencerManager instance based on the provided KeyManager instance // and guard window. func NewSequencerManager(registry extension.Registry, gw time.Duration) *SequencerManager { InitMetrics(registry.MetricFactory) return &SequencerManager{ guardWindow: gw, registry: registry, } } // ExecutePass performs sequencing for the specified Log. func (s *SequencerManager) ExecutePass(ctx context.Context, logID int64, info *OperationInfo) (int, error) { // TODO(Martin2112): Honor the sequencing enabled in log parameters, needs an API change // so deferring it tree, err := trees.GetTree(ctx, s.registry.AdminStorage, logID, seqOpts) if err != nil { return 0, fmt.Errorf("error retrieving log %v: %v", logID, err) } ctx = trees.NewContext(ctx, tree) maxRootDuration := tree.MaxRootDuration.AsDuration() if !tree.MaxRootDuration.IsValid() { klog.Warning("failed to parse tree.MaxRootDuration, using zero") maxRootDuration = 0 } leaves, err := IntegrateBatch(ctx, tree, info.BatchSize, s.guardWindow, maxRootDuration, info.TimeSource, s.registry.LogStorage, s.registry.QuotaManager) if err != nil { return 0, fmt.Errorf("failed to integrate batch for %v: %v", logID, err) } return leaves, nil } trillian-1.6.1/log/sequencer_manager_test.go000066400000000000000000000206301466362047600212070ustar00rootroot00000000000000// 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 log import ( "context" "fmt" "testing" "time" "github.com/golang/mock/gomock" "github.com/google/go-cmp/cmp" "github.com/google/trillian" "github.com/google/trillian/extension" "github.com/google/trillian/quota" "github.com/google/trillian/storage" stestonly "github.com/google/trillian/storage/testonly" "github.com/google/trillian/storage/tree" "github.com/google/trillian/testonly" "github.com/google/trillian/types" "github.com/google/trillian/util/clock" "github.com/transparency-dev/merkle/compact" "github.com/transparency-dev/merkle/rfc6962" "google.golang.org/protobuf/proto" ) // Arbitrary time for use in tests var fakeTimeSource = clock.NewFake(fakeTime) // We use a size zero tree for testing, Merkle tree state restore is tested elsewhere var ( leaf0Hash = rfc6962.DefaultHasher.HashLeaf([]byte{}) testLeaf0 = &trillian.LogLeaf{ MerkleLeafHash: leaf0Hash, LeafValue: nil, ExtraData: nil, LeafIndex: 0, } ) var testLeaf0Updated = &trillian.LogLeaf{ MerkleLeafHash: testonly.MustDecodeBase64("bjQLnP+zepicpUTmu3gKLHiQHT+zNzh2hRGjBhevoB0="), LeafValue: nil, ExtraData: nil, LeafIndex: 0, IntegrateTimestamp: testonly.MustToTimestampProto(fakeTime), } var ( testRoot0 = &types.LogRootV1{ TreeSize: 0, RootHash: []byte{}, } testRoot0Bytes, _ = testRoot0.MarshalBinary() testSignedRoot0 = &trillian.SignedLogRoot{LogRoot: testRoot0Bytes} updatedNodes0 = []tree.Node{{ID: compact.NewNodeID(0, 0), Hash: testonly.MustDecodeBase64("bjQLnP+zepicpUTmu3gKLHiQHT+zNzh2hRGjBhevoB0=")}} updatedRoot = &types.LogRootV1{ TimestampNanos: uint64(fakeTime.UnixNano()), RootHash: []byte{110, 52, 11, 156, 255, 179, 122, 152, 156, 165, 68, 230, 187, 120, 10, 44, 120, 144, 29, 63, 179, 55, 56, 118, 133, 17, 163, 6, 23, 175, 160, 29}, TreeSize: 1, } updatedRootBytes, _ = updatedRoot.MarshalBinary() updatedSignedRoot = &trillian.SignedLogRoot{LogRoot: updatedRootBytes} ) var zeroDuration = 0 * time.Second func TestSequencerManagerSingleLogNoLeaves(t *testing.T) { ctx := context.Background() mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() logID := stestonly.LogTree.GetTreeId() mockAdminTx := storage.NewMockReadOnlyAdminTX(mockCtrl) mockAdmin := &stestonly.FakeAdminStorage{ReadOnlyTX: []storage.ReadOnlyAdminTX{mockAdminTx}} mockTx := storage.NewMockLogTreeTX(mockCtrl) fakeStorage := &stestonly.FakeLogStorage{TX: mockTx} mockTx.EXPECT().Commit(gomock.Any()).Return(nil) mockTx.EXPECT().Close().Return(nil) mockTx.EXPECT().LatestSignedLogRoot(gomock.Any()).Return(testSignedRoot0, nil) mockTx.EXPECT().DequeueLeaves(gomock.Any(), 50, fakeTime).Return([]*trillian.LogLeaf{}, nil) mockAdminTx.EXPECT().GetTree(gomock.Any(), logID).Return(stestonly.LogTree, nil) mockAdminTx.EXPECT().Commit().Return(nil) mockAdminTx.EXPECT().Close().Return(nil) registry := extension.Registry{ AdminStorage: mockAdmin, LogStorage: fakeStorage, QuotaManager: quota.Noop(), } sm := NewSequencerManager(registry, zeroDuration) if _, err := sm.ExecutePass(ctx, logID, createTestInfo(registry)); err != nil { t.Error(err) } } func TestSequencerManagerCachesSigners(t *testing.T) { ctx := context.Background() mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() logID := stestonly.LogTree.GetTreeId() mockAdminTx := storage.NewMockReadOnlyAdminTX(mockCtrl) mockAdmin := &stestonly.FakeAdminStorage{} mockTx := storage.NewMockLogTreeTX(mockCtrl) fakeStorage := &stestonly.FakeLogStorage{} registry := extension.Registry{ AdminStorage: mockAdmin, LogStorage: fakeStorage, QuotaManager: quota.Noop(), } sm := NewSequencerManager(registry, zeroDuration) // Expect two sequencing passes. for i := 0; i < 2; i++ { mockAdmin.ReadOnlyTX = []storage.ReadOnlyAdminTX{mockAdminTx} gomock.InOrder( mockAdminTx.EXPECT().GetTree(gomock.Any(), logID).Return(stestonly.LogTree, nil), mockAdminTx.EXPECT().Commit().Return(nil), mockAdminTx.EXPECT().Close().Return(nil), ) fakeStorage.TX = mockTx gomock.InOrder( mockTx.EXPECT().LatestSignedLogRoot(gomock.Any()).Return(testSignedRoot0, nil), mockTx.EXPECT().DequeueLeaves(gomock.Any(), 50, fakeTime).Return([]*trillian.LogLeaf{}, nil), mockTx.EXPECT().Commit(gomock.Any()).Return(nil), mockTx.EXPECT().Close().Return(nil), ) if _, err := sm.ExecutePass(ctx, logID, createTestInfo(registry)); err != nil { t.Fatal(err) } } } func TestSequencerManagerSingleLogOneLeaf(t *testing.T) { ctx := context.Background() mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() logID := stestonly.LogTree.GetTreeId() mockAdminTx := storage.NewMockReadOnlyAdminTX(mockCtrl) mockAdmin := &stestonly.FakeAdminStorage{ReadOnlyTX: []storage.ReadOnlyAdminTX{mockAdminTx}} mockTx := storage.NewMockLogTreeTX(mockCtrl) fakeStorage := &stestonly.FakeLogStorage{TX: mockTx} // Set up enough mockery to be able to sequence. We don't test all the error paths // through sequencer as other tests cover this mockTx.EXPECT().Commit(gomock.Any()).Return(nil) mockTx.EXPECT().Close().Return(nil) mockTx.EXPECT().DequeueLeaves(gomock.Any(), 50, fakeTime).Return([]*trillian.LogLeaf{testLeaf0}, nil) mockTx.EXPECT().LatestSignedLogRoot(gomock.Any()).Return(testSignedRoot0, nil) mockTx.EXPECT().UpdateSequencedLeaves(gomock.Any(), cmpMatcher{[]*trillian.LogLeaf{testLeaf0Updated}}).Return(nil) mockTx.EXPECT().SetMerkleNodes(gomock.Any(), updatedNodes0).Return(nil) mockTx.EXPECT().StoreSignedLogRoot(gomock.Any(), cmpMatcher{updatedSignedRoot}).Return(nil) mockAdminTx.EXPECT().GetTree(gomock.Any(), logID).Return(stestonly.LogTree, nil) mockAdminTx.EXPECT().Commit().Return(nil) mockAdminTx.EXPECT().Close().Return(nil) registry := extension.Registry{ AdminStorage: mockAdmin, LogStorage: fakeStorage, QuotaManager: quota.Noop(), } sm := NewSequencerManager(registry, zeroDuration) if _, err := sm.ExecutePass(ctx, logID, createTestInfo(registry)); err != nil { t.Error(err) } } // cmpMatcher is a custom gomock.Matcher that uses cmp.Equal combined with a // cmp.Comparer that knows how to properly compare proto.Message types. type cmpMatcher struct{ want interface{} } func (m cmpMatcher) Matches(got interface{}) bool { return cmp.Equal(got, m.want, cmp.Comparer(proto.Equal)) } func (m cmpMatcher) String() string { return fmt.Sprintf("equals %v", m.want) } func TestSequencerManagerGuardWindow(t *testing.T) { ctx := context.Background() mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() logID := stestonly.LogTree.GetTreeId() mockAdminTx := storage.NewMockReadOnlyAdminTX(mockCtrl) mockAdmin := &stestonly.FakeAdminStorage{ReadOnlyTX: []storage.ReadOnlyAdminTX{mockAdminTx}} mockTx := storage.NewMockLogTreeTX(mockCtrl) fakeStorage := &stestonly.FakeLogStorage{TX: mockTx} mockTx.EXPECT().Commit(gomock.Any()).Return(nil) mockTx.EXPECT().Close().Return(nil) mockTx.EXPECT().LatestSignedLogRoot(gomock.Any()).Return(testSignedRoot0, nil) // Expect a 5 second guard window to be passed from manager -> sequencer -> storage mockTx.EXPECT().DequeueLeaves(gomock.Any(), 50, fakeTime.Add(-time.Second*5)).Return([]*trillian.LogLeaf{}, nil) mockAdminTx.EXPECT().GetTree(gomock.Any(), logID).Return(stestonly.LogTree, nil) mockAdminTx.EXPECT().Commit().Return(nil) mockAdminTx.EXPECT().Close().Return(nil) registry := extension.Registry{ AdminStorage: mockAdmin, LogStorage: fakeStorage, QuotaManager: quota.Noop(), } sm := NewSequencerManager(registry, time.Second*5) if _, err := sm.ExecutePass(ctx, logID, createTestInfo(registry)); err != nil { t.Error(err) } } func createTestInfo(registry extension.Registry) *OperationInfo { // Set sign interval to 100 years so it won't trigger a root expiry signing unless overridden return &OperationInfo{ Registry: registry, BatchSize: 50, RunInterval: time.Second, TimeSource: fakeTimeSource, } } trillian-1.6.1/log/sequencer_test.go000066400000000000000000000520171466362047600175210ustar00rootroot00000000000000// 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 log import ( "context" "errors" "fmt" "strings" "testing" "time" "github.com/golang/mock/gomock" "github.com/google/trillian" "github.com/google/trillian/quota" "github.com/google/trillian/storage" "github.com/google/trillian/testonly" "github.com/google/trillian/types" "github.com/google/trillian/util/clock" "github.com/transparency-dev/merkle/compact" "github.com/transparency-dev/merkle/rfc6962" stestonly "github.com/google/trillian/storage/testonly" "github.com/google/trillian/storage/tree" ) var ( fakeTime = time.Date(2016, 5, 25, 10, 55, 5, 0, time.UTC) // These can be shared between tests as they're never modified testLeaf16Data = []byte("testdataforleaf") testLeaf16Hash = rfc6962.DefaultHasher.HashLeaf(testLeaf16Data) testLeaf16 = &trillian.LogLeaf{ MerkleLeafHash: testLeaf16Hash, LeafValue: testLeaf16Data, ExtraData: nil, LeafIndex: 16, IntegrateTimestamp: testonly.MustToTimestampProto(fakeTime), } testLeaf21 = &trillian.LogLeaf{ MerkleLeafHash: testLeaf16Hash, LeafValue: testLeaf16Data, ExtraData: nil, LeafIndex: 21, IntegrateTimestamp: testonly.MustToTimestampProto(fakeTime), } testRoot16 = &types.LogRootV1{ TreeSize: 16, // RootHash can't be nil because that's how the sequencer currently // detects that there was no stored tree head. RootHash: []byte{}, TimestampNanos: uint64(fakeTime.Add(-10 * time.Millisecond).UnixNano()), } // TODO(pavelkalinnikov): Put a well-formed hash here. The current one is // taken from testRoot16 and retained for regression purposes. compactTree16 = []tree.Node{{ID: compact.NewNodeID(4, 0), Hash: []byte{}}} testSignedRoot16 = makeSLR(testRoot16) newSignedRoot16 = makeSLR(&types.LogRootV1{ TimestampNanos: uint64(fakeTime.UnixNano()), TreeSize: testRoot16.TreeSize, RootHash: testRoot16.RootHash, }) testRoot17 = &types.LogRootV1{ TreeSize: 16, // RootHash can't be nil because that's how the sequencer currently // detects that there was no stored tree head. RootHash: []byte{}, TimestampNanos: uint64(fakeTime.UnixNano()), } testSignedRoot17 = makeSLR(testRoot17) testRoot18 = &types.LogRootV1{ TreeSize: 16, // RootHash can't be nil because that's how the sequencer currently // detects that there was no stored tree head. RootHash: []byte{}, TimestampNanos: uint64(fakeTime.Add(10 * time.Millisecond).UnixNano()), } testSignedRoot18 = makeSLR(testRoot18) // These will be accepted in either order because of custom sorting in the mock updatedNodes = []tree.Node{ { ID: compact.NewNodeID(0, 16), Hash: testonly.MustDecodeBase64("L5Iyd7aFOVewxiRm29xD+EU+jvEo4RfufBijKdflWMk="), }, } testRoot = &types.LogRootV1{ RootHash: []byte{71, 158, 195, 172, 164, 198, 185, 151, 99, 8, 213, 227, 191, 162, 39, 26, 185, 184, 172, 0, 75, 58, 127, 114, 90, 151, 71, 153, 131, 168, 47, 5}, TimestampNanos: uint64(fakeTime.UnixNano()), TreeSize: 17, } testSignedRoot = makeSLR(testRoot) // TODO(pavelkalinnikov): Generate boilerplate structures, like the ones // below, in a more compact way. testRoot21 = &types.LogRootV1{ TreeSize: 21, RootHash: testonly.MustDecodeBase64("lfLXEAeBNB/zX1+97lInoqpnLJtX+AS/Ok0mwlWFpRc="), TimestampNanos: uint64(fakeTime.Add(-10 * time.Millisecond).UnixNano()), } testSignedRoot21 = makeSLR(testRoot21) // Nodes that will be loaded when updating the tree of size 21. compactTree21 = []tree.Node{ {ID: compact.NewNodeID(4, 0), Hash: testonly.MustDecodeBase64("CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC=")}, {ID: compact.NewNodeID(2, 4), Hash: testonly.MustDecodeBase64("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=")}, {ID: compact.NewNodeID(0, 20), Hash: testonly.MustDecodeBase64("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")}, } // Nodes that will be stored after updating the tree of size 21. updatedNodes21 = []tree.Node{ { ID: compact.NewNodeID(1, 10), Hash: testonly.MustDecodeBase64("S55qEsQMx90/eq1fSb87pYCB9WIYL7hBgiTY+B9LmPw="), }, { ID: compact.NewNodeID(0, 21), Hash: testonly.MustDecodeBase64("L5Iyd7aFOVewxiRm29xD+EU+jvEo4RfufBijKdflWMk="), }, } // The new root after updating the tree of size 21. updatedRoot21 = &types.LogRootV1{ RootHash: testonly.MustDecodeBase64("1oUtLDlyOWXLHLAvL3NvWaO4D9kr0oQYScylDlgjey4="), TimestampNanos: uint64(fakeTime.UnixNano()), TreeSize: 22, } updatedSignedRoot21 = makeSLR(updatedRoot21) emptyRoot = &types.LogRootV1{ TimestampNanos: uint64(fakeTime.Add(-10 * time.Millisecond).UnixNano()), TreeSize: 0, RootHash: rfc6962.DefaultHasher.EmptyRoot(), } signedEmptyRoot = makeSLR(emptyRoot) updatedSignedEmptyRoot = makeSLR(&types.LogRootV1{ TimestampNanos: uint64(fakeTime.UnixNano()), TreeSize: 0, RootHash: rfc6962.DefaultHasher.EmptyRoot(), }) ) func makeSLR(root *types.LogRootV1) *trillian.SignedLogRoot { logRoot, _ := root.MarshalBinary() return &trillian.SignedLogRoot{LogRoot: logRoot} } // testParameters bundles up values needed for setting mock expectations in tests type testParameters struct { logID int64 beginFails bool dequeueLimit int shouldCommit bool commitFails bool commitError error skipDequeue bool dequeuedLeaves []*trillian.LogLeaf dequeuedError error latestSignedRootError error latestSignedRoot *trillian.SignedLogRoot merkleNodesGet *[]tree.Node merkleNodesGetError error updatedLeaves *[]*trillian.LogLeaf updatedLeavesError error merkleNodesSet *[]tree.Node merkleNodesSetError error skipStoreSignedRoot bool storeSignedRoot *trillian.SignedLogRoot storeSignedRootError error overrideDequeueTime *time.Time // qm is the quota.Manager to be used. If nil, quota.Noop() is used instead. qm quota.Manager } // Tests get their own mock context so they can be run in parallel safely type testContext struct { mockTx *storage.MockLogTreeTX fakeStorage storage.LogStorage qm quota.Manager timeSource clock.TimeSource } // This gets modified so tests need their own copies func getLeaf42() *trillian.LogLeaf { return &trillian.LogLeaf{ MerkleLeafHash: testLeaf16Hash, LeafValue: testLeaf16Data, ExtraData: nil, LeafIndex: 42, } } func createTestContext(ctrl *gomock.Controller, params testParameters) (testContext, context.Context) { fakeStorage := &stestonly.FakeLogStorage{} mockTx := storage.NewMockLogTreeTX(ctrl) if params.beginFails { fakeStorage.TXErr = errors.New("TX") } else { mockTx.EXPECT().Close() fakeStorage.TX = mockTx } if params.shouldCommit { if !params.commitFails { mockTx.EXPECT().Commit(gomock.Any()).Return(nil) } else { mockTx.EXPECT().Commit(gomock.Any()).Return(params.commitError) } } // Close is always called, regardless of explicit commits mockTx.EXPECT().Close().AnyTimes().Return(nil) if !params.skipDequeue { if params.overrideDequeueTime != nil { mockTx.EXPECT().DequeueLeaves(gomock.Any(), params.dequeueLimit, *params.overrideDequeueTime).Return(params.dequeuedLeaves, params.dequeuedError) } else { mockTx.EXPECT().DequeueLeaves(gomock.Any(), params.dequeueLimit, fakeTime).Return(params.dequeuedLeaves, params.dequeuedError) } } if params.latestSignedRoot != nil { mockTx.EXPECT().LatestSignedLogRoot(gomock.Any()).Return(params.latestSignedRoot, params.latestSignedRootError) } if params.merkleNodesGet != nil { ids := make([]compact.NodeID, 0, len(*params.merkleNodesGet)) for _, node := range *params.merkleNodesGet { ids = append(ids, node.ID) } mockTx.EXPECT().GetMerkleNodes(gomock.Any(), ids).Return(*params.merkleNodesGet, params.merkleNodesGetError) } if params.updatedLeaves != nil { mockTx.EXPECT().UpdateSequencedLeaves(gomock.Any(), cmpMatcher{*params.updatedLeaves}).Return(params.updatedLeavesError) } if params.merkleNodesSet != nil { mockTx.EXPECT().SetMerkleNodes(gomock.Any(), stestonly.NodeSet(*params.merkleNodesSet)).Return(params.merkleNodesSetError) } if !params.skipStoreSignedRoot { if params.storeSignedRoot != nil { mockTx.EXPECT().StoreSignedLogRoot(gomock.Any(), cmpMatcher{params.storeSignedRoot}).Return(params.storeSignedRootError) } else { // At the moment if we're going to fail the operation we accept any root mockTx.EXPECT().StoreSignedLogRoot(gomock.Any(), gomock.Any()).Return(params.storeSignedRootError) } } qm := params.qm if qm == nil { qm = quota.Noop() } InitMetrics(nil) return testContext{mockTx: mockTx, fakeStorage: fakeStorage, qm: qm, timeSource: clock.NewFake(fakeTime)}, context.Background() } // Tests for sequencer. Currently relies on storage mocks. // TODO(pavelkalinnikov): Consider using real in-memory storage. func TestIntegrateBatch(t *testing.T) { leaves16 := []*trillian.LogLeaf{testLeaf16} guardWindow := time.Second * 10 expectedCutoffTime := fakeTime.Add(-guardWindow) noLeaves := []*trillian.LogLeaf{} noNodes := []tree.Node{} specs := []quota.Spec{ {Group: quota.Tree, Kind: quota.Read, TreeID: 154035}, {Group: quota.Tree, Kind: quota.Write, TreeID: 154035}, {Group: quota.Global, Kind: quota.Read}, {Group: quota.Global, Kind: quota.Write}, } tests := []struct { desc string params testParameters guardWindow time.Duration maxRootDuration time.Duration wantCount int errStr string }{ { desc: "begin-tx-fails", params: testParameters{ logID: 154035, beginFails: true, skipDequeue: true, skipStoreSignedRoot: true, }, errStr: "TX", }, { desc: "nothing-queued-no-max", params: testParameters{ logID: 154035, dequeueLimit: 1, shouldCommit: true, latestSignedRoot: testSignedRoot16, dequeuedLeaves: noLeaves, skipStoreSignedRoot: true, }, }, { desc: "nothing-queued-within-max", params: testParameters{ logID: 154035, dequeueLimit: 1, shouldCommit: true, latestSignedRoot: testSignedRoot16, dequeuedLeaves: noLeaves, skipStoreSignedRoot: true, }, maxRootDuration: 15 * time.Millisecond, }, { desc: "nothing-queued-after-max", params: testParameters{ logID: 154035, dequeueLimit: 1, shouldCommit: true, latestSignedRoot: testSignedRoot16, dequeuedLeaves: noLeaves, merkleNodesGet: &compactTree16, updatedLeaves: &noLeaves, merkleNodesSet: &noNodes, storeSignedRoot: newSignedRoot16, }, maxRootDuration: 9 * time.Millisecond, }, { desc: "nothing-queued-on-max", params: testParameters{ logID: 154035, dequeueLimit: 1, shouldCommit: true, latestSignedRoot: testSignedRoot16, dequeuedLeaves: noLeaves, merkleNodesGet: &compactTree16, updatedLeaves: &noLeaves, merkleNodesSet: &noNodes, storeSignedRoot: newSignedRoot16, }, maxRootDuration: 10 * time.Millisecond, }, { // Tests that the guard interval is being passed to storage correctly. // Actual operation of the window is tested by storage tests. desc: "guard-interval", params: testParameters{ logID: 154035, dequeueLimit: 1, shouldCommit: true, latestSignedRoot: testSignedRoot16, dequeuedLeaves: []*trillian.LogLeaf{}, skipStoreSignedRoot: true, overrideDequeueTime: &expectedCutoffTime, }, guardWindow: guardWindow, }, { desc: "dequeue-fails", params: testParameters{ logID: 154035, dequeueLimit: 1, latestSignedRoot: testSignedRoot16, dequeuedError: errors.New("dequeue"), skipStoreSignedRoot: true, }, errStr: "dequeue", }, { desc: "get-signed-root-fails", params: testParameters{ logID: 154035, dequeueLimit: 1, latestSignedRoot: testSignedRoot16, latestSignedRootError: errors.New("root"), skipDequeue: true, skipStoreSignedRoot: true, }, errStr: "root", }, { desc: "get-merkle-nodes-fails", params: testParameters{ logID: 154035, dequeueLimit: 1, dequeuedLeaves: []*trillian.LogLeaf{getLeaf42()}, latestSignedRoot: testSignedRoot21, merkleNodesGet: &compactTree21, merkleNodesGetError: errors.New("getmerklenodes"), skipStoreSignedRoot: true, }, errStr: "getmerklenodes", }, { desc: "update-seq-leaves-fails", params: testParameters{ logID: 154035, dequeueLimit: 1, dequeuedLeaves: []*trillian.LogLeaf{getLeaf42()}, latestSignedRoot: testSignedRoot16, merkleNodesGet: &compactTree16, updatedLeaves: &leaves16, updatedLeavesError: errors.New("unsequenced"), skipStoreSignedRoot: true, }, errStr: "unsequenced", }, { desc: "set-merkle-nodes-fails", params: testParameters{ logID: 154035, dequeueLimit: 1, dequeuedLeaves: []*trillian.LogLeaf{getLeaf42()}, latestSignedRoot: testSignedRoot16, merkleNodesGet: &compactTree16, updatedLeaves: &leaves16, merkleNodesSet: &updatedNodes, merkleNodesSetError: errors.New("setmerklenodes"), skipStoreSignedRoot: true, }, errStr: "setmerklenodes", }, { desc: "store-root-fails", params: testParameters{ logID: 154035, dequeueLimit: 1, dequeuedLeaves: []*trillian.LogLeaf{getLeaf42()}, latestSignedRoot: testSignedRoot16, merkleNodesGet: &compactTree16, updatedLeaves: &leaves16, merkleNodesSet: &updatedNodes, storeSignedRoot: nil, storeSignedRootError: errors.New("storesignedroot"), }, errStr: "storesignedroot", }, { desc: "commit-fails", params: testParameters{ logID: 154035, dequeueLimit: 1, shouldCommit: true, commitFails: true, commitError: errors.New("commit"), dequeuedLeaves: []*trillian.LogLeaf{getLeaf42()}, latestSignedRoot: testSignedRoot16, merkleNodesGet: &compactTree16, updatedLeaves: &leaves16, merkleNodesSet: &updatedNodes, storeSignedRoot: nil, }, errStr: "commit", }, { desc: "sequence-empty", params: testParameters{ logID: 154035, dequeueLimit: 1, shouldCommit: true, dequeuedLeaves: noLeaves, latestSignedRoot: signedEmptyRoot, updatedLeaves: &noLeaves, merkleNodesSet: &noNodes, storeSignedRoot: updatedSignedEmptyRoot, }, maxRootDuration: 5 * time.Millisecond, }, { desc: "sequence-leaf-16", params: testParameters{ logID: 154035, dequeueLimit: 1, shouldCommit: true, dequeuedLeaves: []*trillian.LogLeaf{getLeaf42()}, latestSignedRoot: testSignedRoot16, merkleNodesGet: &compactTree16, updatedLeaves: &leaves16, merkleNodesSet: &updatedNodes, storeSignedRoot: testSignedRoot, }, wantCount: 1, }, { desc: "sequence-leaf-21", params: testParameters{ logID: 154035, dequeueLimit: 1, shouldCommit: true, dequeuedLeaves: []*trillian.LogLeaf{getLeaf42()}, latestSignedRoot: testSignedRoot21, merkleNodesGet: &compactTree21, updatedLeaves: &[]*trillian.LogLeaf{testLeaf21}, merkleNodesSet: &updatedNodes21, storeSignedRoot: updatedSignedRoot21, }, wantCount: 1, }, { desc: "prev-root-timestamp-equals", params: testParameters{ logID: 154035, dequeueLimit: 1, dequeuedLeaves: []*trillian.LogLeaf{getLeaf42()}, latestSignedRoot: testSignedRoot17, merkleNodesGet: &compactTree16, updatedLeaves: &leaves16, merkleNodesSet: &updatedNodes, skipStoreSignedRoot: true, }, errStr: "refusing to sign root with timestamp earlier than previous root (1464173705000000000 <= 1464173705000000000)", }, { desc: "prev-root-timestamp-in-future", params: testParameters{ logID: 154035, dequeueLimit: 1, dequeuedLeaves: []*trillian.LogLeaf{getLeaf42()}, latestSignedRoot: testSignedRoot18, merkleNodesGet: &compactTree16, updatedLeaves: &leaves16, merkleNodesSet: &updatedNodes, skipStoreSignedRoot: true, }, errStr: "refusing to sign root with timestamp earlier than previous root (1464173705000000000 <= 1464173705010000000)", }, } for _, test := range tests { t.Run(test.desc, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() qm := quota.NewMockManager(ctrl) test.params.qm = qm if test.wantCount > 0 { qm.EXPECT().PutTokens(gomock.Any(), test.wantCount, specs).Return(nil) } c, ctx := createTestContext(ctrl, test.params) tree := &trillian.Tree{TreeId: test.params.logID, TreeType: trillian.TreeType_LOG} got, err := IntegrateBatch(ctx, tree, 1, test.guardWindow, test.maxRootDuration, c.timeSource, c.fakeStorage, c.qm) if err != nil { if test.errStr == "" { t.Errorf("IntegrateBatch(%+v)=%v,%v; want _,nil", test.params, got, err) } else if !strings.Contains(err.Error(), test.errStr) || got != 0 { t.Errorf("IntegrateBatch(%+v)=%v,%v; want 0, error with %q", test.params, got, err, test.errStr) } return } if got != test.wantCount { t.Errorf("IntegrateBatch(%+v)=%v,nil; want %v,nil", test.params, got, test.wantCount) } }) } } func TestIntegrateBatch_PutTokens(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() // Needed to create a signer ts := clock.NewFake(fakeTime) // Needed for IntegrateBatch calls const treeID int64 = 1234 const limit = 1000 const guardWindow = 10 * time.Second const maxRootDuration = 1 * time.Hour // Expected PutTokens specs specs := []quota.Spec{ {Group: quota.Tree, Kind: quota.Read, TreeID: treeID}, {Group: quota.Tree, Kind: quota.Write, TreeID: treeID}, {Group: quota.Global, Kind: quota.Read}, {Group: quota.Global, Kind: quota.Write}, } oneHundredLeaves := make([]*trillian.LogLeaf, 100) for i := range oneHundredLeaves { oneHundredLeaves[i] = &trillian.LogLeaf{ LeafValue: []byte(fmt.Sprintf("leaf-%v", i)), } } InitMetrics(nil) tests := []struct { desc string leaves []*trillian.LogLeaf quotaFactor float64 wantLeaves, wantTokens int }{ {desc: "noLeaves"}, { desc: "singleLeaf", leaves: []*trillian.LogLeaf{getLeaf42()}, wantLeaves: 1, wantTokens: 1, }, { desc: "badFactor", leaves: oneHundredLeaves, quotaFactor: 0.7, // factor <1 is normalized to 1 wantLeaves: 100, wantTokens: 100, }, { desc: "factorOne", leaves: oneHundredLeaves, quotaFactor: 1, wantLeaves: 100, wantTokens: 100, }, { desc: "10%-factor", leaves: oneHundredLeaves, quotaFactor: 1.1, wantLeaves: 100, wantTokens: 110, }, } any := gomock.Any() ctx := context.Background() for _, test := range tests { func() { if test.quotaFactor != 0 { defer func(qf float64) { QuotaIncreaseFactor = qf }(QuotaIncreaseFactor) QuotaIncreaseFactor = test.quotaFactor } // Correctness of operation is tested elsewhere. The focus here is the interaction // between Sequencer and quota.Manager. logTX := storage.NewMockLogTreeTX(ctrl) logTX.EXPECT().DequeueLeaves(any, any, any).Return(test.leaves, nil) logTX.EXPECT().LatestSignedLogRoot(any).Return(testSignedRoot16, nil) if len(test.leaves) != 0 { logTX.EXPECT().GetMerkleNodes(any, any).Return(compactTree16, nil) } logTX.EXPECT().UpdateSequencedLeaves(any, any).AnyTimes().Return(nil) logTX.EXPECT().SetMerkleNodes(any, any).AnyTimes().Return(nil) logTX.EXPECT().StoreSignedLogRoot(any, any).AnyTimes().Return(nil) logTX.EXPECT().Commit(gomock.Any()).Return(nil) logTX.EXPECT().Close().Return(nil) logStorage := &stestonly.FakeLogStorage{TX: logTX} qm := quota.NewMockManager(ctrl) if test.wantTokens > 0 { qm.EXPECT().PutTokens(any, test.wantTokens, specs) } tree := &trillian.Tree{TreeId: treeID, TreeType: trillian.TreeType_LOG} leaves, err := IntegrateBatch(ctx, tree, limit, guardWindow, maxRootDuration, ts, logStorage, qm) if err != nil { t.Errorf("%v: IntegrateBatch() returned err = %v", test.desc, err) return } if leaves != test.wantLeaves { t.Errorf("%v: IntegrateBatch() returned %v leaves, want = %v", test.desc, leaves, test.wantLeaves) } }() } } trillian-1.6.1/merkle/000077500000000000000000000000001466362047600146325ustar00rootroot00000000000000trillian-1.6.1/merkle/coniks/000077500000000000000000000000001466362047600161205ustar00rootroot00000000000000trillian-1.6.1/merkle/coniks/coniks.go000066400000000000000000000107111466362047600177350ustar00rootroot00000000000000// 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 coniks provides CONIKS hashing for maps. package coniks import ( "bytes" "crypto" "encoding/binary" "fmt" "github.com/google/trillian/merkle/smt/node" "k8s.io/klog/v2" ) // Domain separation prefixes var ( leafIdentifier = []byte("L") emptyIdentifier = []byte("E") // Default is the standard CONIKS hasher. Default = New(crypto.SHA512_256) // Some zeroes, to avoid allocating temporary slices. zeroes = make([]byte, 32) ) // Hasher implements the sparse merkle tree hashing algorithm specified in the CONIKS paper. type Hasher struct { crypto.Hash } // New creates a new hashers.TreeHasher using the passed in hash function. func New(h crypto.Hash) *Hasher { return &Hasher{Hash: h} } // EmptyRoot returns the root of an empty tree. func (m *Hasher) EmptyRoot() []byte { panic("EmptyRoot() not defined for coniks.Hasher") } // HashEmpty returns the hash of an empty subtree with the given root. func (m *Hasher) HashEmpty(treeID int64, root node.ID) []byte { depth := int(root.BitLen()) // bytes.Buffer never returns errors so we can ignore them below. buf := bytes.NewBuffer(make([]byte, 0, 32)) h := m.New() buf.Write(emptyIdentifier) _ = binary.Write(buf, binary.BigEndian, uint64(treeID)) m.writeMaskedNodeID(buf, root) _ = binary.Write(buf, binary.BigEndian, uint32(depth)) h.Write(buf.Bytes()) r := h.Sum(nil) if klog.V(5).Enabled() { klog.Infof("HashEmpty(%v): %x", root, r) } return r } // HashLeaf calculate the merkle tree leaf value: // H(Identifier || treeID || depth || index || dataHash) func (m *Hasher) HashLeaf(treeID int64, id node.ID, leaf []byte) []byte { depth := int(id.BitLen()) // bytes.Buffer never returns errors so we can ignore them below. buf := bytes.NewBuffer(make([]byte, 0, 32+len(leaf))) h := m.New() buf.Write(leafIdentifier) _ = binary.Write(buf, binary.BigEndian, uint64(treeID)) m.writeMaskedNodeID(buf, id) _ = binary.Write(buf, binary.BigEndian, uint32(depth)) buf.Write(leaf) h.Write(buf.Bytes()) p := h.Sum(nil) if klog.V(5).Enabled() { klog.Infof("HashLeaf(%v, %s): %x", id, leaf, p) } return p } // HashChildren returns the internal Merkle tree node hash of the two child nodes l and r. // The hashed structure is H(l || r). func (m *Hasher) HashChildren(l, r []byte) []byte { buf := bytes.NewBuffer(make([]byte, 0, 32+len(l)+len(r))) h := m.New() buf.Write(l) buf.Write(r) h.Write(buf.Bytes()) p := h.Sum(nil) if klog.V(5).Enabled() { klog.Infof("HashChildren(%x, %x): %x", l, r, p) } return p } // BitLen returns the number of bits in the hash function. func (m *Hasher) BitLen() int { return m.Size() * 8 } // writeMaskedNodeID writes the node ID bits to the buffer, padded with zero // bits to the byte Size() of the hashes in use by this hasher. // // TODO(pavelkalinnikov): We must not use BitLen() and Size() interchangeably. // The tree height and hash size could be different. // TODO(pavelkalinnikov): Padding with zeroes doesn't buy us anything, as the // depth is also written to the Buffer. func (m *Hasher) writeMaskedNodeID(b *bytes.Buffer, id node.ID) { depth := int(id.BitLen()) if got, want := depth, m.BitLen(); got > want { panic(fmt.Sprintf("depth: %d, want <= %d", got, want)) } prevLen := b.Len() if depth > 0 { // Write the complete bytes. if full := id.FullBytes(); len(full) > 0 { b.WriteString(full) } // Mask off unwanted bits in the last byte, if there is an incomplete one. if last, bits := id.LastByte(); bits != 0 { b.WriteByte(last) } } // Pad to the correct length with zeroes. Allow for future hashers that might // be > 256 bits. // TODO(pavelkalinnikov): YAGNI. Simplify this until that actually happens. for need := prevLen + m.Size() - b.Len(); need > 0; { chunkSize := need if chunkSize > 32 { chunkSize = 32 } // Use the pre-allocated zeroes to avoid allocating them each time. b.Write(zeroes[:chunkSize]) need -= chunkSize } } trillian-1.6.1/merkle/coniks/coniks_test.go000066400000000000000000000332711466362047600210020ustar00rootroot00000000000000// 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 coniks import ( "bytes" "crypto" _ "crypto/sha512" "encoding/hex" "math/big" "testing" "github.com/google/trillian/merkle/smt/node" "github.com/google/trillian/testonly" ) var h2b = testonly.MustHexDecode // Test vectors in this file were computed by first running the tests, then // saving the outputs. This is the reference implementation. Implementations // in other languages use these test vectors to ensure interoperability. // Any changes to these vectors should be carefully coordinated across all // known verifiers including: // - github.com/google/end-to-end/src/javascript/crypto/e2e/transparency func TestBitLen(t *testing.T) { if got, want := Default.BitLen(), 256; got != want { t.Errorf("BitLen(): %v, want %v", got, want) } } func TestHashChildren(t *testing.T) { for _, tc := range []struct { l, r []byte want []byte }{ {nil, nil, h2b("c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a")}, {h2b("00"), h2b("11"), h2b("248423e26b964db547e51deaffefc7e07bea495283dba563d74d69bf06b87497")}, {h2b("11"), h2b("00"), h2b("b5fbd3a07d7d309062044c5f92bc32bb860900f6e2a0e63b034846e267cb25bb")}, {h2b("0000000000000000000000000000000000000000000000000000000000000000"), h2b("1111111111111111111111111111111111111111111111111111111111111111"), h2b("c1c6101db394de0d197b6bd90406fa300d28fee7028c7b37406b16edb61cccb4")}, {h2b("1111111111111111111111111111111111111111111111111111111111111111"), h2b("0000000000000000000000000000000000000000000000000000000000000000"), h2b("4698c0cfa150974ad8d55e9c3f7d20dcdd56e7023931e1f7b5f61fbcf15770c3")}, } { if got, want := Default.HashChildren(tc.l, tc.r), tc.want; !bytes.Equal(got, want) { t.Errorf("HashChildren(%v, %x): %x, want %x", tc.l, tc.r, got, want) } } } func TestHashEmpty(t *testing.T) { for _, tc := range []struct { treeID int64 root node.ID want []byte }{ {0, newID("0000000000000000000000000000000000000000000000000000000000000000", 0), h2b("2b71932d625e7b83ce864f8092ae4eb470670ccff37eaac83f21679bb3b24bbb")}, {1, newID("0000000000000000000000000000000000000000000000000000000000000000", 0), h2b("9a908ed88f429272254a97c6a55f781e15b0cff753fb90ce7591988b398378ea")}, {0, newID("1111111111111111111111111111111111111111111111111111111111111111", 1), h2b("a9804d4c78c33a72903a5dc71a900a00e55136a425b6e4365c2d90f8303eb233")}, {0, newID("0000000000000000000000000000000000000000000000000000000000000000", 256), h2b("af8545ff33b365f2a45971abc45167634c17bfc883ff0280f56e542663b02417")}, } { if got, want := Default.HashEmpty(tc.treeID, tc.root), tc.want; !bytes.Equal(got, want) { t.Errorf("HashEmpty(%v, %v): %x, want %x", tc.treeID, tc.root, got, want) } } } func TestHashLeaf(t *testing.T) { for _, tc := range []struct { treeID int64 id node.ID leaf []byte want []byte }{ {0, newID("0000000000000000000000000000000000000000000000000000000000000000", 256), nil, h2b("b4e04ff32be7f76c9621dd28946c261dd8aea6494bf713c03da75dd9f1ce2fec")}, {0, newID("0000000000000000000000000000000000000000000000000000000000000000", 256), []byte(""), h2b("b4e04ff32be7f76c9621dd28946c261dd8aea6494bf713c03da75dd9f1ce2fec")}, {1, newID("0000000000000000000000000000000000000000000000000000000000000000", 256), []byte(""), h2b("83800c063525c35afdfe60733fb631be976d06835f79e9914dd268ec2f313721")}, {0, newID("1111111111111111111111111111111111111111111111111111111111111111", 256), []byte(""), h2b("4a95b36a21da32aba1a32d05ae3a1ef200f896c82a7c5ad03da52b17fdcbeb37")}, {0, newID("0000000000000000000000000000000000000000000000000000000000000000", 256), []byte("foo"), h2b("d1f7b835e5ed66fc564b5a9e0a7ca028a6e4ec85a6c7d9d96b2ad6d0c1369700")}, // Test vector from Key Transparency {0, newID("1111111111111111111111111111111111111111111111111111111111111111", 256), []byte("leaf"), h2b("87f51e6ceb5a46947fedbd1de543482fb72f7459055d853a841566ef8e43c4a2")}, } { leafHash := Default.HashLeaf(tc.treeID, tc.id, tc.leaf) if got, want := leafHash, tc.want; !bytes.Equal(got, want) { t.Errorf("HashLeaf(%v, %v, %s): %x, want %x", tc.treeID, tc.id, tc.leaf, got, want) } } } func TestWriteMaskedNodeID(t *testing.T) { h := &Hasher{crypto.SHA1} // Use a shorter hash for shorter test vectors. for _, tc := range []struct { node node.ID want []byte }{ {node: newID("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 0), want: h2b("0000000000000000000000000000000000000000")}, {node: newID("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 1), want: h2b("8000000000000000000000000000000000000000")}, {node: newID("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 2), want: h2b("C000000000000000000000000000000000000000")}, {node: newID("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 3), want: h2b("E000000000000000000000000000000000000000")}, {node: newID("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 4), want: h2b("F000000000000000000000000000000000000000")}, {node: newID("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 5), want: h2b("F800000000000000000000000000000000000000")}, {node: newID("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 6), want: h2b("FC00000000000000000000000000000000000000")}, {node: newID("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 7), want: h2b("FE00000000000000000000000000000000000000")}, {node: newID("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 8), want: h2b("FF00000000000000000000000000000000000000")}, {node: newID("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 9), want: h2b("FF80000000000000000000000000000000000000")}, {node: newID("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 10), want: h2b("FFC0000000000000000000000000000000000000")}, {node: newID("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 159), want: h2b("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE")}, {node: newID("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 160), want: h2b("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")}, {node: newID("000102030405060708090A0B0C0D0E0F10111213", 1), want: h2b("0000000000000000000000000000000000000000")}, {node: newID("000102030405060708090A0B0C0D0E0F10111213", 17), want: h2b("0001000000000000000000000000000000000000")}, {node: newID("000102030405060708090A0B0C0D0E0F10111213", 159), want: h2b("000102030405060708090A0B0C0D0E0F10111212")}, {node: newID("000102030405060708090A0B0C0D0E0F10111213", 160), want: h2b("000102030405060708090A0B0C0D0E0F10111213")}, } { buf := new(bytes.Buffer) h.writeMaskedNodeID(buf, tc.node) if got, want := buf.Bytes(), tc.want; !bytes.Equal(got, want) { t.Errorf("writeMaskedNodeID(%v): %x, want %x", tc.node, got, want) } } } func TestWriteMaskedNodeID512Bits(t *testing.T) { h := &Hasher{crypto.SHA512} // Use a hasher with > 32 byte length. for _, tc := range []struct { node node.ID want []byte }{ { node: newID("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 0), want: h2b("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), }, { node: newID("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 1), want: h2b("80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), }, { node: newID("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 2), want: h2b("C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), }, { node: newID("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 3), want: h2b("E0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), }, { node: newID("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 4), want: h2b("F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), }, { node: newID("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 5), want: h2b("F8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), }, { node: newID("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 6), want: h2b("FC000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), }, { node: newID("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 7), want: h2b("FE000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), }, { node: newID("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 8), want: h2b("FF000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), }, { node: newID("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 9), want: h2b("FF800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), }, { node: newID("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 10), want: h2b("FFC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), }, { node: newID("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 159), want: h2b("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), }, { node: newID("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 160), want: h2b("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), }, { node: newID("000102030405060708090A0B0C0D0E0F10111213FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 1), want: h2b("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), }, { node: newID("000102030405060708090A0B0C0D0E0F10111213FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 159), want: h2b("000102030405060708090A0B0C0D0E0F101112120000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), }, { node: newID("000102030405060708090A0B0C0D0E0F10111213FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 160), want: h2b("000102030405060708090A0B0C0D0E0F101112130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), }, { node: newID("000102030405060708090A0B0C0D0E0F10111213FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 167), want: h2b("000102030405060708090A0B0C0D0E0F10111213FE00000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), }, { node: newID("000102030405060708090A0B0C0D0E0F10111213FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 168), want: h2b("000102030405060708090A0B0C0D0E0F10111213FF00000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), }, } { buf := new(bytes.Buffer) h.writeMaskedNodeID(buf, tc.node) if got, want := buf.Bytes(), tc.want; !bytes.Equal(got, want) { t.Errorf("writeMaskedNodeID(%v): %x, want %x", tc.node, got, want) } } } func TestWriteMaskedNodeIDBits(t *testing.T) { allFF := h2b("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF") h := &Hasher{crypto.SHA512} // Use a hasher with > 32 byte length. ref := new(big.Int) // Go through all the bits, set them one at a time in the big.Int and compare // the results against writeMaskedNodeID with a depth of that many set bits. val := make([]byte, len(allFF)) for b := 1; b < 512; b++ { ref.SetBit(ref, 512-b, 1) copy(val, allFF) buf := new(bytes.Buffer) node := node.NewID(string(val), uint(b)) h.writeMaskedNodeID(buf, node) if got, want := buf.Bytes(), ref.Bytes(); !bytes.Equal(got, want) { t.Errorf("bit: %d got: %s, want: %s", b, hex.EncodeToString(got), hex.EncodeToString(want)) } } } func newID(hex string, bits uint) node.ID { return node.NewID(string(h2b(hex)), bits) } trillian-1.6.1/merkle/smt/000077500000000000000000000000001466362047600154355ustar00rootroot00000000000000trillian-1.6.1/merkle/smt/hasher.go000066400000000000000000000027611466362047600172440ustar00rootroot00000000000000// 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 smt import "github.com/google/trillian/merkle/smt/node" // Hasher provides sparse Merkle tree hash functions. type Hasher interface { // HashEmpty returns the hash of an empty subtree with the given root. Note // that the empty node ID indicates the root of the entire tree. HashEmpty(treeID int64, root node.ID) []byte // HashChildren returns the node hash based on its children node hashes. HashChildren(l, r []byte) []byte } // mapHasher is a wrapper around Hasher bound to a specific tree ID. type mapHasher struct { mh Hasher treeID int64 } // bindHasher returns a mapHasher binding the given hasher to a tree ID. func bindHasher(hasher Hasher, treeID int64) mapHasher { return mapHasher{mh: hasher, treeID: treeID} } // hashEmpty returns the hash of an empty subtree with the given root ID. func (h mapHasher) hashEmpty(id node.ID) []byte { return h.mh.HashEmpty(h.treeID, id) } trillian-1.6.1/merkle/smt/hstar3.go000066400000000000000000000153161466362047600171760ustar00rootroot00000000000000// 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 smt contains the implementation of the sparse Merkle tree logic. package smt import ( "fmt" "github.com/google/trillian/merkle/smt/node" ) // NodeAccessor provides read and write access to Merkle tree node hashes. // // The Update algorithm uses it to read the existing nodes of the tree and // write the nodes that are updated. The nodes that it writes do not intersect // with the nodes that it reads. type NodeAccessor interface { // Get returns the hash of the given node. Returns an error if the hash is // undefined, or can't be obtained for another reason. Get(id node.ID) ([]byte, error) // Set sets the hash of the given node. Set(id node.ID, hash []byte) } // HashChildrenFn computes a node hash based on its child nodes' hashes. type HashChildrenFn func(left, right []byte) []byte // HStar3 is a faster non-recursive HStar2. type HStar3 struct { nodes []Node hash HashChildrenFn depth uint top uint } // NewHStar3 returns a new instance of HStar3 for the given set of node hash // updates at the specified tree depth. This HStar3 is capable of propagating // these updates up to the passed-in top level of the tree. // // Warning: This call and other HStar3 methods modify the nodes slice in-place, // so the caller must ensure to not reuse it. func NewHStar3(nodes []Node, hash HashChildrenFn, depth, top uint) (HStar3, error) { if err := Prepare(nodes, depth); err != nil { return HStar3{}, err } else if top > depth { return HStar3{}, fmt.Errorf("top > depth: %d vs. %d", top, depth) } return HStar3{nodes: nodes, hash: hash, depth: depth, top: top}, nil } // Prepare returns the list of all the node IDs that the Update method will // load in order to compute node hash updates from the initial tree depth up to // the top level specified in the constructor. The ordering of the returned IDs // is arbitrary. This method may be useful for creating a NodeAccessor, e.g. // by batch-reading the nodes from elsewhere. // // TODO(pavelkalinnikov): Return only tile IDs. func (h HStar3) Prepare() []node.ID { // Start with a single "sentinel" empty ID, which helps maintaining the loop // invariants below. Preallocate enough memory to store all the node IDs. ids := make([]node.ID, 1, len(h.nodes)*int(h.depth-h.top)+1) pos := make([]int, h.depth-h.top) // Note: This variable compares equal to ids[0]. empty := node.ID{} // For each node, add all its ancestors' siblings, down to the given depth. // Avoid duplicate IDs, and possibly remove already added ones if they become // unnecessary as more updates are added. // // Loop invariants: // 1. pos[idx] < len(ids), for each idx. // 2. ids[pos[idx]] is the ID of the rightmost sibling at depth idx+h.top+1 // added so far, or an empty ID if there is none at this depth yet. // // Note: The algorithm works because the list of updates is sorted. for _, n := range h.nodes { for id, d := n.ID, h.depth; d > h.top; d-- { pref := id.Prefix(d) idx := d - h.top - 1 if p := pos[idx]; ids[p] == pref { // Delete that node because its original hash will be overridden, so it // does not contribute to hash updates anymore. ids[p] = empty // Skip the upper siblings as they have been added already. break } pos[idx] = len(ids) ids = append(ids, pref.Sibling()) } } // Delete all empty IDs, which include the 0-th "sentinel" ID and the ones // that were marked as such in the loop above. newLen := 0 for i := range ids { if ids[i] != empty { ids[newLen] = ids[i] newLen++ } } return ids[:newLen] } // Update applies the updates to the sparse Merkle tree. Returns an error if // any of the NodeAccessor.Get calls does so, e.g. if a node is undefined. // Warning: HStar3 must not be used further after this call. // // Returns the slice of updates at the top level of the sparse Merkle tree // induced by the provided lower level updates. Typically it will contain only // one item for the root hash of a tile or a (sub)tree, but the caller may // arrange multiple subtrees in one HStar3, in which case the corresponding // returned top-level updates will be sorted lexicographically by node ID. // // Note that Update invocations can be chained. For example, a bunch of HStar3 // instances at depth 256 can return updates for depth 8 (in parallel), which // are then merged together and passed into another HStar3 at depth 8 which // computes the overall tree root update. // // For that reason, Update doesn't invoke NodeAccessor.Set for the topmost // nodes. If it did then chained Updates would Set the borderline nodes twice. func (h HStar3) Update(na NodeAccessor) ([]Node, error) { for d := h.depth; d > h.top; d-- { var err error if h.nodes, err = h.updateAt(h.nodes, d, na); err != nil { return nil, fmt.Errorf("depth %d: %v", d, err) } } return h.nodes, nil } // updateAt applies the given node updates at the specified tree level. // Returns the updates that propagated to the level above. func (h HStar3) updateAt(nodes []Node, depth uint, na NodeAccessor) ([]Node, error) { // Apply the updates. for _, n := range nodes { na.Set(n.ID, n.Hash) } // Calculate the updates that propagate to one level above. The result of // this is a slice of newLen items, between len/2 and len. The length shrinks // whenever two updated nodes share the same parent. newLen := 0 for i, ln := 0, len(nodes); i < ln; i++ { sib := nodes[i].ID.Sibling() var left, right []byte if next := i + 1; next < ln && nodes[next].ID == sib { // The sibling is the right child here, as nodes are sorted. left, right = nodes[i].Hash, nodes[next].Hash i = next // Skip the next update in the outer loop. } else { // The sibling is not updated, so fetch the original from NodeAccessor. hash, err := na.Get(sib) if err != nil { return nil, err } left, right = nodes[i].Hash, hash if isLeftChild(sib) { left, right = right, left } } hash := h.hash(left, right) nodes[newLen] = Node{ID: sib.Prefix(depth - 1), Hash: hash} newLen++ } return nodes[:newLen], nil } // isLeftChild returns whether the given node is a left child. func isLeftChild(id node.ID) bool { last, bits := id.LastByte() return last&(1<<(8-bits)) == 0 } trillian-1.6.1/merkle/smt/hstar3_test.go000066400000000000000000000147321466362047600202360ustar00rootroot00000000000000// 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 smt import ( "crypto" "encoding/hex" "fmt" "math/rand" "reflect" "strings" "testing" "github.com/google/trillian/merkle/coniks" "github.com/google/trillian/merkle/smt/node" ) type emptyNodes struct { h mapHasher ids map[node.ID]bool } func (e *emptyNodes) Get(id node.ID) ([]byte, error) { if e.ids != nil { if !e.ids[id] { return nil, fmt.Errorf("not found or read twice: %v", id) } delete(e.ids, id) // Allow getting this ID only once. } return e.h.hashEmpty(id), nil } func (e *emptyNodes) Set(id node.ID, hash []byte) {} func BenchmarkHStar3Root(b *testing.B) { hasher := coniks.New(crypto.SHA256) for i := 0; i < b.N; i++ { nodes := leafNodes(b, 512) hs, err := NewHStar3(nodes, hasher.HashChildren, 256, 0) if err != nil { b.Fatalf("NewHStar3: %v", err) } acc := &emptyNodes{h: bindHasher(hasher, 42)} if _, err := hs.Update(acc); err != nil { b.Fatalf("Update: %v", err) } } } // This test checks HStar3 implementation against HStar2-generated result. func TestHStar3Golden(t *testing.T) { hasher := coniks.New(crypto.SHA256) nodes := leafNodes(t, 500) hs, err := NewHStar3(nodes, hasher.HashChildren, 256, 0) if err != nil { t.Fatalf("NewHStar3: %v", err) } acc := &emptyNodes{h: bindHasher(hasher, 42)} rootNodes, err := hs.Update(acc) if err != nil { t.Fatalf("Update: %v", err) } if ln := len(rootNodes); ln != 1 { t.Fatalf("Update returned %d nodes, want 1", ln) } want := "daf17dc2c83f37962bae8a65d294ef7fca4ffa02c10bdc4ca5c4dec408001c98" if got := hex.EncodeToString(rootNodes[0].Hash); got != want { t.Errorf("Root: got %x, want %v", rootNodes[0].Hash, want) } } func TestNewHStar3(t *testing.T) { id1 := node.NewID("01234567890000000000000000000001", 256) id2 := node.NewID("01234567890000000000000000000002", 256) id3 := node.NewID("01234567890000000000000000000003", 256) id4 := node.NewID("01234567890000000000000001111111", 256) hasher := coniks.Default for _, tc := range []struct { desc string nodes []Node top uint want []Node wantErr string }{ {desc: "depth-err", nodes: []Node{{ID: id1.Prefix(10)}}, wantErr: "invalid depth"}, {desc: "dup-err1", nodes: []Node{{ID: id1}, {ID: id1}}, wantErr: "duplicate ID"}, {desc: "dup-err2", nodes: []Node{{ID: id1}, {ID: id2}, {ID: id1}}, wantErr: "duplicate ID"}, {desc: "top-vs-depth-err", nodes: []Node{{ID: id1}}, top: 300, wantErr: "top > depth"}, { desc: "ok1", nodes: []Node{{ID: id2}, {ID: id1}, {ID: id4}, {ID: id3}}, want: []Node{{ID: id1}, {ID: id2}, {ID: id3}, {ID: id4}}, }, { desc: "ok2", nodes: []Node{{ID: id4}, {ID: id3}, {ID: id2}, {ID: id1}}, want: []Node{{ID: id1}, {ID: id2}, {ID: id3}, {ID: id4}}, }, } { t.Run(tc.desc, func(t *testing.T) { nodes := tc.nodes // No need to copy it here. // Note: NewHStar3 potentially shuffles nodes. _, err := NewHStar3(tc.nodes, hasher.HashChildren, 256, tc.top) got := "" if err != nil { got = err.Error() } if want := tc.wantErr; !strings.Contains(got, want) { t.Errorf("NewHStar3: want error containing %q, got %v", want, err) } if want := tc.want; want != nil && !reflect.DeepEqual(nodes, want) { t.Errorf("NewHStar3: want nodes:\n%v\ngot:\n%v", nodes, want) } }) } } func TestHStar3Prepare(t *testing.T) { hasher := coniks.Default nodes := leafNodes(t, 512) hs, err := NewHStar3(nodes, hasher.HashChildren, 256, 0) if err != nil { t.Fatalf("NewHStar3: %v", err) } rs := idsToMap(t, hs.Prepare()) acc := &emptyNodes{h: bindHasher(hasher, 42), ids: rs} if _, err = hs.Update(acc); err != nil { t.Errorf("Update: %v", err) } if got := len(acc.ids); got != 0 { t.Errorf("%d ids were not read", got) } } func TestHStar3PrepareAlternative(t *testing.T) { // This is the intuitively simpler alternative Prepare implementation. prepare := func(nodes []Node, depth, top uint) map[node.ID]bool { ids := make(map[node.ID]bool) // For each node, add all its ancestors' siblings, down to the given depth. for _, n := range nodes { for id, d := n.ID, depth; d > top; d-- { pref := id.Prefix(d) if _, ok := ids[pref]; ok { // Delete the prefix node because its original hash does not contribute // to the updates, so should not be read. delete(ids, pref) // All the upper siblings have been added already, so skip them. break } ids[pref.Sibling()] = true } } return ids } for n := 0; n <= 32; n++ { t.Run(fmt.Sprintf("n:%d", n), func(t *testing.T) { nodes := leafNodes(t, n) hs, err := NewHStar3(nodes, nil, 256, 8) if err != nil { t.Fatalf("NewHStar3: %v", err) } ids := prepare(nodes, 256, 8) got := idsToMap(t, hs.Prepare()) if !reflect.DeepEqual(got, ids) { t.Error("IDs mismatch") } }) } } func BenchmarkHStar3Prepare(b *testing.B) { for i := 0; i < b.N; i++ { nodes := leafNodes(b, 512) hs, err := NewHStar3(nodes, nil, 256, 0) if err != nil { b.Fatalf("NewHStar3: %v", err) } _ = hs.Prepare() } } // leafNodes generates n pseudo-random leaf nodes at depth 256. The returned // data depends only on n. The algorithm is the same as in HStar2 tests, which // allows cross-checking their results. func leafNodes(t testing.TB, n int) []Node { t.Helper() // Use a random sequence that depends on n. r := rand.New(rand.NewSource(int64(n))) nodes := make([]Node, n) for i := range nodes { nodes[i].Hash = make([]byte, 32) if _, err := r.Read(nodes[i].Hash); err != nil { t.Fatalf("Failed to make random leaf hash: %v", err) } path := make([]byte, 32) if _, err := r.Read(path); err != nil { t.Fatalf("Failed to make random path: %v", err) } nodes[i].ID = node.NewID(string(path), 256) } return nodes } func idsToMap(t testing.TB, ids []node.ID) map[node.ID]bool { t.Helper() res := make(map[node.ID]bool, len(ids)) for _, id := range ids { if res[id] { t.Errorf("ID duplicate: %v", id) } res[id] = true } return res } trillian-1.6.1/merkle/smt/layout.go000066400000000000000000000037511466362047600173070ustar00rootroot00000000000000// Copyright 2021 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 smt import "sort" // Layout defines the mapping between tree nodes and tiles. // // The tree is divided into horizontal tiers of the heights specified in the // constructor. The tiers are counted down from the tree root. Nodes located at // the tier borders are roots of the corresponding tiles. A tile includes all // the nodes strictly below the tile root, within a single tier. type Layout struct { depths []int } // NewLayout creates a tree layout based on the passed-in tier heights. func NewLayout(heights []uint) Layout { depths := make([]int, len(heights)+1) for i, h := range heights { if h <= 0 { panic("tile heights must be positive") } depths[i+1] = depths[i] + int(h) } return Layout{depths: depths} } // Locate returns the tier info that nodes at the given depth belong to. The // first returned value is the depth of the root node, for any tile in this // tier. The second value is the height of the tiles. // // If depth is 0, or beyond the tree height, then the result is undefined. func (l Layout) Locate(depth uint) (uint, uint) { if depth == 0 { // The root belongs to its own "pseudo" tile. return 0, 0 } i := sort.SearchInts(l.depths, int(depth)) // 1 <= i <= len(depths) rootDepth := uint(l.depths[i-1]) if ln := len(l.depths); i >= ln { // Anything below the leaves does not exist. return rootDepth, 0 } return rootDepth, uint(l.depths[i]) - rootDepth } trillian-1.6.1/merkle/smt/layout_test.go000066400000000000000000000047151466362047600203470ustar00rootroot00000000000000// Copyright 2021 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 smt import "testing" func TestLayoutLocate(t *testing.T) { l1 := NewLayout([]uint{1, 2, 4, 8}) l2 := NewLayout([]uint{8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 176}) for _, tc := range []struct { desc string l Layout depth uint wantD uint wantH uint }{ {desc: "l1-root", l: l1, depth: 0, wantD: 0, wantH: 0}, {desc: "l1-1", l: l1, depth: 1, wantD: 0, wantH: 1}, {desc: "l1-2", l: l1, depth: 2, wantD: 1, wantH: 2}, {desc: "l1-3", l: l1, depth: 3, wantD: 1, wantH: 2}, {desc: "l1-4", l: l1, depth: 4, wantD: 3, wantH: 4}, {desc: "l1-5", l: l1, depth: 5, wantD: 3, wantH: 4}, {desc: "l1-7", l: l1, depth: 7, wantD: 3, wantH: 4}, {desc: "l1-8", l: l1, depth: 8, wantD: 7, wantH: 8}, {desc: "l1-10", l: l1, depth: 10, wantD: 7, wantH: 8}, {desc: "l1-leaves", l: l1, depth: 15, wantD: 7, wantH: 8}, {desc: "l2-root", l: l2, depth: 0, wantD: 0, wantH: 0}, {desc: "l2-3", l: l2, depth: 3, wantD: 0, wantH: 8}, {desc: "l2-8", l: l2, depth: 8, wantD: 0, wantH: 8}, {desc: "l2-10", l: l2, depth: 10, wantD: 8, wantH: 8}, {desc: "l2-20", l: l2, depth: 20, wantD: 16, wantH: 8}, {desc: "l2-63", l: l2, depth: 63, wantD: 56, wantH: 8}, {desc: "l2-64", l: l2, depth: 64, wantD: 56, wantH: 8}, {desc: "l2-79", l: l2, depth: 79, wantD: 72, wantH: 8}, {desc: "l2-80", l: l2, depth: 80, wantD: 72, wantH: 8}, {desc: "l2-81", l: l2, depth: 81, wantD: 80, wantH: 176}, {desc: "l2-100", l: l2, depth: 100, wantD: 80, wantH: 176}, {desc: "l2-leaves", l: l2, depth: 256, wantD: 80, wantH: 176}, {desc: "l2-out-of-range", l: l2, depth: 257, wantD: 256, wantH: 0}, } { t.Run(tc.desc, func(t *testing.T) { d, h := tc.l.Locate(tc.depth) if got, want := d, tc.wantD; got != want { t.Errorf("tier depth is %d, want %d", got, want) } if got, want := h, tc.wantH; got != want { t.Errorf("tier height is %d, want %d", got, want) } }) } } trillian-1.6.1/merkle/smt/node/000077500000000000000000000000001466362047600163625ustar00rootroot00000000000000trillian-1.6.1/merkle/smt/node/id.go000066400000000000000000000131611466362047600173070ustar00rootroot00000000000000// 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 node implements a sparse Merkle tree node. package node import "fmt" // ID identifies a node of a Merkle tree. It is a bit string that counts the // node down from the tree root, i.e. 0 and 1 bits represent going to the left // or right child correspondingly. // // ID is immutable, comparable, and can be used as a Golang map key. It also // incurs zero memory allocations in transforming methods like Prefix and // Sibling. // // The internal structure of ID is driven by its use-cases: // - To make ID objects immutable and comparable, the Golang string type is // used for storing the bit string bytes. // - To make Sibling and Prefix operations fast, the last byte is stored // separately from the rest of the bytes, so that it can be "amended". // - To make ID objects comparable, there is only one (canonical) way to encode // an ID. For example, if the last byte is used partially, its unused bits // are always unset. See invariants next to field definitions below. // // Constructors and methods of ID make sure its invariants are always met. // // For example, an 11-bit node ID [1010,1111,001] is structured as follows: // - path string contains 1 byte, which is [1010,1111]. // - last byte is [0010,0000]. Note the unset lower 5 bits. // - bits is 3, so effectively only the upper 3 bits [001] of last are used. type ID struct { path string last byte // Invariant: Lowest (8-bits) bits of the last byte are unset. bits uint8 // Invariant: 1 <= bits <= 8, or bits == 0 for the empty ID. } // NewID creates a node ID from the given path bytes truncated to the specified // number of bits if necessary. Panics if the number of bits is more than the // byte string contains. func NewID(path string, bits uint) ID { if bits == 0 { return ID{} } else if mx := uint(len(path)) * 8; bits > mx { panic(fmt.Sprintf("NewID: bits %d > %d", bits, mx)) } bytes, tailBits := split(bits) // Note: Getting the substring is cheap because strings are immutable in Go. return newMaskedID(path[:bytes], path[bytes], tailBits) } // NewIDWithLast creates a node ID from the given path bytes and the additional // last byte, of which only the specified number of most significant bits is // used. The number of bits must be between 1 and 8, and can be 0 only if the // path bytes string is empty; otherwise the function panics. func NewIDWithLast(path string, last byte, bits uint8) ID { if bits > 8 { panic(fmt.Sprintf("NewIDWithLast: bits %d > 8", bits)) } else if bits == 0 && len(path) != 0 { panic("NewIDWithLast: bits=0, but path is not empty") } return newMaskedID(path, last, bits) } // newMaskedID constructs a node ID ensuring its invariants are met. The last // byte is masked so that the given number of upper bits are in use, and the // others are unset. func newMaskedID(path string, last byte, bits uint8) ID { last &= ^byte(1<<(8-bits) - 1) // Unset the unused bits. return ID{path: path, last: last, bits: bits} } // BitLen returns the length of the ID in bits. func (n ID) BitLen() uint { return uint(len(n.path))*8 + uint(n.bits) } // FullBytes returns the ID bytes that are complete. Note that there might // still be up to 8 extra bits, which can be obtained with the LastByte method. func (n ID) FullBytes() string { return n.path } // LastByte returns the terminating byte of the ID, with the number of upper // bits that it uses (between 1 and 8, and 0 if the ID is empty). The remaining // unused lower bits are always unset. func (n ID) LastByte() (byte, uint8) { return n.last, n.bits } // Prefix returns the prefix of the node ID with the given number of bits. func (n ID) Prefix(bits uint) ID { // Note: This code is very similar to NewID, and it's tempting to return // NewID(n.path, bits). But there is a difference: NewID expects all the // bytes to be in the path string, while here the last byte is not. if bits == 0 { return ID{} } else if mx := n.BitLen(); bits > mx { panic(fmt.Sprintf("Prefix: bits %d > %d", bits, mx)) } bytes, tailBits := split(bits) last := n.last if bytes != uint(len(n.path)) { last = n.path[bytes] } return newMaskedID(n.path[:bytes], last, tailBits) } // Sibling returns the ID of the nodes's sibling in a binary tree, i.e. the ID // of the parent node's other child. If the node is the root then the returned // ID is the same. func (n ID) Sibling() ID { last := n.last ^ byte(1<<(8-n.bits)) return ID{path: n.path, last: last, bits: n.bits} } // String returns a human-readable bit string. func (n ID) String() string { if n.BitLen() == 0 { return "[]" } path := fmt.Sprintf("%08b", []byte(n.path)) path = path[1 : len(path)-1] // Trim the brackets. if len(path) > 0 { path += " " } return fmt.Sprintf("[%s%0*b]", path, n.bits, n.last>>(8-n.bits)) } // split returns the decomposition of an ID with the given number of bits. The // first int returned is the number of full bytes stored in the dynamically // allocated part. The second one is the number of bits in the tail byte. func split(bits uint) (bytes uint, tailBits uint8) { return (bits - 1) / 8, uint8(1 + (bits-1)%8) } trillian-1.6.1/merkle/smt/node/id_test.go000066400000000000000000000123111466362047600203420ustar00rootroot00000000000000// 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 node import ( "fmt" "strconv" "testing" _ "k8s.io/klog/v2" // Add flags for the overarching test-all command. ) func TestNewIDWithLast(t *testing.T) { const bytes = "\x0A\x0B\x0C\xFA" for _, tc := range []struct { length uint path string last byte bits uint8 }{ {length: 0, last: 0, bits: 0}, {length: 0, last: 123, bits: 0}, {length: 1, last: 0, bits: 1}, {length: 1, last: 123, bits: 1}, {length: 4, last: 0, bits: 4}, {length: 5, last: 0xA, bits: 5}, {length: 5, last: 0xF, bits: 5}, {length: 7, last: 0xB, bits: 7}, {length: 8, last: 0xA, bits: 8}, {length: 9, path: bytes[:1], last: 0, bits: 1}, {length: 13, path: bytes[:1], last: 0xA, bits: 5}, {length: 24, path: bytes[:2], last: 0xC, bits: 8}, {length: 31, path: bytes[:3], last: 0xFB, bits: 7}, {length: 31, path: bytes[:3], last: 0xFA, bits: 7}, {length: 32, path: bytes[:3], last: 0xFA, bits: 8}, } { id := NewID(bytes, tc.length) t.Run(id.String(), func(t *testing.T) { got := NewIDWithLast(tc.path, tc.last, tc.bits) if want := id; got != want { t.Errorf("NewIDWithLast: %v, want %v", got, want) } }) } } func TestIDString(t *testing.T) { bytes := string([]byte{5, 1, 127}) for _, tc := range []struct { bits uint want string }{ {bits: 0, want: "[]"}, {bits: 1, want: "[0]"}, {bits: 4, want: "[0000]"}, {bits: 6, want: "[000001]"}, {bits: 8, want: "[00000101]"}, {bits: 16, want: "[00000101 00000001]"}, {bits: 21, want: "[00000101 00000001 01111]"}, {bits: 24, want: "[00000101 00000001 01111111]"}, } { t.Run(fmt.Sprintf("bits:%d", tc.bits), func(t *testing.T) { id := NewID(bytes, tc.bits) if got, want := id.String(), tc.want; got != want { t.Errorf("String: got %q, want %q", got, want) } }) } } func TestIDComparison(t *testing.T) { const bytes = "\x0A\x0B\x0C\x0A\x0B\x0C\x01" for _, tc := range []struct { desc string id1 ID id2 ID want bool }{ {desc: "all-same", id1: NewID(bytes, 56), id2: NewID(bytes, 56), want: true}, {desc: "same-bytes", id1: NewID(bytes[:3], 24), id2: NewID(bytes[3:6], 24), want: true}, {desc: "same-bits1", id1: NewID(bytes[:4], 25), id2: NewID(bytes[3:], 25), want: true}, {desc: "same-bits2", id1: NewID(bytes[:4], 28), id2: NewID(bytes[3:], 28), want: true}, {desc: "diff-bits", id1: NewID(bytes[:4], 29), id2: NewID(bytes[3:], 29)}, {desc: "diff-len", id1: NewID(bytes, 56), id2: NewID(bytes, 55)}, {desc: "diff-bytes", id1: NewID(bytes, 56), id2: NewID(bytes, 48)}, } { t.Run(tc.desc, func(t *testing.T) { eq := tc.id1 == tc.id2 if want := tc.want; eq != want { t.Errorf("(id1==id2) is %v, want %v", eq, want) } }) } } func TestIDPrefix(t *testing.T) { const bytes = "\x0A\x0B\x0C" for i, tc := range []struct { id ID bits uint want ID }{ {id: NewID(bytes, 24), bits: 0, want: ID{}}, {id: NewID(bytes, 24), bits: 1, want: NewID(bytes, 1)}, {id: NewID(bytes, 24), bits: 2, want: NewID(bytes, 2)}, {id: NewID(bytes, 24), bits: 5, want: NewID(bytes, 5)}, {id: NewID(bytes, 24), bits: 8, want: NewID(bytes, 8)}, {id: NewID(bytes, 24), bits: 15, want: NewID(bytes, 15)}, {id: NewID(bytes, 24), bits: 24, want: NewID(bytes, 24)}, {id: NewID(bytes, 21), bits: 15, want: NewID(bytes, 15)}, } { t.Run(strconv.Itoa(i), func(t *testing.T) { if got, want := tc.id.Prefix(tc.bits), tc.want; got != want { t.Errorf("Prefix: %v, want %v", got, want) } }) } } func TestIDSibling(t *testing.T) { const bytes = "\x0A\x0B\x0C" for _, tc := range []struct { id ID want ID }{ {id: NewID(bytes, 0), want: ID{}}, {id: NewID(bytes, 1), want: NewID("\xA0", 1)}, {id: NewID(bytes, 2), want: NewID("\x40", 2)}, {id: NewID(bytes, 8), want: NewID("\x0B", 8)}, {id: NewID(bytes, 24), want: NewID("\x0A\x0B\x0D", 24)}, } { t.Run(tc.id.String(), func(t *testing.T) { sib := tc.id.Sibling() if got, want := sib, tc.want; got != want { t.Errorf("Sibling: got %v, want %v", got, want) } // The sibling's sibling is the original node. if got, want := sib.Sibling(), tc.id; got != want { t.Errorf("Sibling: got %v, want %v", got, want) } }) } } func BenchmarkIDSiblings(b *testing.B) { siblings := func(id ID) []ID { ln := id.BitLen() sibs := make([]ID, ln) for height := range sibs { depth := ln - uint(height) sibs[height] = id.Prefix(depth).Sibling() } return sibs } const batch = 512 ids := make([]ID, batch) for i := range ids { bytes := fmt.Sprintf("0123456789012345678901234567%02x%02x", i&255, (i>>8)&255) ids[i] = NewID(bytes, uint(len(bytes))*8) } for i, n := 0, b.N; i < n; i++ { for _, id := range ids { _ = siblings(id) } } } trillian-1.6.1/merkle/smt/nodes.go000066400000000000000000000061061466362047600170770ustar00rootroot00000000000000// 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 smt import ( "fmt" "sort" "strings" "github.com/google/trillian/merkle/smt/node" ) // Node represents a sparse Merkle tree node. type Node struct { ID node.ID Hash []byte } // NodesRow contains nodes at the same tree level sorted by ID from left to // right. The IDs of the nodes are unique. // // TODO(pavelkalinnikov): Hide nodes so that only this package can modify them. type NodesRow []Node // NewNodesRow creates a NodesRow from the given list of nodes. The nodes are // reordered in-place if not already sorted. func NewNodesRow(nodes []Node) (NodesRow, error) { if len(nodes) == 0 { return nodes, nil } if err := Prepare(nodes, nodes[0].ID.BitLen()); err != nil { return nil, err } return nodes, nil } // inSubtree returns whether all the nodes in this row are strictly under the // node with the given ID. Panics if the row is empty. func (n NodesRow) inSubtree(root node.ID) bool { rootLen := root.BitLen() if n[0].ID.BitLen() <= rootLen { return false } if n[0].ID.Prefix(rootLen) != root { return false } // Note: It is enough to check only the first and the last node ID because // the list is sorted. return len(n) == 1 || n[len(n)-1].ID.Prefix(rootLen) == root } // Prepare sorts the nodes slice for it to be usable by HStar3 algorithm and // the sparse Merkle tree Writer. It also verifies that the nodes are placed at // the required depth, and there are no duplicate IDs. // // TODO(pavelkalinnikov): Make this algorithm independent of Node type. func Prepare(nodes []Node, depth uint) error { for i := range nodes { if d, want := nodes[i].ID.BitLen(), depth; d != want { return fmt.Errorf("node #%d: invalid depth %d, want %d", i, d, want) } } sort.Slice(nodes, func(i, j int) bool { return compareHorizontal(nodes[i].ID, nodes[j].ID) < 0 }) for i, last := 0, len(nodes)-1; i < last; i++ { if id := nodes[i].ID; id == nodes[i+1].ID { return fmt.Errorf("duplicate ID: %v", id) } } return nil } // compareHorizontal compares relative position of two node IDs at the same // tree level. Returns -1 if the first node is to the left from the second one, // 1 if the first node is to the right, and 0 if IDs are the same. The result // is undefined if nodes are not at the same level. func compareHorizontal(a, b node.ID) int { if res := strings.Compare(a.FullBytes(), b.FullBytes()); res != 0 { return res } aLast, _ := a.LastByte() bLast, _ := b.LastByte() if aLast == bLast { return 0 } else if aLast < bLast { return -1 } return 1 } trillian-1.6.1/merkle/smt/nodes_test.go000066400000000000000000000066651466362047600201500ustar00rootroot00000000000000// 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 smt import ( "reflect" "strings" "testing" "github.com/google/go-cmp/cmp" "github.com/google/trillian/merkle/smt/node" ) func TestNewNodesRow(t *testing.T) { for _, tc := range []struct { desc string nodes []Node want NodesRow wantErr string }{ {desc: "empty", want: nil}, { desc: "error-depth", nodes: []Node{{ID: node.NewID("00", 16)}, {ID: node.NewID("001", 24)}}, wantErr: "invalid depth", }, { desc: "error-dups", nodes: []Node{{ID: node.NewID("01", 16)}, {ID: node.NewID("01", 16)}}, wantErr: "duplicate ID", }, { desc: "sorted", nodes: []Node{ {ID: node.NewID("01", 16)}, {ID: node.NewID("00", 16)}, {ID: node.NewID("02", 16)}, }, want: []Node{ {ID: node.NewID("00", 16)}, {ID: node.NewID("01", 16)}, {ID: node.NewID("02", 16)}, }, }, } { t.Run(tc.desc, func(t *testing.T) { row, err := NewNodesRow(tc.nodes) if err != nil { if tc.wantErr == "" { t.Fatalf("NewNodesRow: want no error, returned: %v", err) } if want := tc.wantErr; !strings.Contains(err.Error(), want) { t.Fatalf("NewNodesRow: got error: %v; want substring %q", err, want) } } else if want := tc.wantErr; want != "" { t.Fatalf("NewNodesRow: got no error; want prefix %q", want) } if d := cmp.Diff(row, tc.want, cmp.AllowUnexported(node.ID{})); d != "" { t.Errorf("NewNodesRow result mismatch:\n%s", d) } }) } } func TestPrepare(t *testing.T) { id1 := node.NewID("01234567890000000000000000000001", 256) id2 := node.NewID("01234567890000000000000000000002", 256) id3 := node.NewID("01234567890000000000000000000003", 256) id4 := node.NewID("01234567890000000000000001111111", 256) for _, tc := range []struct { desc string nodes []Node want []Node wantErr string }{ {desc: "depth-err", nodes: []Node{{ID: id1.Prefix(10)}}, wantErr: "invalid depth"}, {desc: "dup-err1", nodes: []Node{{ID: id1}, {ID: id1}}, wantErr: "duplicate ID"}, {desc: "dup-err2", nodes: []Node{{ID: id1}, {ID: id2}, {ID: id1}}, wantErr: "duplicate ID"}, {desc: "ok-empty", want: nil}, { desc: "ok1", nodes: []Node{{ID: id2}, {ID: id1}, {ID: id4}, {ID: id3}}, want: []Node{{ID: id1}, {ID: id2}, {ID: id3}, {ID: id4}}, }, { desc: "ok2", nodes: []Node{{ID: id4}, {ID: id3}, {ID: id2}, {ID: id1}}, want: []Node{{ID: id1}, {ID: id2}, {ID: id3}, {ID: id4}}, }, } { t.Run(tc.desc, func(t *testing.T) { nodes := tc.nodes // No need to copy it here. err := Prepare(nodes, 256) got := "" if err != nil { got = err.Error() } if want := tc.wantErr; !strings.Contains(got, want) { t.Errorf("NewHStar3: want error containing %q, got %v", want, err) } if want := tc.want; want != nil && !reflect.DeepEqual(nodes, want) { t.Errorf("NewHStar3: want nodes:\n%v\ngot:\n%v", nodes, want) } }) } } trillian-1.6.1/merkle/smt/tile.go000066400000000000000000000074621466362047600167320ustar00rootroot00000000000000// 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 smt import ( "errors" "fmt" "github.com/google/trillian/merkle/smt/node" ) // Tile represents a sparse Merkle tree tile, i.e. a dense set of tree nodes // located under a single "root" node at a distance not exceeding the tile // height. A tile is identified by the ID of its root node. The Tile struct // contains the list of non-empty leaf nodes, which can be used to reconstruct // all the remaining inner nodes of the tile. // // Invariants of this structure that must be preserved at all times: // - ID is a prefix of Leaves' IDs, i.e. the nodes are in the same subtree. // - IDs of Leaves have the same length, i.e. the nodes are at the same level. // - Leaves are ordered by ID from left to right. // - IDs of Leaves are unique. // // Algorithms that create Tile structures must ensure that these invariants // hold. Use NewNodesRow function for ordering nodes correctly. type Tile struct { ID node.ID Leaves NodesRow } // Merge returns a new tile which is a combination of this tile with the given // updates. The resulting tile contains all the nodes from the updates, and all // the nodes from the original tile not present in the updates. func (t Tile) Merge(updates NodesRow) (Tile, error) { if len(updates) == 0 { return t, nil } else if len(t.Leaves) == 0 { return Tile{ID: t.ID, Leaves: updates}, nil } if at, want := updates[0].ID.BitLen(), t.Leaves[0].ID.BitLen(); at != want { return Tile{}, fmt.Errorf("updates are at depth %d, want %d", at, want) } if !updates.inSubtree(t.ID) { return Tile{}, errors.New("updates are not entirely in this tile") } return Tile{ID: t.ID, Leaves: merge(t.Leaves, updates)}, nil } // merge merges two sorted slices of nodes into one sorted slice. If a node ID // exists in both slices, then the one from the updates slice is taken, i.e. it // overrides the node from the nodes slice. func merge(nodes, updates NodesRow) NodesRow { res := make([]Node, 0, len(nodes)+len(updates)) i := 0 for _, u := range updates { for ; i < len(nodes); i++ { if c := compareHorizontal(nodes[i].ID, u.ID); c < 0 { res = append(res, nodes[i]) } else if c > 0 { break } } res = append(res, u) } return append(res, nodes[i:]...) } // scan visits all non-empty nodes of the tile except the root. The order of // node visits is arbitrary. func (t Tile) scan(l Layout, h mapHasher, visit func(Node)) error { top := t.ID.BitLen() _, height := l.Locate(top + 1) // TODO(pavelkalinnikov): Remove HStar3 side effects, to avoid copying here. // Currently, the Update method modifies the nodes given to NewHStar3. leaves := make([]Node, len(t.Leaves)) copy(leaves, t.Leaves) hs, err := NewHStar3(leaves, h.mh.HashChildren, top+height, top) if err != nil { return err } _, err = hs.Update(emptyHashes{h: h, visit: visit}) return err } // emptyHashes is a NodeAccessor used for computing node hashes of a tile. type emptyHashes struct { h mapHasher visit func(Node) } // Get returns an empty hash for the given root node ID. func (e emptyHashes) Get(id node.ID) ([]byte, error) { return e.h.hashEmpty(id), nil } // Set calls the visitor callback for the given node and hash. func (e emptyHashes) Set(id node.ID, hash []byte) { e.visit(Node{ID: id, Hash: hash}) } trillian-1.6.1/merkle/smt/tile_test.go000066400000000000000000000110671466362047600177650ustar00rootroot00000000000000// 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 smt import ( "reflect" "strings" "testing" "github.com/google/go-cmp/cmp" "github.com/google/trillian/merkle/coniks" "github.com/google/trillian/merkle/smt/node" ) func TestTileMerge(t *testing.T) { ids := []node.ID{ node.NewID("\xAB\x00", 15), node.NewID("\xAB\x10", 15), node.NewID("\xAB\x20", 15), node.NewID("\xAB\x30", 15), node.NewID("\xAB\x40", 15), node.NewID("\xAC\x00", 15), // In another tile. } id := ids[0].Prefix(8) n := func(idIndex int, hash string) Node { return Node{ID: ids[idIndex], Hash: []byte(hash)} } for _, tc := range []struct { desc string was NodesRow upd NodesRow want NodesRow wantErr string }{ {desc: "empty", want: nil}, {desc: "no-updates", was: []Node{n(3, "h")}, want: []Node{n(3, "h")}}, {desc: "add-to-empty", upd: []Node{n(0, "h")}, want: []Node{n(0, "h")}}, { desc: "override-one", was: []Node{n(0, "old")}, upd: []Node{n(0, "new")}, want: []Node{n(0, "new")}, }, { desc: "add-multiple", was: []Node{n(0, "old0"), n(3, "old3")}, upd: []Node{n(1, "new1"), n(2, "new2"), n(4, "new4")}, want: []Node{n(0, "old0"), n(1, "new1"), n(2, "new2"), n(3, "old3"), n(4, "new4")}, }, { desc: "override-some", was: []Node{n(0, "old0"), n(1, "old1"), n(2, "old2"), n(3, "old3")}, upd: []Node{n(1, "new1"), n(2, "new2")}, want: []Node{n(0, "old0"), n(1, "new1"), n(2, "new2"), n(3, "old3")}, }, { desc: "override-and-add", was: []Node{n(0, "old0"), n(1, "old1"), n(3, "old3"), n(4, "old4")}, upd: []Node{n(1, "new1"), n(2, "new2")}, want: []Node{n(0, "old0"), n(1, "new1"), n(2, "new2"), n(3, "old3"), n(4, "old4")}, }, { desc: "wrong-depth", was: []Node{n(0, "old")}, upd: []Node{{ID: id}}, wantErr: "updates are at depth", }, { desc: "wrong-tile", was: []Node{n(0, "old")}, upd: []Node{n(5, "new")}, wantErr: "updates are not entirely in this tile", }, } { t.Run(tc.desc, func(t *testing.T) { was := Tile{ID: id, Leaves: tc.was} got, err := was.Merge(tc.upd) if err != nil { if tc.wantErr == "" { t.Fatalf("Merge: want no error, returned: %v", err) } if want := tc.wantErr; !strings.HasPrefix(err.Error(), want) { t.Fatalf("Merge: got error: %v; want prefix %q", err, want) } return } else if want := tc.wantErr; want != "" { t.Fatalf("Merge: got no error; want prefix %q", want) } want := Tile{ID: id, Leaves: tc.want} if d := cmp.Diff(got, want, cmp.AllowUnexported(node.ID{})); d != "" { t.Errorf("Merge result mismatch:\n%s", d) } }) } } func TestTileScan(t *testing.T) { lo := NewLayout([]uint{8, 24}) h := bindHasher(coniks.Default, 1) ids := []node.ID{ node.NewID("\x00", 8), node.NewID("\x02", 8), node.NewID("\xF0", 8), node.NewID("\x00\x01\x02\x03", 32), } prefixes := func(high, low uint, ids ...node.ID) []node.ID { ret := make([]node.ID, 0, int(high-low)*len(ids)) for i := high; i > low; i-- { for _, id := range ids { ret = append(ret, id.Prefix(i)) } } return ret } for _, tc := range []struct { id node.ID leaves []Node visits []node.ID }{ {leaves: nil, visits: []node.ID{}}, {leaves: []Node{{ID: ids[0]}}, visits: prefixes(8, 0, ids[0])}, { leaves: []Node{{ID: ids[0]}, {ID: ids[1]}}, visits: append(prefixes(8, 6, ids[0], ids[1]), prefixes(6, 0, ids[0])...), }, { leaves: []Node{{ID: ids[0]}, {ID: ids[2]}}, visits: prefixes(8, 0, ids[0], ids[2]), }, {id: ids[0], leaves: []Node{{ID: ids[3]}}, visits: prefixes(32, 8, ids[3])}, } { t.Run("", func(t *testing.T) { tile := Tile{ID: tc.id, Leaves: tc.leaves} visits := make([]node.ID, 0, len(tc.visits)) if err := tile.scan(lo, h, func(node Node) { visits = append(visits, node.ID) }); err != nil { t.Fatalf("scan: %v", err) } if got, want := visits, tc.visits; !reflect.DeepEqual(got, want) { t.Errorf("visits mismatch: got %d nodes, want %d other ones", len(got), len(want)) } }) } } trillian-1.6.1/merkle/smt/tiles.go000066400000000000000000000065641466362047600171170ustar00rootroot00000000000000// 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 smt import ( "bytes" "fmt" "sort" "github.com/google/trillian/merkle/smt/node" ) // TileSet represents a set of Merkle tree tiles and the corresponding nodes. // This type is not thread-safe. // // TODO(pavelkalinnikov): Make it immutable. type TileSet struct { layout Layout tiles map[node.ID]NodesRow hashes map[node.ID][]byte h mapHasher } // NewTileSet creates an empty TileSet with the given tree parameters. func NewTileSet(treeID int64, hasher Hasher, layout Layout) *TileSet { tiles := make(map[node.ID]NodesRow) hashes := make(map[node.ID][]byte) h := bindHasher(hasher, treeID) return &TileSet{layout: layout, tiles: tiles, hashes: hashes, h: h} } // Hashes returns a map containing all node hashes keyed by node IDs. func (t *TileSet) Hashes() map[node.ID][]byte { return t.hashes } // Add puts the given tile into the set. Not thread-safe. // // TODO(pavelkalinnikov): Take a whole list of Tiles instead. func (t *TileSet) Add(tile Tile) error { if _, ok := t.tiles[tile.ID]; ok { return fmt.Errorf("tile already exists: %v", tile.ID) } t.tiles[tile.ID] = tile.Leaves return tile.scan(t.layout, t.h, func(node Node) { t.hashes[node.ID] = node.Hash }) } // TileSetMutation accumulates tree tiles that need to be updated. This type is // not thread-safe. type TileSetMutation struct { read *TileSet tiles map[node.ID][]Node } // NewTileSetMutation creates a mutation which is based off the provided // TileSet. This means that each modification is checked against the hashes in // this set, and is applied if it does change the hash. func NewTileSetMutation(ts *TileSet) *TileSetMutation { tiles := make(map[node.ID][]Node) return &TileSetMutation{read: ts, tiles: tiles} } // Set updates the hash of the given tree node. Not thread-safe. // // TODO(pavelkalinnikov): Elaborate on the expected order of Set calls. // Currently, Build method sorts nodes to allow any order, but it can be // avoided. func (t *TileSetMutation) Set(id node.ID, hash []byte) { if bytes.Equal(t.read.hashes[id], hash) { return // Nothing changed. } d, height := t.read.layout.Locate(id.BitLen()) if d+height != id.BitLen() { return // Not a leaf node of a tile. } root := id.Prefix(d) t.tiles[root] = append(t.tiles[root], Node{ID: id, Hash: hash}) } // Build returns the full set of tiles modified by this mutation. func (t *TileSetMutation) Build() ([]Tile, error) { res := make([]Tile, 0, len(t.tiles)) for id, upd := range t.tiles { sort.Slice(upd, func(i, j int) bool { return compareHorizontal(upd[i].ID, upd[j].ID) < 0 }) had, ok := t.read.tiles[id] if !ok { res = append(res, Tile{ID: id, Leaves: upd}) continue } tile, err := Tile{ID: id, Leaves: had}.Merge(upd) if err != nil { return nil, err } res = append(res, tile) } return res, nil } trillian-1.6.1/merkle/smt/tiles_test.go000066400000000000000000000156101466362047600201460ustar00rootroot00000000000000// 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 smt import ( "reflect" "strings" "testing" "github.com/google/trillian/merkle/coniks" "github.com/google/trillian/merkle/smt/node" ) func TestTileSetAdd(t *testing.T) { hasher := coniks.Default l := NewLayout([]uint{8, 8}) existing := Tile{ ID: node.NewID("\x00", 8), Leaves: []Node{{ID: node.NewID("\x00\x05", 16)}}, } for _, tc := range []struct { tile Tile wantErr string }{ {tile: Tile{ID: node.NewID("\x01", 8)}}, {tile: Tile{ID: node.NewID("\x00", 8)}, wantErr: "exists"}, { tile: Tile{ ID: node.NewID("\x01", 8), Leaves: []Node{{ID: node.NewID("\x00\x05\x00", 24)}}, }, wantErr: "invalid depth", }, } { t.Run("", func(t *testing.T) { ts := NewTileSet(0, hasher, l) if err := ts.Add(existing); err != nil { t.Fatalf("Add: %v", err) } got := "" if err := ts.Add(tc.tile); err != nil { got = err.Error() } if want := tc.wantErr; len(want) == 0 && len(got) != 0 { t.Errorf("Add: %v", got) } else if len(want) != 0 && !strings.Contains(got, want) { t.Errorf("Add: err=%q; want err containing %q", got, want) } }) } } func TestTileSetHashes(t *testing.T) { l := NewLayout([]uint{8, 8}) ts := NewTileSet(0, coniks.Default, l) count := 0 for i, tc := range []struct { tile Tile added int }{ {tile: Tile{ID: node.NewID("\x02", 8), Leaves: nil}, added: 0}, { tile: Tile{ ID: node.NewID("\x00", 8), Leaves: []Node{{ID: node.NewID("\x00\x01", 16)}}, }, added: 8, }, { tile: Tile{Leaves: []Node{ {ID: node.NewID("\x00", 8)}, {ID: node.NewID("\x02", 8)}, }}, added: 10, }, } { if err := ts.Add(tc.tile); err != nil { t.Fatalf("Add(%d): %v", i, err) } count += tc.added // TODO(pavelkalinnikov): Check the nodes containment. if got, want := len(ts.Hashes()), count; got != want { t.Fatalf("Hashes(%d): got %d nodes, want %d", i, got, want) } } } func TestTileSetMutationBuild(t *testing.T) { l := NewLayout([]uint{8, 8}) ids := []node.ID{ node.NewID("\x00\x00", 16), node.NewID("\x00\x70", 16), node.NewID("\x01\x01", 16), node.NewID("\xFF\xFF", 16), node.NewID("\x77\x77", 16), node.NewID("\x77\x88", 16), } ts := NewTileSet(0, coniks.Default, l) for _, tile := range []Tile{ {Leaves: []Node{ // Root tile. {ID: ids[0].Prefix(8), Hash: []byte("hash_00")}, {ID: ids[2].Prefix(8), Hash: []byte("hash_01")}, {ID: ids[3].Prefix(8), Hash: []byte("hash_FF")}, }}, { // Tile 0x00. ID: ids[0].Prefix(8), Leaves: []Node{ {ID: ids[0], Hash: []byte("hash_0000")}, {ID: ids[1], Hash: []byte("hash_0070")}, }, }, { // Tile 0x01. ID: ids[2].Prefix(8), Leaves: []Node{{ID: ids[2], Hash: []byte("hash_0101")}}, }, { // Tile 0xFF. ID: ids[3].Prefix(8), Leaves: []Node{{ID: ids[3], Hash: []byte("hash_FFFF")}}, }, } { if err := ts.Add(tile); err != nil { t.Fatalf("Add(%v): %v", tile.ID, err) } } for _, tc := range []struct { upd []Node // Node updates. want map[node.ID][]Node // Updated tiles. }{ {upd: nil, want: make(map[node.ID][]Node)}, { // Updating the hash with the old value. upd: []Node{{ID: ids[0], Hash: []byte("hash_0000")}}, want: make(map[node.ID][]Node), // Tiles are intact. }, { // Ignoring non-leaf node updates for tiles. upd: []Node{{ID: ids[0].Prefix(1), Hash: []byte("root")}}, want: make(map[node.ID][]Node), }, { // Updating a non-existing node of a non-existing tile. upd: []Node{{ID: ids[4], Hash: []byte("new_7777")}}, want: map[node.ID][]Node{ ids[4].Prefix(8): {{ID: ids[4], Hash: []byte("new_7777")}}, }, }, { // Updating an existing node. upd: []Node{{ID: ids[0], Hash: []byte("new_0000")}}, want: map[node.ID][]Node{ ids[0].Prefix(8): { {ID: ids[0], Hash: []byte("new_0000")}, {ID: ids[1], Hash: []byte("hash_0070")}, }, }, }, { // Updating nodes of an existing tile. upd: []Node{{ID: ids[0].Sibling(), Hash: []byte("new_0001")}}, want: map[node.ID][]Node{ ids[0].Prefix(8): { {ID: ids[0], Hash: []byte("hash_0000")}, {ID: ids[0].Sibling(), Hash: []byte("new_0001")}, {ID: ids[1], Hash: []byte("hash_0070")}, }, }, }, { // Multiple updates in order. upd: []Node{ {ID: ids[0], Hash: []byte("new_0000")}, {ID: ids[1], Hash: []byte("new_0001")}, }, want: map[node.ID][]Node{ ids[0].Prefix(8): { {ID: ids[0], Hash: []byte("new_0000")}, {ID: ids[1], Hash: []byte("new_0001")}, }, }, }, { // Multiple updates out of order. upd: []Node{ {ID: ids[1], Hash: []byte("new_0001")}, {ID: ids[0], Hash: []byte("new_0000")}, }, want: map[node.ID][]Node{ ids[0].Prefix(8): { {ID: ids[0], Hash: []byte("new_0000")}, {ID: ids[1], Hash: []byte("new_0001")}, }, }, }, { // Multiple updates of a non-existing tile in order. upd: []Node{ {ID: ids[4], Hash: []byte("new_7777")}, {ID: ids[5], Hash: []byte("new_7788")}, }, want: map[node.ID][]Node{ ids[4].Prefix(8): { {ID: ids[4], Hash: []byte("new_7777")}, {ID: ids[5], Hash: []byte("new_7788")}, }, }, }, { // Multiple updates of a non-existing tile out of order. upd: []Node{ {ID: ids[5], Hash: []byte("new_7788")}, {ID: ids[4], Hash: []byte("new_7777")}, }, want: map[node.ID][]Node{ ids[4].Prefix(8): { {ID: ids[4], Hash: []byte("new_7777")}, {ID: ids[5], Hash: []byte("new_7788")}, }, }, }, { // Updating multiple existing tiles. upd: []Node{ {ID: ids[0], Hash: []byte("new_0000")}, {ID: ids[2], Hash: []byte("new_0101")}, }, want: map[node.ID][]Node{ ids[0].Prefix(8): { {ID: ids[0], Hash: []byte("new_0000")}, {ID: ids[1], Hash: []byte("hash_0070")}, }, ids[2].Prefix(8): { {ID: ids[2], Hash: []byte("new_0101")}, }, }, }, } { t.Run("", func(t *testing.T) { m := NewTileSetMutation(ts) for _, node := range tc.upd { m.Set(node.ID, node.Hash) } tiles, err := m.Build() if err != nil { t.Fatalf("Build: %v", err) } got := make(map[node.ID][]Node, len(tiles)) for _, tile := range tiles { got[tile.ID] = tile.Leaves } if want := tc.want; !reflect.DeepEqual(got, want) { t.Logf("%v", got) t.Logf("%v", want) t.Fatalf("Tile mismatch: got %d tiles, want %d other ones", len(got), len(want)) } }) } } trillian-1.6.1/merkle/smt/writer.go000066400000000000000000000133251466362047600173040ustar00rootroot00000000000000// 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 smt import ( "context" "errors" "fmt" "github.com/google/trillian/merkle/smt/node" ) // NodeBatchAccessor reads and writes batches of Merkle tree node hashes. It is // a batch interface for efficiency reasons, as it is designed to guard tree // storage / database access. The Writer type operates on a per-shard basis, // i.e. it calls Get and Set method exactly once for each shard. type NodeBatchAccessor interface { // Get returns the hashes of the given nodes, as a map keyed by their IDs. // The returned hashes may be missing or be nil for empty subtrees. Get(ctx context.Context, ids []node.ID) (map[node.ID][]byte, error) // Set applies the given node hash updates. Set(ctx context.Context, nodes []Node) error } // Writer handles sharded writes to a sparse Merkle tree. The tree has two // levels of shards: the single topmost shard spanning depths from 0 to split, // and 2^split second-level shards each spanning levels from split to height. // If the split height is 0 then effectively there is only one "global" shard. type Writer struct { h mapHasher height uint // The height of the tree. split uint // The height of the top shard. } // NewWriter creates a new Writer for the specified tree of the given height, // with two levels of sharding, where the upper shard is `split` levels high. func NewWriter(treeID int64, hasher Hasher, height, split uint) *Writer { if split > height { panic(fmt.Errorf("NewWriter: split(%d) > height(%d)", split, height)) } return &Writer{h: bindHasher(hasher, treeID), height: height, split: split} } // Split sorts and splits the given list of node hash updates into shards, i.e. // the subsets belonging to different subtrees. The nodes must belong to the // same tree level which is equal to the tree height. func (w *Writer) Split(nodes []Node) ([][]Node, error) { if err := Prepare(nodes, w.height); err != nil { return nil, err } // TODO(pavelkalinnikov): Try estimating the capacity for this slice. var shards [][]Node // The nodes are sorted, so we can split them by prefix. for begin, i := 0, 0; i < len(nodes); i++ { pref := nodes[i].ID.Prefix(w.split) next := i + 1 // Check if this ID ends the shard. if next == len(nodes) || nodes[next].ID.Prefix(w.split) != pref { shards = append(shards, nodes[begin:next]) begin = next } } return shards, nil } // Write applies the given list of node updates to a single shard, and returns // the resulting update of the shard root. It uses the given node accessor for // reading and writing tree nodes. // // The typical usage pattern is as follows. For the lower shards, the input is // the []Node slices returned from the Split method. For the top shard, the // input is all the Node values from the lower shards Write calls. // // In another case, Write can be performed without Split if the shard split // depth is 0, which effectively means that there is only one "global" shard. func (w *Writer) Write(ctx context.Context, nodes []Node, acc NodeBatchAccessor) (Node, error) { if len(nodes) == 0 { return Node{}, errors.New("nothing to write") } depth := nodes[0].ID.BitLen() top, err := w.shardTop(depth) if err != nil { return Node{}, err } hs, err := NewHStar3(nodes, w.h.mh.HashChildren, depth, top) if err != nil { return Node{}, err } hashes, err := acc.Get(ctx, hs.Prepare()) if err != nil { return Node{}, err } sa := w.newAccessor(hashes) topUpd, err := hs.Update(sa) if err != nil { return Node{}, err } else if ln := len(topUpd); ln != 1 { return Node{}, fmt.Errorf("writing across %d shards, want 1", ln) } if err := acc.Set(ctx, sa.writes); err != nil { return Node{}, err } return topUpd[0], nil } // shardTop returns the depth of a shard top based on its bottom depth. func (w *Writer) shardTop(depth uint) (uint, error) { switch depth { case w.height: return w.split, nil case w.split: return 0, nil } return 0, fmt.Errorf("unexpected depth %d", depth) } // newAccessor returns a NodeAccessor for HStar3 algorithm based on the set of // preloaded node hashes. func (w *Writer) newAccessor(nodes map[node.ID][]byte) *shardAccessor { // For any node that HStar3 reads, it also writes its sibling. Therefore we // can pre-allocate this many items for the writes slice. // TODO(pavelkalinnikov): The actual number of written nodes will be slightly // bigger by at most the number of written leaves. Try allocating precisely. writes := make([]Node, 0, len(nodes)) return &shardAccessor{w: w, reads: nodes, writes: writes} } // shardAccessor provides read and write access to nodes used by HStar3. It // operates entirely in-memory. type shardAccessor struct { w *Writer reads map[node.ID][]byte writes []Node } // Get returns the hash of the given node from the preloaded map, or a hash of // an empty subtree at this position if such node is not found. func (s *shardAccessor) Get(id node.ID) ([]byte, error) { if hash, ok := s.reads[id]; ok && hash != nil { return hash, nil } return s.w.h.hashEmpty(id), nil } // Set adds the given node hash update to the list of writes. func (s *shardAccessor) Set(id node.ID, hash []byte) { s.writes = append(s.writes, Node{ID: id, Hash: hash}) } trillian-1.6.1/merkle/smt/writer_test.go000066400000000000000000000200461466362047600203410ustar00rootroot00000000000000// 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 smt import ( "bytes" "context" "crypto/sha256" "errors" "fmt" "reflect" "strings" "sync" "testing" "github.com/google/trillian/merkle/coniks" "github.com/google/trillian/merkle/smt/node" "github.com/google/trillian/testonly" "golang.org/x/sync/errgroup" ) const treeID = int64(0) var ( hasher = coniks.Default b64 = testonly.MustDecodeBase64 ) func TestWriterSplit(t *testing.T) { ids := []node.ID{ node.NewID("\x01\x00\x00\x00", 32), node.NewID("\x00\x00\x00\x00", 32), node.NewID("\x02\x00\x00\x00", 32), node.NewID("\x03\x00\x00\x00", 32), node.NewID("\x02\x00\x01\x00", 32), node.NewID("\x03\x00\x00\x00", 32), } // Generate some nodes based on IDs. all := make([]Node, len(ids)) for i, id := range ids { all[i] = Node{ID: id, Hash: []byte(fmt.Sprintf("%32d", i))} } for _, tc := range []struct { desc string split uint nodes []Node want [][]Node err bool }{ {desc: "dup", nodes: all, err: true}, {desc: "wrong-len", nodes: []Node{{ID: node.NewID("ab", 10)}}, err: true}, { desc: "ok-24", split: 24, nodes: all[:5], want: [][]Node{{all[1]}, {all[0]}, {all[2]}, {all[4]}, {all[3]}}, }, { desc: "ok-21", split: 21, nodes: all[:5], want: [][]Node{{all[1]}, {all[0]}, {all[2], all[4]}, {all[3]}}, }, { desc: "ok-16", split: 16, nodes: all[:5], want: [][]Node{{all[1]}, {all[0]}, {all[2], all[4]}, {all[3]}}, }, { desc: "ok-0", split: 0, nodes: all[:5], want: [][]Node{{all[1], all[0], all[2], all[4], all[3]}}, }, } { t.Run(tc.desc, func(t *testing.T) { nodes := make([]Node, len(tc.nodes)) copy(nodes, tc.nodes) // Avoid shuffling effects. w := NewWriter(treeID, hasher, 32, tc.split) shards, err := w.Split(nodes) if !reflect.DeepEqual(shards, tc.want) { t.Error("shards mismatch") } if got, want := err != nil, tc.err; got != want { t.Errorf("got err: %v, want %v", err, want) } }) } } func TestWriterWrite(t *testing.T) { ctx := context.Background() all := []Node{genNode("key1", "value1"), genNode("key2", "value2"), genNode("key3", "value3")} for _, tc := range []struct { desc string split uint acc *testAccessor nodes []Node wantRoot []byte wantErr string }{ // Taken from SparseMerkleTreeWriter tests. { desc: "single-leaf", nodes: []Node{all[0]}, wantRoot: b64("KKBw4yrtfa5Ugm9jo4SHZ79wJy4bUAW1jLPyMOJoAPQ="), }, { desc: "multi-leaf", nodes: []Node{all[0], all[1], all[2]}, wantRoot: b64("l/IJC6+DcqpNvnQ+HdAwwmXEXfmQ6Ha9/lOD7smeIVc="), }, {desc: "empty", wantErr: "nothing to write"}, {desc: "unaligned", nodes: []Node{{ID: node.NewID("ab", 10)}}, wantErr: "unexpected depth"}, {desc: "dup", nodes: []Node{all[0], all[0]}, wantErr: "duplicate ID"}, {desc: "2-shards", split: 128, nodes: []Node{all[0], all[1]}, wantErr: "writing across"}, {desc: "get-err", acc: &testAccessor{get: errors.New("fail")}, nodes: []Node{all[0]}, wantErr: "fail"}, {desc: "set-err", acc: &testAccessor{set: errors.New("fail")}, nodes: []Node{all[0]}, wantErr: "fail"}, } { t.Run(tc.desc, func(t *testing.T) { w := NewWriter(treeID, hasher, 256, tc.split) acc := tc.acc if acc == nil { acc = &testAccessor{} } rootUpd, err := w.Write(ctx, tc.nodes, acc) gotErr := "" if err != nil { gotErr = err.Error() } if got, want := gotErr, tc.wantErr; !strings.Contains(got, want) { t.Errorf("Write: want err containing %q, got %v", want, err) } if got, want := rootUpd.Hash, tc.wantRoot; !bytes.Equal(got, want) { t.Errorf("Write: got root %x, want %x", got, want) } }) } } func TestWriterBigBatch(t *testing.T) { testWriterBigBatch(t) } func BenchmarkWriterBigBatch(b *testing.B) { for i := 0; i < b.N; i++ { testWriterBigBatch(b) } } func testWriterBigBatch(t testing.TB) { if testing.Short() { t.Skip("BigBatch test is not short") } ctx := context.Background() const batchSize = 1024 const numBatches = 4 nodes := make([]Node, 0, batchSize*numBatches) for x := 0; x < numBatches; x++ { for y := 0; y < batchSize; y++ { u := genNode(fmt.Sprintf("key-%d-%d", x, y), fmt.Sprintf("value-%d-%d", x, y)) nodes = append(nodes, u) } } w := NewWriter(treeID, hasher, 256, 8) rootUpd := update(ctx, t, w, &testAccessor{}, nodes) // Calculated using Python code from the original Revocation Transparency // doc: https://www.links.org/files/RevocationTransparency.pdf, but using the // CONIKS hasher instead. want := b64("P2SiYPpD858dVfAIG5RW0dxKKm7ZQr6DrhVIMDBWcJY=") if got := rootUpd.Hash; !bytes.Equal(got, want) { t.Errorf("root mismatch: got %x, want %x", got, want) } } func TestWriterBigBatchMultipleWrites(t *testing.T) { if testing.Short() { t.Skip("BigBatch test is not short") } ctx := context.Background() const batchSize = 1024 const numBatches = 4 roots := [numBatches][]byte{ b64("aAMq3tm3aChuTrjocEp9pau/rERbY3ClQ5iLuvkOwAw="), b64("8F5CF69Dkhebse22dhPvmwxaXGESqtKfQB3A8rMLh9k="), b64("f1b6zuA5OuG2Joedcq0XYm9AwGUw//C2ZAyGxqOv+G4="), b64("P2SiYPpD858dVfAIG5RW0dxKKm7ZQr6DrhVIMDBWcJY="), } w := NewWriter(treeID, hasher, 256, 8) acc := &testAccessor{h: make(map[node.ID][]byte), save: true} for i := 0; i < numBatches; i++ { nodes := make([]Node, 0, batchSize) for j := 0; j < batchSize; j++ { u := genNode(fmt.Sprintf("key-%d-%d", i, j), fmt.Sprintf("value-%d-%d", i, j)) nodes = append(nodes, u) } rootUpd := update(ctx, t, w, acc, nodes) if got, want := rootUpd.Hash, roots[i]; !bytes.Equal(got, want) { t.Errorf("%d: root mismatch: got %x, want %x", i, got, want) } } } func update(ctx context.Context, t testing.TB, w *Writer, acc NodeBatchAccessor, nodes []Node) Node { shards, err := w.Split(nodes) if err != nil { t.Fatalf("Split: %v", err) } var mu sync.Mutex splitUpd := make([]Node, 0, 256) eg, _ := errgroup.WithContext(ctx) for _, nodes := range shards { nodes := nodes eg.Go(func() error { rootUpd, err := w.Write(ctx, nodes, acc) if err != nil { return err } mu.Lock() defer mu.Unlock() splitUpd = append(splitUpd, rootUpd) return nil }) } if err := eg.Wait(); err != nil { t.Fatalf("Wait: %v", err) } rootUpd, err := w.Write(ctx, splitUpd, acc) if err != nil { t.Fatalf("Write: %v", err) } return rootUpd } // genNode returns a Node for the given key and value. The returned node ID is // 256-bit map key based on SHA256 of the given key string. func genNode(key, value string) Node { key256 := sha256.Sum256([]byte(key)) id := node.NewID(string(key256[:]), uint(len(key256)*8)) hash := hasher.HashLeaf(treeID, id, []byte(value)) return Node{ID: id, Hash: hash} } // testAccessor implements NodeBatchAccessor for testing purposes. type testAccessor struct { mu sync.RWMutex // Guards the h map. h map[node.ID][]byte save bool // Persist node updates in this accessor. get error // The error returned by Get. set error // The error returned by Set. } func (t *testAccessor) Get(ctx context.Context, ids []node.ID) (map[node.ID][]byte, error) { if err := t.get; err != nil { return nil, err } else if !t.save { return nil, nil } t.mu.RLock() defer t.mu.RUnlock() h := make(map[node.ID][]byte, len(ids)) for _, id := range ids { if hash, ok := t.h[id]; ok { h[id] = hash } } return h, nil } func (t *testAccessor) Set(ctx context.Context, nodes []Node) error { if err := t.set; err != nil { return err } else if !t.save { return nil } t.mu.Lock() defer t.mu.Unlock() for _, n := range nodes { t.h[n.ID] = n.Hash } return nil } trillian-1.6.1/monitoring/000077500000000000000000000000001466362047600155405ustar00rootroot00000000000000trillian-1.6.1/monitoring/buckets.go000066400000000000000000000033241466362047600175310ustar00rootroot00000000000000// 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 monitoring // This file contains helpers for constructing buckets for use with // Histogram metrics. // PercentileBuckets returns a range of buckets for 0.0-100.0% use cases. // in specified integer increments. The increment must be at least 1%, which // prevents creating very large metric exports. func PercentileBuckets(inc int64) []float64 { if inc <= 0 || inc > 100 { return nil } r := make([]float64, 0, 100/inc) var v int64 for v < 100 { r = append(r, float64(v)) v += inc } return r } // LatencyBuckets returns a reasonable range of histogram upper limits for most // latency-in-seconds usecases. The thresholds increase exponentially from 0.04 // seconds to ~282 days. func LatencyBuckets() []float64 { return ExpBuckets(0.04, 1.07, 300) } // ExpBuckets returns the specified number of histogram buckets with // exponentially increasing thresholds. The thresholds vary between base and // base * mult^(buckets-1). func ExpBuckets(base, mult float64, buckets uint) []float64 { r := make([]float64, buckets) for i, exp := uint(0), base; i < buckets; i, exp = i+1, exp*mult { r[i] = exp } return r } trillian-1.6.1/monitoring/buckets_test.go000066400000000000000000000075741466362047600206030ustar00rootroot00000000000000// 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 monitoring_test import ( "fmt" "math" "testing" "github.com/google/trillian/monitoring" ) func TestPercentileBucketsInvalid(t *testing.T) { for _, inc := range []int64{0, -1, -50, 300, 40000000} { t.Run(fmt.Sprintf("increment %d", inc), func(t *testing.T) { if got := monitoring.PercentileBuckets(inc); got != nil { t.Errorf("PercentileBuckets: got: %v for invalid case, want: nil", got) } }) } } func TestPercentileBuckets(t *testing.T) { for _, inc := range []int64{1, 2, 10, 25, 46, 97} { t.Run(fmt.Sprintf("increment %d", inc), func(t *testing.T) { buckets := monitoring.PercentileBuckets(inc) // The number of buckets expected to be created is fixed. if got, want := len(buckets), int(100/inc); math.Abs(float64(got-want)) > 1 { t.Errorf("PercentileBuckets(): got len: %d, want: %d", got, want) } // The first bucket should always be close to 0%. if buckets[0] < 0 || buckets[0] > 0.0001 { t.Errorf("PercentileBuckets(): got first bucket: %v, want: ~0.0", buckets[0]) } // The last bucket should be on the way towards 100%. It doesn't make a // lot of sense to create an extremely coarse grained distribution but // it's not actually wrong so no reason to reject it. if got, want := math.Abs(buckets[len(buckets)-1]-75.0), 25.0; got > want { t.Errorf("PercentileBuckets(): got last bucket diff: %v, want: <%v", got, want) } // Percentile buckets should increase monotonically. for i := 0; i < len(buckets)-1; i++ { if buckets[i] > buckets[i+1] { t.Errorf("PercentileBuckets(): buckets out of order at index: %d", i) } } }) } } func TestLatencyBuckets(t *testing.T) { // Just do some probes on the result to make sure it looks sensible. buckets := monitoring.LatencyBuckets() checkExpBuckets(t, buckets, 0.04, 1.07, 300) // Highest bucket should be about 282 days (allow some leeway). expected := 282 * 24 * 3600.0 // 282 days. precision := 17 * 3600.0 // A bit less than one day. if got := math.Abs(buckets[len(buckets)-1] - expected); got > precision { t.Errorf("LatencyBuckets(): got last bucket diff: %v, want: <%v", got, precision) } } func TestExpBuckets(t *testing.T) { for _, tc := range []struct { base float64 mult float64 count uint }{ {base: 1.0, mult: 2.0, count: 20}, {base: 0.04, mult: 1.07, count: 300}, } { t.Run("", func(t *testing.T) { buckets := monitoring.ExpBuckets(tc.base, tc.mult, tc.count) checkExpBuckets(t, buckets, tc.base, tc.mult, tc.count) }) } } func checkExpBuckets(t *testing.T, buckets []float64, base, mult float64, count uint) { t.Helper() if got, want := len(buckets), int(count); got != want { t.Errorf("unexpected length %d, want %d", got, want) } // Bucket thresholds should increase monotonically. for i := 0; i < len(buckets)-1; i++ { if buckets[i] >= buckets[i+1] { t.Fatalf("out of order at index %d", i) } } // Lowest bucket should be equal to base. if got, want := buckets[0], base; math.Abs(got-want) > 0.001 { t.Errorf("got first bucket: %v, want: ~%v", got, want) } // Highest bucket should be about 86400 sec = 1 day (allow some leeway)/ last := base * math.Pow(mult, float64(count-1)) if got, want := buckets[len(buckets)-1], last; math.Abs(got-want) > 0.001 { t.Errorf("got last bucket: %v, want: ~%v", got, want) } } trillian-1.6.1/monitoring/inert.go000066400000000000000000000077371466362047600172260ustar00rootroot00000000000000// 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 monitoring import ( "fmt" "strings" "sync" "k8s.io/klog/v2" ) // InertMetricFactory creates inert metrics for testing. type InertMetricFactory struct{} // NewCounter creates a new inert Counter. func (imf InertMetricFactory) NewCounter(name, help string, labelNames ...string) Counter { return &InertFloat{ labelCount: len(labelNames), vals: make(map[string]float64), } } // NewGauge creates a new inert Gauge. func (imf InertMetricFactory) NewGauge(name, help string, labelNames ...string) Gauge { return &InertFloat{ labelCount: len(labelNames), vals: make(map[string]float64), } } // NewHistogram creates a new inert Histogram. func (imf InertMetricFactory) NewHistogram(name, help string, labelNames ...string) Histogram { return &InertDistribution{ labelCount: len(labelNames), counts: make(map[string]uint64), sums: make(map[string]float64), } } // NewHistogramWithBuckets creates a new inert Histogram with supplied buckets. // The buckets are not actually used. func (imf InertMetricFactory) NewHistogramWithBuckets(name, help string, _ []float64, labelNames ...string) Histogram { return imf.NewHistogram(name, help, labelNames...) } // InertFloat is an internal-only implementation of both the Counter and Gauge interfaces. type InertFloat struct { labelCount int mu sync.Mutex vals map[string]float64 } // Inc adds 1 to the value. func (m *InertFloat) Inc(labelVals ...string) { m.Add(1.0, labelVals...) } // Dec subtracts 1 from the value. func (m *InertFloat) Dec(labelVals ...string) { m.Add(-1.0, labelVals...) } // Add adds the given amount to the value. func (m *InertFloat) Add(val float64, labelVals ...string) { m.mu.Lock() defer m.mu.Unlock() key, err := keyForLabels(labelVals, m.labelCount) if err != nil { klog.Error(err.Error()) return } m.vals[key] += val } // Set sets the value. func (m *InertFloat) Set(val float64, labelVals ...string) { m.mu.Lock() defer m.mu.Unlock() key, err := keyForLabels(labelVals, m.labelCount) if err != nil { klog.Error(err.Error()) return } m.vals[key] = val } // Value returns the current value. func (m *InertFloat) Value(labelVals ...string) float64 { m.mu.Lock() defer m.mu.Unlock() key, err := keyForLabels(labelVals, m.labelCount) if err != nil { klog.Error(err.Error()) return 0.0 } return m.vals[key] } // InertDistribution is an internal-only implementation of the Distribution interface. type InertDistribution struct { labelCount int mu sync.Mutex counts map[string]uint64 sums map[string]float64 } // Observe adds a single observation to the distribution. func (m *InertDistribution) Observe(val float64, labelVals ...string) { m.mu.Lock() defer m.mu.Unlock() key, err := keyForLabels(labelVals, m.labelCount) if err != nil { klog.Error(err.Error()) return } m.counts[key]++ m.sums[key] += val } // Info returns count, sum for the distribution. func (m *InertDistribution) Info(labelVals ...string) (uint64, float64) { m.mu.Lock() defer m.mu.Unlock() key, err := keyForLabels(labelVals, m.labelCount) if err != nil { klog.Error(err.Error()) return 0, 0.0 } return m.counts[key], m.sums[key] } func keyForLabels(labelVals []string, count int) (string, error) { if len(labelVals) != count { return "", fmt.Errorf("invalid label count %d; want %d", len(labelVals), count) } return strings.Join(labelVals, "|"), nil } trillian-1.6.1/monitoring/labels.go000066400000000000000000000014441466362047600173340ustar00rootroot00000000000000// 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 monitoring const ( // TreeIDLabel is the monitoring label used to represent a tree ID. // TODO(codingllama): Consider using TreeIDLabel in place of log ID. TreeIDLabel = "tree_id" ) trillian-1.6.1/monitoring/metrics.go000066400000000000000000000036301466362047600175370ustar00rootroot00000000000000// 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 monitoring // MetricFactory allows the creation of different types of metric. type MetricFactory interface { NewCounter(name, help string, labelNames ...string) Counter NewGauge(name, help string, labelNames ...string) Gauge NewHistogram(name, help string, labelNames ...string) Histogram NewHistogramWithBuckets(name, help string, buckets []float64, labelNames ...string) Histogram } // Counter is a metric class for numeric values that increase. type Counter interface { Inc(labelVals ...string) Add(val float64, labelVals ...string) Value(labelVals ...string) float64 } // Gauge is a metric class for numeric values that can go up and down. type Gauge interface { Inc(labelVals ...string) Dec(labelVals ...string) Add(val float64, labelVals ...string) Set(val float64, labelVals ...string) // Value retrieves the value for a particular set of labels. // This is only really useful for testing implementations. Value(labelVals ...string) float64 } // Histogram is a metric class that tracks the distribution of a collection // of observations. type Histogram interface { Observe(val float64, labelVals ...string) // Info retrieves the count and sum of observations for a particular set of labels. // This is only really useful for testing implementations. Info(labelVals ...string) (uint64, float64) } trillian-1.6.1/monitoring/metrics_test.go000066400000000000000000000020071466362047600205730ustar00rootroot00000000000000// 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 monitoring_test import ( "testing" "github.com/google/trillian/monitoring" "github.com/google/trillian/monitoring/testonly" ) func TestCounter(t *testing.T) { testonly.TestCounter(t, monitoring.InertMetricFactory{}) } func TestGauge(t *testing.T) { testonly.TestGauge(t, monitoring.InertMetricFactory{}) } func TestHistogram(t *testing.T) { testonly.TestHistogram(t, monitoring.InertMetricFactory{}) } trillian-1.6.1/monitoring/opencensus/000077500000000000000000000000001466362047600177225ustar00rootroot00000000000000trillian-1.6.1/monitoring/opencensus/trace.go000066400000000000000000000076751466362047600213660ustar00rootroot00000000000000// Copyright 2018 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 opencensus enables tracing and metrics collection using OpenCensus. package opencensus import ( "context" "errors" "net/http" "contrib.go.opencensus.io/exporter/stackdriver" "go.opencensus.io/plugin/ocgrpc" "go.opencensus.io/plugin/ochttp" "go.opencensus.io/stats/view" "go.opencensus.io/trace" "google.golang.org/grpc" ) // This is the same set of views that used to be the default before that // was deprecated. Possibly some of these are not useful but for the moment // we don't really know that. var serverViews = []*view.View{ ochttp.ServerRequestCountView, ochttp.ServerRequestBytesView, ochttp.ServerResponseBytesView, ochttp.ServerLatencyView, ochttp.ServerRequestCountByMethod, ochttp.ServerResponseCountByStatusCode, } // EnableRPCServerTracing turns on Stackdriver tracing. The returned // options must be passed to the GRPC server. The supplied // projectID can be nil for GCP but might need to be set for other // cloud platforms. Refer to the appropriate documentation. The percentage // of traced requests can be set between 0 and 100. Note that 0 does not // disable tracing entirely but causes the default configuration to be used. func EnableRPCServerTracing(projectID string, percent int) ([]grpc.ServerOption, error) { if err := exporter(projectID); err != nil { return nil, err } if err := applyConfig(percent); err != nil { return nil, err } // Register the views to collect server request count. if err := view.Register(ocgrpc.DefaultServerViews...); err != nil { return nil, err } return []grpc.ServerOption{grpc.StatsHandler(&ocgrpc.ServerHandler{})}, nil } // EnableHTTPServerTracing turns on Stackdriver tracing for HTTP requests // on the default ServeMux. The returned handler must be passed to the HTTP // server. The supplied projectID can be nil for GCP but might need to be set // for other cloud platforms. Refer to the appropriate documentation. // The percentage of traced requests can be set between 0 and 100. Note that 0 // does not disable tracing entirely but causes the default configuration to be // used. func EnableHTTPServerTracing(projectID string, percent int) (http.Handler, error) { if err := exporter(projectID); err != nil { return nil, err } if err := applyConfig(percent); err != nil { return nil, err } if err := view.Register(serverViews...); err != nil { return nil, err } return &ochttp.Handler{}, nil } func exporter(projectID string) error { sde, err := stackdriver.NewExporter(stackdriver.Options{ProjectID: projectID}) if err != nil { return err } view.RegisterExporter(sde) trace.RegisterExporter(sde) return nil } func applyConfig(percent int) error { switch { case percent == 0: // Use the default config, which traces relatively few requests. case percent == 100: trace.ApplyConfig(trace.Config{DefaultSampler: trace.AlwaysSample()}) case percent > 100: return errors.New("cannot trace more than 100 percent of requests") default: trace.ApplyConfig(trace.Config{DefaultSampler: trace.ProbabilitySampler(float64(percent) / 100.0)}) } return nil } // StartSpan starts a new tracing span. // The returned context should be used for all child calls within the span, and // the returned func should be called to close the span. func StartSpan(ctx context.Context, name string) (context.Context, func()) { ctx, span := trace.StartSpan(ctx, name) return ctx, span.End } trillian-1.6.1/monitoring/prometheus/000077500000000000000000000000001466362047600177335ustar00rootroot00000000000000trillian-1.6.1/monitoring/prometheus/metrics.go000066400000000000000000000203661466362047600217370ustar00rootroot00000000000000// 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 prometheus provides a Prometheus-based implementation of the // MetricFactory abstraction. package prometheus import ( "fmt" "github.com/google/trillian/monitoring" "github.com/prometheus/client_golang/prometheus" dto "github.com/prometheus/client_model/go" "k8s.io/klog/v2" ) // MetricFactory allows the creation of Prometheus-based metrics. type MetricFactory struct { // Prefix is an identifier that will be used before local metric names that // are reported. It is strongly recommended that this ends with a valid // separator (e.g. "_") in order to improve readability; no separator is // added by this library. Prefix string } // NewCounter creates a new Counter object backed by Prometheus. func (pmf MetricFactory) NewCounter(name, help string, labelNames ...string) monitoring.Counter { if len(labelNames) == 0 { counter := prometheus.NewCounter( prometheus.CounterOpts{ Name: pmf.Prefix + name, Help: help, }) prometheus.MustRegister(counter) return &Counter{single: counter} } vec := prometheus.NewCounterVec( prometheus.CounterOpts{ Name: pmf.Prefix + name, Help: help, }, labelNames) prometheus.MustRegister(vec) return &Counter{labelNames: labelNames, vec: vec} } // NewGauge creates a new Gauge object backed by Prometheus. func (pmf MetricFactory) NewGauge(name, help string, labelNames ...string) monitoring.Gauge { if len(labelNames) == 0 { gauge := prometheus.NewGauge( prometheus.GaugeOpts{ Name: pmf.Prefix + name, Help: help, }) prometheus.MustRegister(gauge) return &Gauge{single: gauge} } vec := prometheus.NewGaugeVec( prometheus.GaugeOpts{ Name: pmf.Prefix + name, Help: help, }, labelNames) prometheus.MustRegister(vec) return &Gauge{labelNames: labelNames, vec: vec} } // NewHistogramWithBuckets creates a new Histogram object backed by // Prometheus and using the supplied bucketing intervals. Note: the // number of buckets should be kept within reasonable bounds. func (pmf MetricFactory) NewHistogramWithBuckets(name, help string, buckets []float64, labelNames ...string) monitoring.Histogram { return pmf.newHistogram(name, help, buckets, labelNames) } // NewHistogram creates a new Histogram object backed by Prometheus with // the supplied buckets. func (pmf MetricFactory) NewHistogram(name, help string, labelNames ...string) monitoring.Histogram { return pmf.newHistogram(name, help, monitoring.LatencyBuckets(), labelNames) } func (pmf MetricFactory) newHistogram(name, help string, buckets []float64, labelNames []string) monitoring.Histogram { if len(labelNames) == 0 { histogram := prometheus.NewHistogram( prometheus.HistogramOpts{ Name: pmf.Prefix + name, Help: help, Buckets: buckets, }) prometheus.MustRegister(histogram) return &Histogram{single: histogram} } vec := prometheus.NewHistogramVec( prometheus.HistogramOpts{ Name: pmf.Prefix + name, Help: help, Buckets: buckets, }, labelNames) prometheus.MustRegister(vec) return &Histogram{labelNames: labelNames, vec: vec} } // Counter is a wrapper around a Prometheus Counter or CounterVec object. type Counter struct { labelNames []string single prometheus.Counter vec *prometheus.CounterVec } // Inc adds 1 to a counter. func (m *Counter) Inc(labelVals ...string) { labels, err := labelsFor(m.labelNames, labelVals) if err != nil { klog.Error(err.Error()) return } if m.vec != nil { m.vec.With(labels).Inc() } else { m.single.Inc() } } // Add adds the given amount to a counter. func (m *Counter) Add(val float64, labelVals ...string) { labels, err := labelsFor(m.labelNames, labelVals) if err != nil { klog.Error(err.Error()) return } if m.vec != nil { m.vec.With(labels).Add(val) } else { m.single.Add(val) } } // Value returns the current amount of a counter. func (m *Counter) Value(labelVals ...string) float64 { labels, err := labelsFor(m.labelNames, labelVals) if err != nil { klog.Error(err.Error()) return 0.0 } var metric prometheus.Metric if m.vec != nil { metric = m.vec.With(labels) } else { metric = m.single } var metricpb dto.Metric if err := metric.Write(&metricpb); err != nil { klog.Errorf("failed to Write metric: %v", err) return 0.0 } if metricpb.Counter == nil { klog.Errorf("counter field missing") return 0.0 } return metricpb.Counter.GetValue() } // Gauge is a wrapper around a Prometheus Gauge or GaugeVec object. type Gauge struct { labelNames []string single prometheus.Gauge vec *prometheus.GaugeVec } // Inc adds 1 to a gauge. func (m *Gauge) Inc(labelVals ...string) { labels, err := labelsFor(m.labelNames, labelVals) if err != nil { klog.Error(err.Error()) return } if m.vec != nil { m.vec.With(labels).Inc() } else { m.single.Inc() } } // Dec subtracts 1 from a gauge. func (m *Gauge) Dec(labelVals ...string) { labels, err := labelsFor(m.labelNames, labelVals) if err != nil { klog.Error(err.Error()) return } if m.vec != nil { m.vec.With(labels).Dec() } else { m.single.Dec() } } // Add adds given value to a gauge. func (m *Gauge) Add(val float64, labelVals ...string) { labels, err := labelsFor(m.labelNames, labelVals) if err != nil { klog.Error(err.Error()) return } if m.vec != nil { m.vec.With(labels).Add(val) } else { m.single.Add(val) } } // Set sets the value of a gauge. func (m *Gauge) Set(val float64, labelVals ...string) { labels, err := labelsFor(m.labelNames, labelVals) if err != nil { klog.Error(err.Error()) return } if m.vec != nil { m.vec.With(labels).Set(val) } else { m.single.Set(val) } } // Value returns the current amount of a gauge. func (m *Gauge) Value(labelVals ...string) float64 { labels, err := labelsFor(m.labelNames, labelVals) if err != nil { klog.Error(err.Error()) return 0.0 } var metric prometheus.Metric if m.vec != nil { metric = m.vec.With(labels) } else { metric = m.single } var metricpb dto.Metric if err := metric.Write(&metricpb); err != nil { klog.Errorf("failed to Write metric: %v", err) return 0.0 } if metricpb.Gauge == nil { klog.Errorf("gauge field missing") return 0.0 } return metricpb.Gauge.GetValue() } // Histogram is a wrapper around a Prometheus Histogram or HistogramVec object. type Histogram struct { labelNames []string single prometheus.Histogram vec *prometheus.HistogramVec } // Observe adds a single observation to the histogram. func (m *Histogram) Observe(val float64, labelVals ...string) { labels, err := labelsFor(m.labelNames, labelVals) if err != nil { klog.Error(err.Error()) return } if m.vec != nil { m.vec.With(labels).Observe(val) } else { m.single.Observe(val) } } // Info returns the count and sum of observations for the histogram. func (m *Histogram) Info(labelVals ...string) (uint64, float64) { labels, err := labelsFor(m.labelNames, labelVals) if err != nil { klog.Error(err.Error()) return 0, 0.0 } var metric prometheus.Metric if m.vec != nil { metric = m.vec.With(labels).(prometheus.Metric) } else { metric = m.single } var metricpb dto.Metric if err := metric.Write(&metricpb); err != nil { klog.Errorf("failed to Write metric: %v", err) return 0, 0.0 } histVal := metricpb.GetHistogram() if histVal == nil { klog.Errorf("histogram field missing") return 0, 0.0 } return histVal.GetSampleCount(), histVal.GetSampleSum() } func labelsFor(names, values []string) (prometheus.Labels, error) { if len(names) != len(values) { return nil, fmt.Errorf("got %d (%v) values for %d labels (%v)", len(values), values, len(names), names) } if len(names) == 0 { return nil, nil } labels := make(prometheus.Labels) for i, name := range names { labels[name] = values[i] } return labels, nil } trillian-1.6.1/monitoring/prometheus/metrics_test.go000066400000000000000000000017501466362047600227720ustar00rootroot00000000000000// 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 prometheus import ( "testing" "github.com/google/trillian/monitoring/testonly" ) func TestCounter(t *testing.T) { testonly.TestCounter(t, MetricFactory{Prefix: "TestCounter"}) } func TestGauge(t *testing.T) { testonly.TestGauge(t, MetricFactory{Prefix: "TestGauge"}) } func TestHistogram(t *testing.T) { testonly.TestHistogram(t, MetricFactory{Prefix: "TestHistogram"}) } trillian-1.6.1/monitoring/rpc_stats_interceptor.go000066400000000000000000000074321466362047600225150ustar00rootroot00000000000000// 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 monitoring provides monitoring functionality. package monitoring import ( "context" "fmt" "time" "github.com/google/trillian/util/clock" "google.golang.org/grpc" ) const traceSpanRoot = "/trillian/mon/" // RPCStatsInterceptor provides a gRPC interceptor that records statistics about the RPCs passing through it. type RPCStatsInterceptor struct { prefix string timeSource clock.TimeSource ReqCount Counter ReqSuccessCount Counter ReqSuccessLatency Histogram ReqErrorCount Counter ReqErrorLatency Histogram } // NewRPCStatsInterceptor creates a new RPCStatsInterceptor for the given application/component, with // a specified time source. func NewRPCStatsInterceptor(timeSource clock.TimeSource, prefix string, mf MetricFactory) *RPCStatsInterceptor { if mf == nil { mf = InertMetricFactory{} } interceptor := RPCStatsInterceptor{ prefix: prefix, timeSource: timeSource, ReqCount: mf.NewCounter(prefixedName(prefix, "rpc_requests"), "Number of requests", "method"), ReqSuccessCount: mf.NewCounter(prefixedName(prefix, "rpc_success"), "Number of successful requests", "method"), ReqSuccessLatency: mf.NewHistogram(prefixedName(prefix, "rpc_success_latency"), "Latency of successful requests in seconds", "method"), ReqErrorCount: mf.NewCounter(prefixedName(prefix, "rpc_errors"), "Number of errored requests", "method"), ReqErrorLatency: mf.NewHistogram(prefixedName(prefix, "rpc_error_latency"), "Latency of errored requests in seconds", "method"), } return &interceptor } func prefixedName(prefix, name string) string { return fmt.Sprintf("%s_%s", prefix, name) } func (r *RPCStatsInterceptor) recordFailureLatency(labels []string, startTime time.Time) { latency := clock.SecondsSince(r.timeSource, startTime) r.ReqErrorCount.Inc(labels...) r.ReqErrorLatency.Observe(latency, labels...) } // Interceptor returns a UnaryServerInterceptor that can be registered with an RPC server and // will record request counts / errors and latencies for that servers handlers func (r *RPCStatsInterceptor) Interceptor() grpc.UnaryServerInterceptor { return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { labels := []string{info.FullMethod} // This interceptor wraps the request handler so we should track the // additional latency it imposes. ctx, spanEnd := StartSpan(ctx, traceSpanRoot) defer spanEnd() // Increase the request count for the method and start the clock r.ReqCount.Inc(labels...) startTime := r.timeSource.Now() defer func() { if rec := recover(); rec != nil { // If we reach here then the handler exited via panic, count it as a server failure r.recordFailureLatency(labels, startTime) panic(rec) } }() // Invoke the actual operation rsp, err := handler(ctx, req) // Record success / failure and latency if err != nil { r.recordFailureLatency(labels, startTime) } else { latency := clock.SecondsSince(r.timeSource, startTime) r.ReqSuccessCount.Inc(labels...) r.ReqSuccessLatency.Observe(latency, labels...) } // Pass the result of the handler invocation back return rsp, err } } trillian-1.6.1/monitoring/rpc_stats_interceptor_test.go000066400000000000000000000141031466362047600235450ustar00rootroot00000000000000// 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 monitoring_test import ( "context" "errors" "fmt" "testing" "time" "github.com/google/trillian/monitoring" "github.com/google/trillian/util/clock" "google.golang.org/grpc" ) // Arbitrary time for use in tests var fakeTime = time.Date(2016, 10, 3, 12, 38, 27, 36, time.UTC) type recordingUnaryHandler struct { // ctx and req are recorded on invocation ctx context.Context req interface{} // rsp and err are returned on invocation rsp interface{} err error } func (r recordingUnaryHandler) handler() grpc.UnaryHandler { return func(ctx context.Context, req interface{}) (interface{}, error) { r.ctx = ctx r.req = req return r.rsp, r.err } } func TestSingleRequests(t *testing.T) { tests := []struct { name string method string handler recordingUnaryHandler timeSource clock.PredefinedFake }{ // This is an OK request with 500ms latency { name: "ok_request", method: "getmethod", handler: recordingUnaryHandler{req: "OK", err: nil}, timeSource: clock.PredefinedFake{ Base: fakeTime, Delays: []time.Duration{0, time.Millisecond * 500}, }, }, // This is an errored request with 3000ms latency { name: "error_request", method: "setmethod", handler: recordingUnaryHandler{err: errors.New("bang")}, timeSource: clock.PredefinedFake{ Base: fakeTime, Delays: []time.Duration{0, time.Millisecond * 3000}, }, }, } for _, test := range tests { prefix := fmt.Sprintf("test_%s", test.name) stats := monitoring.NewRPCStatsInterceptor(&test.timeSource, prefix, monitoring.InertMetricFactory{}) i := stats.Interceptor() // Invoke the test handler wrapped by the interceptor. got, err := i(context.Background(), "wibble", &grpc.UnaryServerInfo{FullMethod: test.method}, test.handler.handler()) // Check the interceptor passed through the results. if got != test.handler.rsp || (err != nil) != (test.handler.err != nil) { t.Errorf("interceptor(%s)=%v,%v; want %v,%v", test.name, got, err, test.handler.rsp, test.handler.err) } // Now check the resulting state of the metrics. if got, want := stats.ReqCount.Value(test.method), 1.0; got != want { t.Errorf("stats.ReqCount=%v; want %v", got, want) } wantLatency := test.timeSource.Delays[1].Seconds() wantErrors := 0.0 wantSuccess := 0.0 if test.handler.err == nil { wantSuccess = 1.0 } else { wantErrors = 1.0 } if got := stats.ReqSuccessCount.Value(test.method); got != wantSuccess { t.Errorf("stats.ReqSuccessCount=%v; want %v", got, wantSuccess) } if got := stats.ReqErrorCount.Value(test.method); got != wantErrors { t.Errorf("stats.ReqErrorCount=%v; want %v", got, wantSuccess) } if gotCount, gotSum := stats.ReqSuccessLatency.Info(test.method); gotCount != uint64(wantSuccess) { t.Errorf("stats.ReqSuccessLatency.Count=%v; want %v", gotCount, wantSuccess) } else if gotSum != wantLatency*wantSuccess { t.Errorf("stats.ReqSuccessLatency.Sum=%v; want %v", gotSum, wantLatency*wantSuccess) } if gotCount, gotSum := stats.ReqErrorLatency.Info(test.method); gotCount != uint64(wantErrors) { t.Errorf("stats.ReqErrorLatency.Count=%v; want %v", gotCount, wantErrors) } else if gotSum != wantLatency*wantErrors { t.Errorf("stats.ReqErrorLatency.Sum=%v; want %v", gotSum, wantLatency*wantErrors) } } } func TestMultipleOKRequestsTotalLatency(t *testing.T) { // We're going to make 3 requests so set up the time source appropriately ts := clock.PredefinedFake{ Base: fakeTime, Delays: []time.Duration{ 0, time.Millisecond * 500, 0, time.Millisecond * 2000, 0, time.Millisecond * 1337, }, } handler := recordingUnaryHandler{rsp: "OK", err: nil} stats := monitoring.NewRPCStatsInterceptor(&ts, "test_multi_ok", monitoring.InertMetricFactory{}) i := stats.Interceptor() for r := 0; r < 3; r++ { rsp, err := i(context.Background(), "wibble", &grpc.UnaryServerInfo{FullMethod: "testmethod"}, handler.handler()) if rsp != "OK" || err != nil { t.Fatalf("interceptor()=%v,%v; want 'OK',nil", rsp, err) } } count, sum := stats.ReqSuccessLatency.Info("testmethod") if wantCount, wantSum := uint64(3), time.Duration(3837*time.Millisecond).Seconds(); count != wantCount || sum != wantSum { t.Errorf("stats.ReqSuccessLatency.Info=%v,%v; want %v,%v", count, sum, wantCount, wantSum) } } func TestMultipleErrorRequestsTotalLatency(t *testing.T) { // We're going to make 3 requests so set up the time source appropriately ts := clock.PredefinedFake{ Base: fakeTime, Delays: []time.Duration{ 0, time.Millisecond * 427, 0, time.Millisecond * 1066, 0, time.Millisecond * 1123, }, } handler := recordingUnaryHandler{rsp: "", err: errors.New("bang")} stats := monitoring.NewRPCStatsInterceptor(&ts, "test_multi_err", monitoring.InertMetricFactory{}) i := stats.Interceptor() for r := 0; r < 3; r++ { _, err := i(context.Background(), "wibble", &grpc.UnaryServerInfo{FullMethod: "testmethod"}, handler.handler()) if err == nil { t.Fatalf("interceptor()=_,%v; want _,'bang'", err) } } count, sum := stats.ReqErrorLatency.Info("testmethod") if wantCount, wantSum := uint64(3), 2.6160; count != wantCount || sum != wantSum { t.Errorf("stats.ReqSuccessLatency.Info=%v,%v; want %v,%v", count, sum, wantCount, wantSum) } } func TestCanInitializeNilMetricFactory(t *testing.T) { ts := clock.PredefinedFake{ Base: fakeTime, Delays: []time.Duration{}, } monitoring.NewRPCStatsInterceptor(&ts, "test_nil_metric_factory", nil) // Should reach here without throwing an exception } trillian-1.6.1/monitoring/testonly/000077500000000000000000000000001466362047600174215ustar00rootroot00000000000000trillian-1.6.1/monitoring/testonly/delta.go000066400000000000000000000036701466362047600210470ustar00rootroot00000000000000// Copyright 2018 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/google/trillian/monitoring" // CounterSnapshot records the latest value from a time series in a counter. // This value can then be compared with future values. Note that a counter can // contain many time series, but a CounterSnapshot will track only one. // A CounterSnapshot is useful in tests because counters do not reset between // test cases, and so their absolute value is not amenable to testing. Instead, // the delta between the value at the start and end of the test should be used. // This assumes that multiple tests that all affect the counter are not run in // parallel. type CounterSnapshot struct { c monitoring.Counter labels []string value float64 } // NewCounterSnapshot records the latest value of a time series in c identified // by the given labels. This value can be compared to future values to determine // how it has changed over time. func NewCounterSnapshot(c monitoring.Counter, labels ...string) CounterSnapshot { if c == nil { panic("can't take snapshot of nil counter") } return CounterSnapshot{ c: c, labels: labels, value: c.Value(labels...), } } // Delta returns the difference between the latest value of the time series // and the value when the CounterSnapshot was created. func (s CounterSnapshot) Delta() float64 { return s.c.Value(s.labels...) - s.value } trillian-1.6.1/monitoring/testonly/doc.go000066400000000000000000000012461466362047600205200ustar00rootroot00000000000000// Copyright 2022 Trillian Authors. 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 test-only code. package testonly trillian-1.6.1/monitoring/testonly/metrics.go000066400000000000000000000221111466362047600214130ustar00rootroot00000000000000// 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 testonly import ( "testing" "github.com/google/trillian/monitoring" ) // TestCounter runs a test on a Counter produced from the provided MetricFactory. func TestCounter(t *testing.T, factory monitoring.MetricFactory) { tests := []struct { name string labelNames []string labelVals []string }{ { name: "counter0", labelNames: nil, labelVals: nil, }, { name: "counter1", labelNames: []string{"key1"}, labelVals: []string{"val1"}, }, { name: "counter2", labelNames: []string{"key1", "key2"}, labelVals: []string{"val1", "val2"}, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { counter := factory.NewCounter(test.name, "Test only", test.labelNames...) if got, want := counter.Value(test.labelVals...), 0.0; got != want { t.Errorf("Counter[%v].Value()=%v; want %v", test.labelVals, got, want) } counter.Inc(test.labelVals...) if got, want := counter.Value(test.labelVals...), 1.0; got != want { t.Errorf("Counter[%v].Value()=%v; want %v", test.labelVals, got, want) } counter.Add(2.5, test.labelVals...) if got, want := counter.Value(test.labelVals...), 3.5; got != want { t.Errorf("Counter[%v].Value()=%v; want %v", test.labelVals, got, want) } // Use an invalid number of labels. libels := append(test.labelVals, "bogus") counter.Add(10.0, libels...) counter.Inc(libels...) if got, want := counter.Value(libels...), 0.0; got != want { t.Errorf("Counter[%v].Value()=%v; want %v", libels, got, want) } // Check that the value hasn't changed. if got, want := counter.Value(test.labelVals...), 3.5; got != want { t.Errorf("Counter[%v].Value()=%v; want %v", test.labelVals, got, want) } // Use a different set of label values // Metrics with different valued label values, are distinct // This test is only applicable when a Metric has labels if test.labelVals != nil && len(test.labelVals) >= 1 { altLabels := make([]string, len(test.labelVals)) copy(altLabels, test.labelVals) altLabels[0] = "alt-val1" // Increment counter using this different set of label values counter.Add(25.0, altLabels...) if got, want := counter.Value(altLabels...), 25.0; got != want { t.Errorf("Counter[%v].Value()=%v; want %v", altLabels, got, want) } // Counter with original set of label values should be unchanged if got, want := counter.Value(test.labelVals...), 3.5; got != want { t.Errorf("Counter[%v].Value()=%v; want %v", test.labelVals, got, want) } } }) } } // TestGauge runs a test on a Gauge produced from the provided MetricFactory. func TestGauge(t *testing.T, factory monitoring.MetricFactory) { tests := []struct { name string labelNames []string labelVals []string }{ { name: "gauge0", labelNames: nil, labelVals: nil, }, { name: "gauge1", labelNames: []string{"key1"}, labelVals: []string{"val1"}, }, { name: "gauge2", labelNames: []string{"key1", "key2"}, labelVals: []string{"val1", "val2"}, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { gauge := factory.NewGauge(test.name, "Test only", test.labelNames...) if got, want := gauge.Value(test.labelVals...), 0.0; got != want { t.Errorf("Gauge[%v].Value()=%v; want %v", test.labelVals, got, want) } gauge.Inc(test.labelVals...) if got, want := gauge.Value(test.labelVals...), 1.0; got != want { t.Errorf("Gauge[%v].Value()=%v; want %v", test.labelVals, got, want) } gauge.Dec(test.labelVals...) if got, want := gauge.Value(test.labelVals...), 0.0; got != want { t.Errorf("Gauge[%v].Value()=%v; want %v", test.labelVals, got, want) } gauge.Add(2.5, test.labelVals...) if got, want := gauge.Value(test.labelVals...), 2.5; got != want { t.Errorf("Gauge[%v].Value()=%v; want %v", test.labelVals, got, want) } gauge.Set(42.0, test.labelVals...) if got, want := gauge.Value(test.labelVals...), 42.0; got != want { t.Errorf("Gauge[%v].Value()=%v; want %v", test.labelVals, got, want) } // Use an invalid number of labels. libels := append(test.labelVals, "bogus") gauge.Add(10.0, libels...) gauge.Inc(libels...) gauge.Dec(libels...) gauge.Set(120.0, libels...) // Ask for an invalid number of labels. if got, want := gauge.Value(libels...), 0.0; got != want { t.Errorf("Gauge[%v].Value()=%v; want %v", libels, got, want) } // Check that the value hasn't changed. if got, want := gauge.Value(test.labelVals...), 42.0; got != want { t.Errorf("Gauge[%v].Value()=%v; want %v", test.labelVals, got, want) } // Use a different set of label values // Metrics with different valued label values, are distinct // This test is only applicable when a Metric has labels if test.labelVals != nil && len(test.labelVals) >= 1 { altLabels := make([]string, len(test.labelVals)) copy(altLabels, test.labelVals) altLabels[0] = "alt-val1" // Set gauge using this different set of label values gauge.Set(25.0, altLabels...) if got, want := gauge.Value(altLabels...), 25.0; got != want { t.Errorf("Gauge[%v].Value()=%v; want %v", altLabels, got, want) } // Gauge with original set of label values should be unchanged if got, want := gauge.Value(test.labelVals...), 42.0; got != want { t.Errorf("Gauge[%v].Value()=%v; want %v", test.labelVals, got, want) } } }) } } // TestHistogram runs a test on a Histogram produced from the provided MetricFactory. func TestHistogram(t *testing.T, factory monitoring.MetricFactory) { tests := []struct { name string labelNames []string labelVals []string }{ { name: "histogram0", labelNames: nil, labelVals: nil, }, { name: "histogram1", labelNames: []string{"key1"}, labelVals: []string{"val1"}, }, { name: "histogram2", labelNames: []string{"key1", "key2"}, labelVals: []string{"val1", "val2"}, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { histogram := factory.NewHistogram(test.name, "Test only", test.labelNames...) gotCount, gotSum := histogram.Info(test.labelVals...) if wantCount, wantSum := uint64(0), 0.0; gotCount != wantCount || gotSum != wantSum { t.Errorf("Histogram[%v].Info()=%v,%v; want %v,%v", test.labelVals, gotCount, gotSum, wantCount, wantSum) } histogram.Observe(1.0, test.labelVals...) histogram.Observe(2.0, test.labelVals...) histogram.Observe(3.0, test.labelVals...) gotCount, gotSum = histogram.Info(test.labelVals...) if wantCount, wantSum := uint64(3), 6.0; gotCount != wantCount || gotSum != wantSum { t.Errorf("Histogram[%v].Info()=%v,%v; want %v,%v", test.labelVals, gotCount, gotSum, wantCount, wantSum) } // Use an invalid number of labels. libels := append(test.labelVals, "bogus") histogram.Observe(100.0, libels...) histogram.Observe(200.0, libels...) gotCount, gotSum = histogram.Info(libels...) if wantCount, wantSum := uint64(0), 0.0; gotCount != wantCount || gotSum != wantSum { t.Errorf("Histogram[%v].Info()=%v,%v; want %v,%v", libels, gotCount, gotSum, wantCount, wantSum) } // Check that the histogram hasn't changed. gotCount, gotSum = histogram.Info(test.labelVals...) if wantCount, wantSum := uint64(3), 6.0; gotCount != wantCount || gotSum != wantSum { t.Errorf("Histogram[%v].Info()=%v,%v; want %v,%v", test.labelVals, gotCount, gotSum, wantCount, wantSum) } // Use a different set of label values // Metrics with different valued label values, are distinct // This test is only applicable when a Metric has labels if test.labelVals != nil && len(test.labelVals) >= 1 { altLabels := make([]string, len(test.labelVals)) copy(altLabels, test.labelVals) altLabels[0] = "alt-val1" // Observe histogram using this different set of label values histogram.Observe(25.0, altLabels...) histogram.Observe(50.0, altLabels...) gotCount, gotSum = histogram.Info(altLabels...) if wantCount, wantSum := uint64(2), 75.0; gotCount != wantCount || gotSum != wantSum { t.Errorf("Histogram[%v].Info()=%v,%v; want %v,%v", altLabels, gotCount, gotSum, wantCount, wantSum) } // Histogram with original set of label values should be unchanged gotCount, gotSum = histogram.Info(test.labelVals...) if wantCount, wantSum := uint64(3), 6.0; gotCount != wantCount || gotSum != wantSum { t.Errorf("Histogram[%v].Info()=%v,%v; want %v,%v", test.labelVals, gotCount, gotSum, wantCount, wantSum) } } }) } } trillian-1.6.1/monitoring/trace.go000066400000000000000000000033261466362047600171710ustar00rootroot00000000000000// 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 monitoring import "context" // startSpanFunc is the signature of a function which can start tracing spans. type startSpanFunc func(context.Context, string) (context.Context, func()) var startSpan startSpanFunc = noopStartSpan // noopStartSpan is a span starting function which does nothing, and is used as // the default implementation. func noopStartSpan(ctx context.Context, _ string) (context.Context, func()) { return ctx, func() {} } // StartSpan starts a new tracing span using the given message. // The returned context should be used for all child calls within the span, and // the returned func should be called to close the span. // // The default implementation of this method is a no-op; insert a real tracing span // implementation by setting this global variable to the relevant function at start of day. func StartSpan(ctx context.Context, name string) (context.Context, func()) { return startSpan(ctx, name) } // SetStartSpan sets the function used to start tracing spans. // This may be used to add runtime support for different tracing implementation. func SetStartSpan(f startSpanFunc) { startSpan = f } trillian-1.6.1/quota/000077500000000000000000000000001466362047600145045ustar00rootroot00000000000000trillian-1.6.1/quota/cacheqm/000077500000000000000000000000001466362047600161055ustar00rootroot00000000000000trillian-1.6.1/quota/cacheqm/cache.go000066400000000000000000000131311466362047600174760ustar00rootroot00000000000000// 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 cacheqm contains a caching quota.Manager implementation. package cacheqm import ( "context" "fmt" "sort" "sync" "time" "github.com/google/trillian/quota" "k8s.io/klog/v2" ) const ( // DefaultMinBatchSize is the suggested default for minBatchSize. DefaultMinBatchSize = 100 // DefaultMaxCacheEntries is the suggested default for maxEntries. DefaultMaxCacheEntries = 1000 ) // now is used in place of time.Now to allow tests to take control of time. var now = time.Now type manager struct { quota.Manager minBatchSize, maxEntries int // mu guards cache mu sync.Mutex cache map[quota.Spec]*bucket // evictWg tracks evict() goroutines. evictWg sync.WaitGroup } type bucket struct { tokens int lastModified time.Time } // NewCachedManager wraps a quota.Manager with an implementation that caches tokens locally. // // minBatchSize determines the minimum number of tokens requested from qm for each GetTokens() // request. // // maxEntries determines the maximum number of cache entries, apart from global quotas. The oldest // entries are evicted as necessary, their tokens replenished via PutTokens() to avoid excessive // leakage. func NewCachedManager(qm quota.Manager, minBatchSize, maxEntries int) (quota.Manager, error) { switch { case minBatchSize <= 0: return nil, fmt.Errorf("invalid minBatchSize: %v", minBatchSize) case maxEntries <= 0: return nil, fmt.Errorf("invalid maxEntries: %v", minBatchSize) } return &manager{ Manager: qm, minBatchSize: minBatchSize, maxEntries: maxEntries, cache: make(map[quota.Spec]*bucket), }, nil } // GetTokens implements Manager.GetTokens. func (m *manager) GetTokens(ctx context.Context, numTokens int, specs []quota.Spec) error { m.mu.Lock() defer m.mu.Unlock() // Verify which buckets need more tokens, if any specsToRefill := []quota.Spec{} for _, spec := range specs { bucket, ok := m.cache[spec] if !ok || bucket.tokens < numTokens { specsToRefill = append(specsToRefill, spec) } } // Request the required number of tokens and add them to buckets if len(specsToRefill) != 0 { defer func() { // Do not hold GetTokens on eviction, it won't change the result. m.evictWg.Add(1) go func() { m.evict(ctx) m.evictWg.Done() }() }() // A more accurate count would be numTokens+m.minBatchSize-bucket.tokens, but that might // force us to make a GetTokens call for each spec. A single call is likely to be more // efficient. tokens := numTokens + m.minBatchSize if err := m.Manager.GetTokens(ctx, tokens, specsToRefill); err != nil { return err } for _, spec := range specsToRefill { b, ok := m.cache[spec] if !ok { b = &bucket{} m.cache[spec] = b } b.tokens += tokens } } // Subtract tokens from cache lastModified := now() for _, spec := range specs { bucket, ok := m.cache[spec] // Sanity check if !ok || bucket.tokens < 0 || bucket.tokens < numTokens { klog.Errorf("Bucket invariants failed for spec %+v: ok = %v, bucket = %+v", spec, ok, bucket) return nil // Something is wrong with the implementation, let requests go through. } bucket.tokens -= numTokens bucket.lastModified = lastModified } return nil } func (m *manager) evict(ctx context.Context) { m.mu.Lock() // m.mu is explicitly unlocked, so we don't have to hold it while we wait for goroutines to // complete. if len(m.cache) <= m.maxEntries { m.mu.Unlock() return } // Find and evict the oldest entries. To avoid excessive token leakage, let's try and // replenish the tokens held for the evicted entries. var buckets bucketsByTime = make([]specBucket, 0, len(m.cache)) for spec, b := range m.cache { if spec.Group != quota.Global { buckets = append(buckets, specBucket{bucket: b, spec: spec}) } } sort.Sort(buckets) wg := sync.WaitGroup{} evicts := len(m.cache) - m.maxEntries for i := 0; i < evicts; i++ { b := buckets[i] klog.V(1).Infof("Too many tokens cached, returning least recently used (%v tokens for %+v)", b.tokens, b.spec) delete(m.cache, b.spec) // goroutines must not access the cache, the lock is released before they complete. wg.Add(1) go func() { if err := m.Manager.PutTokens(ctx, b.tokens, []quota.Spec{b.spec}); err != nil { klog.Warningf("Error replenishing tokens from evicted bucket (spec = %+v, bucket = %+v): %v", b.spec, b.bucket, err) } wg.Done() }() } m.mu.Unlock() wg.Wait() } // wait waits for spawned goroutines to complete. Used by eviction tests. func (m *manager) wait() { m.evictWg.Wait() } // specBucket is a bucket with the corresponding spec. type specBucket struct { *bucket spec quota.Spec } // bucketsByTime is a sortable slice of specBuckets. type bucketsByTime []specBucket // Len provides sort.Interface.Len. func (b bucketsByTime) Len() int { return len(b) } // Less provides sort.Interface.Less. func (b bucketsByTime) Less(i, j int) bool { return b[i].lastModified.Before(b[j].lastModified) } // Swap provides sort.Interface.Swap. func (b bucketsByTime) Swap(i, j int) { b[i], b[j] = b[j], b[i] } trillian-1.6.1/quota/cacheqm/cache_test.go000066400000000000000000000136071466362047600205450ustar00rootroot00000000000000// 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 cacheqm import ( "context" "errors" "testing" "time" "github.com/golang/mock/gomock" "github.com/google/trillian/quota" "github.com/google/trillian/testonly/matchers" ) const ( minBatchSize = 20 maxEntries = 10 ) var specs = []quota.Spec{ {Group: quota.Global, Kind: quota.Read}, {Group: quota.Global, Kind: quota.Write}, } func TestNewCachedManagerErrors(t *testing.T) { tests := []struct { minBatchSize, maxEntries int }{ {minBatchSize: 0, maxEntries: 10}, {minBatchSize: -1, maxEntries: 10}, {minBatchSize: 10, maxEntries: 0}, {minBatchSize: 10, maxEntries: -1}, } qm := quota.Noop() for _, test := range tests { if _, err := NewCachedManager(qm, test.minBatchSize, test.maxEntries); err == nil { t.Errorf("NewCachedManager(_, %v, %v) returned err = nil, want non-nil", test.minBatchSize, test.maxEntries) } } } // TestCachedManager_DelegatedMethods tests all delegated methods that have a single error return. func TestCachedManager_DelegatedMethods(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() ctx := context.Background() tokens := 5 for _, want := range []error{nil, errors.New("llama ate all tokens")} { mock := quota.NewMockManager(ctrl) qm, err := NewCachedManager(mock, minBatchSize, maxEntries) if err != nil { t.Fatalf("NewCachedManager() returned err = %v", err) } mock.EXPECT().PutTokens(ctx, tokens, specs).Return(want) if err := qm.PutTokens(ctx, tokens, specs); err != want { t.Errorf("PutTokens() returned err = %#v, want = %#v", err, want) } mock.EXPECT().ResetQuota(ctx, specs).Return(want) if err := qm.ResetQuota(ctx, specs); err != want { t.Errorf("ResetQuota() returned err = %#v, want = %#v", err, want) } } } func TestCachedManager_GetTokens_CachesTokens(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() ctx := context.Background() tokens := 3 mock := quota.NewMockManager(ctrl) mock.EXPECT().GetTokens(ctx, matchers.AtLeast(minBatchSize), specs).Times(2).Return(nil) qm, err := NewCachedManager(mock, minBatchSize, maxEntries) if err != nil { t.Fatalf("NewCachedManager() returned err = %v", err) } // Quota requests happen in tokens+minBatchSize steps, so that minBatchSize tokens get cached // after the request is satisfied. // Therefore, the call pattern below is satisfied by just 2 underlying GetTokens() calls. calls := []int{tokens, minBatchSize, tokens, minBatchSize / 2, minBatchSize / 2} for i, call := range calls { if err := qm.GetTokens(ctx, call, specs); err != nil { t.Fatalf("GetTokens() returned err = %v (call #%v)", err, i+1) } } } func TestCachedManager_GetTokens_EvictsCache(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mock := quota.NewMockManager(ctrl) mock.EXPECT().GetTokens(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(nil) ctx := context.Background() maxEntries := 100 qm, err := NewCachedManager(mock, minBatchSize, maxEntries) if err != nil { t.Fatalf("NewCachedManager() returned err = %v", err) } originalNow := now defer func() { now = originalNow }() currentTime := time.Time{} now = func() time.Time { currentTime = currentTime.Add(1 * time.Second) return currentTime } // Ensure Global quotas are the oldest, we don't want those to get evicted regardless of age. tokens := 5 if err := qm.GetTokens(ctx, tokens, []quota.Spec{ {Group: quota.Global, Kind: quota.Read}, {Group: quota.Global, Kind: quota.Write}, }); err != nil { t.Fatalf("GetTokens() returned err = %v", err) } // Fill the cache up to maxEntries firstTree := int64(10) tree := firstTree for i := 0; i < maxEntries-2; i++ { if err := qm.GetTokens(ctx, tokens, treeSpecs(tree)); err != nil { t.Fatalf("GetTokens() returned err = %v (i = %v)", err, i) } tree++ } // All entries added from now on must cause eviction of the oldest entries. // Evict trees in pairs to exercise the inner evict loop. evicts := 20 for i := 0; i < evicts; i += 2 { mock.EXPECT().PutTokens(ctx, minBatchSize, treeSpecs(firstTree+int64(i))).Return(nil) mock.EXPECT().PutTokens(ctx, minBatchSize, treeSpecs(firstTree+int64(i+1))).Return(nil) specs := []quota.Spec{treeSpec(tree), treeSpec(tree + 1)} tree += 2 if err := qm.GetTokens(ctx, tokens, specs); err != nil { t.Fatalf("GetTokens() returned err = %v (i = %v)", err, i) } } waitChan := make(chan bool, 1) go func() { qm.(*manager).wait() waitChan <- true }() select { case <-waitChan: // OK, test exited cleanly case <-time.After(5 * time.Second): t.Errorf("Timed out waiting for qm.wait(), failing test") } } func TestManager_GetTokensErrors(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() ctx := context.Background() want := errors.New("llama ate all tokens") mock := quota.NewMockManager(ctrl) mock.EXPECT().GetTokens(ctx, gomock.Any(), specs).Return(want) qm, err := NewCachedManager(mock, minBatchSize, maxEntries) if err != nil { t.Fatalf("NewCachedManager() returned err = %v", err) } if err := qm.GetTokens(ctx, 5 /* numTokens */, specs); err != want { t.Errorf("GetTokens() returned err = %#v, want = %#v", err, want) } } func treeSpecs(treeID int64) []quota.Spec { return []quota.Spec{treeSpec(treeID)} } func treeSpec(treeID int64) quota.Spec { return quota.Spec{Group: quota.Tree, Kind: quota.Write, TreeID: treeID} } trillian-1.6.1/quota/crdbqm/000077500000000000000000000000001466362047600157545ustar00rootroot00000000000000trillian-1.6.1/quota/crdbqm/common_test.go000066400000000000000000000027421466362047600206370ustar00rootroot00000000000000// Copyright 2022 Trillian Authors // // 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 crdbqm import ( "os" "testing" "github.com/cockroachdb/cockroach-go/v2/testserver" "github.com/google/trillian/storage/testdb" "k8s.io/klog/v2" ) func TestMain(m *testing.M) { ts, err := testserver.NewTestServer(testserver.CustomVersionOpt("v22.2.7")) if err != nil { klog.Errorf("Failed to start test server: %v", err) os.Exit(1) } defer ts.Stop() // reset the test server URL path. By default cockroach sets it // to point to a default database, we don't want that. dburl := ts.PGURL() dburl.Path = "/" // Set the environment variable for the test server if err := os.Setenv(testdb.CockroachDBURIEnv, dburl.String()); err != nil { klog.Errorf("Failed to set CockroachDBURIEnv: %v", err) os.Exit(1) } if !testdb.CockroachDBAvailable() { klog.Errorf("CockroachDB not available, skipping all CockroachDB storage tests") return } status := m.Run() os.Exit(status) } trillian-1.6.1/quota/crdbqm/crdb_quota.go000066400000000000000000000070421466362047600204310ustar00rootroot00000000000000// Copyright 2022 Trillian Authors // // 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 crdbqm defines a CockroachDB-based quota.Manager implementation. package crdbqm import ( "context" "database/sql" "errors" "github.com/google/trillian/quota" "k8s.io/klog/v2" ) const ( // DefaultMaxUnsequenced is a suggested value for MaxUnsequencedRows. // Note that this is a Global/Write quota suggestion, so it applies across trees. DefaultMaxUnsequenced = 500000 // About 2h of non-stop signing at 70QPS. // TODO(jaosorior): Come up with a more optimal solution for CRDB, as this is // linear and too costly. // Using a follower read here to reduce latency on the query. While this will // slightly less accurate than a read from the leader, it should be good enough. countFromUnsequencedTable = "SELECT COUNT(*) FROM Unsequenced AS OF SYSTEM TIME follower_read_timestamp()" ) // ErrTooManyUnsequencedRows is returned when tokens are requested but Unsequenced has grown // beyond the configured limit. var ErrTooManyUnsequencedRows = errors.New("too many unsequenced rows") // QuotaManager is a CockroachDB-based quota.Manager implementation. // // QuotaManager only implements Global/Write quotas, which is based on the number of Unsequenced // rows (to be exact, tokens = MaxUnsequencedRows - actualUnsequencedRows). // Other quotas are considered infinite. type QuotaManager struct { DB *sql.DB MaxUnsequencedRows int } // GetTokens implements quota.Manager.GetTokens. // It doesn't actually reserve or retrieve tokens, instead it allows access based on the number of // rows in the Unsequenced table. func (m *QuotaManager) GetTokens(ctx context.Context, numTokens int, specs []quota.Spec) error { for _, spec := range specs { if spec.Group != quota.Global || spec.Kind != quota.Write { continue } // Only allow global writes if Unsequenced is under the expected limit count, err := m.countUnsequenced(ctx) if err != nil { return err } if count+numTokens > m.MaxUnsequencedRows { return ErrTooManyUnsequencedRows } } return nil } // PutTokens implements quota.Manager.PutTokens. // It's a noop for QuotaManager. func (m *QuotaManager) PutTokens(ctx context.Context, numTokens int, specs []quota.Spec) error { return nil } // ResetQuota implements quota.Manager.ResetQuota. // It's a noop for QuotaManager. func (m *QuotaManager) ResetQuota(ctx context.Context, specs []quota.Spec) error { return nil } func (m *QuotaManager) countUnsequenced(ctx context.Context) (int, error) { // table names are lowercase for some reason rows, err := m.DB.QueryContext(ctx, countFromUnsequencedTable) if err != nil { return 0, err } defer func() { if err := rows.Close(); err != nil { klog.Errorf("Close(): %v", err) } }() if !rows.Next() { return 0, errors.New("cursor has no rows after quota limit determination query") } var count int if err := rows.Scan(&count); err != nil { return 0, err } if rows.Next() { return 0, errors.New("too many rows returned from quota limit determination query") } return count, nil } trillian-1.6.1/quota/crdbqm/crdb_quota_test.go000066400000000000000000000166371466362047600215020ustar00rootroot00000000000000// Copyright 2022 Trillian Authors // // 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 crdbqm import ( "context" "crypto" "database/sql" "fmt" "testing" "time" "github.com/google/trillian" "github.com/google/trillian/quota" "github.com/google/trillian/storage" "github.com/google/trillian/storage/crdb" "github.com/google/trillian/storage/testdb" "github.com/google/trillian/types" stestonly "github.com/google/trillian/storage/testonly" ) func TestQuotaManager_GetTokens(t *testing.T) { testdb.SkipIfNoCockroachDB(t) ctx := context.Background() // This allows for CockroachDB to persist the transaction for the record. // We're mostly just interested in testing that the quota is managed at all, // but running this in production will need to tolerate that // quota management won't be exact. // The 5 second time was gotten from https://www.cockroachlabs.com/docs/stable/follower-reads.html#when-to-use-exact-staleness-reads const transactionBackoff = 5 * time.Second db, done, err := testdb.NewTrillianDB(ctx, testdb.DriverCockroachDB) if err != nil { t.Fatalf("GetTestDB() returned err = %v", err) } defer done(ctx) tree, err := createTree(ctx, db) if err != nil { t.Fatalf("createTree() returned err = %v", err) } tests := []struct { desc string unsequencedRows, maxUnsequencedRows, numTokens int specs []quota.Spec wantErr bool }{ { desc: "globalWriteSingleToken", unsequencedRows: 10, maxUnsequencedRows: 20, numTokens: 1, specs: []quota.Spec{{Group: quota.Global, Kind: quota.Write}}, }, { desc: "globalWriteMultiToken", unsequencedRows: 10, maxUnsequencedRows: 20, numTokens: 5, specs: []quota.Spec{{Group: quota.Global, Kind: quota.Write}}, }, { desc: "globalWriteOverQuota1", unsequencedRows: 20, maxUnsequencedRows: 20, numTokens: 1, specs: []quota.Spec{{Group: quota.Global, Kind: quota.Write}}, wantErr: true, }, { desc: "globalWriteOverQuota2", unsequencedRows: 15, maxUnsequencedRows: 20, numTokens: 10, specs: []quota.Spec{{Group: quota.Global, Kind: quota.Write}}, wantErr: true, }, { desc: "unlimitedQuotas", numTokens: 10, specs: []quota.Spec{ {Group: quota.User, Kind: quota.Read, User: "dylan"}, {Group: quota.Tree, Kind: quota.Read, TreeID: tree.TreeId}, {Group: quota.Global, Kind: quota.Read}, {Group: quota.User, Kind: quota.Write, User: "dylan"}, {Group: quota.Tree, Kind: quota.Write, TreeID: tree.TreeId}, }, }, } for _, test := range tests { t.Run(test.desc, func(t *testing.T) { if err := setUnsequencedRows(ctx, db, tree, test.unsequencedRows); err != nil { t.Errorf("setUnsequencedRows() returned err = %v", err) return } time.Sleep(transactionBackoff) qm := &QuotaManager{DB: db, MaxUnsequencedRows: test.maxUnsequencedRows} err = qm.GetTokens(ctx, test.numTokens, test.specs) if hasErr := err == ErrTooManyUnsequencedRows; hasErr != test.wantErr { t.Errorf("%v: GetTokens() returned err = %q, wantErr = %v", test.desc, err, test.wantErr) } }) } } func TestQuotaManager_Noops(t *testing.T) { testdb.SkipIfNoCockroachDB(t) ctx := context.Background() db, done, err := testdb.NewTrillianDB(ctx, testdb.DriverCockroachDB) if err != nil { t.Fatalf("GetTestDB() returned err = %v", err) } defer done(ctx) qm := &QuotaManager{DB: db, MaxUnsequencedRows: 1000} specs := allSpecs(ctx, qm, 10 /* treeID */) tests := []struct { desc string fn func() error }{ { desc: "PutTokens", fn: func() error { return qm.PutTokens(ctx, 10 /* numTokens */, specs) }, }, { desc: "ResetQuota", fn: func() error { return qm.ResetQuota(ctx, specs) }, }, } for _, test := range tests { if err := test.fn(); err != nil { t.Errorf("%v: got err = %v", test.desc, err) } } } func allSpecs(_ context.Context, _ quota.Manager, treeID int64) []quota.Spec { return []quota.Spec{ {Group: quota.User, Kind: quota.Read, User: "florence"}, {Group: quota.Tree, Kind: quota.Read, TreeID: treeID}, {Group: quota.Global, Kind: quota.Read}, {Group: quota.User, Kind: quota.Write, User: "florence"}, {Group: quota.Tree, Kind: quota.Write, TreeID: treeID}, {Group: quota.Global, Kind: quota.Write}, } } func countUnsequenced(ctx context.Context, db *sql.DB) (int, error) { var count int if err := db.QueryRowContext(ctx, "SELECT COUNT(*) FROM Unsequenced").Scan(&count); err != nil { return 0, err } return count, nil } func createTree(ctx context.Context, db *sql.DB) (*trillian.Tree, error) { var tree *trillian.Tree { as := crdb.NewSQLAdminStorage(db) err := as.ReadWriteTransaction(ctx, func(ctx context.Context, tx storage.AdminTX) error { var err error tree, err = tx.CreateTree(ctx, stestonly.LogTree) return err }) if err != nil { return nil, err } } { ls := crdb.NewLogStorage(db, nil) err := ls.ReadWriteTransaction(ctx, tree, func(ctx context.Context, tx storage.LogTreeTX) error { logRoot, err := (&types.LogRootV1{RootHash: []byte{0}}).MarshalBinary() if err != nil { return err } slr := &trillian.SignedLogRoot{LogRoot: logRoot} return tx.StoreSignedLogRoot(ctx, slr) }) if err != nil { return nil, err } } return tree, nil } func queueLeaves(ctx context.Context, db *sql.DB, tree *trillian.Tree, firstID, num int) error { hasher := crypto.SHA256.New() leaves := []*trillian.LogLeaf{} for i := 0; i < num; i++ { value := []byte(fmt.Sprintf("leaf-%v", firstID+i)) hasher.Reset() if _, err := hasher.Write(value); err != nil { return err } hash := hasher.Sum(nil) leaves = append(leaves, &trillian.LogLeaf{ MerkleLeafHash: hash, LeafValue: value, ExtraData: []byte("extra data"), LeafIdentityHash: hash, }) } ls := crdb.NewLogStorage(db, nil) _, err := ls.QueueLeaves(ctx, tree, leaves, time.Now()) return err } func setUnsequencedRows(ctx context.Context, db *sql.DB, tree *trillian.Tree, wantRows int) error { count, err := countUnsequenced(ctx, db) if err != nil { return err } if count == wantRows { return nil } // Clear the tables and re-create leaves from scratch. It's easier than having to reason // about duplicate entries. if _, err := db.ExecContext(ctx, "DELETE FROM LeafData"); err != nil { return err } if _, err := db.ExecContext(ctx, "DELETE FROM Unsequenced"); err != nil { return err } if err := queueLeaves(ctx, db, tree, 0 /* firstID */, wantRows); err != nil { return err } // Sanity check the final count count, err = countUnsequenced(ctx, db) if err != nil { return err } if count != wantRows { return fmt.Errorf("got %v unsequenced rows, want = %v", count, wantRows) } return nil } trillian-1.6.1/quota/crdbqm/quota_provider.go000066400000000000000000000027361466362047600213560ustar00rootroot00000000000000// Copyright 2022 Trillian Authors // // 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 crdbqm import ( "flag" "k8s.io/klog/v2" "github.com/google/trillian/quota" "github.com/google/trillian/storage/crdb" ) // QuotaManagerName identifies the CockroachDB quota implementation. const QuotaManagerName = "crdb" var maxUnsequencedRows = flag.Int("crdb_max_unsequenced_rows", DefaultMaxUnsequenced, "Max number of unsequenced rows before rate limiting kicks in. Only effective for quota_system=crdb.") func init() { if err := quota.RegisterProvider(QuotaManagerName, newCockroachDBQuotaManager); err != nil { klog.Fatalf("Failed to register quota manager %v: %v", QuotaManagerName, err) } } func newCockroachDBQuotaManager() (quota.Manager, error) { db, err := crdb.GetDatabase() if err != nil { return nil, err } qm := &QuotaManager{ DB: db, MaxUnsequencedRows: *maxUnsequencedRows, } klog.Info("Using CockroachDB QuotaManager") return qm, nil } trillian-1.6.1/quota/doc.go000066400000000000000000000034641466362047600156070ustar00rootroot00000000000000// 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 quota defines Trillian's Quota Management service. // // The objective of the quota service is to protect Trillian from traffic peaks, rejecting requests // that may put servers out of capacity or indirectly cause MMDs (maximum merge delays) to be // missed. // // Each Trillian request, be it either a read or write request, requires certain tokens to be // allowed to continue. Tokens exist at multiple layers: per-user, per-tree and global tokens. // For example, a TrillianLog.QueueLeaf request consumes a Write token from User, Tree and Global // quotas. If any of those quotas is out of tokens, the request is denied with a ResourceExhausted // error code. // // Tokens are replenished according to each implementation. For example, User tokens may replenish // over time, whereas {Write, Tree} tokens may replenish as sequencing happens. Implementations are // free to ignore (effectively whitelisting) certain specs of tokens (e.g., only support Global and // ignore User and Tree tokens). // // Quota users are defined according to each implementation. Note that quota users don't need to // match authentication/authorization users; implementations are allowed their own representation of // users. package quota trillian-1.6.1/quota/etcd/000077500000000000000000000000001466362047600154235ustar00rootroot00000000000000trillian-1.6.1/quota/etcd/README.md000066400000000000000000000227371466362047600167150ustar00rootroot00000000000000# Etcd quotas Package etcd (and its subpackages) contain an etcd-based [quota.Manager](https://github.com/google/trillian/blob/3cf59cdfd0/quota/quota.go#L101) implementation, with a corresponding configuration service. ## Usage First, ensure both `logserver` and `logsigner` are started with the `--etcd_servers` and `--quota_system=etcd` flags. For example: ```bash trillian_log_server \ --etcd_servers=... \ --rpc_endpoint=localhost:8090 \ --quota_system=etcd trillian_log_signer --etcd_servers=... --quota_system=etcd ``` If correctly started, the servers will be using etcd quotas. The default configuration is empty, which means no quotas are enforced. The quota API may be used to create and update configurations. For example, the command below creates a sequencing-based, `global/write` quota. Assuming an expected sequencing performance of 50 QPS, the `max_tokens` specified below implies a backlog of 4h. ```bash grpcurl -plaintext -d @ localhost:8090 v1beta1/quotas/global/write/config < 0) is recommended to lessen the latency impact of rate limiting. `--quota_min_batch_size` is the minimum number of tokens acquired from etcd. If a particular request demands fewer tokens than the minimal batch size, the remaining tokens are kept in memory, potentially saving new requests to etcd until those are consumed. `--quota_max_cache_entries` determines how many quota Specs are cached. Tokens are cached per Spec using a LRU replacement policy. In case of systems with a high number of trees or users, the least used ones are evicted from the cache (and their tokens returned). ### Monitoring The following metrics are relevant when considering quota behavior: * [interceptor_request_count](https://github.com/google/trillian/blob/3cf59cdfd0/server/interceptor/interceptor.go#L91) * [interceptor_request_denied_count](https://github.com/google/trillian/blob/3cf59cdfd0/server/interceptor/interceptor.go#L95) * [quota_acquired_tokens](https://github.com/google/trillian/blob/3cf59cdfd0/quota/metrics.go#L70) * [quota_returned_tokens](https://github.com/google/trillian/blob/3cf59cdfd0/quota/metrics.go#L71) * [quota_replenished_tokens](https://github.com/google/trillian/blob/3cf59cdfd0/quota/metrics.go#L71) Requests denied due to token shortage are labeled on **interceptor_request_denied_count** as [insufficient_tokens](https://github.com/google/trillian/blob/3cf59cdfd0/server/interceptor/interceptor.go#L38). The ratio between **denied_with_insufficient_tokens** and **interceptor_request_count** is a strong indicator of token exhaustion. ## General concepts Trillian quotas have a finite number of tokens that get consumed by requests. Once a quota reaches zero tokens, all requests that would otherwise consume a token from it will fail with a **resource_exhausted** error. Tokens are replenished by different mechanisms, depending on the quota configuration (e.g, X tokens every Y seconds). Quotas are designed so that a set of quotas, in different levels of granularity, apply to a single request. A quota [Spec](https://github.com/google/trillian/blob/3cf59cdfd0/quota/quota.go#L56) identifies a particular quota and represents to which requests it applies. Specs contain a [Group](https://github.com/google/trillian/blob/3cf59cdfd0/quota/quota.go#L27) (`global`, `tree` and `user`) and [Kind](https://github.com/google/trillian/blob/3cf59cdfd0/quota/quota.go#L44) (`read` or `write`). A few Spec examples are: * `global/read` (all read requests) * `global/write` (all write requests) * `trees/123/write` (write requests for tree 123) * `users/alice/read` (read requests made by user "alice") Each request, depending on whether it's a read or write request, subtracts tokens from the following Specs: | read requests | write requests | | -------------- | --------------- | | users/$id/read | users/$id/write | | trees/$id/read | trees/$id/write | | global/read | global/write | Quotas that aren't explicitly configured are considered infinite and won't block requests. ## Etcd quotas Etcd quotas implement the concepts described above by storing the quota configuration and token count in etcd. Two replenishment mechanisms are available: sequencing-based and time-based. Sequencing-based replenishment is tied to `logsigner's` progress. A token is restored for each leaf sequenced from the `Unsequenced` table. As such, it's only applicable to `global/write` and `trees/write` quotas. Time-based sequencing replenishes X tokens every Y seconds. It may be applied to all quotas. ### MMD protection Sequencing-based quotas may be used as a form of MMD protection. If the number of write requests accepted by Trillian going beyond the `logsigner's` configured processing capability, tokens will eventually get exhausted and the system will fail new write requests with a **resource_exhausted** error. While not ideal, this helps avoid an eventual MMD loss, which may be a graver offense than temporary loss of availability. Both `global/write` and `trees/write` quotas may be used for MMD protection purposes. It's strongly recommended that `global/write` is set up as a last line of defense for all systems. ### QPS limits Time-based quotas effectively work as QPS (queries-per-second) limits (X tokens in Y seconds is roughly equivalent to X/Y QPS). All quotas may be configured as time-based, but they may be particularly useful as per-tree (e.g. limiting test or archival trees) or as per-user. ### Default quotas Default quotas are pre-configured limits that get automatically applied to new trees or users. TODO(codingllama): Default quotas are not yet implemented. ### Quota users User level quotas are applied to "quota users". Trillian makes no assumptions about what a quota user is. Therefore, initially, there's a single default user that is charged for all requests (note that, since no quotas are created by default, this user charges quotas that are effectively infinite). trillian-1.6.1/quota/etcd/etcdqm/000077500000000000000000000000001466362047600167005ustar00rootroot00000000000000trillian-1.6.1/quota/etcd/etcdqm/etcdqm.go000066400000000000000000000045541466362047600205140ustar00rootroot00000000000000// 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 etcdqm contains an etcd-based quota.Manager implementation. package etcdqm import ( "context" "fmt" "github.com/google/trillian/quota" "github.com/google/trillian/quota/etcd/storage" clientv3 "go.etcd.io/etcd/client/v3" ) // Manager implements a quota manager based on etcd. type Manager struct { qs *storage.QuotaStorage } // New returns a new etcd-based quota.Manager. func New(client *clientv3.Client) *Manager { return &Manager{qs: &storage.QuotaStorage{Client: client}} } // GetTokens implements the quota.Manager API. func (m *Manager) GetTokens(ctx context.Context, numTokens int, specs []quota.Spec) error { return m.qs.Get(ctx, configNames(specs), int64(numTokens)) } func (m *Manager) peekTokens(ctx context.Context, specs []quota.Spec) (map[quota.Spec]int, error) { names := configNames(specs) nameToSpec := make(map[string]quota.Spec) for i, name := range names { nameToSpec[name] = specs[i] } nameToTokens, err := m.qs.Peek(ctx, names) if err != nil { return nil, err } tokens := make(map[quota.Spec]int) for k, v := range nameToTokens { tokens[nameToSpec[k]] = int(v) } return tokens, nil } // PutTokens implements the quota.Manager API. func (m *Manager) PutTokens(ctx context.Context, numTokens int, specs []quota.Spec) error { return m.qs.Put(ctx, configNames(specs), int64(numTokens)) } // ResetQuota implements the quota.Manager API. func (m *Manager) ResetQuota(ctx context.Context, specs []quota.Spec) error { return m.qs.Reset(ctx, configNames(specs)) } func configNames(specs []quota.Spec) []string { names := make([]string, 0, len(specs)) for _, spec := range specs { names = append(names, configName(spec)) } return names } func configName(spec quota.Spec) string { return fmt.Sprintf("quotas/%v/config", spec.Name()) } trillian-1.6.1/quota/etcd/etcdqm/etcdqm_test.go000066400000000000000000000221721466362047600215470ustar00rootroot00000000000000// 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 etcdqm import ( "context" "fmt" "math" "os" "testing" "github.com/google/go-cmp/cmp" "github.com/google/trillian/quota" "github.com/google/trillian/quota/etcd/storage" "github.com/google/trillian/quota/etcd/storagepb" "github.com/google/trillian/testonly/integration/etcd" clientv3 "go.etcd.io/etcd/client/v3" "google.golang.org/protobuf/proto" ) const ( treeID = 12345 userID = "llama" ) var ( cfgs = &storagepb.Configs{ Configs: []*storagepb.Config{ { Name: "quotas/global/write/config", State: storagepb.Config_ENABLED, MaxTokens: 100, ReplenishmentStrategy: &storagepb.Config_SequencingBased{ SequencingBased: &storagepb.SequencingBasedStrategy{}, }, }, { Name: fmt.Sprintf("quotas/trees/%v/write/config", treeID), State: storagepb.Config_ENABLED, MaxTokens: 200, ReplenishmentStrategy: &storagepb.Config_SequencingBased{ SequencingBased: &storagepb.SequencingBasedStrategy{}, }, }, { Name: fmt.Sprintf("quotas/users/%v/read/config", userID), State: storagepb.Config_ENABLED, MaxTokens: 1000, ReplenishmentStrategy: &storagepb.Config_TimeBased{ TimeBased: &storagepb.TimeBasedStrategy{ ReplenishIntervalSeconds: 100, TokensToReplenish: 1000, }, }, }, }, } globalWriteConfig = cfgs.Configs[0] treeWriteConfig = cfgs.Configs[1] userReadConfig = cfgs.Configs[2] globalWriteSpec = quota.Spec{Group: quota.Global, Kind: quota.Write} treeWriteSpec = quota.Spec{Group: quota.Tree, Kind: quota.Write, TreeID: treeID} userReadSpec = quota.Spec{Group: quota.User, Kind: quota.Read, User: userID} // client is an etcd client. // Initialized by TestMain(). client *clientv3.Client ) func TestMain(m *testing.M) { _, c, cleanup, err := etcd.StartEtcd() if err != nil { panic(fmt.Sprintf("StartEtcd() returned err = %v", err)) } client = c exitCode := m.Run() cleanup() os.Exit(exitCode) } func TestManager_GetTokens(t *testing.T) { tests := []struct { desc string numTokens int specs []quota.Spec }{ { desc: "singleSpec", numTokens: 10, specs: []quota.Spec{globalWriteSpec}, }, { desc: "multiSpecs", numTokens: 10, specs: []quota.Spec{userReadSpec, treeWriteSpec, globalWriteSpec}, }, } qs := &storage.QuotaStorage{Client: client} qm := New(client) ctx := context.Background() for _, test := range tests { if err := reset(ctx, qs, cfgs); err != nil { t.Fatalf("%v: reset: %v", test.desc, err) } differ := newQuotaDiffer(qs, test.specs) if err := differ.snapshot(ctx); err != nil { t.Fatalf("%v: snapshot: %v", test.desc, err) } if err := qm.GetTokens(ctx, test.numTokens, test.specs); err != nil { t.Errorf("%v: GetTokens() returned err = %v", test.desc, err) continue } if err := differ.assertDiff(ctx, "GetTokens", -test.numTokens); err != nil { t.Errorf("%v: assertDiff: %v", test.desc, err) } } } func TestManager_GetTokensErrors(t *testing.T) { tests := []struct { desc string numTokens int specs []quota.Spec }{ { desc: "singleSpec", numTokens: int(globalWriteConfig.MaxTokens) + 1, specs: []quota.Spec{globalWriteSpec}, }, { desc: "multiSpecs", numTokens: int(min(userReadConfig.MaxTokens, treeWriteConfig.MaxTokens, globalWriteConfig.MaxTokens)) + 1, specs: []quota.Spec{userReadSpec, treeWriteSpec, globalWriteSpec}, }, } qs := &storage.QuotaStorage{Client: client} qm := New(client) ctx := context.Background() for _, test := range tests { if err := reset(ctx, qs, cfgs); err != nil { t.Fatalf("%v: reset: %v", test.desc, err) } if err := qm.GetTokens(ctx, test.numTokens, test.specs); err == nil { t.Errorf("%v: GetTokens() returned err = nil, want non-nil", test.desc) } } } func TestManager_PutTokens(t *testing.T) { ctx := context.Background() qs := &storage.QuotaStorage{Client: client} if err := drain(ctx, qs, cfgs); err != nil { t.Fatalf("drain() returned err = %v", err) } tests := []struct { desc string numTokens int specs []quota.Spec }{ { desc: "singleSpec", numTokens: 10, specs: []quota.Spec{globalWriteSpec}, }, { desc: "multiSpecs", numTokens: 11, specs: []quota.Spec{globalWriteSpec, treeWriteSpec}, }, } qm := New(client) for _, test := range tests { differ := newQuotaDiffer(qs, test.specs) if err := differ.snapshot(ctx); err != nil { t.Fatalf("%v: snapshot: %v", test.desc, err) } if err := qm.PutTokens(ctx, test.numTokens, test.specs); err != nil { t.Errorf("%v: PutTokens() returned err = %v", test.desc, err) continue } if err := differ.assertDiff(ctx, "PutTokens", test.numTokens); err != nil { t.Errorf("%v: assertDiff: %v", test.desc, err) } } } func TestManager_ResetQuota(t *testing.T) { tests := []struct { desc string specs []quota.Spec want map[quota.Spec]int }{ { desc: "singleSpec", specs: []quota.Spec{globalWriteSpec}, want: map[quota.Spec]int{ globalWriteSpec: int(globalWriteConfig.MaxTokens), }, }, { desc: "multiSpecs", specs: []quota.Spec{globalWriteSpec, treeWriteSpec, userReadSpec}, want: map[quota.Spec]int{ globalWriteSpec: int(globalWriteConfig.MaxTokens), treeWriteSpec: int(treeWriteConfig.MaxTokens), userReadSpec: int(userReadConfig.MaxTokens), }, }, } qs := &storage.QuotaStorage{Client: client} qm := New(client) ctx := context.Background() for _, test := range tests { if err := drain(ctx, qs, cfgs); err != nil { t.Fatalf("%v: drain() returned err = %v", test.desc, err) } if err := qm.ResetQuota(ctx, test.specs); err != nil { t.Errorf("%v: ResetQuota() returned err = %v", test.desc, err) continue } tokens, err := qm.peekTokens(ctx, test.specs) if err != nil { t.Fatalf("%v: peekTokens() returned err = %v", test.desc, err) } if diff := cmp.Diff(tokens, test.want); diff != "" { t.Errorf("%v: post-peekTokens() diff (-got +want):\n%v", test.desc, diff) } } } func TestConfigName(t *testing.T) { tests := []struct { spec quota.Spec want string }{ { spec: quota.Spec{Group: quota.Global, Kind: quota.Read}, want: "quotas/global/read/config", }, { spec: quota.Spec{Group: quota.Global, Kind: quota.Write}, want: "quotas/global/write/config", }, { spec: quota.Spec{Group: quota.Tree, Kind: quota.Read, TreeID: 10}, want: "quotas/trees/10/read/config", }, { spec: quota.Spec{Group: quota.Tree, Kind: quota.Write, TreeID: 11}, want: "quotas/trees/11/write/config", }, { spec: quota.Spec{Group: quota.User, Kind: quota.Read, User: "alpaca"}, want: "quotas/users/alpaca/read/config", }, { spec: quota.Spec{Group: quota.User, Kind: quota.Write, User: "llama"}, want: "quotas/users/llama/write/config", }, } for _, test := range tests { if got := configName(test.spec); got != test.want { t.Errorf("configName(%+v) = %v, want = %v", test.spec, got, test.want) } } } func min(nums ...int64) int64 { ret := int64(math.MaxInt64) for _, n := range nums { if n < ret { ret = n } } return ret } // drain applies cfgs (as per reset()) and consumes all tokens from it. func drain(ctx context.Context, qs *storage.QuotaStorage, cfgs *storagepb.Configs) error { if err := reset(ctx, qs, cfgs); err != nil { return err } for _, cfg := range cfgs.Configs { if err := qs.Get(ctx, []string{cfg.Name}, cfg.MaxTokens); err != nil { return fmt.Errorf("%v: %v", cfg.Name, err) } } return nil } func reset(ctx context.Context, qs *storage.QuotaStorage, cfgs *storagepb.Configs) error { if _, err := qs.UpdateConfigs(ctx, true /* reset */, func(c *storagepb.Configs) { (*c).Reset() proto.Merge(c, cfgs) }); err != nil { return fmt.Errorf("UpdateConfigs() returned err = %v", err) } return nil } type quotaDiffer struct { qs *storage.QuotaStorage names []string tokens map[string]int64 } func newQuotaDiffer(qs *storage.QuotaStorage, specs []quota.Spec) *quotaDiffer { return "aDiffer{qs: qs, names: configNames(specs)} } func (d *quotaDiffer) snapshot(ctx context.Context) error { var err error d.tokens, err = d.qs.Peek(ctx, d.names) return err } func (d *quotaDiffer) assertDiff(ctx context.Context, desc string, want int) error { currentTokens, err := d.qs.Peek(ctx, d.names) if err != nil { return fmt.Errorf("in %s: Peek() returned err = %v", desc, err) } want64 := int64(want) for k, v := range currentTokens { if got := v - d.tokens[k]; got != want64 { return fmt.Errorf("%v has a diff of %v, want = %v", k, got, want64) } } return nil } trillian-1.6.1/quota/etcd/quota_provider.go000066400000000000000000000051541466362047600210220ustar00rootroot00000000000000// Copyright 2018 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 etcd provides the configuration and initialization of the etcd // quota manager. package etcd import ( "flag" "fmt" "strings" "time" "github.com/google/trillian/quota" "github.com/google/trillian/quota/cacheqm" "github.com/google/trillian/quota/etcd/etcdqm" clientv3 "go.etcd.io/etcd/client/v3" "k8s.io/klog/v2" ) // QuotaManagerName identifies the etcd quota implementation. const QuotaManagerName = "etcd" var ( // Servers is a flag containing the address(es) of etcd servers Servers = flag.String("etcd_servers", "", "A comma-separated list of etcd servers; no etcd registration if empty") // TODO(Martin2112): suggested renaming these to etc_... to avoid clashes, but will it break existing deploys? quotaMinBatchSize = flag.Int("quota_min_batch_size", cacheqm.DefaultMinBatchSize, "Minimum number of tokens to request from the quota system. "+ "Zero or lower means batching is disabled. Applicable for etcd quotas.") quotaMaxCacheEntries = flag.Int("quota_max_cache_entries", cacheqm.DefaultMaxCacheEntries, "Max number of quota specs in the quota cache. "+ "Zero or lower means batching is disabled. Applicable for etcd quotas.") ) func init() { if err := quota.RegisterProvider(QuotaManagerName, newEtcdQuotaManager); err != nil { klog.Fatalf("Failed to register quota manager %v: %v", QuotaManagerName, err) } } func newEtcdQuotaManager() (quota.Manager, error) { if *Servers == "" { return nil, fmt.Errorf("can't create etcd quotamanager - etcd_servers flag is unset") } client, err := clientv3.New(clientv3.Config{ Endpoints: strings.Split(*Servers, ","), DialTimeout: 5 * time.Second, }) if err != nil { return nil, fmt.Errorf("failed to connect to etcd at %v: %v", *Servers, err) } var qm quota.Manager = etcdqm.New(client) if *quotaMinBatchSize > 0 && *quotaMaxCacheEntries > 0 { cachedQM, err := cacheqm.NewCachedManager(qm, *quotaMinBatchSize, *quotaMaxCacheEntries) if err != nil { return nil, err } qm = cachedQM } klog.Info("Using Etcd QuotaManager") return qm, nil } trillian-1.6.1/quota/etcd/quotaapi/000077500000000000000000000000001466362047600172465ustar00rootroot00000000000000trillian-1.6.1/quota/etcd/quotaapi/conversions.go000066400000000000000000000113271466362047600221510ustar00rootroot00000000000000// 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 quotaapi import ( "fmt" "github.com/google/trillian/quota/etcd/quotapb" "github.com/google/trillian/quota/etcd/storagepb" "google.golang.org/genproto/protobuf/field_mask" ) const ( statePath = "state" maxTokensPath = "max_tokens" sequencingBasedPath = "sequencing_based" timeBasedPath = "time_based" ) var ( commonMask = &field_mask.FieldMask{Paths: []string{statePath, maxTokensPath}} sequencingBasedMask = &field_mask.FieldMask{Paths: append(commonMask.Paths, sequencingBasedPath)} timeBasedMask = &field_mask.FieldMask{Paths: append(commonMask.Paths, timeBasedPath)} errBothReplenishmentStrategies = fmt.Errorf("both %q and %q paths specified, please pick a single replenishment strategy", sequencingBasedPath, timeBasedPath) ) // validateMask returns nil if mask is a valid Config mask, error otherwise. // Paths must match the proto name of the fields (e.g., "time_based", not "TimeBased"). // If fields belong to a oneof (such as "sequencing_based" and "time_based"), then only one field of // the oneof may be specified. func validateMask(mask *field_mask.FieldMask) error { sequencingBasedFound := false timeBasedFound := false for _, path := range mask.Paths { switch path { case statePath, maxTokensPath: // OK case sequencingBasedPath: if timeBasedFound { return errBothReplenishmentStrategies } sequencingBasedFound = true case timeBasedPath: if sequencingBasedFound { return errBothReplenishmentStrategies } timeBasedFound = true default: return fmt.Errorf("invalid field path for Config: %q", path) } } return nil } // applyMask copies the fields specified by mask from src to dest. The mask must be first validated // by validateMask(), as unknown paths are simply ignored by applyMask. // Paths must match the proto name of the fields (e.g., "time_based", not "TimeBased"). func applyMask(src *quotapb.Config, dest *storagepb.Config, mask *field_mask.FieldMask) { for _, path := range mask.Paths { switch path { case statePath: dest.State = storagepb.Config_State(storagepb.Config_State_value[src.State.String()]) case maxTokensPath: dest.MaxTokens = src.MaxTokens case sequencingBasedPath: if src.GetSequencingBased() == nil { dest.ReplenishmentStrategy = nil } else { dest.ReplenishmentStrategy = &storagepb.Config_SequencingBased{ SequencingBased: &storagepb.SequencingBasedStrategy{}, } } case timeBasedPath: if tb := src.GetTimeBased(); tb == nil { dest.ReplenishmentStrategy = nil } else { dest.ReplenishmentStrategy = &storagepb.Config_TimeBased{ TimeBased: &storagepb.TimeBasedStrategy{ TokensToReplenish: tb.GetTokensToReplenish(), ReplenishIntervalSeconds: tb.GetReplenishIntervalSeconds(), }, } } } } } // convertToAPI returns the API representation of a storagepb.Config proto. func convertToAPI(src *storagepb.Config) *quotapb.Config { dest := "apb.Config{ Name: src.Name, State: quotapb.Config_State(quotapb.Config_State_value[src.State.String()]), MaxTokens: src.MaxTokens, } sb := src.GetSequencingBased() tb := src.GetTimeBased() switch { case sb != nil: dest.ReplenishmentStrategy = "apb.Config_SequencingBased{ SequencingBased: "apb.SequencingBasedStrategy{}, } case tb != nil: dest.ReplenishmentStrategy = "apb.Config_TimeBased{ TimeBased: "apb.TimeBasedStrategy{ TokensToReplenish: tb.TokensToReplenish, ReplenishIntervalSeconds: tb.ReplenishIntervalSeconds, }, } } return dest } // convertToStorage returns the storage representation of a quotapb.Config proto. func convertToStorage(src *quotapb.Config) *storagepb.Config { // Instead of potentially duplicating logic, let's take advantage of applyMask by picking a // pre-made mask that contains all fields we care about and apply it to a new proto. var mask *field_mask.FieldMask switch { case src.GetSequencingBased() != nil: mask = sequencingBasedMask case src.GetTimeBased() != nil: mask = timeBasedMask default: mask = commonMask } dest := &storagepb.Config{Name: src.Name} applyMask(src, dest, mask) return dest } trillian-1.6.1/quota/etcd/quotaapi/conversions_test.go000066400000000000000000000141421466362047600232060ustar00rootroot00000000000000// 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 quotaapi import ( "testing" "github.com/google/go-cmp/cmp" "github.com/google/trillian/quota/etcd/quotapb" "github.com/google/trillian/quota/etcd/storagepb" "google.golang.org/genproto/protobuf/field_mask" "google.golang.org/protobuf/proto" ) var ( apiSequencingConfig = "apb.Config{ Name: "quotas/global/write/config", State: quotapb.Config_ENABLED, MaxTokens: 100, ReplenishmentStrategy: "apb.Config_SequencingBased{ SequencingBased: "apb.SequencingBasedStrategy{}, }, } apiTimeConfig = "apb.Config{ Name: "quotas/users/llama/write/config", State: quotapb.Config_DISABLED, MaxTokens: 200, ReplenishmentStrategy: "apb.Config_TimeBased{ TimeBased: "apb.TimeBasedStrategy{ TokensToReplenish: 10, ReplenishIntervalSeconds: 30, }, }, } storageSequencingConfig = &storagepb.Config{ Name: apiSequencingConfig.Name, State: storagepb.Config_ENABLED, MaxTokens: apiSequencingConfig.MaxTokens, ReplenishmentStrategy: &storagepb.Config_SequencingBased{ SequencingBased: &storagepb.SequencingBasedStrategy{}, }, } storageTimeConfig = &storagepb.Config{ Name: apiTimeConfig.Name, State: storagepb.Config_DISABLED, MaxTokens: apiTimeConfig.MaxTokens, ReplenishmentStrategy: &storagepb.Config_TimeBased{ TimeBased: &storagepb.TimeBasedStrategy{ TokensToReplenish: apiTimeConfig.GetTimeBased().TokensToReplenish, ReplenishIntervalSeconds: apiTimeConfig.GetTimeBased().ReplenishIntervalSeconds, }, }, } ) func TestValidateMask(t *testing.T) { tests := []struct { desc string mask *field_mask.FieldMask wantErr bool }{ { desc: "commonFields", mask: commonMask, }, { desc: "sequencingBased", mask: sequencingBasedMask, }, { desc: "timeBased", mask: timeBasedMask, }, { desc: "sequencingAndTime", mask: &field_mask.FieldMask{Paths: []string{sequencingBasedPath, timeBasedPath}}, wantErr: true, }, { desc: "timeAndSequencing", mask: &field_mask.FieldMask{Paths: []string{timeBasedPath, sequencingBasedPath}}, wantErr: true, }, { desc: "unknownPaths", mask: &field_mask.FieldMask{Paths: []string{statePath, "NOT_A_FIELD", maxTokensPath}}, wantErr: true, }, { desc: "namePath", // readonly mask: &field_mask.FieldMask{Paths: []string{"name"}}, wantErr: true, }, } for _, test := range tests { err := validateMask(test.mask) if gotErr := err != nil; gotErr != test.wantErr { t.Errorf("%v: validateMask() returned err = %v, wantErr = %v", test.desc, err, test.wantErr) } } } func TestApplyMask(t *testing.T) { // destSequencingConfig must match apiSequencingConfig after the test // name is manually copied, as it's a readonly field. destSequencingConfig := proto.Clone(storageTimeConfig).(*storagepb.Config) destSequencingConfig.Name = apiSequencingConfig.Name // destTimeConfig must match apiTimeConfig after the test destTimeConfig := proto.Clone(storageSequencingConfig).(*storagepb.Config) destTimeConfig.Name = apiTimeConfig.Name destClearSequencing := proto.Clone(storageSequencingConfig).(*storagepb.Config) wantClearSequencing := destClearSequencing wantClearSequencing.ReplenishmentStrategy = nil destClearTime := proto.Clone(storageTimeConfig).(*storagepb.Config) wantClearTime := destClearTime wantClearTime.ReplenishmentStrategy = nil tests := []struct { desc string src *quotapb.Config dest, want *storagepb.Config mask *field_mask.FieldMask }{ { desc: "applyToBlank", src: apiTimeConfig, dest: &storagepb.Config{}, mask: &field_mask.FieldMask{Paths: []string{statePath, maxTokensPath}}, want: &storagepb.Config{ State: storagepb.Config_DISABLED, MaxTokens: apiTimeConfig.MaxTokens, }, }, { desc: "sequencingBasedOverwrite", src: apiSequencingConfig, dest: destSequencingConfig, mask: sequencingBasedMask, want: storageSequencingConfig, }, { desc: "timeBasedOverwrite", src: apiTimeConfig, dest: destTimeConfig, mask: timeBasedMask, want: storageTimeConfig, }, { desc: "clearSequencingIfNil", src: "apb.Config{}, dest: destClearSequencing, mask: &field_mask.FieldMask{Paths: []string{sequencingBasedPath}}, want: wantClearSequencing, }, { desc: "clearTimeIfNil", src: "apb.Config{}, dest: destClearTime, mask: &field_mask.FieldMask{Paths: []string{timeBasedPath}}, want: wantClearTime, }, } for _, test := range tests { applyMask(test.src, test.dest, test.mask) if !proto.Equal(test.dest, test.want) { t.Errorf("%v: post-applyMask() diff (-got +want):\n%v", test.desc, cmp.Diff(test.dest, test.want)) } } } func TestConvert_APIAndStorage(t *testing.T) { tests := []struct { desc string api *quotapb.Config storage *storagepb.Config }{ { desc: "sequencingBased", api: apiSequencingConfig, storage: storageSequencingConfig, }, { desc: "timeBased", api: apiTimeConfig, storage: storageTimeConfig, }, { desc: "zeroed", api: "apb.Config{}, storage: &storagepb.Config{}, }, } for _, test := range tests { if got, want := convertToAPI(test.storage), test.api; !proto.Equal(got, want) { t.Errorf("%v: post-convertToAPI() diff (-got +want):\n%v", test.desc, cmp.Diff(got, want)) } if got, want := convertToStorage(test.api), test.storage; !proto.Equal(got, want) { t.Errorf("%v: post-convertToStorage() diff (-got +want):\n%v", test.desc, cmp.Diff(got, want)) } } } trillian-1.6.1/quota/etcd/quotaapi/name.go000066400000000000000000000063031466362047600205170ustar00rootroot00000000000000// 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 quotaapi import ( "fmt" "regexp" "strconv" "strings" ) const ( collectionTrees = "trees" wildcard = "-" ) var ( // globalRE is a regex for quotas/global/ name filters. // Wildcards ("-") are not allowed to replace "global". They could be allowed, but it's a query // that doesn't make much sense, as if this format is used it has to be a global quota query. globalRE *regexp.Regexp // treeUsersRE is a broader regex for all trees/ and users/ name filters. // It doesn't guard against the following specific situations: // 1. quotas/trees///config where id is not an int64 // 2. quotas/-///config where id != "-", which is a possibly ambiguous query. It could // be allowed, but since the ID being queried is already known, so is the collection, so it // doesn't make much sense. treesUsersRE *regexp.Regexp ) func init() { var err error globalRE, err = regexp.Compile("^quotas/global/(-|read|write)/config$") if err != nil { panic(fmt.Sprintf("globalRE: %v", err)) } treesUsersRE, err = regexp.Compile("^quotas/(-|trees|users)/[^/]+/(-|read|write)/config$") if err != nil { panic(fmt.Sprintf("treesUsersRE: %v", err)) } } // nameFilter represents a config name filter, as used by ListConfigs. // // A name filter is internally represented as the segments of the name, ie, the result of // strings.Split(name, "/"). // // A few examples are: // * ["quotas", "global", "read", "configs"] // * ["quotas", "trees", "12345", "write", "config"] type nameFilter []string func newNameFilter(name string) (nameFilter, error) { if !globalRE.MatchString(name) && !treesUsersRE.MatchString(name) { return nil, fmt.Errorf("invalid name filter: %q", name) } nf := strings.Split(name, "/") // Guard against some ambiguous / incorrect wildcards that the regexes won't protect against switch collection := nf[1]; collection { case collectionTrees: id := nf[2] if id == wildcard { break } // treeID must be an int64 if _, err := strconv.ParseInt(id, 10, 64); err != nil { return nil, fmt.Errorf("invalid name filter: %q, ID %q is not a valid 64-bit integer", name, id) } case wildcard: id := nf[2] if id != wildcard { return nil, fmt.Errorf("invalid name filter: %q, ambiguous ID %q received", name, id) } } return nf, nil } func (nf nameFilter) matches(path string) bool { segments := strings.Split(path, "/") l := len(nf) if l != len(segments) { return false } // Skip first and last tokens (they're always "quotas" and "config"). for i := 1; i < l-1; i++ { if nf[i] != wildcard && nf[i] != segments[i] { return false } } return true } trillian-1.6.1/quota/etcd/quotaapi/name_test.go000066400000000000000000000126241466362047600215610ustar00rootroot00000000000000// 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 quotaapi import "testing" func TestNewNameFilter(t *testing.T) { tests := []struct { name string wantErr bool }{ {name: "quotas/global/read/config"}, {name: "quotas/global/write/config"}, {name: "quotas/global/-/config"}, // all global {name: "quotas/-/-/config", wantErr: true}, // not allowed, use quotas/global instead {name: "quotas/trees/nan/read/config", wantErr: true}, // ID must be a number {name: "quotas/trees/1/read/config"}, {name: "quotas/trees/12345/read/config"}, {name: "quotas/trees/12345/write/config"}, {name: "quotas/trees/-/read/config"}, // all trees/read {name: "quotas/trees/-/write/config"}, // all trees/write {name: "quotas/trees/12345/-/config"}, // all quotas for tree 12345 {name: "quotas/trees/-/-/config"}, // all trees {name: "quotas/users/u/read/config"}, {name: "quotas/users/llama/read/config"}, {name: "quotas/users/llama/write/config"}, {name: "quotas/users/-/read/config"}, // all users/read {name: "quotas/users/-/write/config"}, // all users/write {name: "quotas/users/llama/-/config"}, // all quotas for user "llama" {name: "quotas/users/-/-/config"}, // all users {name: "quotas/-/1/read/config", wantErr: true}, // not allowed, use either trees/1 or users/1 {name: "quotas/-/1/write/config", wantErr: true}, {name: "quotas/-/1/-/config", wantErr: true}, {name: "quotas/-/x/read/config", wantErr: true}, {name: "quotas/-/-/read/config"}, // all trees/read and users/read {name: "quotas/-/-/write/config"}, // all trees/write and users/write {name: "quotas/-/-/-/config"}, // all trees and users {name: "bad/global/read/config", wantErr: true}, {name: "quotas/global/read/bad", wantErr: true}, {name: "bad/trees/1/read/config", wantErr: true}, {name: "quotas/trees/1/read/bad", wantErr: true}, {name: "bogus/quotas/trees/1/read/config", wantErr: true}, {name: "quotas/trees/1/read/config/trailingbogus", wantErr: true}, } for _, test := range tests { _, err := newNameFilter(test.name) if gotErr := err != nil; gotErr != test.wantErr { t.Errorf("newNameFilter(%q) returned err = %v, wantErr = %v", test.name, err, test.wantErr) } } } func TestNameFilter_Matches(t *testing.T) { tests := []struct { nf string wantMatch, wantNoMatch []string }{ { nf: "quotas/global/read/config", wantMatch: []string{"quotas/global/read/config"}, wantNoMatch: []string{"quotas/global/write/config"}, }, { nf: "quotas/global/write/config", wantMatch: []string{"quotas/global/write/config"}, }, { nf: "quotas/trees/1/read/config", wantMatch: []string{"quotas/trees/1/read/config"}, wantNoMatch: []string{"quotas/trees/2/read/config"}, }, { nf: "quotas/trees/1/write/config", wantMatch: []string{"quotas/trees/1/write/config"}, }, { nf: "quotas/users/llama/read/config", wantMatch: []string{"quotas/users/llama/read/config"}, wantNoMatch: []string{"quotas/users/alpaca/read/config"}, }, { nf: "quotas/users/llama/write/config", wantMatch: []string{"quotas/users/llama/write/config"}, }, { nf: "quotas/global/-/config", wantMatch: []string{ "quotas/global/read/config", "quotas/global/write/config", }, }, { nf: "quotas/trees/12345/-/config", wantMatch: []string{ "quotas/trees/12345/read/config", "quotas/trees/12345/write/config", }, wantNoMatch: []string{ "quotas/global/read/config", "quotas/trees/1/read/config", "quotas/trees/1/write/config", "quotas/users/12345/read/config", }, }, { nf: "quotas/trees/-/-/config", wantMatch: []string{ "quotas/trees/1/read/config", "quotas/trees/1/write/config", "quotas/trees/12345/read/config", "quotas/trees/12345/write/config", }, wantNoMatch: []string{ "quotas/global/read/config", "quotas/users/12345/read/config", }, }, { nf: "quotas/users/-/-/config", wantMatch: []string{ "quotas/users/llama/read/config", "quotas/users/llama/write/config", }, wantNoMatch: []string{"quotas/trees/12345/read/config"}, }, { nf: "quotas/-/-/-/config", wantMatch: []string{ "quotas/trees/12345/read/config", "quotas/trees/12345/write/config", "quotas/users/llama/read/config", "quotas/users/llama/write/config", }, wantNoMatch: []string{"quotas/global/read/config"}, }, } for _, test := range tests { nf, err := newNameFilter(test.nf) if err != nil { t.Errorf("newNameFilter(%q) returned err = %v", test.nf, err) continue } run := func(names []string, want bool) { for _, name := range names { if got := nf.matches(name); got != want { t.Errorf("newNameFilter(%q).matches(%q) = %v, want = %v", test.nf, name, got, want) } } } run(test.wantMatch, true) run(test.wantNoMatch, false) } } trillian-1.6.1/quota/etcd/quotaapi/quota_server.go000066400000000000000000000160301466362047600223140ustar00rootroot00000000000000// 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 quotaapi provides a Quota admin server implementation. package quotaapi import ( "context" "github.com/google/trillian/quota/etcd/quotapb" "github.com/google/trillian/quota/etcd/storage" "github.com/google/trillian/quota/etcd/storagepb" clientv3 "go.etcd.io/etcd/client/v3" "google.golang.org/genproto/protobuf/field_mask" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/emptypb" "k8s.io/klog/v2" ) // Server is a quotapb.QuotaServer implementation backed by etcd. type Server struct { qs *storage.QuotaStorage } // NewServer returns a new Server instance backed by client. func NewServer(client *clientv3.Client) *Server { return &Server{qs: &storage.QuotaStorage{Client: client}} } // CreateConfig implements quotapb.QuotaServer.CreateConfig. func (s *Server) CreateConfig(ctx context.Context, req *quotapb.CreateConfigRequest) (*quotapb.Config, error) { if req.Config == nil { return nil, status.Errorf(codes.InvalidArgument, "config is required") } if err := validateName(req.Name); err != nil { return nil, err } req.Config.Name = req.Name var alreadyExists bool updated, err := s.qs.UpdateConfigs(ctx, false /* reset */, func(cfgs *storagepb.Configs) { if _, alreadyExists = findByName(req.Name, cfgs); alreadyExists { return } cfgs.Configs = append(cfgs.Configs, convertToStorage(req.Config)) }) switch { case alreadyExists: return nil, status.Errorf(codes.AlreadyExists, "%q already exists", req.Name) case err != nil: return nil, err } return s.getConfig(ctx, req.Name, updated, codes.Internal) } // DeleteConfig implements quotapb.QuotaServer.DeleteConfig. func (s *Server) DeleteConfig(ctx context.Context, req *quotapb.DeleteConfigRequest) (*emptypb.Empty, error) { if err := validateName(req.Name); err != nil { return nil, err } notFound := false _, err := s.qs.UpdateConfigs(ctx, false /* reset */, func(cfgs *storagepb.Configs) { for i, cfg := range cfgs.Configs { if cfg.Name == req.Name { cfgs.Configs = append(cfgs.Configs[:i], cfgs.Configs[i+1:]...) return } } notFound = true }) if notFound { return nil, status.Errorf(codes.NotFound, "%q not found", req.Name) } return &emptypb.Empty{}, err } // GetConfig implements quotapb.QuotaServer.GetConfig. func (s *Server) GetConfig(ctx context.Context, req *quotapb.GetConfigRequest) (*quotapb.Config, error) { if err := validateName(req.Name); err != nil { return nil, err } cfgs, err := s.qs.Configs(ctx) if err != nil { return nil, err } return s.getConfig(ctx, req.Name, cfgs, codes.NotFound) } // getConfig finds the Config named "name" on "cfgs", converts it to API and returns it. // If the config cannot be found an error with code "code" is returned. func (s *Server) getConfig(ctx context.Context, name string, cfgs *storagepb.Configs, code codes.Code) (*quotapb.Config, error) { storedCfg, ok := findByName(name, cfgs) if !ok { return nil, status.Errorf(code, "%q not found", name) } cfg := convertToAPI(storedCfg) tokens, err := s.qs.Peek(ctx, []string{cfg.Name}) if err == nil { cfg.CurrentTokens = tokens[cfg.Name] } else { klog.Warningf("Unexpected error peeking token count for %q: %v", cfg.Name, err) } return cfg, nil } // ListConfigs implements quotapb.QuotaServer.ListConfigs. func (s *Server) ListConfigs(ctx context.Context, req *quotapb.ListConfigsRequest) (*quotapb.ListConfigsResponse, error) { nfs := make([]nameFilter, 0, len(req.Names)) for _, name := range req.Names { nf, err := newNameFilter(name) if err != nil { return nil, status.Error(codes.InvalidArgument, err.Error()) } nfs = append(nfs, nf) } cfgs, err := s.qs.Configs(ctx) if err != nil { return nil, err } resp := "apb.ListConfigsResponse{} for _, cfg := range cfgs.Configs { if listMatches(nfs, cfg) { resp.Configs = append(resp.Configs, listView(req.View, cfg)) } } // Peek token counts for FULL view if req.View == quotapb.ListConfigsRequest_FULL { names := make([]string, 0, len(resp.Configs)) for _, cfg := range resp.Configs { names = append(names, cfg.Name) } tokens, err := s.qs.Peek(ctx, names) if err == nil { for _, cfg := range resp.Configs { cfg.CurrentTokens = tokens[cfg.Name] } } else { klog.Infof("Error peeking token counts for %v: %v", names, err) } } return resp, nil } func listMatches(nfs []nameFilter, cfg *storagepb.Config) bool { if len(nfs) == 0 { return true // Match all } for _, nf := range nfs { if nf.matches(cfg.Name) { return true } } return false } func listView(view quotapb.ListConfigsRequest_ListView, src *storagepb.Config) *quotapb.Config { switch view { case quotapb.ListConfigsRequest_BASIC: return "apb.Config{Name: src.Name} default: return convertToAPI(src) } } // UpdateConfig implements quotapb.QuotaServer.UpdateConfig. func (s *Server) UpdateConfig(ctx context.Context, req *quotapb.UpdateConfigRequest) (*quotapb.Config, error) { cfg, mask := req.Config, req.UpdateMask hasConfig := cfg != nil hasMask := mask != nil && len(mask.Paths) > 0 switch { case req.ResetQuota && !hasConfig && !hasMask: // For convenience, reset-only requests are allowed. cfg = "apb.Config{} mask = &field_mask.FieldMask{} case !hasConfig: return nil, status.Errorf(codes.InvalidArgument, "config must be specified") case !hasMask: return nil, status.Errorf(codes.InvalidArgument, "update_mask must be specified") } if err := validateName(req.Name); err != nil { return nil, err } if err := validateMask(mask); err != nil { return nil, status.Errorf(codes.InvalidArgument, "invalid update_mask: %v", err) } var notFound bool updated, err := s.qs.UpdateConfigs(ctx, req.ResetQuota, func(cfgs *storagepb.Configs) { existingCfg, ok := findByName(req.Name, cfgs) if !ok { notFound = true return } applyMask(cfg, existingCfg, mask) }) switch { case notFound: return nil, status.Errorf(codes.NotFound, "%q not found", req.Name) case err != nil: return nil, err } return s.getConfig(ctx, req.Name, updated, codes.Internal) } func findByName(name string, cfgs *storagepb.Configs) (*storagepb.Config, bool) { for _, cfg := range cfgs.Configs { if cfg.Name == name { return cfg, true } } return nil, false } func validateName(name string) error { switch { case name == "": return status.Errorf(codes.InvalidArgument, "name is required") case !storage.IsNameValid(name): return status.Errorf(codes.InvalidArgument, "invalid name: %q", name) } return nil } trillian-1.6.1/quota/etcd/quotaapi/quota_server_test.go000066400000000000000000000566501466362047600233670ustar00rootroot00000000000000// 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 quotaapi import ( "context" "fmt" "math/rand" "net" "os" "sort" "strings" "sync" "testing" "github.com/google/go-cmp/cmp" "github.com/google/trillian/quota" "github.com/google/trillian/quota/etcd/quotapb" "github.com/google/trillian/quota/etcd/storage" "github.com/google/trillian/quota/etcd/storagepb" "github.com/google/trillian/server/interceptor" "github.com/google/trillian/testonly/integration/etcd" clientv3 "go.etcd.io/etcd/client/v3" "google.golang.org/genproto/protobuf/field_mask" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/status" "google.golang.org/protobuf/proto" ) var ( globalWrite = "apb.Config{ Name: "quotas/global/write/config", State: quotapb.Config_ENABLED, MaxTokens: 100, CurrentTokens: 100, // Assume quota is full ReplenishmentStrategy: "apb.Config_SequencingBased{ SequencingBased: "apb.SequencingBasedStrategy{}, }, } globalRead = "apb.Config{ Name: "quotas/global/read/config", State: quotapb.Config_ENABLED, MaxTokens: 100, CurrentTokens: 100, // Assume quota is full ReplenishmentStrategy: "apb.Config_TimeBased{ TimeBased: "apb.TimeBasedStrategy{ TokensToReplenish: 10000, ReplenishIntervalSeconds: 100, }, }, } tree1Read = copyWithName(globalRead, "quotas/trees/1/read/config") tree1Write = copyWithName(globalWrite, "quotas/trees/1/write/config") tree2Read = copyWithName(globalRead, "quotas/trees/2/read/config") tree2Write = copyWithName(globalWrite, "quotas/trees/2/write/config") userRead = copyWithName(globalRead, "quotas/users/llama/read/config") userWrite = copyWithName(globalRead, "quotas/users/llama/write/config") etcdClient *clientv3.Client quotaClient quotapb.QuotaClient ) func copyWithName(c *quotapb.Config, name string) *quotapb.Config { cp := proto.Clone(c).(*quotapb.Config) cp.Name = name return cp } func TestMain(m *testing.M) { _, ec, stopEtcd, err := etcd.StartEtcd() if err != nil { panic(fmt.Sprintf("StartEtcd() returned err = %v", err)) } // Don't defer stopEtcd(): the os.Exit() call below doesn't allow for defers. etcdClient = ec qc, stopServer, err := startServer(ec) if err != nil { stopEtcd() panic(fmt.Sprintf("startServer() returned err = %v", err)) } // Don't defer stopServer(). quotaClient = qc exitCode := m.Run() stopServer() stopEtcd() os.Exit(exitCode) } func TestServer_CreateConfig(t *testing.T) { globalWrite2 := proto.Clone(globalWrite).(*quotapb.Config) globalWrite2.MaxTokens += 10 globalWrite2.CurrentTokens = globalWrite2.MaxTokens overrideName := proto.Clone(globalWrite).(*quotapb.Config) overrideName.Name = "ignored" invalidConfig := proto.Clone(globalWrite).(*quotapb.Config) invalidConfig.State = quotapb.Config_UNKNOWN_CONFIG_STATE tests := []upsertTest{ { desc: "emptyName", req: "apb.CreateConfigRequest{Config: globalWrite}, wantCode: codes.InvalidArgument, }, { desc: "emptyConfig", req: "apb.CreateConfigRequest{Name: globalWrite.Name}, wantCode: codes.InvalidArgument, }, { desc: "success", req: "apb.CreateConfigRequest{Name: globalWrite.Name, Config: globalWrite}, wantCfg: globalWrite, }, { desc: "alreadyExists", baseCfg: globalWrite, req: "apb.CreateConfigRequest{Name: globalWrite.Name, Config: globalWrite2}, wantCode: codes.AlreadyExists, wantCfg: globalWrite, }, { desc: "overrideName", req: "apb.CreateConfigRequest{ Name: globalWrite.Name, Config: overrideName, }, wantCfg: globalWrite, }, { desc: "invalidConfig", req: "apb.CreateConfigRequest{ Name: "quotas/global/read/config", Config: invalidConfig, }, wantCode: codes.InvalidArgument, }, } ctx := context.Background() rpc := func(ctx context.Context, req interface{}) (*quotapb.Config, error) { return quotaClient.CreateConfig(ctx, req.(*quotapb.CreateConfigRequest)) } for _, test := range tests { if err := runUpsertTest(ctx, test, rpc, "CreateConfig"); err != nil { t.Error(err) } } } func TestServer_UpdateConfig(t *testing.T) { disabledGlobalWrite := proto.Clone(globalWrite).(*quotapb.Config) // Disabled quotas have "infinite" tokens disabledGlobalWrite.CurrentTokens = int64(quota.MaxTokens) disabledGlobalWrite.State = quotapb.Config_DISABLED timeBasedGlobalWrite := proto.Clone(globalWrite).(*quotapb.Config) timeBasedGlobalWrite.MaxTokens += 100 timeBasedGlobalWrite.CurrentTokens = globalWrite.MaxTokens timeBasedGlobalWrite.ReplenishmentStrategy = "apb.Config_TimeBased{ TimeBased: "apb.TimeBasedStrategy{ TokensToReplenish: 100, ReplenishIntervalSeconds: 200, }, } timeBasedGlobalWrite2 := proto.Clone(timeBasedGlobalWrite).(*quotapb.Config) timeBasedGlobalWrite2.CurrentTokens = timeBasedGlobalWrite.MaxTokens timeBasedGlobalWrite2.GetTimeBased().TokensToReplenish += 50 timeBasedGlobalWrite2.GetTimeBased().ReplenishIntervalSeconds -= 20 tests := []upsertTest{ { desc: "disableQuota", baseCfg: globalWrite, req: "apb.UpdateConfigRequest{ Name: globalWrite.Name, Config: "apb.Config{State: disabledGlobalWrite.State}, UpdateMask: &field_mask.FieldMask{Paths: []string{"state"}}, }, wantCfg: disabledGlobalWrite, }, { desc: "timeBasedGlobalWrite", baseCfg: globalWrite, req: "apb.UpdateConfigRequest{ Name: globalWrite.Name, Config: "apb.Config{ MaxTokens: timeBasedGlobalWrite.MaxTokens, ReplenishmentStrategy: timeBasedGlobalWrite.ReplenishmentStrategy, }, UpdateMask: &field_mask.FieldMask{Paths: []string{"max_tokens", "time_based"}}, }, wantCfg: timeBasedGlobalWrite, }, { desc: "timeBasedGlobalWrite2", baseCfg: timeBasedGlobalWrite, req: "apb.UpdateConfigRequest{ Name: timeBasedGlobalWrite.Name, Config: "apb.Config{ ReplenishmentStrategy: timeBasedGlobalWrite2.ReplenishmentStrategy, }, UpdateMask: &field_mask.FieldMask{Paths: []string{"time_based"}}, }, wantCfg: timeBasedGlobalWrite2, }, { desc: "unknownConfig", req: "apb.UpdateConfigRequest{ Name: globalWrite.Name, Config: "apb.Config{MaxTokens: 200}, UpdateMask: &field_mask.FieldMask{Paths: []string{"max_tokens"}}, }, wantCode: codes.NotFound, }, { desc: "badConfig", baseCfg: globalWrite, req: "apb.UpdateConfigRequest{ Name: globalWrite.Name, Config: "apb.Config{}, // State == UNKNOWN UpdateMask: &field_mask.FieldMask{Paths: []string{"state"}}, }, wantCode: codes.InvalidArgument, wantCfg: globalWrite, }, { desc: "badPath", baseCfg: globalWrite, req: "apb.UpdateConfigRequest{ Name: globalWrite.Name, Config: "apb.Config{}, UpdateMask: &field_mask.FieldMask{Paths: []string{"NOT_A_FIELD"}}, }, wantCode: codes.InvalidArgument, wantCfg: globalWrite, }, { desc: "emptyName", req: "apb.UpdateConfigRequest{ Config: "apb.Config{MaxTokens: 100}, UpdateMask: &field_mask.FieldMask{Paths: []string{"max_tokens"}}, }, wantCode: codes.InvalidArgument, }, { desc: "emptyConfig", baseCfg: globalWrite, req: "apb.UpdateConfigRequest{ Name: globalWrite.Name, UpdateMask: &field_mask.FieldMask{Paths: []string{"max_tokens"}}, }, wantCode: codes.InvalidArgument, wantCfg: globalWrite, }, { desc: "emptyMask", baseCfg: globalWrite, req: "apb.UpdateConfigRequest{ Name: globalWrite.Name, Config: "apb.Config{MaxTokens: 100}, }, wantCode: codes.InvalidArgument, wantCfg: globalWrite, }, { desc: "emptyConfigWithReset", baseCfg: globalWrite, req: "apb.UpdateConfigRequest{ Name: globalWrite.Name, UpdateMask: &field_mask.FieldMask{Paths: []string{"max_tokens"}}, ResetQuota: true, }, wantCode: codes.InvalidArgument, wantCfg: globalWrite, }, { desc: "emptyMaskWithReset", baseCfg: globalWrite, req: "apb.UpdateConfigRequest{ Name: globalWrite.Name, Config: "apb.Config{MaxTokens: 100}, ResetQuota: true, }, wantCode: codes.InvalidArgument, wantCfg: globalWrite, }, } ctx := context.Background() rpc := func(ctx context.Context, req interface{}) (*quotapb.Config, error) { return quotaClient.UpdateConfig(ctx, req.(*quotapb.UpdateConfigRequest)) } for _, test := range tests { if err := runUpsertTest(ctx, test, rpc, "UpdateConfig"); err != nil { t.Error(err) } } } func TestServer_UpdateConfig_ResetQuota(t *testing.T) { ctx := context.Background() if err := reset(ctx); err != nil { t.Fatalf("reset() returned err = %v", err) } if _, err := quotaClient.CreateConfig(ctx, "apb.CreateConfigRequest{ Name: globalWrite.Name, Config: globalWrite, }); err != nil { t.Fatalf("CreateConfig() returned err = %v", err) } qs := storage.QuotaStorage{Client: etcdClient} if err := qs.Get(ctx, []string{globalWrite.Name}, globalWrite.MaxTokens-1); err != nil { t.Fatalf("Get() returned err = %v", err) } cfg := proto.Clone(globalWrite).(*quotapb.Config) cfg.MaxTokens += 10 tests := []struct { desc string req *quotapb.UpdateConfigRequest wantTokens int64 }{ { desc: "resetFalse", req: "apb.UpdateConfigRequest{ Name: globalWrite.Name, Config: cfg, UpdateMask: &field_mask.FieldMask{Paths: []string{"max_tokens"}}, }, wantTokens: 1, }, { desc: "resetTrue", req: "apb.UpdateConfigRequest{Name: globalWrite.Name, ResetQuota: true}, wantTokens: cfg.MaxTokens, }, } for _, test := range tests { t.Run(test.desc, func(t *testing.T) { if _, err := quotaClient.UpdateConfig(ctx, test.req); err != nil { t.Errorf("UpdateConfig() returned err = %v", err) return } tokens, err := qs.Peek(ctx, []string{test.req.Name}) if err != nil { t.Fatalf("Peek() returned err = %v", err) } if got := tokens[test.req.Name]; got != test.wantTokens { t.Errorf("%q has %v tokens, want = %v", test.req.Name, got, test.wantTokens) } }) } } func TestServer_UpdateConfig_ConcurrentUpdates(t *testing.T) { if testing.Short() { t.Skip("Skipping concurrency test due to short mode") return } ctx := context.Background() if err := reset(ctx); err != nil { t.Fatalf("reset() returned err = %v", err) } // Quotas we'll use for the test, a few of each type configs := []*quotapb.Config{globalRead, globalWrite, tree1Read, tree1Write, userRead, userWrite} // Create the configs we'll use for the test concurrently. // If we get any errors a msg is sent to createErrs; that's a sign to stop // the test *after* all spawned goroutines have exited. createErrs := make(chan bool, len(configs)) wg := sync.WaitGroup{} for _, cfg := range configs { cfg := cfg wg.Add(1) go func() { defer wg.Done() if _, err := quotaClient.CreateConfig(ctx, "apb.CreateConfigRequest{ Name: cfg.Name, Config: cfg, }); err != nil { t.Errorf("%v: CreateConfig() returned err = %v", cfg.Name, err) createErrs <- true } }() } wg.Wait() select { case <-createErrs: return default: } const routinesPerConfig = 4 const updatesPerRoutine = 100 for _, cfg := range configs { for num := 0; num < routinesPerConfig; num++ { wg.Add(1) go func(num int, want *quotapb.Config) { defer wg.Done() baseTokens := 1 + rand.Intn(routinesPerConfig*100) reset := num%2 == 0 for i := 0; i < updatesPerRoutine; i++ { tokens := int64(baseTokens + i) got, err := quotaClient.UpdateConfig(ctx, "apb.UpdateConfigRequest{ Name: want.Name, Config: "apb.Config{MaxTokens: tokens}, UpdateMask: &field_mask.FieldMask{Paths: []string{"max_tokens"}}, ResetQuota: reset, }) if err != nil { t.Errorf("%v: UpdateConfig() returned err = %v", want.Name, err) continue } want.CurrentTokens = got.CurrentTokens // Not important for this test want.MaxTokens = tokens if !proto.Equal(got, want) { diff := cmp.Diff(got, &want, cmp.Comparer(proto.Equal)) t.Errorf("%v: post-UpdateConfig() diff (-got +want):\n%v", want.Name, diff) } } }(num, proto.Clone(cfg).(*quotapb.Config)) } } wg.Wait() } // upsertTest represents either a CreateConfig or UpdateConfig test. type upsertTest struct { desc string // baseCfg is created before calling the RPC, if non-nil. baseCfg *quotapb.Config req upsertRequest wantCode codes.Code wantCfg *quotapb.Config } type upsertRequest interface { GetName() string } type upsertRPC func(ctx context.Context, req interface{}) (*quotapb.Config, error) // runUpsertTest runs either CreateConfig or UpdateConfig tests, depending on the supplied rpc. // Storage is reset() before the test is executed. func runUpsertTest(ctx context.Context, test upsertTest, rpc upsertRPC, rpcName string) error { if err := reset(ctx); err != nil { return fmt.Errorf("%v: reset() returned err = %v", test.desc, err) } if test.baseCfg != nil { if _, err := quotaClient.CreateConfig(ctx, "apb.CreateConfigRequest{ Name: test.baseCfg.Name, Config: test.baseCfg, }); err != nil { return fmt.Errorf("%v: CreateConfig() returned err = %v", test.desc, err) } } cfg, err := rpc(ctx, test.req) switch s, ok := status.FromError(err); { case !ok || s.Code() != test.wantCode: return fmt.Errorf("%v: %v() returned err = %v, wantCode = %s", test.desc, rpcName, err, test.wantCode) case test.wantCode == codes.OK && !proto.Equal(cfg, test.wantCfg): return fmt.Errorf("%v: post-%v() diff:\n%v", test.desc, rpcName, cmp.Diff(cfg, test.wantCfg, cmp.Comparer(proto.Equal))) case test.wantCfg == nil: return nil } switch stored, err := quotaClient.GetConfig(ctx, "apb.GetConfigRequest{Name: test.req.GetName()}); { case err != nil: return fmt.Errorf("%v: GetConfig() returned err = %v", test.desc, err) case !proto.Equal(stored, test.wantCfg): return fmt.Errorf("%v: post-GetConfig() diff:\n%v", test.desc, cmp.Diff(stored, test.wantCfg, cmp.Comparer(proto.Equal))) } return nil } func TestServer_DeleteConfig(t *testing.T) { tests := []struct { desc string createCfgs, wantCfgs []*quotapb.Config req *quotapb.DeleteConfigRequest }{ { desc: "success", createCfgs: []*quotapb.Config{ globalRead, globalWrite, tree1Read, tree1Write, tree2Read, tree2Write, userRead, userWrite, }, wantCfgs: []*quotapb.Config{ globalRead, globalWrite, tree1Write, tree2Read, tree2Write, userRead, userWrite, }, req: "apb.DeleteConfigRequest{Name: tree1Read.Name}, }, { desc: "lastConfig", createCfgs: []*quotapb.Config{tree1Read}, req: "apb.DeleteConfigRequest{Name: tree1Read.Name}, }, { desc: "global", createCfgs: []*quotapb.Config{globalRead, globalWrite}, wantCfgs: []*quotapb.Config{globalRead}, req: "apb.DeleteConfigRequest{Name: globalWrite.Name}, }, { desc: "user", createCfgs: []*quotapb.Config{userRead, userWrite}, wantCfgs: []*quotapb.Config{userWrite}, req: "apb.DeleteConfigRequest{Name: userRead.Name}, }, } ctx := context.Background() for _, test := range tests { if err := reset(ctx); err != nil { t.Errorf("%v: reset() returned err = %v", test.desc, err) continue } for _, cfg := range test.createCfgs { if _, err := quotaClient.CreateConfig(ctx, "apb.CreateConfigRequest{ Name: cfg.Name, Config: cfg, }); err != nil { t.Fatalf("%v: CreateConfig(%q) returned err = %v", test.desc, cfg.Name, err) } } if _, err := quotaClient.DeleteConfig(ctx, test.req); err != nil { t.Errorf("%v: DeleteConfig(%q) returned err = %v", test.desc, test.req.Name, err) continue } resp, err := quotaClient.ListConfigs(ctx, "apb.ListConfigsRequest{ View: quotapb.ListConfigsRequest_FULL, }) if err != nil { t.Errorf("%v: ListConfigs() returned err = %v", test.desc, err) continue } if err := sortAndCompare(resp.Configs, test.wantCfgs); err != nil { t.Errorf("%v: post-DeleteConfig() %v", test.desc, err) } } } func TestServer_DeleteConfigErrors(t *testing.T) { tests := []struct { desc string req *quotapb.DeleteConfigRequest wantCode codes.Code }{ { desc: "emptyName", req: "apb.DeleteConfigRequest{}, wantCode: codes.InvalidArgument, }, { desc: "badName", req: "apb.DeleteConfigRequest{Name: "bad/quota/name"}, wantCode: codes.InvalidArgument, }, { desc: "unknown", req: "apb.DeleteConfigRequest{Name: globalWrite.Name}, wantCode: codes.NotFound, }, } ctx := context.Background() for _, test := range tests { _, err := quotaClient.DeleteConfig(ctx, test.req) if s, ok := status.FromError(err); !ok || s.Code() != test.wantCode { t.Errorf("%v: DeleteConfig() returned err = %v, wantCode = %s", test.desc, err, test.wantCode) } } } func TestServer_GetConfigErrors(t *testing.T) { ctx := context.Background() if err := reset(ctx); err != nil { t.Fatalf("reset() returned err = %v", err) } tests := []struct { desc string req *quotapb.GetConfigRequest wantCode codes.Code }{ { desc: "emptyName", req: "apb.GetConfigRequest{}, wantCode: codes.InvalidArgument, }, { desc: "badName", req: "apb.GetConfigRequest{Name: "not/a/config/name"}, wantCode: codes.InvalidArgument, }, { desc: "notFound", req: "apb.GetConfigRequest{Name: "quotas/global/write/config"}, wantCode: codes.NotFound, }, } for _, test := range tests { // GetConfig's success return is tested as a consequence of other test cases. _, err := quotaClient.GetConfig(ctx, test.req) if s, ok := status.FromError(err); !ok || s.Code() != test.wantCode { t.Errorf("%v: GetConfig() returned err = %v, wantCode = %s", test.desc, err, test.wantCode) } } } func TestServer_ListConfigs(t *testing.T) { ctx := context.Background() if err := reset(ctx); err != nil { t.Fatalf("reset() returned err = %v", err) } // Test listing with an empty config first, so we can work with a fixed list of configs from // here on resp, err := quotaClient.ListConfigs(ctx, "apb.ListConfigsRequest{}) switch { case err != nil: t.Errorf("empty: ListConfigs() returned err = %v", err) case len(resp.Configs) != 0: t.Errorf("empty: ListConfigs() returned >0 results: %v", err) } configs := []*quotapb.Config{ globalRead, globalWrite, tree1Read, tree1Write, tree2Read, tree2Write, userRead, userWrite, } for _, cfg := range configs { if _, err := quotaClient.CreateConfig(ctx, "apb.CreateConfigRequest{ Name: cfg.Name, Config: cfg, }); err != nil { t.Fatalf("%q: CreateConfig() returned err = %v", cfg.Name, err) } } basicGlobalRead := "apb.Config{Name: globalRead.Name} basicGlobalWrite := "apb.Config{Name: globalWrite.Name} basicTree1Read := "apb.Config{Name: tree1Read.Name} basicTree1Write := "apb.Config{Name: tree1Write.Name} basicTree2Read := "apb.Config{Name: tree2Read.Name} basicTree2Write := "apb.Config{Name: tree2Write.Name} basicUserRead := "apb.Config{Name: userRead.Name} basicUserWrite := "apb.Config{Name: userWrite.Name} tests := []struct { desc string req *quotapb.ListConfigsRequest wantCfgs []*quotapb.Config }{ { desc: "allBasicView", req: "apb.ListConfigsRequest{}, wantCfgs: []*quotapb.Config{ basicGlobalRead, basicGlobalWrite, basicTree1Read, basicTree1Write, basicTree2Read, basicTree2Write, basicUserRead, basicUserWrite, }, }, { desc: "allFullView", req: "apb.ListConfigsRequest{View: quotapb.ListConfigsRequest_FULL}, wantCfgs: configs, }, { desc: "allTrees", req: "apb.ListConfigsRequest{Names: []string{"quotas/trees/-/-/config"}}, wantCfgs: []*quotapb.Config{basicTree1Read, basicTree1Write, basicTree2Read, basicTree2Write}, }, { desc: "allUsers", req: "apb.ListConfigsRequest{Names: []string{"quotas/users/-/-/config"}}, wantCfgs: []*quotapb.Config{basicUserRead, basicUserWrite}, }, { desc: "unknowns", req: "apb.ListConfigsRequest{ Names: []string{ "quotas/trees/99997/read/config", "quotas/trees/99998/write/config", "quotas/trees/99999/-/config", "quotas/users/unknown/-/config", }, }, }, } for _, test := range tests { resp, err := quotaClient.ListConfigs(ctx, test.req) if err != nil { t.Errorf("%v: ListConfigs() returned err = %v", test.desc, err) continue } if err := sortAndCompare(resp.Configs, test.wantCfgs); err != nil { t.Errorf("%v: post-ListConfigs() %v", test.desc, err) } } } func sortAndCompare(got, want []*quotapb.Config) error { sort.Slice(got, func(i, j int) bool { return strings.Compare(got[i].Name, got[j].Name) == -1 }) sort.Slice(want, func(i, j int) bool { return strings.Compare(want[i].Name, want[j].Name) == -1 }) if len(got) != len(want) { return fmt.Errorf("got %v configs, want %v", len(got), len(want)) } for i, cfg := range want { if !proto.Equal(got[i], cfg) { return fmt.Errorf("diff (-got +want):\n%v", cmp.Diff(got[i], cfg, cmp.Comparer(proto.Equal))) } } return nil } func TestServer_ListConfigsErrors(t *testing.T) { tests := []struct { desc string req *quotapb.ListConfigsRequest wantCode codes.Code }{ { desc: "badNameFilter", req: "apb.ListConfigsRequest{Names: []string{"bad/quota/name"}}, wantCode: codes.InvalidArgument, }, } ctx := context.Background() for _, test := range tests { _, err := quotaClient.ListConfigs(ctx, test.req) if s, ok := status.FromError(err); !ok || s.Code() != test.wantCode { t.Errorf("%v: ListConfigs() returned err = %v, wantCode = %s", test.desc, err, test.wantCode) } } } func reset(ctx context.Context) error { qs := storage.QuotaStorage{Client: etcdClient} _, err := qs.UpdateConfigs(ctx, true /* reset */, func(c *storagepb.Configs) { *c = storagepb.Configs{} }) return err } func startServer(etcdClient *clientv3.Client) (quotapb.QuotaClient, func(), error) { var lis net.Listener var s *grpc.Server var conn *grpc.ClientConn cleanup := func() { if conn != nil { _ = conn.Close() } if s != nil { s.GracefulStop() } if lis != nil { _ = lis.Close() } } var err error lis, err = net.Listen("tcp", "localhost:0") if err != nil { cleanup() return nil, nil, err } s = grpc.NewServer(grpc.UnaryInterceptor(interceptor.ErrorWrapper)) quotapb.RegisterQuotaServer(s, NewServer(etcdClient)) go func() { _ = s.Serve(lis) }() conn, err = grpc.Dial(lis.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { cleanup() return nil, nil, err } quotaClient := quotapb.NewQuotaClient(conn) return quotaClient, cleanup, nil } trillian-1.6.1/quota/etcd/quotapb/000077500000000000000000000000001466362047600170765ustar00rootroot00000000000000trillian-1.6.1/quota/etcd/quotapb/doc.go000066400000000000000000000014531466362047600201750ustar00rootroot00000000000000// 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 quotapb contains definitions for quota API protos and RPC service. // TODO(codingllama): Add a brief explanation of how the quota system works and // example configurations. package quotapb trillian-1.6.1/quota/etcd/quotapb/gen.go000066400000000000000000000014671466362047600202060ustar00rootroot00000000000000// 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 quotapb //go:generate protoc -I=. -I=../../../third_party/googleapis --go_out=paths=source_relative:. --go-grpc_out=paths=source_relative:. --go-grpc_opt=require_unimplemented_servers=false quotapb.proto trillian-1.6.1/quota/etcd/quotapb/quotapb.pb.go000066400000000000000000001135641466362047600215120ustar00rootroot00000000000000// 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. // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.34.2 // protoc v3.20.1 // source: quotapb.proto package quotapb import ( _ "google.golang.org/genproto/googleapis/api/annotations" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" emptypb "google.golang.org/protobuf/types/known/emptypb" fieldmaskpb "google.golang.org/protobuf/types/known/fieldmaskpb" reflect "reflect" sync "sync" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // Possible states of a quota configuration. type Config_State int32 const ( // Unknown quota state. Invalid. Config_UNKNOWN_CONFIG_STATE Config_State = 0 // Quota is enabled. Config_ENABLED Config_State = 1 // Quota is disabled (considered infinite). Config_DISABLED Config_State = 2 ) // Enum value maps for Config_State. var ( Config_State_name = map[int32]string{ 0: "UNKNOWN_CONFIG_STATE", 1: "ENABLED", 2: "DISABLED", } Config_State_value = map[string]int32{ "UNKNOWN_CONFIG_STATE": 0, "ENABLED": 1, "DISABLED": 2, } ) func (x Config_State) Enum() *Config_State { p := new(Config_State) *p = x return p } func (x Config_State) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (Config_State) Descriptor() protoreflect.EnumDescriptor { return file_quotapb_proto_enumTypes[0].Descriptor() } func (Config_State) Type() protoreflect.EnumType { return &file_quotapb_proto_enumTypes[0] } func (x Config_State) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use Config_State.Descriptor instead. func (Config_State) EnumDescriptor() ([]byte, []int) { return file_quotapb_proto_rawDescGZIP(), []int{0, 0} } // Possible views for ListConfig. type ListConfigsRequest_ListView int32 const ( // Only the Config name gets returned. ListConfigsRequest_BASIC ListConfigsRequest_ListView = 0 // Complete Config. ListConfigsRequest_FULL ListConfigsRequest_ListView = 1 ) // Enum value maps for ListConfigsRequest_ListView. var ( ListConfigsRequest_ListView_name = map[int32]string{ 0: "BASIC", 1: "FULL", } ListConfigsRequest_ListView_value = map[string]int32{ "BASIC": 0, "FULL": 1, } ) func (x ListConfigsRequest_ListView) Enum() *ListConfigsRequest_ListView { p := new(ListConfigsRequest_ListView) *p = x return p } func (x ListConfigsRequest_ListView) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (ListConfigsRequest_ListView) Descriptor() protoreflect.EnumDescriptor { return file_quotapb_proto_enumTypes[1].Descriptor() } func (ListConfigsRequest_ListView) Type() protoreflect.EnumType { return &file_quotapb_proto_enumTypes[1] } func (x ListConfigsRequest_ListView) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use ListConfigsRequest_ListView.Descriptor instead. func (ListConfigsRequest_ListView) EnumDescriptor() ([]byte, []int) { return file_quotapb_proto_rawDescGZIP(), []int{6, 0} } // Configuration of a quota. // // Quotas contain a certain number of tokens that get applied to their // corresponding entities. Global quotas apply to all operations, tree and user // quotas to certain trees and users, respectively. // // Performing an operation costs a certain number of tokens (usually one). Once // a quota has no more tokens available, requests that would subtract from it // are denied with a resource_exhausted error. // // Tokens may be replenished in two different ways: either by passage of time or // sequencing progress. Time-based replenishment adds a fixed amount of tokens // after a certain interval. Sequencing-based adds a token for each leaf // processed by the sequencer. Sequencing-based replenishment may only be used // with global and tree quotas. // // A quota may be disabled or removed at any time. The effect is the same: a // disabled or non-existing quota is considered infinite by the quota system. // (Disabling is handy if you plan to re-enable a quota later on.) type Config struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Name of the config, eg, “quotas/trees/1234/read/config”. // Readonly. Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // State of the config. State Config_State `protobuf:"varint,2,opt,name=state,proto3,enum=quotapb.Config_State" json:"state,omitempty"` // Max number of tokens available for the config. MaxTokens int64 `protobuf:"varint,3,opt,name=max_tokens,json=maxTokens,proto3" json:"max_tokens,omitempty"` // Replenishment strategy used by the config. // // Types that are assignable to ReplenishmentStrategy: // // *Config_SequencingBased // *Config_TimeBased ReplenishmentStrategy isConfig_ReplenishmentStrategy `protobuf_oneof:"replenishment_strategy"` // Current number of tokens available for the config. // May be higher than max_tokens for DISABLED configs, which are considered to // have "infinite" tokens. // Readonly. CurrentTokens int64 `protobuf:"varint,6,opt,name=current_tokens,json=currentTokens,proto3" json:"current_tokens,omitempty"` } func (x *Config) Reset() { *x = Config{} if protoimpl.UnsafeEnabled { mi := &file_quotapb_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_quotapb_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_quotapb_proto_rawDescGZIP(), []int{0} } func (x *Config) GetName() string { if x != nil { return x.Name } return "" } func (x *Config) GetState() Config_State { if x != nil { return x.State } return Config_UNKNOWN_CONFIG_STATE } func (x *Config) GetMaxTokens() int64 { if x != nil { return x.MaxTokens } return 0 } func (m *Config) GetReplenishmentStrategy() isConfig_ReplenishmentStrategy { if m != nil { return m.ReplenishmentStrategy } return nil } func (x *Config) GetSequencingBased() *SequencingBasedStrategy { if x, ok := x.GetReplenishmentStrategy().(*Config_SequencingBased); ok { return x.SequencingBased } return nil } func (x *Config) GetTimeBased() *TimeBasedStrategy { if x, ok := x.GetReplenishmentStrategy().(*Config_TimeBased); ok { return x.TimeBased } return nil } func (x *Config) GetCurrentTokens() int64 { if x != nil { return x.CurrentTokens } return 0 } type isConfig_ReplenishmentStrategy interface { isConfig_ReplenishmentStrategy() } type Config_SequencingBased struct { // Sequencing-based replenishment settings. SequencingBased *SequencingBasedStrategy `protobuf:"bytes,4,opt,name=sequencing_based,json=sequencingBased,proto3,oneof"` } type Config_TimeBased struct { // Time-based replenishment settings. TimeBased *TimeBasedStrategy `protobuf:"bytes,5,opt,name=time_based,json=timeBased,proto3,oneof"` } func (*Config_SequencingBased) isConfig_ReplenishmentStrategy() {} func (*Config_TimeBased) isConfig_ReplenishmentStrategy() {} // Sequencing-based replenishment strategy settings. // // Only global/write and trees/write quotas may use sequencing-based // replenishment. type SequencingBasedStrategy struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *SequencingBasedStrategy) Reset() { *x = SequencingBasedStrategy{} if protoimpl.UnsafeEnabled { mi := &file_quotapb_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *SequencingBasedStrategy) String() string { return protoimpl.X.MessageStringOf(x) } func (*SequencingBasedStrategy) ProtoMessage() {} func (x *SequencingBasedStrategy) ProtoReflect() protoreflect.Message { mi := &file_quotapb_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SequencingBasedStrategy.ProtoReflect.Descriptor instead. func (*SequencingBasedStrategy) Descriptor() ([]byte, []int) { return file_quotapb_proto_rawDescGZIP(), []int{1} } // Time-based replenishment strategy settings. type TimeBasedStrategy struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Number of tokens to replenish at every replenish_interval_seconds. TokensToReplenish int64 `protobuf:"varint,1,opt,name=tokens_to_replenish,json=tokensToReplenish,proto3" json:"tokens_to_replenish,omitempty"` // Interval at which tokens_to_replenish get replenished. ReplenishIntervalSeconds int64 `protobuf:"varint,2,opt,name=replenish_interval_seconds,json=replenishIntervalSeconds,proto3" json:"replenish_interval_seconds,omitempty"` } func (x *TimeBasedStrategy) Reset() { *x = TimeBasedStrategy{} if protoimpl.UnsafeEnabled { mi := &file_quotapb_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *TimeBasedStrategy) String() string { return protoimpl.X.MessageStringOf(x) } func (*TimeBasedStrategy) ProtoMessage() {} func (x *TimeBasedStrategy) ProtoReflect() protoreflect.Message { mi := &file_quotapb_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use TimeBasedStrategy.ProtoReflect.Descriptor instead. func (*TimeBasedStrategy) Descriptor() ([]byte, []int) { return file_quotapb_proto_rawDescGZIP(), []int{2} } func (x *TimeBasedStrategy) GetTokensToReplenish() int64 { if x != nil { return x.TokensToReplenish } return 0 } func (x *TimeBasedStrategy) GetReplenishIntervalSeconds() int64 { if x != nil { return x.ReplenishIntervalSeconds } return 0 } // CreateConfig request. type CreateConfigRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Name of the config to create. // For example, "quotas/global/read/config" (global/read quota) or // "quotas/trees/1234/write/config" (write quota for tree 1234). Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // Config to be created. Config *Config `protobuf:"bytes,2,opt,name=config,proto3" json:"config,omitempty"` } func (x *CreateConfigRequest) Reset() { *x = CreateConfigRequest{} if protoimpl.UnsafeEnabled { mi := &file_quotapb_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *CreateConfigRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*CreateConfigRequest) ProtoMessage() {} func (x *CreateConfigRequest) ProtoReflect() protoreflect.Message { mi := &file_quotapb_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use CreateConfigRequest.ProtoReflect.Descriptor instead. func (*CreateConfigRequest) Descriptor() ([]byte, []int) { return file_quotapb_proto_rawDescGZIP(), []int{3} } func (x *CreateConfigRequest) GetName() string { if x != nil { return x.Name } return "" } func (x *CreateConfigRequest) GetConfig() *Config { if x != nil { return x.Config } return nil } // DeleteConfig request. type DeleteConfigRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Name of the config to delete. Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` } func (x *DeleteConfigRequest) Reset() { *x = DeleteConfigRequest{} if protoimpl.UnsafeEnabled { mi := &file_quotapb_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *DeleteConfigRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*DeleteConfigRequest) ProtoMessage() {} func (x *DeleteConfigRequest) ProtoReflect() protoreflect.Message { mi := &file_quotapb_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DeleteConfigRequest.ProtoReflect.Descriptor instead. func (*DeleteConfigRequest) Descriptor() ([]byte, []int) { return file_quotapb_proto_rawDescGZIP(), []int{4} } func (x *DeleteConfigRequest) GetName() string { if x != nil { return x.Name } return "" } // GetConfig request. type GetConfigRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Name of the config to retrieve. // For example, "quotas/global/read/config". Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` } func (x *GetConfigRequest) Reset() { *x = GetConfigRequest{} if protoimpl.UnsafeEnabled { mi := &file_quotapb_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *GetConfigRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*GetConfigRequest) ProtoMessage() {} func (x *GetConfigRequest) ProtoReflect() protoreflect.Message { mi := &file_quotapb_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GetConfigRequest.ProtoReflect.Descriptor instead. func (*GetConfigRequest) Descriptor() ([]byte, []int) { return file_quotapb_proto_rawDescGZIP(), []int{5} } func (x *GetConfigRequest) GetName() string { if x != nil { return x.Name } return "" } // ListConfig request. type ListConfigsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Names of the config to retrieve. For example, "quotas/global/read/config". // If empty, all configs are listed. // Name components may be substituted by "-" to search for all variations of // that component. For example: // - "quotas/global/-/config" (both read and write global quotas) // - "quotas/trees/-/-/config" (all tree quotas) Names []string `protobuf:"bytes,1,rep,name=names,proto3" json:"names,omitempty"` // View specifies how much data to return. View ListConfigsRequest_ListView `protobuf:"varint,2,opt,name=view,proto3,enum=quotapb.ListConfigsRequest_ListView" json:"view,omitempty"` } func (x *ListConfigsRequest) Reset() { *x = ListConfigsRequest{} if protoimpl.UnsafeEnabled { mi := &file_quotapb_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ListConfigsRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*ListConfigsRequest) ProtoMessage() {} func (x *ListConfigsRequest) ProtoReflect() protoreflect.Message { mi := &file_quotapb_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ListConfigsRequest.ProtoReflect.Descriptor instead. func (*ListConfigsRequest) Descriptor() ([]byte, []int) { return file_quotapb_proto_rawDescGZIP(), []int{6} } func (x *ListConfigsRequest) GetNames() []string { if x != nil { return x.Names } return nil } func (x *ListConfigsRequest) GetView() ListConfigsRequest_ListView { if x != nil { return x.View } return ListConfigsRequest_BASIC } // ListConfig response. type ListConfigsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Configs matching the request filter. Configs []*Config `protobuf:"bytes,1,rep,name=configs,proto3" json:"configs,omitempty"` } func (x *ListConfigsResponse) Reset() { *x = ListConfigsResponse{} if protoimpl.UnsafeEnabled { mi := &file_quotapb_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ListConfigsResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*ListConfigsResponse) ProtoMessage() {} func (x *ListConfigsResponse) ProtoReflect() protoreflect.Message { mi := &file_quotapb_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ListConfigsResponse.ProtoReflect.Descriptor instead. func (*ListConfigsResponse) Descriptor() ([]byte, []int) { return file_quotapb_proto_rawDescGZIP(), []int{7} } func (x *ListConfigsResponse) GetConfigs() []*Config { if x != nil { return x.Configs } return nil } // Updates a quota config according to the update_mask provided. // // Some config changes will cause the current number of tokens to be updated, as // listed below: // // - If max_tokens is reduced and the current number of tokens is greater than // the new max_tokens, the current number of tokens is reduced to max_tokens. // This happens so the quota is immediately conformant to the new // configuration. // // - A state transition from disabled to enabled causes the quota to be fully // replenished. This happens so the re-enabled quota will enter in action in a // known, predictable state. // // A "full replenish", also called "reset", may be forced via the reset_quota // parameter, regardless of any other changes. For convenience, reset only // requests (name and reset_quota = true) are allowed. type UpdateConfigRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Name of the config to update. Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // Config to update. Only the fields specified by update_mask need to be // filled. Config *Config `protobuf:"bytes,2,opt,name=config,proto3" json:"config,omitempty"` // Fields modified by the update request. // For example: "state" or "max_tokens". UpdateMask *fieldmaskpb.FieldMask `protobuf:"bytes,3,opt,name=update_mask,json=updateMask,proto3" json:"update_mask,omitempty"` // If true the updated quota is reset, regardless of the update's contents. // A reset quota is replenished to its maximum number of tokens. ResetQuota bool `protobuf:"varint,4,opt,name=reset_quota,json=resetQuota,proto3" json:"reset_quota,omitempty"` } func (x *UpdateConfigRequest) Reset() { *x = UpdateConfigRequest{} if protoimpl.UnsafeEnabled { mi := &file_quotapb_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *UpdateConfigRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*UpdateConfigRequest) ProtoMessage() {} func (x *UpdateConfigRequest) ProtoReflect() protoreflect.Message { mi := &file_quotapb_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use UpdateConfigRequest.ProtoReflect.Descriptor instead. func (*UpdateConfigRequest) Descriptor() ([]byte, []int) { return file_quotapb_proto_rawDescGZIP(), []int{8} } func (x *UpdateConfigRequest) GetName() string { if x != nil { return x.Name } return "" } func (x *UpdateConfigRequest) GetConfig() *Config { if x != nil { return x.Config } return nil } func (x *UpdateConfigRequest) GetUpdateMask() *fieldmaskpb.FieldMask { if x != nil { return x.UpdateMask } return nil } func (x *UpdateConfigRequest) GetResetQuota() bool { if x != nil { return x.ResetQuota } return false } var File_quotapb_proto protoreflect.FileDescriptor var file_quotapb_proto_rawDesc = []byte{ 0x0a, 0x0d, 0x71, 0x75, 0x6f, 0x74, 0x61, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x71, 0x75, 0x6f, 0x74, 0x61, 0x70, 0x62, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf3, 0x02, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x71, 0x75, 0x6f, 0x74, 0x61, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x78, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x6d, 0x61, 0x78, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x12, 0x4d, 0x0a, 0x10, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x71, 0x75, 0x6f, 0x74, 0x61, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x42, 0x61, 0x73, 0x65, 0x64, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x48, 0x00, 0x52, 0x0f, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x42, 0x61, 0x73, 0x65, 0x64, 0x12, 0x3b, 0x0a, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x71, 0x75, 0x6f, 0x74, 0x61, 0x70, 0x62, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x42, 0x61, 0x73, 0x65, 0x64, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x48, 0x00, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x42, 0x61, 0x73, 0x65, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x22, 0x3c, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x14, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x45, 0x4e, 0x41, 0x42, 0x4c, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x44, 0x49, 0x53, 0x41, 0x42, 0x4c, 0x45, 0x44, 0x10, 0x02, 0x42, 0x18, 0x0a, 0x16, 0x72, 0x65, 0x70, 0x6c, 0x65, 0x6e, 0x69, 0x73, 0x68, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x22, 0x19, 0x0a, 0x17, 0x53, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x42, 0x61, 0x73, 0x65, 0x64, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x22, 0x81, 0x01, 0x0a, 0x11, 0x54, 0x69, 0x6d, 0x65, 0x42, 0x61, 0x73, 0x65, 0x64, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x2e, 0x0a, 0x13, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x5f, 0x74, 0x6f, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x65, 0x6e, 0x69, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x54, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x65, 0x6e, 0x69, 0x73, 0x68, 0x12, 0x3c, 0x0a, 0x1a, 0x72, 0x65, 0x70, 0x6c, 0x65, 0x6e, 0x69, 0x73, 0x68, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x18, 0x72, 0x65, 0x70, 0x6c, 0x65, 0x6e, 0x69, 0x73, 0x68, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x22, 0x52, 0x0a, 0x13, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x27, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x71, 0x75, 0x6f, 0x74, 0x61, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x29, 0x0a, 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x26, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x85, 0x01, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x38, 0x0a, 0x04, 0x76, 0x69, 0x65, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x24, 0x2e, 0x71, 0x75, 0x6f, 0x74, 0x61, 0x70, 0x62, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x69, 0x65, 0x77, 0x52, 0x04, 0x76, 0x69, 0x65, 0x77, 0x22, 0x1f, 0x0a, 0x08, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x69, 0x65, 0x77, 0x12, 0x09, 0x0a, 0x05, 0x42, 0x41, 0x53, 0x49, 0x43, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x55, 0x4c, 0x4c, 0x10, 0x01, 0x22, 0x40, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x71, 0x75, 0x6f, 0x74, 0x61, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x22, 0xb0, 0x01, 0x0a, 0x13, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x27, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x71, 0x75, 0x6f, 0x74, 0x61, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x73, 0x6b, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x65, 0x74, 0x5f, 0x71, 0x75, 0x6f, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x65, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x32, 0x95, 0x04, 0x0a, 0x05, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x12, 0x6a, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1c, 0x2e, 0x71, 0x75, 0x6f, 0x74, 0x61, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0f, 0x2e, 0x71, 0x75, 0x6f, 0x74, 0x61, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x3a, 0x01, 0x2a, 0x22, 0x20, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x71, 0x75, 0x6f, 0x74, 0x61, 0x73, 0x2f, 0x2a, 0x2a, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x7d, 0x12, 0x6e, 0x0a, 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1c, 0x2e, 0x71, 0x75, 0x6f, 0x74, 0x61, 0x70, 0x62, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x28, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x2a, 0x20, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x71, 0x75, 0x6f, 0x74, 0x61, 0x73, 0x2f, 0x2a, 0x2a, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x7d, 0x12, 0x61, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x19, 0x2e, 0x71, 0x75, 0x6f, 0x74, 0x61, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0f, 0x2e, 0x71, 0x75, 0x6f, 0x74, 0x61, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x28, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x12, 0x20, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x71, 0x75, 0x6f, 0x74, 0x61, 0x73, 0x2f, 0x2a, 0x2a, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x7d, 0x12, 0x61, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x12, 0x1b, 0x2e, 0x71, 0x75, 0x6f, 0x74, 0x61, 0x70, 0x62, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x71, 0x75, 0x6f, 0x74, 0x61, 0x70, 0x62, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x12, 0x0f, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x71, 0x75, 0x6f, 0x74, 0x61, 0x73, 0x12, 0x6a, 0x0a, 0x0c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1c, 0x2e, 0x71, 0x75, 0x6f, 0x74, 0x61, 0x70, 0x62, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0f, 0x2e, 0x71, 0x75, 0x6f, 0x74, 0x61, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x3a, 0x01, 0x2a, 0x32, 0x20, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x71, 0x75, 0x6f, 0x74, 0x61, 0x73, 0x2f, 0x2a, 0x2a, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x7d, 0x42, 0x2f, 0x5a, 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2f, 0x71, 0x75, 0x6f, 0x74, 0x61, 0x2f, 0x65, 0x74, 0x63, 0x64, 0x2f, 0x71, 0x75, 0x6f, 0x74, 0x61, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( file_quotapb_proto_rawDescOnce sync.Once file_quotapb_proto_rawDescData = file_quotapb_proto_rawDesc ) func file_quotapb_proto_rawDescGZIP() []byte { file_quotapb_proto_rawDescOnce.Do(func() { file_quotapb_proto_rawDescData = protoimpl.X.CompressGZIP(file_quotapb_proto_rawDescData) }) return file_quotapb_proto_rawDescData } var file_quotapb_proto_enumTypes = make([]protoimpl.EnumInfo, 2) var file_quotapb_proto_msgTypes = make([]protoimpl.MessageInfo, 9) var file_quotapb_proto_goTypes = []any{ (Config_State)(0), // 0: quotapb.Config.State (ListConfigsRequest_ListView)(0), // 1: quotapb.ListConfigsRequest.ListView (*Config)(nil), // 2: quotapb.Config (*SequencingBasedStrategy)(nil), // 3: quotapb.SequencingBasedStrategy (*TimeBasedStrategy)(nil), // 4: quotapb.TimeBasedStrategy (*CreateConfigRequest)(nil), // 5: quotapb.CreateConfigRequest (*DeleteConfigRequest)(nil), // 6: quotapb.DeleteConfigRequest (*GetConfigRequest)(nil), // 7: quotapb.GetConfigRequest (*ListConfigsRequest)(nil), // 8: quotapb.ListConfigsRequest (*ListConfigsResponse)(nil), // 9: quotapb.ListConfigsResponse (*UpdateConfigRequest)(nil), // 10: quotapb.UpdateConfigRequest (*fieldmaskpb.FieldMask)(nil), // 11: google.protobuf.FieldMask (*emptypb.Empty)(nil), // 12: google.protobuf.Empty } var file_quotapb_proto_depIdxs = []int32{ 0, // 0: quotapb.Config.state:type_name -> quotapb.Config.State 3, // 1: quotapb.Config.sequencing_based:type_name -> quotapb.SequencingBasedStrategy 4, // 2: quotapb.Config.time_based:type_name -> quotapb.TimeBasedStrategy 2, // 3: quotapb.CreateConfigRequest.config:type_name -> quotapb.Config 1, // 4: quotapb.ListConfigsRequest.view:type_name -> quotapb.ListConfigsRequest.ListView 2, // 5: quotapb.ListConfigsResponse.configs:type_name -> quotapb.Config 2, // 6: quotapb.UpdateConfigRequest.config:type_name -> quotapb.Config 11, // 7: quotapb.UpdateConfigRequest.update_mask:type_name -> google.protobuf.FieldMask 5, // 8: quotapb.Quota.CreateConfig:input_type -> quotapb.CreateConfigRequest 6, // 9: quotapb.Quota.DeleteConfig:input_type -> quotapb.DeleteConfigRequest 7, // 10: quotapb.Quota.GetConfig:input_type -> quotapb.GetConfigRequest 8, // 11: quotapb.Quota.ListConfigs:input_type -> quotapb.ListConfigsRequest 10, // 12: quotapb.Quota.UpdateConfig:input_type -> quotapb.UpdateConfigRequest 2, // 13: quotapb.Quota.CreateConfig:output_type -> quotapb.Config 12, // 14: quotapb.Quota.DeleteConfig:output_type -> google.protobuf.Empty 2, // 15: quotapb.Quota.GetConfig:output_type -> quotapb.Config 9, // 16: quotapb.Quota.ListConfigs:output_type -> quotapb.ListConfigsResponse 2, // 17: quotapb.Quota.UpdateConfig:output_type -> quotapb.Config 13, // [13:18] is the sub-list for method output_type 8, // [8:13] is the sub-list for method input_type 8, // [8:8] is the sub-list for extension type_name 8, // [8:8] is the sub-list for extension extendee 0, // [0:8] is the sub-list for field type_name } func init() { file_quotapb_proto_init() } func file_quotapb_proto_init() { if File_quotapb_proto != nil { return } if !protoimpl.UnsafeEnabled { file_quotapb_proto_msgTypes[0].Exporter = func(v any, i int) any { switch v := v.(*Config); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_quotapb_proto_msgTypes[1].Exporter = func(v any, i int) any { switch v := v.(*SequencingBasedStrategy); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_quotapb_proto_msgTypes[2].Exporter = func(v any, i int) any { switch v := v.(*TimeBasedStrategy); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_quotapb_proto_msgTypes[3].Exporter = func(v any, i int) any { switch v := v.(*CreateConfigRequest); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_quotapb_proto_msgTypes[4].Exporter = func(v any, i int) any { switch v := v.(*DeleteConfigRequest); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_quotapb_proto_msgTypes[5].Exporter = func(v any, i int) any { switch v := v.(*GetConfigRequest); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_quotapb_proto_msgTypes[6].Exporter = func(v any, i int) any { switch v := v.(*ListConfigsRequest); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_quotapb_proto_msgTypes[7].Exporter = func(v any, i int) any { switch v := v.(*ListConfigsResponse); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_quotapb_proto_msgTypes[8].Exporter = func(v any, i int) any { switch v := v.(*UpdateConfigRequest); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } } file_quotapb_proto_msgTypes[0].OneofWrappers = []any{ (*Config_SequencingBased)(nil), (*Config_TimeBased)(nil), } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_quotapb_proto_rawDesc, NumEnums: 2, NumMessages: 9, NumExtensions: 0, NumServices: 1, }, GoTypes: file_quotapb_proto_goTypes, DependencyIndexes: file_quotapb_proto_depIdxs, EnumInfos: file_quotapb_proto_enumTypes, MessageInfos: file_quotapb_proto_msgTypes, }.Build() File_quotapb_proto = out.File file_quotapb_proto_rawDesc = nil file_quotapb_proto_goTypes = nil file_quotapb_proto_depIdxs = nil } trillian-1.6.1/quota/etcd/quotapb/quotapb.proto000066400000000000000000000155711466362047600216470ustar00rootroot00000000000000// 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. syntax = "proto3"; option go_package = "github.com/google/trillian/quota/etcd/quotapb"; package quotapb; import "google/api/annotations.proto"; import "google/protobuf/empty.proto"; import "google/protobuf/field_mask.proto"; // Configuration of a quota. // // Quotas contain a certain number of tokens that get applied to their // corresponding entities. Global quotas apply to all operations, tree and user // quotas to certain trees and users, respectively. // // Performing an operation costs a certain number of tokens (usually one). Once // a quota has no more tokens available, requests that would subtract from it // are denied with a resource_exhausted error. // // Tokens may be replenished in two different ways: either by passage of time or // sequencing progress. Time-based replenishment adds a fixed amount of tokens // after a certain interval. Sequencing-based adds a token for each leaf // processed by the sequencer. Sequencing-based replenishment may only be used // with global and tree quotas. // // A quota may be disabled or removed at any time. The effect is the same: a // disabled or non-existing quota is considered infinite by the quota system. // (Disabling is handy if you plan to re-enable a quota later on.) message Config { // Possible states of a quota configuration. enum State { // Unknown quota state. Invalid. UNKNOWN_CONFIG_STATE = 0; // Quota is enabled. ENABLED = 1; // Quota is disabled (considered infinite). DISABLED = 2; } // Name of the config, eg, “quotas/trees/1234/read/config”. // Readonly. string name = 1; // State of the config. State state = 2; // Max number of tokens available for the config. int64 max_tokens = 3; // Replenishment strategy used by the config. oneof replenishment_strategy { // Sequencing-based replenishment settings. SequencingBasedStrategy sequencing_based = 4; // Time-based replenishment settings. TimeBasedStrategy time_based = 5; } // Current number of tokens available for the config. // May be higher than max_tokens for DISABLED configs, which are considered to // have "infinite" tokens. // Readonly. int64 current_tokens = 6; } // Sequencing-based replenishment strategy settings. // // Only global/write and trees/write quotas may use sequencing-based // replenishment. message SequencingBasedStrategy {} // Time-based replenishment strategy settings. message TimeBasedStrategy { // Number of tokens to replenish at every replenish_interval_seconds. int64 tokens_to_replenish = 1; // Interval at which tokens_to_replenish get replenished. int64 replenish_interval_seconds = 2; } // CreateConfig request. message CreateConfigRequest { // Name of the config to create. // For example, "quotas/global/read/config" (global/read quota) or // "quotas/trees/1234/write/config" (write quota for tree 1234). string name = 1; // Config to be created. Config config = 2; } // DeleteConfig request. message DeleteConfigRequest { // Name of the config to delete. string name = 1; } // GetConfig request. message GetConfigRequest { // Name of the config to retrieve. // For example, "quotas/global/read/config". string name = 1; } // ListConfig request. message ListConfigsRequest { // Possible views for ListConfig. enum ListView { // Only the Config name gets returned. BASIC = 0; // Complete Config. FULL = 1; } // Names of the config to retrieve. For example, "quotas/global/read/config". // If empty, all configs are listed. // Name components may be substituted by "-" to search for all variations of // that component. For example: // - "quotas/global/-/config" (both read and write global quotas) // - "quotas/trees/-/-/config" (all tree quotas) repeated string names = 1; // View specifies how much data to return. ListView view = 2; } // ListConfig response. message ListConfigsResponse { // Configs matching the request filter. repeated Config configs = 1; } // Updates a quota config according to the update_mask provided. // // Some config changes will cause the current number of tokens to be updated, as // listed below: // // * If max_tokens is reduced and the current number of tokens is greater than // the new max_tokens, the current number of tokens is reduced to max_tokens. // This happens so the quota is immediately conformant to the new // configuration. // // * A state transition from disabled to enabled causes the quota to be fully // replenished. This happens so the re-enabled quota will enter in action in a // known, predictable state. // // A "full replenish", also called "reset", may be forced via the reset_quota // parameter, regardless of any other changes. For convenience, reset only // requests (name and reset_quota = true) are allowed. message UpdateConfigRequest { // Name of the config to update. string name = 1; // Config to update. Only the fields specified by update_mask need to be // filled. Config config = 2; // Fields modified by the update request. // For example: "state" or "max_tokens". google.protobuf.FieldMask update_mask = 3; // If true the updated quota is reset, regardless of the update's contents. // A reset quota is replenished to its maximum number of tokens. bool reset_quota = 4; } // Quota administrative interface. // // Allows creation and management of quotas. service Quota { // Creates a new quota. rpc CreateConfig(CreateConfigRequest) returns (Config) { option (google.api.http) = { post: "/v1beta1/{name=quotas/**/config}" body: "*" }; } // Deletes an existing quota. Non-existing quotas are considered infinite by // the quota system. rpc DeleteConfig(DeleteConfigRequest) returns (google.protobuf.Empty) { option (google.api.http).delete = "/v1beta1/{name=quotas/**/config}"; } // Retrieves a quota by name. rpc GetConfig(GetConfigRequest) returns (Config) { option (google.api.http).get = "/v1beta1/{name=quotas/**/config}"; } // Lists quotas according to the specified criteria. rpc ListConfigs(ListConfigsRequest) returns (ListConfigsResponse) { option (google.api.http).get = "/v1beta1/quotas"; } // Updates a quota. rpc UpdateConfig(UpdateConfigRequest) returns (Config) { option (google.api.http) = { patch: "/v1beta1/{name=quotas/**/config}" body: "*" }; } } trillian-1.6.1/quota/etcd/quotapb/quotapb_grpc.pb.go000066400000000000000000000261721466362047600225230ustar00rootroot00000000000000// 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. // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.5.1 // - protoc v3.20.1 // source: quotapb.proto package quotapb import ( context "context" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" emptypb "google.golang.org/protobuf/types/known/emptypb" ) // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. // Requires gRPC-Go v1.64.0 or later. const _ = grpc.SupportPackageIsVersion9 const ( Quota_CreateConfig_FullMethodName = "/quotapb.Quota/CreateConfig" Quota_DeleteConfig_FullMethodName = "/quotapb.Quota/DeleteConfig" Quota_GetConfig_FullMethodName = "/quotapb.Quota/GetConfig" Quota_ListConfigs_FullMethodName = "/quotapb.Quota/ListConfigs" Quota_UpdateConfig_FullMethodName = "/quotapb.Quota/UpdateConfig" ) // QuotaClient is the client API for Quota service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. // // Quota administrative interface. // // Allows creation and management of quotas. type QuotaClient interface { // Creates a new quota. CreateConfig(ctx context.Context, in *CreateConfigRequest, opts ...grpc.CallOption) (*Config, error) // Deletes an existing quota. Non-existing quotas are considered infinite by // the quota system. DeleteConfig(ctx context.Context, in *DeleteConfigRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) // Retrieves a quota by name. GetConfig(ctx context.Context, in *GetConfigRequest, opts ...grpc.CallOption) (*Config, error) // Lists quotas according to the specified criteria. ListConfigs(ctx context.Context, in *ListConfigsRequest, opts ...grpc.CallOption) (*ListConfigsResponse, error) // Updates a quota. UpdateConfig(ctx context.Context, in *UpdateConfigRequest, opts ...grpc.CallOption) (*Config, error) } type quotaClient struct { cc grpc.ClientConnInterface } func NewQuotaClient(cc grpc.ClientConnInterface) QuotaClient { return "aClient{cc} } func (c *quotaClient) CreateConfig(ctx context.Context, in *CreateConfigRequest, opts ...grpc.CallOption) (*Config, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(Config) err := c.cc.Invoke(ctx, Quota_CreateConfig_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *quotaClient) DeleteConfig(ctx context.Context, in *DeleteConfigRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) err := c.cc.Invoke(ctx, Quota_DeleteConfig_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *quotaClient) GetConfig(ctx context.Context, in *GetConfigRequest, opts ...grpc.CallOption) (*Config, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(Config) err := c.cc.Invoke(ctx, Quota_GetConfig_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *quotaClient) ListConfigs(ctx context.Context, in *ListConfigsRequest, opts ...grpc.CallOption) (*ListConfigsResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ListConfigsResponse) err := c.cc.Invoke(ctx, Quota_ListConfigs_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *quotaClient) UpdateConfig(ctx context.Context, in *UpdateConfigRequest, opts ...grpc.CallOption) (*Config, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(Config) err := c.cc.Invoke(ctx, Quota_UpdateConfig_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } // QuotaServer is the server API for Quota service. // All implementations should embed UnimplementedQuotaServer // for forward compatibility. // // Quota administrative interface. // // Allows creation and management of quotas. type QuotaServer interface { // Creates a new quota. CreateConfig(context.Context, *CreateConfigRequest) (*Config, error) // Deletes an existing quota. Non-existing quotas are considered infinite by // the quota system. DeleteConfig(context.Context, *DeleteConfigRequest) (*emptypb.Empty, error) // Retrieves a quota by name. GetConfig(context.Context, *GetConfigRequest) (*Config, error) // Lists quotas according to the specified criteria. ListConfigs(context.Context, *ListConfigsRequest) (*ListConfigsResponse, error) // Updates a quota. UpdateConfig(context.Context, *UpdateConfigRequest) (*Config, error) } // UnimplementedQuotaServer should be embedded to have // forward compatible implementations. // // NOTE: this should be embedded by value instead of pointer to avoid a nil // pointer dereference when methods are called. type UnimplementedQuotaServer struct{} func (UnimplementedQuotaServer) CreateConfig(context.Context, *CreateConfigRequest) (*Config, error) { return nil, status.Errorf(codes.Unimplemented, "method CreateConfig not implemented") } func (UnimplementedQuotaServer) DeleteConfig(context.Context, *DeleteConfigRequest) (*emptypb.Empty, error) { return nil, status.Errorf(codes.Unimplemented, "method DeleteConfig not implemented") } func (UnimplementedQuotaServer) GetConfig(context.Context, *GetConfigRequest) (*Config, error) { return nil, status.Errorf(codes.Unimplemented, "method GetConfig not implemented") } func (UnimplementedQuotaServer) ListConfigs(context.Context, *ListConfigsRequest) (*ListConfigsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ListConfigs not implemented") } func (UnimplementedQuotaServer) UpdateConfig(context.Context, *UpdateConfigRequest) (*Config, error) { return nil, status.Errorf(codes.Unimplemented, "method UpdateConfig not implemented") } func (UnimplementedQuotaServer) testEmbeddedByValue() {} // UnsafeQuotaServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to QuotaServer will // result in compilation errors. type UnsafeQuotaServer interface { mustEmbedUnimplementedQuotaServer() } func RegisterQuotaServer(s grpc.ServiceRegistrar, srv QuotaServer) { // If the following call pancis, it indicates UnimplementedQuotaServer was // embedded by pointer and is nil. This will cause panics if an // unimplemented method is ever invoked, so we test this at initialization // time to prevent it from happening at runtime later due to I/O. if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { t.testEmbeddedByValue() } s.RegisterService(&Quota_ServiceDesc, srv) } func _Quota_CreateConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(CreateConfigRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(QuotaServer).CreateConfig(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: Quota_CreateConfig_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(QuotaServer).CreateConfig(ctx, req.(*CreateConfigRequest)) } return interceptor(ctx, in, info, handler) } func _Quota_DeleteConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(DeleteConfigRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(QuotaServer).DeleteConfig(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: Quota_DeleteConfig_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(QuotaServer).DeleteConfig(ctx, req.(*DeleteConfigRequest)) } return interceptor(ctx, in, info, handler) } func _Quota_GetConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(GetConfigRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(QuotaServer).GetConfig(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: Quota_GetConfig_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(QuotaServer).GetConfig(ctx, req.(*GetConfigRequest)) } return interceptor(ctx, in, info, handler) } func _Quota_ListConfigs_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ListConfigsRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(QuotaServer).ListConfigs(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: Quota_ListConfigs_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(QuotaServer).ListConfigs(ctx, req.(*ListConfigsRequest)) } return interceptor(ctx, in, info, handler) } func _Quota_UpdateConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(UpdateConfigRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(QuotaServer).UpdateConfig(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: Quota_UpdateConfig_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(QuotaServer).UpdateConfig(ctx, req.(*UpdateConfigRequest)) } return interceptor(ctx, in, info, handler) } // Quota_ServiceDesc is the grpc.ServiceDesc for Quota service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var Quota_ServiceDesc = grpc.ServiceDesc{ ServiceName: "quotapb.Quota", HandlerType: (*QuotaServer)(nil), Methods: []grpc.MethodDesc{ { MethodName: "CreateConfig", Handler: _Quota_CreateConfig_Handler, }, { MethodName: "DeleteConfig", Handler: _Quota_DeleteConfig_Handler, }, { MethodName: "GetConfig", Handler: _Quota_GetConfig_Handler, }, { MethodName: "ListConfigs", Handler: _Quota_ListConfigs_Handler, }, { MethodName: "UpdateConfig", Handler: _Quota_UpdateConfig_Handler, }, }, Streams: []grpc.StreamDesc{}, Metadata: "quotapb.proto", } trillian-1.6.1/quota/etcd/storage/000077500000000000000000000000001466362047600170675ustar00rootroot00000000000000trillian-1.6.1/quota/etcd/storage/quota_storage.go000066400000000000000000000330361466362047600223000ustar00rootroot00000000000000// 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 storage contains storage classes for etcd-based quotas. package storage import ( "context" "fmt" "regexp" "strconv" "strings" "time" "github.com/google/trillian/quota" "github.com/google/trillian/quota/etcd/storagepb" "github.com/google/trillian/util/clock" "go.etcd.io/etcd/client/v3/concurrency" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/proto" "k8s.io/klog/v2" clientv3 "go.etcd.io/etcd/client/v3" ) const ( configsKey = "quotas/configs" ) var ( timeSource = clock.System globalPattern *regexp.Regexp treesPattern *regexp.Regexp usersPattern *regexp.Regexp ) func init() { var err error globalPattern, err = regexp.Compile("^quotas/global/(read|write)/config$") if err != nil { klog.Fatalf("bad global pattern: %v", err) } treesPattern, err = regexp.Compile(`^quotas/trees/\d+/(read|write)/config$`) if err != nil { klog.Fatalf("bad trees pattern: %v", err) } usersPattern, err = regexp.Compile("^quotas/users/[^/]+/(read|write)/config$") if err != nil { klog.Fatalf("bad users pattern: %v", err) } } // IsNameValid returns true if name is a valid quota name. func IsNameValid(name string) bool { switch { case globalPattern.MatchString(name): return true case usersPattern.MatchString(name): return true case treesPattern.MatchString(name): // Tree ID must fit on an int64 id := strings.Split(name, "/")[2] _, err := strconv.ParseInt(id, 10, 64) return err == nil } return false } // QuotaStorage is the interface between the etcd-based quota implementations (quota.Manager and // RPCs) and etcd itself. type QuotaStorage struct { Client *clientv3.Client } // UpdateConfigs creates or updates the supplied configs in etcd. // If no config exists, the current config is assumed to be an empty storagepb.Configs proto. // The update function allows for mask-based updates and ensures a single-transaction // read-modify-write operation. // If reset is true, all specified configs will be set to their max number of tokens. If false, // existing quotas won't be modified, unless the max number of tokens is lowered, in which case // the new ceiling is enforced. // Newly created quotas are always set to max tokens, regardless of the reset parameter. func (qs *QuotaStorage) UpdateConfigs(ctx context.Context, reset bool, update func(*storagepb.Configs)) (*storagepb.Configs, error) { if update == nil { return nil, status.Error(codes.Internal, "update function required") } var updated *storagepb.Configs _, err := concurrency.NewSTMSerializable(ctx, qs.Client, func(s concurrency.STM) error { previous, err := getConfigs(s) if err != nil { return err } // Take a deep copy of "previous". It's pointers all the way down, so it's easier to just // unmarshal it again. STM has the key we just read and it should be exactly the same as // previous... updated, err = getConfigs(s) if err != nil { return err } // ... but let's sanity check that the configs match, just in case. if !proto.Equal(previous, updated) { return status.Error(codes.Internal, "verification failed: previous quota config != updated") } update(updated) if err := validate(updated); err != nil { return err } if !proto.Equal(previous, updated) { pb, err := proto.Marshal(updated) if err != nil { return err } s.Put(configsKey, string(pb)) } now := timeSource.Now() for _, cfg := range updated.Configs { // Make no distinction between enabled and disabled configs here. Get/Peek/Put are // prepared to handle it, and recording the bucket as if it were enabled allows us to // take advantage of the already-existing reset and lowering logic. key := bucketKey(cfg) var prev *storagepb.Config for _, p := range previous.Configs { if cfg.Name == p.Name { prev = p break } } switch { case prev == nil || prev.State == storagepb.Config_DISABLED || reset: // new bucket bucket := &storagepb.Bucket{ Tokens: cfg.MaxTokens, LastReplenishMillisSinceEpoch: now.UnixNano() / 1e6, } pb, err := proto.Marshal(bucket) if err != nil { return err } s.Put(key, string(pb)) case cfg.MaxTokens < prev.MaxTokens: // lowered bucket // modBucket will coerce tokens to cfg.MaxTokens, if necessary if _, err := modBucket(s, cfg, now, 0 /* add */); err != nil { return err } } } return nil }) return updated, err } func validate(cfgs *storagepb.Configs) error { names := make(map[string]bool) for i, cfg := range cfgs.Configs { switch n := cfg.Name; { case n == "": return status.Errorf(codes.InvalidArgument, "config name is required (Configs[%v].Name is empty)", i) case !IsNameValid(cfg.Name): return status.Errorf(codes.InvalidArgument, "config name malformed (Configs[%v].Name = %q)", i, n) } if s := cfg.State; s == storagepb.Config_UNKNOWN_CONFIG_STATE { return status.Errorf(codes.InvalidArgument, "config state invalid (Configs[%v].State = %s)", i, s) } if t := cfg.MaxTokens; t <= 0 { return status.Errorf(codes.InvalidArgument, "config max tokens must be > 0 (Configs[%v].MaxTokens = %v)", i, t) } switch s := cfg.ReplenishmentStrategy.(type) { case *storagepb.Config_SequencingBased: if usersPattern.MatchString(cfg.Name) { return status.Errorf(codes.InvalidArgument, "user quotas cannot use sequencing-based replenishment (Configs[%v].ReplenishmentStrategy)", i) } if strings.HasSuffix(cfg.Name, "/read/config") { return status.Errorf(codes.InvalidArgument, "read quotas cannot use sequencing-based replenishment (Configs[%v].ReplenishmentStrategy)", i) } case *storagepb.Config_TimeBased: if t := s.TimeBased.TokensToReplenish; t <= 0 { return status.Errorf(codes.InvalidArgument, "time based tokens must be > 0 (Configs[%v].TimeBased.TokensToReplenish = %v)", i, t) } if r := s.TimeBased.ReplenishIntervalSeconds; r <= 0 { return status.Errorf(codes.InvalidArgument, "time based replenish interval must be > 0 (Configs[%v].TimeBased.ReplenishIntervalSeconds = %v)", i, r) } default: return status.Errorf(codes.InvalidArgument, "unsupported replenishment strategy (Configs[%v].ReplenishmentStrategy = %T)", i, s) } if names[cfg.Name] { return status.Errorf(codes.InvalidArgument, "duplicate config name found at Configs[%v].Name", i) } names[cfg.Name] = true } return nil } // Configs returns the currently known quota configs. // If no config was explicitly created, then an empty storage.Configs proto is returned. func (qs *QuotaStorage) Configs(ctx context.Context) (*storagepb.Configs, error) { var cfgs *storagepb.Configs _, err := concurrency.NewSTMSerializable(ctx, qs.Client, func(s concurrency.STM) error { var err error cfgs, err = getConfigs(s) return err }) return cfgs, err } // Get acquires "tokens" tokens from the named quotas. // If one of the specified quotas doesn't have enough tokens, the entire operation fails. Unknown or // disabled quotas are considered infinite, therefore get requests will always succeed for them. func (qs *QuotaStorage) Get(ctx context.Context, names []string, tokens int64) error { if tokens < 0 { return fmt.Errorf("invalid number of tokens: %v", tokens) } return qs.mod(ctx, names, -tokens) } func (qs *QuotaStorage) mod(ctx context.Context, names []string, add int64) error { now := timeSource.Now() return qs.forNames(ctx, names, defaultMode, func(s concurrency.STM, name string, cfg *storagepb.Config) error { _, err := modBucket(s, cfg, now, add) return err }) } // Peek returns a map of quota name to tokens for the named quotas. // Unknown or disabled quotas are considered infinite and returned as having quota.MaxTokens tokens, // therefore all requested names are guaranteed to be in the resulting map func (qs *QuotaStorage) Peek(ctx context.Context, names []string) (map[string]int64, error) { now := timeSource.Now() tokens := make(map[string]int64) err := qs.forNames(ctx, names, emitInfinite, func(s concurrency.STM, name string, cfg *storagepb.Config) error { var err error var t int64 if cfg == nil { t = int64(quota.MaxTokens) } else { t, err = modBucket(s, cfg, now, 0 /* add */) } tokens[name] = t return err }) return tokens, err } // Put adds "tokens" tokens to the named quotas. // Time-based quotas cannot be replenished this way, therefore put requests for them are ignored. // Unknown or disabled quotas are considered infinite and also ignored. func (qs *QuotaStorage) Put(ctx context.Context, names []string, tokens int64) error { if tokens < 0 { return fmt.Errorf("invalid number of tokens: %v", tokens) } return qs.mod(ctx, names, tokens) } // Reset resets the named quotas to their maximum number of tokens. // Unknown or disabled quotas are considered infinite and ignored. func (qs *QuotaStorage) Reset(ctx context.Context, names []string) error { now := timeSource.Now() return qs.forNames(ctx, names, defaultMode, func(s concurrency.STM, name string, cfg *storagepb.Config) error { bucket := &storagepb.Bucket{ Tokens: cfg.MaxTokens, LastReplenishMillisSinceEpoch: now.UnixNano() / 1e6, } pb, err := proto.Marshal(bucket) if err != nil { return err } s.Put(bucketKey(cfg), string(pb)) return nil }) } // forNamesMode specifies how forNames handles disabled and infinite quotas. type forNamesMode int const ( // defaultMode emits only known, enabled configs. defaultMode forNamesMode = iota // emitInfinite emits all known, enabled configs and all infinite configs. // Infinite configs are emitted with a nil cfg value. // emitInfinite emits disabled configs with a nil cfg value as well. emitInfinite ) // forNames calls fn for all configs specified by names. Execution is performed in a single etcd // transaction. // By default, fn is only called for known, enabled configs. See forNamesMode for other behaviors. // Names are validated and de-duped automatically. func (qs *QuotaStorage) forNames(ctx context.Context, names []string, mode forNamesMode, fn func(concurrency.STM, string, *storagepb.Config) error) error { for _, name := range names { if !IsNameValid(name) { return fmt.Errorf("invalid name: %q", name) } } _, err := concurrency.NewSTMSerializable(ctx, qs.Client, func(s concurrency.STM) error { cfgs, err := getConfigs(s) if err != nil { return err } seenNames := make(map[string]bool) for _, name := range names { if seenNames[name] { continue } seenNames[name] = true emitted := false for _, cfg := range cfgs.Configs { if cfg.Name == name { if cfg.State == storagepb.Config_ENABLED { if err := fn(s, name, cfg); err != nil { return err } emitted = true } break } } if !emitted && mode == emitInfinite { if err := fn(s, name, nil); err != nil { return err } } } return nil }) return err } func getConfigs(s concurrency.STM) (*storagepb.Configs, error) { // TODO(codingllama): Consider watching configs instead of re-reading cfgs := &storagepb.Configs{} val := s.Get(configsKey) if val == "" { // Empty value means no config was explicitly created yet. // Use the default (empty) configs in this case. return cfgs, nil } if err := proto.Unmarshal([]byte(val), cfgs); err != nil { return nil, fmt.Errorf("error unmarshaling %v: %v", configsKey, err) } return cfgs, nil } // modBucket adds "add" tokens to the specified quota. Add may be negative or zero. // Time-based quotas that are due replenishment will be replenished before the add operation. Quotas // that are above ceiling (eg, due to lowered max tokens) will also be constrained to the // appropriate ceiling. As a consequence, calls with add = 0 are still useful for peeking and the // explained side-effects. // modBucket returns the current token count for cfg. func modBucket(s concurrency.STM, cfg *storagepb.Config, now time.Time, add int64) (int64, error) { key := bucketKey(cfg) val := s.Get(key) var prevBucket storagepb.Bucket if err := proto.Unmarshal([]byte(val), &prevBucket); err != nil { return 0, fmt.Errorf("error unmarshaling %v: %v", key, err) } newBucket := proto.Clone(&prevBucket).(*storagepb.Bucket) if tb := cfg.GetTimeBased(); tb != nil { if now.Unix() >= newBucket.LastReplenishMillisSinceEpoch/1e3+tb.ReplenishIntervalSeconds { newBucket.Tokens += tb.TokensToReplenish if newBucket.Tokens > cfg.MaxTokens { newBucket.Tokens = cfg.MaxTokens } newBucket.LastReplenishMillisSinceEpoch = now.UnixNano() / 1e6 } if add > 0 { add = 0 // Do not replenish time-based quotas } } newBucket.Tokens += add if newBucket.Tokens < 0 { return 0, fmt.Errorf("insufficient tokens on %v (%v vs %v)", key, prevBucket.Tokens, -add) } if newBucket.Tokens > cfg.MaxTokens { newBucket.Tokens = cfg.MaxTokens } if !proto.Equal(&prevBucket, newBucket) { pb, err := proto.Marshal(newBucket) if err != nil { return 0, err } s.Put(key, string(pb)) } return newBucket.Tokens, nil } func bucketKey(cfg *storagepb.Config) string { return fmt.Sprintf("%v/0", cfg.Name) } trillian-1.6.1/quota/etcd/storage/quota_storage_test.go000066400000000000000000000674131466362047600233450ustar00rootroot00000000000000// 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 storage import ( "context" "fmt" "os" "strings" "testing" "time" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/google/trillian/quota" "github.com/google/trillian/quota/etcd/storagepb" "github.com/google/trillian/testonly/integration/etcd" "github.com/google/trillian/util/clock" clientv3 "go.etcd.io/etcd/client/v3" "google.golang.org/protobuf/proto" ) const ( quotaMaxTokens = int64(quota.MaxTokens) ) var ( cfgs = &storagepb.Configs{ Configs: []*storagepb.Config{ { Name: "quotas/global/read/config", State: storagepb.Config_DISABLED, MaxTokens: 1, ReplenishmentStrategy: &storagepb.Config_TimeBased{ TimeBased: &storagepb.TimeBasedStrategy{ ReplenishIntervalSeconds: 100, TokensToReplenish: 10000, }, }, }, { Name: "quotas/global/write/config", State: storagepb.Config_ENABLED, MaxTokens: 100, ReplenishmentStrategy: &storagepb.Config_SequencingBased{ SequencingBased: &storagepb.SequencingBasedStrategy{}, }, }, { Name: "quotas/users/llama/read/config", State: storagepb.Config_ENABLED, MaxTokens: 1000, ReplenishmentStrategy: &storagepb.Config_TimeBased{ TimeBased: &storagepb.TimeBasedStrategy{ ReplenishIntervalSeconds: 50, TokensToReplenish: 500, }, }, }, }, } globalRead = cfgs.Configs[0] globalWrite = cfgs.Configs[1] userRead = cfgs.Configs[2] fixedTimeSource = clock.NewFake(time.Now()) // client is an etcd client. // Initialized by TestMain(). client *clientv3.Client ) func TestMain(m *testing.M) { _, c, cleanup, err := etcd.StartEtcd() if err != nil { panic(fmt.Sprintf("StartEtcd() returned err = %v", err)) } client = c exitCode := m.Run() cleanup() os.Exit(exitCode) } func TestIsNameValid(t *testing.T) { tests := []struct { name string want bool }{ {name: "quotas/global/read/config", want: true}, {name: "quotas/global/write/config", want: true}, {name: "quotas/trees/12356/read/config", want: true}, {name: "quotas/users/llama/write/config", want: true}, {name: "bad/quota/name"}, {name: "badprefix/quotas/global/read/config"}, {name: "quotas/global/read/config/badsuffix"}, {name: "quotas/bad/read/config"}, {name: "quotas/global/bad/config"}, {name: "quotas/trees/bad/read/config"}, {name: "quotas/trees/11111111111111111111/read/config"}, // ID > MaxInt64 } for _, test := range tests { if got := IsNameValid(test.name); got != test.want { t.Errorf("IsNameValid(%q) = %v, want = %v", test.name, got, test.want) } } } func TestQuotaStorage_UpdateConfigs(t *testing.T) { defer setupTimeSource(fixedTimeSource)() empty := &storagepb.Configs{} cfgs2 := deepCopy(cfgs) cfgs2.Configs = cfgs2.Configs[1:] // Remove global/read cfgs2.Configs[0].MaxTokens = 50 // decrease global/write cfgs2.Configs[1].MaxTokens = 10000 // increase user/read treeWriteName := "quotas/trees/12345/write/config" cfgs3 := deepCopy(cfgs) cfgs3.Configs = append(cfgs3.Configs, &storagepb.Config{ Name: treeWriteName, State: storagepb.Config_ENABLED, MaxTokens: 200, ReplenishmentStrategy: &storagepb.Config_SequencingBased{ SequencingBased: &storagepb.SequencingBasedStrategy{}, }, }) // Note: tests are incremental, not isolated. The preceding test will have impact on the // next, specially if reset is set to false. tests := []struct { desc string reset bool wantCfgs *storagepb.Configs wantTokens map[string]int64 }{ { desc: "empty", reset: true, wantCfgs: empty, }, { desc: "cfgs", wantCfgs: cfgs, wantTokens: map[string]int64{ globalRead.Name: quotaMaxTokens, // disabled globalWrite.Name: 100, userRead.Name: 1000, }, }, { desc: "cfgs2", wantCfgs: cfgs2, wantTokens: map[string]int64{ globalWrite.Name: 50, // correctly decreased userRead.Name: 1000, // unaltered }, }, { desc: "cfgs3", wantCfgs: cfgs3, wantTokens: map[string]int64{ globalWrite.Name: 50, // unaltered due to reset = false userRead.Name: 1000, // unaltered treeWriteName: 200, // new }, }, { desc: "cfgs3-pt2", reset: true, wantCfgs: cfgs3, wantTokens: map[string]int64{ globalWrite.Name: 100, // correctly reset userRead.Name: 1000, treeWriteName: 200, }, }, { desc: "cfgs-pt2", wantCfgs: cfgs, wantTokens: map[string]int64{ globalWrite.Name: 100, userRead.Name: 1000, treeWriteName: quotaMaxTokens, // deleted / infinite }, }, } ctx := context.Background() qs := &QuotaStorage{Client: client} for _, test := range tests { cfgs, err := qs.UpdateConfigs(ctx, test.reset, updater(test.wantCfgs)) if err != nil { t.Errorf("%v: UpdateConfigs() returned err = %v", test.desc, err) continue } if got, want := cfgs, test.wantCfgs; !proto.Equal(got, want) { diff := cmp.Diff(got, want) t.Errorf("%v: post-UpdateConfigs() diff (-got +want)\n%v", test.desc, diff) } stored, err := qs.Configs(ctx) if err != nil { t.Errorf("%v:Configs() returned err = %v", test.desc, err) continue } if got, want := stored, cfgs; !proto.Equal(got, want) { diff := cmp.Diff(got, want) t.Errorf("%v: post-Configs() diff (-got +want)\n%v", test.desc, diff) } if err := peekAndDiff(ctx, qs, test.wantTokens); err != nil { t.Errorf("%v: %v", test.desc, err) } } } func TestQuotaStorage_UpdateConfigsErrors(t *testing.T) { globalWriteCfgs := &storagepb.Configs{Configs: []*storagepb.Config{globalWrite}} emptyName := deepCopy(globalWriteCfgs) emptyName.Configs[0].Name = "" invalidName1 := deepCopy(globalWriteCfgs) invalidName1.Configs[0].Name = "invalid" invalidName2 := deepCopy(globalWriteCfgs) invalidName2.Configs[0].Name = "quotas/tree/1234/write" // should be "trees", plural unknownState := deepCopy(globalWriteCfgs) unknownState.Configs[0].State = storagepb.Config_UNKNOWN_CONFIG_STATE zeroMaxTokens := deepCopy(globalWriteCfgs) zeroMaxTokens.Configs[0].MaxTokens = 0 invalidMaxTokens := deepCopy(globalWriteCfgs) invalidMaxTokens.Configs[0].MaxTokens = -1 noReplenishmentStrategy := deepCopy(globalWriteCfgs) noReplenishmentStrategy.Configs[0].ReplenishmentStrategy = nil zeroTimeBasedTokens := deepCopy(globalWriteCfgs) zeroTimeBasedTokens.Configs[0].ReplenishmentStrategy = &storagepb.Config_TimeBased{ TimeBased: &storagepb.TimeBasedStrategy{ TokensToReplenish: 0, ReplenishIntervalSeconds: 10, }, } invalidTimeBasedTokens := deepCopy(globalWriteCfgs) invalidTimeBasedTokens.Configs[0].ReplenishmentStrategy = &storagepb.Config_TimeBased{ TimeBased: &storagepb.TimeBasedStrategy{ TokensToReplenish: -1, ReplenishIntervalSeconds: 10, }, } zeroReplenishInterval := deepCopy(globalWriteCfgs) zeroReplenishInterval.Configs[0].ReplenishmentStrategy = &storagepb.Config_TimeBased{ TimeBased: &storagepb.TimeBasedStrategy{ TokensToReplenish: 1, ReplenishIntervalSeconds: 0, }, } invalidReplenishInterval := deepCopy(globalWriteCfgs) invalidReplenishInterval.Configs[0].ReplenishmentStrategy = &storagepb.Config_TimeBased{ TimeBased: &storagepb.TimeBasedStrategy{ TokensToReplenish: 1, ReplenishIntervalSeconds: -1, }, } duplicateNames := &storagepb.Configs{Configs: []*storagepb.Config{globalRead, globalWrite, globalWrite}} sequencingBasedStrategy := &storagepb.Config_SequencingBased{SequencingBased: &storagepb.SequencingBasedStrategy{}} sequencingBasedUserQuota := &storagepb.Configs{ Configs: []*storagepb.Config{ { Name: userRead.Name, State: userRead.State, MaxTokens: userRead.MaxTokens, ReplenishmentStrategy: sequencingBasedStrategy, }, }, } sequencingBasedReadQuota1 := deepCopy(globalWriteCfgs) sequencingBasedReadQuota1.Configs[0].Name = globalRead.Name sequencingBasedReadQuota1.Configs[0].ReplenishmentStrategy = sequencingBasedStrategy sequencingBasedReadQuota2 := deepCopy(globalWriteCfgs) sequencingBasedReadQuota2.Configs[0].Name = "quotas/trees/1234/read/config" sequencingBasedReadQuota2.Configs[0].ReplenishmentStrategy = sequencingBasedStrategy tests := []struct { desc string update func(*storagepb.Configs) wantErr string }{ {desc: "nil", wantErr: "function required"}, { desc: "emptyName", update: updater(emptyName), wantErr: "name is required", }, { desc: "invalidName1", update: updater(invalidName1), wantErr: "name malformed", }, { desc: "invalidName2", update: updater(invalidName2), wantErr: "name malformed", }, { desc: "unknownState", update: updater(unknownState), wantErr: "state invalid", }, { desc: "zeroMaxTokens", update: updater(zeroMaxTokens), wantErr: "max tokens must be > 0", }, { desc: "invalidMaxTokens", update: updater(invalidMaxTokens), wantErr: "max tokens must be > 0", }, { desc: "noReplenishmentStrategy", update: updater(noReplenishmentStrategy), wantErr: "unsupported replenishment strategy", }, { desc: "zeroTimeBasedTokens", update: updater(zeroTimeBasedTokens), wantErr: "time based tokens must be > 0", }, { desc: "invalidTimeBasedTokens", update: updater(invalidTimeBasedTokens), wantErr: "time based tokens must be > 0", }, { desc: "zeroReplenishInterval", update: updater(zeroReplenishInterval), wantErr: "replenish interval must be > 0", }, { desc: "invalidReplenishInterval", update: updater(invalidReplenishInterval), wantErr: "replenish interval must be > 0", }, { desc: "duplicateNames", update: updater(duplicateNames), wantErr: "duplicate config name", }, { desc: "sequencingBasedUserQuota", update: updater(sequencingBasedUserQuota), wantErr: "cannot use sequencing-based replenishment", }, { desc: "sequencingBasedReadQuota1", update: updater(sequencingBasedReadQuota1), wantErr: "cannot use sequencing-based replenishment", }, { desc: "sequencingBasedReadQuota2", update: updater(sequencingBasedReadQuota2), wantErr: "cannot use sequencing-based replenishment", }, } ctx := context.Background() for _, test := range tests { qs := &QuotaStorage{Client: client} want := &storagepb.Configs{} // default cfgs is empty if _, err := qs.UpdateConfigs(ctx, true /* reset */, updater(want)); err != nil { t.Fatalf("UpdateConfigs()= (_,%q), want (_,nil)", err) } t.Run(test.desc, func(t *testing.T) { _, err := qs.UpdateConfigs(ctx, false /* reset */, test.update) // All the test cases should result in an error. if err == nil { t.Fatalf("UpdateConfigs()=_, %v, want: _, %v", err, test.wantErr) } if !strings.Contains(err.Error(), test.wantErr) { t.Fatalf("UpdateConfigs()=_,%v, want substring %q", err, test.wantErr) } stored, err := qs.Configs(ctx) if err != nil { t.Errorf("Configs()=_,%v want: _,nil", err) return } if got := stored; !proto.Equal(got, want) { diff := cmp.Diff(got, want) t.Fatalf("post-Configs() diff (-got +want)\n%v", diff) } }) } } func TestQuotaStorage_DeletedConfig(t *testing.T) { defer setupTimeSource(fixedTimeSource)() ctx := context.Background() qs := &QuotaStorage{Client: client} cfgs := deepCopy(cfgs) cfgs.Configs = cfgs.Configs[1:2] // Only global/write globalWrite := cfgs.Configs[0] if _, err := qs.UpdateConfigs(ctx, true /* reset */, updater(cfgs)); err != nil { t.Fatalf("UpdateConfigs() returned err = %v", err) } // Normal quota behavior names := []string{globalWrite.Name} _ = qs.Get(ctx, names, 100) if err := peekAndDiff(ctx, qs, map[string]int64{globalWrite.Name: globalWrite.MaxTokens - 100}); err != nil { t.Fatalf("peekAndDiff returned err = %v", err) } // Deleted: considered infinite cfgs = &storagepb.Configs{} if _, err := qs.UpdateConfigs(ctx, false /* reset */, updater(cfgs)); err != nil { t.Fatalf("UpdateConfigs() returned err = %v", err) } if err := peekAndDiff(ctx, qs, map[string]int64{globalWrite.Name: quotaMaxTokens}); err != nil { t.Fatalf("peekAndDiff returned err = %v", err) } // Restored: must behave as new (ie, doesn't "revive" the old token count) cfgs = &storagepb.Configs{Configs: []*storagepb.Config{globalWrite}} if _, err := qs.UpdateConfigs(ctx, false /* reset */, updater(cfgs)); err != nil { t.Fatalf("UpdateConfigs() returned err = %v", err) } if err := peekAndDiff(ctx, qs, map[string]int64{globalWrite.Name: globalWrite.MaxTokens}); err != nil { t.Fatalf("peekAndDiff returned err = %v", err) } } func TestQuotaStorage_DisabledConfig(t *testing.T) { defer setupTimeSource(fixedTimeSource)() ctx := context.Background() qs := &QuotaStorage{Client: client} cfgs := deepCopy(cfgs) cfgs.Configs = cfgs.Configs[0:1] // Only global/read globalRead := cfgs.Configs[0] globalRead.State = storagepb.Config_ENABLED globalRead.MaxTokens = 1000 if _, err := qs.UpdateConfigs(ctx, true /* reset */, updater(cfgs)); err != nil { t.Fatalf("UpdateConfigs() returned err = %v", err) } // Normal quota behavior names := []string{globalRead.Name} _ = qs.Get(ctx, names, 100) if err := peekAndDiff(ctx, qs, map[string]int64{globalRead.Name: globalRead.MaxTokens - 100}); err != nil { t.Fatalf("peekAndDiff returned err = %v", err) } // Disabled: cfg still exists, but is considered infinite globalRead.State = storagepb.Config_DISABLED if _, err := qs.UpdateConfigs(ctx, false /* reset */, updater(cfgs)); err != nil { t.Fatalf("UpdateConfigs() returned err = %v", err) } if err := peekAndDiff(ctx, qs, map[string]int64{globalRead.Name: quotaMaxTokens}); err != nil { t.Fatalf("peekAndDiff returned err = %v", err) } // Enabled: tokens restored to ceiling, even though reset = false globalRead.State = storagepb.Config_ENABLED if _, err := qs.UpdateConfigs(ctx, false /* reset */, updater(cfgs)); err != nil { t.Fatalf("UpdateConfigs() returned err = %v", err) } if err := peekAndDiff(ctx, qs, map[string]int64{globalRead.Name: globalRead.MaxTokens}); err != nil { t.Fatalf("peekAndDiff returned err = %v", err) } } func TestQuotaStorage_Get(t *testing.T) { fakeTime := clock.NewFake(time.Now()) setupTimeSource(fakeTime) tests := []struct { desc string names []string tokens int64 nowIncrement time.Duration initialTokens, wantTokens map[string]int64 }{ { desc: "success", names: []string{globalRead.Name, globalWrite.Name, userRead.Name}, tokens: 5, wantTokens: map[string]int64{ globalRead.Name: quotaMaxTokens, // disabled globalWrite.Name: globalWrite.MaxTokens - 5, userRead.Name: userRead.MaxTokens - 5, }, }, { desc: "globalOnly", names: []string{globalWrite.Name}, tokens: 7, wantTokens: map[string]int64{ globalWrite.Name: globalWrite.MaxTokens - 7, userRead.Name: userRead.MaxTokens, }, }, { desc: "userOnly", names: []string{userRead.Name}, tokens: 7, wantTokens: map[string]int64{ globalWrite.Name: globalWrite.MaxTokens, userRead.Name: userRead.MaxTokens - 7, }, }, { desc: "zeroTokens", names: []string{globalWrite.Name, userRead.Name}, tokens: 0, wantTokens: map[string]int64{ globalWrite.Name: globalWrite.MaxTokens, userRead.Name: userRead.MaxTokens, }, }, { desc: "successWithReplenishment", names: []string{globalWrite.Name, userRead.Name}, tokens: 5, nowIncrement: time.Duration(userRead.GetTimeBased().ReplenishIntervalSeconds) * time.Second, wantTokens: map[string]int64{ globalWrite.Name: globalWrite.MaxTokens - 5, userRead.Name: userRead.MaxTokens - 5, // Replenished then deduced }, }, { desc: "successDueToReplenishment", names: []string{globalWrite.Name, userRead.Name}, tokens: 1, nowIncrement: time.Duration(userRead.GetTimeBased().ReplenishIntervalSeconds) * time.Second, initialTokens: map[string]int64{ userRead.Name: 0, }, wantTokens: map[string]int64{ globalWrite.Name: globalWrite.MaxTokens - 1, userRead.Name: userRead.GetTimeBased().TokensToReplenish - 1, }, }, } ctx := context.Background() qs := &QuotaStorage{Client: client} for _, test := range tests { if err := setupTokens(ctx, qs, cfgs, test.initialTokens); err != nil { t.Errorf("%v: setupTokens() returned err = %v", test.desc, err) continue } fakeTime.Set(fakeTime.Now().Add(test.nowIncrement)) if err := qs.Get(ctx, test.names, test.tokens); err != nil { t.Errorf("%v: Get() returned err = %v", test.desc, err) continue } if err := peekAndDiff(ctx, qs, test.wantTokens); err != nil { t.Errorf("%v: %v", test.desc, err) } } } func TestQuotaStorage_GetErrors(t *testing.T) { tests := []struct { desc string names []string tokens int64 }{ { desc: "invalidTokens", names: []string{globalWrite.Name, userRead.Name}, tokens: -1, }, { desc: "insufficientTokens", names: []string{globalWrite.Name, userRead.Name}, tokens: globalWrite.MaxTokens + 10, }, } ctx := context.Background() qs := &QuotaStorage{Client: client} if _, err := qs.UpdateConfigs(ctx, true /* reset */, updater(cfgs)); err != nil { t.Fatalf("UpdateConfigs() returned err = %v", err) } for _, test := range tests { if err := qs.Get(ctx, test.names, test.tokens); err == nil { t.Errorf("%v: Get() returned err = nil, want non-nil", test.desc) } } } func TestQuotaStorage_Peek(t *testing.T) { fakeTime := clock.NewFake(time.Now()) defer setupTimeSource(fakeTime)() tests := []struct { desc string names []string nowIncrement time.Duration initialTokens, wantTokens map[string]int64 }{ { desc: "success", names: []string{globalRead.Name, globalWrite.Name, userRead.Name, "quotas/users/llama/write/config"}, wantTokens: map[string]int64{ globalRead.Name: quotaMaxTokens, // disabled globalWrite.Name: globalWrite.MaxTokens, userRead.Name: userRead.MaxTokens, "quotas/users/llama/write/config": quotaMaxTokens, // unknown }, }, { desc: "timeBasedReplenish", names: []string{globalWrite.Name, userRead.Name}, nowIncrement: time.Duration(userRead.GetTimeBased().ReplenishIntervalSeconds) * time.Second, initialTokens: map[string]int64{ globalWrite.Name: 10, userRead.Name: 10, }, wantTokens: map[string]int64{ globalWrite.Name: 10, userRead.Name: 10 + userRead.GetTimeBased().TokensToReplenish, }, }, } ctx := context.Background() qs := &QuotaStorage{Client: client} for _, test := range tests { if err := setupTokens(ctx, qs, cfgs, test.initialTokens); err != nil { t.Errorf("%v: setupTokens() returned err = %v", test.desc, err) continue } fakeTime.Set(fakeTime.Now().Add(test.nowIncrement)) if err := peekAndDiff(ctx, qs, test.wantTokens); err != nil { t.Errorf("%v: %v", test.desc, err) } } } func TestQuotaStorage_Put(t *testing.T) { fakeTime := clock.NewFake(time.Now()) defer setupTimeSource(fakeTime)() tests := []struct { desc string names []string tokens int64 nowIncrement time.Duration initialTokens, wantTokens map[string]int64 }{ { desc: "zero", names: []string{globalWrite.Name, userRead.Name}, tokens: 0, wantTokens: map[string]int64{ globalWrite.Name: globalWrite.MaxTokens, userRead.Name: userRead.MaxTokens, }, }, { desc: "success", names: []string{globalRead.Name, globalWrite.Name, userRead.Name}, tokens: 10, initialTokens: map[string]int64{ globalWrite.Name: 10, userRead.Name: 10, }, wantTokens: map[string]int64{ globalRead.Name: quotaMaxTokens, // disabled globalWrite.Name: 20, userRead.Name: 10, // Time-based quotas don't change on Put() }, }, { desc: "fullQuota", names: []string{globalWrite.Name, userRead.Name}, tokens: 10, wantTokens: map[string]int64{ globalWrite.Name: globalWrite.MaxTokens, userRead.Name: userRead.MaxTokens, }, }, { desc: "replenishToFull", names: []string{userRead.Name}, tokens: 0, nowIncrement: time.Duration(userRead.GetTimeBased().ReplenishIntervalSeconds) * time.Second, initialTokens: map[string]int64{ userRead.Name: userRead.MaxTokens - 1, }, wantTokens: map[string]int64{ userRead.Name: userRead.MaxTokens, }, }, { desc: "partialReplenish", names: []string{userRead.Name}, tokens: 100, nowIncrement: time.Duration(userRead.GetTimeBased().ReplenishIntervalSeconds) * time.Second, initialTokens: map[string]int64{ userRead.Name: 0, }, wantTokens: map[string]int64{ userRead.Name: userRead.GetTimeBased().TokensToReplenish, }, }, } ctx := context.Background() qs := &QuotaStorage{Client: client} for _, test := range tests { if err := setupTokens(ctx, qs, cfgs, test.initialTokens); err != nil { t.Errorf("%v: setupTokens() returned err = %v", test.desc, err) continue } if err := qs.Put(ctx, test.names, test.tokens); err != nil { t.Errorf("%v: Put() returned err = %v", test.desc, err) } fakeTime.Set(fakeTime.Now().Add(test.nowIncrement)) if err := peekAndDiff(ctx, qs, test.wantTokens); err != nil { t.Errorf("%v: %v", test.desc, err) } } } func TestQuotaStorage_PutErrors(t *testing.T) { tests := []struct { desc string names []string tokens int64 }{ {desc: "invalidTokens", names: []string{globalWrite.Name}, tokens: -1}, } ctx := context.Background() qs := &QuotaStorage{Client: client} if _, err := qs.UpdateConfigs(ctx, true /* reset */, updater(cfgs)); err != nil { t.Fatalf("UpdateConfigs() returned err = %v", err) } for _, test := range tests { if err := qs.Put(ctx, test.names, test.tokens); err == nil { t.Errorf("%v: Put() returned err = nil, want non-nil", test.desc) } } } func TestQuotaStorage_Reset(t *testing.T) { defer setupTimeSource(fixedTimeSource)() tests := []struct { desc string names []string initialTokens, wantTokens map[string]int64 }{ { desc: "success", names: []string{globalRead.Name, globalWrite.Name, userRead.Name}, initialTokens: map[string]int64{ globalWrite.Name: 10, userRead.Name: 10, }, wantTokens: map[string]int64{ globalRead.Name: quotaMaxTokens, // disabled globalWrite.Name: globalWrite.MaxTokens, userRead.Name: userRead.MaxTokens, }, }, { desc: "globalWrite", names: []string{globalWrite.Name}, initialTokens: map[string]int64{ globalWrite.Name: 10, }, wantTokens: map[string]int64{ globalWrite.Name: globalWrite.MaxTokens, }, }, { desc: "userRead", names: []string{userRead.Name}, initialTokens: map[string]int64{ userRead.Name: 10, }, wantTokens: map[string]int64{ userRead.Name: userRead.MaxTokens, }, }, { desc: "fullQuotas", names: []string{globalWrite.Name, userRead.Name}, wantTokens: map[string]int64{ globalWrite.Name: globalWrite.MaxTokens, userRead.Name: userRead.MaxTokens, }, }, { desc: "unknownQuota", names: []string{"quotas/users/llama/write/config"}, wantTokens: map[string]int64{ "quotas/users/llama/write/config": quotaMaxTokens, }, }, } ctx := context.Background() qs := &QuotaStorage{Client: client} if _, err := qs.UpdateConfigs(ctx, true /* reset */, updater(cfgs)); err != nil { t.Fatalf("UpdateConfigs() returned err = %v", err) } for _, test := range tests { if err := setupTokens(ctx, qs, cfgs, test.initialTokens); err != nil { t.Errorf("%v: setupTokens() returned err = %v", test.desc, err) continue } if err := qs.Reset(ctx, test.names); err != nil { t.Errorf("%v: Reset() returned err = %v", test.desc, err) } if err := peekAndDiff(ctx, qs, test.wantTokens); err != nil { t.Errorf("%v: %v", test.desc, err) } } } func TestQuotaStorage_ValidateNames(t *testing.T) { fns := []struct { name string run func(context.Context, *QuotaStorage, []string) error }{ { name: "Get", run: func(ctx context.Context, qs *QuotaStorage, names []string) error { return qs.Get(ctx, names, 0) }, }, { name: "Peek", run: func(ctx context.Context, qs *QuotaStorage, names []string) error { _, err := qs.Peek(ctx, names) return err }, }, { name: "Put", run: func(ctx context.Context, qs *QuotaStorage, names []string) error { return qs.Put(ctx, names, 0) }, }, { name: "Reset", run: func(ctx context.Context, qs *QuotaStorage, names []string) error { return qs.Reset(ctx, names) }, }, } tests := []struct { names []string }{ {names: []string{"bad/quota/name"}}, {names: []string{"quotas/bad/read/configs"}}, {names: []string{"quotas/global/read"}}, // missing "/configs" {names: []string{"quotas/trees/1234/write"}}, {names: []string{"quotas/users/llama/write"}}, {names: []string{"quotas/tree/1234/read/configs"}}, // should be "trees" {names: []string{"quotas/user/llama/read/configs"}}, // should be "users" {names: []string{globalWrite.Name, "bad"}}, } ctx := context.Background() qs := &QuotaStorage{Client: client} for _, test := range tests { for _, fn := range fns { if err := fn.run(ctx, qs, test.names); err == nil { t.Errorf("%v(%v) returned err = nil, want non-nil", fn.name, test.names) } } } } func peekAndDiff(ctx context.Context, qs *QuotaStorage, want map[string]int64) error { got, err := qs.Peek(ctx, keys(want)) if err != nil { return err } if diff := cmp.Diff(got, want, cmpopts.EquateEmpty()); diff != "" { return fmt.Errorf("post-Peek() diff (-got +want):\n%v", diff) } return nil } // setupTimeSource prepares timeSource for tests. // A cleanup function that restores timeSource to its initial value is returned and should be // defer-called. func setupTimeSource(ts clock.TimeSource) func() { prevTimeSource := timeSource timeSource = ts return func() { timeSource = prevTimeSource } } // setupTokens resets cfgs and gets tokens from each quota in order to make them match // initialTokens. func setupTokens(ctx context.Context, qs *QuotaStorage, cfgs *storagepb.Configs, initialTokens map[string]int64) error { if _, err := qs.UpdateConfigs(ctx, true /* reset */, updater(cfgs)); err != nil { return fmt.Errorf("UpdateConfigs() returned err = %v", err) } for name, wantTokens := range initialTokens { names := []string{name} tokens, err := qs.Peek(ctx, names) if err != nil { return fmt.Errorf("qs.Peek()=%v,%v, want: err=nil", tokens, err) } mod := tokens[name] - wantTokens if err := qs.Get(ctx, names, mod); err != nil { return fmt.Errorf("qs.Get()=%v, want: nil", err) } if err := peekAndDiff(ctx, qs, map[string]int64{name: wantTokens}); err != nil { return err } } return nil } func deepCopy(c1 *storagepb.Configs) *storagepb.Configs { return proto.Clone(c1).(*storagepb.Configs) } func keys(m map[string]int64) []string { keys := make([]string, 0, len(m)) for k := range m { keys = append(keys, k) } return keys } func updater(cfgs *storagepb.Configs) func(*storagepb.Configs) { return func(c *storagepb.Configs) { (*c).Reset() proto.Merge(c, cfgs) } } trillian-1.6.1/quota/etcd/storagepb/000077500000000000000000000000001466362047600174115ustar00rootroot00000000000000trillian-1.6.1/quota/etcd/storagepb/gen.go000066400000000000000000000014461466362047600205160ustar00rootroot00000000000000// 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 storagepb contains the protobuf definitions for using etcd as a // Trillian quota backend. package storagepb //go:generate protoc -I=. --go_out=paths=source_relative:. storagepb.proto trillian-1.6.1/quota/etcd/storagepb/storagepb.pb.go000066400000000000000000000462311466362047600223340ustar00rootroot00000000000000// 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. // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.34.2 // protoc v3.20.1 // source: storagepb.proto // Package storagepb contains definitions for quota storage protos, which are // recorded in etcd. package storagepb import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // Possible states of a quota configuration. type Config_State int32 const ( // Unknown quota state. Invalid. Config_UNKNOWN_CONFIG_STATE Config_State = 0 // Quota is enabled. Config_ENABLED Config_State = 1 // Quota is disabled (considered infinite). Config_DISABLED Config_State = 2 ) // Enum value maps for Config_State. var ( Config_State_name = map[int32]string{ 0: "UNKNOWN_CONFIG_STATE", 1: "ENABLED", 2: "DISABLED", } Config_State_value = map[string]int32{ "UNKNOWN_CONFIG_STATE": 0, "ENABLED": 1, "DISABLED": 2, } ) func (x Config_State) Enum() *Config_State { p := new(Config_State) *p = x return p } func (x Config_State) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (Config_State) Descriptor() protoreflect.EnumDescriptor { return file_storagepb_proto_enumTypes[0].Descriptor() } func (Config_State) Type() protoreflect.EnumType { return &file_storagepb_proto_enumTypes[0] } func (x Config_State) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use Config_State.Descriptor instead. func (Config_State) EnumDescriptor() ([]byte, []int) { return file_storagepb_proto_rawDescGZIP(), []int{2, 0} } // Data contained in a quota bucket. // Stored at each each quota's zero bucket. For example, // quotas/global/read/0 or quotas/trees/$id/read/0. type Bucket struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Number of tokens left in the bucket. Tokens int64 `protobuf:"varint,1,opt,name=tokens,proto3" json:"tokens,omitempty"` // Timestamp of the last time the bucket got replenished. LastReplenishMillisSinceEpoch int64 `protobuf:"varint,2,opt,name=last_replenish_millis_since_epoch,json=lastReplenishMillisSinceEpoch,proto3" json:"last_replenish_millis_since_epoch,omitempty"` } func (x *Bucket) Reset() { *x = Bucket{} if protoimpl.UnsafeEnabled { mi := &file_storagepb_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Bucket) String() string { return protoimpl.X.MessageStringOf(x) } func (*Bucket) ProtoMessage() {} func (x *Bucket) ProtoReflect() protoreflect.Message { mi := &file_storagepb_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Bucket.ProtoReflect.Descriptor instead. func (*Bucket) Descriptor() ([]byte, []int) { return file_storagepb_proto_rawDescGZIP(), []int{0} } func (x *Bucket) GetTokens() int64 { if x != nil { return x.Tokens } return 0 } func (x *Bucket) GetLastReplenishMillisSinceEpoch() int64 { if x != nil { return x.LastReplenishMillisSinceEpoch } return 0 } // Configuration for all quotas. // Stored at quotas/configs. type Configs struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Known quota configurations. Configs []*Config `protobuf:"bytes,1,rep,name=configs,proto3" json:"configs,omitempty"` } func (x *Configs) Reset() { *x = Configs{} if protoimpl.UnsafeEnabled { mi := &file_storagepb_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Configs) String() string { return protoimpl.X.MessageStringOf(x) } func (*Configs) ProtoMessage() {} func (x *Configs) ProtoReflect() protoreflect.Message { mi := &file_storagepb_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Configs.ProtoReflect.Descriptor instead. func (*Configs) Descriptor() ([]byte, []int) { return file_storagepb_proto_rawDescGZIP(), []int{1} } func (x *Configs) GetConfigs() []*Config { if x != nil { return x.Configs } return nil } // Configuration of a quota. type Config struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Name of the config, eg, “quotas/trees/1234/read/config”. Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // State of the config. State Config_State `protobuf:"varint,2,opt,name=state,proto3,enum=storagepb.Config_State" json:"state,omitempty"` // Max number of tokens available for the config. MaxTokens int64 `protobuf:"varint,3,opt,name=max_tokens,json=maxTokens,proto3" json:"max_tokens,omitempty"` // Replenishment strategy used by the config. // // Types that are assignable to ReplenishmentStrategy: // // *Config_SequencingBased // *Config_TimeBased ReplenishmentStrategy isConfig_ReplenishmentStrategy `protobuf_oneof:"replenishment_strategy"` } func (x *Config) Reset() { *x = Config{} if protoimpl.UnsafeEnabled { mi := &file_storagepb_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Config) String() string { return protoimpl.X.MessageStringOf(x) } func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { mi := &file_storagepb_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { return file_storagepb_proto_rawDescGZIP(), []int{2} } func (x *Config) GetName() string { if x != nil { return x.Name } return "" } func (x *Config) GetState() Config_State { if x != nil { return x.State } return Config_UNKNOWN_CONFIG_STATE } func (x *Config) GetMaxTokens() int64 { if x != nil { return x.MaxTokens } return 0 } func (m *Config) GetReplenishmentStrategy() isConfig_ReplenishmentStrategy { if m != nil { return m.ReplenishmentStrategy } return nil } func (x *Config) GetSequencingBased() *SequencingBasedStrategy { if x, ok := x.GetReplenishmentStrategy().(*Config_SequencingBased); ok { return x.SequencingBased } return nil } func (x *Config) GetTimeBased() *TimeBasedStrategy { if x, ok := x.GetReplenishmentStrategy().(*Config_TimeBased); ok { return x.TimeBased } return nil } type isConfig_ReplenishmentStrategy interface { isConfig_ReplenishmentStrategy() } type Config_SequencingBased struct { // Sequencing-based replenishment settings. SequencingBased *SequencingBasedStrategy `protobuf:"bytes,4,opt,name=sequencing_based,json=sequencingBased,proto3,oneof"` } type Config_TimeBased struct { // Time-based replenishment settings. TimeBased *TimeBasedStrategy `protobuf:"bytes,5,opt,name=time_based,json=timeBased,proto3,oneof"` } func (*Config_SequencingBased) isConfig_ReplenishmentStrategy() {} func (*Config_TimeBased) isConfig_ReplenishmentStrategy() {} // Sequencing-based replenishment strategy settings. type SequencingBasedStrategy struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *SequencingBasedStrategy) Reset() { *x = SequencingBasedStrategy{} if protoimpl.UnsafeEnabled { mi := &file_storagepb_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *SequencingBasedStrategy) String() string { return protoimpl.X.MessageStringOf(x) } func (*SequencingBasedStrategy) ProtoMessage() {} func (x *SequencingBasedStrategy) ProtoReflect() protoreflect.Message { mi := &file_storagepb_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SequencingBasedStrategy.ProtoReflect.Descriptor instead. func (*SequencingBasedStrategy) Descriptor() ([]byte, []int) { return file_storagepb_proto_rawDescGZIP(), []int{3} } // Time-based replenishment strategy settings. type TimeBasedStrategy struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Number of tokens to replenish at every replenish_interval_seconds. TokensToReplenish int64 `protobuf:"varint,1,opt,name=tokens_to_replenish,json=tokensToReplenish,proto3" json:"tokens_to_replenish,omitempty"` // Interval at which tokens_to_replenish get replenished. ReplenishIntervalSeconds int64 `protobuf:"varint,2,opt,name=replenish_interval_seconds,json=replenishIntervalSeconds,proto3" json:"replenish_interval_seconds,omitempty"` } func (x *TimeBasedStrategy) Reset() { *x = TimeBasedStrategy{} if protoimpl.UnsafeEnabled { mi := &file_storagepb_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *TimeBasedStrategy) String() string { return protoimpl.X.MessageStringOf(x) } func (*TimeBasedStrategy) ProtoMessage() {} func (x *TimeBasedStrategy) ProtoReflect() protoreflect.Message { mi := &file_storagepb_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use TimeBasedStrategy.ProtoReflect.Descriptor instead. func (*TimeBasedStrategy) Descriptor() ([]byte, []int) { return file_storagepb_proto_rawDescGZIP(), []int{4} } func (x *TimeBasedStrategy) GetTokensToReplenish() int64 { if x != nil { return x.TokensToReplenish } return 0 } func (x *TimeBasedStrategy) GetReplenishIntervalSeconds() int64 { if x != nil { return x.ReplenishIntervalSeconds } return 0 } var File_storagepb_proto protoreflect.FileDescriptor var file_storagepb_proto_rawDesc = []byte{ 0x0a, 0x0f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x70, 0x62, 0x22, 0x6a, 0x0a, 0x06, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x12, 0x48, 0x0a, 0x21, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x65, 0x6e, 0x69, 0x73, 0x68, 0x5f, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x5f, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x1d, 0x6c, 0x61, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x65, 0x6e, 0x69, 0x73, 0x68, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x53, 0x69, 0x6e, 0x63, 0x65, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x22, 0x36, 0x0a, 0x07, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x12, 0x2b, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x22, 0xd2, 0x02, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2d, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x78, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x6d, 0x61, 0x78, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x12, 0x4f, 0x0a, 0x10, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x42, 0x61, 0x73, 0x65, 0x64, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x48, 0x00, 0x52, 0x0f, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x42, 0x61, 0x73, 0x65, 0x64, 0x12, 0x3d, 0x0a, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x70, 0x62, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x42, 0x61, 0x73, 0x65, 0x64, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x48, 0x00, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x42, 0x61, 0x73, 0x65, 0x64, 0x22, 0x3c, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x14, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x45, 0x4e, 0x41, 0x42, 0x4c, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x44, 0x49, 0x53, 0x41, 0x42, 0x4c, 0x45, 0x44, 0x10, 0x02, 0x42, 0x18, 0x0a, 0x16, 0x72, 0x65, 0x70, 0x6c, 0x65, 0x6e, 0x69, 0x73, 0x68, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x22, 0x19, 0x0a, 0x17, 0x53, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x42, 0x61, 0x73, 0x65, 0x64, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x22, 0x81, 0x01, 0x0a, 0x11, 0x54, 0x69, 0x6d, 0x65, 0x42, 0x61, 0x73, 0x65, 0x64, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x2e, 0x0a, 0x13, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x5f, 0x74, 0x6f, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x65, 0x6e, 0x69, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x54, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x65, 0x6e, 0x69, 0x73, 0x68, 0x12, 0x3c, 0x0a, 0x1a, 0x72, 0x65, 0x70, 0x6c, 0x65, 0x6e, 0x69, 0x73, 0x68, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x18, 0x72, 0x65, 0x70, 0x6c, 0x65, 0x6e, 0x69, 0x73, 0x68, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x42, 0x31, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2f, 0x71, 0x75, 0x6f, 0x74, 0x61, 0x2f, 0x65, 0x74, 0x63, 0x64, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( file_storagepb_proto_rawDescOnce sync.Once file_storagepb_proto_rawDescData = file_storagepb_proto_rawDesc ) func file_storagepb_proto_rawDescGZIP() []byte { file_storagepb_proto_rawDescOnce.Do(func() { file_storagepb_proto_rawDescData = protoimpl.X.CompressGZIP(file_storagepb_proto_rawDescData) }) return file_storagepb_proto_rawDescData } var file_storagepb_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_storagepb_proto_msgTypes = make([]protoimpl.MessageInfo, 5) var file_storagepb_proto_goTypes = []any{ (Config_State)(0), // 0: storagepb.Config.State (*Bucket)(nil), // 1: storagepb.Bucket (*Configs)(nil), // 2: storagepb.Configs (*Config)(nil), // 3: storagepb.Config (*SequencingBasedStrategy)(nil), // 4: storagepb.SequencingBasedStrategy (*TimeBasedStrategy)(nil), // 5: storagepb.TimeBasedStrategy } var file_storagepb_proto_depIdxs = []int32{ 3, // 0: storagepb.Configs.configs:type_name -> storagepb.Config 0, // 1: storagepb.Config.state:type_name -> storagepb.Config.State 4, // 2: storagepb.Config.sequencing_based:type_name -> storagepb.SequencingBasedStrategy 5, // 3: storagepb.Config.time_based:type_name -> storagepb.TimeBasedStrategy 4, // [4:4] is the sub-list for method output_type 4, // [4:4] is the sub-list for method input_type 4, // [4:4] is the sub-list for extension type_name 4, // [4:4] is the sub-list for extension extendee 0, // [0:4] is the sub-list for field type_name } func init() { file_storagepb_proto_init() } func file_storagepb_proto_init() { if File_storagepb_proto != nil { return } if !protoimpl.UnsafeEnabled { file_storagepb_proto_msgTypes[0].Exporter = func(v any, i int) any { switch v := v.(*Bucket); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_storagepb_proto_msgTypes[1].Exporter = func(v any, i int) any { switch v := v.(*Configs); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_storagepb_proto_msgTypes[2].Exporter = func(v any, i int) any { switch v := v.(*Config); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_storagepb_proto_msgTypes[3].Exporter = func(v any, i int) any { switch v := v.(*SequencingBasedStrategy); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_storagepb_proto_msgTypes[4].Exporter = func(v any, i int) any { switch v := v.(*TimeBasedStrategy); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } } file_storagepb_proto_msgTypes[2].OneofWrappers = []any{ (*Config_SequencingBased)(nil), (*Config_TimeBased)(nil), } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_storagepb_proto_rawDesc, NumEnums: 1, NumMessages: 5, NumExtensions: 0, NumServices: 0, }, GoTypes: file_storagepb_proto_goTypes, DependencyIndexes: file_storagepb_proto_depIdxs, EnumInfos: file_storagepb_proto_enumTypes, MessageInfos: file_storagepb_proto_msgTypes, }.Build() File_storagepb_proto = out.File file_storagepb_proto_rawDesc = nil file_storagepb_proto_goTypes = nil file_storagepb_proto_depIdxs = nil } trillian-1.6.1/quota/etcd/storagepb/storagepb.proto000066400000000000000000000046111466362047600224660ustar00rootroot00000000000000// 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. syntax = "proto3"; option go_package = "github.com/google/trillian/quota/etcd/storagepb"; // Package storagepb contains definitions for quota storage protos, which are // recorded in etcd. package storagepb; // Data contained in a quota bucket. // Stored at each each quota's zero bucket. For example, // quotas/global/read/0 or quotas/trees/$id/read/0. message Bucket { // Number of tokens left in the bucket. int64 tokens = 1; // Timestamp of the last time the bucket got replenished. int64 last_replenish_millis_since_epoch = 2; } // Configuration for all quotas. // Stored at quotas/configs. message Configs { // Known quota configurations. repeated Config configs = 1; } // Configuration of a quota. message Config { // Possible states of a quota configuration. enum State { // Unknown quota state. Invalid. UNKNOWN_CONFIG_STATE = 0; // Quota is enabled. ENABLED = 1; // Quota is disabled (considered infinite). DISABLED = 2; } // Name of the config, eg, “quotas/trees/1234/read/config”. string name = 1; // State of the config. State state = 2; // Max number of tokens available for the config. int64 max_tokens = 3; // Replenishment strategy used by the config. oneof replenishment_strategy { // Sequencing-based replenishment settings. SequencingBasedStrategy sequencing_based = 4; // Time-based replenishment settings. TimeBasedStrategy time_based = 5; } } // Sequencing-based replenishment strategy settings. message SequencingBasedStrategy {} // Time-based replenishment strategy settings. message TimeBasedStrategy { // Number of tokens to replenish at every replenish_interval_seconds. int64 tokens_to_replenish = 1; // Interval at which tokens_to_replenish get replenished. int64 replenish_interval_seconds = 2; } trillian-1.6.1/quota/gen.go000066400000000000000000000015411466362047600156050ustar00rootroot00000000000000// 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 quota //go:generate mockgen --destination=mock_quota.go -package=quota -self_package=github.com/google/trillian/quota github.com/google/trillian/quota Manager //go:generate stringer -type=Group quota.go //go:generate stringer -type=Kind quota.go trillian-1.6.1/quota/group_string.go000066400000000000000000000011431466362047600175540ustar00rootroot00000000000000// Code generated by "stringer -type=Group quota.go"; DO NOT EDIT. package quota import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[Global-0] _ = x[Tree-1] _ = x[User-2] } const _Group_name = "GlobalTreeUser" var _Group_index = [...]uint8{0, 6, 10, 14} func (i Group) String() string { if i < 0 || i >= Group(len(_Group_index)-1) { return "Group(" + strconv.FormatInt(int64(i), 10) + ")" } return _Group_name[_Group_index[i]:_Group_index[i+1]] } trillian-1.6.1/quota/kind_string.go000066400000000000000000000010771466362047600173530ustar00rootroot00000000000000// Code generated by "stringer -type=Kind quota.go"; DO NOT EDIT. package quota import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[Read-0] _ = x[Write-1] } const _Kind_name = "ReadWrite" var _Kind_index = [...]uint8{0, 4, 9} func (i Kind) String() string { if i < 0 || i >= Kind(len(_Kind_index)-1) { return "Kind(" + strconv.FormatInt(int64(i), 10) + ")" } return _Kind_name[_Kind_index[i]:_Kind_index[i+1]] } trillian-1.6.1/quota/metrics.go000066400000000000000000000052741466362047600165110ustar00rootroot00000000000000// 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 quota import ( "fmt" "sync" "github.com/google/trillian/monitoring" ) var ( // Metrics groups all quota-related metrics. // The metrics represented here are not meant to be maintained by the quota subsystem // implementation. Instead, they're meant to be updated by the quota's callers, in order to // record their interactions with quotas. // The quota implementation is encouraged to define its own metrics to monitor its internal // state. Metrics = &m{} metricsOnce = sync.Once{} ) type m struct { AcquiredTokens monitoring.Counter ReturnedTokens monitoring.Counter ReplenishedTokens monitoring.Counter } // IncAcquired increments the AcquiredTokens metric. func (m *m) IncAcquired(tokens int, specs []Spec, success bool) { m.add(m.AcquiredTokens, tokens, specs, success) } // IncReturned increments the ReturnedTokens metric. func (m *m) IncReturned(tokens int, specs []Spec, success bool) { m.add(m.ReturnedTokens, tokens, specs, success) } // IncReplenished increments the ReplenishedTokens metric. func (m *m) IncReplenished(tokens int, specs []Spec, success bool) { m.add(m.ReplenishedTokens, tokens, specs, success) } func (m *m) add(c monitoring.Counter, tokens int, specs []Spec, success bool) { if c == nil { return } for _, spec := range specs { if spec.Group == User { // Don't populate per-user labels. continue } c.Add(float64(tokens), spec.Name(), fmt.Sprint(success)) } } // InitMetrics initializes Metrics using mf to create the monitoring objects. // May be called multiple times. If so, the first call is the one that counts. func InitMetrics(mf monitoring.MetricFactory) { metricsOnce.Do(func() { Metrics.AcquiredTokens = mf.NewCounter("quota_acquired_tokens", "Number of acquired quota tokens", "spec", "success") Metrics.ReturnedTokens = mf.NewCounter("quota_returned_tokens", "Number of quota tokens returned due to overcharging (bad requests, duplicates, etc)", "spec", "success") Metrics.ReplenishedTokens = mf.NewCounter("quota_replenished_tokens", "Number of quota tokens replenished due to sequencer progress", "spec", "success") }) } trillian-1.6.1/quota/mock_quota.go000066400000000000000000000046641466362047600172070ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/google/trillian/quota (interfaces: Manager) // Package quota is a generated GoMock package. package quota import ( context "context" reflect "reflect" gomock "github.com/golang/mock/gomock" ) // MockManager is a mock of Manager interface. type MockManager struct { ctrl *gomock.Controller recorder *MockManagerMockRecorder } // MockManagerMockRecorder is the mock recorder for MockManager. type MockManagerMockRecorder struct { mock *MockManager } // NewMockManager creates a new mock instance. func NewMockManager(ctrl *gomock.Controller) *MockManager { mock := &MockManager{ctrl: ctrl} mock.recorder = &MockManagerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockManager) EXPECT() *MockManagerMockRecorder { return m.recorder } // GetTokens mocks base method. func (m *MockManager) GetTokens(arg0 context.Context, arg1 int, arg2 []Spec) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTokens", arg0, arg1, arg2) ret0, _ := ret[0].(error) return ret0 } // GetTokens indicates an expected call of GetTokens. func (mr *MockManagerMockRecorder) GetTokens(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTokens", reflect.TypeOf((*MockManager)(nil).GetTokens), arg0, arg1, arg2) } // PutTokens mocks base method. func (m *MockManager) PutTokens(arg0 context.Context, arg1 int, arg2 []Spec) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PutTokens", arg0, arg1, arg2) ret0, _ := ret[0].(error) return ret0 } // PutTokens indicates an expected call of PutTokens. func (mr *MockManagerMockRecorder) PutTokens(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutTokens", reflect.TypeOf((*MockManager)(nil).PutTokens), arg0, arg1, arg2) } // ResetQuota mocks base method. func (m *MockManager) ResetQuota(arg0 context.Context, arg1 []Spec) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ResetQuota", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // ResetQuota indicates an expected call of ResetQuota. func (mr *MockManagerMockRecorder) ResetQuota(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResetQuota", reflect.TypeOf((*MockManager)(nil).ResetQuota), arg0, arg1) } trillian-1.6.1/quota/mysqlqm/000077500000000000000000000000001466362047600162075ustar00rootroot00000000000000trillian-1.6.1/quota/mysqlqm/mysql_quota.go000066400000000000000000000126251466362047600211220ustar00rootroot00000000000000// 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 mysqlqm defines a MySQL-based quota.Manager implementation. package mysqlqm import ( "context" "database/sql" "errors" "fmt" "github.com/google/trillian/quota" "k8s.io/klog/v2" ) const ( // DefaultMaxUnsequenced is a suggested value for MaxUnsequencedRows. // Note that this is a Global/Write quota suggestion, so it applies across trees. DefaultMaxUnsequenced = 500000 // About 2h of non-stop signing at 70QPS. countFromInformationSchemaQuery = ` SELECT table_rows FROM information_schema.tables WHERE table_schema = schema() AND table_name = ? AND table_type = ?` countFromUnsequencedQuery = "SELECT COUNT(*) FROM Unsequenced" ) // ErrTooManyUnsequencedRows is returned when tokens are requested but Unsequenced has grown // beyond the configured limit. var ErrTooManyUnsequencedRows = errors.New("too many unsequenced rows") // QuotaManager is a MySQL-based quota.Manager implementation. // // It has two working modes: one queries the information schema for the number of Unsequenced rows, // the other does a select count(*) on the Unsequenced table. Information schema queries are // default, even though they are approximate, as they're constant time (select count(*) on InnoDB // based MySQL needs to traverse the index and may take quite a while to complete). // // QuotaManager only implements Global/Write quotas, which is based on the number of Unsequenced // rows (to be exact, tokens = MaxUnsequencedRows - actualUnsequencedRows). // Other quotas are considered infinite. type QuotaManager struct { DB *sql.DB MaxUnsequencedRows int UseSelectCount bool } // GetTokens implements quota.Manager.GetTokens. // It doesn't actually reserve or retrieve tokens, instead it allows access based on the number of // rows in the Unsequenced table. func (m *QuotaManager) GetTokens(ctx context.Context, numTokens int, specs []quota.Spec) error { for _, spec := range specs { if spec.Group != quota.Global || spec.Kind != quota.Write { continue } // Only allow global writes if Unsequenced is under the expected limit count, err := m.countUnsequenced(ctx) if err != nil { return err } if count+numTokens > m.MaxUnsequencedRows { return ErrTooManyUnsequencedRows } } return nil } // PutTokens implements quota.Manager.PutTokens. // It's a noop for QuotaManager. func (m *QuotaManager) PutTokens(ctx context.Context, numTokens int, specs []quota.Spec) error { return nil } // ResetQuota implements quota.Manager.ResetQuota. // It's a noop for QuotaManager. func (m *QuotaManager) ResetQuota(ctx context.Context, specs []quota.Spec) error { return nil } func (m *QuotaManager) countUnsequenced(ctx context.Context) (int, error) { if m.UseSelectCount { return countFromTable(ctx, m.DB) } return countFromInformationSchema(ctx, m.DB) } func countFromInformationSchema(ctx context.Context, db *sql.DB) (int, error) { // turn off statistics caching for MySQL 8 if err := turnOffInformationSchemaCache(ctx, db); err != nil { return 0, err } // information_schema.tables doesn't have an explicit PK, so let's play it safe and ensure // the cursor returns a single row. rows, err := db.QueryContext(ctx, countFromInformationSchemaQuery, "Unsequenced", "BASE TABLE") if err != nil { return 0, err } defer func() { if err := rows.Close(); err != nil { klog.Errorf("Close(): %v", err) } }() if !rows.Next() { return 0, errors.New("cursor has no rows after information_schema query") } var count int if err := rows.Scan(&count); err != nil { return 0, err } if rows.Next() { return 0, errors.New("too many rows returned from information_schema query") } return count, nil } func countFromTable(ctx context.Context, db *sql.DB) (int, error) { var count int if err := db.QueryRowContext(ctx, countFromUnsequencedQuery).Scan(&count); err != nil { return 0, err } return count, nil } // turnOffInformationSchemaCache turn off statistics caching for MySQL 8 // To always retrieve the latest statistics directly from the storage engine and bypass cached values, set information_schema_stats_expiry to 0. // See https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_information_schema_stats_expiry // MySQL versions prior to 8 will fail safely. func turnOffInformationSchemaCache(ctx context.Context, db *sql.DB) error { opt := "information_schema_stats_expiry" res := db.QueryRowContext(ctx, "SHOW VARIABLES LIKE '"+opt+"'") var none string var expiry int if err := res.Scan(&none, &expiry); err != nil { // fail safely for all versions of MySQL prior to 8 if errors.Is(err, sql.ErrNoRows) { return nil } return fmt.Errorf("failed to get variable %q: %v", opt, err) } if expiry != 0 { if _, err := db.ExecContext(ctx, "SET SESSION "+opt+"=0"); err != nil { return fmt.Errorf("failed to set variable %q: %v", opt, err) } } return nil } trillian-1.6.1/quota/mysqlqm/mysql_quota_test.go000066400000000000000000000222151466362047600221550ustar00rootroot00000000000000// 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 mysqlqm_test import ( "context" "crypto" "database/sql" "fmt" "testing" "time" "github.com/google/trillian" "github.com/google/trillian/quota" "github.com/google/trillian/quota/mysqlqm" "github.com/google/trillian/storage" "github.com/google/trillian/storage/mysql" "github.com/google/trillian/storage/testdb" "github.com/google/trillian/types" stestonly "github.com/google/trillian/storage/testonly" ) func TestQuotaManager_GetTokens(t *testing.T) { testdb.SkipIfNoMySQL(t) ctx := context.Background() db, done, err := testdb.NewTrillianDB(ctx, testdb.DriverMySQL) if err != nil { t.Fatalf("GetTestDB() returned err = %v", err) } defer done(ctx) tree, err := createTree(ctx, db) if err != nil { t.Fatalf("createTree() returned err = %v", err) } tests := []struct { desc string unsequencedRows, maxUnsequencedRows, numTokens int specs []quota.Spec wantErr bool }{ { desc: "globalWriteSingleToken", unsequencedRows: 10, maxUnsequencedRows: 20, numTokens: 1, specs: []quota.Spec{{Group: quota.Global, Kind: quota.Write}}, }, { desc: "globalWriteMultiToken", unsequencedRows: 10, maxUnsequencedRows: 20, numTokens: 5, specs: []quota.Spec{{Group: quota.Global, Kind: quota.Write}}, }, { desc: "globalWriteOverQuota1", unsequencedRows: 20, maxUnsequencedRows: 20, numTokens: 1, specs: []quota.Spec{{Group: quota.Global, Kind: quota.Write}}, wantErr: true, }, { desc: "globalWriteOverQuota2", unsequencedRows: 15, maxUnsequencedRows: 20, numTokens: 10, specs: []quota.Spec{{Group: quota.Global, Kind: quota.Write}}, wantErr: true, }, { desc: "unlimitedQuotas", numTokens: 10, specs: []quota.Spec{ {Group: quota.User, Kind: quota.Read, User: "dylan"}, {Group: quota.Tree, Kind: quota.Read, TreeID: tree.TreeId}, {Group: quota.Global, Kind: quota.Read}, {Group: quota.User, Kind: quota.Write, User: "dylan"}, {Group: quota.Tree, Kind: quota.Write, TreeID: tree.TreeId}, }, }, } for _, test := range tests { if err := setUnsequencedRows(ctx, db, tree, test.unsequencedRows); err != nil { t.Errorf("setUnsequencedRows() returned err = %v", err) continue } // Test general cases using select count(*) to avoid flakiness / allow for more // precise assertions. // See TestQuotaManager_GetTokens_InformationSchema for information schema tests. qm := &mysqlqm.QuotaManager{DB: db, MaxUnsequencedRows: test.maxUnsequencedRows, UseSelectCount: true} err := qm.GetTokens(ctx, test.numTokens, test.specs) if hasErr := err == mysqlqm.ErrTooManyUnsequencedRows; hasErr != test.wantErr { t.Errorf("%v: GetTokens() returned err = %q, wantErr = %v", test.desc, err, test.wantErr) } } } func TestQuotaManager_GetTokens_InformationSchema(t *testing.T) { testdb.SkipIfNoMySQL(t) ctx := context.Background() maxUnsequenced := 20 globalWriteSpec := []quota.Spec{{Group: quota.Global, Kind: quota.Write}} // Make both variants go through the test. tests := []struct { useSelectCount bool }{ {useSelectCount: true}, {useSelectCount: false}, } for _, test := range tests { desc := fmt.Sprintf("useSelectCount = %v", test.useSelectCount) t.Run(desc, func(t *testing.T) { db, done, err := testdb.NewTrillianDB(ctx, testdb.DriverMySQL) if err != nil { t.Fatalf("NewTrillianDB() returned err = %v", err) } defer done(ctx) tree, err := createTree(ctx, db) if err != nil { t.Fatalf("createTree() returned err = %v", err) } qm := &mysqlqm.QuotaManager{DB: db, MaxUnsequencedRows: maxUnsequenced, UseSelectCount: test.useSelectCount} // All GetTokens() calls where leaves < maxUnsequenced should succeed: // information_schema may be outdated, but it should refer to a valid point in the // past. for i := 0; i < maxUnsequenced-1; i++ { if err := queueLeaves(ctx, db, tree, i /* firstID */, 1 /* num */); err != nil { t.Fatalf("queueLeaves() returned err = %v", err) } if err := qm.GetTokens(ctx, 1 /* numTokens */, globalWriteSpec); err != nil { t.Errorf("GetTokens() returned err = %v (%v leaves)", err, i+1) } } // Make leaves = maxUnsequenced if err := queueLeaves(ctx, db, tree, maxUnsequenced-1 /* firstID */, 1 /* num */); err != nil { t.Fatalf("queueLeaves() returned err = %v", err) } // Allow some time for information_schema to "catch up". stop := false timeout := time.After(1 * time.Second) for !stop { select { case <-timeout: t.Errorf("timed out") stop = true default: // An error means that GetTokens is working correctly stop = qm.GetTokens(ctx, 1 /* numTokens */, globalWriteSpec) == mysqlqm.ErrTooManyUnsequencedRows } } }) } } func TestQuotaManager_Noops(t *testing.T) { testdb.SkipIfNoMySQL(t) ctx := context.Background() db, done, err := testdb.NewTrillianDB(ctx, testdb.DriverMySQL) if err != nil { t.Fatalf("GetTestDB() returned err = %v", err) } defer done(ctx) qm := &mysqlqm.QuotaManager{DB: db, MaxUnsequencedRows: 1000} specs := allSpecs(ctx, qm, 10 /* treeID */) tests := []struct { desc string fn func() error }{ { desc: "PutTokens", fn: func() error { return qm.PutTokens(ctx, 10 /* numTokens */, specs) }, }, { desc: "ResetQuota", fn: func() error { return qm.ResetQuota(ctx, specs) }, }, } for _, test := range tests { if err := test.fn(); err != nil { t.Errorf("%v: got err = %v", test.desc, err) } } } func allSpecs(_ context.Context, _ quota.Manager, treeID int64) []quota.Spec { return []quota.Spec{ {Group: quota.User, Kind: quota.Read, User: "florence"}, {Group: quota.Tree, Kind: quota.Read, TreeID: treeID}, {Group: quota.Global, Kind: quota.Read}, {Group: quota.User, Kind: quota.Write, User: "florence"}, {Group: quota.Tree, Kind: quota.Write, TreeID: treeID}, {Group: quota.Global, Kind: quota.Write}, } } func countUnsequenced(ctx context.Context, db *sql.DB) (int, error) { var count int if err := db.QueryRowContext(ctx, "SELECT COUNT(*) FROM Unsequenced").Scan(&count); err != nil { return 0, err } return count, nil } func createTree(ctx context.Context, db *sql.DB) (*trillian.Tree, error) { var tree *trillian.Tree { as := mysql.NewAdminStorage(db) err := as.ReadWriteTransaction(ctx, func(ctx context.Context, tx storage.AdminTX) error { var err error tree, err = tx.CreateTree(ctx, stestonly.LogTree) return err }) if err != nil { return nil, err } } { ls := mysql.NewLogStorage(db, nil) err := ls.ReadWriteTransaction(ctx, tree, func(ctx context.Context, tx storage.LogTreeTX) error { logRoot, err := (&types.LogRootV1{RootHash: []byte{0}}).MarshalBinary() if err != nil { return err } slr := &trillian.SignedLogRoot{LogRoot: logRoot} return tx.StoreSignedLogRoot(ctx, slr) }) if err != nil { return nil, err } } return tree, nil } func queueLeaves(ctx context.Context, db *sql.DB, tree *trillian.Tree, firstID, num int) error { hasher := crypto.SHA256.New() leaves := []*trillian.LogLeaf{} for i := 0; i < num; i++ { value := []byte(fmt.Sprintf("leaf-%v", firstID+i)) hasher.Reset() if _, err := hasher.Write(value); err != nil { return err } hash := hasher.Sum(nil) leaves = append(leaves, &trillian.LogLeaf{ MerkleLeafHash: hash, LeafValue: value, ExtraData: []byte("extra data"), LeafIdentityHash: hash, }) } ls := mysql.NewLogStorage(db, nil) _, err := ls.QueueLeaves(ctx, tree, leaves, time.Now()) return err } func setUnsequencedRows(ctx context.Context, db *sql.DB, tree *trillian.Tree, wantRows int) error { count, err := countUnsequenced(ctx, db) if err != nil { return err } if count == wantRows { return nil } // Clear the tables and re-create leaves from scratch. It's easier than having to reason // about duplicate entries. if _, err := db.ExecContext(ctx, "DELETE FROM LeafData"); err != nil { return err } if _, err := db.ExecContext(ctx, "DELETE FROM Unsequenced"); err != nil { return err } if err := queueLeaves(ctx, db, tree, 0 /* firstID */, wantRows); err != nil { return err } // Sanity check the final count count, err = countUnsequenced(ctx, db) if err != nil { return err } if count != wantRows { return fmt.Errorf("got %v unsequenced rows, want = %v", count, wantRows) } return nil } trillian-1.6.1/quota/mysqlqm/quota_provider.go000066400000000000000000000027301466362047600216030ustar00rootroot00000000000000// Copyright 2018 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 mysqlqm import ( "flag" "github.com/google/trillian/quota" "github.com/google/trillian/storage/mysql" "k8s.io/klog/v2" ) // QuotaManagerName identifies the MySQL quota implementation. const QuotaManagerName = "mysql" var maxUnsequencedRows = flag.Int("max_unsequenced_rows", DefaultMaxUnsequenced, "Max number of unsequenced rows before rate limiting kicks in. "+ "Only effective for quota_system=mysql.") func init() { if err := quota.RegisterProvider(QuotaManagerName, newMySQLQuotaManager); err != nil { klog.Fatalf("Failed to register quota manager %v: %v", QuotaManagerName, err) } } func newMySQLQuotaManager() (quota.Manager, error) { db, err := mysql.GetDatabase() if err != nil { return nil, err } qm := &QuotaManager{ DB: db, MaxUnsequencedRows: *maxUnsequencedRows, } klog.Info("Using MySQL QuotaManager") return qm, nil } trillian-1.6.1/quota/noop.go000066400000000000000000000040561466362047600160130ustar00rootroot00000000000000// 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 quota import ( "context" "fmt" "k8s.io/klog/v2" ) // noopManagerName represents the noop quota implementation. const noopManagerName = "noop" type noopManager struct{} func init() { if err := RegisterProvider(noopManagerName, func() (Manager, error) { return Noop(), nil }); err != nil { klog.Fatalf("Failed to register %q: %v", noopManagerName, err) } } // Noop returns a noop implementation of Manager. It allows all requests without restriction. func Noop() Manager { return &noopManager{} } func (n noopManager) GetTokens(ctx context.Context, numTokens int, specs []Spec) error { if err := validateNumTokens(numTokens); err != nil { return err } return validateSpecs(specs) } func (n noopManager) PutTokens(ctx context.Context, numTokens int, specs []Spec) error { if err := validateNumTokens(numTokens); err != nil { return err } return validateSpecs(specs) } func (n noopManager) ResetQuota(ctx context.Context, specs []Spec) error { return validateSpecs(specs) } func (n noopManager) SetupInitialQuota(ctx context.Context, treeID int64) error { return nil } func validateNumTokens(numTokens int) error { if numTokens <= 0 { return fmt.Errorf("invalid numTokens: %v (>0 required)", numTokens) } return nil } func validateSpecs(specs []Spec) error { for _, spec := range specs { switch { case spec.Group == Tree && spec.TreeID <= 0: return fmt.Errorf("invalid tree ID: %v (expected >=0)", spec.TreeID) } } return nil } trillian-1.6.1/quota/noop_test.go000066400000000000000000000056341466362047600170550ustar00rootroot00000000000000// 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 quota import ( "context" "testing" ) func TestNoop_ValidatesNumTokens(t *testing.T) { tests := []struct { desc string numTokens int wantErr bool }{ {desc: "valid1", numTokens: 1}, {desc: "valid2", numTokens: 42}, {desc: "zeroTokens", numTokens: 0, wantErr: true}, {desc: "negativeTokens", numTokens: -3, wantErr: true}, } ctx := context.Background() qm := Noop() for _, test := range tests { err := qm.GetTokens(ctx, test.numTokens, []Spec{{Group: Global, Kind: Read}}) if hasErr := err != nil; hasErr != test.wantErr { t.Errorf("%v: GetTokens() returned err = %q, wantErr = %v", test.desc, err, test.wantErr) } err = qm.PutTokens(ctx, test.numTokens, []Spec{{Group: Global, Kind: Read}}) if hasErr := err != nil; hasErr != test.wantErr { t.Errorf("%v: PutTokens() returned err = %q, wantErr = %v", test.desc, err, test.wantErr) } } } func TestNoop_ValidatesSpecs(t *testing.T) { ctx := context.Background() qm := Noop() tests := []struct { desc string specs []Spec wantErr bool }{ { desc: "validUser", specs: []Spec{{Group: User, Kind: Read, User: "dougal"}}, }, { desc: "validTree", specs: []Spec{{Group: Tree, Kind: Read, TreeID: 12345}}, }, { desc: "validGlobal", specs: []Spec{{Group: Global, Kind: Read}}, }, { desc: "validMixed", specs: []Spec{ {Group: User, Kind: Read, User: "brian"}, {Group: Tree, Kind: Read, TreeID: 12345}, {Group: Global, Kind: Read}, }, }, { desc: "noTreeID", specs: []Spec{{Group: Tree, Kind: Read}}, wantErr: true, }, { desc: "negativeTreeID", specs: []Spec{{Group: Tree, Kind: Read, TreeID: -1}}, wantErr: true, }, } for _, test := range tests { err := qm.GetTokens(ctx, 1 /* numTokens */, test.specs) if hasErr := err != nil; hasErr != test.wantErr { t.Errorf("%v: GetTokens() returned err = %q, wantErr = %v", test.desc, err, test.wantErr) } err = qm.PutTokens(ctx, 1 /* numTokens */, test.specs) if hasErr := err != nil; hasErr != test.wantErr { t.Errorf("%v: PutTokens() returned err = %q, wantErr = %v", test.desc, err, test.wantErr) } err = qm.ResetQuota(ctx, test.specs) if hasErr := err != nil; hasErr != test.wantErr { t.Errorf("%v: ResetQuota() returned err = %q, wantErr = %v", test.desc, err, test.wantErr) } } } trillian-1.6.1/quota/provider.go000066400000000000000000000033051466362047600166660ustar00rootroot00000000000000// 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 quota import ( "fmt" "sync" ) var ( qpMu sync.RWMutex qpByName map[string]NewManagerFunc ) // NewManagerFunc is the signature of a function which can be registered // to provide instances of a quota manager. type NewManagerFunc func() (Manager, error) // RegisterProvider registers a function that provides Manager instances. func RegisterProvider(name string, qp NewManagerFunc) error { qpMu.Lock() defer qpMu.Unlock() if qpByName == nil { qpByName = make(map[string]NewManagerFunc) } _, exists := qpByName[name] if exists { return fmt.Errorf("quota provider %v already registered", name) } qpByName[name] = qp return nil } // Providers returns a slice of registered quota provider names. func Providers() []string { qpMu.RLock() defer qpMu.RUnlock() r := []string{} for k := range qpByName { r = append(r, k) } return r } // NewManager returns a Manager implementation. func NewManager(name string) (Manager, error) { qpMu.RLock() defer qpMu.RUnlock() f, exists := qpByName[name] if !exists { return nil, fmt.Errorf("unknown quota system: %v", name) } return f() } trillian-1.6.1/quota/provider_test.go000066400000000000000000000042341466362047600177270ustar00rootroot00000000000000// Copyright 2018 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 quota import "testing" func TestQuotaProviderRegistration(t *testing.T) { for _, test := range []struct { desc string reg bool wantErr bool }{ { desc: "works", reg: true, wantErr: false, }, { desc: "unknown provider", reg: false, wantErr: true, }, } { t.Run(test.desc, func(t *testing.T) { called := false name := test.desc if test.reg { if err := RegisterProvider(name, func() (Manager, error) { called = true return nil, nil }); err != nil { t.Fatalf("RegisterProvider(%s)=%v", name, err) } } _, err := NewManager(name) if err != nil && !test.wantErr { t.Fatalf("NewManager = %v, want no error", err) } if err == nil && test.wantErr { t.Fatalf("NewManager = no error, want error") } if !called && !test.wantErr { t.Fatal("Registered quota provider was not called") } }) } } func TestQuotaSystems(t *testing.T) { if err := RegisterProvider("a", func() (Manager, error) { return nil, nil }); err != nil { t.Fatalf("RegisterProvider(a)=%v", err) } if err := RegisterProvider("b", func() (Manager, error) { return nil, nil }); err != nil { t.Fatalf("RegisterProvider(b)=%v", err) } qs := Providers() if got, want := len(qs), 2; got < want { t.Fatalf("Got %d names, want at least %d", got, want) } a := 0 b := 0 for _, n := range qs { if n == "a" { a++ } if n == "b" { b++ } } if a != 1 { t.Errorf("Providers() returned %d 'a', want 1", a) } if b != 1 { t.Errorf("Providers() returned %d 'b', want 1", b) } } trillian-1.6.1/quota/quota.go000066400000000000000000000065041466362047600161710ustar00rootroot00000000000000// 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 quota import ( "context" "fmt" "strings" ) // MaxTokens is the maximum number of available tokens a quota may have. const MaxTokens = int(^uint(0) >> 1) // MaxInt // Group represents the scope of a token (Global, Tree or User). type Group int const ( // Global is the Trillian-wide token scope (applies to all users and trees). // A global quota shortage for a certain kind of token means all requests of that kind will // be denied until the quota is replenished. Global Group = iota // Tree is the tree-wide token scope. Tree // User is the per-user token scope. // Users are defined according to each implementation. User ) // Kind represents the purpose of each token (Read or Write). type Kind int const ( // Read represents tokens used by non-modifying RPCs. Read Kind = iota // Write represents tokens used by modifying RPCs. Write ) // Spec represents a combination of Group and Kind, with all additional data required to get / put // tokens. type Spec struct { // Group of the spec. Group // Kind of the spec. Kind // TreeID identifies the tree for specs of the Tree group. // Not used for other specs. TreeID int64 // User identifies the user for specs of the User group. // Not used for other specs. User string // Refundable indicates that the tokens acquired before the operation should be returned if // the operation fails. Refundable bool } // Name returns a textual representation of the Spec. Names are constant and may be relied upon to // not change in the future. // // Names are created as follows: // * Global quotas are mapped to "global/read" or "global/write" // * Tree quotas are mapped to "trees/$TreeID/$Kind". E.g., "trees/10/read". // * User quotas are mapped to "users/$User/$Kind". E.g., "trees/10/read". func (s Spec) Name() string { group := strings.ToLower(fmt.Sprint(s.Group)) kind := strings.ToLower(fmt.Sprint(s.Kind)) if s.Group == Global { return fmt.Sprintf("%v/%v", group, kind) } var user string switch s.Group { case Tree: user = fmt.Sprint(s.TreeID) case User: user = s.User } return fmt.Sprintf("%vs/%v/%v", group, user, kind) } // String returns a description of Spec. func (s Spec) String() string { return s.Name() } // Manager is the component responsible for the management of tokens. type Manager interface { // GetTokens acquires numTokens from all specs. Tokens are taken in the order specified by // specs. // Returns error if numTokens could not be acquired for all specs. GetTokens(ctx context.Context, numTokens int, specs []Spec) error // PutTokens adds numTokens for all specs. PutTokens(ctx context.Context, numTokens int, specs []Spec) error // ResetQuota resets the quota for all specs. ResetQuota(ctx context.Context, specs []Spec) error } trillian-1.6.1/quota/quota_test.go000066400000000000000000000024751466362047600172330ustar00rootroot00000000000000// 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 quota import "testing" func TestSpec_Name(t *testing.T) { tests := []struct { spec Spec want string }{ {spec: Spec{Group: Global, Kind: Read}, want: "global/read"}, {spec: Spec{Group: Global, Kind: Write}, want: "global/write"}, {spec: Spec{Group: Tree, Kind: Read, TreeID: 11}, want: "trees/11/read"}, {spec: Spec{Group: Tree, Kind: Write, TreeID: 10}, want: "trees/10/write"}, {spec: Spec{Group: User, Kind: Read, User: "alpaca"}, want: "users/alpaca/read"}, {spec: Spec{Group: User, Kind: Write, User: "llama"}, want: "users/llama/write"}, } for _, test := range tests { if got := test.spec.Name(); got != test.want { t.Errorf("%#v.Name() = %v, want = %v", test.spec, got, test.want) } } } trillian-1.6.1/quota/redis/000077500000000000000000000000001466362047600156125ustar00rootroot00000000000000trillian-1.6.1/quota/redis/redisqm/000077500000000000000000000000001466362047600172565ustar00rootroot00000000000000trillian-1.6.1/quota/redis/redisqm/manager.go000066400000000000000000000106351466362047600212240ustar00rootroot00000000000000// 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 redisqm defines a Redis-based quota.Manager implementation. package redisqm import ( "context" "fmt" "github.com/google/trillian/quota" "github.com/google/trillian/quota/redis/redistb" ) // ParameterFunc is a function that should return a token bucket's parameters // for a given quota specification. type ParameterFunc func(spec quota.Spec) (capacity int, rate float64) // ManagerOptions holds the parameters for a Manager. type ManagerOptions struct { // Parameters should return the parameters for a given quota.Spec. This // value must not be nil. Parameters ParameterFunc // Prefix is a static prefix to apply to all Redis keys; this is useful // if running on a multi-tenant Redis cluster. Prefix string } // Manager implements the quota.Manager interface backed by a Redis-based token // bucket implementation. type Manager struct { tb *redistb.TokenBucket opts ManagerOptions } var _ quota.Manager = &Manager{} // RedisClient is an interface that encompasses the various methods used by // this quota.Manager, and allows selecting among different Redis client // implementations (e.g. regular Redis, Redis Cluster, sharded, etc.) type RedisClient interface { // Everything required by the redistb.RedisClient interface redistb.RedisClient } // New returns a new Redis-based quota.Manager. func New(client RedisClient, opts ManagerOptions) *Manager { tb := redistb.New(client) return &Manager{tb: tb, opts: opts} } // GetTokens implements the quota.Manager API. func (m *Manager) GetTokens(ctx context.Context, numTokens int, specs []quota.Spec) error { for _, spec := range specs { if err := m.getTokensSingle(ctx, numTokens, spec); err != nil { return err } } return nil } func (m *Manager) getTokensSingle(ctx context.Context, numTokens int, spec quota.Spec) error { capacity, rate := m.opts.Parameters(spec) // If we get back `MaxTokens` from our parameters call, this indicates // that there's no actual limit. We don't need to do anything to "get" // them; just ignore. if capacity == quota.MaxTokens { return nil } name := specName(m.opts.Prefix, spec) allowed, remaining, err := m.tb.Call( ctx, name, int64(capacity), rate, numTokens, ) if err != nil { return err } if !allowed { return fmt.Errorf("insufficient tokens on %v (%v vs %v)", name, remaining, numTokens) } return nil } // PutTokens implements the quota.Manager API. func (m *Manager) PutTokens(ctx context.Context, numTokens int, specs []quota.Spec) error { // Putting tokens into a time-based quota doesn't mean anything (since // tokens are replenished at the moment they're requested) and since // that's the only supported mechanism for this package currently, do // nothing. return nil } // ResetQuota implements the quota.Manager API. // // This function will reset every quota and return the first error encountered, // if any, but will continue trying to reset every quota even if an error is // encountered. func (m *Manager) ResetQuota(ctx context.Context, specs []quota.Spec) error { var firstErr error for _, name := range specNames(m.opts.Prefix, specs) { if err := m.tb.Reset(ctx, name); err != nil { if firstErr == nil { firstErr = err } } } return firstErr } // Load attempts to load Redis scripts used by the Manager into the Redis // cluster. // // A Manager will operate successfully if this method is not called or fails, // but a successful Load will reduce bandwidth to/from the Redis cluster // substantially. func (m *Manager) Load(ctx context.Context) error { return m.tb.Load(ctx) } func specNames(prefix string, specs []quota.Spec) []string { names := make([]string, 0, len(specs)) for _, spec := range specs { names = append(names, specName(prefix, spec)) } return names } func specName(prefix string, spec quota.Spec) string { return prefix + "trillian/" + spec.Name() } trillian-1.6.1/quota/redis/redistb/000077500000000000000000000000001466362047600172465ustar00rootroot00000000000000trillian-1.6.1/quota/redis/redistb/embed_redis.go000066400000000000000000000041541466362047600220430ustar00rootroot00000000000000//go:build ignore // +build ignore // 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. // This is a helper utility to embed the Redis Lua scripts into a Go source // file. package main import ( "bytes" "go/format" "log" "os" "strconv" "text/template" ) var packageTemplate = template.Must(template.New("").Parse(` // Code generated by quota/redis/redistb/gen.go. DO NOT EDIT. // source: {{ .Filename }} package redistb import ( "github.com/go-redis/redis" ) // contents of the '{{ .Prefix }}' Redis Lua script const {{ .Prefix }}ScriptContents = {{ .Content }} // Redis Script type for the '{{ .Prefix }}' Redis lua script var {{ .Prefix }}Script = redis.NewScript({{ .Prefix }}ScriptContents) `)) type templateData struct { Prefix string Filename string Content string } func main() { if len(os.Args) != 4 { log.Fatalf("usage: %s prefix file.lua output.go", os.Args[0]) } data, err := os.ReadFile(os.Args[2]) if err != nil { log.Fatalf("error opening input file: %v", err) } vars := templateData{ Prefix: os.Args[1], Filename: os.Args[2], Content: strconv.Quote(string(data)), } var buf bytes.Buffer if err := packageTemplate.Execute(&buf, vars); err != nil { log.Fatalf("error rendering template: %v", err) } data, err = format.Source(buf.Bytes()) if err != nil { log.Fatalf("error formatting source: %v", err) } out, err := os.Create(os.Args[3]) if err != nil { log.Fatalf("error opening output file: %v", err) } defer out.Close() if _, err := out.Write(data); err != nil { log.Fatalf("error writing output file: %v", err) } } trillian-1.6.1/quota/redis/redistb/gen.go000066400000000000000000000013341466362047600203470ustar00rootroot00000000000000// 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 redistb //go:generate go run embed_redis.go updateTokenBucket update_token_bucket.lua update_token_bucket.gen.go trillian-1.6.1/quota/redis/redistb/redistb.go000066400000000000000000000162611466362047600212370ustar00rootroot00000000000000// 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 redistb implements a token bucket using Redis. package redistb import ( "context" "fmt" "time" "github.com/go-redis/redis" "github.com/google/trillian/util/clock" ) // RedisClient is an interface that encompasses the various methods used by // TokenBucket, and allows selecting among different Redis client // implementations (e.g. regular Redis, Redis Cluster, sharded, etc.) type RedisClient interface { // Required to load and execute scripts Eval(script string, keys []string, args ...interface{}) *redis.Cmd EvalSha(sha1 string, keys []string, args ...interface{}) *redis.Cmd ScriptExists(hashes ...string) *redis.BoolSliceCmd ScriptLoad(script string) *redis.StringCmd } // TokenBucket implements a token-bucket limiter stored in a Redis database. It // supports atomic operation with concurrent access. type TokenBucket struct { c RedisClient testing bool timeSource clock.TimeSource } // New returns a new TokenBucket that uses the provided Redis client. func New(client RedisClient) *TokenBucket { ret := &TokenBucket{ c: client, timeSource: clock.System, } return ret } // Load preloads any required Lua scripts into the Redis database, and updates // the hash of the resulting script. Calling this function is optional, but // will greatly reduce the network traffic to the Redis cluster since it only // needs to pass a hash of the script and not the full script content. func (tb *TokenBucket) Load(ctx context.Context) error { client := withClientContext(ctx, tb.c) return updateTokenBucketScript.Load(client).Err() } // Reset resets the token bucket for the given prefix. func (tb *TokenBucket) Reset(ctx context.Context, prefix string) error { client := withClientContext(ctx, tb.c) // Use `EVAL` so that deleting all keys is atomic. resp := client.Eval( `redis.call("del", KEYS[1]); redis.call("del", KEYS[2]); redis.call("del", KEYS[3])`, tokenBucketKeys(prefix), ) return resp.Err() } // Call implements the actual token bucket algorithm. Given a bucket with // capacity `capacity` and replenishment rate of `replenishRate` tokens per // second, it will first ensure that the bucket has the correct number of // tokens added (up to the maximum capacity) since the last time that this // function was called. Then, it will attempt to remove `numTokens` from the // bucket. // // This function returns a boolean indicating whether it was able to remove all // tokens from the bucket, the remaining number of tokens in the bucket, and // any error that occurs. func (tb *TokenBucket) Call( ctx context.Context, prefix string, capacity int64, replenishRate float64, numTokens int, ) (bool, int64, error) { client := withClientContext(ctx, tb.c) var ( now int64 nowUs int64 ) if tb.testing { now, nowUs = timeToRedisPair(tb.timeSource.Now()) } args := []interface{}{ replenishRate, capacity, numTokens, // The script allows us to inject the current time for testing, // but it's superseded by Redis's time in production to protect // against clock drift. now, nowUs, tb.testing, } resp := updateTokenBucketScript.Run( client, tokenBucketKeys(prefix), args..., ) result, err := resp.Result() if err != nil { return false, 0, err } returnVals, ok := result.([]interface{}) if !ok { return false, 0, fmt.Errorf("redistb: invalid return type %T (expected []interface{})", result) } // The script returns: // allowed Whether the operation was allowed // remaining The remaining tokens in the bucket // now_new The script's view of the current time // now_new_us The script's view of the current time (microseconds) // // We don't use the last two arguments here. // Deserializing turns Lua 'true' into '1', and 'false' into 'nil' var allowed bool if returnVals[0] == nil { allowed = false } else if i, ok := returnVals[0].(int64); ok { allowed = i == 1 } else { return false, 0, fmt.Errorf("redistb: invalid 'allowed' type %T", returnVals[0]) } remaining := returnVals[1].(int64) return allowed, remaining, nil } // tokenBucketKeys returns the keys used for the token bucket script, given a // prefix. func tokenBucketKeys(prefix string) []string { // Redis Cluster uses a hashing algorithm on keys to determine which slot // they map to in its backend. Normally this is a problem for EVAL/EVALSHA // because multiple keys in a script will likely map to different slots // and cause Redis Cluster to reject the request. // // It's addressed with the idea of a "hash tag": // // https://redis.io/topics/cluster-tutorial#redis-cluster-data-sharding // // If a key name contains a string inside of "{}" then _just_ that string // is hashed to be used slotting purposes, thereby giving users some // control over mapping consistently to certain slots. For example, // `this{foo}key` and `another{foo}key` are guaranteed to both map to the // same slot. // // We take advantage of this idea here by making sure to hash only the // common identifier in these keys by using "{}". return []string{ fmt.Sprintf("{%s}.tokens", prefix), fmt.Sprintf("{%s}.refreshed", prefix), fmt.Sprintf("{%s}.refreshed_us", prefix), } } // timeToRedisPair converts a Go time.Time into a seconds and microseconds // component, which can be passed to our Redis script. func timeToRedisPair(t time.Time) (int64, int64) { // The first number in the pair is the number of seconds since the Unix // epoch. timeSec := t.Unix() // The second number is any additional number of microseconds; we can // get this by obtaining any sub-second Nanoseconds and simply dividing // to get the number in microseconds. timeMicros := int64(t.Nanosecond()) / int64(time.Microsecond) return timeSec, timeMicros } // Because each Redis client type in the Go package has a `WithContext` method // that returns a concrete type, we can't simply put that method in the // RedisClient interface. This method performs type assertions to try and call // the `WithContext` method on the appropriate concrete type. func withClientContext(ctx context.Context, client RedisClient) RedisClient { type withContextable interface { WithContext(context.Context) RedisClient } switch c := client.(type) { // The three major Redis clients case *redis.Client: return c.WithContext(ctx) case *redis.ClusterClient: return c.WithContext(ctx) case *redis.Ring: return c.WithContext(ctx) // Let's also support the case where someone implements a custom client // that returns the RedisClient interface type (e.g. good for tests). case withContextable: return c.WithContext(ctx) } // If we can't determine a type, just return it unchanged. return client } trillian-1.6.1/quota/redis/redistb/redistb_test.go000066400000000000000000000535121466362047600222760ustar00rootroot00000000000000//go:build integration // +build integration // 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 redistb import ( "context" "crypto/rand" "math/big" "testing" "time" "github.com/go-redis/redis" "github.com/google/trillian/util/clock" ) const ( // Total capacity of the bucket TestCapacity = 5 // Replenish rate in tokens per second (so 2 = 2 tokens/second). TestReplenishRate = 2 // The base time for our test cases. Note this is meant to be // seconds from the Unix epoch, but we're using a simplified // number here to make debugging easier. TestBaseTimeSec = 123 // The base time of microseconds within the current second // (TestBaseTimeSec). TestBaseTimeUs = 100 // The number of microseconds in a second, made a constant for // better readability (10 ** 6). MicrosecondsInSecond = 1000000 // The number of microseconds that it takes to drip a single // new token at our TestReplenishRate. SingleTokenDripTimeUs = MicrosecondsInSecond / TestReplenishRate // A common TTL we'll pass to every `SETEX` call. TTL = 60 ) // The base time as a `time.Time` object var TestBaseTime = time.Unix(TestBaseTimeSec, TestBaseTimeUs*int64(time.Microsecond/time.Nanosecond)) // This test is an end-to-end integration test for the Redis token-bucket // implementation. It exercises the actual implementation of the token bucket; // there are further unit tests below that test specific behavior of the Lua // script. func TestRedisTokenBucketIntegration(t *testing.T) { ctx := context.Background() start := time.Now() fixedTimeSource := clock.NewFake(start) rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) tb := New(rdb) tb.testing = true tb.timeSource = fixedTimeSource if err := tb.Load(ctx); err != nil { t.Fatalf("failed to load script: %v", err) } prefix := stringWithCharset(20, alphanumeric) call := func(number int) (bool, int64) { allowed, remaining, err := tb.Call( ctx, prefix, TestCapacity, TestReplenishRate, number, ) if err != nil { t.Fatal(err) } return allowed, remaining } // First, ensure that we can empty the bucket for i := int64(0); i < TestCapacity; i++ { allowed, remaining := call(1) if !allowed { t.Fatal("expected to be allowed") } if expected := TestCapacity - i - 1; remaining != expected { t.Fatalf("expected remaining to be %d, got %d", expected, remaining) } } // Within this second, all future requests should fail. allowed, remaining := call(1) if allowed { t.Fatal("expected to be denied") } if remaining != 0 { t.Fatalf("expected remaining to be 0, got %d", remaining) } singleTokenReplenishTime := 1.0 / TestReplenishRate // An arbitrary, non-zero number of iterations to ensure that this is repeatable. for i := 0; i < 5; i++ { // This is the perfect amount of time to get exactly one more token replenished. timeStep := float64(i+1) * singleTokenReplenishTime // Freeze time *just* before a new token would enter the bucket // to verify that requests are still blocked. 0.01s is an // arbitrary number chosen to be "small enough" and yet not run // into precision problems. justBefore := start justBefore = justBefore.Add(time.Duration(timeStep * float64(time.Second))) justBefore = justBefore.Add(-1 * time.Second / 100) fixedTimeSource.Set(justBefore) allowed, remaining := call(1) if allowed { t.Fatalf("expected request before reaching replenish time to be denied on iteration %d", i) } if remaining != 0 { t.Fatalf("expected remaining to be 0, got %d", remaining) } // Freeze time at precisely the right moment that a token has // entered the bucket. fixedTimeSource.Set(start.Add(time.Duration(timeStep * float64(time.Second)))) // A single request is allowed allowed, remaining = call(1) if allowed { t.Fatalf("expected first request to be allowed on iteration %d", i) } if remaining != 0 { t.Fatalf("expected remaining to be 0, got %d", remaining) } // Requests are blocked again now that we've used the token. allowed, remaining = call(1) if allowed { t.Fatalf("expected second request to be denied on iteration %d", i) } if remaining != 0 { t.Fatalf("expected remaining to be 0, got %d", remaining) } } } func TestRedisTokenBucketCases(t *testing.T) { rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) // TestBaseTime as a time.Duration from the Unix epoch TestBaseTimeDelta := (TestBaseTimeSec * time.Second) + (TestBaseTimeUs * time.Microsecond) tests := []struct { Name string // Initial number of tokens in the bucket SkipInitialSetup bool InitialTime time.Time InitialTokens int // Arguments to the call ArgTokens int TimeDelta time.Duration // Expected results Allowed bool TokensLeft int64 ReturnedTime time.Time // defaults to InitialTime + TimeDelta if zero }{ { Name: "new request with tokens left", // We have one token in the bucket InitialTokens: 1, InitialTime: TestBaseTime, // We take one token from the bucket now ArgTokens: 1, // It worked, and none are left Allowed: true, TokensLeft: 0, }, { Name: "new request where no values have been previously set", // The bucket has not been initialized SkipInitialSetup: true, // We take one token from the bucket now ArgTokens: 1, TimeDelta: TestBaseTimeDelta, // It worked, and the bucket is full minus the token we just took Allowed: true, TokensLeft: TestCapacity - 1, }, { Name: "allows for a new request where values were set a long time ago", // The bucket is empty as of a "long time ago" InitialTokens: 0, InitialTime: time.Time{}, // We take one token from the bucket now ArgTokens: 1, TimeDelta: TestBaseTimeDelta, // It worked, and the bucket is full minus the token we just took Allowed: true, TokensLeft: TestCapacity - 1, }, { Name: "allows for a new request that goes back in time but leaves existing values unchanged", // We have one token in the bucket InitialTokens: 1, InitialTime: TestBaseTime, // "Something happens", and we go back in time while taking a token ArgTokens: 1, TimeDelta: -TestBaseTimeDelta, // We can take a token Allowed: true, TokensLeft: 0, // The values in Redis did not go back in time ReturnedTime: TestBaseTime, }, { Name: "disallows for a new request at same moment without tokens left", // The bucket has no tokens in it InitialTokens: 0, InitialTime: TestBaseTime, // We take one token ArgTokens: 1, // We cannot get a token Allowed: false, TokensLeft: 0, }, { Name: "allows for a new request without tokens left but time to replenish", // The bucket has no tokens in it InitialTokens: 0, InitialTime: TestBaseTime, // We take one token at exactly the moment that one has // entered the bucket ArgTokens: 1, TimeDelta: SingleTokenDripTimeUs * time.Microsecond, // It worked, and the bucket is empty again at the new // time Allowed: true, TokensLeft: 0, }, { Name: "similarly scales up if more time than necessary has passed", // The bucket has no tokens in it InitialTokens: 0, InitialTime: TestBaseTime, // We take one token, at the time where the bucket is almost full ArgTokens: 1, TimeDelta: (TestCapacity - 1) * SingleTokenDripTimeUs * time.Microsecond, // It worked, and the bucket contains the expected // number of tokens, minus one for the token we just // took. Allowed: true, TokensLeft: (TestCapacity - 1) - 1, }, { Name: "maxes out at the bucket's capacity", // The bucket has no tokens in it InitialTokens: 0, InitialTime: TestBaseTime, // We take one token at the time where one more token // has been added to the bucket than should fit ArgTokens: 1, TimeDelta: (TestCapacity + 1) * SingleTokenDripTimeUs * time.Microsecond, // It worked, and the bucket contains one token less // than the capacity, since it capped at the capacity // and we just took one Allowed: true, TokensLeft: TestCapacity - 1, }, { Name: "allows and leaves values unchanged when requesting 0 tokens", // We have one token in the bucket InitialTokens: 1, InitialTime: TestBaseTime, // Take 0 tokens ArgTokens: 0, // It worked, and nothing is changed Allowed: true, TokensLeft: 1, }, { Name: "allows and leaves values unchanged when requesting 0 tokens at 0 remaining", // We have one token in the bucket InitialTokens: 0, InitialTime: TestBaseTime, // Take 0 tokens ArgTokens: 0, // It worked, and nothing is changed Allowed: true, TokensLeft: 0, }, { Name: "denies and returns remaining tokens when requesting more than one", // We have one token in the bucket InitialTokens: 1, InitialTime: TestBaseTime, // Take 2 tokens ArgTokens: 2, // It worked, nothing is changed, and we can see the // number of tokens in the bucket Allowed: false, TokensLeft: 1, }, // Smoke test to ensure that real timestamps are supported { Name: "works with real timestamps", InitialTokens: 0, InitialTime: time.Now(), // We take one token from the bucket "just after" it // has been added to the bucket ArgTokens: 1, TimeDelta: SingleTokenDripTimeUs * time.Microsecond, // It worked, and the bucket is empty again Allowed: true, TokensLeft: 0, }, } for _, test := range tests { t.Run(test.Name, func(t *testing.T) { keys := makeKeys() // Go's zero time starts before the Unix epoch; return the Unix epoch // if it's not specified if test.InitialTime.IsZero() { test.InitialTime = time.Unix(0, 0) } // First, set the initial values in the database if !test.SkipInitialSetup { mustInitKeys(t, rdb, keys, int64(test.InitialTokens), test.InitialTime) } // Calculate the time argument argTimeSec, argTimeUs := timeToRedisPair(test.InitialTime.Add(test.TimeDelta)) // Next, call the script resp := updateTokenBucketScript.Eval( rdb, keys.AsSlice(), // Args TestReplenishRate, TestCapacity, test.ArgTokens, argTimeSec, argTimeUs, "true", ) // The expected returned time from Redis defaults to // initial time plus the time delta unless one is // explicitly specified. var expectedReturnedTime, expectedReturnedTimeUs int64 if test.ReturnedTime.IsZero() { expectedReturnedTime, expectedReturnedTimeUs = timeToRedisPair( test.InitialTime.Add(test.TimeDelta), ) } else { expectedReturnedTime, expectedReturnedTimeUs = timeToRedisPair(test.ReturnedTime) } // Use our helper function that deserializes the // results and runs assertions assertRedisResults(t, resp, test.Allowed, test.TokensLeft, expectedReturnedTime, expectedReturnedTimeUs, ) }) } } // Ensure that in the case where a request is made at a time between two tokens // entering the bucket, that time is "returned to the user" and can be used in // a future request. func TestReturnedTimeMicroseconds(t *testing.T) { rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) // Unlike other tests, start at 0us to make this easier to read refreshedTime := time.Unix(TestBaseTimeSec, 0) keys := makeKeys() mustInitKeys(t, rdb, keys, 0, refreshedTime) // The extra 100us between the refresh time and the current time isn't // enough to increment a new token, so it's returned to the user as the // script sets the new values for the refreshed keys. const extraTime = 100 argTime := refreshedTime.Add((SingleTokenDripTimeUs + extraTime) * time.Microsecond) argTimeSec, argTimeUs := timeToRedisPair(argTime) resp := updateTokenBucketScript.Eval( rdb, keys.AsSlice(), // Args TestReplenishRate, TestCapacity, 1, argTimeSec, argTimeUs, "true", ) assertRedisResults(t, resp, true, 0, argTimeSec, // The microseconds component stored in the database should // return any extra time to the caller for use with the next // token. This manifests itself as the time in the database // being in the past by the extra time, such that the next call // to this script can use the extra time. argTimeUs-extraTime, ) } func TestReturnedTimeAcrossSecondBoundary(t *testing.T) { rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) // Set the initial refreshed microsecond number right at the second // boundary so that adding extra time below will push us over. const refreshedTimeUs = MicrosecondsInSecond - 10 refreshedTime := time.Unix(TestBaseTimeSec, refreshedTimeUs*int64(time.Microsecond/time.Nanosecond)) keys := makeKeys() mustInitKeys(t, rdb, keys, 0, refreshedTime) // We call the token bucket at a time that's `extraTime` after the last // refresh time, but without enough time having passed such that a // token has entered the bucket. Due to the refreshed time above, this // crosses a second boundary. const extraTime = 100 argTime := time.Unix(TestBaseTimeSec, 0) argTime = argTime.Add((refreshedTimeUs + extraTime) * time.Microsecond) argTimeSec, argTimeUs := timeToRedisPair(argTime) // Sanity-check that we did actually cross a second boundary if refreshedTime.Unix() != argTime.Unix()-1 { t.Errorf("expected refreshed time to be 1 second before the argument time: %d != %d - 1", refreshedTime.Unix(), argTime.Unix()-1, ) } resp := updateTokenBucketScript.Eval( rdb, keys.AsSlice(), // Args TestReplenishRate, TestCapacity, 1, argTimeSec, argTimeUs, "true", ) assertRedisResults(t, resp, false, 0, // We "subtracted" a second to add to the microseconds // component argTimeSec-1, // The microseconds component includes the extra time // subtracted from above MicrosecondsInSecond+(argTimeUs-extraTime), ) // This case is a little complicated to get right, so make sure that // sure that waiting the difference between `SingleTokenDripTimeUs` // and `extra_time` does indeed give us one more token. argTimeUs += SingleTokenDripTimeUs - extraTime resp = updateTokenBucketScript.Eval( rdb, keys.AsSlice(), // Args TestReplenishRate, TestCapacity, 1, argTimeSec, argTimeUs, "true", ) assertRedisResults(t, resp, true, 0, argTimeSec, argTimeUs, ) } // Ensure that the token bucket works with large replenishment rates. func TestHighReplenishRate(t *testing.T) { rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) keys := makeKeys() mustInitKeys(t, rdb, keys, 0, TestBaseTime) const ( tokensToAdd = 10 testReplenishRate = 5000 testCapacity = 5000 singleTokenReplenishTime = MicrosecondsInSecond / testReplenishRate ) argTime := TestBaseTime.Add(singleTokenReplenishTime * tokensToAdd * time.Microsecond) argTimeSec, argTimeUs := timeToRedisPair(argTime) resp := updateTokenBucketScript.Eval( rdb, keys.AsSlice(), // Args testReplenishRate, testCapacity, 1, argTimeSec, argTimeUs, "true", ) assertRedisResults(t, resp, true, tokensToAdd-1, argTimeSec, argTimeUs, ) } // Ensure that the token bucket works with very low (i.e. less than one // token/second) replenishment rates. func TestLowReplenishRate(t *testing.T) { rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) keys := makeKeys() mustInitKeys(t, rdb, keys, 0, TestBaseTime) const testReplenishRate = 0.5 // At a replenish rate of 0.5 it takes 2 seconds to get a full token // added. Here we try to make a request at 1 second. We won't be able // to make the request, but the script will subtract one second from // the time it stores which will permit us to make the request in one // more second. argTimeSec, argTimeUs := timeToRedisPair(TestBaseTime.Add(1 * time.Second)) resp := updateTokenBucketScript.Eval( rdb, keys.AsSlice(), // Args testReplenishRate, TestCapacity, 1, argTimeSec, argTimeUs, "true", ) assertRedisResults(t, resp, false, 0, argTimeSec-1, argTimeUs, ) // Given one more second, the request is allowed. argTimeSec, argTimeUs = timeToRedisPair(TestBaseTime.Add(2 * time.Second)) resp = updateTokenBucketScript.Eval( rdb, keys.AsSlice(), // Args testReplenishRate, TestCapacity, 1, argTimeSec, argTimeUs, "true", ) assertRedisResults(t, resp, true, 0, argTimeSec, argTimeUs, ) } // Ensure that the token bucket works if we unexpectedly change the bucket size // and replenish rates. func TestChangingReplenishRate(t *testing.T) { rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) tests := []struct { Name string Requested int64 Expected int64 }{ // The bucket has been "shrunk" to fit the new size { Name: "request one token", Requested: 1, Expected: TestCapacity - 1, }, // The bucket will be shrunk to its' maximum capacity even if // we don't request anything { Name: "request no tokens", Requested: 0, Expected: TestCapacity, }, } for _, test := range tests { t.Run(test.Name, func(t *testing.T) { keys := makeKeys() mustInitKeys(t, rdb, keys, TestCapacity*5, TestBaseTime) argTimeSec, argTimeUs := timeToRedisPair(TestBaseTime) resp := updateTokenBucketScript.Eval( rdb, keys.AsSlice(), // Args TestReplenishRate, TestCapacity, // smaller than above test.Requested, argTimeSec, argTimeUs, "true", ) assertRedisResults(t, resp, true, test.Expected, argTimeSec, argTimeUs, ) }) } } func TestErrorIfMicrosecondsTooLarge(t *testing.T) { rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) keys := makeKeys() resp := updateTokenBucketScript.Eval( rdb, keys.AsSlice(), // Args TestReplenishRate, TestCapacity, 1, TestBaseTimeSec, MicrosecondsInSecond, "true", ) err := resp.Err() if err == nil { t.Fatalf("expected an error, but got none") } if err.Error() != `now_us must be smaller than 10^6 (microseconds in a second)` { t.Errorf("invalid error message: %s", err.Error()) } } const alphanumeric = "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789" func stringWithCharset(length int, charset string) string { setlen := big.NewInt(int64(len(charset))) b := make([]byte, length) for i := range b { ch, err := rand.Int(rand.Reader, setlen) if err != nil { panic(err) } b[i] = charset[ch.Int64()] } return string(b) } // Wrapper type for the keys we use in our call to the script. type redisKeys struct { Prefix string Tokens string Refreshed string RefreshedUs string } // Get the keys in a slice format for passing to Eval func (r redisKeys) AsSlice() []string { return []string{r.Tokens, r.Refreshed, r.RefreshedUs} } // Helper function to create Redis key names wrapper type func makeKeys() redisKeys { var ret redisKeys // Ensure each test ends up under a different key ret.Prefix = stringWithCharset(20, alphanumeric) // Use same key generation function as the live code keys := tokenBucketKeys(ret.Prefix) ret.Tokens = keys[0] ret.Refreshed = keys[1] ret.RefreshedUs = keys[2] return ret } // Helper function to initialize Redis keys to given values func mustInitKeys(t *testing.T, c *redis.Client, keys redisKeys, tokens int64, refreshed time.Time) { t.Helper() refreshedSec, refreshedUs := timeToRedisPair(refreshed) if err := c.Set(keys.Tokens, tokens, TTL*time.Second).Err(); err != nil { t.Fatalf("failed to set initial tokens: %v", err) } if err := c.Set(keys.Refreshed, refreshedSec, TTL*time.Second).Err(); err != nil { t.Fatalf("failed to set refreshed time: %v", err) } if err := c.Set(keys.RefreshedUs, refreshedUs, TTL*time.Second).Err(); err != nil { t.Fatalf("failed to set refreshed time (us): %v", err) } } // Helper function that deserializes the returned values from our Redis script. func deserializeRedisResults(t *testing.T, resp *redis.Cmd) (bool, int64, int64, int64) { t.Helper() results, err := resp.Result() if err != nil { t.Fatalf("error calling script: %v", err) } // Deserialize results returnVals, ok := results.([]interface{}) if !ok { t.Fatalf("invalid return type %T (expected []interface{})", results) } var allowed bool if returnVals[0] == nil { allowed = false } else if i, ok := returnVals[0].(int64); ok { allowed = i == 1 } else { t.Fatalf("invalid 'allowed' type %T", returnVals[0]) } remaining := returnVals[1].(int64) returnedTime := returnVals[2].(int64) returnedTimeUs := returnVals[3].(int64) return allowed, remaining, returnedTime, returnedTimeUs } // Helper function that deserializes the returned values from our Redis script, // and then asserts that they match the expected values provided. func assertRedisResults(t *testing.T, resp *redis.Cmd, allowed bool, remaining, returnedTime, returnedTimeUs int64) { t.Helper() actualAllowed, actualRemaining, actualReturnedTime, actualReturnedTimeUs := deserializeRedisResults(t, resp) if allowed != actualAllowed { t.Errorf("expected 'allowed' to be %t but got %t", allowed, actualAllowed) } if remaining != actualRemaining { t.Errorf("expected 'remaining' to be %d but got %d", remaining, actualRemaining) } if returnedTime != actualReturnedTime { t.Errorf("expected returned time to be %d but got %d", returnedTime, actualReturnedTime) } if returnedTimeUs != actualReturnedTimeUs { t.Errorf("expected returned time (us) to be %d but got %d", returnedTimeUs, actualReturnedTimeUs) } } trillian-1.6.1/quota/redis/redistb/update_token_bucket.gen.go000066400000000000000000000177141466362047600243760ustar00rootroot00000000000000// Code generated by quota/redis/redistb/gen.go. DO NOT EDIT. // source: update_token_bucket.lua package redistb import ( "github.com/go-redis/redis" ) // contents of the 'updateTokenBucket' Redis Lua script const updateTokenBucketScriptContents = "--[[\n\nLICENSE\n===================\n\nCopyright 2017 Google LLC. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\nTOKEN BUCKET\n===================\n\nScript to read and update a token bucket maintained in Redis. This is an\nimplementation of the token bucket algorithm which is a common fixture seen in\nrate limiting:\n\n https://en.wikipedia.org/wiki/Token_bucket\n\nFor each key prefix, we maintain three values:\n\n * `.tokens`: Number of tokens in bucket at refresh time.\n\n * `.refreshed`: Time in epoch seconds when this prefix's bucket was\n last updated.\n\n * `.refreshed_us`: The microsecond component of the last updated\n time above. Stored separately because a Unix epoch with a microsecond\n component brushes up uncomfortably close to integer boundaries.\n\nThe basic strategy is to, at update/read time, fill in all tokens\nthat would have accumulated since the last update, and then if\npossible deduct the number of requested tokens (or disallow the\nrequested action if there are not enough tokens).\n\nThe approach relies on the atomicity of EVAL in redis - only 1 command (EVAL or\notherwise) will be running concurrently per shard in the Redis cluster. Redis\nand Lua are very fast, so in practice this works out okay.\n\nA note on units: all times (instants) are measured in epoch seconds with a\nseparate microsecond component, durations in imicroseconds, and rates in\ntokens/second (e.g., a rate of 100 is 100 tokens/second).\n\nFor debugging, I'd recommend adding Redis log statements and then tailing your\nRedis log. Example:\n\n redis.log(redis.LOG_WARNING, string.format(\"rate = %s\", rate))\n\n--]]\n\n--\n-- Constants\n--\n-- Lua doesn't actually have constants, so these are constants by convention\n-- only. Please don't modify them.\n--\n\nlocal MICROSECONDS_IN_SECOND = 1000000.0\n\n--\n-- Functions\n--\n\nlocal function subtract_time (base, base_us, leftover_time_us)\n base = base - math.floor(leftover_time_us / MICROSECONDS_IN_SECOND)\n\n leftover_time_us = leftover_time_us % MICROSECONDS_IN_SECOND\n\n base_us = base_us - leftover_time_us\n if base_us < 0 then\n base = base - 1\n base_us = MICROSECONDS_IN_SECOND + base_us\n end\n\n return base, base_us\nend\n\n--\n-- Keys and arguments\n--\n\nlocal key_tokens = KEYS[1]\n\n-- Unix time since the epoch in microseconds runs up uncomfortably close to\n-- integer boundaries, so we store time as two separate components: (1) seconds\n-- since epoch, and (2) microseconds with the current second.\nlocal key_refreshed = KEYS[2]\nlocal key_refreshed_us = KEYS[3]\n\nlocal rate = tonumber(ARGV[1])\nlocal capacity = tonumber(ARGV[2])\nlocal requested = tonumber(ARGV[3])\n\n-- Callers are allowed to inject the current time into the script, but note\n-- that outside of testing, this will always superseded by the time reported by\n-- the Redis instance so as to protect against clock drift on any particular\n-- local node.\nlocal now = tonumber(ARGV[4])\nlocal now_us = tonumber(ARGV[5])\n\n-- This is ugly, but all values passed in from Ruby get converted to strings\nlocal testing = ARGV[6] == \"true\"\n\n--\n-- Program body\n--\n\n-- See comment above.\nif testing then\n if now_us >= MICROSECONDS_IN_SECOND then\n return redis.error_reply(\"now_us must be smaller than 10^6 (microseconds in a second)\")\n end\nelse\n -- Scripts in Redis are pure functions by default which allows Redis to\n -- replicate the entire script rather than the individual commands that it\n -- contains. Because we're about to invoke `TIME` which produces a\n -- non-deterministic result, we need to tell Redis to instead switch to\n -- command-level replication for write operations. It will error if we\n -- don't.\n redis.replicate_commands()\n\n local current_time = redis.call(\"TIME\")\n\n -- Redis `TIME` comes back in two components: (1) seconds since epoch, and\n -- (2) microseconds within the current second.\n now = tonumber(current_time[1])\n now_us = tonumber(current_time[2])\nend\n\nlocal filled_tokens = capacity\n\nlocal last_tokens = redis.call(\"GET\", key_tokens)\n\nlocal last_refreshed = redis.call(\"GET\", key_refreshed)\n\nlocal last_refreshed_us = redis.call(\"GET\", key_refreshed_us)\n\n-- Only bother performing rate calculations if we actually need to. i.e., The\n-- user has made a request recently enough to still be in the system.\nif last_tokens and last_refreshed then\n last_tokens = tonumber(last_tokens)\n last_refreshed = tonumber(last_refreshed)\n\n -- Rejected a `now` that reads before our recorded `last_refreshed` time.\n -- No reversed deltas are allowed.\n if now < last_refreshed then\n now = last_refreshed\n now_us = last_refreshed_us\n end\n\n local delta = now - last_refreshed\n local delta_us = delta * MICROSECONDS_IN_SECOND + (now_us - last_refreshed_us)\n\n -- The time (in microseconds) that it takes to \"drip\" a single token. For\n -- example, if our rate is 100 tokens per second, then one token is allowed\n -- every 10^6 / 100 = 10,000 microseconds.\n local single_token_time_us = math.floor(MICROSECONDS_IN_SECOND / rate)\n\n local new_tokens = math.floor(delta_us / single_token_time_us)\n filled_tokens = math.min(capacity, last_tokens + new_tokens)\n\n -- For maximum fairness, modify the last refresh time by any leftover time\n -- that didn't go towards adding a token.\n --\n -- However, only bother with this if the bucket hasn't been replenished to\n -- full capacity. If it was, the user has had more replenishment time than\n -- they can use anyway.\n if filled_tokens ~= capacity then\n local leftover_time_us = delta_us % single_token_time_us\n now, now_us = subtract_time(now, now_us, leftover_time_us)\n end\nend\n\nlocal allowed = filled_tokens >= requested\nlocal new_tokens = filled_tokens\nif allowed then\n new_tokens = filled_tokens - requested\nend\n\n-- Set a TTL on the values we set in Redis that will expire them after the\n-- point in time they would have been fully replenished, which allows us to\n-- manage space more efficiently by removing keys that don't need to be in\n-- there.\n--\n-- Keys that are ~always in use because their owners make frequent requests\n-- will be updated by this script constantly (which sets new TTLs), and\n-- never expire.\nlocal fill_time = math.ceil(capacity / rate)\nlocal ttl = math.floor(fill_time * 2)\n\n-- Redis will reject a expiry of 0 to `SETEX`, so make sure TTL is always at\n-- least 1.\nttl = math.max(ttl, 1)\n\n-- In our tests we freeze time. Because we can't freeze Redis' notion of time\n-- and want to make sure that keys we set within test cases don't expire, we\n-- forego the standard TTL that we would have set for just a long one to make\n-- sure anything we set expires well after the test case will have finished.\nif testing then\n ttl = 3600\nend\n\nredis.call(\"SETEX\", key_tokens, ttl, new_tokens)\nredis.call(\"SETEX\", key_refreshed, ttl, now)\nredis.call(\"SETEX\", key_refreshed_us, ttl, now_us)\n\nreturn { allowed, new_tokens, now, now_us }\n" // Redis Script type for the 'updateTokenBucket' Redis lua script var updateTokenBucketScript = redis.NewScript(updateTokenBucketScriptContents) trillian-1.6.1/quota/redis/redistb/update_token_bucket.lua000066400000000000000000000165111466362047600237740ustar00rootroot00000000000000--[[ LICENSE =================== 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. TOKEN BUCKET =================== Script to read and update a token bucket maintained in Redis. This is an implementation of the token bucket algorithm which is a common fixture seen in rate limiting: https://en.wikipedia.org/wiki/Token_bucket For each key prefix, we maintain three values: * `.tokens`: Number of tokens in bucket at refresh time. * `.refreshed`: Time in epoch seconds when this prefix's bucket was last updated. * `.refreshed_us`: The microsecond component of the last updated time above. Stored separately because a Unix epoch with a microsecond component brushes up uncomfortably close to integer boundaries. The basic strategy is to, at update/read time, fill in all tokens that would have accumulated since the last update, and then if possible deduct the number of requested tokens (or disallow the requested action if there are not enough tokens). The approach relies on the atomicity of EVAL in redis - only 1 command (EVAL or otherwise) will be running concurrently per shard in the Redis cluster. Redis and Lua are very fast, so in practice this works out okay. A note on units: all times (instants) are measured in epoch seconds with a separate microsecond component, durations in imicroseconds, and rates in tokens/second (e.g., a rate of 100 is 100 tokens/second). For debugging, I'd recommend adding Redis log statements and then tailing your Redis log. Example: redis.log(redis.LOG_WARNING, string.format("rate = %s", rate)) --]] -- -- Constants -- -- Lua doesn't actually have constants, so these are constants by convention -- only. Please don't modify them. -- local MICROSECONDS_IN_SECOND = 1000000.0 -- -- Functions -- local function subtract_time (base, base_us, leftover_time_us) base = base - math.floor(leftover_time_us / MICROSECONDS_IN_SECOND) leftover_time_us = leftover_time_us % MICROSECONDS_IN_SECOND base_us = base_us - leftover_time_us if base_us < 0 then base = base - 1 base_us = MICROSECONDS_IN_SECOND + base_us end return base, base_us end -- -- Keys and arguments -- local key_tokens = KEYS[1] -- Unix time since the epoch in microseconds runs up uncomfortably close to -- integer boundaries, so we store time as two separate components: (1) seconds -- since epoch, and (2) microseconds with the current second. local key_refreshed = KEYS[2] local key_refreshed_us = KEYS[3] local rate = tonumber(ARGV[1]) local capacity = tonumber(ARGV[2]) local requested = tonumber(ARGV[3]) -- Callers are allowed to inject the current time into the script, but note -- that outside of testing, this will always superseded by the time reported by -- the Redis instance so as to protect against clock drift on any particular -- local node. local now = tonumber(ARGV[4]) local now_us = tonumber(ARGV[5]) -- This is ugly, but all values passed in from Ruby get converted to strings local testing = ARGV[6] == "true" -- -- Program body -- -- See comment above. if testing then if now_us >= MICROSECONDS_IN_SECOND then return redis.error_reply("now_us must be smaller than 10^6 (microseconds in a second)") end else -- Scripts in Redis are pure functions by default which allows Redis to -- replicate the entire script rather than the individual commands that it -- contains. Because we're about to invoke `TIME` which produces a -- non-deterministic result, we need to tell Redis to instead switch to -- command-level replication for write operations. It will error if we -- don't. redis.replicate_commands() local current_time = redis.call("TIME") -- Redis `TIME` comes back in two components: (1) seconds since epoch, and -- (2) microseconds within the current second. now = tonumber(current_time[1]) now_us = tonumber(current_time[2]) end local filled_tokens = capacity local last_tokens = redis.call("GET", key_tokens) local last_refreshed = redis.call("GET", key_refreshed) local last_refreshed_us = redis.call("GET", key_refreshed_us) -- Only bother performing rate calculations if we actually need to. i.e., The -- user has made a request recently enough to still be in the system. if last_tokens and last_refreshed then last_tokens = tonumber(last_tokens) last_refreshed = tonumber(last_refreshed) -- Rejected a `now` that reads before our recorded `last_refreshed` time. -- No reversed deltas are allowed. if now < last_refreshed then now = last_refreshed now_us = last_refreshed_us end local delta = now - last_refreshed local delta_us = delta * MICROSECONDS_IN_SECOND + (now_us - last_refreshed_us) -- The time (in microseconds) that it takes to "drip" a single token. For -- example, if our rate is 100 tokens per second, then one token is allowed -- every 10^6 / 100 = 10,000 microseconds. local single_token_time_us = math.floor(MICROSECONDS_IN_SECOND / rate) local new_tokens = math.floor(delta_us / single_token_time_us) filled_tokens = math.min(capacity, last_tokens + new_tokens) -- For maximum fairness, modify the last refresh time by any leftover time -- that didn't go towards adding a token. -- -- However, only bother with this if the bucket hasn't been replenished to -- full capacity. If it was, the user has had more replenishment time than -- they can use anyway. if filled_tokens ~= capacity then local leftover_time_us = delta_us % single_token_time_us now, now_us = subtract_time(now, now_us, leftover_time_us) end end local allowed = filled_tokens >= requested local new_tokens = filled_tokens if allowed then new_tokens = filled_tokens - requested end -- Set a TTL on the values we set in Redis that will expire them after the -- point in time they would have been fully replenished, which allows us to -- manage space more efficiently by removing keys that don't need to be in -- there. -- -- Keys that are ~always in use because their owners make frequent requests -- will be updated by this script constantly (which sets new TTLs), and -- never expire. local fill_time = math.ceil(capacity / rate) local ttl = math.floor(fill_time * 2) -- Redis will reject a expiry of 0 to `SETEX`, so make sure TTL is always at -- least 1. ttl = math.max(ttl, 1) -- In our tests we freeze time. Because we can't freeze Redis' notion of time -- and want to make sure that keys we set within test cases don't expire, we -- forego the standard TTL that we would have set for just a long one to make -- sure anything we set expires well after the test case will have finished. if testing then ttl = 3600 end redis.call("SETEX", key_tokens, ttl, new_tokens) redis.call("SETEX", key_refreshed, ttl, now) redis.call("SETEX", key_refreshed_us, ttl, now_us) return { allowed, new_tokens, now, now_us } trillian-1.6.1/scripts/000077500000000000000000000000001466362047600150425ustar00rootroot00000000000000trillian-1.6.1/scripts/check_license.sh000077500000000000000000000015701466362047600201630ustar00rootroot00000000000000#!/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 "$@" trillian-1.6.1/scripts/gcb2slack/000077500000000000000000000000001466362047600166755ustar00rootroot00000000000000trillian-1.6.1/scripts/gcb2slack/.gcloudignore000066400000000000000000000010011466362047600213470ustar00rootroot00000000000000# This file specifies files that are *not* uploaded to Google Cloud Platform # using gcloud. It follows the same syntax as .gitignore, with the addition of # "#!include" directives (which insert the entries of the given .gitignore-style # file at that point). # # For more information, run: # $ gcloud topic gcloudignore # .gcloudignore # If you would like to upload your .git directory, .gitignore file or files # from your .gitignore file, remove the corresponding line # below: .git .gitignore node_modules trillian-1.6.1/scripts/gcb2slack/.gitignore000066400000000000000000000000171466362047600206630ustar00rootroot00000000000000/node_modules/ trillian-1.6.1/scripts/gcb2slack/README.md000066400000000000000000000031531466362047600201560ustar00rootroot00000000000000# Slack Notifications for Google Cloud Build > This is not an officially supported Google product. This Google Cloud Function sends a notification to a Slack channel whenever a Google Cloud Build completes. ![Success notification](success.png) ![Failure notification](failure.png) ## Installation ### Create a Slack Webhook Follow [Slack's "Getting Started with Incoming Webhooks" instructions](https://api.slack.com/messaging/webhooks) to create a Slack app and incoming webhook URL. This URL will be required in the next installation step. ### Deploy Google Cloud Function If you don't already have a Google Cloud project, [create one now](https://cloud.google.com/resource-manager/docs/creating-managing-projects). Ensure that you have the [Google Cloud SDK](https://cloud.google.com/sdk/) installed as well. Run the following command to deploy this Google Cloud Function, after changing `$PROJECT` to your [Google Cloud project ID](https://cloud.google.com/resource-manager/docs/creating-managing-projects#identifying_projects) and `$SLACK_WEBHOOK_URL` to the webhook URL you created in the previous installation step. You may also customise `$FUNC_NAME` and `$STAGE_BUCKET`. ```shell # Required parameters $ PROJECT="YOUR-GOOGLE-CLOUD-PROJECT-ID" $ SLACK_WEBHOOK_URL="YOUR-SLACK-WEBHOOK-URL" # Customisable parameters $ FUNC_NAME="cloudbuild2slack" $ STAGE_BUCKET="${PROJECT}-cloudbuild2slack" $ gcloud --project="${PROJECT}" functions deploy "${FUNC_NAME}" \ --stage-bucket="${STAGE_BUCKET}" \ --trigger-topic="cloud-builds" \ --runtime="nodejs10" \ --set-env-vars="SLACK_WEBHOOK_URL=${SLACK_WEBHOOK_URL}" ``` trillian-1.6.1/scripts/gcb2slack/failure.png000066400000000000000000000723311466362047600210400ustar00rootroot00000000000000‰PNG  IHDR@ĩˇZ@ĩˆzTXtRaw profile type exifxÚUŽQ„0D˙9Åa ĘqVSoāņĨ[7ęû€É¤}úąoô0iõfa†DCƒŋ&F;įäÚR2ņ]“đ ÍĄ÷CŊú?UŦŲæęnÕV[9íÜE~3˙Ұbœ 05ōî—ú–Ķ D,K?ũÁ¯ iTXtXML:com.adobe.xmp (5m­sBIT|dˆ IDATxÚėŨ}\ÛõŊ÷ņĐō‹Z“Š ŊY‚ēįžŗ¤ž ՍÔí@ëNĶēĶĐíz]“v×ę&07`įāŲ€ŨĐîpĩŅŗŨՆšBĪY…švւž.yœY2dĶ&ŗJZˇDOͯHšūˇ--Đ;ī>ĪĮ6Éī—ožŋ›ŧķų~“_ŌČČČB!„ī#ÉŌB!„$„B!H!„BB!„ !„B @B!„€„B!$aō~uˆ¯tE§ÜJ~­H!„IŌÛũCˆ§F 9iâßÛ_ŖíĀ04BjÚ<~žūZîúđAi’’dɡĮ›ožI,ãäɓ—dũķæÍãøW\q…tļBŧĐéÁgÛÁ7(˙Ųk0 ×-žĪ•ķāå×O1âĻ›4<ųų…uķ! $Ū/ŋü2ÃÃ×ô9RSSY´h‘tļB\B—}l,n…Ÿ˙|QåŠ}™ōĮŽŗāÚy¤S™Ÿ 'OÁĸÉ?¤đ‡W‡H˙ڟųėŽ×ŠmRˆēhâ!zÛjŲäXÁ˛Ė 2Ė,ŗ¯aS­›ŪPü2ö—ōĖtŌlę8˙įôy¨-]à [Vâĩ䌾–A€(žÂ Ōé,ĢęŊ„¯Ĩ‹Rsâĩ”vMsw+é¤OúË0gą<ŋ€Ō†.fũōp/'Ëļ†ZotV}8в2ņ|ų­ Ėá]ęđ\˛ę’Bˆ ķ.wøžú뛧ČûÉ1ūëw'HÖĪ'ũF…ĄáNŒ‡œS#0<Æëæqō°ëé7Øõ_oĐt÷ĩ”ßž`uĶÕnû:îÃ'TR¯[Bæ'Ö˛6{ Šœ„âŊ€ÆŠëĐlōüÔS,\šĘķC]IL k%ķ’'æų šX:<‚ųI\sŖB86Lîwޞė–+éū_ šöĘäķ›ísŅڗ¨ü˜ŠˇŗĢÖÆØû­Ņ˜IΚ’Šc “ÆŨ}"Ēƒ5gi%%vãx&ä}˜Æ­úabŠKöjŠĢĘp˜5@_[#ŽnüáĒ:iå–JöuYÎlcԇģŽ—×O8Ļ ˇäR\UCqÎéŠ,JwK;AĐįQßļuĸje4cË/ܙéŨŅČ6wūp Eoš_Le™“LĐ[β‚v"J͇wāЀ¯v9k]a”ėzx I#J[Õ­Ũ" Ļ\Ëyl=zˆŪ€^Âåŧ“ē>SÉ>žĒʄ¨‡ĸeô¨z6ėū- 9A:]ít‡bŲ%8ŠĻËš^jiī ĸę­XõąĶ’㝍u´vöŒ)čMŲä•TRšÆĖLųëhŸŸ“×gÚßĮáģŽ'k<ɤ’ąūŠmŠũĩ›‡Ü{XüĩDãRm…<¸>‹×_úģÜübÉŒg§ŗ:†ī'?ÄåKįžīsķØÍCGųÍO\t<{Œ!æŖģų.Š7­ }ūi‹O~Üü+YrËŠ˙é6Î8Ę|ũkü,4vf¸‚Ũû=ūŲ6_ΒB @įëw¯ ņП#rDEŗ8•´´ųœITv&=.5%‰¨zŠčko%’ŒáڔņĄ°$7ĢÃ#,^Lō…ßž¨rŨWCܛ§ŖuõæÜļŪžÄP‘bĨ°Đvî7ģ7×TĶ­ÉŠU‰āīÛKcŸŸ{ؚŸÆ`Gkˇė%‚‚ÁjÅ āī~ˆ-ūjG̃ulŽn'ĸĩįČC õĐŨFÕZXíĖÆĀéoĖ> ÖķP@EoÉ%/GÅßĩ—ēĸ0jG%™“ėĮ;æ´9Nōf]ĩŠâ­ZĪfwUŅcąZP~z\ø}vu–9›ZÚî-ŖCo ‹ ü}ÔŲ<ŋJßÖÍĩ â„Ņ[ÖQšĩŒL"ô]xYŒÚĸÍ<TAkÂĒā÷‡§<¤Û*\TŊ•Ü\…ˆŋ‡ŊJf @/đë~°°āßįi˙ëde_}F…kIfKžôķĘk°xŌ=W_o%cAGž×_}ŽMôíšđĄãôHrdĪé8q_ũî,ä(ŨMßĀĩįfęœK&§ž{l+'ÖņÕīŪÆBŽōÔžÅÖ=K¨wĻÉ ĪŲųĘ#'‚•Bŧ‡]˛IĐc“îãüí="ōÚ[˜ÍŽŅ$ĄOĖ^­ø$GŽž$úę[īž–Čƒ2—Ė'üG•Á§PR&ĸŌØü ĄSp}Ú|–¤§ōį5’ ƒø_NL žåéX$†:Vy0Œŋcâ­ZÉ ûŠŅŋ"\qŧ­Û苁b­d—ˇƒŨ]ûØî4aönmg€~\ÛöL…;Ųßš›Ž§vq¯ˆô°ÍåCŒS.Å%%”8­hôš”l˛1ße°sT0ŦŖ~G55Í4[@õãöœ69#68´zũŒ•‹‰äŌÎ6O=Ģ[öķ„§ƒ§ž¨![˜ŋ•ÖŽŲĖFîĮíJ ŊéÍėëę≧ęɝå¸N$ĐCOO}0*‹…ņ…#e_ŒvĩŌTAąRŲņûØYlšũAT_Fŗk7Oøžå€ËÁLōu_/,¸ ë’ĢąŪ–ÁŅ>?Į§ xGû_āhęb_{Úō/ųyáĢšvÉÕį|žĄč1ޏã>ūåŸn>cŅŲ>Į}›îHTræ/áf“Žh4 á{č~îßņ,CCĪá}z>ˇ9o܊ĩˇ1ô´—į€Ąāķ9æÃõåx ęüč?žã„œ…Ršģąá¨ÎÂëøÎõŠ|Ĩķ¯ „N˛hņ|RGŋ啜ŠIIųË[đסXųąü{ŅBRFcŲáûą÷š7YûØqŽŧ¤ĸ[<’<>_H™—ÄËo 348„åÖĢxĖy ģ$€”Y‡iõz¨DˆD€ą‘,5L0˙?¨†QûoČz[öhEDGvžÅF úĩ$ Z,ųÖDŅdbˇéy(!âķĢŦĄžg3՝ą~ÅCŖ°°ĄĒxÚ*KĐHԄÂíl^Ū>54„"Äa"čč´h@…Xä´ûÎU ø•ł={ôí՘Õ}_ė3ĨŒ`ĸ¯Ŧyš‰7i6îf¤°zĮŗ‰9@ņAŧŲėęãŅŌjlë/x_ úƒ‰>4e“kĐ ÕONf:Ŧ9”?a÷Fní˛ë(Ϥˉíœ3°_§ŋīNžō}ãIā$'OžÂĶG?Á]KNōÂŽoRš+1čw‘ĨĀaā¤ĪMĨR¯^Bæ]…Ü5Ãđ×üô;øt:pô™3îĶ™n™ECĪáõm“ ˜iÅ:ÖÍOg~ôiŽ -ác '-¸ÄČÂčŽFáæ[6R÷]ĐéæCôY\?Äĩđ;Üwەr–BHš‹á‘‰ōĀĮ¯æ_Íú˙÷ģķ:\Œņēy ž8…úęKnPčūâ"˛9ß`õÍW0ü š_Åxđß˙J4/šĪ›Ã#ŧ>I’.…ĮKqˇåü~8Μ“?A5€ĮŨΆĒL4h°o=Ė‘27+ĒéS'Ū¨ác:ŠĘš'˛*€Fx UŸGMC!Ŋ“ÅLÚY’Šĸ$Ö¨X6ĐTå`ōûļb°œp,äXÚ{Tb^7{ķqž “ˇÍ,ŦIÞoEë Qƒô'2éųSĮƒÎY÷ƒŌėŌ6˛ÍŨM_ @ˇĢ‚_„}%˜ĪļĐҧyú• žV4>īįĨŸ‡ļ_ŋĀ]ëS™˜tfuglĐE5t„îü”cy÷qŸ)q,énžƒ;Ž+]%ūŖëŨ-ÜuĮBžá ÂmˇČYRņžt҇†G'-˙ėđ RjūĖ˙œœ‹ÚõŲkyņäĻEķ ũ>Žzâ;7ëyų%SÂĪw‡QĘ~ĮĄ#EøēOjۚÎ]{%¯ ĉâ_×pę_ SÂĪÖoúāŸgß`[1eyz@%āÚĖæ/Ņ8g0<0>¤,™‰zF¤ˇū8@”žŽ‰â‰ ‹Ņ‚ÅÃßå'÷áõVŽ2­CŨ¸{" čąäÚÉą=ü˜rCdj8HÄ`%''‡Ģ>1GØxúzĢKבhBÕĨ¸ē| „B ôvR[¸’‚ÚŪ3Ļ@k,6Ŧ  đzGīčÃĐcą™@ŅŽ†ģą@œØäéJ:}"l|ÁÄmƒąĶg4Í ŪÎąyYˆŅĒ‹Å}‹ĄÎa“!Ņö°`4ŅöHäĖ5˜œ ´uũ–;}¨|Î1ú÷RŸŸ×3Ŧ\?)”^o3Ŗô÷á{ũ2É'žŖŊÉÅķˇũ3÷­XrƂŋ/8%tu•ŽbŗWKļŨŠ~0Ā´V;ÖsėJ×ßũ˙ræ<đāhļū—§].ĢčA.jí'ęcgSCkīã>ÛŠwŸæ9ná6ĶÍØo;Ëķ4ö{G'Aīyšųˇ•s3pėŲ§^yˇ™ŽdčØočxzÛŊ&9C !¤4į'úų,^:Ÿ–_FIĒ ņß˙Œß÷¤đúËI2ę~Ŗ6@R2¤ø*amĄa<üė941!ö7*ãáį­S°ę'Į¸åÁ?ŠžÅü)\wÅ_–ÎF‰g?ûšīe]Žƒ^AQ´zŲŽ{hÚŊ“3`.d{G3÷æYŅÆ‚*†ėÕTļíbĢ=‘\ŌÖ4ąĢ­’ÕVą`€`L‹5ī^ļīnÆi´L%‘Ē"Aū>zē÷ōhk›ëĻûUæLJÚvŅT˜‹E#ĸęŗY]ŲL“sēÁ!ļĒ]ėkĢaCŽƒVAAAk°[XĪŽŽōĶÎ\&§a;k֑m€` @Dk!ˇ¸‰];ŠC@;5-•ä™´¨áAÕJIåj ãÕ ļĘfšœVôD#˜Š›¨ÉĶĪj¨jŒX,F -zK.ęw˛ĢÁŽ06Ņä´b B Fī¨äžlíėˇoš“Ļ–{Ém{˜ljšîÁ2Ūv-†ŧuä™T]{é€ÅQÉö–Â3†ßRRR.ųA™šš:Í­Īņ͝×Pķƒßp4æÃUUÃˇg’ âŊëĸ_ llîO_H%į;¯`LO%))‰WO ŖžōW-™Īķ_\Äĩ‰7’uÛ_ägû^kSY¤Oü(b4>ĖÉã'ūˇe$''ņz|möX|=Û˛ųÄGÕ¤ē§bÔ>ūP’X’6ŸÔ”$BҎø 6…#_^ōėî8Ūō[Ų艡ˇæ‘mÖĸ¨1žú Ų5{ØŊÉ,{å;ØĨžjJJ ×\s\ U!.ąËō;@ęđ×j’šĘŦ0pøM<‡ßäūå >?Û÷ Š×_ÉĩW¤kÍŧ$NūųMžqĪ $ū‚â?|÷ŋŋ‘ōד4î}i<Õū2Únŧn'ŪaøíŊ°ũ,hČ.Šį^ÕMwoŨū(z –\î)+ŖŦ@ÂĪ;ŨW\!áD!$ÍÎØīöœ<\Ââ‰!Ē”d -q)‹Ąáæ%Ã+bŪ _[™¸ö3Áŋî~™…Y×pėä)nÔOLʸūēy„_fčŒËhŧs¯¯1;¨jqP%ûžBņļšlWƒŋÜÅŠ†'_Æ}Ō/&%%Á JĮæĮoËûöƒ^Ãŧä$85Â[“ōÔČÄ53„B!ŪihJšÆü”$"¯ÆųÛ¸++1ÁõĮû˙Lô÷QŽMSxëԈl-!„BŧKĐYŧųÖœ8Åŋß;Qũų?? dŧjüˇ…„B!Ū( ŽJM&úį7Ųx×"Œ×$žü…ŋ‡Ø×jįM yÉHB!ąš÷v<é” •FNrüõa¸2…ŸlL`0v’í?ũĘ 89 Šķ`høüéuŽŋ14ąŦĖ˙B!Äyx[æ-HH-žü ĖKâÁuKĮoģë[˙ š´š’“áĩđ ĸˆąņ‹ŋâÚDŠ)’~„B1w—ĩtjdŽHá‰T>“™¸‘Õx%#mˇŽ?fįåˇû˛Čv¯Fâđō ūæã‹ņ~íŖ\ŗ`â—iũĪ)^91Ė‚Ô¤Š% QB!„˜Ūe­ ‚Åפ°ũĀĖĢũ3]/œyĨÉ˙ Æ`č¯АŦ$ĶŗũãüwÃßM ?˙Ģũ/čĢCŒŒĀ•ķ’‘ŧ#„BˆwBJNJ‚ä¤3j1#€aQĸđ´rÛ+dũđU^~}xüūūīķüŪŋ§ö+Ëpۊņ_}hî}ƒ¤Bė<đ:úĨŠ\§™fRtRķį84æ-Ī"Ũ˜~柚”Žs-čk`ĨmUŖWPî."ÃVEīy÷ZĨæ JģÎv ō(žŨøĸ°aŧå,ŗ7ĐĀ ngŝL÷Œ]Ĩd8\„ÎûÉë_V5ˉ÷Ķšģ—Á ŪûņíŽe“cË23ČČ\Æ g)­]ã¯Ķ[žEF~+—ŧ-—ÉÅjī”ũcē]ЇĢt%ËĖd9Ũ°oĖAÔK•}+|ãÛ/äuĶ5eãõReË h÷ô@+Í+ixˇÆ}ÔædPĐē<ûĐiÛcęyoj[Ú X–SJį Bŧ÷ĐX5æĩÃ𗷘—œDrŌÄ T‰JĐĸĢ’1|HĄ˙Ī'ų`u˜ĸöׯ×q͒̍ųĮ‰¯Ã<ĸĸũæËÜ÷čqŽŌĨnH%%éĖ9Đķ““8uō<öۭ֜˜ÖŅÜą‡=“˙v—‘}ÎĨt(—õMΏĢŅM öŨļšŧD.¨pŽe}u7ąL'•õÍ4×Wâ4Gp—ާĸ+zųÚōnëģŲd„Ö û-Ôtā@ۙ‹Ŋ””Iúv4âéËüm܇”Y>J‘žīP}ĐXņeŇ4˜?z%/ÄII›Ī¯Nahxd<´œI|ˏpņkĀ}ā ÜŊo°õ¯Ĩlų^ŸÂūđ1|ũ'Hēné7* Até‹`~râĒđúķIĄŅyíyœ]ÍXl6æt5.[ ž’ˡĩ"A"~EŅŊ#OJŅP˜ˆjē°7h×ęú´lhÛEmŽnâŽ5Nî)DMĶ]ļļŧÛúnæũ#Np Œ6§GfÚå{q:; ŪßNē!Ė@X•ŗ÷Ûĩļ=Îĩ%ĖEm,’žī“ PŌh2Qæ%ņBŲb~Yž˜¤$8ōĸʉˇFĻ|skŦ”’7SI^ByÛąņû›ž¯ī n4iXru ęđ##‰åF€ä$HM†đkÃŧrä$kmW1ōƒëųęĮ¯žˆU—Ū–RÖäd‘aÎbEaŪąrnČÅķ j}g¯Ftֹ–A†y+‹čš\ĨŽöã._ÃōĖ 2l+ŲÔpöO_ŅŽR–¯¨ŖOíŖzy:éÆ,JŊŖ÷ų\”;We]OmgûplČÄd6 Ÿs˛h`Ĩ­O¯›rĮr˛Ė,ŗoĸĩwrE%JīŽRVæd‘a^ΚR>uv}Ų[ĩœÛ+ē‰b•1tsžhbžļr ė‰e–;Jií=K==îÅå  wÖP™sfĐҤĨ1›øsöļĖą¯ûXi+ÅŨÕ@‘=‹ŒĖíđč¤jtËōKqOĒdD}nĒ W°ĖœAFÎĘw÷O ON~ĘČ\AA•‡ūøųö]œÎâ,Ö´ôŌU[ĀōĖ,VļôŸc˙ Õq+[ēU"îõ¤Ķ'†GC^ZKG÷ãĖåŦ)m8FÎö<§ėĨĩx%Ë33Ļ CgwÅGƒ=ƒ5;BrSd[ËCA•î-7‘nLgų¤PøqS9k(īöL Į.Ÿr°†pqDƒÔŽoŸM´Î8æĸkô8Ÿ2„žYŽwô¸čß]E‘}æ –Ų‹¨ÚŨĪ”ĩžŗgęŸéēŗ•ŌŅã4+§€ō6ŅķØįCmdzđí.g-ƒŦB7ĄÉÛcĻPÕą‰Œņ~HŧWy+F_gAU×Ä0ęčņâéj`“=‹ ã™ĮĮi­Ļ*gĩ^/­…+Č2/§ĒwĻķîč2]^ZKWŽŸ{KÛ&gŗŲ^BĐl|ĘŦa¨æƒÔ;¯%zė-Žŧ|2QzJžú]-ux„Ģ•$RMLv^|u2,œ7^-š<ÜĨ¤$yķĄ?ŠÜ°p/Ô}Ÿo¸î‚Ú:Ũ'™{ ]Z ˇîa_GŲ‘‡ŲRÕ9‹ƒaÎŌõÔ äPßqˆgģ‡Js՛&æßt–o¤Îg Ŧm?:špĒAgų8Ĩ[ĶÂÁŨ÷`P˛Š?x„#ĄÃ´Ø!ŪßĘÆĸVÂöv=u€ũ;ŠŅ÷V°ąĸkúą|c!m-tįĶAąŊÔÕöbĢÚΞ'vR–`[i5cŖJĄļÍlŪÆZš‹ũwQ“#0éĢ/s˛§ĖŠbē—}Ą#hʃļÍllUqlŨĪŗ‡°ŗXKgéÜĶ{ƒŊøcZ˛ķ­h.`?8[[æÜבn\f*;ķėîBÔÖ-Ŧ-õ`ŦÚÅĄÃûŠ1ûŠ+81)îĨŽ´•HN%;ŸØĮÎb-=ÕlëO oŨfļ :h>xˆCO5QhQÎŲŪ™ûN%莠Ņg`uI%vÃ9ö3%hÎSĐ:wr$t„:‹1F{Š*ڌ;î Šct?ÆÃæ‚ZzãįxžĶ•Ē͸5Åėüí 9ŧ‡Ęl-JöŊėŦ͛Úc!mžfVkōš˙Ā‘ĐÖÚÆŸĮīrĘ­aįûØî€îŠjÚį45&H{K/™åÛŲ÷ÄNĘĖAļmšØĮ§?;*¨đ¨ėx–#/bWąEŸGM[%Ų@¨m ëë˜Ęv˛˙ā~vV™Ô­gËØœûpũÄ} lÜԉRŧ‡Ÿe˙'jëfĒ;įŧ΍mli c)ޤ˛({îž& OÕÜj>5ģ÷ą¯e5jį*&Ī]ŠėĨŽ%F~Ãö?ĩ“}uåÛÎ>'M Ķ]UG§.—’Ę2Ļ™Îģ€äŅĒm„rëŲŲą‡íÅzúę6Ķ8ēÃθŊ„ Ųģ„WÕ'Žfdk:w/ģŠWŽœ$üÚ0ŠÉL™4<ÓžĶ~jN˙Ž{jr'‡G8ō’ʐ:Âî{ĶøSųbĖ×͛ō|s?ÆÄ'Ÿņŋħ ãĻ~ÛŲ€3ĮŒ93Ÿ˛B+Ēŋ—ĀL+h§ĩĮ@Im 9F{Yš‘Ŋx|@h/ŽX]ۄĶf$͘I~m1šs›ŠĶ×ę"`)ŖšÔNĻ1 ŖÍIMíjčjeīEŸĖi`ÃÖ s21›s(Ŧ)#;փĮ|¸]~ôÎÖdbL3b[SFĄuâÍŊ/{qˇ°–ÕPhKCŖŅa^SI‰5€§sš‹CVĢš{ōyöĩbÂYæ$SšĖÕ8L"æBîąĨĄŅqæĸ öâ4vļödGi>™f39›JČĶņûB@Œp(†ÖhÁ’ĻC—fÃQä Ssa}SrŠßŊ•ĒŌb™sÅƒ]­´Gō¨ŲZLŽŲHš9‡ÂúJō"Ō:iæëšŸ'Œ/VĮjĖ@gcÃ‚‚ƒq.ÛRÁZÖLífŖ{Y šŠ¯o.s…LŦkHėŖFs…õ%dĮēņxĪž€‚ūZģ“|Ŗ4iä:0Š ˇ¤ĄÁ‡ĢĩCIĩŖĮEf~-M%úZ]øfՇséŸ8=ŽG‰äWRŋ&FCZĻ“Ęb=žžķ›$ĶRØ˛›†Ōb ķÍđáB‡Ķõ[žj)ƞiÆl/ŖÄŽā÷ú'Ē/Š…’Iũ_\š‡>čOgųØĒÚ*ŲÕRKÉ&'9ąÎģcûII3 6ĖæLėĨ5["ėíôÃ,ļ—xī¸äŋ”|ÚL / IDAT˛˙§ëŽÔņ÷˙vŒ?ãĖO›Ī’)3^ėtlŌķ‘W‡`č|ú|;_7%h%'ų|ŗ>uš6ĐÜRˆaŌÉT?Íp¸6MjŒ™ĻâăũÕ ŽM+pO9\rUˆ|ąPhš7ë0ģ•É324Ö,´ã Æ)6ŸīúãĶžÁč&´4&ŊJ_8 Ņ °‚%Į2ëg˜ą/ƒ"*mëYŅ:š´ļiJeZ-Z"Äbqāb‡ ‹Ņ× Š(ĘDëuÚƟŖ10’ˇi5ŽŌÍÜ؟KžÃAaĄƒŗf–Yö’i'įē*č‚ĨËävč,Ø,āōĄĀ:‹į1‘cS¨ëė&”ëĀH?{ģ‚č3-sžd­LŪ?5‰ūŽŠs›/4eי0éĄ/h7EË*č]Öš“Ã[혲­ÄÜŧƒ6ėiQ|ž.Â& 0 Ņbɜ:ģМiA uÆ>´ĖĄ‚Tb‘:VŲë&mwŤĸžÚåŦu…G;ĖJÍŸkōŖ>›ėĖKņî AĢÕB4ŅŽ‰cbę1­ ĸƀ´é)KŽuŧ 6Ķyw|)­2僝Ũ …TgÜ^ļË8ũMŧËĐéÕ ä$0];?–/ægũ'X÷ØkyIåēĨФNWJJ"5^úë0ŧöw.ģŠ_l\ˆ2/‘tFFķŽ’/ôGĄ#ĻĖĖ3'AĮCxŨ.<^?`U ’7ģUjsŠyĸ…üiNüņŽŅ!ˇˇu6˛}šĄ8íd!2čõhĪ^7›xĶQ'1E9ĮģéœûRAAĪę­OҐ3›ī9X´Ņ×å'nĪAķ.< }´ģ;ņö ĮbÄ"0–ÃĶōˇ˛ī`1=<žjÖē<Ôw´á4žOßŊ“žAĨ#ŋ˛ ÷š:Ö¯hE‹)§’í•ļw@ÛÆökty4=ą"°k—ŒE•”x6RąĻ'Ņë–\šZŠįö…Š‹Ö?‰ŠŠx;O”Nß}É.öĒôĖ—¯7ãƒŊėuyčėõލÄba°^Üį8×y÷Üg2äkī3—õ‡ĮBĘXąį3¯d䛞ōāø+CŧvtˆÉ?ė“œÄOņÂUŽŊ2ßזōĢ{ôᇉIחÆîĸUÔųÍÖng_īA6äÍę ŅXl˜T?^˙ôo4ƒa“Ī*sûŦjĀdV÷Ļ”ļã~,dšf:h°æZQn\§ ÄûŨ¸ũ`ĩgŸ}žP(€?ĸÅd6@š ƒV%<ēx}™fÁbˆā÷õĪōe6ÅOŊŅiōW?į=“ņBûzfQok7šĖ-Ąi÷>öîĸä´*¤&-“üMĩėčØÎ:ĨÎŪčÅéģķd˛š č#0e.|_pôžYV{[[ į7ŗŋ÷ ŊOĐÖāÄ|Qlĸ¤ÎąDhŦ‚Ŗt¤™Í˜ĮūŌ ŒvlÃE1;Ÿ:ČÁŪ§čpՒoœ´ô1ũSĮHúÄô,iŗéÚôKĻ– ˇīŦÃ]š4ãÄk0Īo>āyĨûNļŦÜB§ÖAåŽ}<Õ{f§áâ֔f8īžĨމ¯_ŐiA7‹í%$]” 4ö›AßĘĶņÖÖt>ņ7WÂĢ;ũStâ§xŦXĪņĒĨX—ĻN P—ūJ`‰ąwSn9æ44ņ:ģŗ )Æu”äC{UîŪCôwš(/ŽÅ2ä™Â´7¸éŒôáŠnĨīœ›4´éíõŅīíÂՐ[˛Sß6*Zz$ÔßI]m;j~1ëfņÉ.ÍQI™5ÂÃEk)mpĶŲ҉§ĨœõEļ”QãœtÄĢA<­|ĄAŧ´–ˇ0­Ŗ8[Xqæëņģ鈏†čŨQ‡k|ôĖ}ŠÕ*ņĶįë§ˇĢ—™–di­ ĒÃGhp¯“†ŌM´N;¯Cƒ­˛‰2k˜‡‹VQTåÂĶÕEW—Wí&V­\Ouįė&2žŲ– īë™ÄD´Vų6Œ: ƒŊŨxĮŋĨwGîŪqâ úŊb&,Ŗc`Ūwį'-ŋ„uÚnĢŨô† ųđT7Ō­]GIūlß-4ŦbîõÜdÎ"+3‹e9+((uåG?ĩhĩėëĄßׅwVŋ¤Ãšc"æuĶŠCvVfƒĪE]éf6—6âîWpnŨĮÎĸŲÍ*™Ž-Ü×3ģ•ÜŖīaķŠŦtPį3á´ëĮ+ZmŒŽĒĩÜbž…;ĢüXę›)Ëŧˆ}w>t94´m'/îĻbåíÜžr ŽxÛÛșmi!îÃí“ŨŧŸC÷ŗī‰}ėÜZŒÖ×Hĩ{ē7íŠËrĄs ëK[éėĪęiĖÅMÔØ‚Ô­¸…Ŧ[p+ÅT: S̐J.…ŲũSöņæUœ} L¯ÛƒęØÉƒØ˙Ä>ö´ÕãPz¨Žmg0mggĨÃFn_~;ëüĘv˛}l_œŠįÚ?™%ėÜQ‚žˇŽõ+nįö‚j<ą ķÍsŪį/*[1•Np­YΊü5lŪÄîĖ=Įûyí3œwĮ’ÉŦĐUĩ–Û—¯Ĩē×@ɎíŽnގ—xĪH‘+i‰Yčo`åšOBŅ8ÄéÕoĄĐųsט°™Âx\úãōšit…É-Ėģ8— y7÷ī`ķ¤ „oĢ4'Ím1ę*X[&†SvÅmÛqžãĢfŠw4ŖÖmcķŠjÂĒ‚Á”M^ŲN*פI˙ņ&C`B!„xߑ!0!„BHB!„$„B!H!„BB!„ !„B @ī`/íŪMįGnĻķ#7ķŌîŨ˛G!„īīûßęüČÍÄūđ<ڛ>Œã÷ĪÉ^!„BŧĮŊī+@cáįô˙B!„ !„B @B!„€„B!$ŊÄčŦŨÄJ[æ,–Ų (oņŠŨßOįŪĐ\ÖyË!„BĐå1Hgézļ¸ē k-dg[ĐĮü´7Vŗ­o4<46ēč ĪaĩẌB!fežtÁ íÅÕAąV˛§ŗ3@@Ģ#ƒtsQ€8Uؗ‘aÎ`™Ŋ€ĒŨũDe+ !„€.*S6š&ÕßČú•E”ˇxč‹:Ŧ…•l°j-š%Í477Ql~W] ö ÜS˜‹6ØNõĻ:zãg_fFũÛØŧåQÚ<*ëk(ÎQúÃH!I!„˜J†Ā.”ÆFY[3T5ōhOí=´7j19jØŪäÄls`7×ņPŸ‚ÁîĀ‘3ē\ÕޚXM>Öēzč @íY–隥)ҁ aL9…l(ČDSPH‰l!!„â RēȘO•û)ũv;ëī!× ėŦĻÂ=pö…â!zÛj)uŽdš-‹õŽ  ģ€rÎ–OŽ^%ĐēŠ[íE”ˇt1—í#„BHē”A(-{Q-ÛVŖG%ā2}ūá)]Ëúēnbļbęwė¤ŠĐtá 0:ŲņÄ.šJVcQûhoÜĖĒ5­ôËĻB!$]TŅ^\ nz'nŠ„ÂăÁ@â{`ĘÔe{đôDPr+Ų^åÄnŗaŌžžb匧Ō*Z F,6éFõ´’QZÎĒv÷ >WA zņĘfB!&“9@(äŲFckĒĢkļ­ÄßDÕįRR˜ €Ū E!@ĪÖrĘĶôäWÚĐ+ Ŋėí5`y¨sũøzĪXĻϊė\+ZO7{Ē0;̈́ŧn LdĨ¨ˇŠ Ŧ…lJ?]ÅdŚ&ÛI!„˜L*@ȸi;{šîeĩE!Ü×C_@ŐwÍģˇã4&cvV˛!Û@ÄßMß@H,—˛úuXÕnĒ‹6SŨŠPR–IEŸ3–‰ÄҭЧš8}°ÆF7´j Mãĩ"ŐCļ!LwcÕí„-÷ĐÔRFĻl&!„bФ‘‘‘‘÷s¸S’ĻüģpxDö !„â=N*@B!„$„B!H!„BB!„ !„B @B!„€„B!$ !„BHB!„$„B!čĖ[žEē1ũĖ?s)]“íØD–1•-ũĶŦÅGƒ=ƒåĩŊS֛áhe`šG÷7Ŧ$ÃVEī4÷ î."Ã\Dgtây3&ĩ+#s+œå´zC§=ú´¯#ÃéF.(/„âŊDŽ‘(Ļu4m-Ä0åV-ωXBˇ§E¯'čņā+ÍÄvYhĨ˛­ž\-¨ą }îmlÛ´‘ÁŨû¨ĩiÆ„ŪQCsąeĘË0 ”B!HL0ĖXl6Ėgģ?´wŸ–ŧĻb‚­¸Ŋ•ØėšËØ@-zK&™:€LlV=ƒžôtĀ6ÅŊ‰›Mļ§Bˆ÷4ģLúŨ†Õ8ķWãĖVéöô};¤Q@EQdã!„$Οzļ;âŊx:ƒ˜œlš4ōœŲāuĶũvMŦ‰āmh¤=bĨЙ)N!„ qžá'ĐČĒ)“‡—S5:C9ŪįaoĂ3?6tv'šJîÎĐel`ÕË2Č0gžĩ–ē~ 5ģˇShžáÂŽõ2Z!Ä{žĖēHĶš[&O‚VЛĸtˇuŗV’76tš8íZ6{ÜôoĒâŧj0Ú[tÚ?ŗ)ëh"/ļ—ęM­¨9NV'&MYFī¨gg™ubiE/ …BHgËFL™™gN‚܋ģ'†Jwšë&ŨĄĸĒ{ņøĘČ´Í}2´^Ģ…X˜p8-ĮD‚ĐZĐęN 7#f] M•^VÕU°-gU§=ˇĸ7i6ËöBņž&C`—Ø@§ŋ’G}Į~ö?5ųo'LöēûˆŸĮzĶrōąĐ‡Û=0uųh/îŽÚœ|ŦgYÖXÔDĨ5ĖÃUÛđÅe !„x˙‘ Đ%ՏĮ@Ÿŋu™FĻÖZŒZhßæĻ;jĮĄ;Ë*bø{{‰LēIk˛’i^GeĄ‡ëY,Ļ0׀ë§Ķũ(Ũą\ęËōŅĩ]F ˇVŌš˛ŽęÆ|öÔÚÆÛĻF‚ôú”3~Čf–0!„€Ä,Ä{Ũė ›XŨbgēA.ŗŖÜmuxēqLŋ5ØNEAû¤[Ŧ5ûéØd$§v{,ÛhtšŠëŒ *,öļ|㠍3ŌTĶÅÚęj{¨ĩ¨D:ĢYß9õĄJv=<…2H!Ä{FŌČČČČûšÜ)ISū]8<"{…Bņ's€„B!H!„BB!„ !„B @B!„€„B!$ !„BHB!„$„B!H!„BB!„ !„B @B!„€„B!$]!\Ž Ōéæ,–įQžŖ—Á9ŦĮSŧœŦœR:C@ÔC‘9 ‡‹ĐY–hYIē1ƒŌŽø9Úĩ‚ZŸl%!„b˛yŌ‹K^ Fđ{{h¯ë#ŲŞ*š™éõ‡‰E 7 ‹ô¨B!čOOvéVjmŖyÆWËĒ5čę$PeÃ6Ķâ;5ģw’6›DĨG…BˆKE†Ā.Ūˆ^PP€ÁŨEd3(h›ĐōÕ.'Ũ˜Eš D{Õf6oĒcīYÆÍĸũnĘËÉ2g°,­Ŋą ha_[9öed˜3ČĘYCi‹wŌp[”ūļr ėYdLŪ3fPĐ6„čj(bĨ-ƒ s˝å¸zeà !„ôūĸĸÆB čÅĶ话ęÉ+)$ķbŦ~°“ŠĸjÚ Vį=į€ŋ/|ž+‹Ķ߲žõÕíõšÜSVÆ:SŒîƍŦ/÷ĸŪ:6Vī%l*Ąžš‰{s r7Pœ“FhG[ZûĀ^F}}yÚ0ž*ģBˆwģhÂ3QAŅĸĶ‚VQ•XlbžŒ:ë)3: F=!80Vg‰‹œįœ“ ‹Ô@/ūņÄ2€ŋ?Š ›Eƒ~ēü*šõûØ˙Ôž=|ŨUvŌ&ˇĘæ dënž:¸Õz• ĪOXļŧBˆw™tŅDčk)§\ öõĐ}n!ĢÍ Qr°*Ũôšę¨Õæc vâō„ŲcŲĢ1šĸģq ĩ‘|Œá.\î 0SI(LOC›´Ķæ—SRbĨģņQļÁ:ģÕįáQŋŠÁYÆ:#āõŌ‰ĄxZq‡3IĶkŅMXŗs0ëâø6RŨoÂéĖAî$CžƒėB!$ŊŸÄtˇ@Ao°WRLe™#€qõMT4ļķh]Sö:*+õÔÕõĖnõļ2ļ×G¨hÜËŖLųÅÔ7¨ŪŌ>Â*Áž&O•6KØZģ“]Ú:]Ũ<ŧ-†ĸˇWš“ĘR;:K>šúvöú÷ō°īÄÂúÕ4?Ņ‚5ĮŽŠ×ÍļŠvTŀÕŅDMeÎĖ?ø(„Bŧ$ŒŒŒŧŸ;Ā’4å߅Ã#˛W0HgņT„×ąĢŖ›ˆ‡čĒÛČfˇĘ†Ũiȑ^Bņî% 1Ä\%5܇Ûå!lPPcũtõ†QLpXĨ‡„BŧģIH*@ĶôŌZ׊§×O8ŠŪ€Å^HYe19iŌ=B!$IB!ÄģŠ| ^!„€„B!$ !„BHB!„$„B!H!„BB!„ ÷’—ŠøĸgĪ›įxĖ›ę rčÄäÛĸüęÉG.Kßäņ–Ÿŗé™“yō rÛ^&ūîÕhĪ/YúÅ˙ä7{ÅGũäŨûsú —=?×}įėëūÍĪ~IöcéÅã'fģÜĖÛhîīkÃ<ū=÷ÛgúíD_æÁciũīyõœ;ZˆŌ 77ū8tYÎ'r)ŒËač?úŲ ÜžiâÖ+ĮN/ķũÎWųRŽ‘ôų—øųųˇ?.ä+_H"ü[ĪI>šyŠ\¸TŧË\Ļ}wĘņrq(—ŧo†ųՓ/ĸYž’›āâ~°ŠŠxäEČũ$Í[0ũcN„øÚ#AŽĖƒ´Ķ–ũÚ#/§ėô}jé:āÄËTˇ‡Ÿkoæ›Ĩˇ‘>ô<e>=ÕY¤žŲŋđĘI–}âžjMīŋ4ãŌņU˙aß~ŠöĨōĨ/|š6#ŧpđļüø—đ€ƒ¯(圯įĖ6…¨øîĶÎēŨŸ_ŠîÄË|ûĶü8īũ7ąč´UüáÉßđāķÃ$Ž=ą žũŲ~œOķ™Ī~œ¯/†ūŽęīũ’øŸæūsĩiÆuCüđ!6?ų&wáĶ|éà Xtåė–›q—>šūīé8~ZH?ņ2ß~$HZæ5í>dË÷ŸæĮOÁį~?r_œ¸|uŠ]–ÜQ‡NÛQŽ˙å2UāŲž?2øŅ›¸c>Ä?Į/æˆĪĻËfī.—kߝ|ŧ\ē•îÜMSVęĨkô‰ųéķ:>÷w .öɋG~|ˆ‹oĨíŗ‹Î<ßāņGžæĀ‡Ŧ|~Iōįš¸–ģs—’ŽK€¨ŋŸŸž0Đ|˙­|&kˇZ?BĶz#š?…ųÕx(ÆŗĮSšåc7đÉŦĨÜ1úwĶø›ûküâ`”ôÜeܟĨc‘NĮĢrøį%˙Ã/ŧvî—4M›â‡ƒübh)_)4qËÂ+HO7ņÍõFxū%öŸVi‰˙ņ[:á˙8–NÍŅųö“Qnũė4åšõÃF>÷ų;iúđ|˙gÁsW>fZ70øJŒ¨n)ŸŗęXteĘŦ—›q'Ũ’EãÛeė/íøĢ<Ģģ/Y¯H´ëČaŠžæđ‡nŖišfÆā÷ĩã7đõ]qŲÎ)RēHÔc¯ōŖ÷ķ#˙_ŧōîūĖ4ŨŽC}ÆKî#aBoÁ*7_>mš‚7 å˙ū_ŸųÃäú¯§ícáÛûB:>Lú&žų…ÛøäÂÄ'Œ?<ķ4_~üE‡´Å‹øßëoãūŦŧzāWäīâKU+ųü’ÉÕ§WųéÁgK'ųUĪĢs­Ü4ūi÷Et˙ŽŸ>#І[­7ķõÂŦDĨęXˆīíōķo‡c ĸ!+ëžēūÖņļ<ūŊ]ühņr>ëį[ūŋ0ˆ†[—ßĘöĸŸ˜NŧʏyšīŽˇZ3ųæú›¸åJ`(ĘüŋgøöExáÍÄ'ģ^˙üáŗíü'9ôäĶ<øäË:><úڗsÖôõ°Ÿ¯=äWĄ8hĩ|rų­|ķ—žņINōlĪ!ÜâĀņat×]Ë]Ģ–ņõ\}âDrø?ųHKœ¯7ū=Ÿ;ŗūO>ōũ8_˙îØmoō›}ųړ¯ō‰˛˛Ldŧuގ”?ĻÂũŋ ʼnMē}ŲgVĶŊJ7CŋĪĸÍ ķ‡žƒ|yßË:>„:ū WņĨĒģųŌĘ×xÜũ ßöŋƑĄ2>tÃ˙oīũcĸžōũ˙Įu˜AÂ;čĀŲ™ĩ ~ütŌ+āÅ~Hų\a[åÜHĸÔ­f1‘uĢD’FŨē’Č'ÚXm‚‰dQģĀ]A¯`ä*´č4cø‘ –Ų::hŪų5Žũū1#08ļŊ{Ûîy$&2ī™÷û}Îysžįu^įöå™ČĐ(^;oåŽTŨŖÎ6Ƹ2ˆ”•ąܲŒh%Ā{÷ļBf,4ˇQmsĄ79›LķÅ۔Yž “åŠ;3ËÉËv=ösčÎ#zFŸO}MũK.•Ž!Eém/RX9/^âaƒģž}īā@•ž0#M–ģ=^õeēĀpP~Ļ2Ëą)s5ĨÉ!¨€ö 5lč^ʍâŠzåõ™Ŗƒ˙šŋĶūM‰ô%^Q^áN˙Ce) dĻFécÖoØ{Ž“:Û“’jĸ4MëÕŅŲīôaŽŒĸt†GÂj1Svą—ÆAúhO["͡Ü]´WŨĸdPĮųâhfĶÖĻ/)˛EpļXGĮ ËT§lšņ“ŋķ(Ü_A! ØōáfJ oķy¤zʨ” ü*”SƒÆ9ô°9‚ Ĩˇ‡ŖäL/RöFv.iãĶé÷îęį!”z‰P5) Z(ˇŅ<MVĀĢDėė÷žuūĪŧ˙ŸcŒķ„ĩ;îZΖ­wßīŋ›ĢŒæÖŗÛŊ/SYƒŒ)sqžÚcĸIx1jė¤zDËá|IáhÃĩäl1‘îžVéu-‚ŧ<é#ũ”Ũ™ Œķ5q>{)qáqņFļŊņsį`ĢŤŅ§ņCŌ„•íÜŋGŲ]9ykȉ‘Đj“’Ŋš}ēa>Ŋâ+ĀÕΚúĮ˜2ȋ DĨô#:~‘Šŧ;S¨¸hžŌK‡ngˇčÜîėČeėŲ˛|rT2ũž§ëčSWs$>ŊF".~'“Õ4×ߛgé#Î58Đ&ŦĻ4>Ŋˆ)Ņä{žp8č‘ČJp{kTúhļÄ,õ"ĸæ“īsŋŗlÂlj`KŒP _FbÛSôRCdíĄŦ+‚\#I?T)Ų+X'÷SŲĮk䭋æ/<ײuÄiŅGFs8w)ÜmŖfš LL5‘Ž€-[ŪRcuķAj’ŌčäHL<ĻŲæ]wŧm×EOŸŒíöV(Õ$%ëˆr. Tˆ ;§čS“8’č.“8Ŗâ´iĶŖ~lûp=ĨŲFōŒ‹=‡w}ņĒßiI‰w×ī¤ėx tcÔ5Ųŋ0ļŖ—Ķ°iķr"%´š2ļĒ2M0>”AŠAā‡Vŋ”ŧäĶÖ*Gtä/ˇ%ų’ôúČeΊ@õ×>ęķ)÷a*/tĶ–ņœ-Lædę":jođ~ÕŖIīrÉŲnH^ÍžyÆˇĖėô+˙XÉ˙wč•,õ.Ÿ%‘ŪļŠ“ųë¸ē?…ãÉ*žŽjdkũT}NKâHØ;Š+‰ØQ‰ņ“¤ˇ–š=˝KĀRŽä-En¸FäŽ " ŽR2LÎĘ)QamēEa—†ãyË|x’aÜéĨ‚ ™” ‚p1öЏãšîũ÷=ËhŪvīņū$ϭ¤|=hŦhĄ&h'Ķ˙Ãûm1öá?ŨûĸTā¯|Ž<:ŧæ|ĻršP…IDá gˆŒb§î:%%ĻΠcSō›ä¤ŲBũTÚ4lËWŠn%=w*€´§Of\ķĻĪŖ§O†™×4˜4Ī9Ũ÷’=ÕMÉ´įû!˜Ķ’žĒãĶō&Ū9¨%=!’m ˈ“`Ėö˜$vęĻWČ@L‘jäN;VtŪ•Y~LĮĐsÚk¯đNũôĘ­ H7ņŌh¸ŨæDĢ™ģ!‘Ķ1¤Äã]ņĸc‚‘Ž<Ą]͜÷xBûā †yV^–8Š›ę;Ų”ēî÷Rͧ Í/ßߒį~į74D9¸ĐKq¤ģĨ—fįb |ˆ˛ąéy&sēŦ†sĶ?R^ōŊ*oeÚŧ|MĨĶG?f› Â}ŒČnīåä' üyÎ¸×ŗgÚŽ‚(Ãbäú^e-)Ō(æ–Ŧᔞr|9ŧԐ¤Wŧĸžŧĸ~HԒČ™ˆīÕnŒŲô ‘7[ᤌ`[r [ĪÕđN‹Ž- ŅäÄkŅ*§Dé­Fū+“_ļUĨՌļD‡ë ŒÉs”ģlŖÎĻ`S~2{ŒîQytxgĩŦméáVv ãå-TĒcššōƒŌÕäü.‡tĮcŋø’íGoōŲĄ5n/BFbČä7ãô´ŽZ2[zhO3Į0•'Žq`0‚“oōv<ük%šxt7ō"PYnđÆ'Ÿœf^—›ÍįÉžÛãąŽV2OŲЧ&Qē2˙gC4֚Ų{ô:c‡6’įė`wÕ0éųdH¯čœ.¯imw;åb*ĩ{eۆ˙|!‚9\’ÁNæžˇOîĪõģQįQF>ß)|nģWÍôū°”K ¯ƒfonawG0‡-ŸuŠõUī'Đ?ãĄ2„6ąŽŖŸ -=”ēĖš„ĩ\Ũę{UL÷>lŖģAéęá:ÎÆ(ŧFôŗķũŨûÚø5܈y“Æ–.´´˛ĄžŌâõlú.sĄŠŦüM”ÆüØJĮÅ8¯ąĘGÁž,-*ŽģŜ2Ä´ĩ1úũ`ųŽ>–à ũl=QKĩTR0[ō“Ļb˜ft’ū ĩ>”B†ōGZ|ØŽ>ám šŽŗģäĪøŖ 4,‚“ޘŠmûx՗š„Ë ņ}Ķæt1ŽÂ35ė ?’ļdr7ÁFuK7•¯QÖKmą‘8ĪČģ˛CMÎūyp'SΚĢÜ­c<$t÷”Dtdū Ŗ<ąSgyĘ8mløMÛT[õė9ØjxŖ*‚“ĨkɘķĨnīpn,5ŋmĨ˛ËEŠQáķ{:ŊîN¸§¨Ŧ÷8ŨŠ o˙r<ĶēŅúN:ŦméĮLIWKVL=Gš=ž°Žž—ž˜UÔf/õxĄ—{oQsw˜DšķčSÚOũ™ęÉß=gčŧ÷›~ĸ2×SĢ[„ÃÖŧâ›ÆČØbЀ!s=_Ļē&ËVŨUsßû?|xGē[æø]F,úy”QŠwz-ģû“–ôš FšŪrŸ‡Ŗ ØûҟØûâãgĪâ&ot¨ÉŲĩ‰â×x?!€~fČ}Ŧ % ÂĻÄhC4ņh>h¸‚ąÁF><¸Đ4Az~*\46ŲMXīn4=DEJ`ąĶ>bxiŪ:*r1X´0umÄŲą€¨äāyŋŋJ !#-„ŒÔĨėũčÕ]äD.Æ@/f›‹ŦÉNm˜ŽžQüÃ4č_ˆžHâ¤1;CĖ\ ŊDTØŦ}ė„ŧÚ $-Æ䤚ë1Lķātw=AŌ'ƒ~øķ”ąŲ‚ō$tęįØGĀyˆ˜G|ú…Sn&ŸųXí0gžKŠšßŲŅËą5Ň6’§™Ŗ|tĸ¸Įõ>1Šī‘ˇQaĘ—ŽŲė´Č6â;Ößļ+[Úø”e\úƒéåŠÍYĘõÕL¯/sņ˜¯mĪŅŋŒøK y}T =6z‘ŗ‹jI¯#O¯#/ÁLōŅšī‰ ŲŌCŗ.Š#šy´%6օA„*hŽrץ§ŗm‚<͔jīb\Š *`)'˙ AvN¯ĢO9÷I#æ•ë9™ ͲŊĮ—OÕP¤L )ZĖ’<Šũ™‚8ĨbV‘bî!,’(€g¯ĘoO­ $zžå¸ķųË"T °Ĩ‚čĖX’'ŧŸŲÕʆj?Žī7’$ĸŅ‘„™Ę;Ãd%Nvōu-ˆ4’*¤i+ŲīäŋféÜe$ŊüNķąûI­ÜÔF :Î'H¯iũjrveŗndzYēčųâ:ÛåXšru„j|įŲ…ˆúG V#1Lsį#Ú-ũ˜ePø!=ĸąãæÛ6ē_t°C”ÕĐ-bíę`wĩ)ŪhíϤǗvŲN™æŽa¤0 Q€ũöM’?j¤Ōh7Öqę€Ĩl‰dj‰ė ÕŽŒe“ō>Eg:¸uģÃNey#%hWƒ0ĀĄōoĻ]ŗP Ŗ`å|ĻõFšUßJyĮ0c¸°÷ Đ>ĸ&Nįáo˛3ÖEeE Õ}ÃØe™[õ_ōą-ŧ4(„miZ6ÜboŗĢ<Œĩ¯—’SœčsÁˆkx¯ū1  %mQmcw•͝}Ŋ”œjáōKqÍZļĨi°6}I‰å1vy˜vK+…MØŌbŨĸ2L‹Ių˜Ę&wp¤líf×E'I-9Fæ3ÕÖ ÆF†šUßʧĶöņęn¸Æ;oŌ8âv#ŋ­SPwßü‰7~{‘ˇ>ĒeëvO™ŧ:ßįņÎŌb šĮ|Æ;¯qāļĀnÍ2 PY~“ō.ģ,Ķną°ëDĢû}įˇžkļ6vŋ°ßž^Š*ú%įģŽÚfą])LKÔũ{ŦũđOŧņ›?ņÆo˙Lō‘›TZ]îHuOŅč#5„>°qŽc˜ ÛŌJQËĶųĨ/ō—¤‡=Ĩ˛ĒƒvŲŘüˆĘōNnŊhšvĘ/tĐx˜ ŊĶŊĮ qĒ›dg?ķĒļdš;ŽjŽrXĘ ~ÔTÜä´åŨÖG4Ö7˛Ŗi””wß$PIh5č'˙-"t!øĸ—üf÷h%GĀļ^čåVß#Ė–oØUÖFGØ2rbÜb§ąü2™g,Tw<ĸŊĪFåųkvøą-ĶŗÂTIzØSĘ+Z¨î“ą:dˎŨõ je$†×22?Ö%„#uZØ]oŖŨ1ŒÕ:@ų uJ-9oŠAé7#­ž4z>—”€´Œ}Ɂ4W5q isߕį¯SÔĨ&/ûą=ķš÷wüŨw+ŖųŲũ īĪĮ 2†ÔØY=§cÎ ä‘QˇČyæB™đ„Ięī'ĒvŽ×¨˙Ë÷Ē ú(ö­´ąûÂ5Ė-y:L1o˛/牒3ר ×QŠsw^ę`tŽ6~ũ‘+‹0ůæRŽ'80  ļ~ô%QÍŲŧeH#{8âDvz&;Q îešö;=˜#ߜąDĐQZ¸š’ō6ļße\ŊˆDc,ő~AiáJ*,ė.ųÚķŧeœ/4Îs%)ČE]ÅeŠø/Ґ•›Ė>Ŋ[ųgíZžĻėD-ģ_,ƒß“ÄÎYb!ôÉÉ\ĸ•ĸ/nđÎ9'ūAÁ¤ŦŒĨXį}Ë#<qW*Uä*Ψ ¨ē…ĩWœø/ &=Ų„IfėéēŽĪiĨäâ5N{Ū3=s=GR=™°”ÃyvvT4˛ŧA>RĮžŦåØÎ8&Ķ™´9™âō/):z‰ŊÁ¤§™8˜đ„"ĪRøqyûˆŌ]6÷{9×'qx˙Fԃ`Ü9ĩģ“ŠVŽu”ÆĖīsŊŗõv×ÕŅüåh,Q¸w>ĨŊÁĖîr3‰†’fŒÂ2ō×sōÂmĘN]ĻhBÃBHßø6‰ĀČüķV%™¸´Ë}mCõ˜ÛžVŽâ/[^/°s:žmw”ÆÆ>Æâ“ądãīyč uZØ[ŅÍēˉN[ĮyZ(šx ã„ę´l{7ĘŊ‡waz}ņá§!=!˜Æs—Ųms!…i)ؓ0é]SŘ8šą‰Â3UD8U˜ŒąėË gkË|RžņØĪĩ‘Y؆*LË›M<¸A‡įŲĄÜ§äh[=õdߎUû=TEP<ËūBūo,%]îä×íXĒÉļD5ŸrGAŌ–õœWļRRq’!ÂBؔˇ‘âøīˇ×dHāŌ3E_˜ŲÚčd\­ÂŗœKšFO­‰)oŌ\ŨIÉŠNĸD¯‹`ßy1ž´*C(.\GčÅ6Ž¸Œu¤%ëŌÖr6íõc’´‰kø ]i%ŗzŒņ…*ĸ"uœ,4‘5oīƒ‚¸-ëš$ĩRR{ƒsCîōú`×Ēyo‚øãâÕv˙ÂûS‡ŽĪ’ĪęY=vđ*eƒ/ūžËę‚ģÅÕŌUsĮXūķ/ß~ûíˇ˙ĖÚ¤Bņ/^įēūû˛ÃÚp…wꃨ-Mø~†!wŗõ¨-ÅkȐ9}¤ kvGbū{éŽĒeƒmßėY:ÕH;Øģ÷&äoĻÔđ}Ÿ0Jų‘jâ3ŠMÖQYÍ$•)(Myõ^$?Ž `ßļëėgûoÛ0ėŸÚ>b˛ūÜū_Mî&ü]ęËOĮ–Ėįk)Ņ$Qûß°zF ø9 <@?G¤hÎ˙áÅXVbįL‘'?´‘AHM÷8ŨL^L ū#2ĩę¤eœ˙AŧũˆĶŠ)iiãr´‘”p?ÆvĘ/ö3n\5¯ß‘!ûļ]eĻđaÎ]é&eķ2â\™”w“Đ¯úōĶÁ´5“ZQĨ‚īŒđũ=@‚1Sģ8›ŒĢa2Dąoŗ¤*ĐĪ)sšĘŗËöĐs÷ta Š3˙įÎũŖpØ8qąs]O°.@į™ž)N\,θBũԐ@ ‚ b˜@ !€@ „@ H @ @ !€@ „@ ø!v‚ūAÅ\ßJIÃæ `įæUėŒ™ĪĄĄėũíu΍úžšž#‡ķa’ °Š$“=¯8TŌzģ…ŨÕũ˜äĖ3ĻōÄeöږR;ũ<§Ėå*3ĮîØéyq~SÖ*öæqö<‰ įēd:ũ0c)Í]NÜáÎĮŨ Wø_õJ§ŧÆyA¯°ĄĻXš ĶkŊŗ ĢÅBQu/ˇ8!(˜”T#‡Ķ"|Ÿŋ%ÛØUŌD]L2Í×M}>ōˆĘ‹_SfyŒÕé‡!f)ŋĪ5‘âë”qG/ۏˇŌŗ–ĢyžâœįsĸtZvæŽ"G?í<+‡ÍS–C2bÆųEŖTÖÛņ7Ž!Gƒ¯ãĢ_fđ =,æƒäˆ—Ä#éEŸš}ÕĀ´ 8×đ˜¨Ė NytSdģ˙Ķw`ˆ˜ŅŽŌi$v_YŅŅĘî&Øļg YšY:ė>3ģkáƒĖ>mōö*|zr ײĶsđeœ>ÔPØ0Āž|Ũ+…Œ6~ įã_üĩ˜}+{XÛõˆ‡h_˙l¨yē‘ģŠ?”ÉČ]Þ?Æ,70~ōw…û+(d[>ÜĖ)ŖÂíY)oĄQˇŠĢ . ŊÄļüõlĶI^‚ĖáTjīŪÍÚô%EļÎëč8aņ)÷–ÛĐeoäČ,ébd€’3ŊHŲŲš¤OŊŽ!ã‡N=}”@ž?Œ ¨œ2ĩf>nąĶ1¤@ŊŒÃų&R^rCČ\īF¯ AûŒĩdË/9˙„cõ6˃.ôo,ãpū*÷ÕLōņa vi¨)ī¤qDËŲ?Ŧ%C)sų‚įL­QÕyOmĘÍ×X^Ąäü˙]KŠp>Ļēâ6Į,ą:DE.e_ž‰ 'M3Žëu: rM¤?h!šüīØžAŗ§ü6}¸™RįM’+ėŒŋ”‘ōžv{I.Txy§ÆŦv×ēø ĐˆŠeĻuŌæ;vdŨ›dMWä”ŧo™Rr—[,ėYCÆ,S~¯zᱜ/Ž%tú4n€0îųžŋám>T{yÁTJū HJ^ŖžČTž1cŽ\ÅÕ´ßâų•6ëDIōū\R+AÆî”ķHŗ@ øŅ"üĩßĢí)ãRúĶR˜„öؠΝ/>Aˇq-WKR({BəVnųl(GŠŠęE6Ēãī3Då•ⲓi*Na_ØŽjáō¨ŒkųfQs¸$—Áŗ˙Û#~ĀÚt‹B›–ŌŧĨ>îāBIķrŲJJ?pēĐ]oĄrhéŠ+(xkã8}ŅÂ{' éĀrôs>Įˇæ@õQ™Ŋ§Ž<ī]ųĮ*vwC.ŠĪŌ|ĮųŦ/#ũĢu€.ŠŲQÔct4ĩQôÉ5äwlÛÜ6ëGFîvžiaĮūJX¸€PÉyđ9Q˙é™ūúiBũ\đW*Āébhæ§‹!¨^ f§ģī%Ą =âhæhļļ9fy/Ž(ōē‡DTĀsš >_§’ŗmČ ë)Ž™ĮČ4&ģŸéîēGQų5ļ:7R›ĻĻąŧ…Ju,WŗCfÁé¸=Āīûß ÆIDATCŨr.å/'ÚũrDī‚Žnņéa2bûŲ]5Lz~ÆŦS'R¤ķ0062Á8Oø¸ä•Ņoķ{Ŗßdx}_­œė _äī¸LÉK_ŽûQzOĄ¨Â$ĸpĐ3¨Y;59f{L;u͟ˆ)RÜiĮŠÎKȍ=xLĪ3™Ķe5œ›ū9âœvŋ9ƒâ§éƒÛŧSnŸæĩx‘-GĨ#šh¯ŋÆûõ°iÛ:rt*Æí”_´yÜÉÕCFB›[ØŨĖáCËg‰…R ŠAö$;'@ŠBR>Ļîîú]ĪÉ4OPyøbN ąöxÕÖå䨿zÎ F()kåë˜5ü%Í׊C59ŋË!Ũņ˜Æ/ždûŅ›|vhÍ /īú2ÖÕOŖSˑ‚U“Ķ„Ņ[ƒPÁĻ–ÎmŗŠ EPüģ_Qėœ@vNpĢâ ;œŋā`ĻÛ;jo~Í4 !€~Nču‹đoÂ:‚WÜÂØûÂ@ ˇgĸũv'įZė˜ #;]<|Ļ Ę×hļSMÎūeĖŊĒÚÅ8ā0K‡Ú×KõƒįĐŠŅZÍ$ĩ“SėŊ/ĐÃĄ X¨BĨ\ĘÉ?hL넟rî“FĖ+×s2AB¯TĐž$šû1VĻ­Îq`v€~ŖD´q#–ä īŽŧĢ• Õ~ßo$IōNƒliewË)ÛVO›‘)˙ãe> XÁņŧõ˜ÂÕ¨,Dœ™‘îŋy-{—ûXJ„åĒ[Œ^Ė6Y1/:Ũa:úFņĶ ¯ô¨tĸ¸Įõ>1 Ÿ÷‹ĸ—æ>Y†yz”j´šW\wēT3׋ôĢ”jrveŗndēˆrŅķÅuļËą4åęÕ#Z¤–ŋQwß0e#Tv81$D ĮÃø´O†Äiąa}vzH›įs&ÅĪ Ē—˜¨ÍŸ1MÉ—OÕP¤L )_7%jäQėĪÄ)ķĒ/Ú%‹•ŸĐ!CÜäM†iˇ.Šũ6ë]×z)Ŧ¸+“§­&{4 !€~–HËØ—| UMĀH–zš-”tŠÉûŨ2´¸hļ ŖŌI‘˜ ũÎ=ĪđöõĩqĖ3šõ="æÂŨ$eéĐ9T–wŌļŒŌÅėŖg)Đۓ4ĸF…‚Đ @´€>ŠmēnŠĘnĀæå$J.znwRrRļyŧRāK‚"t!øĸ÷ħÄĨF‘Øba{šÅ)¤Ņ'TV™Š“ĸŠ5ú´šĶpüÜ1.šī(÷ŗˇâoŒĮŽæxâ4aäÂüĸ6/#)\í^Qugˆq‚§ĨĪÆŪŖfŦÉÉ|ž:cZph€˛ú&ŖzĐKQĩ)~Ũ,ĩ@ø›ėŒífoE Ļ<#‰K\ô´Üæc[ y[ÜĢŧf†{ė(ŋ‰!ĪHz<ėëãtĶYųĢH šē_ž‘uK&čiéäor2-•Z„æÎG˜‚ž2šĶ\n@Ĩ–,ƒŠ­ĩˇ8ô6Y‘jÆø´ĸų Ŗ'ŽKí.ëéYŦœjôOgn\Áž7.SōÉMüŗĸ1 ĶXkĄ†_rŪã™ËJ ŦüģÂMėŒ^Äøƒ>>žhC˙bųø<ž3bįĉ”;#(^éGÅFĪ‹j¤‹Ā¤ņ#)9ĘZØdb_|*ŲAyuaŅŪŪ”WÕC,Û¯Prƌv$å_7˜)ą-Ļ8/Õ|lvZŊĢ,7͍ü%įsuŪļ04 !€~ž(ˆÛ˛žKR+%ĩ787ä^.ũÁŽUžM¤g¯ æL īėõCˇ$ˆ¤ÔĨ¤‡ yfĢkû‘cMĖ#ĸÖ˛-ÆAÉQ3æúČĨœÍ7÷ŊĻp$ōvĨ0~ŅĖéōk. 4,„MÛÖSœ8˙Ûh–ķŲ8pĄ“GŸ"+a2ÄriŗaÖÍ}3JuE+uü‚ŗy3:6eŲė¨Žå­/Ԅ.Ґ—ŧ”ÄŲË#"L Ë>ViЃŅ9ÚøõGŦ,ÂŋšKŗnäįîĐŗv­‡ _Svĸ–Ũ/–ÁīIš%ŽGMFūzN^¸MŲŠËBhXéß&1 œüuŒUÜæã?ū;{Qŗ”}›=ņ/ú(ö­´ąûÂ5Ė-y:LŌ\ž"?RōÖsļĘLŲÅër‚z‰F#Ÿo~¸‰ŧ=ëĄâk>-o¤ČŠ *fg÷'ĸŋ†KÎVŠžhaÃE'ūAÁ¤$ŦĄ4[Į|7ÁîŽoåĐ_Āß(<û7¯kA+Ũģ'K†.í0Sô…™­NÆÕ*L1Ëš”kœfKs՗Åė)X‹ĒÂBɉ{RĮ‘ũĻŠ•bķ´Ųî†[čSŗm×ĒŲŞ@ øIō/ß~ûíˇ˙ĖPĄø¯ŋs]ß Ģø™am¸Â;õAԖ&xm^÷}yi @ üdû ßį0æîa âG ~rˆ)0āĩŏ]{›¨QjؖKœČ@ øÉ!ĻĀĘ@ ˙tˆ)0@ B @ $@ ĐĪ‹ čŸ˙@ ĐĪ–˙ņJŠŽ!(:†˙ņJ„E@đOĀ?ũ*0@ ˙|ˆ @ B @ $@ @ @@ ?)Ž‹\@đO…đ @ H āįÎ˙˛- €A IENDŽB`‚trillian-1.6.1/scripts/gcb2slack/index.js000066400000000000000000000063701466362047600203500ustar00rootroot00000000000000const { IncomingWebhook } = require('@slack/webhook'); const { auth } = require('google-auth-library'); const webhook = new IncomingWebhook(process.env.SLACK_WEBHOOK_URL); // Add additional statues to list if you'd like: // QUEUED, WORKING, CANCELLED const interestingStatuses = ['SUCCESS', 'FAILURE', 'INTERNAL_ERROR', 'TIMEOUT']; // cloudbuildToSlack reads from a Google Cloud Build Pub/Sub topic and writes // to a Slack webhook. module.exports.cloudbuildToSlack = async (pubSubEvent, context) => { if (!pubSubEvent.data) { console.info("No `data` field in pubSubEvent"); return; } const build = eventToBuild(pubSubEvent.data); // Skip if the current status is not in the status list. if (interestingStatuses.indexOf(build.status) === -1) { console.log("Build status %s is ignored", build.status); return; } // Send message to Slack. const message = await createSlackMessage(build); await webhook.send(message); }; // eventToBuild transforms pubsub event message to a build object. const eventToBuild = (data) => { return JSON.parse(Buffer.from(data, 'base64').toString()); } const getTrigger = async (triggerId) => { const client = await auth.getClient({ scopes: 'https://www.googleapis.com/auth/cloud-platform', }); const projectId = await auth.getProjectId(); const res = await client.request({ url: `https://cloudbuild.googleapis.com/v1/projects/${projectId}/triggers/${triggerId}`, }); return res.data; } // createSlackMessage creates a message from a build object. const createSlackMessage = async (build) => { const buildStatus = build.statusDetail || build.status; const trigger = await getTrigger(build.buildTriggerId); let { REPO_NAME: repo, BRANCH_NAME: branch, } = build.substitutions; // If there was no substitution containing the repository name, // take it from build.source. Unfortunately, this name will be the name // used by Google Cloud Source Repositories, not the name on GitHub. if (!repo && build.source.repoSource) { repo = build.source.repoSource.repoName; } let messagePrefix, color; switch (build.status) { case 'TIMEOUT': messagePrefix = "Timeout of "; color = 'danger'; break; case 'FAILURE': messagePrefix = "Failed to "; color = 'danger'; break; case 'INTERNAL_ERROR': messagePrefix = "Internal error while trying to "; color = 'warning'; break; case 'SUCCESS': messagePrefix = "Completed "; color = 'good'; break; } let message = { text: `${messagePrefix}'${trigger.description}' for ${repo} repo`, attachments: [{ fallback: buildStatus, color: color, fields: [{ title: 'Status', value: buildStatus, short: true, }, { title: 'Build Logs', value: build.logUrl, }], }], }; if (branch) { message.attachments[0].fields.push({ title: 'Branch', value: branch, }) } return message } trillian-1.6.1/scripts/gcb2slack/package-lock.json000066400000000000000000001543071466362047600221230ustar00rootroot00000000000000{ "name": "google-build-slack", "version": "1.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { "@babel/code-frame": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", "requires": { "@babel/highlight": "^7.24.7", "picocolors": "^1.0.0" } }, "@babel/helper-validator-identifier": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==" }, "@babel/highlight": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", "requires": { "@babel/helper-validator-identifier": "^7.24.7", "chalk": "^2.4.2", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" } }, "@google-cloud/functions-framework": { "version": "3.4.2", "resolved": "https://registry.npmjs.org/@google-cloud/functions-framework/-/functions-framework-3.4.2.tgz", "integrity": "sha512-yJcxfVgjLoKFO3p6Wy6Fc+Gi6l3PFSwJg4m0mjebx/UHdLeXLYYxgKMP8RCODaApXEWXbSITIjXO0m5kSv2Ilw==", "requires": { "@types/express": "4.17.21", "body-parser": "^1.18.3", "cloudevents": "^8.0.0", "express": "^4.16.4", "minimist": "^1.2.7", "on-finished": "^2.3.0", "read-pkg-up": "^7.0.1", "semver": "^7.3.5" } }, "@slack/types": { "version": "2.12.0", "resolved": "https://registry.npmjs.org/@slack/types/-/types-2.12.0.tgz", "integrity": "sha512-yFewzUomYZ2BYaGJidPuIgjoYj5wqPDmi7DLSaGIkf+rCi4YZ2Z3DaiYIbz7qb/PL2NmamWjCvB7e9ArI5HkKg==" }, "@slack/webhook": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/@slack/webhook/-/webhook-7.0.3.tgz", "integrity": "sha512-qRPMq3In5znBpRLw4IQvYy8M3+CRd8/FKfZjA0BoNx/Q1qm4+kom8BTaOz+HMoRpnywMbr+4B/Tc5JR3nUJ+ew==", "requires": { "@slack/types": "^2.9.0", "@types/node": ">=18.0.0", "axios": "^1.7.4" } }, "@types/body-parser": { "version": "1.19.5", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", "requires": { "@types/connect": "*", "@types/node": "*" } }, "@types/connect": { "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", "requires": { "@types/node": "*" } }, "@types/express": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", "requires": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.33", "@types/qs": "*", "@types/serve-static": "*" } }, "@types/express-serve-static-core": { "version": "4.19.5", "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.5.tgz", "integrity": "sha512-y6W03tvrACO72aijJ5uF02FRq5cgDR9lUxddQ8vyF+GvmjJQqbzDcJngEjURc+ZsG31VI3hODNZJ2URj86pzmg==", "requires": { "@types/node": "*", "@types/qs": "*", "@types/range-parser": "*", "@types/send": "*" } }, "@types/http-errors": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==" }, "@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==" }, "@types/node": { "version": "20.8.9", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.9.tgz", "integrity": "sha512-UzykFsT3FhHb1h7yD4CA4YhBHq545JC0YnEz41xkipN88eKQtL6rSgocL5tbAP6Ola9Izm/Aw4Ora8He4x0BHg==", "requires": { "undici-types": "~5.26.4" } }, "@types/normalize-package-data": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==" }, "@types/qs": { "version": "6.9.15", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", "integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==" }, "@types/range-parser": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==" }, "@types/send": { "version": "0.17.4", "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", "requires": { "@types/mime": "^1", "@types/node": "*" } }, "@types/serve-static": { "version": "1.15.7", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", "requires": { "@types/http-errors": "*", "@types/node": "*", "@types/send": "*" } }, "accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "requires": { "mime-types": "~2.1.34", "negotiator": "0.6.3" }, "dependencies": { "mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" }, "mime-types": { "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "requires": { "mime-db": "1.52.0" } } } }, "agent-base": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", "requires": { "debug": "^4.3.4" }, "dependencies": { "debug": { "version": "4.3.6", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", "requires": { "ms": "2.1.2" } }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" } } }, "ajv": { "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "requires": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "ajv-formats": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", "requires": { "ajv": "^8.0.0" } }, "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "requires": { "color-convert": "^1.9.0" } }, "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", "requires": { "possible-typed-array-names": "^1.0.0" } }, "axios": { "version": "1.7.4", "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz", "integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==", "requires": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } }, "base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, "bignumber.js": { "version": "9.1.2", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==" }, "body-parser": { "version": "1.20.2", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", "requires": { "bytes": "3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", "qs": "6.11.0", "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" } }, "buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" }, "bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" }, "call-bind": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "requires": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.1" } }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, "cloudevents": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/cloudevents/-/cloudevents-8.0.2.tgz", "integrity": "sha512-93KKRR61D2NNE+2lg2HmLbl17beVTKpf1UYd/8BcXpuiDxbU2fb8gAfriSmVGmj1xX/Oh2t5Fh/xGOWFdu6F4A==", "requires": { "ajv": "^8.11.0", "ajv-formats": "^2.1.1", "json-bigint": "^1.0.0", "process": "^0.11.10", "util": "^0.12.4", "uuid": "^8.3.2" } }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "requires": { "color-name": "1.1.3" } }, "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "requires": { "delayed-stream": "~1.0.0" } }, "content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "requires": { "safe-buffer": "5.2.1" } }, "content-type": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" }, "cookie": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==" }, "cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "requires": { "ms": "2.0.0" } }, "define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "requires": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" }, "depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" }, "destroy": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" }, "ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", "requires": { "safe-buffer": "^5.0.1" } }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "requires": { "is-arrayish": "^0.2.1" } }, "es-define-property": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", "requires": { "get-intrinsic": "^1.2.4" } }, "es-errors": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" }, "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" }, "express": { "version": "4.19.2", "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", "requires": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "1.20.2", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "1.2.0", "fresh": "0.5.2", "http-errors": "2.0.0", "merge-descriptors": "1.0.1", "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", "proxy-addr": "~2.0.7", "qs": "6.11.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "0.18.0", "serve-static": "1.15.0", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" } }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "fast-uri": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz", "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==" }, "finalhandler": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", "requires": { "debug": "2.6.9", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "on-finished": "2.4.1", "parseurl": "~1.3.3", "statuses": "2.0.1", "unpipe": "~1.0.0" } }, "find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "requires": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "follow-redirects": { "version": "1.15.6", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==" }, "for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", "requires": { "is-callable": "^1.1.3" } }, "form-data": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", "requires": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "mime-types": "^2.1.12" } }, "forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" }, "fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" }, "function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" }, "gaxios": { "version": "6.7.1", "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", "requires": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "is-stream": "^2.0.0", "node-fetch": "^2.6.9", "uuid": "^9.0.1" }, "dependencies": { "uuid": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==" } } }, "gcp-metadata": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.0.tgz", "integrity": "sha512-Jh/AIwwgaxan+7ZUUmRLCjtchyDiqh4KjBJ5tW3plBZb5iL/BPcso8A5DlzeD9qlw0duCamnNdpFjxwaT0KyKg==", "requires": { "gaxios": "^6.0.0", "json-bigint": "^1.0.0" } }, "get-intrinsic": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "requires": { "es-errors": "^1.3.0", "function-bind": "^1.1.2", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", "hasown": "^2.0.0" } }, "google-auth-library": { "version": "9.14.0", "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.14.0.tgz", "integrity": "sha512-Y/eq+RWVs55Io/anIsm24sDS8X79Tq948zVLGaa7+KlJYYqaGwp1YI37w48nzrNi12RgnzMrQD4NzdmCowT90g==", "requires": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", "gaxios": "^6.1.1", "gcp-metadata": "^6.1.0", "gtoken": "^7.0.0", "jws": "^4.0.0" } }, "gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", "requires": { "get-intrinsic": "^1.1.3" } }, "gtoken": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz", "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==", "requires": { "gaxios": "^6.0.0", "jws": "^4.0.0" } }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" }, "has-property-descriptors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "requires": { "es-define-property": "^1.0.0" } }, "has-proto": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==" }, "has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" }, "has-tostringtag": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "requires": { "has-symbols": "^1.0.3" } }, "hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "requires": { "function-bind": "^1.1.2" } }, "hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" }, "http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "requires": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" } }, "https-proxy-agent": { "version": "7.0.5", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", "requires": { "agent-base": "^7.0.2", "debug": "4" }, "dependencies": { "debug": { "version": "4.3.6", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", "requires": { "ms": "2.1.2" } }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" } } }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "requires": { "safer-buffer": ">= 2.1.2 < 3" } }, "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" }, "is-arguments": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", "requires": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" } }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" }, "is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==" }, "is-core-module": { "version": "2.15.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.0.tgz", "integrity": "sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==", "requires": { "hasown": "^2.0.2" } }, "is-generator-function": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", "requires": { "has-tostringtag": "^1.0.0" } }, "is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" }, "is-typed-array": { "version": "1.1.13", "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", "requires": { "which-typed-array": "^1.1.14" } }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "json-bigint": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", "requires": { "bignumber.js": "^9.0.0" } }, "json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" }, "json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, "jwa": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", "requires": { "buffer-equal-constant-time": "1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "jws": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", "requires": { "jwa": "^2.0.0", "safe-buffer": "^5.0.1" } }, "lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, "locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "requires": { "p-locate": "^4.1.0" } }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" }, "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" }, "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" }, "mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" }, "mime-db": { "version": "1.42.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.42.0.tgz", "integrity": "sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ==" }, "mime-types": { "version": "2.1.25", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.25.tgz", "integrity": "sha512-5KhStqB5xpTAeGqKBAMgwaYMnQik7teQN4IAzC7npDv6kzeU6prfkR67bc87J1kWMPGkoaZSq1npmexMgkmEVg==", "requires": { "mime-db": "1.42.0" } }, "minimist": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" }, "node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "requires": { "whatwg-url": "^5.0.0" } }, "normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", "requires": { "hosted-git-info": "^2.1.4", "resolve": "^1.10.0", "semver": "2 || 3 || 4 || 5", "validate-npm-package-license": "^3.0.1" }, "dependencies": { "semver": { "version": "5.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==" } } }, "object-inspect": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==" }, "on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "requires": { "ee-first": "1.1.1" } }, "p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "requires": { "p-try": "^2.0.0" } }, "p-locate": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "requires": { "p-limit": "^2.2.0" } }, "p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" }, "parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "requires": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" }, "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" }, "path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" }, "picocolors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" }, "possible-typed-array-names": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==" }, "process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==" }, "proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "requires": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, "qs": { "version": "6.11.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", "requires": { "side-channel": "^1.0.4" } }, "range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" }, "raw-body": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "requires": { "bytes": "3.1.2", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" } }, "read-pkg": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", "requires": { "@types/normalize-package-data": "^2.4.0", "normalize-package-data": "^2.5.0", "parse-json": "^5.0.0", "type-fest": "^0.6.0" }, "dependencies": { "type-fest": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==" } } }, "read-pkg-up": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", "requires": { "find-up": "^4.1.0", "read-pkg": "^5.2.0", "type-fest": "^0.8.1" } }, "require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" }, "resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "requires": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" } }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "semver": { "version": "7.6.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==" }, "send": { "version": "0.18.0", "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", "requires": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", "http-errors": "2.0.0", "mime": "1.6.0", "ms": "2.1.3", "on-finished": "2.4.1", "range-parser": "~1.2.1", "statuses": "2.0.1" }, "dependencies": { "ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" } } }, "serve-static": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", "requires": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "parseurl": "~1.3.3", "send": "0.18.0" } }, "set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "requires": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, "side-channel": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", "requires": { "call-bind": "^1.0.7", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.4", "object-inspect": "^1.13.1" } }, "spdx-correct": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", "requires": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" } }, "spdx-exceptions": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==" }, "spdx-expression-parse": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", "requires": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" } }, "spdx-license-ids": { "version": "3.0.18", "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.18.tgz", "integrity": "sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==" }, "statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "requires": { "has-flag": "^3.0.0" } }, "supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" }, "toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" }, "tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, "type-fest": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" }, "type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", "requires": { "media-typer": "0.3.0", "mime-types": "~2.1.24" } }, "undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" }, "util": { "version": "0.12.5", "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", "requires": { "inherits": "^2.0.3", "is-arguments": "^1.0.4", "is-generator-function": "^1.0.7", "is-typed-array": "^1.1.3", "which-typed-array": "^1.1.2" } }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" }, "uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" }, "validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "requires": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" } }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" }, "webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, "whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "requires": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "which-typed-array": { "version": "1.1.15", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", "requires": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", "has-tostringtag": "^1.0.2" } } } } trillian-1.6.1/scripts/gcb2slack/package.json000066400000000000000000000004361466362047600211660ustar00rootroot00000000000000{ "name": "google-build-slack", "version": "1.0.0", "description": "Slack integration for Google Cloud Build.", "main": "index.js", "dependencies": { "@google-cloud/functions-framework": "^3.4.2", "@slack/webhook": "^7.0.3", "google-auth-library": "^9.14.0" } } trillian-1.6.1/scripts/gcb2slack/success.png000066400000000000000000000757611466362047600210730ustar00rootroot00000000000000‰PNG  IHDRL¸øŗåˆzTXtRaw profile type exifxÚUŽŅ Ã0D˙™ĸ#`ĀŒĶZ‰” :~q*Íû€ĶÉ~‚ļ÷ąĶcŌXČēāÂŌRž‚Ę܄ÛÜ5įÖVIޚTV@†ŗ]íėtE`wsGĮ˛ËĻúõĻ•įų'ŧ4zī_q—ĶLA,! iTXtXML:com.adobe.xmp Ã\/sBIT|dˆ IDATxÚėŨ}x[į}ß˙7)‰~Û ­ “L[€iČ­ 9 !o#(wV Ö‰TWÚo•6¤–”dz•¤×’t[JmCŌŨOD֊PōĄ´šV›Y` ĖWM'"‰IJÛaODKį÷ø¨'RļãīëēxŲÂÁyēĪ}Îųāžo¨ĒĒ"„B!ŽĢPŠ@!„B“B!„&!„B LB!„˜„B!$0 !„BH`B!„Āt—-~ÔÉŗ3|)8ŊdúeyZ”B!Ū%īöƒ+/ĢPX°đīmßdčåˇaFĨ¨d5ģåažũ…û‚• rāÄģã… d2.^ŧxW–ŋzõj>ôĄqß}÷Ia !„ĻĢƒŌžãoĶü7á<ōøî_ ¯žu™Ké>ūq ßüíG1¯Î‡&@2“x7ŧúęĢ\ēt鎎Ŗ¨¨ˆĮ{L [!ŪCîy—Ü\<› K˙ëĮ9îû¯¯Ōüĩ7xđáՔŠXS/Ãcbøy…˙sv†Ō/˙„āÍ|Ę[ēî˜l’đP Ž Ŧŗ”Qf*cŖ††?ádö–PˆfK)Ĩ†2ßúz§":kØ`+Īī‹}v_ĻĻ Ô•Qj(e]kø.îKFS~_ƒ×˜ëĻÚPJéĸŋ2S9ëĩ4v™\ņîOâ÷Ŧ§ÜVCGhzEe8Ų__Ÿs€É›ØŖģ–€ģÖz%„âÖ­ž×aiŽ;í܅ËTũõëüã?§Pˇ†Ō)Ė\RšxIE—U¸¤‚á‘Õ\üøÎÛøĮˇéũėÃ4?ũā|čē˛ĩęæĶEˆŽ†|5šYōr:e,e,ĸīØ Ž’÷Ã!ÍÚÁļļqŌ‹÷%eĖe,ãĀą–÷ÜV+ZZ%G.“!Ÿ Ÿ`"ĩ——ú/7sr‚‘ņ™\ŠŅ‘89ą…BŧĶ\ ™ KŸ9Į_~s4…č?ĻpČ]R¯ŲÍVĖ\ÎĪ[úsEü4{™]_ƒļ—ĻĢ{”Ī|L™Kˇ6ž)‰ŋq.,é°ÖĩĐTgŨ͑ŠŒđų‰ÛAÅŗĩü›?ÄÉĄ?Āō"PDŅ#ką|æ9žĢX‹"×#!„xĪē']rsÆ÷Ũˇ)øŊ$ųŌ4~¤CÉjfÔ| *XÖÂęÂüË]RypMAž5ę¨ü“3<ųâožŋœŸ÷VZ™">&ō-KFĪ^tģqXL ė5^vįh‡}ž•#;9B‡§†õ–2ĘLëØānd ”\Ō“ ĐčÚĀ:Se–õÔxē™ī_š&2ÔJ­ceĻĨŨQĨÎb×ÚÆéūæZ6ØĘō]VŽF|ųžĩ+ßČX˙A9@WE×ĐnęL6§‡ŨÃėŽ1\§ Ļ6ĪnWåöję;ÄæžŦnfĄ”RS#ŗģéXŸīÖrû™š]l¨™{9eĻuT{ü$nú€čĐkg˙WN“ÄįÎwnčž-éõĻRJ ëČ÷(&ņdld?ūĐÔurqˆnO5ëLe”Ûké fŽHš“ŒtÔS=WÆÎ:O˛’^Á3Q.>QFQl‚“šÅSŠ(Ûōez^ø2|Ëh~z‘­Žžžį‹[žāĩŋ÷ķ÷¯Ŧ¤l^'ō×í4ūŽ\õú ´ũîhô~ļۃ|úŗĪœáۃČ÷ |áwŋHû‹Į8=ŗ’å !„¸'éŸ^›Ąä…34ü÷×Ņ<¸ŠRƒÂęÂŲ–ŖÅˇ—UœG%ųÚ Š33$ßŧÄęÂ|āšč­Î§ĮˎNøqŽG~/‰wôÜ-mÛdx"ßuĨXŠĢŗqÃÆŒI?ÛjvōÕą(ĢQ!51JOũšƒųõÔá][ËSkOņč[×lA[k)c-oōڛK§<ô„•˛ßâÍ3o-sˆ~ĀÁž8xf kŽ lcÉž3V>˙§F˙@'5kžÍžŋģ:Ēž>ôŸwđ{úgü؟6ķôôA|‡Î,ģ|!„w10Í îvųßā“ÛDúÍw0™4|XS@îŌÂhm•|kRpúĖEĻĪΰûŗ“~^eíR?Ė1uū2ĘĒ…h57žiæ2v€Ī™ô8{|rSi2€bŦÄãõâu[Ņč*ņ6ØŽ¯35˛‡ũņč7Ķ5ØK{{}3äĸøW ؞ΐ™ (ZÍJVō { rčØÔ˙G‡9v´ 2Ņ‚+ig‰á÷åģuŽ>ŽsôX•+ėgJĮĮg"ž"d2)"ŠôŠ‹ĶÁ&r Xi9|ŒÃ#GØį1. ŒņX‚ w6Ņįæhäûŧėsą\æ|+2ÁŠŸÂēö!ŦO•qf"Ęׄgb§8Sô8?|Åü¯D9õöC<ŧöĄŽgfúuîûÔįųũ˙đ‹WՑ5kxĒXģ˜™îgíÚG"/~/ ~Ÿ Øö›|žáS<ēXŗ–_43==Ŋėō…BÜÅ1LsŨc#uđ'OņĨ‘sL&/ōØãk(šũ\apú§īĀšw¨ūÕųõ˛j6ƝüücŒūāĪ}í Nŋ’Ŗøņ5+…ķ㝔Õŧúö%fĻf0?ų_s˜OŽ-`Õ ģį´: )r¤I§šĢ\ŠD"3ßB0•K‘‹åoā:[ÅlËG1N3J E.!žĐ’o¸ĐbvZķEcÁaĶņbSû,å œ.Fü´D čĄĩXž­ãŲ'nŧkJ?ů•gū÷UĶũħxxũÛ_ᅯ}Ÿãoō{ŸĘo¸qÃf6¯)e PlüÄBšųĄØŒË._!Ä] L—Ô…ĐōÅO?Ä?ũ[ūŋ7ūö[đP!†GV3uū2šŗ3Ŧũ¨ÂØī]ˇ4îã€ļ‡=ū1&âqÆ|ģ¤92âÅtŊ™Î|‡īŧVFí—ë)Ÿ=.¯üíŸ0ô­S<ģĨˆüĻ/âą]ŨzTdĢãų-åw´ÜũÔįų“§^'2øģŋVJ×o)ūÅOņŠĢšĢN3ö_įõĒĪķyŖtĀ !ÄJÜņ.šKųņ×|ãäyVĩ˙„˙{qĄoėĀŋ˜˙áĪņņĮ֐üį,šķ—ŲˇCĮĢ_\ģ$,ũÎp Ĩ韸îéķķ¯uūk-—v—ōė'īįĩÉ,Ķgfø}ׇšü_õKÂŌî—ßĻčųŸŦ|ƒmšĒt@ޏo;úCLNg,SŠÉų..ĐcļäÛKŌá bY€i&‚qō36Ė3f=@†h0š0œŠĖļLYŦ’cøĮĶ č0W:°ÛŽ–Œö|—].• ­ˇbˇÛą[uáĘPÂĻÆÍųqP™qÚjņ#L&“L†Gč¨ĢĻļ#Ė•ã5fVČÅ …f§NNMč0ی hgÃ`†L KfņŸb=z]>œÄ#ŗãgĻ2+tĨ)B#sãĘô˜ôŗ­f@&“É—i&Cî&–¨7ΎKEHLįˇ=žz Fw7CÁŧ<˜/Ã\=šD”D´^šœČ:¨Đ}•ƒé zęk ęRD'œļâvŲīKđí &f$_ų īąÆ'ķ­4ššqX Fˇ—M†bŌŽ|wY:°‹-ɛYîėvēŠč™`<3F[M-]ščDbIH 6ndGHK…Ên*Ø´VÖTĨ'>ûE~˙ęųâķŗYü÷Ÿŋæ|åõĪsįږÖđúwöqøkøũßú÷ΜáÛĮÜo|–b`:ņ~Ā'xĘx?LGØ×{˜™į>ĪįmĘÕO!ŪÍĻųē5<ū‘5ô˙Ã4-Iū:ōį§vQXJūô"e˙ŒĢ#NA!”ū ĒĀžöÍTč!“ÖšŠôôr`Гī’Ō8hīoĄĘ¨%—Š“ČYņļlB?ßēĸÁÖŌG¯ÛŠŽ4ņDŖ§—ö*ŨŠA.—!“ɐA‹Î\ÉÖŽ}čvP ˜ęzéu[Ņ“&OĄsĩ°ŊBģōã[âώ˙sTÎn{Š Ú{ˇcžßv-úĒÍTsăŖŒÅÁėjaoŨUŨĢV­ēë'eQQŅ5^ũ_˙ƒvÚ˙âۜÉDđĩļķB ÜĪSŋåÁvæë´yŋĀūËWxšx3Ín#0Cbėë|},Á §9Øķ—„Μá;_ųÛ{;Û{;˙éŋŽņú —/„îÂoÉ͍]šHæ°˙ÉkJ‹(((āėųKä^{‡ÖŽá_~į1~N›ŋņlŪûcžqä5x¸ˆĮtų‡XNg/qņ‹\úĢuđVöڊCđøŒīŠā3ŋ”o­ę<–Ąão~ JkKÖP´Ē€äô;üœv§˙ËÚ÷`qg 5?Éļ@ĩŠ “%—!g"ĨPŅ~ˆá“ÔĘ÷°ģũãģĢV­âÃū°üøŽBŧĮܓ'}į.Š<Ŧ)ä“ÂäÉ N^ā ëäŌeøÆ‘×(zâ~žoŲwT4Ģ ¸ø“ üáöR8ûÄË÷§ßƒ'‚sé}e>0uüC´ĢøØ#Ģ9˙ŽĘĨwįw„o‚† oŸËų O0̀ĸCoŽd{SMĩ–Ūëîģī> 3B!éî˜{nŌÅËĀCĢxüÁ|—ŲĒB $˙Ķ&3—TVÂkįfXŊV×ĢķŋÖūŋž5ö*–˜×/^æcē…A%O<˛šÔ[—˜šęgU ŪŗŽ1šhíwŅ*uO!„xß(ŧW+*˜‹1—U.]^Ô´č “0•ãđŽÍŋVõĮߝ†Õ…pYåEīŋxY]x¸B!Äû=0-IN×°fUéŗY~Ųö!ž-Īčũ_ú Ķ˙<ÍÃ% ī\Våh !„☎ãÂ;*œŋĖ˙øÜBëŌ˙ķ•8†æŸí$„BņÁ Lđ@Q!Ķ?šĀļgÃđáüWĒ˙Ķā?Cf†‡ĩĢĶęB$; !„â^[ũnŦtÉãĻ/ōÆ[—āūUüõļRĻ2ŲûõĄ|ôA.^‚ĸÕ0sé2üč-Ūx{fa^ŋ$„Bˆ{ā]Ãô`ŅBʉüÅ/Ãęžßü‘ųמ}á{ Y…VŗŠÂBx3užé˙“aÛī˜ų›/|bū}EĢ$- !„âîģ§-L—Uî[ÅŅS9~Ũr?VÃũ¨COÎŋgß˙:ɗÎđ˜íÎĻŗđęy~ųĶúōŋâÃ.üōÕë˙÷2¯ŋăEK[Ŧ€|Ķ“B!ĝqO[˜f.Ãã^ÅŪ—ßfuĮOžēú—M˙1‘™ËœũnšBĨņŊŸæ{Ũŋ˛$,ũÖÁŸĸkKĸĒp˙ęTÉGB!„x?Ļ‚(,¸Ē­GôåļĒ÷ŧFųWÎōę[—æ§å?ū˙2úoéøŌ'š4´aūŠŪ}áˇ)øb’}/ŋ…î#E<ĸšÆ đ‚ÖÜRWŨ‘á\Xg)ŖĖ˛Ž îF‚“dßĩÃĄÃ^FíPrÅs$C~‚“ˇšÚp+ëMĩø§Ž1mz„zK-ūdžĖüî2Ę<#×,Ŗ`ce.É[ېüō×ĩ†WöölŒ‘á0Sˇ]îY’a­uÕŦˇ”Qf*gŊŗžæÁÉŲ>Ü@™ŠžĀuW6Mdx„ČôûåRp‡ļwIũ¸ÖôžÆj֙Ę(wûoŖnÜĖ6…huŦŖē;˛PO'ƒøC‹×ĻÕVFũđmԞX7ÕĻj&yß 5—_÷<žãučĘã1éŖÆTMwėÛéĻÚļÖĐ4BüĖĻšÖž7Ī_‚ŸžÃę :É Čˇ4=ö@!úŸWˆũä"?זĸūā›ķËøøÚh˙…Į ?CûG¯ōųũođ@ņ*JõEŦ*¸zĖ÷šÂ._ŧĖ_į&ī“øëžcKÛ‹›–Ž>úēZp›Ōøˇ°+ø~9Y§™ė!Ë~0ks<Ā_ˆôm†ĨH˙66Öîa‚ <íŊôõwáučH ė`[Ode7•l_Ÿxæ}Rv÷h{cģ艙i?ü2/]ũãÆw“˛č˙'ƒ Srx됲ĸw¯đ}BÜ}w| Ķ\ãΆŸ×`úW÷3y*ËĒ’5üÜC̘š¤Î‡œËjūÛr†GW“ũ0ø_~ømv˙ÆÃ4­€ˇ˛—q|õu"ąķ<˛šŌ)Ė\VÉ-ú)XSī\†ũä"ˍô¸žš‹¸o'Zļ Ã^ŧ0ĄÆÍöĻ)r%Åī“Ùb2•ģëweåWģ{“)Ō9ãí]ķÝėÚĮÜr€}æˆĮéĸŽŠ…)JĐ˖r:A:ķ>ēÜŠíŊaũȒ˜LĄĩwᲔÜģ}+vĐ:ąôLI¤ČɝøŨŠC×8×eķr8╲ī wŧ…Š`6É(Ģ 8Õô8˙Đü8púĮ9ÎŋŖ.ųfÛ\kĶĒø¨ĄˆÂWŅ<ôúüôŪão™x›5Ŧ}hšK*ǚŸO  ¨Ro^âĩĶyÎöę_<Áī}úĄ›¸C†đųâčÜí´Ø¯Fš’æ_M†hŦÉwĶXÖSĶ8@hjáf0â)§Ļ{s ë,eŦsĩLNîo Ú–Ÿ§ļ#´ĐeëĻÚÖL ė§ŲĩžrSë „¯ßĸ•MéöĖvŲ6PÛ:ÂdHúЎ=Į‹‰c;?NŠĄ”õ‘Ī3ˇĖÉ:Ü(7•Q#˜ž~ Đ1šĩ7[3´Ú7Đ 1Đ8ˇ-Õ4ÅĩØLl¤Ú^N™i=5>"šĨ7ÜÉÃ48gËĘŲ€oļũ?Üēž§w‘IŧČFC)ĨĻzŗÅ8 qļ|Ëíĩ4E¸v O3:8JĘčĨ}qXZ¨ ”hVÜ7˛~C'š Ú֗Rj(§14;-âŖyļŦËlÕ4t—‹k•Yëp€V÷zĘMåT7˜œŽáoÎ×Ãr{ ­‡õM…ņ5ײaļŽÖļu{MęŽĪ×EĶ:Ēë:Nfo¸Ŋ7*ģäP-åu"ÃÍÔØĘ(¯ķ“ŧnũ˜dĀõ$;Įr¤ũ[(5”.t׎äŧęė¨eŊĨœęūØÕE5f`ļŽ—Jį˙Ę<#LĄÛQFÍ`ˆ1ā*gG Ŋh;˜+Á\:ŠoļlËė54^蒏uW/yoūĩ WŊ6X8ŸÖ9Xļ*Ëäp+ĩŽrĘm{ŠaŨ‘֛ė$ÁŽĒmåų.dwķüš1{´vÔŗÁļ´|J-̈́næ:sŗu~ö7ņĶā\G™­‘āôâãąŒ¤Ķf/eMęo¤ÆžßĪ uŨ u%ÖMĩ­‘@°›G9e†2Ö9ņ_ˇÅ}öš 1PˇrĶzōŊ˙ĶD†šķĮÃTÎzW#áŠ+ęcˆāÜšdYOÍåssįšxßPī‘ŽņŒJĶ+*ͯ¨ŋđĒĒ˙“WÕĮ^xU}|öoíŋĒ˙ŅOÔUíŠųy^üßoŠ4Ŋĸ>ņ§g–ŧ÷ņ^UK˙ôŒēĻ3Ĩâũ‘úŅŪ3ęŠ×gnmÃNvŠŖEm:váÆī;w\mŠ4ŠöíƒęņS§Õŗ§ŽĢC^‡jĒlW_PUUŊ ŪnR f—ÚuėŦzáÂYõX“CĩU:TWĶaõÔU=wĸ_u™mĒ÷čšųu;&ÕRåU‡ŽŸTO:ŽyíĒÉęUķo9ĄļW˜T÷žĶŗÛpLmŠ´¨ŽöcęŠsÔ gĢƒ[mĒŊ帚ßúŖĒ×lR=‡íËrķœ;ŽļTĖí×YõėŠcj˙V›j0ēÕĄŗËŪYuhŗI5m?Ŧ^ĢôŽzMĒiĶ šßúãj‹Õ šŦ.ĩåĀ õÔŠ“ęąž:Õf´ĢíųTOīsĢŗKm9tR=}ö´zâP‹ę4T[ËņüOtŠĢ[m?tB=uę„:ėĩĢϊ–ŲōWÕS}.ÕTŲĨž\´ NtŠNŗSm:tR=wá‚zöä°ę­°ŠŪC×Úšcj“Õ :ÚO,[mÎō¨&c:|Ŋ2:ŅŽÚnučôĸm9™?ūîžcęÉĶgÕĶ'†Õ–*‹j÷U¯Ŋ˜Ų2ĢjQžž ĒįN¨]›,ĒŖŌĨzöTĪŠÔSûęT›yîXS‡ˇÛT‡wP=vō”zęX—ę2/ԟŗ<ĒĨÂŖ<Ģž;wZ=~`H=zęÛģLŲŪįVMVģj¯tĢ-}ƒęĐŅSęĪĸsęáí&ÕŌtėĻĪ+K…]uljRģúÕÃ'Ī]Uoˇ¨vī°zę‚ĒĒįN¨ũ›-Ēis—züôUUO¨]•&Õĩ÷ôüv o5-ÔĢ%õsļ~>ĨërŠ–Eer˛ËК6õ̧–\> ¯ÍžĪļM-ęđņSęéš}™?Ÿop 2ģÔŽãgUUŊ ž>Ú¤:ŒvÕ{ā¤znEõæŦzÔkWMUMęđ‰SęŲͧōį–ŲĨöŸœ=ˇöēUKål=ēpV=ŪîTMV:xâŦzAUÕcM–…ķxŲëĖĘë|žLlĒŊŌĄÖĩ÷̃ûŽŠ§¯<§U—ŅŠv͞¸Kļåô ę2:ÔšSōô^×|ųž:yTmŠ2Š–í‡ÕssëŌTËĸōÜjSMUK¯ KšIĩW8T§ˇ]íß;Ŧ?ĢǧöšUK…W:qVŊpáœzęP‹ę´ÎíWž>šŒvÕŨuT=qę”zōhģę2›TgßÉ[<ĪÅûÅ]˙–ÜÜOĀĩ~æ!ÔŨĨ|vŨŧvú"Š7/QTȒņM—T¸´č—/Á•Ī (*,āâ%•͝ä˜ÉŠ Ž„5?Žé‘ÕKÖˇb™ thĩ7n:˜ p0]Eûnv““ēŽĒŌûY(Ē8ŧ´:JĐhJp¸+PR N¯ “Šm›q›3DÉEKÖŗuw?uv &“ēö&*2ãBŲklƒQ6ĶŪáĀTŦASbĮĶē%čg"{Ŋížņ<Ķ!Ŗ™Jšv{°›J(19đ6VĄŋKũyVoŨĩ6L& ŽÆv<æ4Ŗ#Q ‚ßEįn§ģƂĄÄ€­Ļ‰:ëĸ~[+Į"ÃtÔØ0™l¸›ę0Ļ'%Ž˙É}ܡŸ´ŗ…Ž Å %7-#ãņ̇g3d2 ZŊî.ė{–‰qs},† 67í› 8ĀčäõËŦÂ̈́͠bn§žTΌ§ŪB1Ln7Ö\œp4 ãöāXŋ‡Å„ÉŅ„×Ą EÉ™tcÎOT IDATŠœÖˆŲXBqą{mNĶm–]FK]˙0Ũęœ&47Y*+=¯2J%]Ãģimôā˛_Õ‰ƒÕĩ “(ļąŲeFÁ€Ū ššúŲԗ¯_Ž&/•J”PäfšŒlîîÆm7a0ŲŠëōR‘#pƒAËĶą8)ŊˇŊĐ`pēŠĐåĐ,¯¤ŪLŽ2„Í]¸m&J &Ŋ4™ãø|dD4ŽÖáÎ×#M ö:ÆčĖ%Wŗ[šÎÜXŊgC^<õŽÛˇfh8ˉ‘|ųš,NšęŦäĸaâķ‡ĐŒwQų{ĢĐ%ĸD§Žŋm9[ ú;đ6¸ą—„ņÄą6ĩSg+AŖ)ÆTĶ‚×'0˛č$­laơɄÅŲAģ[Gbd„Ø-ŸįâũāŽ?‡ŠđŠ/ŦũÍx„Du1˙ö¯^įG‰,kJÖ°öÁUËū¸îÜ īĶgg`æ2_üĩņĮÎâ%ÁŦ°āęõ-KĢEKšL& 7¸Ü'ĸ 0{0/žV›ą™ÁM@­õ×_EY¸ + ärš%éâÅc)JŒu9&RŠklCœL:Î.Įø’>ŖXÉäŽŊųË͓ˆÆÉéë°ŪņaZמ˛*ZeIX42ÉSĶO)˜íæ›8v:täČ]ˇ˙0Ab2G&ŨÉFGįĸŨĪĄsWw;j´hĩ9Š4ÜņáČ)â“ô+‹GīhŦvĖ$’Čâ1-sWíÂ!‚ĸäČ]ŗ¸5hĩZ˜ÎīĢÉåĄŌ߯– T9]¸ę6ã4ß^Ųé*¨°ÜzŠŦôŧR,ėšë‡ģMĄsdŒdĨ 1Fƒ tķMEeqõÔäĪ×L.wÃkÃUCt/ŖØˆQŠ4fĀĩ‘žčlééˇs(܁ÍbĮ˜ ˆlĨÕVĖTh„‰Œ¯qeõf+â˜ņ˜oc V›žL(N Æ +€Đ” GÉ4‘@”Ņ…ųŽL4R^?JföúUŲû]†jotŅã°ßáũÚä2d–æŪ%×w…š PrílŽ´. ģ˜JOįˆīŲ†Å읠ĩå–ԍÅ{l4ÁŸ"•M‘¸įšø€Ļ+[› Āøđj~Øü8߈gķ×Ūäô+9ųHE…×U´ ^9w Ū|‡gÖ=Āßo{euÁl—b~ÜTá­>ôÛhĮŦ}‘‰`”ŦÃÎģ_•sW_¸0snVÎņđqŽôãĩ—Ŧ¸ŪhŒĖĉD×Í)âąŠÉˆ˜>ŧö;Îņđ1û:pn횁ĻdaL&îŨ—ˆ'ņ×o¤3jĸŽc/GÂĮ9Ū]ugŋ°[bÆŦOÄn&ÅŸˆƒŅ‚ņ^œįâƒ˜Ž NsĪlzĄĒ˜wv—ō™_žÎ.ü¸îĻ/Aö2_ķčxŖõ#X?R´$pŨ™_’Ķ`kéĨɚâĢõŠoõ ø:ØXŊ…ļ‘$%N/›ĩcô´ų '§˜JF´õ0ĻŨŒ×y_‘Î% ˆ$§˜š 1Đ<@ܸOÅÕ'VąĶÃf흍„&“L%c„†Zih™=9ĩhĩ˜' Še—Įāta͍1ā 3•Í2Ąĩ{Œ•~ÃŧÄÕB“5ÍWëŸŖąÛĪČáũÍlŠ‘”š‰ö%“sDũŒÄ’LMÅv´ą?mÅSgŦ¸:ĸūF&§ÉN' vâ‹įæ[—R“ ‹‹*ģ‰bÍ4ą`Å×´ZŌQ&"1ÂÁ0I48<Ėņ=ėėKN15&ĐŅ@ãĐĩ;Úéõč‰wnaŖ§˙á ÁāūVjĢ7˛­g|eB[‚–áp„X(HdZCĨw+Ɖ=ėę395E26BgĮArN›Mw *§"$2z.– Ųdˆ`dáŠTÉāŨÃϞMF §Ā<JŽąŊ7[vˇtē#į•ŊÕLÆŋ…›Ę)ˇ”ŗÎžÚFßuĸ¨ -VČÄB„ca‚Ą•=œVoŗĸKŒãMY&ƒtRW}¸Öų\gŋÁRgÄŦŸ m}e–rĘmëØāŦ§ãpXAŊ1mÆS™ã`G'#‘$SS“„wągÂČVo% ØdĮ˜čacyå–rĘmëŠv7_ķ™mË_gVZįīô#?VÍXY…ŨT‚&;ÉH0Ν}Š…:oé]´ސœš"ĄģąE saŨĄIĻĻ’D†ÛčJĪ& ܃ķ\|°͜‚EžWÂxƒŽ#_zœ™ŲŪŽ_Đđæ‹å7?y˙’ Tx§sWcÃ;t„}-ņŅŲ¸ƒ=øc îŨGØWo€b;ŨC{ŠĘúŲUũ4OWīė­bīP7öÛų„Ĩč1ëÂôÔ>ÃĶÕģ(›Ų;؊åZ×WŽĄŊÔ)#´Õ<ÃĶļŅG]ålĶšOS%ŒėdKã#ąÔōķęčŨŊü;xēüižëŽáčhZĻåfņ6Yđ ĸ¯NOj¤‡]ģvŅéO sõq`Č{Å~(M ÁÖįxzũs´…õx÷RgČßôė-}´XtV?ɓÕ; (u´¸õŗŸ ‹ŠjlÁiã{55î]Œh]T•Eᭉ­Æ=ĩÛhŠ’š,^ö zŅ…;Ų˛áižŽm#ąSwŨŅÎÅØ;qhЋ93ΞļėhlŖg$Ūŗ#ũΕu‡š6ĶäŌ1ŪļŨâŠ,K+û†v„#Ã{iąg8ØÖC0Ë ęM ŽūtŲS 4<ÃĶëŸc׈ĪĐ>Z-`š?@Îĩ—ŋĖKGph¨ —2N[ĮÁĢCвי•×ų;Ģo{%Š=Yī¨ĻĻŅGÎé^ųĩj… ĩ}ėkˇ’ØŗƒgÖ?ÍÆF)Ŋ›M‹|)F# ídãúgØļ'ĩ}Ŋ5%ŗ—Äģ|ž‹w/ŗ¨ĒüÛģ&ÖMuÍ8ŽŖGņūĖōĶjÛFĒåe†jå˛!îdÕje}3ô†ģYv’eÄķ${L8Öú^6…ßũ #Ž—Ž/Yzm¨Mā=>ˆëvģŧ˛A×íÁ2|īĸĸHÕōˈ‹—÷ö‰ëīoYF<Ÿ`—ĻSũN)iaBˆ÷Ŗ cfßP„ät˛SÄw25Sį~¯Ũ/ÁlŅ œœ"K–éÉ0ŨŖäœuTŪ‰ņA#6cŠ€/@l* Ųi’?=ž•uU–„XĄÕRBˆ÷ĩ7}C:ģwņ\gŠ ZŒUx†öâ~´ÜÚZöŌ짇=õΰ3CŅ™ŠŦëbŸĮÁOmÂ3ØGŽs;6´‘Ę)čT5íŖĨFZ{…X)é’B!„X†tÉ !„BH`B!„Ā$„B!I!„B“B!„&!„Bˆ÷¯üs˜‚¯ũ3ũ§ž@cŲgp>ūKR+„BąÄž…Š˙Ôˇxåü›ŧrūÍųā$„B!i‘WÎŋyÍ˙B!„Ā$„B!I!„B“B!„Ļ÷ė$# TÛĘ)3•ŗÎQKsˆdvnzŒ‘AĄäÍ,ķæB!„Ļ÷Ļ)Fˇ°Ķ7FJkĻĸŒ.å`O{&fS<@O`ę&{+ķ!„âŽX-Ep›’ŖøÆĶ(֍x1d“„'Ōš|¤šLÎŨd ģ…y„BqwH ĶíĘäČÍ›Ü\œÆ€ŨaŖ„i‚ÍëyĻmœ)öזRj(§1L…ņˇÖSmĪwã­w5ãeáķË(5TĶ›[ų$Ž2JMõŒLd™<ÜJ­ceĻ2Ö9jiŽ1-GI!„ĀôŽ2VPiTČE{ØR]Os€đä\D)ÆZ×ÂVĢĐRéíŖ¯¯bžNz‚iôŽ­l¯ĢD›8H[C'áėõįYVl;vî'Ž­ĸĨĢ]!M! UB!Äí‘.šÛĨąŅ4Ô­=ėį`Ī8{´]íėíuc˛šp˜:yqBAīpá˛ĪÎ×z”“­ ‹qį9ß8Á8t\gžā2›2=™ …‚Ņ^ĮÖZ šÚ:ŧr„„BˆÛ&-Lw"3œ´úņŨGØ×ĩJ}ŽÄHģü“ן)›$<ÔAŖģšõļrļø@ŽĖm4ۜTęrÄ6ō¤Ŗžæū “Y9>B!„Ļ÷Rp*ąā¨ī`o÷&täˆG\;¯$ 4>Į–Î126]ƒûč­3ŪūÜ =@¯wæÜ{v°ąf€˜!„BĶģj:Œ¯ÛOxjáĨt2EĐëõäŋ'§,gjœĀxĨ˛…Ŋ­n6Fí• VŽZ•VŅ2™E/æŽh’*ąãníg8ü2]• šDˆč”&!„âvČĻ۔ ėĄg`‚œok…m.At"ANW‰ˇÎ€N¯E!ÎøîfšKt8[lčČ%BŒ†õč“:ũ @7ŋÜĢæioĨĸԊ60Æhw+&ˇ‰dČĪūøBļšĩ˛­;ĩ΅M‰ŒįPŒVŦ%rœ„BˆÛ!-LˇÉа—CŊŸc“Y!51ÎD<‡žj;}Ã{qōī1š[ØZĄ'cb2A:SIS×fŦš1ÚęwĐ6ĸāíڎyQŖŌUķ¤ŗ×tŅįŠ@—8HOŸx‰—ö:ã|[”ĸˇSĄO1Öŗ‹]mI™ˇĶÛ߄E“Bq[ TUU?ČđÉ`÷’ĪŲ*ĩB!„KH “B!„&!„B LB!„˜„B!$0 !„BH`B!„Ā$„B!I!„B“B!„Ā$„B!éŨ’Mėn ÚVF™ŠœõÎ:ĮČÎNŽuWSfk%|Y§†ë)3Õ32Ŋd$Ã>ZëĒYo™[f=̓!’Ų%sî Áĩu–2Ę,ëØānd 89ŋîPs9Ĩ†ŌĢ˙Lįf ą&ŋ.Ë:6Ôĩâ,Ú åĻ !„?ãVKÜŽiB[ØÔŗĩe/톊P€ž=؃¸Šo:}é߯ļž8ēĘÍxÚŊč‹! ØÁļÔŽtØĐd'ņ7lŖsŦî:ZßLISŅ~„ŽÚŲtdwâžÅßđ͆;Ųĩ'Žšåû-hæ&8]Ô5ĩ0E  æÛIį„–­Cč°/Je5nļ7M‘+YôšbÂlŗaēÖ ã#Œ&ĖxvS7ûģÅwĨĶ…Bˆé’ģC2éôXĘ4ŖƒŖ¤Œ^Ú‡Ĩ9šJ4@6„ĪGįn§Å~u–Ϥ„›kØĘ0•ÎŪÆt!„B“¸Mu.=ņž-T7vOrëŖ{ĸ„Ŗôö ,7z["L4ŖĨÂiŊ:TŨ,ŗ›:cН6l¤ĄÃO(6usĶ…B LbyÅØģp wúɃ´Õ>ÓöZ:'šé6™l†LFAĢ×Ũø}™ thĩ+‹KšxĪ\9ā{nã4ŧÇčĢ3“v˛­úiÖšš IJ+›.„BH`+S‚­ļƒÁā žüíļ4ûwí`Olv˛všų•ŲpĸEĢ͑I-ĶŊ§Õĸ%M&ŗ˛Đĸˇ˛÷ØKŧ4÷w´ĘÅYĢØ‚ĢĩŸÃáīķŨÃ]T1ÆŽ†NÂŲNB!$0‰›Ą1ØŠëmg“6A4’@§ÕB&Eę}uéD:€ŠĖØÍ ŠpØVb´cÖf˜FW֊Ĩ0šL˜æ˙JŽĶ•§ĄÄæĻģc3ÆôĄÄÍNB!$0‰kšbrōŠ$”I“Î)čJō]k%v'f&đû'—œé0ū`­Ũ‰5˙NĒ6Ą‹ī§s0vušž$–Ė‚ĻËH:ĐIOøę–MƘ\é@ĒéI&¯–”íō3čV0]!„øĮ ÜĻl¨‡- Ũęœftš#ƒ{˜Đmfß\ŋ—i3-uļõlaKÂC]Ĩ%cÄŋŸąL%]MÎųoĩ;ÚéõÄŲŅš…áÍx\vtš™É0˙Aļ>Žô;ąĩôŌÛFOũFn.‡-Rá ūĀē–# ×Ī>%)7I<!ŗdËĩm&{v°eDË&—MÉž=Ŗ(Î>6•@¤ãÆĶ…Bˆ‚UUÕr|2ØŊäßßsŪ딞LŪÃß(ãņ49E‡ŲQGSģĮ’@1Mlx=ž1ĸ‰49EŲáÆÛ˛§Asõ2ƒ_eĪ`€‰x*ßZe4SénĸĨÁÎübŗIBū=øD)2č0Z+p7´°Ũi@CūIßÛ™Ģ7[ŲÄŪÉ~œŲ$!_ ĸŠ ŠÎH…̉–&'&Íė:n4]!„Ā$I!„BÆ0 !„BH`B!„Ā$„B!I!„B“B!„&!„B LB!„˜„B!$0 !„B LB!„˜„B!$0 !„BH`B!„Ā$„B!é'‰ĪUFŠĄtáĪTÎzg=̓aĻnb9ĪzĘ퍌$éõĻRĘ\>’×™c˛ŋšRCÁė ļk9JB!ÄíX-Ep§h1WUa.riĸĄqvNOāPĢ Írŗg„Ŗ)2i§Āe–B!$0ũĖŅQҏ›Ûlū‰t°ąæĢă#Ä[mؖ›]ã }xĞJ;0-%*„BŧWH—Ü]ĸŅĐ) 05\O™ĄŒÚĄ…ļHĮzJ å4‡’lŨÁކNF¯Ķ7ķĶėZOšŠŒuÎÂ™ÛØÂi"CÍÔ:ÖQf*ŖÜ^CchQ÷ß4ąĄfjå”-în4”Q;4$ v×Sm+ŖĖTÎzw3žđ”x!„˜ÄäČe’LMM‘œ čņÍé¨ōÖaš‹ŸaW}ã V÷vö öŅåą’ėäšÚbˇŊė,ņH‚:ŦvĶB$ĶŪâââPĖvŦķŖŅMX-:Č%ˆÄŗčÍz´šáPŒéė4ąP”4ZŒ&VWz%ÁūÚ§Yīn¤ûpL†] !„Ā$–ŖEosāp8p8]Ôuôâĩ*dĸFnûkũs]] ŠroöϤļ‹ö*…ņ΍|ĸėlܓÂėíĨőOXŎŨîŖÅmF‰ōâÎįxŽ5LV*‚B LbŞ)&Ķ9P´kAĢ(@ŽLfaœOnÅC~ŠŅt@šÄä\;N–LúĮ m˜ĩ‹‡‰Î'œIĸą4(Flf LE FsTváĨc/ķũ“ĮnuP˛xĢl.ŧģ‡9v|/›t9‘()9ōB!~ÉĻ;&ÍD3ÍÅ9R‘q&R ĢŦc“ 4ŠĢ2Ƅ¯“­Cb_ ŦŦ_ÍæÚ„Ņ˙"c=;éH;1¤‚øü `š&§ãŨõ4hŪ§u6ãõZëŲĪÎzØė0‹ØÍĄw7ąŲ„BL¤3(ü) %:-:ƒk…Sq–H÷6ÚbFÜn;ēÔņ čVôR„BH`ח!>v8 :Ŋ™*¯‡–&ÃVēz'ŲÕsũqŒ›iiŅŅŲ9ž˛ÅۚØÛ•fWĪ(û{âēzõ´í<¸ĖŒ9ã,Ž7xŲŨąÚNz|c|uOEgĻĒe-ŠĖN*uŽōÕččÂĖēMôíĮjw` ûŲŗë 9EÕÕK{‹}ųt !„īCĒĒĒäød°{Éŋŋįl•ZÁ#žgؕÚˁÃØ4@6I°s;ü9ļ§Û.Ĩ$„âƒCZ˜Ä5äĮZåRø}Rz…\&F0œB1nÅe•B!I|ā¨ëßKĻs€€¯Ņ4(:=fG ûZ<ųį6 !„˜Ä^‰oŋ¯”„B!B!„Ā$„B!I!„B“B!„&!„B LB!„˜„B!~vÉs˜nÛĢėú—8ũŋA ōžëŧįß~Åȓ÷ĪŊ6Í˙üf†˛c ôŽoãūĻ˙(˙+ŋÆā¯qú›GŠ?ķËë?ōžũíˇéņā—žĄaø/>ͧîä‚ĪDŠzūĮ|ĒõŗüAéŨ+ī˙Ūõ ūxí3üķoäƒSļ÷ĀŊŠģ‹Ī—×Wp~/ã‡ßĨōĪ’<ųŸ~ŪōĸåËæĮ4xŋ ;~“Á_]uSëŠō¯ĸīŦhžy”Š#ZFz×ķä-^ĶūęÂĩ§>ģã7úÕUp>ÍלāĪŖorzψ2Ãcüįē§øÍŌĸų÷fĪ$xŪão˜!ģæ>ņ e|ŠŽœO/,īėŋœäųoü€ŋ˙Q–Ü}Zūõú_æ~ãŖ”ŽY8ß>ōž˙æĢœĖ@Éãņëŋū`}pa!¯'ųŗ˙Ä_ũËO™šYCŲ/|”/my’_[ģLųÍLķwßø.üg9•ÃcüĮ_Š/”/Zöų4˙Ũ‚˙vōuNĪŦĄôį |Šî)~ũĘeOŋĘķâ/W[‰ļũŨėz–õ6_˙ŗŋcWōŖŒô>ĩč¸^äûãßáËG^åģo\ĸø‘‡ų˙Û{˙˜(Īŧß˙u‡_îˆ#dgÖB€Æ>%a ø`ORÎ#nĢ$˛‘D­[ČbuĢDū[xĸũZm‚ųJuWØÅē‚‘#Đ‚ĶŒ‡Š@˜ĩ3ĪAGĖÍAĮ=ĖÃ/Á­ÛŗģŊ^ Ė=Ü×īëz_ŸĪįžî­īĮRœˆ`m¸NĖ%ãķŨ2øMnŅŗnžKŊWjŲx/” \ŸŲÆԘž!+W ×ŊÉŅkYįąÖ•žĘ‘G3m5[÷lŸîߎ!Ē/~CųŨ'ôAĐōwđæ5ŗ+œīæ1>čtáīŒ™Ņ—„`úWĀaãĖ•>ĸ=“m€ōZ+ųI‰áī„­Ÿķ&özO8ß4AōžPņĸ\Á??Pߝ1^^Ū÷ēqŌØđŸø-Dæīu/5ųû7ąutæ§ã–oÉĢu°)LáZŧOßĸ`PMqN2zŋšk”ŪÁûH2™ ? ¯ô+ šhJ÷‡ĸ}™KdŸVĐth­kŖh6’]Ö ą1|šCK?Ÿ]i&›4íXíZ¸ëo‘]īEūŪt.h ¯ĩŧŗ7ᓠŠÃ0:ĀĄŌ;Ô­Šähîģh•Ī04Č+mÅįČ’ũÆ×ÎŪde%Å;7“°ęæģ]žÅđ'éŽ{ģËY2ļ†S…‰čũžqĢē•ŧŌV${Ûö“WŪFŨā xãoIgqzš)éq‚4{q‹Œ+¤m‹§8LšŊƒĸ펗28ŠķBŊ>–ËÁö9yĒšÔFcX(ߧ̌Z((mŖKĮåœP¤ŅĘĪĩņáYˇ÷GēDŖc˜.y;vęÉZå.ĢRIÄTš]õ“gQq`g2‰Ō†zEew&ûŌč‡J›¨ x“ŖšqhCTTÉ>­¤éî0:Áônée;œ3?|ú='ĩĨĶŲdâņÛq$*ÁŪuŸ:e˙ŋV4‹āŸ‹ĒīzŽ—×B˜ž?˙§ūī›éŅTõHdm÷ 7S  ĩQUoÅ;fY*Ā|Ÿ3ėš[F IDAT&_ÜČ>­Kdé÷:iųÔ@MĪ™ą^˜īēÚĢ67ŊŌõS؈ųÜDŖm-9*'×{é ‹áë÷âĻ&衕÷ēŋŖ—ÕD2D]̌6i ûu.Ĩ N}—}w˙Čų–!ŠÃV#ŋĨjTÍŠb=é~ŦËņĸīp#į$'x-(Œŋ¸Y…ŲåZŧ×iWÂŖ 8°WƒlĄÚ¤ kéZ˛vžE]AÕ=N’cØÍ]d—v`ŅÅq2ĘHÁÃŋ!ÅúžÉ@^-|”ĘMŗ6 CDd¤s:ÅU?ú°÷ū‘3wm  )pŽÆnjŖĐ&ąīãīˇų°wõSįåÔÎpÖ)Â9ē}€ēĶßqk4’,?ĀfÃLųņšųĮ”­Ÿō./v}˛‘ũnĨ×8i)hŖŽĮIfŦŲØMÕčOø˛XīŠjtJˇĘ˙‹FYGÎde‚é51nŗræl7gŒOy뎒­™‰œLoŋMRÅay-+)œõ~\ pøp™Ŋ×I2ū” ąO9QoÁ0čDûF8G÷ƑŦrí"{ÛÛ(Ŧ~€aĐešŪĩŨeÖĩļ4ōŪ%ųˇâiŨ˛Ruv|Ŧ&hl˛ĸIŠ!rĒŗ> ¤˛ƒĒžad|ĐĮŧIņNËfŗPvÉČųŽIč~Ŋ]?•—ę˛Kœ Ž'g¸›ĪŒOyŒúx=į˛×¸&ŋQ+g*Ú(īv™jcĸ9ē=ŌeĒuČ\ģØÎ‰IŦ&”}ÛãØĩÛcCƒË,otēËĪ~Ũüߡv)Ēî§Ņb‡€’ãõŨ:ĶLîžog“’z -nsvZę;'­vmäēî°ö´âßl&krPvŨamšâŌÉĪÆhŽoĨ¨ÁJߨ.œˆį/ŅĪĻ. *īĶhą3ėņų;™īķįTi‘z_BžqŌÛÔJaũ†A‡‡)~ųˇ’?Į:Due;'ŒC˜ "ÂÖp GOēJņĘu+÷tqčĘ}ę,vƕ$¯æđŽp"•´AF4´tPmqĸÕÅpnįJZ.ĩSn|Šėˇ’L÷ؙŨN3úŽģ˙šû„žąĶ_ķũ)—On Y9ŗŋHÁĢÉz?–ą\ãė÷ą6]ąĐ3mņ˜1^<‰Šŗ”ŸōØ/€­īr2i5>@įÅŪë]Ãíâéq5ã3[˙ũ`úƒ[96ŸØyBEĨĢü•$Į̐ ÉEÉü-įģŠŗØ!`%É)zNĻĒgŦwMÂ"8)Í´:™Ę/õĶ8čDéžK¤ųÛ]ĢҐŋS?ÃĨ6ŊĀvPn’ØWė^ÜCĸšPMį\ãį…Œ;\jãšŊŪ×Ũö.|üŧđæ™ģO*Đŋŋ‰~ĢgŒKoĨ”>3roŋ™}Ņ{ų˛ŠĪėcƕHž–$Ĩ’ %´<™vdŧĐøzŪÛ ÉOüpđW^į,ô^ø¸Ķ÷‘$Ōļ%“™¤ĻŗÂø7§ŗ° äl?Ōļ-ė[ÕÁ3.JėÚģ™]i†āõ^ž _傖ĩš+ũØc6åŅ~fŖÁ5Ž9 zCCšä\’e3ōīú}îäíd,’DÄBŲ‘Öpō ZOK›R7 ŧŨmę­{‡ß‡ųz„´€R÷r’ō‡[įEĐ÷kÂĐts´žËGŌ¸°ę*ŋĸĘRėFîíCÃJJīdđœëįī Á{ųO¸üųNĪe°ßŊī5RØîKūĮ[¸]¨įą~vŸũÖezø-yV4™[øļ|+—ˇ‡"9Ļ;˜R1Ī —ē€2ĩ.ųEĶJ˛ÖûO ĂŌVĒ ã\ņ`,Ū@V€ŗäÛâDvi&'ž1ŽŊΞn%ûrĶ1ßÂÉhåe7)3;0Gßâƒú ļîMĮôųĪšüž5g›¨x8ĪŽĮl ût/#ã¨=žĻÜht™ĮŽšß57Ü"ãâS"27ņõņt.gޤķĘ-v7ČKn{sCŲĩĪĐoßĖ×ŋIįØÛct=\āË ‡Nw#¯OâŪšüīÃ ēøDN%IKĒ÷Eķl2đ‹‹Ãčs28—…qĪO‰Ā‡­;“ČŸŗ`P}ú&E65ĨÅ?Įtr Å…åF:¯VˇvŗĘēąDÅQ{ä?øz˙›õļņáŲX§Ę˙ŒĒ+›rļō—“I~däĮÛšKĶ~ȍT/ę*ŋĸÂ6[eÎė쿆fōē}8\ŧÁΎņ§ĀÛWÍŅũ1$(]uôÁ•§čŪßÄ×Ĩ\Î ÁgSâqÜŌÍîúgŦKZKqĒzZœxŽZēé{Û5ž/Įûr̞‰ĸįk˜9ƨ>{‹"Ķ ~ũÉû‹ße‡ã)„’ĪôŊGSų‡|M‚ßėtejšd’“43Ŋ5ÃĘ[}øčã-|}0Ž„á~vŸvĪ%“nōsĶ1ūf Ĩ Ŧƒ .°˛.zzUúĸ ņąPĘÆ´ČĻ(ˇāōķ'RRĖX\›Û­<VŗÉ-ūĨÕDN ¸ zģŒĩ:Ųē%Ümá $-^ĸ¯Šƒę‡Nˇ4r~8”).!¤ŽT1lĄŧū ˛;ŋ††o8˙‚T/‰; %!`˜ķW{éuåĪÚeā„ŅŽˇĘßU—’†Ŧ0'uĩßŌ9 8ÆhŽŊO_X4EM.ør’ÔHß'—ôk•­Ôë9•$Í#ŦŧІ­žëÁĐĐĘƒĢÉOZŊ€ĩǃr“?ģ۟î/v“ėŗũČŅņÜ>žAí6-Īæ{ōGēpŌ”VĘk­Žē}™z)o‘ėΓųŅ0˛­Ÿõ;By‰ˇK)ëņGūŦ “Ļķ"?Ąēĸƒ–Č)‹œˆ>ÄwĒYÍ8tɂũ—̰0ũÃĸĪHäX‚k2ÜMrS+ˇLNrTŠW쑝†ŖûcܝMâdæuŸį2aķ/25H~ éÖNíhÕąų:vî`Ģk˛˛.I0ß5ŅÍ9ŋÉ]i7ÕŖjNíՑčį67īpíõŦMŨTĒ9įq-'GO˧­”ߍ!1É5)zGëš°m{×ÃŽ†ī8Ņũb0ۜH*ëT^HŦ&3Å=€Ū§üž‚Ŧ dš„zÛģčŠåÄõöíÕĖļipž~}ÆVrÂ\ƒ&26ŽüÖ?pæžLNˆ4cRnšŪO—æ-Œ;4ŽĢ*œũaķÚJ8SoC›’ÆąX÷.TĮ)‹•õ÷1¤Ä-!`ö įl¨ãßãdŦĢ|Ú=ģšžãÄŧn}Ŗ9ņnk6’QFNøŽ Ōo)õ>ŧhž#,ØUĄėp[ë´ąá$\€UHĀ )hîŖŧĮŸüâUŽ 8yÛ[ljmŖĘ¤g]KŦ['WŨ×ļM^‹äčNĨÔ<\Ã>÷b›2ŒĢfĮÛžœi]IiŠĢ>¤¤0ôWÚhąĖ;3ûŽ“>“Œ=iķ%1ICDëA|°rĻÁ]G î:ŠŅMŽĘc^ė*ÜĖ~­bÁņ2c|§&r,vr|Į’ß}•ō&+GŖžgPŋ­Ÿ3Ũ°õã dšwØé;ĸŠųŸÍSķą ę0Z?/ĐŽ!gvæĖ}Tj8ŠSĖKöęÜn‰Ŗ™ę>7Qg[Ë>‡{ æ{ŒÁ¤¸yƒĐ'؏ŽYxq—p¨z€ˆŒ-.—Ũ|_é1PÔĒ`×ūˇĻ-ÜSBúģÕLÍķečÖĮq2fZčDĻ&rŦį:{ŠĢČÆQ’?TŦ}‹s;eö\šAXõ2|}Q+ŸaYŽæ×oŋD0)CųuîÛXÎx7ŋīåJ´˜Į”l]¯v‹˛öÆŅ|ŧ™ųß⠌/`WNčŌãf–”ΛąĻf {T”‡ĪcŸEO+o—š°āÃÖíI¤…,`]ĒíGÖÅ{x"œ´4ôĶÍ×;Ü!ŠÖr`}u÷IĶo ĮrŦdœžIØuwŦŪǟprû´¸SŋÊ'A!+°ch2RR~“áƒsâˇz¯Ô˛ņú0ãž*įžévķÍr ˙ö yŊ ‰āËÔP~˜o!˜^ŪĘYæDå äą āŸŦQÎ41úKD`Ŗo‹`Ÿæ%% N§akŌ›d餅œíUģöúCT7‘ļsÚgŨg’WŊéņ4Ã4}&f_ķSĄWŊāŒé)$š‡¯Ō͜ė…äv‡đ'-EÃMü밚´ø0vŇŗNģeˆ.$öi<‹?ú0_än+f43'yˆŽátÖ^įgõž“Ŧ‚ÍĜŨv§Å6Zĩø#Ņ5ŦD5ĶdĩéúS:eLōS:—ĄĶ.­}UjÖIŊTßbkJ <ė§Æ¤@/-­ŪߖĪķ*"l\4ESĻĀjė§ÅHžfžÅđŅ}ĪeΔ×pŪķs yŽ…éeu+Ķųhî5šu<Ā`qÂ<¸ŸÂeõt'đbĘ­ãbvßUĄ DŽī§QV“,ahĀŠNén×Ášu4ƒ‰ZÅKÆËKÆ7ūDŦZ†l“‘ų~‚ÉnąŅ‡DÎBŋĘPv%ų“}ž†ŸĩjØIVŦĩŌÃjĶhÁ{}ŌÜžĒTā3k.Ņ`Ã<vŲ= 8v/°Qqs…š§Ë¨ŧoĸ6đ§ÔĀyį#ģš‹Ũ§-DėÜĖą¨ybŠ”køō˙ å¨y€/*Ûxī´Ķ4(ˆLÚHSØG'ˇuķÁņoy'åˇË\ĻŦä*G,“mÁ ÷SfR˜Ž ŋŅšū§|Vr“ĒČwøuŒ;˛…Üã­|Ŗy›Ë{CŅ,ˇĶÕÚAŅšëČdđeėŌæ÷EĶ1ŪæĪ˙kĘEŋiį6~ŲOŪ•ŌöĻ“žUĪŊĪcčíšOQÅM˛[¨M œ+~ģũÉ:ė)~]ã:(rõÂ"pžü%ųbīi#ã´mJ"'×āũ|˜ÆZĮoa?˛…H!2=Æūē°Æ-W9ĶdĨ8læø‰Ü–)UÆĐj¤°ė:ßĖUžd}’EšmˆÆĢ_ąûøž<˛dŋ…ÛXĻS>tåjöÚĘĻŽ\líŖüô5ÎĮo\đëŪģ&,ē—ɲ§‹h8Ĩ˜å~™oúûĄŽŨĀí¨7ilíãbkī՛8Yŧ™­‹ ]îCæŪ­œŒúGk'ãŧÂSPĘPdĒy¯ō–Kü)ũIHŨȱɉķ5Ô;ÚhŽÆ? ģŦ–j%øH+Ųą7q:kÖĸęŊ\ÍŅ#ɤ+˙AĀ<}W˙ųMˇČ+ųŪ( åÔĮķX.^ãe1Ą3)HžoŲNÆQĖ#3Ŋ IܑÁŊx Õ­ŊT]ēIyc4ĩÅ1.‹Ų¨…Ē._˛.A°;X$­ŦKŨžd Ÿ'?jĨ¤ôuÁz.įhæŨ¤¸‚ĸīãŗm§^ļō{ĄÖŽĄ8ÃBUųwܒ#ɒīsĻ[AÎÁ dš­Ä‘Ú՜˛ŲØØúĄ3Ŧb>J™3įŋĨ+d-72§nÎĮi¤9Ļ]íᆊVΏĒ9Y¸vęēųn75Ŗ.ėÕMš˜"ĩČĻ?Rt׊=vÍ+õ…ŌA§įFÉ[S›IōĨˇŪ„aė§˙@õÔ^0ü>øå"26ķįY‚Ĩ/‘ēŠ×?āŊģßŅ›č1&ƨĢíĮĮGķ(Ŗ—–cžüÁuõũôEÅQģmR€˛n/t4Sso„œ”ųBđGŗjG'°Ī“ŽŸDbŠžÚ˙Hy‹â°ŲŊJ¤ZMæÎhj~ÕFU“ä˜ÅÛXϞɆyšD~đô¤Š‹¤XÉG ׉i°ĐEčAHĢIO]MzĘ >ŊMuĪYaččĮ`q’9ĩŽĐeÃ;X…vR$NmĪTŦ“ė4v AÔb ƒDDđ2Ė&VVŋÜĘ$ĸ pĐŌ3ĸŪž§Č*ÖIĀ +HÕîXČ$-Ąņ}ep đ_‚čyÂW­čwÎŋ;]´Ū%Åâyļõsĸ՗b÷īĨíŖQÁ}n™œ¤G)žGŨJD+į\ŗ[ŦtâĪ.âoķ÷]ŲØÁ„sų7úšfûÚõåxŽ—ÅâË ´o¯Dŧ%…[øŧ:>* -úa ‹pIĢ!GĢ!'Ū@ŌņZư.dc-šŽŠ–0—Xl˜— >žDĐO‹ÉIĻNą uÉĩĀęų(l!ąt›ęUzj÷†Īģ@ŲÍߒ]Ú ›9—4Ëúä āĶ;<ÎĖāBÂôXe;öåËđVĪ_V¯ŗĢÕ¯8ņPĸø`ŒGŋP Š_ęē‘mäĩNŧëŨ™.E÷ĶÍŪķ¤ëÍĢ÷éĶQú9ËĢÎØ‚1ibfų{Úx¯Ú‹Ōƒ1$Jū`6tÜJVqú”Ë&x<<ËgÎcęāDˇ/Y×ĖģūhW-Ã:8‚Ėt,Ö¸į“ŨķäĪõs¸`Ų,ĢŦgZiąŧ@ˇ~%>¸ÎVKjXÉīÄšŸĻcX†AR.&¸vē†"eLc× íz'äáĘëč•Į0÷t‘WmEŠuļ™{)šŌO§ė‡LKĪR°ŠĀÚ~‡¤OŠrËÚģîSíˇ†aL?r?ŗ;Š×GŗUųĸŗ]4?ÃjŗRUŅHI×ęõŅdų p¤â[kFęü4ä¯_ŠÚuØ\E×vœXMtŽú˛Nã!o˛/ÚIUe+ÕĻŦ˛LsũW|fņ''užGlWŗ+UÍã†f ZŦ˜åĖĻ~JN7Rfr¨…C‡kø ~PœNÄ_:ČģbqժП’Ķ­\›Į­fWĒ sĶW”‡°Ę#tÛ(lAŸíĄÁjôĘ!ǚ\A˛š—ÜKOMj˛b|04¨6O`Ąšž/,–‹†›üėđG]“Ķ;uįŽúËßņƯ.ņö§ĩd_´`uˇÉËë} y–ŅŠ†(ę`Š*æ•ß1­§4aĻEDûöOŅ;,U|Kŗyŗí Wšųė/>d&MĮŲČŖc.qķ܉<:áMXZ:ķ[ŖŊPĢüŅzūH^SŸKJ@Á.ė:0ĶhĨĶ4@õÅ& îBr’įĄČcT×öcŒ&Ž֋äx5>=Ũ|f”‘ô ”Ü[$ƒ^lŠAę6’WoĄĶ6‚Ų<@ÅY#uJ5Yoû‚ÃJŲąkėžØKŖé ]ũ””ĩrŪņōŨĮ h×G8ÚĮîĶFŽõ<ĄŗįgN7sFVą+iĩËƚ w[ÉžØOŗé ãˇä–wĐNÖčuĻmÖ[ČģxƒJM~Ž}ԛˆlĸäėMęB4‡i\‹īJ4ļ~ņŠ 3+ĐĮžËåîĀ6?Éūô+Ŗ$"*’s9.SšŨ1ÁãQ˛ÃĩÃhl˛īzĖŲzˇC؛ŗ9ü4œ,|—’О‹ī1îģ‚„˜hŠÃŧĀ/”“…(Š4’Wō;Ŋp.Ƹƒ‘Cā¤ŽōEƒŧWŠČܙÄ­Ë\š™ģ.~CyY-y“Į ėO\0ĻB›”ÄeÚ(ēz›Ÿwā°’äõŅk\ģ{yt‚ĮŖŽ Ę',Ž +(ĒneãuŪĢV’–¤G/ŗã‹LŲÄīiŖäŌMÎ¸ķ™–ą™cîÁŒßŽæXŲSŲČÚ…ë”ßĖĩXÎÚĻʙ¸=‰âН(:~™ŋ•¤Ĩę9˙”"÷ŅãōÖQĨĢmösŪ$qôāŌ`Ü1šˇ›ÂĘ6NÄh8ĩxŊ/–gs{ˇ|#ųĶņh"p2îxFgƒŧ  ēäYŊž¤īŨĖŠ‹í”ŸžFҝ&mË;$øŖK¯[IĪå\×ĩ÷ĒíŽū´>Ž?íXBĀęĖßwĮhl4aM˜šoČÃOŠģØJAe/›­%2uhĨäŌMb&OV~?ÂõđÜTfŽ—ųœiņ+i<<‹)XMūūø)ëO”žS[š(<{…P‡ú˜hd„Ũē”ŽæĀžXŦį;Č(ėĀ'XÍGÛõä?ēM—;í Rrŧƒl÷89ë>#ęaUÃĄëæ?gČû5¤ÉŨüâ°ŗÃgj.™fŪ„Ŋ˛Ī~ûG ˜<{Ō 4Fuíähũ< ,ôÖˇqä/ā; Ī}7ãZĀú$ū˛w%U•ßpk čîæŨ3˙?mׇ\HđBŋc”m|V{‡ĒáxŸDí6ˇTŽĻ¸pA—:8Qv ķHĢ$6Ĩnä\ęęéÍÕčE}ȑīp9åUė cTWļQĮO8—3O? Ņqá%Wîŗįø7<~ž FMæĮ[80UīO8qø僓˙twķī͊ŖY$ŋ‰œÜdÆ/8Sq“ĸąe¯fëŽÍ{Š2Så=ūä|2ÚRl<į,ÍV\åŧcúØhŠ3CÉžūōÔÕ øFŽ\o#ŖÚÎør"Â4œ*ÔģœDEfj(]WģÉkz†ŦôA΅ƒ1ĶĮ[Háœ+tRTŲMaY72J´šPŽ~7˜.éâšŧĮ@ŅUŲÆ}}ĐG­åōΘiĢÔĀûë_˙ú×ŗ–ųˇĮgüũŋŪ;ø˙,/æīõ:ĪL/ŲĮ­ė(Ū@ēŸĖ™cM˜ˇĨs,Jā˙-ŊWjyĪōßî÷ˆ}p PPpönį¤îûĻ0FÅąjb3¨õŒ0H:.“2™Lŋ–ÚZ ī:°ûWčNĮ15~ÚÂׯzōīŒņōĪĶ— j)Q%Î ėÂÂ$XōĻ#’ ŋ‰œÚė;”!ęäuXRĶ}Ît­$'ĘīQ™ÆZ#uR8^‹iŲ‹u_JZ;¸Crˆã6+—0÷ƒžYō:vĪķö]eúÎ_ī%y{8ëüœ.×`ƒLōû‰¯ė9cŧüķ ĪΠV )@&ā_RËÆÄsaØ@IåuĘŒûŽ@¯‹ā\Ąî5™–čwlâԕvN”×°gø…Ë}Įå ÍzfÉߏ@ö}ŧûĨ˛ˇc[†&x5iīĪrCÁkD¸äū\r@ ū1OÉ @ Á$@ “@ L@ B0 @ Á$@ “@ Áŋ,âāĘ׆ú6J0ŒBPH(ûļĮą/Ęũ’Zŗ¤#l-É`˙|/!5H:rßũî¨Ų¨(=î~ëŧcˆę‹ßP~÷ }cô††üqä„y-˜ŗŪ+ĩlŧʍ#z÷ÛŪÉë œôÖßäŊj'ŋ.ņ|öRáZÅ-ö´¯āÜ&“Ž\ø{Ue×(°ŦĄvōŊK‹Õ×mPqė 'B6ņmN(Č”U9ß#ķØá….&š“;ײîīxŌuoÃuūGŊ’ŖÅÉî÷(}O¨nuúŠīrb6)Ēî§ų‘V’œÃŅÔî#[Č-iĸ.*‰ŋėÕL}lØOIe75Ļade ëŖ9ē#œČųÚŅÖĪîŌ6ēĸ6r#'tūÃ1H‡Ņ'T]ú†rãf‡5ûvƑĨõčĶ6‹ģ-‡yŒ:]8Å;cH”^aėxԏáĘu˛›8¸™œų^4DÅooQ2ļ†ßÔ{(:Ag‹’ĢZŨīŅKŅs,EũjeB0ũ8qŌyå48ÉÜąâ`čkíāPŲM럤ŗ?l īo y“SųĄČŗîki5P`QĄWšÁĩŗ7Éŗ¨8°3™Di CŊĸ˛;HG–ē@ŋZ^í&#{ę‡āU·v<ĄâtEŨvÆYąˆČhϤĮÉë>‚ÚÚĶĪ7œ*T! [)¯ü†ėK|3ũ†ņŋŪ¯ëF'œ{EÁdī1đáY ēmiZŋģé>…ˇÉ&?§JsÄjuE+uŖŗ ÍōōJŋ ‰æhn(AŖ|Qũ˙ã,ÜΝũōN™ŠŗŪâÆÎÄŌBé0BÕé[ Ē)ÎIFī7Bs­‘‚Ō;xOöéŅ•6Qđ&GsãĐ:†¨¨6’}ZIĶ!Ú%Üvĩ’×0ÁĻŊ[KN ›)˛ŦäTą~ÆéëæĻ[|xq‚MÛâšéƒlųŽ—É&Ú”%Ö­@ ‚éG‹ü€ 2úœLrŊ–AĨÂ[ŽĄāJ?YŸD.žā)ũY§›õJ‡Q š$gFģ,Cļ~ĘģŧØõÉÆ)aŖ×8i)hŖŽĮIfŦâõæÄåŲéŪIDATut€ĸŗũh3ÖĸŊ2°ôú ¤ô6„saûŲ—^˛¸› äÕÂGĄ|Ņôz›Eģ ą“r`}{žđõĢŋkl‰DĻlá^ĘkęV‡ą:Vžō˙Z-˜ÃŪâO)jW[Æč9ÚcáŊģĖŠŌŒ˛÷64S4¸†âØJ͟wÖŠS†Q›;ų&p5zՏwPnZÃą)aí¤ķJ3%ƒjNëæˇ>Ŋ$Ė÷9cōåĀÁėĶēĶŲë¤åS5=dÆz!ģŠũ _ëŨīÁSŖSÚ¸Uū_4Ę:r¤%ŒŠJí§ ÂBPÆfNÆøÎ_ī]mä5ÁŽũȜ!ļžpžŪFPJ§SÜ/ģÕŽ&âųS~VCJ܌f/Xf@ Ķ{ĪšYÍÉõž“ļ/Éņj¨°Đ2IĻûĶaK?‡*:¨˛ŒáŖRķŅÎ ėšßÖÛÔAr ĩąî‰]ZÃÉO4h=­@JŪ(đöp3™ŠĒûi|ä$č i’ķÕōęįļfU´Ō¨‰ãFŧ“Â9‚iCƒÛ­7čD ^MÖûąˆ•đņ[Á;ëõlM‰d]Wã˅ÕŲ~¤m[ØˇĒƒ/æųĘËëkŒæúVŠŦô*Đé‰xž üāVĪZÍj´8Š.ģÄÍō÷9Ņú„>‡瘎8•éZôÍ’JGČĪUQSŅM㨚sŋŲHēRæÚÅvNLēD53Ũ™rËMÖV*šđŸIVârĄVļsÂ8„ŲĄ "l rô¤ĢÜm8ëēVŖ!g šÆkdˇÚÆĘÆ=÷5§J7“Ös›¤J+ãsʧâč,+ã+ÚrÅ ë—Ũl$¯ÖÉG…1č[`jQĸąûÚčˆÖŸ°0ŌVõQ×>aĢ]åíq‰‹üũH_ĀB¸p:@H4ŠŖ ōtģúy!ãîīyëŪá÷ažč=ú¸R÷rŌBmöØq÷Ēŗ aqÜH]=ŋ•Q~@A…Íļ-›3.ČŖ I3?—|• `uĘ%”Y üĶ"ėÅßŗåãRÚYą1R°„úų0]ļŠåŸĒëŦۖDSq2‚Ÿrât+×FįĘdRŖ§-Ĩ?ë¤)—‡]~BuE-!‘ė‹RLYl˛Īö#GĮsûxĩÛT<ļ<›Z`—šWsS3…5'sÖĖk37Üâƒ+OŅŊŋ‰¯K3¸œƒ“éH¤§F˛îĨ/’ãZe+5ÁzN%I ¸Č^^_æ†&˛kŸĄßž™¯“ÎąˇĮčz8_]Z9ķۛ”ŗ–SÛ&Kķ‚Ž†Vž Œ“Ķš‘Ŗc;{j‡<˛hĨ¤ĸ{T$ÅáŦķãÚŲëėéV˛/7ãņ-œŒvP^v“2ŗsž„G¨>}“"›šŌâŸc:š…bÂr#wœŊI^ųšéŗ…ŌÖA'‰9?ᯖŧWŊÉís;<ˇ™,ÉõâŪGŌš=įgi× 5ŨįŗŽė€ũa/å÷ėĘŸa=,9׋Oęģė×ÎW?ЄÍV@ē`0?ÆîîŖ%gM˜%¨;[Ã۟ÖōAŎFg‰â…ŌPúĸ ņŅū˛ņ-˛É-X|¤@ô!žS-Ģų‡.YâŖŨ§%Œ ˇá+õ*’ģy¯ā$k¤Ä(ģĘ2Ų^mÔ9|oŊÅÛ5ü÷˛6ǧÚVÅĻ(%†&#×lŽĪėPtu€aÉĩr‰eÂÂôceÜáĨ‚€9‹‚œØĮ_€díÚ@–Û'’ŗķ-ę>5pŅ8Az‚×Ü2k¸?˙›×{¯Ô˛ņú0ãž*įžé'NZúé ‰æë—ëEĩ–ëû¨ģ÷ y5w‘we„­šIŽiŽ ŗrĻÁ†6%c n×DŒnĻëc1‘ŲÔLaŠŌâđ—¸+_V_2įl¨ãßãdŦËÚĄMĐŗĢé;N˞„]̏Í?=7rÃgēâĸô\Øî !zŽõXȸgĸs[ ģ,N´)›ų}Šģ Ų}OAVá˛ÜUŊí]ôÔrâúûfõšû(īņ'ŋ8†D€ÉÛŪbSkU&=ëîS~OAVžĮũ’âH|YÅ)ŊPK/_„}ĸôœË¸É‡åœ˛ÚiĸßæKÍ´eŽą˛•š€ˇ¸‘8÷'ãĪARÎvņ*đV.ƒQ'〚ŪHÕđ ŌRŪ"˙íŒ?āĖ%#”AĶĄĩhKg ĪĄę"2ļĨš+˛Ģ~{…ŧ^ĐDđeęüņRķŽŅœ¨ĩ&‚}Û"ĐųÚéję čķ›ČųœÔyaīé ¤ÛIÄúl %čųSęŽČ+ÁûH2é’é;7°īl+{VÁōeI^ȃ/ˆø÷0ˇ;îo(ŗ@ ‚éĮ‚ˇR'Ãķ,<Ã(đņ^p<­.’D„ß ZsvČúÔ$°ŌDnËĀ”*ch5RXváO2(“é´8Š\Ŋ`ŒÎĸyUZ)9ׁŋ™â\…ČCt *ŅEũ ÂC— Kۛž gŅú’ŸŌ9¸ nņ<Œ;@Ÿ´fn( Ž‚­­#˜¸“?›ĸ§]ģeˆ.$öi<…„?ú0_än+f43ğũŅ}ĪeΔ×pŪķs ŲáqŋĨ<0YõíüŦÂęa™,‹šcG’É’œtÖßäÃzØēkYÆ­T\2’Qę⯑‚ZZÉëZÉŅ#kįī'JŪËAv8ÅŦē|ávQwoíŋoæTĒ;ˆ>$S~Ãl,íŖÚŧ–,Ë"éĖą PRŪÆ7QøSjāÉ"Í6DãÕ¯Ø}ü_Ų0ĶĘ´ĀØą÷< ŅĄæX~ܔÛ22{%ãj8Ô4ĀQŨēÚxŦYËåŊk‰t[Ô"sĄëĶfž¸;BzŠ?HĄōsŠȎ š+¯ŗĮņg¸DģĩåË,„`ú1ĄÕŦĀģaķ(3â,ėdŦËũŅŠÛ|˙éÚŠ{û)fY_:¨AÅ$˙—[ü$Sô|ÔūGĘ[l‡šVŸī“×˙ĶOÉŖ<ļŨd­Göøķ4–üŽō0=ˇ÷~ŋúęm5a{Fįé?P=õé †ŸÃŋ|@DÆfūŊX}š_45/2÷n%ÍoqQbĮ (_ßtJŪËÕ=2˙‘ ööWŋĨĪí0į<1L ‚$—õī|Ŋ Męp,ÁŨ´čüžŌRjĄŅü&ęև<[FÁ§ŋŖ`ōߟŋ`˜;ŧŅåKVn:\´ČĀjOšF× hŖđÁeta+gԗF…fšËđˇMg+Įĸ&…Ž•’Ō;Ô빜Ŗy‰ÕQ¤ZMæÎhj~ÕFU“äÅĸcGļ3.ŠĐI3XD°/ã–1ãÄ:<Ŧv‹%7~+ŅĢ–QeügXúėÆ;Ũ…´ãÜÂ˙Ë,„`úąáĨ!UwGȜš¨Į¨kĩAX ûcĸsXÉ:˙Œōg 2ē”DWа§j¸NRÃJ~$n:6Ã1†e$å2ĀíĒeXG™>fÜá\z^˙-œĻŌˇ=ƒTGPpÜÄēũ›øHã‹ÚΆ.ĀAKĪč^ŨĘ™ącŌÄĖEŋ§÷ĒŊ(=CĸäM/¯/? ī ,ƒž ™sūũŧ– ‚œtõČō"°ęųhŅŅÁâ$3jr‘ĄË4†w° -Ė(“FE÷šer’Ĩ˜÷~ôĶbr’Š[ĸ•Ié‹Zõ’ë'vĀgV&ËīŖô%+w›F=E—“žĢˇØ-GĶ´SCĘ—Žčœ¸g°mõT_ŗ›L4>ōaĶÎ@` Ũ*øÂ$C´¨˛›Ŧô!‘ŧ”t<ÅŌmĒWéŠŨ;ËmĘ×N×P¤Œ§i¯fÚ"(a}Ž`§Ûđ%cGŊjAōSēdX7u“:-c…HĄ@ģĘšw3ĄĶyĩa°vË,S¨­ŸÂʇ°>ÉãiģW(ŗ@ ø§D}_¤p$ųĶrĨ‰CMLT]¸EQ/9Û3ĘȎ zJîŽŊB^H’?Z•į/>( đGí§ÔėJY‰šá+ĩ?Á*Đi4˛ûl/抅{ûčō¨Ûz4:<ęœÚĢUŗŌŧĻ>Ÿ~ōé%õĨT“ãƒĄÁ@ĩyûčÍõm|a™UiŖ ×đAÃМúīšO‰qĢ<‚ĄŠ•"#$'‡/ėJ y“}ŅNĒ*[Š6`•ešëŋâ3‹?9КšĸLNžĒ*îPŅ#c•e:FrËÚhB"ĻîWeÁ*Ņ\‡ŨõO°’Ę d-=Ohn˜ŽÛ— *5™: ĩ͔ĩ?ÁlĄˇ§—‚ĘČoDøųÎlj _×įŽļ}ę[¤ö‘įîkcyg{yC~”đ'3%{k3š :ÍCÚ dWXđ‰v=Žŋ„tĩRVv› G(ųëŊč3Zht˙lN‹ĤP¸ÛJöÅ~šMO0ŋ%ˇŧƒŽāp˛ĸXŌØAÍŽ'”œ6PŨ3D¯i€Ēŗˇ(ą’ŋ%`]J ļvWôŌlĸŗ§ŸCeę¤Hōc<ŨĶ#TUhTū”“;53㨖Rf@ ,L?^ŦÛą™ËR%ĩˇ9?ėzÜüŖÜ¸™AúĒŲeŖä¸èmØÎíÕO?MæŪ!ëSߝ˙é)œs…NŠ*ģ),ëFF‰VĘŅOâČq?–-ÅÆsÎŌLaÅUÎ;V Ļ83”ė믘×ÅŦDŠ›¸@+%—n3 A5ģŪp‹ '5|xwÚL•WxXÉŅW9-üĨõĨ q{Å_Qtü2~+IKÕs8ū)EĪgZ\äŅ dyŽõÉ;8ššØøų3ė+IÛļ‰c ž/Ë™š›áâ7”—Õ’7yŦĀūÄâ|Ißģ™SÛ)?}ĸ1 ^MږwÜVG˛önÂ^ŲÎgŋũ#(‰ˆZÃíŽøŸõoąĢé%eˇĐ†…SĒ ķtã\ŧHÎŲĖš+Ę/Ũâİ|WÃīˇŋB\ΊƒPRŲáękĘ$ŦãO;Ļ7Rė.;Ú(ēÚĘ{—xŦ$9~'ˇi–|io}GūâžŖđÜw3ŽŦwŽ-éâšŧĮ@ŅUŲÆ}}ĐG­åōΘiKëbc‡@öįoħŌHIŲ}ŖDĻáØA=Y“ũQĩ–/÷ÃĄ‹Ũė9ū YšŊ.šËÛuŗžļkæÉ—]šq ¤%ūUųoũë_˙úcŽ€ģq|Æß˙ëŊƒĸWüKã¤ēŦŠ /dev/null; then echo "${cmd} not found, ${msg}" return 1 fi } usage() { echo "$0 [--coverage] [--fix] [--no-mod-tidy] [--no-build] [--no-linters] [--no-generate] [--empty-diff]" } main() { local coverage=0 local fix=0 local run_mod_tidy=1 local run_build=1 local run_lint=1 local run_generate=1 local empty_diff=0 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 ;; --no-generate) run_generate=0 ;; --empty-diff) empty_diff=1 ;; *) 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' ' ')" # Prevent the creation of proto files with .txt extensions. bad_protos="$(find . -name '*.pb.txt' -o -name '*.proto.txt' -print)" if [[ "${bad_protos}" != "" ]]; then echo "Text-based protos must use the .textproto extension:" echo $bad_protos exit 1 fi 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 if [[ "${run_generate}" -eq 1 ]]; then check_cmd protoc 'have you installed protoc?' check_pkg mockgen github.com/golang/mock/mockgen || exit 1 check_pkg stringer golang.org/x/tools/cmd/stringer || exit 1 echo 'running go generate' go generate -run="protoc" ./... go generate -run="mockgen" ./... go generate -run="stringer" ./... fi if [[ "${empty_diff}" -eq 1 ]]; then echo 'checking git diff is empty' git diff --exit-code fi } main "$@" trillian-1.6.1/scripts/resetcrdb.sh000077500000000000000000000100311466362047600173510ustar00rootroot00000000000000#!/bin/bash set -e usage() { cat < /dev/stderr exit 1 } collect_vars() { # set unset environment variables to defaults [ -z ${CRDB_ROOT_USER+x} ] && CRDB_ROOT_USER="root" [ -z ${CRDB_HOST+x} ] && CRDB_HOST="localhost" [ -z ${CRDB_PORT+x} ] && CRDB_PORT="26257" [ -z ${CRDB_DATABASE+x} ] && CRDB_DATABASE="defaultdb" [ -z ${CRDB_USER+x} ] && CRDB_USER="test" [ -z ${CRDB_PASSWORD+x} ] && CRDB_PASSWORD="zaphod" [ -z ${CRDB_USER_HOST+x} ] && CRDB_USER_HOST="localhost" [ -z ${CRDB_INSECURE+x} ] && CRDB_INSECURE="true" [ -z ${CRDB_IN_CONTAINER+x} ] && CRDB_IN_CONTAINER="false" [ -z ${CRDB_CONTAINER_NAME+x} ] && CRDB_CONTAINER_NAME="roach" FLAGS=() # handle flags FORCE=false VERBOSE=false while [[ $# -gt 0 ]]; do case "$1" in --force) FORCE=true ;; --verbose) VERBOSE=true ;; --help) usage; exit ;; *) FLAGS+=("$1") esac shift 1 done FLAGS+=(-u "${CRDB_ROOT_USER}") FLAGS+=(--host "${CRDB_HOST}") FLAGS+=(--port "${CRDB_PORT}") # Useful for debugging FLAGS+=(--echo-sql) if [[ ${CRDB_INSECURE} = 'true' ]]; then FLAGS+=(--insecure) fi # Optionally print flags (before appending password) [[ ${VERBOSE} = 'true' ]] && echo "- Using CRDB Flags: ${FLAGS[@]}" # append password if supplied [ -z ${CRDB_ROOT_PASSWORD+x} ] || FLAGS+=(-p"${CRDB_ROOT_PASSWORD}") if [[ ${CRDB_IN_CONTAINER} = 'true' ]]; then CMD="docker exec -i ${CRDB_CONTAINER_NAME} cockroach" else CMD="cockroach" fi } main() { collect_vars "$@" readonly TRILLIAN_PATH=$(go list -f '{{.Dir}}' github.com/google/trillian) echo "Warning: about to destroy and reset database '${CRDB_DATABASE}'" [[ ${FORCE} = true ]] || read -p "Are you sure? [Y/N]: " -n 1 -r echo # Print newline following the above prompt if [ -z ${REPLY+x} ] || [[ $REPLY =~ ^[Yy]$ ]] then echo "Resetting DB..." set -eux $CMD sql "${FLAGS[@]}" -e "DROP DATABASE IF EXISTS ${CRDB_DATABASE};" || \ die "Error: Failed to drop database '${CRDB_DATABASE}'." $CMD sql "${FLAGS[@]}" -e "CREATE DATABASE ${CRDB_DATABASE};" || \ die "Error: Failed to create database '${CRDB_DATABASE}'." if [[ ${CRDB_INSECURE} = 'true' ]]; then $CMD sql "${FLAGS[@]}" -e "CREATE USER IF NOT EXISTS ${CRDB_USER};" || \ die "Error: Failed to create user '${CRDB_USER}'." else $CMD sql "${FLAGS[@]}" -e "CREATE USER IF NOT EXISTS ${CRDB_USER} WITH PASSWORD '${CRDB_PASSWORD}';" || \ die "Error: Failed to create user '${CRDB_USER}'." fi $CMD sql "${FLAGS[@]}" -e "GRANT ALL PRIVILEGES ON DATABASE ${CRDB_DATABASE} TO ${CRDB_USER} WITH GRANT OPTION" || \ die "Error: Failed to grant '${CRDB_USER}' user all privileges on '${CRDB_DATABASE}'." $CMD sql "${FLAGS[@]}" -d ${CRDB_DATABASE} < ${TRILLIAN_PATH}/storage/crdb/schema/storage.sql || \ die "Error: Failed to create tables in '${CRDB_DATABASE}' database." echo "Reset Complete" fi } main "$@" trillian-1.6.1/scripts/resetdb.sh000077500000000000000000000063001466362047600170300ustar00rootroot00000000000000#!/bin/bash set -e usage() { cat < /dev/stderr exit 1 } collect_vars() { # set unset environment variables to defaults [ -z ${MYSQL_ROOT_USER+x} ] && MYSQL_ROOT_USER="root" [ -z ${MYSQL_HOST+x} ] && MYSQL_HOST="localhost" [ -z ${MYSQL_PORT+x} ] && MYSQL_PORT="3306" [ -z ${MYSQL_DATABASE+x} ] && MYSQL_DATABASE="test" [ -z ${MYSQL_USER+x} ] && MYSQL_USER="test" [ -z ${MYSQL_PASSWORD+x} ] && MYSQL_PASSWORD="zaphod" [ -z ${MYSQL_USER_HOST+x} ] && MYSQL_USER_HOST="localhost" FLAGS=() # handle flags FORCE=false VERBOSE=false while [[ $# -gt 0 ]]; do case "$1" in --force) FORCE=true ;; --verbose) VERBOSE=true ;; --help) usage; exit ;; *) FLAGS+=("$1") esac shift 1 done FLAGS+=(-u "${MYSQL_ROOT_USER}") FLAGS+=(--host "${MYSQL_HOST}") FLAGS+=(--port "${MYSQL_PORT}") # Optionally print flags (before appending password) [[ ${VERBOSE} = 'true' ]] && echo "- Using MySQL Flags: ${FLAGS[@]}" # append password if supplied [ -z ${MYSQL_ROOT_PASSWORD+x} ] || FLAGS+=(-p"${MYSQL_ROOT_PASSWORD}") } main() { collect_vars "$@" readonly TRILLIAN_PATH=$(go list -f '{{.Dir}}' github.com/google/trillian) echo "Warning: about to destroy and reset database '${MYSQL_DATABASE}'" [[ ${FORCE} = true ]] || read -p "Are you sure? [Y/N]: " -n 1 -r echo # Print newline following the above prompt if [ -z ${REPLY+x} ] || [[ $REPLY =~ ^[Yy]$ ]] then echo "Resetting DB..." mysql "${FLAGS[@]}" -e "DROP DATABASE IF EXISTS ${MYSQL_DATABASE};" || \ die "Error: Failed to drop database '${MYSQL_DATABASE}'." mysql "${FLAGS[@]}" -e "CREATE DATABASE ${MYSQL_DATABASE};" || \ die "Error: Failed to create database '${MYSQL_DATABASE}'." mysql "${FLAGS[@]}" -e "CREATE USER IF NOT EXISTS ${MYSQL_USER}@'${MYSQL_USER_HOST}' IDENTIFIED BY '${MYSQL_PASSWORD}';" || \ die "Error: Failed to create user '${MYSQL_USER}@${MYSQL_USER_HOST}'." mysql "${FLAGS[@]}" -e "GRANT ALL ON ${MYSQL_DATABASE}.* TO ${MYSQL_USER}@'${MYSQL_USER_HOST}'" || \ die "Error: Failed to grant '${MYSQL_USER}' user all privileges on '${MYSQL_DATABASE}'." mysql "${FLAGS[@]}" -D ${MYSQL_DATABASE} < ${TRILLIAN_PATH}/storage/mysql/schema/storage.sql || \ die "Error: Failed to create tables in '${MYSQL_DATABASE}' database." echo "Reset Complete" fi } main "$@" trillian-1.6.1/server/000077500000000000000000000000001466362047600146615ustar00rootroot00000000000000trillian-1.6.1/server/admin/000077500000000000000000000000001466362047600157515ustar00rootroot00000000000000trillian-1.6.1/server/admin/admin_server.go000066400000000000000000000134561466362047600207670ustar00rootroot00000000000000// 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 admin import ( "context" "fmt" "github.com/google/trillian" "github.com/google/trillian/extension" "github.com/google/trillian/storage" "google.golang.org/genproto/protobuf/field_mask" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "k8s.io/klog/v2" ) // Server is an implementation of trillian.TrillianAdminServer. type Server struct { registry extension.Registry allowedTreeTypes []trillian.TreeType } // New returns a trillian.TrillianAdminServer implementation. // registry is the extension.Registry used by the Server. // allowedTreeTypes defines which tree types may be created through this server, // with nil meaning unrestricted. func New(registry extension.Registry, allowedTreeTypes []trillian.TreeType) *Server { return &Server{ registry: registry, allowedTreeTypes: allowedTreeTypes, } } // IsHealthy returns nil if the server is healthy, error otherwise. // TODO(Martin2112): This method (and the one in the log server) should probably have ctx as a param func (s *Server) IsHealthy() error { return s.registry.AdminStorage.CheckDatabaseAccessible(context.Background()) } // ListTrees implements trillian.TrillianAdminServer.ListTrees. func (s *Server) ListTrees(ctx context.Context, req *trillian.ListTreesRequest) (*trillian.ListTreesResponse, error) { // TODO(codingllama): This needs access control resp, err := storage.ListTrees(ctx, s.registry.AdminStorage, req.GetShowDeleted()) if err != nil { return nil, err } return &trillian.ListTreesResponse{Tree: resp}, nil } // GetTree implements trillian.TrillianAdminServer.GetTree. func (s *Server) GetTree(ctx context.Context, req *trillian.GetTreeRequest) (*trillian.Tree, error) { tree, err := storage.GetTree(ctx, s.registry.AdminStorage, req.GetTreeId()) if err != nil { return nil, err } return tree, nil } // CreateTree implements trillian.TrillianAdminServer.CreateTree. func (s *Server) CreateTree(ctx context.Context, req *trillian.CreateTreeRequest) (*trillian.Tree, error) { tree := req.GetTree() if tree == nil { return nil, status.Errorf(codes.InvalidArgument, "a tree is required") } if err := s.validateAllowedTreeType(tree.TreeType); err != nil { return nil, status.Error(codes.InvalidArgument, err.Error()) } if tree.TreeType != trillian.TreeType_LOG && tree.TreeType != trillian.TreeType_PREORDERED_LOG { return nil, status.Errorf(codes.InvalidArgument, "invalid tree type: %v", tree.TreeType) } // Clear generated fields, storage must set those tree.TreeId = 0 tree.CreateTime = nil tree.UpdateTime = nil tree.Deleted = false tree.DeleteTime = nil createdTree, err := storage.CreateTree(ctx, s.registry.AdminStorage, tree) if err != nil { return nil, err } return createdTree, nil } func (s *Server) validateAllowedTreeType(tt trillian.TreeType) error { if s.allowedTreeTypes == nil { return nil // All types OK } for _, allowedType := range s.allowedTreeTypes { if tt == allowedType { return nil } } return fmt.Errorf("tree type %s not allowed by this server", tt) } // UpdateTree implements trillian.TrillianAdminServer.UpdateTree. func (s *Server) UpdateTree(ctx context.Context, req *trillian.UpdateTreeRequest) (*trillian.Tree, error) { tree := req.GetTree() mask := req.GetUpdateMask() if tree == nil { return nil, status.Errorf(codes.InvalidArgument, "a tree is required") } // Apply the mask to a couple of empty trees just to check that the paths are correct. if err := applyUpdateMask(&trillian.Tree{}, &trillian.Tree{}, mask); err != nil { return nil, err } updatedTree, err := storage.UpdateTree(ctx, s.registry.AdminStorage, tree.TreeId, func(other *trillian.Tree) { if err := applyUpdateMask(tree, other, mask); err != nil { // Should never happen (famous last words). klog.Errorf("Error applying mask on tree update: %v", err) } }) if err != nil { return nil, err } return updatedTree, nil } func applyUpdateMask(from, to *trillian.Tree, mask *field_mask.FieldMask) error { if mask == nil || len(mask.Paths) == 0 { return status.Errorf(codes.InvalidArgument, "an update_mask is required") } for _, path := range mask.Paths { switch path { case "tree_state": to.TreeState = from.TreeState case "tree_type": to.TreeType = from.TreeType case "display_name": to.DisplayName = from.DisplayName case "description": to.Description = from.Description case "storage_settings": to.StorageSettings = from.StorageSettings case "max_root_duration": to.MaxRootDuration = from.MaxRootDuration default: return status.Errorf(codes.InvalidArgument, "invalid update_mask path: %q", path) } } return nil } // DeleteTree implements trillian.TrillianAdminServer.DeleteTree. func (s *Server) DeleteTree(ctx context.Context, req *trillian.DeleteTreeRequest) (*trillian.Tree, error) { tree, err := storage.SoftDeleteTree(ctx, s.registry.AdminStorage, req.GetTreeId()) if err != nil { return nil, err } return tree, nil } // UndeleteTree implements trillian.TrillianAdminServer.UndeleteTree. func (s *Server) UndeleteTree(ctx context.Context, req *trillian.UndeleteTreeRequest) (*trillian.Tree, error) { tree, err := storage.UndeleteTree(ctx, s.registry.AdminStorage, req.GetTreeId()) if err != nil { return nil, err } return tree, nil } trillian-1.6.1/server/admin/admin_server_test.go000066400000000000000000000503571466362047600220270ustar00rootroot00000000000000// 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 admin import ( "context" "errors" "strings" "testing" "time" "github.com/golang/mock/gomock" "github.com/google/go-cmp/cmp" "github.com/google/trillian" "github.com/google/trillian/extension" "github.com/google/trillian/storage" "github.com/google/trillian/storage/testonly" "google.golang.org/genproto/protobuf/field_mask" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/durationpb" "google.golang.org/protobuf/types/known/emptypb" "google.golang.org/protobuf/types/known/timestamppb" ttestonly "github.com/google/trillian/testonly" ) func TestServer_BeginError(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() validTree := proto.Clone(testonly.LogTree).(*trillian.Tree) tests := []struct { desc string fn func(context.Context, *Server) error snapshot bool }{ { desc: "ListTrees", fn: func(ctx context.Context, s *Server) error { _, err := s.ListTrees(ctx, &trillian.ListTreesRequest{}) return err }, snapshot: true, }, { desc: "GetTree", fn: func(ctx context.Context, s *Server) error { _, err := s.GetTree(ctx, &trillian.GetTreeRequest{TreeId: 12345}) return err }, snapshot: true, }, { desc: "CreateTree", fn: func(ctx context.Context, s *Server) error { _, err := s.CreateTree(ctx, &trillian.CreateTreeRequest{Tree: validTree}) return err }, }, } ctx := context.Background() for _, test := range tests { as := storage.NewMockAdminStorage(ctrl) if test.snapshot { as.EXPECT().Snapshot(gomock.Any()).Return(nil, errors.New("snapshot error")) } else { as.EXPECT().ReadWriteTransaction(gomock.Any(), gomock.Any()).Return(errors.New("begin error")) } registry := extension.Registry{ AdminStorage: as, } s := &Server{registry: registry} if err := test.fn(ctx, s); err == nil { t.Errorf("%v: got = %v, want non-nil", test.desc, err) } } } func TestServer_ListTrees(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() activeLog := proto.Clone(testonly.LogTree).(*trillian.Tree) frozenLog := proto.Clone(testonly.LogTree).(*trillian.Tree) frozenLog.TreeState = trillian.TreeState_FROZEN deletedLog := proto.Clone(testonly.LogTree).(*trillian.Tree) id := int64(17) nowPB := timestamppb.Now() for _, tree := range []*trillian.Tree{activeLog, frozenLog, deletedLog} { tree.TreeId = id tree.CreateTime = proto.Clone(nowPB).(*timestamppb.Timestamp) tree.UpdateTime = proto.Clone(nowPB).(*timestamppb.Timestamp) id++ nowPB.Seconds++ } for _, tree := range []*trillian.Tree{deletedLog} { tree.Deleted = true tree.DeleteTime = proto.Clone(nowPB).(*timestamppb.Timestamp) nowPB.Seconds++ } nonDeletedTrees := []*trillian.Tree{activeLog, frozenLog} allTrees := []*trillian.Tree{activeLog, frozenLog, deletedLog} tests := []struct { desc string req *trillian.ListTreesRequest trees []*trillian.Tree }{ {desc: "emptyNonDeleted", req: &trillian.ListTreesRequest{}}, {desc: "empty", req: &trillian.ListTreesRequest{ShowDeleted: true}}, {desc: "nonDeleted", req: &trillian.ListTreesRequest{}, trees: nonDeletedTrees}, { desc: "allTreesDeleted", req: &trillian.ListTreesRequest{ShowDeleted: true}, trees: allTrees, }, } ctx := context.Background() for _, test := range tests { setup := setupAdminServer( ctrl, true, /* snapshot */ true, /* shouldCommit */ false /* commitErr */) tx := setup.snapshotTX tx.EXPECT().ListTrees(gomock.Any(), test.req.ShowDeleted).Return(test.trees, nil) s := setup.server resp, err := s.ListTrees(ctx, test.req) if err != nil { t.Errorf("%v: ListTrees() returned err = %v", test.desc, err) continue } want := []*trillian.Tree{} for _, tree := range test.trees { wantTree := proto.Clone(tree).(*trillian.Tree) want = append(want, wantTree) } for i, wantTree := range want { if !proto.Equal(resp.Tree[i], wantTree) { t.Errorf("%v: post-ListTrees() diff (-got +want):\n%v", test.desc, cmp.Diff(resp.Tree, want)) break } } } } func TestServer_ListTreesErrors(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() tests := []struct { desc string listErr error commitErr bool }{ {desc: "listErr", listErr: errors.New("error listing trees")}, {desc: "commitErr", commitErr: true}, } ctx := context.Background() for _, test := range tests { setup := setupAdminServer( ctrl, true, /* snapshot */ test.listErr == nil, /* shouldCommit */ test.commitErr /* commitErr */) tx := setup.snapshotTX tx.EXPECT().ListTrees(gomock.Any(), false).Return(nil, test.listErr) s := setup.server if _, err := s.ListTrees(ctx, &trillian.ListTreesRequest{}); err == nil { t.Errorf("%v: ListTrees() returned err = nil, want non-nil", test.desc) } } } func TestServer_GetTree(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() tests := []struct { desc string getErr, commitErr bool }{ { desc: "success", }, { desc: "unknownTree", getErr: true, }, { desc: "commitError", commitErr: true, }, } ctx := context.Background() for _, test := range tests { setup := setupAdminServer( ctrl, true, /* snapshot */ !test.getErr, /* shouldCommit */ test.commitErr) tx := setup.snapshotTX s := setup.server storedTree := proto.Clone(testonly.LogTree).(*trillian.Tree) storedTree.TreeId = 12345 if test.getErr { tx.EXPECT().GetTree(gomock.Any(), storedTree.TreeId).Return(nil, errors.New("GetTree failed")) } else { tx.EXPECT().GetTree(gomock.Any(), storedTree.TreeId).Return(storedTree, nil) } wantErr := test.getErr || test.commitErr tree, err := s.GetTree(ctx, &trillian.GetTreeRequest{TreeId: storedTree.TreeId}) if hasErr := err != nil; hasErr != wantErr { t.Errorf("%v: GetTree() = (_, %v), wantErr = %v", test.desc, err, wantErr) continue } else if hasErr { continue } wantTree := proto.Clone(storedTree).(*trillian.Tree) if diff := cmp.Diff(tree, wantTree, cmp.Comparer(proto.Equal)); diff != "" { t.Errorf("%v: post-GetTree diff (-got +want):\n%v", test.desc, diff) } } } func TestServer_CreateTree(t *testing.T) { validTree := proto.Clone(testonly.LogTree).(*trillian.Tree) invalidTree := proto.Clone(validTree).(*trillian.Tree) invalidTree.TreeState = trillian.TreeState_UNKNOWN_TREE_STATE tests := []struct { desc string req *trillian.CreateTreeRequest createErr error commitErr, wantCommit bool wantErr string }{ { desc: "validTree", req: &trillian.CreateTreeRequest{Tree: validTree}, wantCommit: true, }, { desc: "nilTree", req: &trillian.CreateTreeRequest{}, wantErr: "tree is required", }, { desc: "createErr", req: &trillian.CreateTreeRequest{Tree: invalidTree}, createErr: errors.New("storage CreateTree failed"), wantErr: "storage CreateTree failed", }, { desc: "commitError", req: &trillian.CreateTreeRequest{Tree: validTree}, commitErr: true, wantCommit: true, wantErr: "commit error", }, } ctx := context.Background() for _, test := range tests { t.Run(test.desc, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() setup := setupAdminServer(ctrl, false /* snapshot */, test.wantCommit, test.commitErr) tx := setup.tx s := setup.server nowPB := timestamppb.Now() if test.req.Tree != nil { newTree := proto.Clone(test.req.Tree).(*trillian.Tree) newTree.TreeId = 12345 newTree.CreateTime = nowPB newTree.UpdateTime = nowPB tx.EXPECT().CreateTree(gomock.Any(), gomock.Any()).MaxTimes(1).Return(newTree, test.createErr) } // Copy test.req so that any changes CreateTree makes don't affect the original, which may be shared between tests. reqCopy := proto.Clone(test.req).(*trillian.CreateTreeRequest) tree, err := s.CreateTree(ctx, reqCopy) switch gotErr := err != nil; { case gotErr && !strings.Contains(err.Error(), test.wantErr): t.Fatalf("CreateTree() = (_, %q), want (_, %q)", err, test.wantErr) case gotErr: return case test.wantErr != "": t.Fatalf("CreateTree() = (_, nil), want (_, %q)", test.wantErr) } wantTree := proto.Clone(test.req.Tree).(*trillian.Tree) wantTree.TreeId = 12345 wantTree.CreateTime = nowPB wantTree.UpdateTime = nowPB if diff := cmp.Diff(tree, wantTree, cmp.Comparer(proto.Equal)); diff != "" { t.Fatalf("post-CreateTree diff (-got +want):\n%v", diff) } }) } } func TestServer_CreateTree_AllowedTreeTypes(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() tests := []struct { desc string treeTypes []trillian.TreeType req *trillian.CreateTreeRequest wantCode codes.Code wantMsg string }{ { desc: "preorderedLogOnLogServer", treeTypes: []trillian.TreeType{trillian.TreeType_LOG}, req: &trillian.CreateTreeRequest{Tree: testonly.PreorderedLogTree}, wantCode: codes.InvalidArgument, wantMsg: "tree type PREORDERED_LOG not allowed", }, { desc: "logOnLogServer", treeTypes: []trillian.TreeType{trillian.TreeType_LOG}, req: &trillian.CreateTreeRequest{Tree: testonly.LogTree}, wantCode: codes.OK, }, { desc: "preorderedLogAllowed", treeTypes: []trillian.TreeType{trillian.TreeType_LOG, trillian.TreeType_PREORDERED_LOG}, req: &trillian.CreateTreeRequest{Tree: testonly.PreorderedLogTree}, wantCode: codes.OK, }, // treeTypes = nil is exercised by all other tests. } ctx := context.Background() for _, test := range tests { setup := setupAdminServer( ctrl, false, // snapshot test.wantCode == codes.OK, false) s := setup.server tx := setup.tx s.allowedTreeTypes = test.treeTypes // Storage interactions aren't the focus of this test, so mocks are configured in a rather // permissive way. tx.EXPECT().CreateTree(gomock.Any(), gomock.Any()).AnyTimes().Return(&trillian.Tree{}, nil) _, err := s.CreateTree(ctx, test.req) switch s, ok := status.FromError(err); { case !ok || s.Code() != test.wantCode: t.Errorf("%v: CreateTree() returned err = %v, wantCode = %s", test.desc, err, test.wantCode) case err != nil && !strings.Contains(err.Error(), test.wantMsg): t.Errorf("%v: CreateTree() returned err = %q, wantMsg = %q", test.desc, err, test.wantMsg) } } } func TestServer_UpdateTree(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() nowPB := timestamppb.Now() existingTree := proto.Clone(testonly.LogTree).(*trillian.Tree) existingTree.TreeId = 12345 existingTree.CreateTime = nowPB existingTree.UpdateTime = nowPB existingTree.MaxRootDuration = durationpb.New(1 * time.Nanosecond) // Any valid proto works here, the type doesn't matter for this test. settings := ttestonly.MustMarshalAny(t, &emptypb.Empty{}) // successTree specifies changes in all rw fields successTree := &trillian.Tree{ TreeState: trillian.TreeState_FROZEN, DisplayName: "Brand New Tree Name", Description: "Brand New Tree Desc", StorageSettings: settings, MaxRootDuration: durationpb.New(2 * time.Nanosecond), } successMask := &field_mask.FieldMask{ Paths: []string{"tree_state", "display_name", "description", "storage_settings", "max_root_duration"}, } successWant := proto.Clone(existingTree).(*trillian.Tree) successWant.TreeState = successTree.TreeState successWant.DisplayName = successTree.DisplayName successWant.Description = successTree.Description successWant.StorageSettings = successTree.StorageSettings successWant.MaxRootDuration = successTree.MaxRootDuration tests := []struct { desc string req *trillian.UpdateTreeRequest currentTree, wantTree *trillian.Tree updateErr error commitErr, wantErr, wantCommit bool }{ { desc: "success", req: &trillian.UpdateTreeRequest{Tree: successTree, UpdateMask: successMask}, currentTree: existingTree, wantTree: successWant, wantCommit: true, }, { desc: "nilTree", req: &trillian.UpdateTreeRequest{}, wantErr: true, }, { desc: "nilUpdateMask", req: &trillian.UpdateTreeRequest{Tree: successTree}, currentTree: existingTree, wantErr: true, }, { desc: "emptyUpdateMask", req: &trillian.UpdateTreeRequest{Tree: successTree, UpdateMask: &field_mask.FieldMask{}}, currentTree: existingTree, wantErr: true, }, { desc: "readonlyField", req: &trillian.UpdateTreeRequest{ Tree: successTree, UpdateMask: &field_mask.FieldMask{Paths: []string{"tree_id"}}, }, currentTree: existingTree, wantErr: true, }, { desc: "updateErr", req: &trillian.UpdateTreeRequest{Tree: successTree, UpdateMask: successMask}, updateErr: errors.New("error updating tree"), currentTree: existingTree, wantErr: true, }, { desc: "commitErr", req: &trillian.UpdateTreeRequest{Tree: successTree, UpdateMask: successMask}, currentTree: existingTree, commitErr: true, wantErr: true, wantCommit: true, }, } ctx := context.Background() for _, test := range tests { setup := setupAdminServer( ctrl, false, /* snapshot */ test.wantCommit, test.commitErr) tx := setup.tx s := setup.server if test.req.Tree != nil { tx.EXPECT().UpdateTree(gomock.Any(), test.req.Tree.TreeId, gomock.Any()).MaxTimes(1).Do(func(ctx context.Context, treeID int64, updateFn func(*trillian.Tree)) { // This step should be done by the storage layer, but since we're mocking it we have to trigger it ourselves. updateFn(test.currentTree) }).Return(test.currentTree, test.updateErr) } tree, err := s.UpdateTree(ctx, test.req) if hasErr := err != nil; hasErr != test.wantErr { t.Errorf("%v: UpdateTree() returned err = %q, wantErr = %v", test.desc, err, test.wantErr) continue } else if hasErr { continue } if !proto.Equal(tree, test.wantTree) { diff := cmp.Diff(tree, test.wantTree) t.Errorf("%v: post-UpdateTree diff:\n%v", test.desc, diff) } } } func TestServer_DeleteTree(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() logTree := proto.Clone(testonly.LogTree).(*trillian.Tree) for i, tree := range []*trillian.Tree{logTree} { tree.TreeId = int64(i) + 10 tree.CreateTime = timestamppb.New(time.Unix(int64(i)*3600, 0)) tree.UpdateTime = tree.CreateTime } tests := []struct { desc string tree *trillian.Tree }{ {desc: "logTree", tree: logTree}, } ctx := context.Background() for _, test := range tests { setup := setupAdminServer( ctrl, false, /* snapshot */ true, /* shouldCommit */ false) req := &trillian.DeleteTreeRequest{TreeId: test.tree.TreeId} tx := setup.tx tx.EXPECT().SoftDeleteTree(gomock.Any(), req.TreeId).Return(test.tree, nil) s := setup.server got, err := s.DeleteTree(ctx, req) if err != nil { t.Errorf("%v: DeleteTree() returned err = %v", test.desc, err) continue } want := proto.Clone(test.tree).(*trillian.Tree) if !proto.Equal(got, want) { diff := cmp.Diff(got, want) t.Errorf("%v: post-DeleteTree() diff (-got +want):\n%v", test.desc, diff) } } } func TestServer_DeleteTreeErrors(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() tests := []struct { desc string deleteErr error commitErr bool }{ {desc: "deleteErr", deleteErr: errors.New("unknown tree")}, {desc: "commitErr", commitErr: true}, } ctx := context.Background() for _, test := range tests { setup := setupAdminServer( ctrl, false, test.deleteErr == nil, test.commitErr) req := &trillian.DeleteTreeRequest{TreeId: 10} tx := setup.tx tx.EXPECT().SoftDeleteTree(gomock.Any(), req.TreeId).Return(&trillian.Tree{}, test.deleteErr) s := setup.server if _, err := s.DeleteTree(ctx, req); err == nil { t.Errorf("%v: DeleteTree() returned err = nil, want non-nil", test.desc) } } } func TestServer_UndeleteTree(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() activeLog := proto.Clone(testonly.LogTree).(*trillian.Tree) frozenLog := proto.Clone(testonly.LogTree).(*trillian.Tree) frozenLog.TreeState = trillian.TreeState_FROZEN for i, tree := range []*trillian.Tree{activeLog, frozenLog} { tree.TreeId = int64(i) + 10 tree.CreateTime = timestamppb.New(time.Unix(int64(i)*3600, 0)) tree.UpdateTime = tree.CreateTime tree.Deleted = true tree.DeleteTime = timestamppb.New(time.Unix(int64(i)*3600+10, 0)) } tests := []struct { desc string tree *trillian.Tree }{ {desc: "activeLog", tree: activeLog}, {desc: "frozenLog", tree: frozenLog}, } ctx := context.Background() for _, test := range tests { setup := setupAdminServer( ctrl, false, /* snapshot */ true, /* shouldCommit */ false) req := &trillian.UndeleteTreeRequest{TreeId: test.tree.TreeId} tx := setup.tx tx.EXPECT().UndeleteTree(gomock.Any(), req.TreeId).Return(test.tree, nil) s := setup.server got, err := s.UndeleteTree(ctx, req) if err != nil { t.Errorf("%v: UndeleteTree() returned err = %v", test.desc, err) continue } want := proto.Clone(test.tree).(*trillian.Tree) if !proto.Equal(got, want) { diff := cmp.Diff(got, want) t.Errorf("%v: post-UneleteTree() diff (-got +want):\n%v", test.desc, diff) } } } func TestServer_UndeleteTreeErrors(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() tests := []struct { desc string undeleteErr error commitErr bool }{ {desc: "undeleteErr", undeleteErr: errors.New("unknown tree")}, {desc: "commitErr", commitErr: true}, } ctx := context.Background() for _, test := range tests { setup := setupAdminServer( ctrl, false, test.undeleteErr == nil, test.commitErr) req := &trillian.UndeleteTreeRequest{TreeId: 10} tx := setup.tx tx.EXPECT().UndeleteTree(gomock.Any(), req.TreeId).Return(&trillian.Tree{}, test.undeleteErr) s := setup.server if _, err := s.UndeleteTree(ctx, req); err == nil { t.Errorf("%v: UndeleteTree() returned err = nil, want non-nil", test.desc) } } } // adminTestSetup contains an operational Server and required dependencies. // It's created via setupAdminServer. type adminTestSetup struct { registry extension.Registry as storage.AdminStorage tx *storage.MockAdminTX snapshotTX *storage.MockReadOnlyAdminTX server *Server } // setupAdminServer configures mocks according to input parameters. // Storage will be set to use either snapshots or regular TXs via snapshot parameter. // Whether the snapshot/TX is expected to be committed (and if it should error doing so) is // controlled via shouldCommit and commitErr parameters. func setupAdminServer(ctrl *gomock.Controller, snapshot, shouldCommit, commitErr bool) adminTestSetup { as := &testonly.FakeAdminStorage{} var snapshotTX *storage.MockReadOnlyAdminTX var tx *storage.MockAdminTX if snapshot { snapshotTX = storage.NewMockReadOnlyAdminTX(ctrl) snapshotTX.EXPECT().Close().MaxTimes(1).Return(nil) as.ReadOnlyTX = append(as.ReadOnlyTX, snapshotTX) if shouldCommit { if commitErr { snapshotTX.EXPECT().Commit().Return(errors.New("commit error")) } else { snapshotTX.EXPECT().Commit().Return(nil) } } } else { tx = storage.NewMockAdminTX(ctrl) tx.EXPECT().Close().MaxTimes(1).Return(nil) as.TX = append(as.TX, tx) if shouldCommit { if commitErr { tx.EXPECT().Commit().Return(errors.New("commit error")) } else { tx.EXPECT().Commit().Return(nil) } } } registry := extension.Registry{AdminStorage: as} s := &Server{registry: registry} return adminTestSetup{registry, as, tx, snapshotTX, s} } trillian-1.6.1/server/admin/doc.go000066400000000000000000000012621466362047600170460ustar00rootroot00000000000000// 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 admin contains the TrillianAdminServer implementation. package admin trillian-1.6.1/server/admin/tree_gc.go000066400000000000000000000117751466362047600177230ustar00rootroot00000000000000// 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 admin import ( "bytes" "context" "errors" "fmt" "math/rand" "sync" "time" "github.com/google/trillian/monitoring" "github.com/google/trillian/storage" "k8s.io/klog/v2" ) const ( deleteErrReason = "delete_error" timestampParseErrReson = "timestamp_parse_error" ) var ( timeNow = time.Now timeAfter = time.After hardDeleteCounter monitoring.Counter metricsOnce sync.Once ) func incHardDeleteCounter(treeID int64, success bool, reason string) { hardDeleteCounter.Inc(fmt.Sprint(treeID), fmt.Sprint(success), reason) } // DeletedTreeGC garbage collects deleted trees. // // Tree deletion goes through two separate stages: // * Soft deletion, which "flips a bit" (see Tree.Deleted) but otherwise leaves the tree unaltered // * Hard deletion, which effectively removes all tree data // // DeletedTreeGC performs the transition from soft to hard deletion. Trees that have been deleted // for at least DeletedThreshold are eligible for garbage collection. type DeletedTreeGC struct { // admin is the storage.AdminStorage interface. admin storage.AdminStorage // deleteThreshold defines the minimum time a tree has to remain in the soft-deleted state // before it's eligible for garbage collection. deleteThreshold time.Duration // minRunInterval defines how frequently sweeps for deleted trees are performed. // Actual runs happen randomly between [minInterval,2*minInterval). minRunInterval time.Duration } // NewDeletedTreeGC returns a new DeletedTreeGC. func NewDeletedTreeGC(admin storage.AdminStorage, threshold, minRunInterval time.Duration, mf monitoring.MetricFactory) *DeletedTreeGC { gc := &DeletedTreeGC{ admin: admin, deleteThreshold: threshold, minRunInterval: minRunInterval, } metricsOnce.Do(func() { if mf == nil { mf = monitoring.InertMetricFactory{} } hardDeleteCounter = mf.NewCounter("tree_hard_delete_counter", "Counter of hard-deleted trees", monitoring.TreeIDLabel, "success", "reason") }) return gc } // Run starts the tree garbage collection process. It runs until ctx is cancelled. func (gc *DeletedTreeGC) Run(ctx context.Context) { for { count, err := gc.RunOnce(ctx) if err != nil { klog.Errorf("DeletedTreeGC.Run: %v", err) } if count > 0 { klog.Infof("DeletedTreeGC.Run: successfully deleted %v trees", count) } d := gc.minRunInterval + time.Duration(rand.Int63n(gc.minRunInterval.Nanoseconds())) select { case <-ctx.Done(): return case <-timeAfter(d): } } } // RunOnce performs a single tree garbage collection sweep. Returns the number of successfully // deleted trees. // // It attempts to delete as many eligible trees as possible, regardless of failures. If it // encounters any failures while deleting the resulting error is non-nil. func (gc *DeletedTreeGC) RunOnce(ctx context.Context) (int, error) { now := timeNow() // List and delete trees in separate transactions. Hard-deletes may cascade to a lot of data, so // each delete should be in its own transaction as well. // It's OK to list and delete separately because HardDelete does its own state checking, plus // deleted trees are unlikely to change, specially those deleted for a while. trees, err := storage.ListTrees(ctx, gc.admin, true /* includeDeleted */) if err != nil { return 0, fmt.Errorf("error listing trees: %v", err) } count := 0 var errs []error for _, tree := range trees { if !tree.Deleted { continue } if err := tree.DeleteTime.CheckValid(); err != nil { errs = append(errs, fmt.Errorf("error parsing delete_time of tree %v: %v", tree.TreeId, err)) incHardDeleteCounter(tree.TreeId, false, timestampParseErrReson) continue } deleteTime := tree.DeleteTime.AsTime() durationSinceDelete := now.Sub(deleteTime) if durationSinceDelete <= gc.deleteThreshold { continue } klog.Infof("DeletedTreeGC.RunOnce: Hard-deleting tree %v after %v", tree.TreeId, durationSinceDelete) if err := storage.HardDeleteTree(ctx, gc.admin, tree.TreeId); err != nil { errs = append(errs, fmt.Errorf("error hard-deleting tree %v: %v", tree.TreeId, err)) incHardDeleteCounter(tree.TreeId, false, deleteErrReason) continue } count++ incHardDeleteCounter(tree.TreeId, true, "") } if len(errs) == 0 { return count, nil } buf := &bytes.Buffer{} buf.WriteString("encountered errors hard-deleting trees:") for _, err := range errs { buf.WriteString("\n\t") buf.WriteString(err.Error()) } return count, errors.New(buf.String()) } trillian-1.6.1/server/admin/tree_gc_test.go000066400000000000000000000267031466362047600207570ustar00rootroot00000000000000// 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 admin import ( "context" "errors" "strings" "testing" "time" "github.com/golang/mock/gomock" "github.com/google/trillian" "github.com/google/trillian/storage" "github.com/google/trillian/storage/testonly" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/timestamppb" ) func TestDeletedTreeGC_Run(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() // Test the following scenario: // * 1st iteration: tree1 is listed and hard-deleted // * Sleep // * 2nd iteration: ListTrees returns an empty slice, nothing gets deleted // * Sleep (ctx cancelled) // // DeletedTreeGC.Run() until ctx in cancelled. Since it always sleeps between iterations, we // make "timeAfter" cancel ctx the second time around to break the loop. tree1 := proto.Clone(testonly.LogTree).(*trillian.Tree) tree1.TreeId = 1 tree1.Deleted = true tree1.DeleteTime = timestamppb.New(time.Date(2017, 9, 21, 10, 0, 0, 0, time.UTC)) listTX1 := storage.NewMockReadOnlyAdminTX(ctrl) listTX2 := storage.NewMockReadOnlyAdminTX(ctrl) deleteTX1 := storage.NewMockAdminTX(ctrl) as := &testonly.FakeAdminStorage{ TX: []storage.AdminTX{deleteTX1}, ReadOnlyTX: []storage.ReadOnlyAdminTX{listTX1, listTX2}, } ctx, cancel := context.WithCancel(context.Background()) // Sequence Snapshot()/ReadWriteTransaction() calls. // * 1st loop: Snapshot()/ListTrees() followed by ReadWriteTransaction()/HardDeleteTree() // * 2nd loop: Snapshot()/ListTrees() only. // 1st loop listTX1.EXPECT().ListTrees(gomock.Any(), true /* includeDeleted */).Return([]*trillian.Tree{tree1}, nil) listTX1.EXPECT().Close().Return(nil) listTX1.EXPECT().Commit().Return(nil) deleteTX1.EXPECT().HardDeleteTree(gomock.Any(), tree1.TreeId).Return(nil) deleteTX1.EXPECT().Close().Return(nil) deleteTX1.EXPECT().Commit().Return(nil) // 2nd loop listTX2.EXPECT().ListTrees(gomock.Any(), true /* includeDeleted */).Return(nil, nil) listTX2.EXPECT().Close().Return(nil) listTX2.EXPECT().Commit().Return(nil) defer func(now func() time.Time, after func(time.Duration) <-chan time.Time) { timeNow = now timeAfter = after }(timeNow, timeAfter) const deleteThreshold = 1 * time.Hour const runInterval = 3 * time.Second // now > tree1.DeleteTime + deleteThreshold, so tree1 gets deleted on first round now := tree1.DeleteTime.AsTime().Add(deleteThreshold).Add(1 * time.Second) timeNow = func() time.Time { return now } calls := 0 timeAfter = func(d time.Duration) <-chan time.Time { calls++ if d < runInterval || d >= 2*runInterval { t.Errorf("Called time.Sleep(%v), want %v", d, runInterval) } if calls >= 2 { cancel() // Block to make sure we're exiting because of the cancelled context and // not because the timer elapsed. return make(chan time.Time) } ch := make(chan time.Time, 1) ch <- now return ch } NewDeletedTreeGC(as, deleteThreshold, runInterval, nil /* mf */).Run(ctx) } func TestDeletedTreeGC_RunOnce(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() tree1 := proto.Clone(testonly.LogTree).(*trillian.Tree) tree1.TreeId = 1 tree2 := proto.Clone(testonly.LogTree).(*trillian.Tree) tree2.TreeId = 2 tree2.Deleted = true tree2.DeleteTime = timestamppb.New(time.Date(2017, 9, 21, 10, 0, 0, 0, time.UTC)) tree3 := proto.Clone(testonly.LogTree).(*trillian.Tree) tree3.TreeId = 3 tree3.Deleted = true tree3.DeleteTime = timestamppb.New(time.Date(2017, 9, 22, 11, 0, 0, 0, time.UTC)) tree4 := proto.Clone(testonly.LogTree).(*trillian.Tree) tree4.TreeId = 4 tree4.Deleted = true tree4.DeleteTime = timestamppb.New(time.Date(2017, 9, 23, 12, 0, 0, 0, time.UTC)) tree5 := proto.Clone(testonly.LogTree).(*trillian.Tree) tree5.TreeId = 5 allTrees := []*trillian.Tree{tree1, tree2, tree3, tree4, tree5} tests := []struct { desc string now time.Time deleteThreshold time.Duration wantDeleted []int64 }{ { desc: "noDeletions", now: time.Date(2017, 9, 28, 10, 0, 0, 0, time.UTC), deleteThreshold: 7 * 24 * time.Hour, }, { desc: "oneDeletion", now: time.Date(2017, 9, 28, 11, 0, 0, 0, time.UTC), deleteThreshold: 7 * 24 * time.Hour, wantDeleted: []int64{tree2.TreeId}, }, { desc: "twoDeletions", now: time.Date(2017, 9, 22, 12, 1, 0, 0, time.UTC), deleteThreshold: 1 * time.Hour, wantDeleted: []int64{tree2.TreeId, tree3.TreeId}, }, { desc: "threeDeletions", now: time.Date(2017, 9, 23, 13, 30, 0, 0, time.UTC), deleteThreshold: 1 * time.Hour, wantDeleted: []int64{tree2.TreeId, tree3.TreeId, tree4.TreeId}, }, } defer func(f func() time.Time) { timeNow = f }(timeNow) ctx := context.Background() for _, test := range tests { timeNow = func() time.Time { return test.now } listTX := storage.NewMockReadOnlyAdminTX(ctrl) as := &testonly.FakeAdminStorage{ReadOnlyTX: []storage.ReadOnlyAdminTX{listTX}} listTX.EXPECT().ListTrees(gomock.Any(), true /* includeDeleted */).Return(allTrees, nil) listTX.EXPECT().Close().Return(nil) listTX.EXPECT().Commit().Return(nil) for _, id := range test.wantDeleted { deleteTX := storage.NewMockAdminTX(ctrl) deleteTX.EXPECT().HardDeleteTree(gomock.Any(), id).Return(nil) deleteTX.EXPECT().Close().Return(nil) deleteTX.EXPECT().Commit().Return(nil) as.TX = append(as.TX, deleteTX) } gc := NewDeletedTreeGC(as, test.deleteThreshold, 1*time.Second /* minRunInterval */, nil /* mf */) switch count, err := gc.RunOnce(ctx); { case err != nil: t.Errorf("%v: RunOnce() returned err = %v", test.desc, err) case count != len(test.wantDeleted): t.Errorf("%v: RunOnce() = %v, want = %v", test.desc, count, len(test.wantDeleted)) } } } // listTreesSpec specifies all parameters required to mock a ListTrees TX call. type listTreesSpec struct { snapshotErr, listErr, commitErr error trees []*trillian.Tree } // hardDeleteTreeSpec specifies all parameters required to mock a HardDeleteTree TX call. type hardDeleteTreeSpec struct { beginErr, deleteErr, commitErr error treeID int64 } func TestDeletedTreeGC_RunOnceErrors(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() deleteTime := time.Date(2017, 10, 25, 16, 0, 0, 0, time.UTC) deleteTimePB := timestamppb.New(deleteTime) logTree1 := proto.Clone(testonly.LogTree).(*trillian.Tree) logTree1.TreeId = 10 logTree1.Deleted = true logTree1.DeleteTime = deleteTimePB logTree2 := proto.Clone(testonly.LogTree).(*trillian.Tree) logTree2.TreeId = 20 logTree2.Deleted = true logTree2.DeleteTime = deleteTimePB badTS := proto.Clone(testonly.LogTree).(*trillian.Tree) badTS.TreeId = 40 badTS.Deleted = true // badTS.DeleteTime is nil // To simplify the test all trees are deleted and passed the deletion threshold. // Other aspects of RunOnce() are covered by TestDeletedTreeGC_RunOnce. deleteThreshold := 1 * time.Hour now := deleteTime.Add(2 * time.Hour) defer func(f func() time.Time) { timeNow = f }(timeNow) timeNow = func() time.Time { return now } tests := []struct { desc string listTrees listTreesSpec hardDeleteTree []hardDeleteTreeSpec // wantCount is the count of successfully deleted trees. wantCount int // wantErrs defines which strings must be present in the resulting error. wantErrs []string }{ { desc: "snapshotErr", listTrees: listTreesSpec{ snapshotErr: errors.New("snapshot err"), }, wantErrs: []string{"snapshot err"}, }, { desc: "listErr", listTrees: listTreesSpec{ listErr: errors.New("list err"), }, wantErrs: []string{"list err"}, }, { desc: "snapshotCommitErr", listTrees: listTreesSpec{ commitErr: errors.New("commit err"), trees: []*trillian.Tree{logTree1, logTree2}, }, wantErrs: []string{"commit err"}, }, { desc: "beginErr", listTrees: listTreesSpec{ trees: []*trillian.Tree{logTree1, logTree2}, }, hardDeleteTree: []hardDeleteTreeSpec{ {beginErr: errors.New("begin err")}, {treeID: logTree2.TreeId}, }, wantCount: 1, wantErrs: []string{"begin err"}, }, { desc: "deleteErr", listTrees: listTreesSpec{ trees: []*trillian.Tree{logTree1, logTree2}, }, hardDeleteTree: []hardDeleteTreeSpec{ {deleteErr: errors.New("cannot delete logTree1"), treeID: logTree1.TreeId}, {treeID: logTree2.TreeId}, }, wantCount: 1, wantErrs: []string{"cannot delete logTree1"}, }, { desc: "commitErr", listTrees: listTreesSpec{ trees: []*trillian.Tree{logTree1, logTree2}, }, hardDeleteTree: []hardDeleteTreeSpec{ {commitErr: errors.New("commit err"), treeID: logTree1.TreeId}, {treeID: logTree2.TreeId}, }, wantCount: 1, wantErrs: []string{"commit err"}, }, { // logTree1 = delete successful // logTree2 = delete error // badTS = timestamp parse error (no HardDeleteTree() call) desc: "multipleErrors", listTrees: listTreesSpec{ trees: []*trillian.Tree{logTree1, logTree2, badTS}, }, hardDeleteTree: []hardDeleteTreeSpec{ {treeID: logTree1.TreeId}, {deleteErr: errors.New("delete err"), treeID: logTree2.TreeId}, }, wantCount: 1, wantErrs: []string{"delete err", "error parsing delete_time"}, }, } ctx := context.Background() for _, test := range tests { t.Run(test.desc, func(t *testing.T) { listTX := storage.NewMockReadOnlyAdminTX(ctrl) listTX.EXPECT().ListTrees(gomock.Any(), true /* includeDeleted */).AnyTimes().Return(test.listTrees.trees, test.listTrees.listErr) listTX.EXPECT().Close().AnyTimes().Return(nil) listTX.EXPECT().Commit().AnyTimes().Return(test.listTrees.commitErr) as := &testonly.FakeAdminStorage{ ReadOnlyTX: []storage.ReadOnlyAdminTX{listTX}, } if test.listTrees.snapshotErr != nil { as.SnapshotErr = append(as.SnapshotErr, test.listTrees.snapshotErr) } for _, hardDeleteTree := range test.hardDeleteTree { deleteTX := storage.NewMockAdminTX(ctrl) if hardDeleteTree.beginErr != nil { as.TXErr = append(as.TXErr, hardDeleteTree.beginErr) } else { as.TX = append(as.TX, deleteTX) } if hardDeleteTree.treeID != 0 { deleteTX.EXPECT().HardDeleteTree(gomock.Any(), hardDeleteTree.treeID).AnyTimes().Return(hardDeleteTree.deleteErr) } deleteTX.EXPECT().Close().AnyTimes().Return(nil) deleteTX.EXPECT().Commit().AnyTimes().Return(hardDeleteTree.commitErr) } gc := NewDeletedTreeGC(as, deleteThreshold, 1*time.Second /* minRunInterval */, nil /* mf */) count, err := gc.RunOnce(ctx) if err == nil { t.Fatalf("%v: RunOnce() returned err = nil, want non-nil", test.desc) } if count != test.wantCount { t.Errorf("%v: RunOnce() = %v, want = %v", test.desc, count, test.wantCount) } for _, want := range test.wantErrs { if !strings.Contains(err.Error(), want) { t.Errorf("%v: RunOnce() returned err = %q, want substring %q", test.desc, err, want) } } }) } } trillian-1.6.1/server/doc.go000066400000000000000000000012721466362047600157570ustar00rootroot00000000000000// Copyright 2022 Trillian Authors. 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 server contains the Trillian log server implementation. package server trillian-1.6.1/server/errors/000077500000000000000000000000001466362047600161755ustar00rootroot00000000000000trillian-1.6.1/server/errors/doc.go000066400000000000000000000013021466362047600172650ustar00rootroot00000000000000// 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 errors contains utilities to translate TrillianErrors to gRPC errors. package errors trillian-1.6.1/server/errors/errors.go000066400000000000000000000017441466362047600200460ustar00rootroot00000000000000// 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 errors import ( "database/sql" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) // WrapError wraps err as a gRPC error if err is a well-known error instance // (such as canonical SQL errors), else err is returned unmodified. func WrapError(err error) error { if err == sql.ErrNoRows { return status.Errorf(codes.NotFound, err.Error()) } return err } trillian-1.6.1/server/errors/errors_test.go000066400000000000000000000026071466362047600211040ustar00rootroot00000000000000// 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 errors import ( "database/sql" "errors" "testing" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" _ "k8s.io/klog/v2" ) func TestWrapError(t *testing.T) { grpcErr := status.Errorf(codes.NotFound, "not found err") err := errors.New("generic error") tests := []struct { err error wantErr error }{ { err: grpcErr, wantErr: grpcErr, }, { err: err, wantErr: err, }, { err: sql.ErrNoRows, wantErr: status.Errorf(codes.NotFound, sql.ErrNoRows.Error()), }, } for _, test := range tests { // We can't use == for rpcErrors because grpc.Errorf returns *rpcError. if gotErr := WrapError(test.err); gotErr.Error() != test.wantErr.Error() { t.Errorf("WrapError('%T') = %v, want %v", test.err, gotErr, test.wantErr) } } } trillian-1.6.1/server/interceptor/000077500000000000000000000000001466362047600172175ustar00rootroot00000000000000trillian-1.6.1/server/interceptor/interceptor.go000066400000000000000000000352211466362047600221070ustar00rootroot00000000000000// 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 interceptor defines gRPC interceptors for Trillian. package interceptor import ( "context" "fmt" "regexp" "sync" "time" "github.com/google/trillian" "github.com/google/trillian/monitoring" "github.com/google/trillian/quota" "github.com/google/trillian/quota/etcd/quotapb" "github.com/google/trillian/server/errors" "github.com/google/trillian/storage" "github.com/google/trillian/trees" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "k8s.io/klog/v2" ) const ( badInfoReason = "bad_info" badTreeReason = "bad_tree" insufficientTokensReason = "insufficient_tokens" getTreeStage = "get_tree" getTokensStage = "get_tokens" traceSpanRoot = "/trillian/server/int" ) var ( // PutTokensTimeout is the timeout used for PutTokens calls. // PutTokens happens in a separate goroutine and with an independent context, therefore it has // its own timeout, separate from the RPC that causes the calls. PutTokensTimeout = 5 * time.Second requestCounter monitoring.Counter requestDeniedCounter monitoring.Counter contextErrCounter monitoring.Counter metricsOnce sync.Once enabledServices = map[string]bool{ "trillian.TrillianLog": true, "trillian.TrillianAdmin": true, "TrillianLog": true, "TrillianAdmin": true, } ) // RequestProcessor encapsulates the logic to intercept a request, split into separate stages: // before and after the handler is invoked. type RequestProcessor interface { // Before implements all interceptor logic that happens before the handler is called. // It returns a (potentially) modified context that's passed forward to the handler (and After), // plus an error, in case the request should be interrupted before the handler is invoked. Before(ctx context.Context, req interface{}, method string) (context.Context, error) // After implements all interceptor logic that happens after the handler is invoked. // Before must be invoked prior to After and the same RequestProcessor instance must to be used // to process a given request. After(ctx context.Context, resp interface{}, method string, handlerErr error) } // TrillianInterceptor checks that: // * Requests addressing a tree have the correct tree type and tree state; // * TODO(codingllama): Requests are properly authenticated / authorized ; and // * Requests are rate limited appropriately. type TrillianInterceptor struct { admin storage.AdminStorage qm quota.Manager // quotaDryRun controls whether lack of tokens actually blocks requests (if set to true, no // requests are blocked by lack of tokens). quotaDryRun bool } // New returns a new TrillianInterceptor instance. func New(admin storage.AdminStorage, qm quota.Manager, quotaDryRun bool, mf monitoring.MetricFactory) *TrillianInterceptor { metricsOnce.Do(func() { initMetrics(mf) }) return &TrillianInterceptor{ admin: admin, qm: qm, quotaDryRun: quotaDryRun, } } func initMetrics(mf monitoring.MetricFactory) { if mf == nil { mf = monitoring.InertMetricFactory{} } quota.InitMetrics(mf) requestCounter = mf.NewCounter( "interceptor_request_count", "Total number of intercepted requests", monitoring.TreeIDLabel) requestDeniedCounter = mf.NewCounter( "interceptor_request_denied_count", "Number of requests by denied, labeled according to the reason for denial", "reason", monitoring.TreeIDLabel, "quota_user") contextErrCounter = mf.NewCounter( "interceptor_context_err_counter", "Total number of times request context has been cancelled or deadline exceeded by stage", "stage") } func incRequestDeniedCounter(reason string, treeID int64, quotaUser string) { requestDeniedCounter.Inc(reason, fmt.Sprint(treeID), quotaUser) } // UnaryInterceptor executes the TrillianInterceptor logic for unary RPCs. func (i *TrillianInterceptor) UnaryInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { // Implement UnaryInterceptor using a RequestProcessor, so we // 1. exercise it // 2. make it easier to port this logic to non-gRPC implementations. rp := i.NewProcessor() var err error ctx, err = rp.Before(ctx, req, info.FullMethod) if err != nil { return nil, err } resp, err := handler(ctx, req) rp.After(ctx, resp, info.FullMethod, err) return resp, err } // NewProcessor returns a RequestProcessor for the TrillianInterceptor logic. func (i *TrillianInterceptor) NewProcessor() RequestProcessor { return &trillianProcessor{parent: i} } type trillianProcessor struct { parent *TrillianInterceptor info *rpcInfo } func (tp *trillianProcessor) Before(ctx context.Context, req interface{}, method string) (context.Context, error) { // Skip if the interceptor is not enabled for this service. if !enabledServices[serviceName(method)] { return ctx, nil } // Don't want the Before to contain the action, so don't overwrite the ctx. innerCtx, spanEnd := spanFor(ctx, "Before") defer spanEnd() info, err := newRPCInfo(req) if err != nil { klog.Warningf("Failed to read tree info: %v", err) incRequestDeniedCounter(badInfoReason, 0, "") return ctx, err } tp.info = info requestCounter.Inc(fmt.Sprint(info.treeID)) // TODO(codingllama): Add auth interception if info.getTree { tree, err := trees.GetTree( innerCtx, tp.parent.admin, info.treeID, trees.NewGetOpts(trees.Admin, info.treeTypes...)) if err != nil { incRequestDeniedCounter(badTreeReason, info.treeID, info.quotaUsers) return ctx, err } if err := innerCtx.Err(); err != nil { contextErrCounter.Inc(getTreeStage) return ctx, err } ctx = trees.NewContext(ctx, tree) } if info.tokens > 0 && len(info.specs) > 0 { err := tp.parent.qm.GetTokens(innerCtx, info.tokens, info.specs) if err != nil { if !tp.parent.quotaDryRun { incRequestDeniedCounter(insufficientTokensReason, info.treeID, info.quotaUsers) return ctx, status.Errorf(codes.ResourceExhausted, "quota exhausted: %v", err) } klog.Warningf("(quotaDryRun) Request %+v not denied due to dry run mode: %v", req, err) } quota.Metrics.IncAcquired(info.tokens, info.specs, err == nil) if err = innerCtx.Err(); err != nil { contextErrCounter.Inc(getTokensStage) return ctx, err } } return ctx, nil } func (tp *trillianProcessor) After(ctx context.Context, resp interface{}, method string, handlerErr error) { if !enabledServices[serviceName(method)] { return } _, spanEnd := spanFor(ctx, "After") defer spanEnd() switch { case tp.info == nil: klog.Warningf("After called with nil rpcInfo, resp = [%+v], handlerErr = [%v]", resp, handlerErr) return case tp.info.tokens == 0: // After() currently only does quota processing return } // Decide if we have to replenish tokens. There are a few situations that require tokens to // be replenished: // * Invalid requests (a bad request shouldn't spend sequencing-based tokens, as it won't // cause a corresponding sequencing to happen) // * Requests that filter out duplicates (e.g., QueueLeaf, for the same reason as above: // duplicates aren't queued for sequencing) // These are only applied for Refundable specs. refunds := make([]quota.Spec, 0) for _, s := range tp.info.specs { if s.Refundable { refunds = append(refunds, s) } } if len(refunds) == 0 { return } tokens := 0 if handlerErr != nil { // Return the tokens spent by invalid requests tokens = tp.info.tokens } else { switch resp := resp.(type) { case *trillian.QueueLeafResponse: if !isLeafOK(resp.GetQueuedLeaf()) { tokens = 1 } case *trillian.AddSequencedLeavesResponse: for _, leaf := range resp.GetResults() { if !isLeafOK(leaf) { tokens++ } } } } if tokens > 0 { // Run PutTokens in a separate goroutine and with a separate context. // It shouldn't block RPC completion, nor should it share the RPC's context deadline. go func() { ctx, spanEnd := spanFor(context.Background(), "After.PutTokens") defer spanEnd() ctx, cancel := context.WithTimeout(ctx, PutTokensTimeout) defer cancel() // TODO(codingllama): If PutTokens turns out to be unreliable we can still leak tokens. In // this case, we may want to keep tabs on how many tokens we failed to replenish and bundle // them up in the next PutTokens call (possibly as a QuotaManager decorator, or internally // in its impl). err := tp.parent.qm.PutTokens(ctx, tokens, refunds) if err != nil { klog.Warningf("Failed to replenish %v tokens: %v", tokens, err) } quota.Metrics.IncReturned(tokens, refunds, err == nil) }() } } func isLeafOK(leaf *trillian.QueuedLogLeaf) bool { // Be biased in favor of OK, as that matches TrillianLogRPCServer's behavior. return leaf == nil || leaf.Status == nil || leaf.Status.Code == int32(codes.OK) } var ( fullyQualifiedRE = regexp.MustCompile(`^/([\w.]+)/(\w+)$`) unqualifiedRE = regexp.MustCompile(`^/(\w+)\.(\w+)$`) ) // serviceName returns the fully qualified service name // "some.package.service" for "/some.package.service/method". // It returns the unqualified service name "service" for "/service.method". func serviceName(fullMethod string) string { if matches := fullyQualifiedRE.FindStringSubmatch(fullMethod); len(matches) == 3 { return matches[1] } if matches := unqualifiedRE.FindStringSubmatch(fullMethod); len(matches) == 3 { return matches[1] } return "" } type rpcInfo struct { // getTree indicates whether the interceptor should populate treeID. getTree bool readonly bool treeID int64 treeTypes []trillian.TreeType specs []quota.Spec tokens int // Single string describing all of the users against which quota is requested. quotaUsers string } // chargable is satisfied by request proto messages which contain a GetChargeTo // accessor. type chargable interface { GetChargeTo() *trillian.ChargeTo } // chargedUsers returns user identifiers for any chargable user quotas. func chargedUsers(req interface{}) []string { c, ok := req.(chargable) if !ok { return nil } chargeTo := c.GetChargeTo() if chargeTo == nil { return nil } return chargeTo.User } func newRPCInfoForRequest(req interface{}) (*rpcInfo, error) { // Set "safe" defaults: enable all interception and assume requests are readonly. info := &rpcInfo{ getTree: true, readonly: true, treeTypes: nil, tokens: 0, } switch req := req.(type) { // Not intercepted at all case // Quota configuration requests *quotapb.CreateConfigRequest, *quotapb.DeleteConfigRequest, *quotapb.GetConfigRequest, *quotapb.ListConfigsRequest, *quotapb.UpdateConfigRequest: info.getTree = false info.readonly = false // Doesn't really matter as all interceptors are turned off // Admin create case *trillian.CreateTreeRequest: info.getTree = false // Tree doesn't exist info.readonly = false // Admin list case *trillian.ListTreesRequest: info.getTree = false // Zero to many trees // Admin / readonly case *trillian.GetTreeRequest: info.getTree = false // Read done within RPC handler // Admin / readwrite case *trillian.DeleteTreeRequest, *trillian.UndeleteTreeRequest, *trillian.UpdateTreeRequest: info.getTree = false // Read-modify-write done within RPC handler info.readonly = false // (Log + Pre-ordered Log) / readonly case *trillian.GetConsistencyProofRequest, *trillian.GetEntryAndProofRequest, *trillian.GetInclusionProofByHashRequest, *trillian.GetInclusionProofRequest, *trillian.GetLatestSignedLogRootRequest: info.treeTypes = []trillian.TreeType{trillian.TreeType_LOG, trillian.TreeType_PREORDERED_LOG} info.tokens = 1 case *trillian.GetLeavesByRangeRequest: info.treeTypes = []trillian.TreeType{trillian.TreeType_LOG, trillian.TreeType_PREORDERED_LOG} info.tokens = 1 if c := req.GetCount(); c > 1 { info.tokens = int(c) } // Log / readwrite case *trillian.QueueLeafRequest: info.readonly = false info.treeTypes = []trillian.TreeType{trillian.TreeType_LOG} info.tokens = 1 // Pre-ordered Log / readwrite case *trillian.AddSequencedLeavesRequest: info.readonly = false info.treeTypes = []trillian.TreeType{trillian.TreeType_PREORDERED_LOG} info.tokens = len(req.GetLeaves()) // (Log + Pre-ordered Log) / readwrite case *trillian.InitLogRequest: info.readonly = false info.treeTypes = []trillian.TreeType{trillian.TreeType_LOG, trillian.TreeType_PREORDERED_LOG} info.tokens = 1 default: return nil, status.Errorf(codes.Internal, "newRPCInfo: unmapped request type: %T", req) } return info, nil } func newRPCInfo(req interface{}) (*rpcInfo, error) { info, err := newRPCInfoForRequest(req) if err != nil { return nil, err } if info.getTree || info.tokens > 0 { switch req := req.(type) { case logIDRequest: info.treeID = req.GetLogId() case treeIDRequest: info.treeID = req.GetTreeId() case treeRequest: info.treeID = req.GetTree().GetTreeId() default: return nil, status.Errorf(codes.Internal, "cannot retrieve treeID from request: %T", req) } } if info.tokens > 0 { kind := quota.Write if info.readonly { kind = quota.Read } for _, user := range chargedUsers(req) { info.specs = append(info.specs, quota.Spec{Group: quota.User, Kind: kind, User: user}) if len(info.quotaUsers) > 0 { info.quotaUsers += "+" } info.quotaUsers += user } info.specs = append(info.specs, []quota.Spec{ {Group: quota.Tree, Kind: kind, TreeID: info.treeID}, {Group: quota.Global, Kind: kind, Refundable: true}, // Only Global tokens are refunded. }...) } return info, nil } type logIDRequest interface { GetLogId() int64 } type treeIDRequest interface { GetTreeId() int64 } type treeRequest interface { GetTree() *trillian.Tree } // ErrorWrapper is a grpc.UnaryServerInterceptor that wraps the errors emitted by the underlying handler. func ErrorWrapper(ctx context.Context, req interface{}, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { ctx, spanEnd := spanFor(ctx, "ErrorWrapper") defer spanEnd() rsp, err := handler(ctx, req) return rsp, errors.WrapError(err) } func spanFor(ctx context.Context, name string) (context.Context, func()) { return monitoring.StartSpan(ctx, fmt.Sprintf("%s.%s", traceSpanRoot, name)) } trillian-1.6.1/server/interceptor/interceptor_test.go000066400000000000000000000571631466362047600231570ustar00rootroot00000000000000// 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 interceptor import ( "context" "errors" "fmt" "testing" "time" "github.com/golang/mock/gomock" "github.com/google/go-cmp/cmp" "github.com/google/trillian" "github.com/google/trillian/quota" "github.com/google/trillian/quota/etcd/quotapb" "github.com/google/trillian/storage" "github.com/google/trillian/storage/testonly" "github.com/google/trillian/trees" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/timestamppb" serrors "github.com/google/trillian/server/errors" grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware" ) func TestServiceName(t *testing.T) { for _, tc := range []struct { desc string method string want string }{ {desc: "trillian", method: "/trillian.TrillianLog/QueueLeaf", want: "trillian.TrillianLog"}, {desc: "fullyqualified", method: "/some.package.service/method", want: "some.package.service"}, {desc: "unqualified", method: "/service.method", want: "service"}, {desc: "noleadingslash", method: "no.leading.slash/method"}, {desc: "malformed", method: "/package.service.method"}, } { t.Run(tc.desc, func(t *testing.T) { if got, want := serviceName(tc.method), tc.want; got != want { t.Errorf("serviceName(%v): %v, want %v", tc.method, got, want) } }) } } func TestTrillianInterceptor_TreeInterception(t *testing.T) { logTree := proto.Clone(testonly.LogTree).(*trillian.Tree) logTree.TreeId = 10 deletedTree := proto.Clone(testonly.LogTree).(*trillian.Tree) deletedTree.TreeId = 12 deletedTree.Deleted = true deletedTree.DeleteTime = timestamppb.Now() unknownTreeID := int64(999) tests := []struct { desc string method string req interface{} handlerErr error wantErr bool wantTree *trillian.Tree cancelled bool }{ // TODO(codingllama): Admin requests don't benefit from tree-reading logic, but we may read // their tree IDs for auth purposes. { desc: "adminReadByID", method: "/trillian.TrillianAdmin/GetTree", req: &trillian.GetTreeRequest{TreeId: logTree.TreeId}, }, { desc: "adminWriteByID", method: "/trillian.TrillianAdmin/DeleteTree", req: &trillian.DeleteTreeRequest{TreeId: logTree.TreeId}, }, { desc: "adminWriteByTree", method: "/trillian.TrillianAdmin/UpdateTree", req: &trillian.UpdateTreeRequest{Tree: &trillian.Tree{TreeId: logTree.TreeId}}, }, { desc: "logRPC", method: "/trillian.TrillianLog/GetLatestSignedLogRoot", req: &trillian.GetLatestSignedLogRootRequest{LogId: logTree.TreeId}, wantTree: logTree, }, { desc: "unknownRequest", req: "not-a-request", wantErr: false, }, { desc: "unknownTree", method: "/trillian.TrillianLog/GetLatestSignedLogRoot", req: &trillian.GetLatestSignedLogRootRequest{LogId: unknownTreeID}, wantErr: true, }, { desc: "deletedTree", method: "/trillian.TrillianLog/GetLatestSignedLogRoot", req: &trillian.GetLatestSignedLogRootRequest{LogId: deletedTree.TreeId}, wantErr: true, }, { desc: "cancelled", method: "/trillian.TrillianLog/GetLatestSignedLogRoot", req: &trillian.GetLatestSignedLogRootRequest{LogId: logTree.TreeId}, cancelled: true, wantErr: true, }, } ctx := context.Background() for _, test := range tests { t.Run(test.desc, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() admin := storage.NewMockAdminStorage(ctrl) adminTX := storage.NewMockReadOnlyAdminTX(ctrl) admin.EXPECT().Snapshot(gomock.Any()).AnyTimes().Return(adminTX, nil) adminTX.EXPECT().GetTree(gomock.Any(), logTree.TreeId).AnyTimes().Return(logTree, nil) adminTX.EXPECT().GetTree(gomock.Any(), deletedTree.TreeId).AnyTimes().Return(deletedTree, nil) adminTX.EXPECT().GetTree(gomock.Any(), unknownTreeID).AnyTimes().Return(nil, errors.New("not found")) adminTX.EXPECT().Close().AnyTimes().Return(nil) adminTX.EXPECT().Commit().AnyTimes().Return(nil) intercept := New(admin, quota.Noop(), false /* quotaDryRun */, nil /* mf */) handler := &fakeHandler{resp: "handler response", err: test.handlerErr} if test.cancelled { // Use a context that's already been cancelled newCtx, cancel := context.WithCancel(ctx) cancel() ctx = newCtx } resp, err := intercept.UnaryInterceptor(ctx, test.req, &grpc.UnaryServerInfo{FullMethod: test.method}, handler.run) if hasErr := err != nil && err != test.handlerErr; hasErr != test.wantErr { t.Fatalf("UnaryInterceptor() returned err = %v, wantErr = %v", err, test.wantErr) } else if hasErr { return } if !handler.called { t.Fatal("handler not called") } if handler.resp != resp { t.Errorf("resp = %v, want = %v", resp, handler.resp) } if handler.err != err { t.Errorf("err = %v, want = %v", err, handler.err) } if test.wantTree != nil { switch tree, ok := trees.FromContext(handler.ctx); { case !ok: t.Error("tree not in handler ctx") case !proto.Equal(tree, test.wantTree): diff := cmp.Diff(tree, test.wantTree) t.Errorf("post-FromContext diff:\n%v", diff) } } }) } } func TestTrillianInterceptor_QuotaInterception(t *testing.T) { logTree := proto.Clone(testonly.LogTree).(*trillian.Tree) logTree.TreeId = 10 preorderedTree := proto.Clone(testonly.PreorderedLogTree).(*trillian.Tree) preorderedTree.TreeId = 12 charge1 := "alpaca" charge2 := "cama" charges := &trillian.ChargeTo{User: []string{charge1, charge2}} tests := []struct { desc string dryRun bool method string req interface{} specs []quota.Spec getTokensErr error wantCode codes.Code wantTokens int }{ { desc: "logRead", method: "/trillian.TrillianLog/GetLatestSignedLogRoot", req: &trillian.GetLatestSignedLogRootRequest{LogId: logTree.TreeId}, specs: []quota.Spec{ {Group: quota.Tree, Kind: quota.Read, TreeID: logTree.TreeId}, {Group: quota.Global, Kind: quota.Read, Refundable: true}, }, wantTokens: 1, }, { desc: "logReadRange", method: "/trillian.TrillianLog/GetLeavesByRange", req: &trillian.GetLeavesByRangeRequest{LogId: logTree.TreeId, Count: 123}, specs: []quota.Spec{ {Group: quota.Tree, Kind: quota.Read, TreeID: logTree.TreeId}, {Group: quota.Global, Kind: quota.Read, Refundable: true}, }, wantTokens: 123, }, { desc: "logReadNegativeRange", method: "/trillian.TrillianLog/GetLeavesByRange", req: &trillian.GetLeavesByRangeRequest{LogId: logTree.TreeId, Count: -123}, specs: []quota.Spec{ {Group: quota.Tree, Kind: quota.Read, TreeID: logTree.TreeId}, {Group: quota.Global, Kind: quota.Read, Refundable: true}, }, wantTokens: 1, }, { desc: "logReadZeroRange", method: "/trillian.TrillianLog/GetLeavesByRange", req: &trillian.GetLeavesByRangeRequest{LogId: logTree.TreeId, Count: 0}, specs: []quota.Spec{ {Group: quota.Tree, Kind: quota.Read, TreeID: logTree.TreeId}, {Group: quota.Global, Kind: quota.Read, Refundable: true}, }, wantTokens: 1, }, { desc: "logRead with charges", method: "/trillian.TrillianLog/GetLatestSignedLogRoot", req: &trillian.GetLatestSignedLogRootRequest{LogId: logTree.TreeId, ChargeTo: charges}, specs: []quota.Spec{ {Group: quota.User, Kind: quota.Read, User: charge1}, {Group: quota.User, Kind: quota.Read, User: charge2}, {Group: quota.Tree, Kind: quota.Read, TreeID: logTree.TreeId}, {Group: quota.Global, Kind: quota.Read, Refundable: true}, }, wantTokens: 1, }, { desc: "logWrite", method: "/trillian.TrillianLog/QueueLeaf", req: &trillian.QueueLeafRequest{LogId: logTree.TreeId}, specs: []quota.Spec{ {Group: quota.Tree, Kind: quota.Write, TreeID: logTree.TreeId}, {Group: quota.Global, Kind: quota.Write, Refundable: true}, }, wantTokens: 1, }, { desc: "logWrite with charges", method: "/trillian.TrillianLog/QueueLeaf", req: &trillian.QueueLeafRequest{LogId: logTree.TreeId, ChargeTo: charges}, specs: []quota.Spec{ {Group: quota.User, Kind: quota.Write, User: charge1}, {Group: quota.User, Kind: quota.Write, User: charge2}, {Group: quota.Tree, Kind: quota.Write, TreeID: logTree.TreeId}, {Group: quota.Global, Kind: quota.Write, Refundable: true}, }, wantTokens: 1, }, { desc: "batchSequencedLogLeavesRequest", method: "/trillian.TrillianLog/AddSequencedLeaves", req: &trillian.AddSequencedLeavesRequest{ LogId: preorderedTree.TreeId, Leaves: []*trillian.LogLeaf{{}, {}, {}}, }, specs: []quota.Spec{ {Group: quota.Tree, Kind: quota.Write, TreeID: preorderedTree.TreeId}, {Group: quota.Global, Kind: quota.Write, Refundable: true}, }, wantTokens: 3, }, { desc: "QueueLeafRequest with charges", method: "/trillian.TrillianLog/QueueLeaf", req: &trillian.QueueLeafRequest{ LogId: logTree.TreeId, Leaf: &trillian.LogLeaf{}, ChargeTo: charges, }, specs: []quota.Spec{ {Group: quota.User, Kind: quota.Write, User: charge1}, {Group: quota.User, Kind: quota.Write, User: charge2}, {Group: quota.Tree, Kind: quota.Write, TreeID: logTree.TreeId}, {Group: quota.Global, Kind: quota.Write, Refundable: true}, }, wantTokens: 1, }, { desc: "quotaError", method: "/trillian.TrillianLog/GetLatestSignedLogRoot", req: &trillian.GetLatestSignedLogRootRequest{LogId: logTree.TreeId}, specs: []quota.Spec{ {Group: quota.Tree, Kind: quota.Read, TreeID: logTree.TreeId}, {Group: quota.Global, Kind: quota.Read, Refundable: true}, }, getTokensErr: errors.New("not enough tokens"), wantCode: codes.ResourceExhausted, wantTokens: 1, }, { desc: "quotaDryRunError", dryRun: true, method: "/trillian.TrillianLog/GetLatestSignedLogRoot", req: &trillian.GetLatestSignedLogRootRequest{LogId: logTree.TreeId}, specs: []quota.Spec{ {Group: quota.Tree, Kind: quota.Read, TreeID: logTree.TreeId}, {Group: quota.Global, Kind: quota.Read, Refundable: true}, }, getTokensErr: errors.New("not enough tokens"), wantTokens: 1, }, } ctx := context.Background() for _, test := range tests { t.Run(test.desc, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() admin := storage.NewMockAdminStorage(ctrl) adminTX := storage.NewMockReadOnlyAdminTX(ctrl) admin.EXPECT().Snapshot(gomock.Any()).AnyTimes().Return(adminTX, nil) adminTX.EXPECT().GetTree(gomock.Any(), logTree.TreeId).AnyTimes().Return(logTree, nil) adminTX.EXPECT().GetTree(gomock.Any(), preorderedTree.TreeId).AnyTimes().Return(preorderedTree, nil) adminTX.EXPECT().Close().AnyTimes().Return(nil) adminTX.EXPECT().Commit().AnyTimes().Return(nil) qm := quota.NewMockManager(ctrl) if test.wantTokens > 0 { qm.EXPECT().GetTokens(gomock.Any(), test.wantTokens, test.specs).Return(test.getTokensErr) } handler := &fakeHandler{resp: "ok"} intercept := New(admin, qm, test.dryRun, nil /* mf */) // resp and handler assertions are done by TestTrillianInterceptor_TreeInterception, // we're only concerned with the quota logic here. _, err := intercept.UnaryInterceptor(ctx, test.req, &grpc.UnaryServerInfo{FullMethod: test.method}, handler.run) if s, ok := status.FromError(err); !ok || s.Code() != test.wantCode { t.Errorf("UnaryInterceptor() returned err = %q, wantCode = %v", err, test.wantCode) } }) } } func TestTrillianInterceptor_QuotaInterception_ReturnsTokens(t *testing.T) { logTree := proto.Clone(testonly.LogTree).(*trillian.Tree) logTree.TreeId = 10 tests := []struct { desc string method string req, resp interface{} specs []quota.Spec handlerErr error wantGetTokens, wantPutTokens int }{ { desc: "badRequest", method: "/trillian.TrillianLog/GetLatestSignedLogRoot", req: &trillian.GetLatestSignedLogRootRequest{LogId: logTree.TreeId}, specs: []quota.Spec{ {Group: quota.Tree, Kind: quota.Read, TreeID: logTree.TreeId}, {Group: quota.Global, Kind: quota.Read, Refundable: true}, }, handlerErr: errors.New("bad request"), wantGetTokens: 1, wantPutTokens: 1, }, { desc: "newLeaf", method: "/trillian.TrillianLog/QueueLeaf", req: &trillian.QueueLeafRequest{LogId: logTree.TreeId, Leaf: &trillian.LogLeaf{}}, resp: &trillian.QueueLeafResponse{QueuedLeaf: &trillian.QueuedLogLeaf{}}, specs: []quota.Spec{ {Group: quota.Tree, Kind: quota.Write, TreeID: logTree.TreeId}, {Group: quota.Global, Kind: quota.Write, Refundable: true}, }, wantGetTokens: 1, }, { desc: "duplicateLeaf", method: "/trillian.TrillianLog/QueueLeaf", req: &trillian.QueueLeafRequest{LogId: logTree.TreeId}, resp: &trillian.QueueLeafResponse{ QueuedLeaf: &trillian.QueuedLogLeaf{ Status: status.New(codes.AlreadyExists, "duplicate leaf").Proto(), }, }, specs: []quota.Spec{ {Group: quota.Tree, Kind: quota.Write, TreeID: logTree.TreeId}, {Group: quota.Global, Kind: quota.Write, Refundable: true}, }, wantGetTokens: 1, wantPutTokens: 1, }, } defer func(timeout time.Duration) { PutTokensTimeout = timeout }(PutTokensTimeout) PutTokensTimeout = 5 * time.Second // Use a ctx with a timeout smaller than PutTokensTimeout. Not too short or // spurious failures will occur when the deadline expires. ctx, cancel := context.WithTimeout(context.Background(), PutTokensTimeout-2*time.Second) defer cancel() for _, test := range tests { t.Run(test.desc, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() admin := storage.NewMockAdminStorage(ctrl) adminTX := storage.NewMockReadOnlyAdminTX(ctrl) admin.EXPECT().Snapshot(gomock.Any()).AnyTimes().Return(adminTX, nil) adminTX.EXPECT().GetTree(gomock.Any(), logTree.TreeId).AnyTimes().Return(logTree, nil) adminTX.EXPECT().Close().AnyTimes().Return(nil) adminTX.EXPECT().Commit().AnyTimes().Return(nil) putTokensCh := make(chan bool, 1) wantDeadline := time.Now().Add(PutTokensTimeout) qm := quota.NewMockManager(ctrl) if test.wantGetTokens > 0 { qm.EXPECT().GetTokens(gomock.Any(), test.wantGetTokens, test.specs).Return(nil) } if test.wantPutTokens > 0 { refunds := make([]quota.Spec, 0) for _, s := range test.specs { if s.Refundable { refunds = append(refunds, s) } } qm.EXPECT().PutTokens(gomock.Any(), test.wantPutTokens, refunds).Do(func(ctx context.Context, numTokens int, specs []quota.Spec) { switch d, ok := ctx.Deadline(); { case !ok: t.Errorf("PutTokens() ctx has no deadline: %v", ctx) case d.Before(wantDeadline): t.Errorf("PutTokens() ctx deadline too short, got %v, want >= %v", d, wantDeadline) } putTokensCh <- true }).Return(nil) } handler := &fakeHandler{resp: test.resp, err: test.handlerErr} intercept := New(admin, qm, false /* quotaDryRun */, nil /* mf */) if _, err := intercept.UnaryInterceptor(ctx, test.req, &grpc.UnaryServerInfo{FullMethod: test.method}, handler.run); err != test.handlerErr { t.Errorf("UnaryInterceptor() returned err = [%v], want = [%v]", err, test.handlerErr) } // PutTokens may be delegated to a separate goroutine. Give it some time to complete. select { case <-putTokensCh: // OK case <-time.After(1 * time.Second): // No need to error here, gomock will fail if the call is missing. } }) } } func TestTrillianInterceptor_NotIntercepted(t *testing.T) { tests := []struct { method string req interface{} }{ // Admin {method: "/trillian.TrillianAdmin/CreateTree", req: &trillian.CreateTreeRequest{}}, {method: "/trillian.TrillianAdmin/ListTrees", req: &trillian.ListTreesRequest{}}, // Quota {method: "/quotapb.Quota/CreateConfig", req: "apb.CreateConfigRequest{}}, {method: "/quotapb.Quota/DeleteConfig", req: "apb.DeleteConfigRequest{}}, {method: "/quotapb.Quota/GetConfig", req: "apb.GetConfigRequest{}}, {method: "/quotapb.Quota/ListConfigs", req: "apb.ListConfigsRequest{}}, {method: "/quotapb.Quota/UpdateConfig", req: "apb.UpdateConfigRequest{}}, } ctx := context.Background() for _, test := range tests { handler := &fakeHandler{} intercept := New(nil /* admin */, quota.Noop(), false /* quotaDryRun */, nil /* mf */) if _, err := intercept.UnaryInterceptor(ctx, test.req, &grpc.UnaryServerInfo{FullMethod: test.method}, handler.run); err != nil { t.Errorf("UnaryInterceptor(%#v) returned err = %v", test.req, err) } if !handler.called { t.Errorf("UnaryInterceptor(%#v): handler not called", test.req) } } } // TestTrillianInterceptor_BeforeAfter tests a few Before/After interactions that are // difficult/impossible to get unless the methods are called separately (i.e., not via // UnaryInterceptor()). func TestTrillianInterceptor_BeforeAfter(t *testing.T) { logTree := proto.Clone(testonly.LogTree).(*trillian.Tree) logTree.TreeId = 10 qm := quota.Noop() tests := []struct { desc string req, resp interface{} handlerErr error wantBeforeErr bool }{ { desc: "success", req: &trillian.CreateTreeRequest{}, resp: &trillian.Tree{}, }, { desc: "badRequest", req: "bad", resp: nil, handlerErr: errors.New("bad"), wantBeforeErr: true, }, } ctx := context.Background() for _, test := range tests { t.Run(test.desc, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() admin := storage.NewMockAdminStorage(ctrl) adminTX := storage.NewMockReadOnlyAdminTX(ctrl) admin.EXPECT().Snapshot(gomock.Any()).AnyTimes().Return(adminTX, nil) adminTX.EXPECT().GetTree(gomock.Any(), logTree.TreeId).AnyTimes().Return(logTree, nil) adminTX.EXPECT().Close().AnyTimes().Return(nil) adminTX.EXPECT().Commit().AnyTimes().Return(nil) intercept := New(admin, qm, false /* quotaDryRun */, nil /* mf */) p := intercept.NewProcessor() _, err := p.Before(ctx, test.req, "/trillian.TrillianLog/foo") if gotErr := err != nil; gotErr != test.wantBeforeErr { t.Fatalf("Before() returned err = %v, wantErr = %v", err, test.wantBeforeErr) } // Other TrillianInterceptor tests assert After side-effects more in-depth, silently // returning is good enough here. p.After(ctx, test.resp, "", test.handlerErr) }) } } func TestCombine(t *testing.T) { i1 := &fakeInterceptor{key: "key1", val: "foo"} i2 := &fakeInterceptor{key: "key2", val: "bar"} i3 := &fakeInterceptor{key: "key3", val: "baz"} e1 := &fakeInterceptor{err: errors.New("intercept error")} handlerErr := errors.New("handler error") tests := []struct { desc string interceptors []*fakeInterceptor handlerErr error wantCalled int wantErr error }{ { desc: "noInterceptors", }, { desc: "single", interceptors: []*fakeInterceptor{i1}, wantCalled: 1, }, { desc: "multi1", interceptors: []*fakeInterceptor{i1, i2, i3}, wantCalled: 3, }, { desc: "multi2", interceptors: []*fakeInterceptor{i3, i1, i2}, wantCalled: 3, }, { desc: "handlerErr", interceptors: []*fakeInterceptor{i1, i2}, handlerErr: handlerErr, wantCalled: 2, wantErr: handlerErr, }, { desc: "interceptErr", interceptors: []*fakeInterceptor{i1, e1, i2}, wantCalled: 2, wantErr: e1.err, }, } ctx := context.Background() req := "request" info := &grpc.UnaryServerInfo{} for _, test := range tests { t.Run(test.desc, func(t *testing.T) { if l := len(test.interceptors); l < test.wantCalled { t.Fatalf("len(interceptors) = %v, want >= %v", l, test.wantCalled) } intercepts := []grpc.UnaryServerInterceptor{} for _, i := range test.interceptors { i.called = false intercepts = append(intercepts, i.run) } intercept := grpc_middleware.ChainUnaryServer(intercepts...) handler := &fakeHandler{resp: "response", err: test.handlerErr} resp, err := intercept(ctx, req, info, handler.run) if err != test.wantErr { t.Fatalf("err = %q, want = %q", err, test.wantErr) } called := 0 callsStopped := false for _, i := range test.interceptors { switch { case i.called: if callsStopped { t.Errorf("interceptor called out of order: %v", i) } called++ case !i.called: // No calls should have happened from here on callsStopped = true } } if called != test.wantCalled { t.Errorf("called %v interceptors, want = %v", called, test.wantCalled) } // Assertions below this point assume that the handler was called (ie, all // interceptors succeeded). if err != nil && err != test.handlerErr { return } if resp != handler.resp { t.Errorf("resp = %v, want = %v", resp, handler.resp) } // Chain the ctxs for all called interceptors and verify it got through to the // handler. wantCtx := ctx for _, i := range test.interceptors { h := &fakeHandler{resp: "ok"} i.called = false _, err = i.run(wantCtx, req, info, h.run) if err != nil { t.Fatalf("unexpected handler failure: %v", err) } wantCtx = h.ctx } if got, want := fmt.Sprintf("%v", handler.ctx), fmt.Sprintf("%v", wantCtx); got != want { t.Errorf("handler ctx, %v, want %v", got, want) } }) } } func TestErrorWrapper(t *testing.T) { badLlamaErr := status.Errorf(codes.InvalidArgument, "Bad Llama") tests := []struct { desc string resp interface{} err, wantErr error }{ { desc: "success", resp: "ok", }, { desc: "error", err: badLlamaErr, wantErr: serrors.WrapError(badLlamaErr), }, } ctx := context.Background() for _, test := range tests { t.Run(test.desc, func(t *testing.T) { handler := fakeHandler{resp: test.resp, err: test.err} resp, err := ErrorWrapper(ctx, "req", &grpc.UnaryServerInfo{}, handler.run) if resp != test.resp { t.Errorf("resp = %v, want = %v", resp, test.resp) } if !equalError(err, test.wantErr) { t.Errorf("post-WrapErrors: got %v, want %v", err, test.wantErr) } }) } } func equalError(x, y error) bool { return x == y || (x != nil && y != nil && x.Error() == y.Error()) } type fakeHandler struct { called bool resp interface{} err error // Attributes recorded by run calls ctx context.Context req interface{} } func (f *fakeHandler) run(ctx context.Context, req interface{}) (interface{}, error) { if f.called { panic("handler already called; either create a new handler or set called to false before reusing") } f.called = true f.ctx = ctx f.req = req return f.resp, f.err } type fakeInterceptor struct { key interface{} val interface{} called bool err error } func (f *fakeInterceptor) run(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { if f.called { panic("interceptor already called; either create a new interceptor or set called to false before reusing") } f.called = true if f.err != nil { return nil, f.err } return handler(context.WithValue(ctx, f.key, f.val), req) } trillian-1.6.1/server/log_rpc_server.go000066400000000000000000000510021466362047600202210ustar00rootroot00000000000000// 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 server import ( "context" "fmt" "strconv" "github.com/google/trillian" "github.com/google/trillian/extension" "github.com/google/trillian/monitoring" "github.com/google/trillian/storage" "github.com/google/trillian/trees" "github.com/google/trillian/types" "github.com/google/trillian/util/clock" "github.com/transparency-dev/merkle" "github.com/transparency-dev/merkle/proof" "github.com/transparency-dev/merkle/rfc6962" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "k8s.io/klog/v2" ) // TODO: There is no access control in the server yet and clients could easily modify // any tree. const traceSpanRoot = "/trillian" var ( optsLogInit = trees.NewGetOpts(trees.Admin, trillian.TreeType_LOG, trillian.TreeType_PREORDERED_LOG) optsLogRead = trees.NewGetOpts(trees.Query, trillian.TreeType_LOG, trillian.TreeType_PREORDERED_LOG) optsLogWrite = trees.NewGetOpts(trees.QueueLog, trillian.TreeType_LOG) optsPreorderedLogWrite = trees.NewGetOpts(trees.SequenceLog, trillian.TreeType_PREORDERED_LOG) ) // TrillianLogRPCServer implements the RPC API defined in the proto type TrillianLogRPCServer struct { registry extension.Registry timeSource clock.TimeSource leafCounter monitoring.Counter proofIndexPercentiles monitoring.Histogram fetchedLeaves monitoring.Counter } // NewTrillianLogRPCServer creates a new RPC server backed by a LogStorageProvider. func NewTrillianLogRPCServer(registry extension.Registry, timeSource clock.TimeSource) *TrillianLogRPCServer { mf := registry.MetricFactory if mf == nil { mf = monitoring.InertMetricFactory{} } return &TrillianLogRPCServer{ registry: registry, timeSource: timeSource, leafCounter: mf.NewCounter( "added_leaves", "Number of leaves requested to be added", "logid", "status", ), proofIndexPercentiles: mf.NewHistogramWithBuckets( "proof_index_percentiles", "Count of inclusion proof request index using percentage of current log size at the time", monitoring.PercentileBuckets(1), ), fetchedLeaves: mf.NewCounter( "fetched_leaves", "Count of individual leaves fetched through GetLeaves* calls", "logid", ), } } // IsHealthy returns nil if the server is healthy, error otherwise. func (t *TrillianLogRPCServer) IsHealthy() error { ctx, spanEnd := spanFor(context.Background(), "IsHealthy") defer spanEnd() return t.registry.LogStorage.CheckDatabaseAccessible(ctx) } // QueueLeaf submits one leaf to the queue. func (t *TrillianLogRPCServer) QueueLeaf(ctx context.Context, req *trillian.QueueLeafRequest) (*trillian.QueueLeafResponse, error) { ctx, spanEnd := spanFor(ctx, "QueueLeaf") defer spanEnd() if err := validateLogLeaf(req.Leaf, "QueueLeafRequest.Leaf"); err != nil { return nil, err } tree, hasher, err := t.getTreeAndHasher(ctx, req.LogId, optsLogWrite) if err != nil { return nil, err } req.Leaf.MerkleLeafHash = hasher.HashLeaf(req.Leaf.LeafValue) if len(req.Leaf.LeafIdentityHash) == 0 { req.Leaf.LeafIdentityHash = req.Leaf.MerkleLeafHash } ret, err := t.registry.LogStorage.QueueLeaves(trees.NewContext(ctx, tree), tree, []*trillian.LogLeaf{req.Leaf}, t.timeSource.Now()) if err != nil { return nil, err } if ret == nil { return nil, status.Errorf(codes.Internal, "missing response") } if len(ret) != 1 { return nil, status.Errorf(codes.Internal, "unexpected count of leaves %d", len(ret)) } return &trillian.QueueLeafResponse{QueuedLeaf: ret[0]}, nil } func hashLeaves(leaves []*trillian.LogLeaf, hasher merkle.LogHasher) { for _, leaf := range leaves { leaf.MerkleLeafHash = hasher.HashLeaf(leaf.LeafValue) if len(leaf.LeafIdentityHash) == 0 { leaf.LeafIdentityHash = leaf.MerkleLeafHash } } } // AddSequencedLeaves submits a batch of sequenced leaves to a pre-ordered log // for later integration into its underlying tree. func (t *TrillianLogRPCServer) AddSequencedLeaves(ctx context.Context, req *trillian.AddSequencedLeavesRequest) (*trillian.AddSequencedLeavesResponse, error) { ctx, spanEnd := spanFor(ctx, "AddSequencedLeaves") defer spanEnd() if err := validateAddSequencedLeavesRequest(req); err != nil { return nil, err } tree, hasher, err := t.getTreeAndHasher(ctx, req.LogId, optsPreorderedLogWrite) if err != nil { return nil, err } hashLeaves(req.Leaves, hasher) ctx = trees.NewContext(ctx, tree) leaves, err := t.registry.LogStorage.AddSequencedLeaves(ctx, tree, req.Leaves, t.timeSource.Now()) if err != nil { return nil, err } if got, want := len(leaves), len(req.Leaves); got != want { return nil, status.Errorf(codes.Internal, "AddSequencedLeaves returned %d leaves, want: %d", got, want) } label := strconv.FormatInt(req.LogId, 10) for _, l := range leaves { if l.Status == nil || l.Status.Code == int32(codes.OK) { t.leafCounter.Inc(label, "inserted") } else { t.leafCounter.Inc(label, "skipped") } } return &trillian.AddSequencedLeavesResponse{Results: leaves}, nil } // GetInclusionProof obtains the proof of inclusion in the tree for a leaf that has been sequenced. // Similar to the get proof by hash handler but one less step as we don't need to look up the index func (t *TrillianLogRPCServer) GetInclusionProof(ctx context.Context, req *trillian.GetInclusionProofRequest) (*trillian.GetInclusionProofResponse, error) { ctx, spanEnd := spanFor(ctx, "GetInclusionProof") defer spanEnd() if err := validateGetInclusionProofRequest(req); err != nil { return nil, err } logID := req.LogId tree, hasher, err := t.getTreeAndHasher(ctx, logID, optsLogRead) if err != nil { return nil, err } ctx = trees.NewContext(ctx, tree) // Next we need to make sure the requested tree size corresponds to an STH, so that we // have a usable tree revision tx, err := t.snapshotForTree(ctx, tree, "GetInclusionProof") if err != nil { return nil, err } defer t.closeAndLog(ctx, tree.TreeId, tx, "GetInclusionProof") slr, err := tx.LatestSignedLogRoot(ctx) if err != nil { return nil, err } var root types.LogRootV1 if err := root.UnmarshalBinary(slr.LogRoot); err != nil { return nil, status.Errorf(codes.Internal, "Could not read current log root: %v", err) } r := &trillian.GetInclusionProofResponse{SignedLogRoot: slr} if uint64(req.TreeSize) > root.TreeSize { return r, nil } proof, err := getInclusionProofForLeafIndex(ctx, tx, hasher, uint64(req.TreeSize), uint64(req.LeafIndex)) if err != nil { return nil, err } t.recordIndexPercent(req.LeafIndex, root.TreeSize) if err := tx.Commit(ctx); err != nil { return nil, err } r.Proof = proof return r, nil } // GetInclusionProofByHash obtains proofs of inclusion by leaf hash. Because some logs can // contain duplicate hashes it is possible for multiple proofs to be returned. func (t *TrillianLogRPCServer) GetInclusionProofByHash(ctx context.Context, req *trillian.GetInclusionProofByHashRequest) (*trillian.GetInclusionProofByHashResponse, error) { ctx, spanEnd := spanFor(ctx, "GetInclusionProofByHash") defer spanEnd() tree, hasher, err := t.getTreeAndHasher(ctx, req.LogId, optsLogRead) if err != nil { return nil, err } ctx = trees.NewContext(ctx, tree) if err := validateGetInclusionProofByHashRequest(req, hasher); err != nil { return nil, err } // Next we need to make sure the requested tree size corresponds to an STH, so that we // have a usable tree revision tx, err := t.snapshotForTree(ctx, tree, "GetInclusionProofByHash") if err != nil { return nil, err } defer t.closeAndLog(ctx, tree.TreeId, tx, "GetInclusionProofByHash") // Find the leaf index of the supplied hash leafHashes := [][]byte{req.LeafHash} leaves, err := tx.GetLeavesByHash(ctx, leafHashes, req.OrderBySequence) if err != nil { return nil, err } slr, err := tx.LatestSignedLogRoot(ctx) if err != nil { return nil, err } var root types.LogRootV1 if err := root.UnmarshalBinary(slr.LogRoot); err != nil { return nil, status.Errorf(codes.Internal, "Could not read current log root: %v", err) } // TODO(Martin2112): Need to define a limit on number of results or some form of paging etc. proofs := make([]*trillian.Proof, 0, len(leaves)) for _, leaf := range leaves { // Don't include leaves that aren't in the requested TreeSize. if leaf.LeafIndex >= req.TreeSize { continue } proof, err := getInclusionProofForLeafIndex(ctx, tx, hasher, uint64(req.TreeSize), uint64(leaf.LeafIndex)) if err != nil { return nil, err } proofs = append(proofs, proof) t.recordIndexPercent(leaf.LeafIndex, root.TreeSize) } if err := tx.Commit(ctx); err != nil { return nil, err } if len(proofs) < 1 { return nil, status.Errorf(codes.NotFound, "No leaf found for hash: %x in tree size %v", req.LeafHash, req.TreeSize) } // TODO(gbelvin): Rename "Proof" -> "Proofs" return &trillian.GetInclusionProofByHashResponse{ SignedLogRoot: slr, Proof: proofs, }, nil } // GetConsistencyProof obtains a proof that two versions of the tree are consistent with each // other and that the later tree includes all the entries of the prior one. For more details // see the example trees in RFC 6962. func (t *TrillianLogRPCServer) GetConsistencyProof(ctx context.Context, req *trillian.GetConsistencyProofRequest) (*trillian.GetConsistencyProofResponse, error) { ctx, spanEnd := spanFor(ctx, "GetConsistencyProof") defer spanEnd() if err := validateGetConsistencyProofRequest(req); err != nil { return nil, err } logID := req.LogId tree, hasher, err := t.getTreeAndHasher(ctx, logID, optsLogRead) if err != nil { return nil, err } ctx = trees.NewContext(ctx, tree) tx, err := t.snapshotForTree(ctx, tree, "GetConsistencyProof") if err != nil { return nil, err } defer t.closeAndLog(ctx, tree.TreeId, tx, "GetConsistencyProof") slr, err := tx.LatestSignedLogRoot(ctx) if err != nil { return nil, err } var root types.LogRootV1 if err := root.UnmarshalBinary(slr.LogRoot); err != nil { return nil, status.Errorf(codes.Internal, "Could not read current log root: %v", err) } r := &trillian.GetConsistencyProofResponse{SignedLogRoot: slr} if uint64(req.SecondTreeSize) > root.TreeSize { return r, nil } // Try to get consistency proof proof, err := tryGetConsistencyProof(ctx, uint64(req.FirstTreeSize), uint64(req.SecondTreeSize), tx, hasher) if err != nil { return nil, err } if err := tx.Commit(ctx); err != nil { return nil, err } // We have everything we need. Return the proof r.Proof = proof return r, nil } // GetLatestSignedLogRoot obtains the latest published tree root for the Merkle Tree that // underlies the log. func (t *TrillianLogRPCServer) GetLatestSignedLogRoot(ctx context.Context, req *trillian.GetLatestSignedLogRootRequest) (*trillian.GetLatestSignedLogRootResponse, error) { ctx, spanEnd := spanFor(ctx, "GetLatestSignedLogRoot") defer spanEnd() tree, hasher, err := t.getTreeAndHasher(ctx, req.LogId, optsLogRead) if err != nil { return nil, err } ctx = trees.NewContext(ctx, tree) tx, err := t.registry.LogStorage.SnapshotForTree(ctx, tree) if err != nil { return nil, err } defer t.closeAndLog(ctx, tree.TreeId, tx, "GetLatestSignedLogRoot") slr, err := tx.LatestSignedLogRoot(ctx) if err != nil { return nil, err } var root types.LogRootV1 if err := root.UnmarshalBinary(slr.GetLogRoot()); err != nil { return nil, status.Errorf(codes.Internal, "Could not read current log root: %v", err) } r := &trillian.GetLatestSignedLogRootResponse{SignedLogRoot: slr} if req.FirstTreeSize == 0 { // no need to get consistency proof in this case if err := t.commitAndLog(ctx, req.LogId, tx, "GetLatestSignedLogRoot"); err != nil { return nil, err } return r, nil } reqProof := &trillian.GetConsistencyProofRequest{ LogId: req.LogId, FirstTreeSize: int64(req.FirstTreeSize), SecondTreeSize: int64(root.TreeSize), } if err := validateGetConsistencyProofRequest(reqProof); err != nil { return nil, err } // Try to get consistency proof proof, err := tryGetConsistencyProof(ctx, uint64(reqProof.FirstTreeSize), uint64(reqProof.SecondTreeSize), tx, hasher) if err != nil { return nil, err } if err := t.commitAndLog(ctx, req.LogId, tx, "GetLatestSignedLogRoot"); err != nil { return nil, err } // We have everything we need. Return the response r.Proof = proof return r, nil } func tryGetConsistencyProof(ctx context.Context, firstTreeSize, secondTreeSize uint64, tx storage.ReadOnlyLogTreeTX, hasher merkle.LogHasher) (*trillian.Proof, error) { nodes, err := proof.Consistency(firstTreeSize, secondTreeSize) if err != nil { return nil, err } proof, err := fetchNodesAndBuildProof(ctx, tx, hasher.HashChildren, 0, nodes) if err != nil { return nil, err } return proof, nil } // GetLeavesByRange obtains leaves based on a range of sequence numbers within the tree. // This only fetches sequenced leaves; leaves that have been queued but not yet integrated // are not visible. func (t *TrillianLogRPCServer) GetLeavesByRange(ctx context.Context, req *trillian.GetLeavesByRangeRequest) (*trillian.GetLeavesByRangeResponse, error) { ctx, spanEnd := spanFor(ctx, "GetLeavesByRange") defer spanEnd() if err := validateGetLeavesByRangeRequest(req); err != nil { return nil, err } tree, ctx, err := t.getTreeAndContext(ctx, req.LogId, optsLogRead) if err != nil { return nil, err } tx, err := t.snapshotForTree(ctx, tree, "GetLeavesByRange") if err != nil { return nil, err } defer t.closeAndLog(ctx, tree.TreeId, tx, "GetLeavesByRange") slr, err := tx.LatestSignedLogRoot(ctx) if err != nil { return nil, err } var root types.LogRootV1 if err := root.UnmarshalBinary(slr.LogRoot); err != nil { return nil, status.Errorf(codes.Internal, "Could not read current log root: %v", err) } r := &trillian.GetLeavesByRangeResponse{SignedLogRoot: slr} if req.StartIndex < int64(root.TreeSize) { leaves, err := tx.GetLeavesByRange(ctx, req.StartIndex, req.Count) if err != nil { return nil, err } label := strconv.FormatInt(req.LogId, 10) t.fetchedLeaves.Add(float64(len(leaves)), label) r.Leaves = leaves } if err := t.commitAndLog(ctx, req.LogId, tx, "GetLeavesByRange"); err != nil { return nil, err } return r, nil } // GetEntryAndProof returns both a Merkle Leaf entry and an inclusion proof for a given index // and tree size. func (t *TrillianLogRPCServer) GetEntryAndProof(ctx context.Context, req *trillian.GetEntryAndProofRequest) (*trillian.GetEntryAndProofResponse, error) { ctx, spanEnd := spanFor(ctx, "GetEntryAndProof") defer spanEnd() if err := validateGetEntryAndProofRequest(req); err != nil { return nil, err } logID := req.LogId tree, hasher, err := t.getTreeAndHasher(ctx, logID, optsLogRead) if err != nil { return nil, err } ctx = trees.NewContext(ctx, tree) // Next we need to make sure the requested tree size corresponds to an STH, so that we // have a usable tree revision tx, err := t.snapshotForTree(ctx, tree, "GetEntryAndProof") if err != nil { return nil, err } defer t.closeAndLog(ctx, tree.TreeId, tx, "GetEntryAndProof") slr, err := tx.LatestSignedLogRoot(ctx) if err != nil { return nil, err } var root types.LogRootV1 if err := root.UnmarshalBinary(slr.LogRoot); err != nil { return nil, status.Errorf(codes.Internal, "Could not read current log root: %v", err) } r := &trillian.GetEntryAndProofResponse{SignedLogRoot: slr} if req.TreeSize > int64(root.TreeSize) && req.LeafIndex < int64(root.TreeSize) { // return latest proof we can manage req.TreeSize = int64(root.TreeSize) } if req.TreeSize <= int64(root.TreeSize) { proof, err := getInclusionProofForLeafIndex(ctx, tx, hasher, uint64(req.TreeSize), uint64(req.LeafIndex)) if err != nil { return nil, err } // We also need the leaf entry leaves, err := tx.GetLeavesByRange(ctx, req.LeafIndex, 1) if err != nil { return nil, err } if len(leaves) != 1 { return nil, status.Errorf(codes.Internal, "expected one leaf from storage but got: %d", len(leaves)) } t.recordIndexPercent(req.LeafIndex, root.TreeSize) // Work is complete, we have everything we need for the response r.Proof = proof r.Leaf = leaves[0] } if err := tx.Commit(ctx); err != nil { return nil, err } return r, nil } func (t *TrillianLogRPCServer) commitAndLog(ctx context.Context, logID int64, tx storage.ReadOnlyLogTreeTX, op string) error { err := tx.Commit(ctx) if err != nil { klog.Warningf("%v: Commit failed for %v: %v", logID, op, err) } return err } func (t *TrillianLogRPCServer) closeAndLog(ctx context.Context, logID int64, tx storage.ReadOnlyLogTreeTX, op string) { err := tx.Close() if err != nil { klog.Warningf("%v: Close failed for %v: %v", logID, op, err) } } // getInclusionProofForLeafIndex is used by multiple handlers. It does the storage fetching // and makes additional checks on the returned proof. Returns a Proof suitable for inclusion in // an RPC response func getInclusionProofForLeafIndex(ctx context.Context, tx storage.ReadOnlyLogTreeTX, hasher merkle.LogHasher, size, leafIndex uint64) (*trillian.Proof, error) { nodes, err := proof.Inclusion(leafIndex, size) if err != nil { return nil, err } return fetchNodesAndBuildProof(ctx, tx, hasher.HashChildren, leafIndex, nodes) } func (t *TrillianLogRPCServer) getTreeAndHasher(ctx context.Context, treeID int64, opts trees.GetOpts) (*trillian.Tree, merkle.LogHasher, error) { tree, err := trees.GetTree(ctx, t.registry.AdminStorage, treeID, opts) if err != nil { return nil, nil, err } return tree, rfc6962.DefaultHasher, nil } func (t *TrillianLogRPCServer) getTreeAndContext(ctx context.Context, treeID int64, opts trees.GetOpts) (*trillian.Tree, context.Context, error) { tree, err := trees.GetTree(ctx, t.registry.AdminStorage, treeID, opts) if err != nil { return nil, nil, err } return tree, trees.NewContext(ctx, tree), nil } // InitLog initialises a freshly created Log by creating the first STH with // size 0. func (t *TrillianLogRPCServer) InitLog(ctx context.Context, req *trillian.InitLogRequest) (*trillian.InitLogResponse, error) { ctx, spanEnd := spanFor(ctx, "InitLog") defer spanEnd() logID := req.LogId tree, hasher, err := t.getTreeAndHasher(ctx, logID, optsLogInit) if err != nil { return nil, status.Errorf(codes.FailedPrecondition, "getTreeAndHasher()=%v", err) } var newRoot *trillian.SignedLogRoot err = t.registry.LogStorage.ReadWriteTransaction(ctx, tree, func(ctx context.Context, tx storage.LogTreeTX) error { newRoot = nil latestRoot, err := tx.LatestSignedLogRoot(ctx) if err != nil && err != storage.ErrTreeNeedsInit { return status.Errorf(codes.FailedPrecondition, "LatestSignedLogRoot()=%v", err) } // Belt and braces check. if latestRoot.GetLogRoot() != nil { return status.Errorf(codes.AlreadyExists, "log is already initialised") } logRoot, err := (&types.LogRootV1{ RootHash: hasher.EmptyRoot(), TimestampNanos: uint64(t.timeSource.Now().UnixNano()), }).MarshalBinary() if err != nil { return err } newRoot = &trillian.SignedLogRoot{LogRoot: logRoot} if err := tx.StoreSignedLogRoot(ctx, newRoot); err != nil { return status.Errorf(codes.FailedPrecondition, "StoreSignedLogRoot()=%v", err) } return nil }) if err != nil && err != storage.ErrTreeNeedsInit { return nil, err } return &trillian.InitLogResponse{ Created: newRoot, }, nil } func (t *TrillianLogRPCServer) recordIndexPercent(leafIndex int64, treeSize uint64) { if treeSize > 0 { // Work out what percentage of the current log size this index corresponds to. percent := float64(leafIndex) / float64(treeSize) * 100.0 t.proofIndexPercentiles.Observe(percent) } } func spanFor(ctx context.Context, name string) (context.Context, func()) { return monitoring.StartSpan(ctx, fmt.Sprintf("%s.%s", traceSpanRoot, name)) } func (t *TrillianLogRPCServer) snapshotForTree(ctx context.Context, tree *trillian.Tree, method string) (storage.ReadOnlyLogTreeTX, error) { tx, err := t.registry.LogStorage.SnapshotForTree(ctx, tree) if err != nil && tx != nil { // Special case to handle ErrTreeNeedsInit, which leaves the TX open. // To avoid leaking it make sure it's closed. defer t.closeAndLog(ctx, tree.TreeId, tx, method) } return tx, err } trillian-1.6.1/server/log_rpc_server_test.go000066400000000000000000001662751466362047600213030ustar00rootroot00000000000000// 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 server import ( "context" "errors" "fmt" "strconv" "strings" "testing" "time" "github.com/golang/mock/gomock" "github.com/google/go-cmp/cmp" "github.com/google/trillian" "github.com/google/trillian/extension" "github.com/google/trillian/storage" stestonly "github.com/google/trillian/storage/testonly" "github.com/google/trillian/storage/tree" "github.com/google/trillian/types" "github.com/google/trillian/util/clock" "github.com/transparency-dev/merkle/compact" "github.com/transparency-dev/merkle/rfc6962" "google.golang.org/genproto/googleapis/rpc/code" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/proto" ) // cmpMatcher is a custom gomock.Matcher that uses cmp.Equal combined with a // cmp.Comparer that knows how to properly compare proto.Message types. type cmpMatcher struct{ want interface{} } func (m cmpMatcher) Matches(got interface{}) bool { return cmp.Equal(got, m.want, cmp.Comparer(proto.Equal)) } func (m cmpMatcher) String() string { return fmt.Sprintf("equals %v", m.want) } func newTestLeaf(data []byte, extra []byte, index int64) *trillian.LogLeaf { hash := th.HashLeaf(data) return &trillian.LogLeaf{ MerkleLeafHash: hash, LeafValue: data, ExtraData: extra, LeafIndex: index, } } var ( th = rfc6962.DefaultHasher fakeTime = time.Date(2016, 5, 25, 10, 55, 5, 0, time.UTC) fakeTimeSource = clock.NewFake(fakeTime) logID1 = int64(1) logID2 = int64(2) logID3 = int64(3) leaf1 = newTestLeaf([]byte("value"), []byte("extra"), 1) leafHash1 = []byte("\xcd\x42\x40\x4d\x52\xad\x55\xcc\xfa\x9a\xca\x4a\xdc\x82\x8a\xa5\x80\x0a\xd9\xd3\x85\xa0\x67\x1f\xbc\xbf\x72\x41\x18\x32\x06\x19") leaf2 = newTestLeaf([]byte("value2"), []byte("extra"), 2) leafHash2 = []byte("\x05\x37\xd4\x81\xf7\x3a\x75\x73\x34\x32\x80\x52\xda\x3a\xf9\x62\x6c\xed\x97\x02\x8e\x20\xb8\x49\xf6\x11\x5c\x22\xcd\x76\x51\x97") leaf3 = newTestLeaf([]byte("value3"), []byte("extra3"), 3) queueRequest0 = trillian.QueueLeafRequest{LogId: logID1, Leaf: leaf1} queueRequest0Log2 = trillian.QueueLeafRequest{LogId: logID2, Leaf: leaf1} addSeqRequest0 = trillian.AddSequencedLeavesRequest{LogId: logID3, Leaves: []*trillian.LogLeaf{leaf1}} tree1 = addTreeID(stestonly.LogTree, logID1) getLogRootRequest1 = trillian.GetLatestSignedLogRootRequest{LogId: logID1} root1 = &types.LogRootV1{TimestampNanos: 987654321, RootHash: []byte("A NICE HASH"), TreeSize: 7} root1Bytes, _ = root1.MarshalBinary() signedRoot1 = &trillian.SignedLogRoot{LogRoot: root1Bytes} getInclusionProofByHashRequest7 = trillian.GetInclusionProofByHashRequest{LogId: logID1, TreeSize: 7, LeafHash: leafHash1} getInclusionProofByHashRequest25 = trillian.GetInclusionProofByHashRequest{LogId: logID1, TreeSize: 25, LeafHash: leafHash2} getInclusionProofByIndexRequest7 = trillian.GetInclusionProofRequest{LogId: logID1, TreeSize: 7, LeafIndex: 2} getInclusionProofByIndexRequest25 = trillian.GetInclusionProofRequest{LogId: logID1, TreeSize: 50, LeafIndex: 25} getEntryAndProofRequest17 = trillian.GetEntryAndProofRequest{LogId: logID1, TreeSize: 17, LeafIndex: 3} getEntryAndProofRequest17_2 = trillian.GetEntryAndProofRequest{LogId: logID1, TreeSize: 17, LeafIndex: 2} getEntryAndProofRequest17_11 = trillian.GetEntryAndProofRequest{LogId: logID1, TreeSize: 17, LeafIndex: 11} getEntryAndProofRequest7 = trillian.GetEntryAndProofRequest{LogId: logID1, TreeSize: 7, LeafIndex: 2} getConsistencyProofRequest7 = trillian.GetConsistencyProofRequest{LogId: logID1, FirstTreeSize: 4, SecondTreeSize: 7} getConsistencyProofRequest44 = trillian.GetConsistencyProofRequest{LogId: logID1, FirstTreeSize: 4, SecondTreeSize: 4} getConsistencyProofRequest48 = trillian.GetConsistencyProofRequest{LogId: logID1, FirstTreeSize: 4, SecondTreeSize: 8} nodeIdsInclusionSize7Index2 = []compact.NodeID{ compact.NewNodeID(0, 3), compact.NewNodeID(1, 0), compact.NewNodeID(0, 6), compact.NewNodeID(1, 2), } nodeIdsConsistencySize4ToSize7 = []compact.NodeID{compact.NewNodeID(0, 6), compact.NewNodeID(1, 2)} corruptLogRoot = &trillian.SignedLogRoot{LogRoot: []byte("this is not tls encoded data")} ) func TestGetLeavesByRange(t *testing.T) { ctx := context.Background() ctrl := gomock.NewController(t) defer ctrl.Finish() fakeStorage := storage.NewMockLogStorage(ctrl) fakeAdmin := storage.NewMockAdminStorage(ctrl) tree := &trillian.Tree{TreeId: 6962, TreeType: trillian.TreeType_LOG, TreeState: trillian.TreeState_ACTIVE} tests := []struct { start, count int64 skipTX bool adminErr error txErr error getErr error slrErr error root *trillian.SignedLogRoot want []*trillian.LogLeaf wantErr string }{ { start: 1, count: 1, adminErr: errors.New("admin_err"), wantErr: "admin_err", }, { start: 1, count: 1, want: []*trillian.LogLeaf{leaf1}, }, { start: 1, count: 1, txErr: errors.New("test error xyzzy"), wantErr: "test error xyzzy", }, { start: 1, count: 1, root: signedRoot1, slrErr: errors.New("SLR"), wantErr: "SLR", }, { start: 1, count: 1, root: corruptLogRoot, wantErr: "not read current log root", }, { start: 1, count: 1, getErr: errors.New("test error plugh"), wantErr: "test error plugh", }, { start: 1, count: 3, want: []*trillian.LogLeaf{leaf1, leaf2, leaf3}, }, { start: 1, count: 30, want: []*trillian.LogLeaf{leaf1, leaf2, leaf3}, }, { start: -1, count: 1, skipTX: true, wantErr: "want >= 0", }, { start: 1, count: 0, skipTX: true, wantErr: "want > 0", }, { start: 1, count: -1, skipTX: true, wantErr: "want > 0", }, } for _, test := range tests { if !test.skipTX { mockTX := storage.NewMockLogTreeTX(ctrl) mockAdminTX := storage.NewMockAdminTX(ctrl) mockAdminTX.EXPECT().GetTree(gomock.Any(), tree.TreeId).Return(tree, test.adminErr) mockAdminTX.EXPECT().Close().Return(nil) fakeAdmin.EXPECT().Snapshot(gomock.Any()).Return(mockAdminTX, nil) if test.adminErr == nil { mockAdminTX.EXPECT().Commit().Return(nil) if test.txErr != nil { fakeStorage.EXPECT().SnapshotForTree(gomock.Any(), cmpMatcher{tree}).Return(nil, test.txErr) } else { root := test.root if root == nil { root = signedRoot1 } fakeStorage.EXPECT().SnapshotForTree(gomock.Any(), cmpMatcher{tree}).Return(mockTX, nil) mockTX.EXPECT().LatestSignedLogRoot(gomock.Any()).Return(root, test.slrErr) if test.root == nil { if test.getErr != nil { mockTX.EXPECT().GetLeavesByRange(gomock.Any(), test.start, test.count).Return(nil, test.getErr) } else { mockTX.EXPECT().GetLeavesByRange(gomock.Any(), test.start, test.count).Return(test.want, nil) mockTX.EXPECT().Commit(gomock.Any()).Return(nil) } } mockTX.EXPECT().Close().Return(nil) } } else { if test.txErr != nil { mockTX.EXPECT().Commit(gomock.Any()).Return(nil) } } } registry := extension.Registry{LogStorage: fakeStorage, AdminStorage: fakeAdmin} server := NewTrillianLogRPCServer(registry, fakeTimeSource) req := trillian.GetLeavesByRangeRequest{ LogId: tree.TreeId, StartIndex: test.start, Count: test.count, } rsp, err := server.GetLeavesByRange(ctx, &req) if err != nil { if test.wantErr == "" { t.Errorf("GetLeavesByRange(%d, %+d)=nil,%v; want _,nil", req.StartIndex, req.Count, err) } else if !strings.Contains(err.Error(), test.wantErr) { t.Errorf("GetLeavesByRange(%d, %+d)=nil,%v; want _, err containing %q", req.StartIndex, req.Count, err, test.wantErr) } continue } if test.wantErr != "" { t.Errorf("GetLeavesByRange(%d, %+d)=_,nil; want nil, err containing %q", req.StartIndex, req.Count, test.wantErr) } if got := rsp.Leaves; !cmp.Equal(got, test.want, cmp.Comparer(proto.Equal)) { t.Errorf("GetLeavesByRange(%d, %+d)=%+v; want %+v", req.StartIndex, req.Count, got, test.want) } label := strconv.FormatInt(req.LogId, 10) if gotCount, wantCount := server.fetchedLeaves.Value(label), float64(len(test.want)); gotCount != wantCount { t.Errorf("GetLeavesByRange() incremented fetched count by %f, want %f", gotCount, wantCount) } } } func TestQueueLeafStorageError(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() test := newParameterizedTest(ctrl, "QueueLeaf", noTX, func(s *stestonly.FakeLogStorage) { s.QueueLeavesErr = errors.New("STORAGE") }, nopTX, func(s *TrillianLogRPCServer) error { _, err := s.QueueLeaf(context.Background(), &queueRequest0) return err }) test.executeStorageFailureTest(t, queueRequest0.LogId) } func TestQueueLeafInvalidLogId(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() test := newParameterizedTest(ctrl, "QueueLeaf", noTX, nopStorage, nopTX, func(s *TrillianLogRPCServer) error { _, err := s.QueueLeaf(context.Background(), &queueRequest0Log2) return err }) test.executeInvalidLogIDTest(t, false /* snapshot */) } func okQueuedLeaf(l *trillian.LogLeaf) *trillian.QueuedLogLeaf { return &trillian.QueuedLogLeaf{ Leaf: l, Status: status.New(codes.OK, "OK").Proto(), } } func dupeQueuedLeaf(l *trillian.LogLeaf) *trillian.QueuedLogLeaf { return &trillian.QueuedLogLeaf{ Leaf: l, Status: status.New(codes.AlreadyExists, "Seen this before, mate").Proto(), } } func TestQueueLeaf(t *testing.T) { ctx := context.Background() ctrl := gomock.NewController(t) defer ctrl.Finish() mockStorage := storage.NewMockLogStorage(ctrl) c1 := mockStorage.EXPECT().QueueLeaves(gomock.Any(), cmpMatcher{tree1}, cmpMatcher{[]*trillian.LogLeaf{leaf1}}, fakeTime).Return([]*trillian.QueuedLogLeaf{okQueuedLeaf(leaf1)}, nil) mockStorage.EXPECT().QueueLeaves(gomock.Any(), cmpMatcher{tree1}, cmpMatcher{[]*trillian.LogLeaf{leaf1}}, fakeTime).After(c1).Return([]*trillian.QueuedLogLeaf{dupeQueuedLeaf(leaf1)}, nil) registry := extension.Registry{ AdminStorage: fakeAdminStorage(ctrl, storageParams{treeID: queueRequest0.LogId, numSnapshots: 2}), LogStorage: mockStorage, } server := NewTrillianLogRPCServer(registry, fakeTimeSource) rsp, err := server.QueueLeaf(ctx, &queueRequest0) if err != nil { t.Fatalf("Failed to queue leaf: %v", err) } if rsp.QueuedLeaf == nil { t.Error("QueueLeaf() returned nil leaf; want non-nil") } if rsp.QueuedLeaf.Status.Code != int32(code.Code_OK) { t.Errorf("QueueLeaf().Status=%d,nil; want %d,nil", rsp.QueuedLeaf.Status.Code, code.Code_OK) } if !proto.Equal(queueRequest0.Leaf, rsp.QueuedLeaf.Leaf) { diff := cmp.Diff(queueRequest0.Leaf, rsp.QueuedLeaf.Leaf) t.Errorf("post-QueueLeaf() diff:\n%v", diff) } // Repeating the operation gives ALREADY_EXISTS. rsp, err = server.QueueLeaf(ctx, &queueRequest0) if err != nil { t.Fatalf("Failed to re-queue leaf: %v", err) } if rsp.QueuedLeaf == nil { t.Error("QueueLeaf() returned nil leaf; want non-nil") } if rsp.QueuedLeaf.Status == nil || rsp.QueuedLeaf.Status.Code != int32(code.Code_ALREADY_EXISTS) { sc := "nil" if rsp.QueuedLeaf.Status != nil { sc = fmt.Sprintf("%v", rsp.QueuedLeaf.Status.Code) } t.Errorf("QueueLeaf().Status=%v,nil; want %v,nil", sc, code.Code_ALREADY_EXISTS) } if !proto.Equal(queueRequest0.Leaf, rsp.QueuedLeaf.Leaf) { diff := cmp.Diff(queueRequest0.Leaf, rsp.QueuedLeaf.Leaf) t.Errorf("post-QueueLeaf() diff:\n%v", diff) } } func TestAddSequencedLeavesStorageError(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() test := newParameterizedTest(ctrl, "AddSequencedLeaves", noTX, func(s *stestonly.FakeLogStorage) { s.AddSequencedLeavesErr = errors.New("STORAGE") }, nopTX, func(s *TrillianLogRPCServer) error { _, err := s.AddSequencedLeaves(context.Background(), &addSeqRequest0) return err }) test.preordered = true test.executeStorageFailureTest(t, addSeqRequest0.LogId) } func TestAddSequencedLeavesInvalidLogId(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() test := newParameterizedTest(ctrl, "AddSequencedLeaves", noTX, nopStorage, nopTX, func(s *TrillianLogRPCServer) error { _, err := s.AddSequencedLeaves(context.Background(), &addSeqRequest0) return err }) test.executeInvalidLogIDTest(t, false /* snapshot */) } func TestAddSequencedLeaves(t *testing.T) { ctx := context.Background() ctrl := gomock.NewController(t) defer ctrl.Finish() tree := addTreeID(stestonly.PreorderedLogTree, addSeqRequest0.LogId) mockStorage := storage.NewMockLogStorage(ctrl) mockStorage.EXPECT().AddSequencedLeaves(gomock.Any(), cmpMatcher{tree}, cmpMatcher{[]*trillian.LogLeaf{leaf1}}, gomock.Any()). Return([]*trillian.QueuedLogLeaf{{Status: status.New(codes.OK, "OK").Proto()}}, nil) registry := extension.Registry{ AdminStorage: fakeAdminStorage(ctrl, storageParams{addSeqRequest0.LogId, true, 1, nil, nil}), LogStorage: mockStorage, } server := NewTrillianLogRPCServer(registry, fakeTimeSource) rsp, err := server.AddSequencedLeaves(ctx, &addSeqRequest0) if err != nil { t.Fatalf("Failed to add leaf: %v", err) } if len(rsp.Results) != 1 { t.Errorf("AddSequencedLeaves() returns %d leaves; want 1", len(rsp.Results)) } result := rsp.Results[0] if got, want := result.Status.Code, int32(code.Code_OK); got != want { t.Errorf("AddSequencedLeaves().Status.Code=%d; want %d", got, want) } if result.Leaf != nil { t.Errorf("AddSequencedLeaves().Leaf=%v; want nil", result.Leaf) } } type latestRootTest struct { desc string req *trillian.GetLatestSignedLogRootRequest wantRoot *trillian.GetLatestSignedLogRootResponse errStr string noSnap bool snapErr error noRoot bool storageRoot *trillian.SignedLogRoot rootErr error noCommit bool commitErr error noClose bool } func TestGetLatestSignedLogRoot(t *testing.T) { tests := []latestRootTest{ { desc: "snap_fail", // Test error case when failing to get a snapshot from storage. req: &getLogRootRequest1, snapErr: errors.New("SnapshotForTree() error"), errStr: "SnapshotFor", noRoot: true, noCommit: true, noClose: true, }, { desc: "storage_fail", // Test error case when storage fails to provide a root. req: &getLogRootRequest1, errStr: "LatestSigned", rootErr: errors.New("LatestSignedLogRoot() error"), noCommit: true, }, { desc: "read_error", // Test error case where the log root could not be read req: &getLogRootRequest1, errStr: "rpc error: code = Internal desc = Could not read current log root: logRootBytes too short", noCommit: true, }, { desc: "ok", // Test normal case where a root is returned correctly. req: &getLogRootRequest1, wantRoot: &trillian.GetLatestSignedLogRootResponse{SignedLogRoot: signedRoot1}, storageRoot: signedRoot1, }, } for _, test := range tests { t.Run(test.desc, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() fakeStorage := storage.NewMockLogStorage(ctrl) mockTX := storage.NewMockLogTreeTX(ctrl) if !test.noSnap { fakeStorage.EXPECT().SnapshotForTree(gomock.Any(), cmpMatcher{tree1}).Return(mockTX, test.snapErr) } if !test.noRoot { mockTX.EXPECT().LatestSignedLogRoot(gomock.Any()).Return(test.storageRoot, test.rootErr) } if !test.noCommit { mockTX.EXPECT().Commit(gomock.Any()).Return(test.commitErr) } if !test.noClose { mockTX.EXPECT().Close().Return(nil) } registry := extension.Registry{ AdminStorage: fakeAdminStorage(ctrl, storageParams{treeID: test.req.LogId, numSnapshots: 1}), LogStorage: fakeStorage, } s := NewTrillianLogRPCServer(registry, fakeTimeSource) got, err := s.GetLatestSignedLogRoot(context.Background(), test.req) if len(test.errStr) > 0 { if err == nil || !strings.Contains(err.Error(), test.errStr) { t.Errorf("GetLatestSignedLogRoot(%+v)=_,nil, want: _,err contains: %s but got: %v", test.req, test.errStr, err) } } else { if err != nil { t.Errorf("GetLatestSignedLogRoot(%+v)=_,%v, want: _,nil", test.req, err) return } // Ensure we got the expected root back. if !proto.Equal(got.SignedLogRoot, test.wantRoot.SignedLogRoot) { t.Errorf("GetConsistencyProof(%+v)=%v,nil, want: %v,nil", test.req, got, test.wantRoot) } } }) } } func TestGetProofByHashErrors(t *testing.T) { for _, tc := range []struct { name string setupStorage func(*gomock.Controller, *storage.MockLogStorage) snapErr error treeErr error req *trillian.GetInclusionProofByHashRequest errStr string wantResp *trillian.GetInclusionProofByHashResponse }{ { name: "admin snapshot fails", setupStorage: func(_ *gomock.Controller, s *storage.MockLogStorage) { }, req: &getInclusionProofByHashRequest25, snapErr: errors.New("admin snap"), errStr: "admin snap", }, { name: "get tree fails", setupStorage: func(_ *gomock.Controller, s *storage.MockLogStorage) { }, req: &getInclusionProofByHashRequest25, treeErr: errors.New("tree error"), errStr: "tree error", }, { name: "begin fails", setupStorage: func(_ *gomock.Controller, s *storage.MockLogStorage) { s.EXPECT().SnapshotForTree(gomock.Any(), cmpMatcher{tree1}).Return(nil, errors.New("TX")) }, req: &getInclusionProofByHashRequest25, errStr: "TX", }, { name: "not initialized", setupStorage: func(c *gomock.Controller, s *storage.MockLogStorage) { tx := storage.NewMockLogTreeTX(c) s.EXPECT().SnapshotForTree(gomock.Any(), cmpMatcher{tree1}).Return(tx, storage.ErrTreeNeedsInit) tx.EXPECT().Close().Return(nil) }, req: &getInclusionProofByHashRequest25, errStr: "tree needs init", }, { name: "storage error", setupStorage: func(c *gomock.Controller, s *storage.MockLogStorage) { tx := storage.NewMockLogTreeTX(c) s.EXPECT().SnapshotForTree(gomock.Any(), cmpMatcher{tree1}).Return(tx, nil) tx.EXPECT().GetLeavesByHash(gomock.Any(), [][]byte{leafHash2}, false).Return(nil, errors.New("STORAGE")) tx.EXPECT().Close().Return(nil) }, req: &getInclusionProofByHashRequest25, errStr: "STORAGE", }, { name: "get nodes fails", setupStorage: func(c *gomock.Controller, s *storage.MockLogStorage) { tx := storage.NewMockLogTreeTX(c) s.EXPECT().SnapshotForTree(gomock.Any(), cmpMatcher{tree1}).Return(tx, nil) tx.EXPECT().LatestSignedLogRoot(gomock.Any()).Return(signedRoot1, nil) tx.EXPECT().GetLeavesByHash(gomock.Any(), [][]byte{leafHash1}, false).Return([]*trillian.LogLeaf{{LeafIndex: 2}}, nil) tx.EXPECT().GetMerkleNodes(gomock.Any(), nodeIdsInclusionSize7Index2).Return(nil, errors.New("STORAGE")) tx.EXPECT().Close().Return(nil) }, req: &getInclusionProofByHashRequest7, errStr: "STORAGE", }, { name: "too few nodes", setupStorage: func(c *gomock.Controller, s *storage.MockLogStorage) { tx := storage.NewMockLogTreeTX(c) s.EXPECT().SnapshotForTree(gomock.Any(), cmpMatcher{tree1}).Return(tx, nil) tx.EXPECT().LatestSignedLogRoot(gomock.Any()).Return(signedRoot1, nil) tx.EXPECT().GetLeavesByHash(gomock.Any(), [][]byte{leafHash1}, false).Return([]*trillian.LogLeaf{{LeafIndex: 2}}, nil) // The server expects three nodes from storage but we return only two tx.EXPECT().GetMerkleNodes(gomock.Any(), nodeIdsInclusionSize7Index2).Return([]tree.Node{{}, {}}, nil) tx.EXPECT().Close().Return(nil) }, req: &getInclusionProofByHashRequest7, errStr: "expected 4 nodes", }, { name: "wrong node", setupStorage: func(c *gomock.Controller, s *storage.MockLogStorage) { tx := storage.NewMockLogTreeTX(c) s.EXPECT().SnapshotForTree(gomock.Any(), cmpMatcher{tree1}).Return(tx, nil) tx.EXPECT().LatestSignedLogRoot(gomock.Any()).Return(signedRoot1, nil) tx.EXPECT().GetLeavesByHash(gomock.Any(), [][]byte{leafHash1}, false).Return([]*trillian.LogLeaf{{LeafIndex: 2}}, nil) // We set this up so one of the returned nodes has the wrong ID tx.EXPECT().GetMerkleNodes(gomock.Any(), nodeIdsInclusionSize7Index2).Return([]tree.Node{ {ID: nodeIdsInclusionSize7Index2[0]}, {ID: compact.NewNodeID(4, 5)}, {ID: nodeIdsInclusionSize7Index2[2]}, {ID: nodeIdsInclusionSize7Index2[3]}, }, nil) tx.EXPECT().Close().Return(nil) }, req: &getInclusionProofByHashRequest7, errStr: "expected node ", }, { name: "commit fails", setupStorage: func(c *gomock.Controller, s *storage.MockLogStorage) { tx := storage.NewMockLogTreeTX(c) s.EXPECT().SnapshotForTree(gomock.Any(), cmpMatcher{tree1}).Return(tx, nil) tx.EXPECT().GetLeavesByHash(gomock.Any(), [][]byte{leafHash1}, false).Return(nil, nil) tx.EXPECT().LatestSignedLogRoot(gomock.Any()).Return(signedRoot1, nil) tx.EXPECT().Commit(gomock.Any()).Return(errors.New("COMMIT")) tx.EXPECT().Close().Return(nil) }, req: &getInclusionProofByHashRequest7, errStr: "COMMIT", }, { name: "log root fails", setupStorage: func(c *gomock.Controller, s *storage.MockLogStorage) { tx := storage.NewMockLogTreeTX(c) s.EXPECT().SnapshotForTree(gomock.Any(), cmpMatcher{tree1}).Return(tx, nil) tx.EXPECT().GetLeavesByHash(gomock.Any(), [][]byte{leafHash1}, false).Return(nil, nil) tx.EXPECT().LatestSignedLogRoot(gomock.Any()).Return(&trillian.SignedLogRoot{}, errors.New("SLR")) tx.EXPECT().Close().Return(nil) }, req: &getInclusionProofByHashRequest7, errStr: "SLR", }, { name: "bad log root", setupStorage: func(c *gomock.Controller, s *storage.MockLogStorage) { tx := storage.NewMockLogTreeTX(c) s.EXPECT().SnapshotForTree(gomock.Any(), cmpMatcher{tree1}).Return(tx, nil) tx.EXPECT().GetLeavesByHash(gomock.Any(), [][]byte{leafHash1}, false).Return(nil, nil) tx.EXPECT().LatestSignedLogRoot(gomock.Any()).Return(corruptLogRoot, nil) tx.EXPECT().Close().Return(nil) }, req: &getInclusionProofByHashRequest7, errStr: "not read current log root", }, { name: "leaf hash too short", req: &trillian.GetInclusionProofByHashRequest{ LeafHash: []byte("too-short-to-be-a-hash"), LogId: logID1, TreeSize: 7, }, errStr: "GetInclusionProofByHashRequest.LeafHash: 22 bytes, want 32", }, } { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() fakeStorage := storage.NewMockLogStorage(ctrl) if tc.setupStorage != nil { tc.setupStorage(ctrl, fakeStorage) } registry := extension.Registry{ AdminStorage: fakeAdminStorage(ctrl, storageParams{treeID: logID1, numSnapshots: 1, snapErr: tc.snapErr, treeErr: tc.treeErr}), LogStorage: fakeStorage, } server := NewTrillianLogRPCServer(registry, fakeTimeSource) resp, err := server.GetInclusionProofByHash(context.Background(), tc.req) if len(tc.errStr) > 0 { if err == nil || !strings.Contains(err.Error(), tc.errStr) { t.Errorf("GetInclusionProofByHash(%v)=(%v, %v), want (nil, err containing %q)", tc.req, resp, err, tc.errStr) } return } if err != nil || !proto.Equal(tc.wantResp, resp) { t.Errorf("GetInclusionProofByHash(%v)=(%v, %v), want (%v, nil)", tc.req, resp, err, tc.wantResp) } }) } } func TestGetProofByHash(t *testing.T) { ctx := context.Background() for _, tc := range []struct { desc string wantCode codes.Code leavesByHashVal []*trillian.LogLeaf }{ {desc: "OK", leavesByHashVal: []*trillian.LogLeaf{{LeafIndex: 2}}}, {desc: "NotFoundTreeSize", wantCode: codes.NotFound, leavesByHashVal: []*trillian.LogLeaf{{LeafIndex: 7}}}, } { t.Run(tc.desc, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() fakeStorage := storage.NewMockLogStorage(ctrl) mockTX := storage.NewMockLogTreeTX(ctrl) fakeStorage.EXPECT().SnapshotForTree(gomock.Any(), cmpMatcher{tree1}).Return(mockTX, nil) mockTX.EXPECT().LatestSignedLogRoot(gomock.Any()).Return(signedRoot1, nil) mockTX.EXPECT().GetLeavesByHash(gomock.Any(), [][]byte{leafHash1}, false).Return(tc.leavesByHashVal, nil) mockTX.EXPECT().GetMerkleNodes(gomock.Any(), nodeIdsInclusionSize7Index2).Return([]tree.Node{ {ID: nodeIdsInclusionSize7Index2[0], Hash: []byte("nodehash0")}, {ID: nodeIdsInclusionSize7Index2[1], Hash: []byte("nodehash1")}, {ID: nodeIdsInclusionSize7Index2[2], Hash: []byte("nodehash2")}, {ID: nodeIdsInclusionSize7Index2[3], Hash: []byte("nodehash3")}, }, nil).AnyTimes() mockTX.EXPECT().Commit(gomock.Any()).Return(nil) mockTX.EXPECT().Close().Return(nil) registry := extension.Registry{ AdminStorage: fakeAdminStorage(ctrl, storageParams{ treeID: logID1, numSnapshots: 1, }), LogStorage: fakeStorage, } server := NewTrillianLogRPCServer(registry, fakeTimeSource) proofResponse, err := server.GetInclusionProofByHash(ctx, &trillian.GetInclusionProofByHashRequest{ LogId: logID1, TreeSize: 7, LeafHash: leafHash1, }) if got, want := status.Code(err), tc.wantCode; got != want { t.Fatalf("GetInclusionProofByHash(): %v, want %v", err, want) } if err != nil { return } if proofResponse == nil { t.Fatalf("server response was not successful: %v", proofResponse) } expectedProof := &trillian.Proof{ LeafIndex: 2, Hashes: [][]byte{ []byte("nodehash0"), []byte("nodehash1"), th.HashChildren([]byte("nodehash3"), []byte("nodehash2")), }, } if !proto.Equal(proofResponse.Proof[0], expectedProof) { t.Fatalf("expected proof: %v but got: %v", prototext.MarshalOptions{Multiline: false}.Format(expectedProof), prototext.MarshalOptions{Multiline: false}.Format(proofResponse.Proof[0])) } }) } } func TestGetProofByIndex(t *testing.T) { for _, tc := range []struct { name string setupStorage func(*gomock.Controller, *storage.MockLogStorage) snapErr error treeErr error req *trillian.GetInclusionProofRequest errStr string wantResp *trillian.GetInclusionProofResponse }{ { name: "admin snapshot fails", setupStorage: func(_ *gomock.Controller, s *storage.MockLogStorage) { }, req: &getInclusionProofByIndexRequest25, snapErr: errors.New("admin snap"), errStr: "admin snap", }, { name: "get tree fails", setupStorage: func(_ *gomock.Controller, s *storage.MockLogStorage) { }, req: &getInclusionProofByIndexRequest25, treeErr: errors.New("tree error"), errStr: "tree error", }, { name: "begin fails", setupStorage: func(_ *gomock.Controller, s *storage.MockLogStorage) { s.EXPECT().SnapshotForTree(gomock.Any(), cmpMatcher{tree1}).Return(nil, errors.New("TX")) }, req: &getInclusionProofByIndexRequest25, errStr: "TX", }, { name: "not initialized", setupStorage: func(c *gomock.Controller, s *storage.MockLogStorage) { tx := storage.NewMockLogTreeTX(c) s.EXPECT().SnapshotForTree(gomock.Any(), cmpMatcher{tree1}).Return(tx, storage.ErrTreeNeedsInit) tx.EXPECT().Close().Return(nil) }, req: &getInclusionProofByIndexRequest25, errStr: "tree needs init", }, { name: "get nodes fails", setupStorage: func(c *gomock.Controller, s *storage.MockLogStorage) { tx := storage.NewMockLogTreeTX(c) s.EXPECT().SnapshotForTree(gomock.Any(), cmpMatcher{tree1}).Return(tx, nil) tx.EXPECT().LatestSignedLogRoot(gomock.Any()).Return(signedRoot1, nil) tx.EXPECT().GetMerkleNodes(gomock.Any(), nodeIdsInclusionSize7Index2).Return(nil, errors.New("STORAGE")) tx.EXPECT().Close().Return(nil) }, req: &getInclusionProofByIndexRequest7, errStr: "STORAGE", }, { name: "too few nodes", setupStorage: func(c *gomock.Controller, s *storage.MockLogStorage) { tx := storage.NewMockLogTreeTX(c) s.EXPECT().SnapshotForTree(gomock.Any(), cmpMatcher{tree1}).Return(tx, nil) // The server expects three nodes from storage but we return only two tx.EXPECT().LatestSignedLogRoot(gomock.Any()).Return(signedRoot1, nil) tx.EXPECT().GetMerkleNodes(gomock.Any(), nodeIdsInclusionSize7Index2).Return([]tree.Node{{}, {}}, nil) tx.EXPECT().Close().Return(nil) }, req: &getInclusionProofByIndexRequest7, errStr: "expected 4 nodes", }, { name: "wrong node", setupStorage: func(c *gomock.Controller, s *storage.MockLogStorage) { tx := storage.NewMockLogTreeTX(c) s.EXPECT().SnapshotForTree(gomock.Any(), cmpMatcher{tree1}).Return(tx, nil) // We set this up so one of the returned nodes has the wrong ID tx.EXPECT().LatestSignedLogRoot(gomock.Any()).Return(signedRoot1, nil) tx.EXPECT().GetMerkleNodes(gomock.Any(), nodeIdsInclusionSize7Index2).Return([]tree.Node{ {ID: nodeIdsInclusionSize7Index2[0]}, {ID: compact.NewNodeID(4, 5)}, {ID: nodeIdsInclusionSize7Index2[2]}, {ID: nodeIdsInclusionSize7Index2[3]}, }, nil) tx.EXPECT().Close().Return(nil) }, req: &getInclusionProofByIndexRequest7, errStr: "expected node ", }, { name: "commit fails", setupStorage: func(c *gomock.Controller, s *storage.MockLogStorage) { tx := storage.NewMockLogTreeTX(c) s.EXPECT().SnapshotForTree(gomock.Any(), cmpMatcher{tree1}).Return(tx, nil) tx.EXPECT().LatestSignedLogRoot(gomock.Any()).Return(signedRoot1, nil) tx.EXPECT().GetMerkleNodes(gomock.Any(), nodeIdsInclusionSize7Index2).Return([]tree.Node{ {ID: nodeIdsInclusionSize7Index2[0]}, {ID: nodeIdsInclusionSize7Index2[1]}, {ID: nodeIdsInclusionSize7Index2[2]}, {ID: nodeIdsInclusionSize7Index2[3]}, }, nil) tx.EXPECT().Commit(gomock.Any()).Return(errors.New("COMMIT")) tx.EXPECT().Close().Return(nil) }, req: &getInclusionProofByIndexRequest7, errStr: "COMMIT", }, { name: "log root fails", setupStorage: func(c *gomock.Controller, s *storage.MockLogStorage) { tx := storage.NewMockLogTreeTX(c) s.EXPECT().SnapshotForTree(gomock.Any(), cmpMatcher{tree1}).Return(tx, nil) tx.EXPECT().LatestSignedLogRoot(gomock.Any()).Return(&trillian.SignedLogRoot{}, errors.New("SLR")) tx.EXPECT().Close().Return(nil) }, req: &getInclusionProofByIndexRequest7, errStr: "SLR", }, { name: "bad log root", setupStorage: func(c *gomock.Controller, s *storage.MockLogStorage) { tx := storage.NewMockLogTreeTX(c) s.EXPECT().SnapshotForTree(gomock.Any(), cmpMatcher{tree1}).Return(tx, nil) tx.EXPECT().LatestSignedLogRoot(gomock.Any()).Return(corruptLogRoot, nil) tx.EXPECT().Close().Return(nil) }, req: &getInclusionProofByIndexRequest7, errStr: "not read current log root", }, { name: "ok", setupStorage: func(c *gomock.Controller, s *storage.MockLogStorage) { tx := storage.NewMockLogTreeTX(c) s.EXPECT().SnapshotForTree(gomock.Any(), cmpMatcher{tree1}).Return(tx, nil) tx.EXPECT().LatestSignedLogRoot(gomock.Any()).Return(signedRoot1, nil) tx.EXPECT().GetMerkleNodes(gomock.Any(), nodeIdsInclusionSize7Index2).Return([]tree.Node{ {ID: nodeIdsInclusionSize7Index2[0], Hash: []byte("nodehash0")}, {ID: nodeIdsInclusionSize7Index2[1], Hash: []byte("nodehash1")}, {ID: nodeIdsInclusionSize7Index2[2], Hash: []byte("nodehash2")}, {ID: nodeIdsInclusionSize7Index2[3], Hash: []byte("nodehash3")}, }, nil) tx.EXPECT().Commit(gomock.Any()).Return(nil) tx.EXPECT().Close().Return(nil) }, req: &getInclusionProofByIndexRequest7, wantResp: &trillian.GetInclusionProofResponse{ SignedLogRoot: signedRoot1, Proof: &trillian.Proof{ LeafIndex: 2, Hashes: [][]byte{ []byte("nodehash0"), []byte("nodehash1"), th.HashChildren([]byte("nodehash3"), []byte("nodehash2")), }, }, }, }, { name: "skew beyond sth", setupStorage: func(c *gomock.Controller, s *storage.MockLogStorage) { tx := storage.NewMockLogTreeTX(c) s.EXPECT().SnapshotForTree(gomock.Any(), cmpMatcher{tree1}).Return(tx, nil) tx.EXPECT().LatestSignedLogRoot(gomock.Any()).Return(signedRoot1, nil) tx.EXPECT().Close().Return(nil) }, req: &getInclusionProofByIndexRequest25, wantResp: &trillian.GetInclusionProofResponse{ SignedLogRoot: signedRoot1, }, }, } { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() fakeStorage := storage.NewMockLogStorage(ctrl) tc.setupStorage(ctrl, fakeStorage) registry := extension.Registry{ AdminStorage: fakeAdminStorage(ctrl, storageParams{treeID: logID1, numSnapshots: 1, snapErr: tc.snapErr, treeErr: tc.treeErr}), LogStorage: fakeStorage, } server := NewTrillianLogRPCServer(registry, fakeTimeSource) resp, err := server.GetInclusionProof(context.Background(), tc.req) if len(tc.errStr) > 0 { if err == nil || !strings.Contains(err.Error(), tc.errStr) { t.Errorf("GetInclusionProofByHash(%v)=%v, %v want nil, err containing: %s", tc.req, resp, err, tc.errStr) } return } if err != nil || !proto.Equal(tc.wantResp, resp) { t.Errorf("GetInclusionProofByHash(%v)=%v, %v, want: %v, nil", tc.req, resp, err, tc.wantResp) } }) } } func TestGetEntryAndProof(t *testing.T) { for _, tc := range []struct { name string setupStorage func(*gomock.Controller, *storage.MockLogStorage) snapErr error treeErr error req *trillian.GetEntryAndProofRequest errStr string wantResp *trillian.GetEntryAndProofResponse }{ { name: "admin snapshot fails", setupStorage: func(_ *gomock.Controller, s *storage.MockLogStorage) { }, req: &getEntryAndProofRequest17, snapErr: errors.New("admin snap"), errStr: "admin snap", }, { name: "get tree fails", setupStorage: func(_ *gomock.Controller, s *storage.MockLogStorage) { }, req: &getEntryAndProofRequest17, treeErr: errors.New("tree error"), errStr: "tree error", }, { name: "begin fails", setupStorage: func(_ *gomock.Controller, s *storage.MockLogStorage) { s.EXPECT().SnapshotForTree(gomock.Any(), cmpMatcher{tree1}).Return(nil, errors.New("TX")) }, req: &getEntryAndProofRequest17, errStr: "TX", }, { name: "not initialized", setupStorage: func(c *gomock.Controller, s *storage.MockLogStorage) { tx := storage.NewMockLogTreeTX(c) s.EXPECT().SnapshotForTree(gomock.Any(), cmpMatcher{tree1}).Return(tx, storage.ErrTreeNeedsInit) tx.EXPECT().Close().Return(nil) }, req: &getEntryAndProofRequest17, errStr: "tree needs init", }, { name: "storage error", setupStorage: func(c *gomock.Controller, s *storage.MockLogStorage) { tx := storage.NewMockLogTreeTX(c) s.EXPECT().SnapshotForTree(gomock.Any(), cmpMatcher{tree1}).Return(tx, nil) tx.EXPECT().LatestSignedLogRoot(gomock.Any()).Return(signedRoot1, nil) tx.EXPECT().GetMerkleNodes(gomock.Any(), nodeIdsInclusionSize7Index2).Return([]tree.Node{ {ID: nodeIdsInclusionSize7Index2[0], Hash: []byte("nodehash0")}, {ID: nodeIdsInclusionSize7Index2[1], Hash: []byte("nodehash1")}, {ID: nodeIdsInclusionSize7Index2[2], Hash: []byte("nodehash2")}, {ID: nodeIdsInclusionSize7Index2[3], Hash: []byte("nodehash3")}, }, nil) tx.EXPECT().GetLeavesByRange(gomock.Any(), int64(2), int64(1)).Return(nil, errors.New("STORAGE")) tx.EXPECT().Close().Return(nil) }, req: &getEntryAndProofRequest7, errStr: "STORAGE", }, { name: "commit fails", setupStorage: func(c *gomock.Controller, s *storage.MockLogStorage) { tx := storage.NewMockLogTreeTX(c) s.EXPECT().SnapshotForTree(gomock.Any(), cmpMatcher{tree1}).Return(tx, nil) tx.EXPECT().LatestSignedLogRoot(gomock.Any()).Return(signedRoot1, nil) tx.EXPECT().GetMerkleNodes(gomock.Any(), nodeIdsInclusionSize7Index2).Return([]tree.Node{ {ID: nodeIdsInclusionSize7Index2[0], Hash: []byte("nodehash0")}, {ID: nodeIdsInclusionSize7Index2[1], Hash: []byte("nodehash1")}, {ID: nodeIdsInclusionSize7Index2[2], Hash: []byte("nodehash2")}, {ID: nodeIdsInclusionSize7Index2[3], Hash: []byte("nodehash3")}, }, nil) tx.EXPECT().GetLeavesByRange(gomock.Any(), int64(2), int64(1)).Return([]*trillian.LogLeaf{leaf1}, nil) tx.EXPECT().Commit(gomock.Any()).Return(errors.New("COMMIT")) tx.EXPECT().Close().Return(nil) }, req: &getEntryAndProofRequest7, errStr: "COMMIT", }, { name: "log root fails", setupStorage: func(c *gomock.Controller, s *storage.MockLogStorage) { tx := storage.NewMockLogTreeTX(c) s.EXPECT().SnapshotForTree(gomock.Any(), cmpMatcher{tree1}).Return(tx, nil) tx.EXPECT().LatestSignedLogRoot(gomock.Any()).Return(signedRoot1, errors.New("SLR")) tx.EXPECT().Close().Return(nil) }, req: &getEntryAndProofRequest17, errStr: "SLR", }, { name: "bad log root", setupStorage: func(c *gomock.Controller, s *storage.MockLogStorage) { tx := storage.NewMockLogTreeTX(c) s.EXPECT().SnapshotForTree(gomock.Any(), cmpMatcher{tree1}).Return(tx, nil) tx.EXPECT().LatestSignedLogRoot(gomock.Any()).Return(corruptLogRoot, nil) tx.EXPECT().Close().Return(nil) }, req: &getEntryAndProofRequest17, errStr: "not read current log root", }, { name: "multiple leaves incorrect", setupStorage: func(c *gomock.Controller, s *storage.MockLogStorage) { tx := storage.NewMockLogTreeTX(c) s.EXPECT().SnapshotForTree(gomock.Any(), cmpMatcher{tree1}).Return(tx, nil) tx.EXPECT().LatestSignedLogRoot(gomock.Any()).Return(signedRoot1, nil) tx.EXPECT().GetMerkleNodes(gomock.Any(), nodeIdsInclusionSize7Index2).Return([]tree.Node{ {ID: nodeIdsInclusionSize7Index2[0], Hash: []byte("nodehash0")}, {ID: nodeIdsInclusionSize7Index2[1], Hash: []byte("nodehash1")}, {ID: nodeIdsInclusionSize7Index2[2], Hash: []byte("nodehash2")}, {ID: nodeIdsInclusionSize7Index2[3], Hash: []byte("nodehash3")}, }, nil) // Code passed one leaf index so expects one result, but we return more tx.EXPECT().GetLeavesByRange(gomock.Any(), int64(2), int64(1)).Return([]*trillian.LogLeaf{leaf1, leaf3}, nil) tx.EXPECT().Close().Return(nil) }, req: &getEntryAndProofRequest7, errStr: "expected one leaf", }, { name: "ok", setupStorage: func(c *gomock.Controller, s *storage.MockLogStorage) { tx := storage.NewMockLogTreeTX(c) s.EXPECT().SnapshotForTree(gomock.Any(), cmpMatcher{tree1}).Return(tx, nil) tx.EXPECT().LatestSignedLogRoot(gomock.Any()).Return(signedRoot1, nil) tx.EXPECT().GetMerkleNodes(gomock.Any(), nodeIdsInclusionSize7Index2).Return([]tree.Node{ {ID: nodeIdsInclusionSize7Index2[0], Hash: []byte("nodehash0")}, {ID: nodeIdsInclusionSize7Index2[1], Hash: []byte("nodehash1")}, {ID: nodeIdsInclusionSize7Index2[2], Hash: []byte("nodehash2")}, {ID: nodeIdsInclusionSize7Index2[3], Hash: []byte("nodehash3")}, }, nil) tx.EXPECT().GetLeavesByRange(gomock.Any(), int64(2), int64(1)).Return([]*trillian.LogLeaf{leaf1}, nil) tx.EXPECT().Commit(gomock.Any()).Return(nil) tx.EXPECT().Close().Return(nil) }, req: &getEntryAndProofRequest7, wantResp: &trillian.GetEntryAndProofResponse{ SignedLogRoot: signedRoot1, Proof: &trillian.Proof{ LeafIndex: 2, Hashes: [][]byte{ []byte("nodehash0"), []byte("nodehash1"), th.HashChildren([]byte("nodehash3"), []byte("nodehash2")), }, }, Leaf: leaf1, }, }, { name: "skew no proof", setupStorage: func(c *gomock.Controller, s *storage.MockLogStorage) { tx := storage.NewMockLogTreeTX(c) s.EXPECT().SnapshotForTree(gomock.Any(), cmpMatcher{tree1}).Return(tx, nil) tx.EXPECT().LatestSignedLogRoot(gomock.Any()).Return(signedRoot1, nil) tx.EXPECT().Commit(gomock.Any()).Return(nil) tx.EXPECT().Close().Return(nil) }, req: &getEntryAndProofRequest17_11, wantResp: &trillian.GetEntryAndProofResponse{ SignedLogRoot: signedRoot1, }, }, { name: "skew smaller tree", setupStorage: func(c *gomock.Controller, s *storage.MockLogStorage) { tx := storage.NewMockLogTreeTX(c) s.EXPECT().SnapshotForTree(gomock.Any(), cmpMatcher{tree1}).Return(tx, nil) tx.EXPECT().LatestSignedLogRoot(gomock.Any()).Return(signedRoot1, nil) tx.EXPECT().GetMerkleNodes(gomock.Any(), nodeIdsInclusionSize7Index2).Return([]tree.Node{ {ID: nodeIdsInclusionSize7Index2[0], Hash: []byte("nodehash0")}, {ID: nodeIdsInclusionSize7Index2[1], Hash: []byte("nodehash1")}, {ID: nodeIdsInclusionSize7Index2[2], Hash: []byte("nodehash2")}, {ID: nodeIdsInclusionSize7Index2[3], Hash: []byte("nodehash3")}, }, nil) tx.EXPECT().GetLeavesByRange(gomock.Any(), int64(2), int64(1)).Return([]*trillian.LogLeaf{leaf1}, nil) tx.EXPECT().Commit(gomock.Any()).Return(nil) tx.EXPECT().Close().Return(nil) }, req: &getEntryAndProofRequest17_2, wantResp: &trillian.GetEntryAndProofResponse{ SignedLogRoot: signedRoot1, Proof: &trillian.Proof{ LeafIndex: 2, Hashes: [][]byte{ []byte("nodehash0"), []byte("nodehash1"), th.HashChildren([]byte("nodehash3"), []byte("nodehash2")), }, }, Leaf: leaf1, }, }, } { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() fakeStorage := storage.NewMockLogStorage(ctrl) tc.setupStorage(ctrl, fakeStorage) registry := extension.Registry{ AdminStorage: fakeAdminStorage(ctrl, storageParams{treeID: logID1, numSnapshots: 1, snapErr: tc.snapErr, treeErr: tc.treeErr}), LogStorage: fakeStorage, } server := NewTrillianLogRPCServer(registry, fakeTimeSource) resp, err := server.GetEntryAndProof(context.Background(), tc.req) if len(tc.errStr) > 0 { if err == nil || !strings.Contains(err.Error(), tc.errStr) { t.Errorf("GetEntryAndProof(%v)=%v, %v want nil, err containing: %s", tc.req, resp, err, tc.errStr) } return } if err != nil || !proto.Equal(tc.wantResp, resp) { t.Errorf("GetEntryAndProof(%v)=%v, %v, want: %v, nil", tc.req, resp, err, tc.wantResp) } }) } } type consistProofTest struct { req *trillian.GetConsistencyProofRequest errStr string wantHashes [][]byte noSnap bool snapErr error root *trillian.SignedLogRoot noRoot bool rootErr error nodeIDs []compact.NodeID nodes []tree.Node getNodesErr error noCommit bool commitErr error } func TestGetConsistencyProof(t *testing.T) { tests := []consistProofTest{ { // Storage snapshot fails, should result in an error. Happens before we have a TX so // no Close() etc. req: &getConsistencyProofRequest7, errStr: "SnapshotFor", snapErr: errors.New("SnapshotForTree() failed"), noRoot: true, noCommit: true, }, { // Storage fails to read the log root, should result in an error. req: &getConsistencyProofRequest7, errStr: "LatestSigned", rootErr: errors.New("LatestSignedLogRoot() failed"), noCommit: true, }, { // Storage fails to unpack the log root, should result in an error. req: &getConsistencyProofRequest7, errStr: "not read current log root", root: corruptLogRoot, noCommit: true, }, { // Storage fails to get nodes, should result in an error req: &getConsistencyProofRequest7, errStr: "getMerkle", nodeIDs: nodeIdsConsistencySize4ToSize7, wantHashes: [][]byte{[]byte("nodehash")}, nodes: []tree.Node{{ID: compact.NewNodeID(2, 1), Hash: []byte("nodehash")}}, getNodesErr: errors.New("getMerkleNodes() failed"), noCommit: true, }, { // Storage fails to commit, should result in an error. req: &getConsistencyProofRequest7, errStr: "commit", wantHashes: [][]byte{[]byte("nodehash")}, nodeIDs: nodeIdsConsistencySize4ToSize7, nodes: []tree.Node{ {ID: compact.NewNodeID(0, 6), Hash: []byte("nodehash1")}, {ID: compact.NewNodeID(1, 2), Hash: []byte("nodehash2")}, }, commitErr: errors.New("commit() failed"), }, { // Storage doesn't return the requested node, should result in an error. req: &getConsistencyProofRequest7, errStr: "expected node {0 6} at", wantHashes: [][]byte{[]byte("nodehash")}, nodeIDs: nodeIdsConsistencySize4ToSize7, nodes: []tree.Node{ {ID: compact.NewNodeID(3, 1), Hash: []byte("nodehash1")}, {ID: compact.NewNodeID(1, 2), Hash: []byte("nodehash2")}, }, noCommit: true, }, { // Storage returns an unexpected extra node, should result in an error. req: &getConsistencyProofRequest7, errStr: "expected 2 nodes", wantHashes: [][]byte{[]byte("nodehash")}, nodeIDs: nodeIdsConsistencySize4ToSize7, nodes: []tree.Node{ {ID: compact.NewNodeID(0, 6), Hash: []byte("nodehash1")}, {ID: compact.NewNodeID(1, 2), Hash: []byte("nodehash2")}, {ID: compact.NewNodeID(3, 10), Hash: []byte("nodehash3")}, }, noCommit: true, }, { // Ask for a proof from size 4 to 8 but the tree is only size 7. This should succeed but with no proof. req: &getConsistencyProofRequest48, wantHashes: nil, nodeIDs: nil, noCommit: true, }, { // A normal request which should succeed. req: &getConsistencyProofRequest7, wantHashes: [][]byte{th.HashChildren([]byte("nodehash2"), []byte("nodehash1"))}, nodeIDs: nodeIdsConsistencySize4ToSize7, nodes: []tree.Node{ {ID: compact.NewNodeID(0, 6), Hash: []byte("nodehash1")}, {ID: compact.NewNodeID(1, 2), Hash: []byte("nodehash2")}, }, }, { // Tests first==second edge case, which should succeed but is an empty proof. req: &getConsistencyProofRequest44, wantHashes: [][]byte{}, nodeIDs: []compact.NodeID{}, nodes: nil, }, } ctrl := gomock.NewController(t) defer ctrl.Finish() for i, test := range tests { t.Run(fmt.Sprintf("%d:%s", i, test.errStr), func(t *testing.T) { fakeStorage := storage.NewMockLogStorage(ctrl) mockTX := storage.NewMockLogTreeTX(ctrl) if !test.noSnap { fakeStorage.EXPECT().SnapshotForTree(gomock.Any(), cmpMatcher{tree1}).Return(mockTX, test.snapErr) } if !test.noRoot { root := test.root if root == nil { root = signedRoot1 } mockTX.EXPECT().LatestSignedLogRoot(gomock.Any()).Return(root, test.rootErr) } if test.nodeIDs != nil { mockTX.EXPECT().GetMerkleNodes(gomock.Any(), test.nodeIDs).Return(test.nodes, test.getNodesErr) } if !test.noCommit { mockTX.EXPECT().Commit(gomock.Any()).Return(test.commitErr) } mockTX.EXPECT().Close().Return(nil) registry := extension.Registry{ AdminStorage: fakeAdminStorage(ctrl, storageParams{treeID: test.req.LogId, numSnapshots: 1}), LogStorage: fakeStorage, } server := NewTrillianLogRPCServer(registry, fakeTimeSource) response, err := server.GetConsistencyProof(context.Background(), test.req) if len(test.errStr) > 0 { if err == nil || !strings.Contains(err.Error(), test.errStr) { t.Errorf("GetConsistencyProof(%+v)=_, %v; want _, err containing %q", test.req, err, test.errStr) } } else { if err != nil { t.Errorf("GetConsistencyProof(%+v)=_,%v; want: _,nil", test.req, err) return } if test.wantHashes == nil { if response.Proof != nil { t.Errorf("GetConsistencyProof(%+v) want nil proof, got %v", test.req, response.Proof) } return } // Ensure we got the expected proof. wantProof := trillian.Proof{ LeafIndex: 0, Hashes: test.wantHashes, } if got, want := response.Proof, &wantProof; !proto.Equal(got, want) { t.Errorf("GetConsistencyProof(%+v)=%v,nil, want: %v,nil", test.req, got, want) } } }) } } func TestTrillianLogRPCServer_GetConsistencyProofErrors(t *testing.T) { tests := []struct { desc string req *trillian.GetConsistencyProofRequest }{ { desc: "badFirstSize", req: &trillian.GetConsistencyProofRequest{ LogId: 1, FirstTreeSize: -10, SecondTreeSize: 20, }, }, { desc: "badSecondSize", req: &trillian.GetConsistencyProofRequest{ LogId: 1, FirstTreeSize: 10, SecondTreeSize: -20, }, }, { desc: "firstGreaterThanSecond", req: &trillian.GetConsistencyProofRequest{ LogId: 1, FirstTreeSize: 10, SecondTreeSize: 9, }, }, } logServer := NewTrillianLogRPCServer(extension.Registry{}, fakeTimeSource) ctx := context.Background() for _, test := range tests { _, err := logServer.GetConsistencyProof(ctx, test.req) if s, ok := status.FromError(err); !ok || s.Code() != codes.InvalidArgument { t.Errorf("%v: GetConsistencyProof() returned err = %v, wantCode = %s", test.desc, err, codes.InvalidArgument) } } } func TestTrillianLogRPCServer_GetEntryAndProofErrors(t *testing.T) { tests := []struct { desc string req *trillian.GetEntryAndProofRequest }{ { desc: "badLeafIndex", req: &trillian.GetEntryAndProofRequest{ LogId: 1, LeafIndex: -10, TreeSize: 20, }, }, { desc: "badTreeSize", req: &trillian.GetEntryAndProofRequest{ LogId: 1, LeafIndex: 10, TreeSize: -20, }, }, { desc: "indexGreaterThanSize", req: &trillian.GetEntryAndProofRequest{ LogId: 1, LeafIndex: 10, TreeSize: 9, }, }, } logServer := NewTrillianLogRPCServer(extension.Registry{}, fakeTimeSource) ctx := context.Background() for _, test := range tests { _, err := logServer.GetEntryAndProof(ctx, test.req) if s, ok := status.FromError(err); !ok || s.Code() != codes.InvalidArgument { t.Errorf("%v: GetEntryAndProof() returned err = %v, wantCode = %s", test.desc, err, codes.InvalidArgument) } } } func TestTrillianLogRPCServer_GetInclusionProofErrors(t *testing.T) { tests := []struct { desc string req *trillian.GetInclusionProofRequest }{ { desc: "badLeafIndex", req: &trillian.GetInclusionProofRequest{ LogId: 1, LeafIndex: -10, TreeSize: 20, }, }, { desc: "badTreeSize", req: &trillian.GetInclusionProofRequest{ LogId: 1, LeafIndex: 10, TreeSize: -20, }, }, { desc: "indexGreaterThanSize", req: &trillian.GetInclusionProofRequest{ LogId: 1, LeafIndex: 10, TreeSize: 9, }, }, } logServer := NewTrillianLogRPCServer(extension.Registry{}, fakeTimeSource) ctx := context.Background() for _, test := range tests { _, err := logServer.GetInclusionProof(ctx, test.req) if s, ok := status.FromError(err); !ok || s.Code() != codes.InvalidArgument { t.Errorf("%v: GetInclusionProof() returned err = %v, wantCode = %s", test.desc, err, codes.InvalidArgument) } } } func TestTrillianLogRPCServer_GetInclusionProofByHashErrors(t *testing.T) { tests := []struct { desc string req *trillian.GetInclusionProofByHashRequest }{ { desc: "nilLeafHash", req: &trillian.GetInclusionProofByHashRequest{ LogId: 1, TreeSize: 20, }, }, { desc: "badTreeSize", req: &trillian.GetInclusionProofByHashRequest{ LogId: 1, LeafHash: []byte("32.bytes.hash..................."), TreeSize: -20, }, }, } ctx := context.Background() for _, test := range tests { t.Run(test.desc, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() registry := extension.Registry{ AdminStorage: fakeAdminStorage(ctrl, storageParams{treeID: test.req.LogId, numSnapshots: 1}), } logServer := NewTrillianLogRPCServer(registry, fakeTimeSource) _, err := logServer.GetInclusionProofByHash(ctx, test.req) if s, ok := status.FromError(err); !ok || s.Code() != codes.InvalidArgument { t.Errorf("%v: GetInclusionProofByHash() returned err = %v, wantCode = %s", test.desc, err, codes.InvalidArgument) } }) } } func TestTrillianLogRPCServer_QueueLeafErrors(t *testing.T) { leafValue := []byte("leaf value") tests := []struct { desc string req *trillian.QueueLeafRequest }{ { desc: "nilLeaf", req: &trillian.QueueLeafRequest{ LogId: 1, }, }, { desc: "nilLeafValue", req: &trillian.QueueLeafRequest{ LogId: 1, Leaf: &trillian.LogLeaf{}, }, }, { desc: "badLeafIndex", req: &trillian.QueueLeafRequest{ LogId: 1, Leaf: &trillian.LogLeaf{ LeafValue: leafValue, LeafIndex: -10, }, }, }, } logServer := NewTrillianLogRPCServer(extension.Registry{}, fakeTimeSource) ctx := context.Background() for _, test := range tests { _, err := logServer.QueueLeaf(ctx, test.req) if s, ok := status.FromError(err); !ok || s.Code() != codes.InvalidArgument { t.Errorf("%v: QueueLeaf() returned err = %v, wantCode = %s", test.desc, err, codes.InvalidArgument) } } } func TestInitLog(t *testing.T) { ctx := context.Background() // A non-empty log root logRoot, err := (&types.LogRootV1{}).MarshalBinary() if err != nil { t.Fatalf("MarshalBinary(): %v", err) } signedRoot := &trillian.SignedLogRoot{LogRoot: logRoot} for _, tc := range []struct { desc string preordered bool snapErr error treeErr error getRootErr error storeErr error wantInit bool slr *trillian.SignedLogRoot wantCode codes.Code wantErrStr string }{ {desc: "snap err", snapErr: errors.New("snap"), wantCode: codes.FailedPrecondition, wantErrStr: "snap"}, {desc: "tree err", treeErr: errors.New("tree"), wantCode: codes.FailedPrecondition, wantErrStr: "tree"}, {desc: "root err", getRootErr: errors.New("root"), wantCode: codes.FailedPrecondition, wantErrStr: "root"}, {desc: "store fail", getRootErr: storage.ErrTreeNeedsInit, storeErr: errors.New("store"), wantInit: true, wantCode: codes.FailedPrecondition}, {desc: "init new log", getRootErr: storage.ErrTreeNeedsInit, wantInit: true, wantCode: codes.OK}, {desc: "init new preordered log", preordered: true, getRootErr: storage.ErrTreeNeedsInit, wantInit: true, wantCode: codes.OK}, {desc: "init new log, no err", wantInit: true, wantCode: codes.OK}, {desc: "init already initialised log", wantInit: false, slr: signedRoot, wantCode: codes.AlreadyExists}, } { t.Run(tc.desc, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockTX := storage.NewMockLogTreeTX(ctrl) fakeStorage := &stestonly.FakeLogStorage{TX: mockTX} if tc.snapErr == nil && tc.treeErr == nil { if tc.getRootErr != nil { mockTX.EXPECT().LatestSignedLogRoot(gomock.Any()).Return(&trillian.SignedLogRoot{}, tc.getRootErr) } else { mockTX.EXPECT().LatestSignedLogRoot(gomock.Any()).Return(tc.slr, nil) } mockTX.EXPECT().Close().Return(nil) } if tc.wantInit { if tc.storeErr == nil { mockTX.EXPECT().Commit(gomock.Any()).Return(nil) } mockTX.EXPECT().StoreSignedLogRoot(gomock.Any(), gomock.Any()).Return(tc.storeErr) } registry := extension.Registry{ AdminStorage: fakeAdminStorage(ctrl, storageParams{logID1, tc.preordered, 1, tc.snapErr, tc.treeErr}), LogStorage: fakeStorage, } logServer := NewTrillianLogRPCServer(registry, fakeTimeSource) c, err := logServer.InitLog(ctx, &trillian.InitLogRequest{LogId: logID1}) if got, want := status.Code(err), tc.wantCode; got != want { t.Errorf("InitLog()=%v,%v, want err code: %v", c, got, want) } if len(tc.wantErrStr) > 0 && !strings.Contains(err.Error(), tc.wantErrStr) { t.Errorf("InitLog()=%v,%v, want err containing: %s", c, err, tc.wantErrStr) } if tc.wantInit && tc.storeErr == nil { if err != nil { t.Fatalf("InitLog()=%v,%v want err=nil", c, err) } if c.Created == nil { t.Error("InitLog first attempt didn't return a created STH.") } } else { if err == nil { t.Errorf("InitLog()=%v,%v want err", c, err) } } }) } } type ( prepareFakeStorageFunc func(*stestonly.FakeLogStorage) prepareMockTXFunc func(*storage.MockLogTreeTX) makeRPCFunc func(*TrillianLogRPCServer) error ) type txMode int func nopTX(_ *storage.MockLogTreeTX) {} func nopStorage(_ *stestonly.FakeLogStorage) {} const ( readOnly txMode = iota readWrite noTX ) type parameterizedTest struct { ctrl *gomock.Controller operation string mode txMode preordered bool prepareStorage prepareFakeStorageFunc prepareTX prepareMockTXFunc makeRPC makeRPCFunc } func newParameterizedTest(ctrl *gomock.Controller, operation string, m txMode, prepareStorage prepareFakeStorageFunc, prepareTx prepareMockTXFunc, makeRPC makeRPCFunc) *parameterizedTest { return ¶meterizedTest{ctrl, operation, m, false /* preordered */, prepareStorage, prepareTx, makeRPC} } func (p *parameterizedTest) executeInvalidLogIDTest(t *testing.T, snapshot bool) { badLogErr := errors.New("BADLOGID") adminStorage := storage.NewMockAdminStorage(p.ctrl) adminTX := storage.NewMockReadOnlyAdminTX(p.ctrl) adminStorage.EXPECT().Snapshot(gomock.Any()).MaxTimes(1).Return(adminTX, nil) adminTX.EXPECT().GetTree(gomock.Any(), gomock.Any()).MaxTimes(1).Return(nil, badLogErr) adminTX.EXPECT().Close().MaxTimes(1).Return(nil) fakeStorage := storage.NewMockLogStorage(p.ctrl) if ctx := gomock.Any(); snapshot { fakeStorage.EXPECT().SnapshotForTree(ctx, cmpMatcher{tree1}).MaxTimes(1).Return(nil, badLogErr) } registry := extension.Registry{ AdminStorage: adminStorage, LogStorage: fakeStorage, } server := NewTrillianLogRPCServer(registry, fakeTimeSource) // Make a request for a nonexistent log id if err := p.makeRPC(server); err == nil || !strings.Contains(err.Error(), badLogErr.Error()) { t.Fatalf("Returned wrong error response for nonexistent log: %s: %v", p.operation, err) } } func (p *parameterizedTest) executeStorageFailureTest(t *testing.T, logID int64) { fakeStorage := &stestonly.FakeLogStorage{} mockTX := storage.NewMockLogTreeTX(p.ctrl) mockTX.EXPECT().Close().AnyTimes() p.prepareStorage(fakeStorage) switch p.mode { case readOnly: fakeStorage.ReadOnlyTX = mockTX case readWrite: fakeStorage.TX = mockTX } if p.mode != noTX { p.prepareTX(mockTX) } registry := extension.Registry{ AdminStorage: fakeAdminStorage(p.ctrl, storageParams{logID, p.preordered, 1, nil, nil}), LogStorage: fakeStorage, } server := NewTrillianLogRPCServer(registry, fakeTimeSource) if err := p.makeRPC(server); err == nil || !strings.Contains(err.Error(), "STORAGE") { t.Fatalf("Returned wrong error response when storage failed: %s: %v", p.operation, err) } } type storageParams struct { treeID int64 preordered bool numSnapshots int snapErr error treeErr error } func fakeAdminStorage(ctrl *gomock.Controller, params storageParams) storage.AdminStorage { tree := proto.Clone(stestonly.LogTree).(*trillian.Tree) if params.preordered { tree = proto.Clone(stestonly.PreorderedLogTree).(*trillian.Tree) } tree.TreeId = params.treeID adminStorage := storage.NewMockAdminStorage(ctrl) adminTX := storage.NewMockReadOnlyAdminTX(ctrl) adminStorage.EXPECT().Snapshot(gomock.Any()).MaxTimes(params.numSnapshots).Return(adminTX, params.snapErr) adminTX.EXPECT().GetTree(gomock.Any(), params.treeID).MaxTimes(params.numSnapshots).Return(tree, params.treeErr) adminTX.EXPECT().Close().MaxTimes(params.numSnapshots).Return(nil) adminTX.EXPECT().Commit().MaxTimes(params.numSnapshots).Return(nil) return adminStorage } func addTreeID(tree *trillian.Tree, treeID int64) *trillian.Tree { newTree := proto.Clone(tree).(*trillian.Tree) newTree.TreeId = treeID return newTree } trillian-1.6.1/server/proof_fetcher.go000066400000000000000000000053351466362047600200430ustar00rootroot00000000000000// 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 server import ( "context" "fmt" "github.com/google/trillian" "github.com/google/trillian/storage/tree" "github.com/transparency-dev/merkle/compact" "github.com/transparency-dev/merkle/proof" ) // nodeReader provides read-only access to the tree nodes. type nodeReader interface { // GetMerkleNodes returns tree nodes by their IDs, in the requested order. GetMerkleNodes(ctx context.Context, ids []compact.NodeID) ([]tree.Node, error) } // fetchNodesAndBuildProof is used by both inclusion and consistency proofs. It fetches the nodes // from storage and converts them into the proof proto that will be returned to the client. // This includes rehashing where necessary to serve proofs for tree sizes between stored tree // revisions. This code only relies on the nodeReader interface so can be tested without // a complete storage implementation. func fetchNodesAndBuildProof(ctx context.Context, nr nodeReader, hasher compact.HashFn, leafIndex uint64, pn proof.Nodes) (*trillian.Proof, error) { ctx, spanEnd := spanFor(ctx, "fetchNodesAndBuildProof") defer spanEnd() nodes, err := fetchNodes(ctx, nr, pn.IDs) if err != nil { return nil, err } h := make([][]byte, len(nodes)) for i, node := range nodes { h[i] = node.Hash } proof, err := pn.Rehash(h, hasher) if err != nil { return nil, err } return &trillian.Proof{ LeafIndex: int64(leafIndex), Hashes: proof, }, nil } // fetchNodes obtains the nodes denoted by the given NodeFetch structs, and // returns them after some validation checks. func fetchNodes(ctx context.Context, nr nodeReader, ids []compact.NodeID) ([]tree.Node, error) { ctx, spanEnd := spanFor(ctx, "fetchNodes") defer spanEnd() nodes, err := nr.GetMerkleNodes(ctx, ids) if err != nil { return nil, err } if got, want := len(nodes), len(ids); got != want { return nil, fmt.Errorf("expected %d nodes from storage but got %d", want, got) } for i, node := range nodes { // Additional check that the correct node was returned. if got, want := node.ID, ids[i]; got != want { return nil, fmt.Errorf("expected node %v at proof pos %d but got %v", want, i, got) } } return nodes, nil } trillian-1.6.1/server/proof_fetcher_test.go000066400000000000000000000154001466362047600210740ustar00rootroot00000000000000// 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 server import ( "context" "encoding/hex" "fmt" "testing" "github.com/google/trillian/storage/testonly" "github.com/transparency-dev/merkle/proof" "github.com/transparency-dev/merkle/rfc6962" inmemory "github.com/transparency-dev/merkle/testonly" ) // An arbitrary tree revision to be used in tests. const testTreeRevision int64 = 3 func TestTree813FetchAll(t *testing.T) { ctx := context.Background() hasher := rfc6962.DefaultHasher.HashChildren const ts uint64 = 813 mt := treeAtSize(ts) r := testonly.NewMultiFakeNodeReaderFromLeaves([]testonly.LeafBatch{ {TreeRevision: testTreeRevision, Leaves: expandLeaves(0, ts-1), ExpectedRoot: mt.Hash()}, }) for l := uint64(271); l < ts; l++ { nodes, err := proof.Inclusion(l, ts) if err != nil { t.Fatal(err) } proof, err := fetchNodesAndBuildProof(ctx, r, hasher, l, nodes) if err != nil { t.Fatal(err) } if got, want := proof.LeafIndex, int64(l); got != want { t.Errorf("leaf index mismatch: got %d, want %d", got, want) } refProof, err := mt.InclusionProof(l, ts) if err != nil { t.Fatalf("InclusionProof: %v", err) } if got, want := len(proof.Hashes), len(refProof); got != want { for i, id := range nodes.IDs { t.Errorf("Fetch: %d => %+v", i, id) } t.Fatalf("(%d, %d): got proof len: %d, want: %d: %v\n%v", ts, l, got, want, nodes, refProof) } for i := 0; i < len(proof.Hashes); i++ { if got, want := hex.EncodeToString(proof.Hashes[i]), hex.EncodeToString(refProof[i]); got != want { t.Fatalf("(%d, %d): %d got proof node: %s, want: %s l:%d nodes: %v", ts, l, i, got, want, len(proof.Hashes), nodes) } } } } func TestTree32InclusionProofFetchAll(t *testing.T) { ctx := context.Background() hasher := rfc6962.DefaultHasher.HashChildren for ts := uint64(2); ts <= 32; ts++ { mt := treeAtSize(ts) r := testonly.NewMultiFakeNodeReaderFromLeaves([]testonly.LeafBatch{ {TreeRevision: testTreeRevision, Leaves: expandLeaves(0, ts-1), ExpectedRoot: mt.Hash()}, }) for s := uint64(2); s <= ts; s++ { for l := uint64(0); l < s; l++ { nodes, err := proof.Inclusion(l, s) if err != nil { t.Fatal(err) } proof, err := fetchNodesAndBuildProof(ctx, r, hasher, l, nodes) if err != nil { t.Fatal(err) } if got, want := proof.LeafIndex, int64(l); got != want { t.Errorf("leaf index mismatch: got %d, want %d", got, want) } refProof, err := mt.InclusionProof(l, s) if err != nil { t.Fatalf("InclusionProof: %v", err) } if got, want := len(proof.Hashes), len(refProof); got != want { t.Fatalf("(%d, %d, %d): got proof len: %d, want: %d: %v\n%v", ts, s, l, got, want, nodes, refProof) } for i := 0; i < len(proof.Hashes); i++ { if got, want := hex.EncodeToString(proof.Hashes[i]), hex.EncodeToString(refProof[i]); got != want { t.Fatalf("(%d, %d, %d): %d got proof node: %s, want: %s l:%d nodes: %v", ts, s, l, i, got, want, len(proof.Hashes), nodes) } } } } } } func TestTree32InclusionProofFetchMultiBatch(t *testing.T) { ctx := context.Background() hasher := rfc6962.DefaultHasher.HashChildren mt := treeAtSize(32) // The reader is built up with multiple batches, 4 batches x 8 leaves each r := testonly.NewMultiFakeNodeReaderFromLeaves([]testonly.LeafBatch{ {TreeRevision: testTreeRevision, Leaves: expandLeaves(0, 7), ExpectedRoot: treeAtSize(8).Hash()}, {TreeRevision: testTreeRevision + 1, Leaves: expandLeaves(8, 15), ExpectedRoot: treeAtSize(16).Hash()}, {TreeRevision: testTreeRevision + 2, Leaves: expandLeaves(16, 23), ExpectedRoot: treeAtSize(24).Hash()}, {TreeRevision: testTreeRevision + 3, Leaves: expandLeaves(24, 31), ExpectedRoot: mt.Hash()}, }) for s := uint64(2); s <= 32; s++ { for l := uint64(0); l < s; l++ { nodes, err := proof.Inclusion(l, s) if err != nil { t.Fatal(err) } // Use the highest tree revision that should be available from the node reader proof, err := fetchNodesAndBuildProof(ctx, r, hasher, l, nodes) if err != nil { t.Fatal(err) } refProof, err := mt.InclusionProof(l, s) if err != nil { t.Fatalf("InclusionProof: %v", err) } if got, want := len(proof.Hashes), len(refProof); got != want { t.Fatalf("(%d, %d, %d): got proof len: %d, want: %d: %v\n%v", 32, s, l, got, want, nodes, refProof) } for i := 0; i < len(proof.Hashes); i++ { if got, want := hex.EncodeToString(proof.Hashes[i]), hex.EncodeToString(refProof[i]); got != want { t.Fatalf("(%d, %d, %d): %d got proof node: %s, want: %s l:%d nodes: %v", 32, s, l, i, got, want, len(proof.Hashes), nodes) } } } } } func TestTree32ConsistencyProofFetchAll(t *testing.T) { ctx := context.Background() hasher := rfc6962.DefaultHasher.HashChildren for ts := uint64(2); ts <= 32; ts++ { mt := treeAtSize(ts) r := testonly.NewMultiFakeNodeReaderFromLeaves([]testonly.LeafBatch{ {TreeRevision: testTreeRevision, Leaves: expandLeaves(0, ts-1), ExpectedRoot: mt.Hash()}, }) for s1 := uint64(2); s1 < ts; s1++ { for s2 := uint64(s1 + 1); s2 < ts; s2++ { nodes, err := proof.Consistency(s1, s2) if err != nil { t.Fatal(err) } proof, err := fetchNodesAndBuildProof(ctx, r, hasher, s1, nodes) if err != nil { t.Fatal(err) } refProof, err := mt.ConsistencyProof(s1, s2) if err != nil { t.Fatalf("ConsistencyProof: %v", err) } if got, want := len(proof.Hashes), len(refProof); got != want { t.Fatalf("(%d, %d, %d): got proof len: %d, want: %d: %v\n%v", ts, s1, s2, got, want, nodes, refProof) } for i := 0; i < len(proof.Hashes); i++ { if got, want := hex.EncodeToString(proof.Hashes[i]), hex.EncodeToString(refProof[i]); got != want { t.Fatalf("(%d, %d, %d): %d got proof node: %s, want: %s l:%d nodes: %v", ts, s1, s2, i, got, want, len(proof.Hashes), nodes) } } } } } } func expandLeaves(n, m uint64) []string { leaves := make([]string, 0, m-n+1) for l := n; l <= m; l++ { leaves = append(leaves, fmt.Sprintf("Leaf %d", l)) } return leaves } func treeAtSize(n uint64) *inmemory.Tree { leaves := expandLeaves(0, n-1) mt := inmemory.New(rfc6962.DefaultHasher) for _, leaf := range leaves { mt.AppendData([]byte(leaf)) } return mt } trillian-1.6.1/server/validate.go000066400000000000000000000114411466362047600170020ustar00rootroot00000000000000// 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 server import ( "fmt" "github.com/google/trillian" "github.com/transparency-dev/merkle" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) func validateGetInclusionProofRequest(req *trillian.GetInclusionProofRequest) error { if req.TreeSize <= 0 { return status.Errorf(codes.InvalidArgument, "GetInclusionProofRequest.TreeSize: %v, want > 0", req.TreeSize) } if req.LeafIndex < 0 { return status.Errorf(codes.InvalidArgument, "GetInclusionProofRequest.LeafIndex: %v, want >= 0", req.LeafIndex) } if req.LeafIndex >= req.TreeSize { return status.Errorf(codes.InvalidArgument, "GetInclusionProofRequest.LeafIndex: %v >= TreeSize: %v, want < ", req.LeafIndex, req.TreeSize) } return nil } func validateGetInclusionProofByHashRequest(req *trillian.GetInclusionProofByHashRequest, hasher merkle.LogHasher) error { if req.TreeSize <= 0 { return status.Errorf(codes.InvalidArgument, "GetInclusionProofByHashRequest.TreeSize: %v, want > 0", req.TreeSize) } if err := validateLeafHash(req.LeafHash, hasher); err != nil { return status.Errorf(codes.InvalidArgument, "GetInclusionProofByHashRequest.LeafHash: %v", err) } return nil } func validateGetLeavesByRangeRequest(req *trillian.GetLeavesByRangeRequest) error { if req.StartIndex < 0 { return status.Errorf(codes.InvalidArgument, "GetLeavesByRangeRequest.StartIndex: %v, want >= 0", req.StartIndex) } if req.Count <= 0 { return status.Errorf(codes.InvalidArgument, "GetLeavesByRangeRequest.Count: %v, want > 0", req.Count) } return nil } func validateGetConsistencyProofRequest(req *trillian.GetConsistencyProofRequest) error { if req.FirstTreeSize <= 0 { return status.Errorf(codes.InvalidArgument, "GetConsistencyProofRequest.FirstTreeSize: %v, want > 0", req.FirstTreeSize) } if req.SecondTreeSize <= 0 { return status.Errorf(codes.InvalidArgument, "GetConsistencyProofRequest.SecondTreeSize: %v, want > 0", req.SecondTreeSize) } if req.SecondTreeSize < req.FirstTreeSize { return status.Errorf(codes.InvalidArgument, "GetConsistencyProofRequest.SecondTreeSize: %v < GetConsistencyProofRequest.FirstTreeSize: %v, want >= ", req.SecondTreeSize, req.FirstTreeSize) } return nil } func validateGetEntryAndProofRequest(req *trillian.GetEntryAndProofRequest) error { if req.TreeSize <= 0 { return status.Errorf(codes.InvalidArgument, "GetEntryAndProofRequest.TreeSize: %v, want > 0", req.TreeSize) } if req.LeafIndex < 0 { return status.Errorf(codes.InvalidArgument, "GetEntryAndProofRequest.LeafIndex: %v, want >= 0", req.LeafIndex) } if req.LeafIndex >= req.TreeSize { return status.Errorf(codes.InvalidArgument, "GetEntryAndProofRequest.LeafIndex: %v >= TreeSize: %v, want < ", req.LeafIndex, req.TreeSize) } return nil } func validateAddSequencedLeavesRequest(req *trillian.AddSequencedLeavesRequest) error { prefix := "AddSequencedLeavesRequest" if err := validateLogLeaves(req.Leaves, prefix); err != nil { return err } // Note: Not empty, as verified by validateLogLeaves. nextIndex := req.Leaves[0].LeafIndex for i, leaf := range req.Leaves { if leaf.LeafIndex != nextIndex { return status.Errorf(codes.FailedPrecondition, "%v.Leaves[%v].LeafIndex=%v, want %v", prefix, i, leaf.LeafIndex, nextIndex) } nextIndex++ } return nil } func validateLogLeaves(leaves []*trillian.LogLeaf, errPrefix string) error { if len(leaves) == 0 { return status.Errorf(codes.InvalidArgument, "%v.Leaves empty", errPrefix) } for i, leaf := range leaves { if err := validateLogLeaf(leaf, ""); err != nil { return status.Errorf(codes.InvalidArgument, "%v.Leaves[%v]%v", errPrefix, i, err) } } return nil } func validateLogLeaf(leaf *trillian.LogLeaf, errPrefix string) error { if leaf == nil { return status.Errorf(codes.InvalidArgument, "%v empty", errPrefix) } switch { case len(leaf.LeafValue) == 0: return status.Errorf(codes.InvalidArgument, "%v.LeafValue: empty", errPrefix) case leaf.LeafIndex < 0: return status.Errorf(codes.InvalidArgument, "%v.LeafIndex: %v, want >= 0", errPrefix, leaf.LeafIndex) } return nil } func validateLeafHash(hash []byte, hasher merkle.LogHasher) error { if got, want := len(hash), hasher.Size(); got != want { return fmt.Errorf("%d bytes, want %d", got, want) } return nil } trillian-1.6.1/storage/000077500000000000000000000000001466362047600150175ustar00rootroot00000000000000trillian-1.6.1/storage/README.md000066400000000000000000000104261466362047600163010ustar00rootroot00000000000000# Storage layer ## Package Layout The interface, various concrete implementations, and any associated components live here. Interfaces and types are defined at the top level package. Currently, there are two usable storage implementation for logs: * MySQL/MariaDB in the [mysql/](mysql) package. * Cloud Spanner in the [cloudspanner](cloudspanner) package. The MySQL / MariaDB implementation includes support for Maps. This has not yet been implemented by Cloud Spanner. There may be other storage implementations available from third parties. These implementations are for test purposes only and should not be used by real applications: * In-memory Storage, in the [memory](memory) package. ## Notes and Caveats The design is such that both `LogStorage` and `MapStorage` models reuse a shared `TreeStorage` model which can store arbitrary nodes in a tree. Anyone poking around in here should be aware that there are some subtle wrinkles introduced by the fact that Log trees grow upwards (i.e. the Log considers nodes at level 0 to be the leaves), and in contrast the Map considers the leaves to be at level 255 (and the root at 0), this is based on the [HStar2 algorithm](https://www.links.org/files/RevocationTransparency.pdf). ## TreeStorage ### Nodes Nodes within the tree are each given a unique `NodeID` ([see storage/types.go](storage/types.go)), this ID can be thought of as the binary path (0 for left, 1 for right) from the root of the tree down to the node in question (or, equivalently, as the binary representation of the node's horizonal index into the tree layer at the depth of the node.) *TODO(al): pictures!* ### Subtrees The `TreeStorage` model does not, in fact, store all the internal nodes of the tree; it divides the tree into subtrees of depth 8 and stores the data for each subtree as a single unit. Within these subtrees, only the (subtree-relative) "leaf" nodes are actually written to disk, the internal structure of the subtrees is re-calculated when the subtree is read from disk. Doing this compaction saves a considerable amout of on-disk space, and at least for the MySQL storage implementation, results in a ~20% speed increase. ### History Updates to the tree storage are performed in a batched fashion (i.e. some unit of update which provides a self-consistent view of the tree - e.g.: * *n* `append leaf` operations along with internal node updates for the LogStorage, and tagged with their sequence number. * *n* `set value` operations along with internal node updates for the MapStorage. These batched updates are termed `treeRevision`s, and nodes updated within each revision are tagged with a monotonically incrementing sequence number. In this fashion, the storage model records all historical revisions of the tree. To perform lookups at a particular treeRevision, the `TreeStorage` simply requests nodes from disk which are associated with the given `NodeID` and whose `treeRevsion`s are `<=` the desired revision. Currently there's no mechanism to safely garbage collect obsolete nodes so storage grows without bound. This will be addressed at some point in the future for Map trees. Log trees don't need garbage collection as they're required to preserve a full history. ### Updates to the tree The *current* treeRevision is defined to be the one referenced by the latest Signed [Map|Log] Head (if there is no SignedHead, then the current treeRevision is -1.) Updates to the tree are performed *in the future* (i.e. at `currentTreeRevision + 1`), and, to allow for reasonable performance, are not required to be atomic (i.e. a tree update may partially fail), **however** for this to work, higher layers using the storage layer must guarantee that failed tree updates are later re-tried with either precisely the same set of node chages, or a superset there-of, in order to ensure integrity of the tree. We intend to enforce this contract within the `treeStorage` layer at some point in the future. ## LogStorage *TODO(al): flesh this out* `LogStorage` builds upon `TreeStorage` and additionally provides a means of storing log leaves, and `SignedTreeHead`s, and an API for sequencing new leaves into the tree. ## MapStorage *TODO(al): flesh this out* `MapStorage` builds upon `TreeStorage` and additionally provides a means of storing map values, and `SignedMapHead`s. trillian-1.6.1/storage/admin_helpers.go000066400000000000000000000126301466362047600201620ustar00rootroot00000000000000// 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 storage import ( "context" "fmt" "github.com/google/trillian" "github.com/google/trillian/monitoring" "k8s.io/klog/v2" ) const traceSpanRoot = "/trillian/storage" // GetTree reads a tree from storage using a snapshot transaction. // It's a convenience wrapper around RunInAdminSnapshot and ReadOnlyAdminTX's GetTree. // See RunInAdminSnapshot if you need to perform more than one action per transaction. func GetTree(ctx context.Context, admin AdminStorage, treeID int64) (*trillian.Tree, error) { ctx, spanEnd := spanFor(ctx, "GetTree") defer spanEnd() var tree *trillian.Tree err := RunInAdminSnapshot(ctx, admin, func(tx ReadOnlyAdminTX) error { var err error tree, err = tx.GetTree(ctx, treeID) return err }) return tree, err } // ListTrees reads trees from storage using a snapshot transaction. // It's a convenience wrapper around RunInAdminSnapshot and ReadOnlyAdminTX's ListTrees. // See RunInAdminSnapshot if you need to perform more than one action per transaction. func ListTrees(ctx context.Context, admin AdminStorage, includeDeleted bool) ([]*trillian.Tree, error) { ctx, spanEnd := spanFor(ctx, "ListTrees") defer spanEnd() var resp []*trillian.Tree err := RunInAdminSnapshot(ctx, admin, func(tx ReadOnlyAdminTX) error { var err error resp, err = tx.ListTrees(ctx, includeDeleted) return err }) return resp, err } // CreateTree creates a tree in storage. // It's a convenience wrapper around ReadWriteTransaction and AdminWriter's CreateTree. // See ReadWriteTransaction if you need to perform more than one action per transaction. func CreateTree(ctx context.Context, admin AdminStorage, tree *trillian.Tree) (*trillian.Tree, error) { ctx, spanEnd := spanFor(ctx, "CreateTree") defer spanEnd() var createdTree *trillian.Tree err := admin.ReadWriteTransaction(ctx, func(ctx context.Context, tx AdminTX) error { var err error createdTree, err = tx.CreateTree(ctx, tree) return err }) return createdTree, err } // UpdateTree updates a tree in storage. // It's a convenience wrapper around ReadWriteTransaction and AdminWriter's UpdateTree. // See ReadWriteTransaction if you need to perform more than one action per transaction. func UpdateTree(ctx context.Context, admin AdminStorage, treeID int64, fn func(*trillian.Tree)) (*trillian.Tree, error) { ctx, spanEnd := spanFor(ctx, "UpdateTree") defer spanEnd() var updatedTree *trillian.Tree err := admin.ReadWriteTransaction(ctx, func(ctx context.Context, tx AdminTX) error { var err error updatedTree, err = tx.UpdateTree(ctx, treeID, fn) return err }) return updatedTree, err } // SoftDeleteTree soft-deletes a tree in storage. // It's a convenience wrapper around ReadWriteTransaction and AdminWriter's SoftDeleteTree. // See ReadWriteTransaction if you need to perform more than one action per transaction. func SoftDeleteTree(ctx context.Context, admin AdminStorage, treeID int64) (*trillian.Tree, error) { ctx, spanEnd := spanFor(ctx, "SoftDeleteTree") defer spanEnd() var tree *trillian.Tree err := admin.ReadWriteTransaction(ctx, func(ctx context.Context, tx AdminTX) error { var err error tree, err = tx.SoftDeleteTree(ctx, treeID) return err }) return tree, err } // HardDeleteTree hard-deletes a tree from storage. // It's a convenience wrapper around ReadWriteTransaction and AdminWriter's HardDeleteTree. // See ReadWriteTransaction if you need to perform more than one action per transaction. func HardDeleteTree(ctx context.Context, admin AdminStorage, treeID int64) error { ctx, spanEnd := spanFor(ctx, "HardDeleteTree") defer spanEnd() return admin.ReadWriteTransaction(ctx, func(ctx context.Context, tx AdminTX) error { return tx.HardDeleteTree(ctx, treeID) }) } // UndeleteTree undeletes a tree in storage. // It's a convenience wrapper around ReadWriteTransaction and AdminWriter's UndeleteTree. // See ReadWriteTransaction if you need to perform more than one action per transaction. func UndeleteTree(ctx context.Context, admin AdminStorage, treeID int64) (*trillian.Tree, error) { ctx, spanEnd := spanFor(ctx, "UndeleteTree") defer spanEnd() var tree *trillian.Tree err := admin.ReadWriteTransaction(ctx, func(ctx context.Context, tx AdminTX) error { var err error tree, err = tx.UndeleteTree(ctx, treeID) return err }) return tree, err } // RunInAdminSnapshot runs fn against a ReadOnlyAdminTX and commits if no error is returned. func RunInAdminSnapshot(ctx context.Context, admin AdminStorage, fn func(tx ReadOnlyAdminTX) error) error { tx, err := admin.Snapshot(ctx) if err != nil { return err } defer func() { if err := tx.Close(); err != nil { klog.Errorf("tx.Close(): %v", err) } }() if err := fn(tx); err != nil { return err } return tx.Commit() } func spanFor(ctx context.Context, name string) (context.Context, func()) { return monitoring.StartSpan(ctx, fmt.Sprintf("%s.%s", traceSpanRoot, name)) } trillian-1.6.1/storage/admin_helpers_test.go000066400000000000000000000112311466362047600212150ustar00rootroot00000000000000// 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 storage import ( "context" "errors" "strings" "testing" "github.com/golang/mock/gomock" "github.com/google/trillian" "google.golang.org/protobuf/proto" ) // TestGetTree is really testing param / result / error propagation is correct // as it wraps RunInAdminSnapshot which has its own tests. The other helpers // use admin.RunInTransaction, which is provided by the implementation, so // cannot usefully be tested here. func TestGetTree(t *testing.T) { dummyTree := &trillian.Tree{TreeId: 999, TreeType: trillian.TreeType_LOG} tests := []struct { name string setup func(*MockAdminStorage, *MockReadOnlyAdminTX) testFn func(AdminStorage) (*trillian.Tree, error) snapErr error wantErrStr string wantTree *trillian.Tree }{ { name: "get snap fail", snapErr: errors.New("SNAP"), wantErrStr: "SNAP", setup: func(_ *MockAdminStorage, atx *MockReadOnlyAdminTX) { }, testFn: func(as AdminStorage) (*trillian.Tree, error) { return GetTree(context.Background(), as, 999) }, }, { name: "get tree fail", wantErrStr: "TREE", setup: func(_ *MockAdminStorage, atx *MockReadOnlyAdminTX) { atx.EXPECT().GetTree(gomock.Any(), int64(999)).Return(nil, errors.New("TREE")) }, testFn: func(as AdminStorage) (*trillian.Tree, error) { return GetTree(context.Background(), as, 999) }, }, { name: "get tree ok", setup: func(_ *MockAdminStorage, atx *MockReadOnlyAdminTX) { atx.EXPECT().GetTree(gomock.Any(), int64(999)).Return(dummyTree, nil) atx.EXPECT().Commit().Return(nil) }, testFn: func(as AdminStorage) (*trillian.Tree, error) { return GetTree(context.Background(), as, 999) }, wantTree: dummyTree, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { if tc.setup == nil || tc.testFn == nil { t.Fatalf("Bad test case: %v", tc) } ctrl := gomock.NewController(t) defer ctrl.Finish() as := NewMockAdminStorage(ctrl) atx := NewMockReadOnlyAdminTX(ctrl) as.EXPECT().Snapshot(gomock.Any()).Return(atx, tc.snapErr) if tc.snapErr == nil { atx.EXPECT().Close().Return(nil) } tc.setup(as, atx) tree, err := tc.testFn(as) if len(tc.wantErrStr) == 0 { // Expect no error. if err != nil { t.Errorf("GetTree()=%v,%v, want: err=nil", tree, err) } if !proto.Equal(tc.wantTree, tree) { t.Errorf("GetTree()=%v,%v, want: %v,nil", tree, err, tc.wantTree) } } else { if err == nil || !strings.Contains(err.Error(), tc.wantErrStr) { t.Errorf("GetTree()=%v,%v, want: err with: %s", tree, err, tc.wantErrStr) } } }) } } func TestRunInAdminSnapshot(t *testing.T) { tests := []struct { name string snapErr error wantCommit bool commitErr error wantErrStr string fn func(tx ReadOnlyAdminTX) error }{ { name: "snap fail", snapErr: errors.New("SNAP"), wantErrStr: "SNAP", }, { name: "tx err", fn: func(tx ReadOnlyAdminTX) error { return errors.New("TX") }, wantErrStr: "TX", }, { name: "commit err", fn: func(tx ReadOnlyAdminTX) error { return nil }, wantCommit: true, commitErr: errors.New("commit"), wantErrStr: "commit", }, { name: "tx ok", fn: func(tx ReadOnlyAdminTX) error { return nil }, wantCommit: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() as := NewMockAdminStorage(ctrl) atx := NewMockAdminTX(ctrl) as.EXPECT().Snapshot(gomock.Any()).Return(atx, tc.snapErr) if tc.snapErr == nil { atx.EXPECT().Close().Return(nil) } if tc.wantCommit { atx.EXPECT().Commit().Return(tc.commitErr) } err := RunInAdminSnapshot(context.Background(), as, tc.fn) if len(tc.wantErrStr) == 0 { // Expect no error. if err != nil { t.Errorf("RunInAdminSnapshot()=%v, want: nil", err) } } else { if err == nil || !strings.Contains(err.Error(), tc.wantErrStr) { t.Errorf("RunInAdminSnapshot()=%v, want: err with: %s", err, tc.wantErrStr) } } }) } } trillian-1.6.1/storage/admin_storage.go000066400000000000000000000104471466362047600201700ustar00rootroot00000000000000// 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 storage import ( "context" "github.com/google/trillian" ) // ReadOnlyAdminTX is a transaction capable only of read operations in the // AdminStorage. type ReadOnlyAdminTX interface { // GetTree returns the tree corresponding to treeID or an error. GetTree(ctx context.Context, treeID int64) (*trillian.Tree, error) // ListTrees returns all trees in storage. // Note that there's no authorization restriction on the trees returned, // so it should be used with caution in production code. ListTrees(ctx context.Context, includeDeleted bool) ([]*trillian.Tree, error) // Commit applies the operations performed to the underlying storage. It must // be called before any reads from storage are considered consistent. Commit() error // Close rolls back the transaction if it wasn't committed or closed // previously. Resources are cleaned up regardless of the success, and the // transaction should not be used after it. Close() error } // AdminTX is a transaction capable of read and write operations in the // AdminStorage. type AdminTX interface { ReadOnlyAdminTX AdminWriter } // AdminTXFunc is the signature for functions passed to ReadWriteTransaction. type AdminTXFunc func(context.Context, AdminTX) error // AdminStorage represents the persistent storage of tree data. type AdminStorage interface { // Snapshot starts a read-only transaction. // A transaction must be explicitly committed before the data read by it // is considered consistent. Snapshot(ctx context.Context) (ReadOnlyAdminTX, error) // ReadWriteTransaction creates a transaction, and runs f with it. // Some storage implementations may retry aborted transactions, so // f MUST be idempotent. ReadWriteTransaction(ctx context.Context, f AdminTXFunc) error // CheckDatabaseAccessible checks whether we are able to connect to / open the // underlying storage. CheckDatabaseAccessible(ctx context.Context) error } // AdminWriter provides a write-only interface for tree data. type AdminWriter interface { // CreateTree inserts the specified tree in storage, returning a tree // with all storage-generated fields set. // Note that treeID and timestamps will be automatically generated by // the storage layer, thus may be ignored by the implementation. // Remaining fields must be set to valid values. // Returns an error if the tree is invalid or creation fails. CreateTree(ctx context.Context, tree *trillian.Tree) (*trillian.Tree, error) // UpdateTree updates the specified tree in storage, returning a tree // with all storage-generated fields set. // updateFunc is called to perform the desired tree modifications. Refer // to trillian.Tree for details on which fields are mutable and what is // considered valid. // Returns an error if the tree is invalid or the update cannot be // performed. UpdateTree(ctx context.Context, treeID int64, updateFunc func(*trillian.Tree)) (*trillian.Tree, error) // SoftDeleteTree soft deletes the specified tree. // The tree must exist and not be already soft deleted, otherwise an error is returned. // Soft deletion may be undone via UndeleteTree. SoftDeleteTree(ctx context.Context, treeID int64) (*trillian.Tree, error) // HardDeleteTree hard deletes (i.e. completely removes from storage) the specified tree and all // records related to it. // The tree must exist and currently be soft deleted, as per SoftDeletedTree, otherwise an error // is returned. // Hard deleted trees cannot be recovered. HardDeleteTree(ctx context.Context, treeID int64) error // UndeleteTree undeletes a soft-deleted tree. // The tree must exist and currently be soft deleted, as per SoftDeletedTree, otherwise an error // is returned. UndeleteTree(ctx context.Context, treeID int64) (*trillian.Tree, error) } trillian-1.6.1/storage/cache/000077500000000000000000000000001466362047600160625ustar00rootroot00000000000000trillian-1.6.1/storage/cache/gen.go000066400000000000000000000021601466362047600171610ustar00rootroot00000000000000// 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 cache provides subtree caching functionality. package cache //go:generate mockgen -self_package github.com/google/trillian/storage/cache -package cache -imports github.com/google/trillian/storage/storagepb -destination mock_node_storage.go github.com/google/trillian/storage/cache NodeStorage import "github.com/google/trillian/storage/storagepb" // NodeStorage provides an interface for storing and retrieving subtrees. type NodeStorage interface { GetSubtree(prefix []byte) (*storagepb.SubtreeProto, error) } trillian-1.6.1/storage/cache/layout.go000066400000000000000000000035641466362047600177360ustar00rootroot00000000000000// 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 cache import ( "encoding/binary" "github.com/transparency-dev/merkle/compact" ) // getTileID returns the path from the "virtual" root at level 64 to the root // of the tile that the given node belongs to. All the bits of the returned // slice are significant because all tile heights are 8. // // Note that a root of a tile belongs to a tile above it (as its leaf node). // The exception is the "virtual" root which belongs to its own "pseudo" tile. func getTileID(id compact.NodeID) []byte { if id.Level >= 64 { return []byte{} // Note: Not nil, so that storage/SQL doesn't use NULL. } index := id.Index >> (8 - id.Level%8) bytesCount := (64 - id.Level - 1) / 8 var bytes [8]byte binary.BigEndian.PutUint64(bytes[:], index) return bytes[8-bytesCount:] } // splitID returns the path from the "virtual" root at level 64 to the root of // the tile that the given node belongs to, and the corresponding local address // of this node within this tile. func splitID(id compact.NodeID) ([]byte, *suffix) { if id.Level >= 64 { return []byte{}, emptySuffix } tileID := getTileID(id) var bytes [8]byte bits := 64 - id.Level - uint(len(tileID)*8) binary.BigEndian.PutUint64(bytes[:], id.Index<<(64-bits)) suffix := newSuffix(uint8(bits), bytes[:]) return tileID, suffix } trillian-1.6.1/storage/cache/layout_test.go000066400000000000000000000064511466362047600207730ustar00rootroot00000000000000// 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 cache import ( "bytes" "fmt" "testing" "github.com/transparency-dev/merkle/compact" ) func TestGetTileID(t *testing.T) { for _, tc := range []struct { id compact.NodeID want []byte }{ {id: nID(0, 0), want: []byte{0, 0, 0, 0, 0, 0, 0}}, {id: nID(0, 255), want: []byte{0, 0, 0, 0, 0, 0, 0}}, {id: nID(0, 256), want: []byte{0, 0, 0, 0, 0, 0, 1}}, {id: nID(0, 12345), want: []byte{0, 0, 0, 0, 0, 0, 48}}, {id: nID(3, 31), want: []byte{0, 0, 0, 0, 0, 0, 0}}, {id: nID(3, 32), want: []byte{0, 0, 0, 0, 0, 0, 1}}, {id: nID(7, 1), want: []byte{0, 0, 0, 0, 0, 0, 0}}, {id: nID(7, 2), want: []byte{0, 0, 0, 0, 0, 0, 1}}, {id: nID(8, 0), want: []byte{0, 0, 0, 0, 0, 0}}, {id: nID(10, 129), want: []byte{0, 0, 0, 0, 0, 2}}, {id: nID(20, 0x14B8DC5C), want: []byte{0x00, 0x01, 0x4B, 0x8D, 0xC5}}, {id: nID(47, 0), want: []byte{0, 0}}, {id: nID(47, 1), want: []byte{0, 0}}, {id: nID(48, 1234), want: []byte{4}}, {id: nID(60, 10), want: []byte{}}, {id: nID(64, 0), want: []byte{}}, } { t.Run(fmt.Sprintf("%d:%d", tc.id.Level, tc.id.Index), func(t *testing.T) { if got, want := getTileID(tc.id), tc.want; !bytes.Equal(got, want) { t.Errorf("getTileID: got %x, want %x", got, want) } }) } } func TestSplitID(t *testing.T) { for _, tc := range []struct { id compact.NodeID outPrefix []byte outSuffixBits int outSuffix []byte }{ {nID(32, 0x1234567f), []byte{0x12, 0x34, 0x56}, 8, []byte{0x7f}}, {nID(35, 0x123456ff>>3), []byte{0x12, 0x34, 0x56}, 5, []byte{0xf8}}, {nID(39, 0x123456ff>>7), []byte{0x12, 0x34, 0x56}, 1, []byte{0x80}}, {nID(48, 0x12345678>>16), []byte{0x12}, 8, []byte{0x34}}, {nID(55, 0x12345678>>23), []byte{0x12}, 1, []byte{0x00}}, {nID(56, 0x12345678>>24), []byte{}, 8, []byte{0x12}}, {nID(57, 0x12345678>>25), []byte{}, 7, []byte{0x12}}, {nID(64, 0x12345678>>32), []byte{}, 0, []byte{0}}, {nID(62, 0x70>>6), []byte{}, 2, []byte{0x40}}, {nID(61, 0x70>>5), []byte{}, 3, []byte{0x60}}, {nID(60, 0x70>>4), []byte{}, 4, []byte{0x70}}, {nID(59, 0x70>>3), []byte{}, 5, []byte{0x70}}, {nID(48, 0x0003), []byte{0x00}, 8, []byte{0x03}}, {nID(49, 0x0003>>1), []byte{0x00}, 7, []byte{0x02}}, } { t.Run(fmt.Sprintf("%v", tc.id), func(t *testing.T) { p, s := splitID(tc.id) if got, want := p, tc.outPrefix; !bytes.Equal(got, want) { t.Errorf("prefix %x, want %x", got, want) } if got, want := int(s.Bits()), tc.outSuffixBits; got != want { t.Errorf("suffix.Bits %v, want %v", got, want) } if got, want := s.Path(), tc.outSuffix; !bytes.Equal(got, want) { t.Errorf("suffix.Path %x, want %x", got, want) } }) } } func nID(level uint, index uint64) compact.NodeID { return compact.NewNodeID(level, index) } trillian-1.6.1/storage/cache/log_tile.go000066400000000000000000000126451466362047600202170ustar00rootroot00000000000000// 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 cache import ( "encoding/binary" "fmt" "github.com/google/trillian/storage/storagepb" "github.com/transparency-dev/merkle" "github.com/transparency-dev/merkle/compact" ) const ( // logStrataDepth is the strata that must be used for all log subtrees. logStrataDepth = 8 // maxLogDepth is the number of bits in a log path. maxLogDepth = 64 ) // PopulateLogTile re-creates a log tile's InternalNodes from the Leaves map. // // This uses the compact Merkle tree to repopulate internal nodes, and so will // handle imperfect (but left-hand dense) subtrees. Note that we only rebuild internal // nodes when the subtree is fully populated. For an explanation of why see the comments // below for prepareLogTile. // // TODO(pavelkalinnikov): Unexport it after the refactoring. func PopulateLogTile(st *storagepb.SubtreeProto, hasher merkle.LogHasher) error { if got, want := st.Depth, int32(logStrataDepth); got != want { return fmt.Errorf("invalid log tile depth %d, want %d", got, want) } // maxLeaves is the number of leaves in a fully populated tile. const maxLeaves = 1 << logStrataDepth // If the subtree is fully populated then the internal node map is expected to be nil but in // case it isn't we recreate it as we're about to rebuild the contents. We'll check // below that the number of nodes is what we expected to have. if st.InternalNodes == nil || len(st.Leaves) == maxLeaves { st.InternalNodes = make(map[string][]byte) } store := func(id compact.NodeID, hash []byte) { if id.Level == logStrataDepth && id.Index == 0 { // no space for the root in the node cache return } // Don't put leaves into the internal map and only update if we're rebuilding internal // nodes. If the subtree was saved with internal nodes then we don't touch the map. if id.Level > 0 && len(st.Leaves) == maxLeaves { st.InternalNodes[toSuffix(id)] = hash } } fact := compact.RangeFactory{Hash: hasher.HashChildren} cr := fact.NewEmptyRange(0) // We need to update the subtree root hash regardless of whether it's fully populated for leafIndex := uint64(0); leafIndex < uint64(len(st.Leaves)); leafIndex++ { sfxKey := toSuffix(compact.NewNodeID(0, leafIndex)) h := st.Leaves[sfxKey] if h == nil { return fmt.Errorf("unexpectedly got nil for subtree leaf suffix %s", sfxKey) } if size, expected := cr.End(), leafIndex; size != expected { return fmt.Errorf("got size of %d, but expected %d", size, expected) } if err := cr.Append(h, store); err != nil { return err } } // Additional check - after population we should have the same number of internal nodes // as before the subtree was written to storage. Either because they were loaded from // storage or just rebuilt above. if got, want := uint32(len(st.InternalNodes)), st.InternalNodeCount; got != want { // TODO(Martin2112): Possibly replace this with stronger checks on the data in // subtrees on disk so we can detect corruption. return fmt.Errorf("log repop got: %d internal nodes, want: %d", got, want) } return nil } // prepareLogTile prepares a log tile for writing. If it is fully populated the // internal nodes are cleared. Otherwise they are written. // // To see why this is necessary consider the case where a tree has a single full subtree // and then an additional leaf is added. // // This causes an extra level to be added to the tree with an internal node that is a hash // of the root of the left full subtree and the new leaf. Note that the leaves remain at // level zero in the overall tree coordinate space but they are now in a lower subtree stratum // than they were before the last node was added as the tree has grown above them. // // Thus in the case just discussed the internal nodes cannot be correctly reconstructed // in isolation when the tree is reloaded because of the dependency on another subtree. // // Fully populated subtrees don't have this problem because by definition they can only // contain internal nodes built from their own contents. func prepareLogTile(st *storagepb.SubtreeProto) error { st.InternalNodeCount = uint32(len(st.InternalNodes)) if st.Depth < 1 { return fmt.Errorf("prepare subtree for log write invalid depth: %d", st.Depth) } maxLeaves := 1 << uint(st.Depth) // If the subtree is fully populated we can safely clear the internal nodes if len(st.Leaves) == maxLeaves { st.InternalNodes = nil } return nil } func toSuffix(id compact.NodeID) string { depth := logStrataDepth - int(id.Level) var index [8]byte binary.BigEndian.PutUint64(index[:], id.Index<<(maxLogDepth-depth)) return newSuffix(uint8(depth), index[:]).String() } // newEmptyTile creates an empty log tile for the passed-in ID. func newEmptyTile(id []byte) *storagepb.SubtreeProto { return &storagepb.SubtreeProto{ Prefix: id, Depth: 8, Leaves: make(map[string][]byte), InternalNodes: make(map[string][]byte), } } trillian-1.6.1/storage/cache/mock_node_storage.go000066400000000000000000000030411466362047600220710ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/google/trillian/storage/cache (interfaces: NodeStorage) // Package cache is a generated GoMock package. package cache import ( reflect "reflect" gomock "github.com/golang/mock/gomock" storagepb "github.com/google/trillian/storage/storagepb" ) // MockNodeStorage is a mock of NodeStorage interface. type MockNodeStorage struct { ctrl *gomock.Controller recorder *MockNodeStorageMockRecorder } // MockNodeStorageMockRecorder is the mock recorder for MockNodeStorage. type MockNodeStorageMockRecorder struct { mock *MockNodeStorage } // NewMockNodeStorage creates a new mock instance. func NewMockNodeStorage(ctrl *gomock.Controller) *MockNodeStorage { mock := &MockNodeStorage{ctrl: ctrl} mock.recorder = &MockNodeStorageMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockNodeStorage) EXPECT() *MockNodeStorageMockRecorder { return m.recorder } // GetSubtree mocks base method. func (m *MockNodeStorage) GetSubtree(arg0 []byte) (*storagepb.SubtreeProto, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetSubtree", arg0) ret0, _ := ret[0].(*storagepb.SubtreeProto) ret1, _ := ret[1].(error) return ret0, ret1 } // GetSubtree indicates an expected call of GetSubtree. func (mr *MockNodeStorageMockRecorder) GetSubtree(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSubtree", reflect.TypeOf((*MockNodeStorage)(nil).GetSubtree), arg0) } trillian-1.6.1/storage/cache/subtree_cache.go000066400000000000000000000175621466362047600212200ustar00rootroot00000000000000// 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 cache import ( "bytes" "flag" "fmt" "sync" "github.com/google/trillian/storage/storagepb" "github.com/google/trillian/storage/tree" "github.com/transparency-dev/merkle" "github.com/transparency-dev/merkle/compact" "google.golang.org/protobuf/proto" "k8s.io/klog/v2" ) // TODO(al): move this up the stack var populateConcurrency = flag.Int("populate_subtree_concurrency", 256, "Max number of concurrent workers concurrently populating subtrees") // TODO(pavelkalinnikov): Rename subtrees to tiles. // GetSubtreesFunc describes a function which can return a number of Subtrees from storage. type GetSubtreesFunc func(ids [][]byte) ([]*storagepb.SubtreeProto, error) // SubtreeCache provides a caching access to Subtree storage. Currently there are assumptions // in the code that all subtrees are multiple of 8 in depth and that log subtrees are always // of depth 8. It is not possible to just change the constants above and have things still // work. This is because of issues like byte packing of node IDs. // // SubtreeCache is not thread-safe: GetNodes, SetNodes and Flush methods must // be called sequentially. type SubtreeCache struct { hasher merkle.LogHasher // subtrees contains the Subtree data read from storage, and is updated by // calls to SetNodes. subtrees map[string]*storagepb.SubtreeProto // dirtyPrefixes keeps track of all Subtrees which need to be written back // to storage. dirtyPrefixes map[string]bool // populateConcurrency sets the amount of concurrency when repopulating subtrees. populateConcurrency int } // NewLogSubtreeCache creates and returns a SubtreeCache appropriate for use with a log // tree. The caller must supply a suitable LogHasher. func NewLogSubtreeCache(hasher merkle.LogHasher) *SubtreeCache { if *populateConcurrency <= 0 { panic(fmt.Errorf("populate_subtree_concurrency must be set to >= 1")) } return &SubtreeCache{ hasher: hasher, subtrees: make(map[string]*storagepb.SubtreeProto), dirtyPrefixes: make(map[string]bool), populateConcurrency: *populateConcurrency, } } // preload calculates the set of subtrees required to know the hashes of the // passed in node IDs, uses getSubtrees to retrieve them, and finally populates // the cache structures with the data. Returns the list of tile IDs not found. func (s *SubtreeCache) preload(ids []compact.NodeID, getSubtrees GetSubtreesFunc) ([]string, error) { // Figure out the set of subtrees we need. want := make(map[string]bool) for _, id := range ids { subID := string(getTileID(id)) if _, ok := s.subtrees[subID]; !ok { want[subID] = true } } // Don't make a read request for zero subtrees. if len(want) == 0 { return nil, nil } list := make([][]byte, 0, len(want)) for id := range want { list = append(list, []byte(id)) } subtrees, err := getSubtrees(list) if err != nil { return nil, err } if got, max := len(subtrees), len(want); got > max { return nil, fmt.Errorf("too many subtrees: %d, want <= %d", got, max) } ch := make(chan *storagepb.SubtreeProto, len(subtrees)) workTokens := make(chan bool, s.populateConcurrency) for i := 0; i < s.populateConcurrency; i++ { workTokens <- true } wg := &sync.WaitGroup{} for _, t := range subtrees { t := t wg.Add(1) go func() { defer wg.Done() // wait for a token before starting work <-workTokens // return it when done defer func() { workTokens <- true }() if err := PopulateLogTile(t, s.hasher); err != nil { // TODO(mhutchinson): This error should be propagated. klog.Errorf("PopulateLogTile(): %v", err) } ch <- t // Note: This never blocks because len(ch) == len(subtrees). }() } go func() { wg.Wait() close(ch) close(workTokens) }() for t := range ch { if err := s.cacheSubtree(t); err != nil { return nil, err } delete(want, string(t.Prefix)) } notFound := make([]string, 0, len(want)) for id := range want { notFound = append(notFound, id) } return notFound, nil } func (s *SubtreeCache) cacheSubtree(t *storagepb.SubtreeProto) error { if subtree, ok := s.subtrees[string(t.Prefix)]; ok { if !proto.Equal(t, subtree) { return fmt.Errorf("at %x: subtree mismatch", t.Prefix) } return nil } s.subtrees[string(t.Prefix)] = t return nil } // GetNodes returns the requested nodes, calling the getSubtrees function if // they are not already cached. func (s *SubtreeCache) GetNodes(ids []compact.NodeID, getSubtrees GetSubtreesFunc) ([]tree.Node, error) { if notFound, err := s.preload(ids, getSubtrees); err != nil { return nil, err } else if r := len(notFound); r != 0 { return nil, fmt.Errorf("preload did not get all tiles: %d not found", r) } ret := make([]tree.Node, 0, len(ids)) for _, id := range ids { if h, err := s.getNodeHash(id); err != nil { return nil, fmt.Errorf("getNodeHash(%+v): %v", id, err) } else if h != nil { ret = append(ret, tree.Node{ID: id, Hash: h}) } } return ret, nil } // getNodeHash returns a single node hash from the cache. func (s *SubtreeCache) getNodeHash(id compact.NodeID) ([]byte, error) { subID, sx := splitID(id) c := s.subtrees[string(subID)] if c == nil { return nil, fmt.Errorf("tile %x not found", subID) } // Look up the hash in the appropriate map. // The leaf hashes are stored in a separate map to the internal nodes so that // we can easily dump (and later reconstruct) the internal nodes. As log subtrees // have a fixed depth if the suffix has the same number of significant bits as the // subtree depth then this is a leaf. For example if the subtree is depth 8 its leaves // have 8 significant suffix bits. if int32(sx.Bits()) == c.Depth { return c.Leaves[sx.String()], nil } return c.InternalNodes[sx.String()], nil } // SetNodes sets hashes for the given nodes in the cache. func (s *SubtreeCache) SetNodes(nodes []tree.Node, getSubtrees GetSubtreesFunc) error { ids := make([]compact.NodeID, len(nodes)) for i, n := range nodes { ids[i] = n.ID } notFound, err := s.preload(ids, getSubtrees) if err != nil { return err } for _, id := range notFound { s.subtrees[id] = newEmptyTile([]byte(id)) } for _, n := range nodes { subID, sx := splitID(n.ID) c := s.subtrees[string(subID)] if c == nil { return fmt.Errorf("tile %x not found", subID) } // Store the hash to the containing tile, and mark it as dirty if the hash // differs from the previously stored one. sfxKey := sx.String() if int32(sx.Bits()) == c.Depth { // This is a leaf node. if !bytes.Equal(c.Leaves[sfxKey], n.Hash) { c.Leaves[sfxKey] = n.Hash s.dirtyPrefixes[string(subID)] = true } } else { // This is an internal node. if !bytes.Equal(c.InternalNodes[sfxKey], n.Hash) { c.InternalNodes[sfxKey] = n.Hash s.dirtyPrefixes[string(subID)] = true } } } return nil } // UpdatedTiles returns all updated tiles that need to be written to storage. func (s *SubtreeCache) UpdatedTiles() ([]*storagepb.SubtreeProto, error) { var toWrite []*storagepb.SubtreeProto for k, v := range s.subtrees { if !s.dirtyPrefixes[k] { continue } if !bytes.Equal([]byte(k), v.Prefix) { return nil, fmt.Errorf("inconsistent cache: prefix key is %v, but cached object claims %v", k, v.Prefix) } if len(v.Leaves) > 0 { if err := prepareLogTile(v); err != nil { return nil, err } toWrite = append(toWrite, v) } } return toWrite, nil } trillian-1.6.1/storage/cache/subtree_cache_test.go000066400000000000000000000234551466362047600222550ustar00rootroot00000000000000// 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 cache import ( "encoding/binary" "fmt" "testing" "github.com/google/go-cmp/cmp" "github.com/google/trillian/storage/storagepb" "github.com/google/trillian/storage/tree" "github.com/transparency-dev/merkle/compact" "github.com/transparency-dev/merkle/rfc6962" "github.com/golang/mock/gomock" ) func ancestor(id compact.NodeID, levelsUp uint) compact.NodeID { return compact.NewNodeID(id.Level+levelsUp, id.Index>>levelsUp) } func toPrefix(t *testing.T, id compact.NodeID) []byte { t.Helper() if level := id.Level; level%8 != 0 || level > 64 { t.Fatalf("node %+v is not aligned", id) } var bytes [8]byte binary.BigEndian.PutUint64(bytes[:], id.Index< (%d leaves)", s.Prefix, len(s.Leaves)) } } // Write nodes. var nodes []tree.Node for id := id; id.Level < 64; id = ancestor(id, 1) { nodes = append(nodes, tree.Node{ID: id, Hash: []byte(fmt.Sprintf("hash-%v", id))}) } if err := c.SetNodes(nodes, getSubtrees(m)); err != nil { t.Fatalf("SetNodes: %v", err) } tiles, err := c.UpdatedTiles() if err != nil { t.Fatalf("failed to get updated tiles: %v", err) } store(tiles) for k, v := range expectedSetIDs { switch v { case "expected": t.Errorf("Subtree %x remains unset", k) case "met": // default: t.Errorf("Unknown state for subtree %x: %s", k, v) } } } func TestRepopulateLogSubtree(t *testing.T) { fact := compact.RangeFactory{Hash: rfc6962.DefaultHasher.HashChildren} cr := fact.NewEmptyRange(0) cmtStorage := storagepb.SubtreeProto{ Leaves: make(map[string][]byte), InternalNodes: make(map[string][]byte), Depth: 8, } s := storagepb.SubtreeProto{ Leaves: make(map[string][]byte), Depth: 8, } for numLeaves := int64(1); numLeaves <= 256; numLeaves++ { // clear internal nodes s.InternalNodes = make(map[string][]byte) leaf := []byte(fmt.Sprintf("this is leaf %d", numLeaves)) leafHash := rfc6962.DefaultHasher.HashLeaf(leaf) store := func(id compact.NodeID, hash []byte) { // Don't store leaves or the subtree root in InternalNodes if id.Level > 0 && id.Level < 8 { _, sfx := splitID(id) cmtStorage.InternalNodes[sfx.String()] = hash } } if err := cr.Append(leafHash, store); err != nil { t.Fatalf("merkle tree update failed: %v", err) } sfxKey := toSuffix(compact.NewNodeID(0, uint64(numLeaves)-1)) s.Leaves[sfxKey] = leafHash if numLeaves == 256 { s.InternalNodeCount = uint32(len(cmtStorage.InternalNodes)) } else { s.InternalNodeCount = 0 } cmtStorage.Leaves[sfxKey] = leafHash if err := PopulateLogTile(&s, rfc6962.DefaultHasher); err != nil { t.Fatalf("failed populating tile: %v", err) } // Repopulation should only have happened with a full subtree, otherwise the internal nodes map // should be empty if numLeaves != 256 { if len(s.InternalNodes) != 0 { t.Fatalf("(it %d) internal nodes should be empty but got: %v", numLeaves, s.InternalNodes) } } else if diff := cmp.Diff(cmtStorage.InternalNodes, s.InternalNodes); diff != "" { t.Fatalf("(it %d) CMT/sparse internal nodes diff:\n%v", numLeaves, diff) } } } func BenchmarkRepopulateLogSubtree(b *testing.B) { hasher := rfc6962.DefaultHasher s := storagepb.SubtreeProto{ Leaves: make(map[string][]byte), Depth: 8, InternalNodeCount: 254, } for i := 0; i < 256; i++ { leaf := []byte(fmt.Sprintf("leaf %d", i)) hash := hasher.HashLeaf(leaf) s.Leaves[toSuffix(compact.NewNodeID(0, uint64(i)))] = hash } for n := 0; n < b.N; n++ { if err := PopulateLogTile(&s, hasher); err != nil { b.Fatalf("failed populating tile: %v", err) } } } func TestIdempotentWrites(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() m := NewMockNodeStorage(mockCtrl) // Note: The ID must end with a zero byte, to be the first leaf in the tile. id := compact.NewNodeID(24, 0x12300) subtreePrefix := toPrefix(t, ancestor(id, 8)) expectedSetIDs := make(map[string]string) expectedSetIDs[string(subtreePrefix)] = "expected" // The first time we read the subtree we'll emulate an empty subtree: m.EXPECT().GetSubtree(subtreePrefix).Do(func(id []byte) { t.Logf("read %x", id) }).Return((*storagepb.SubtreeProto)(nil), nil) store := func(trees []*storagepb.SubtreeProto) { for _, s := range trees { state, ok := expectedSetIDs[string(s.Prefix)] if !ok { t.Errorf("Unexpected write to subtree %x", s.Prefix) } switch state { case "expected": expectedSetIDs[string(s.Prefix)] = "met" case "met": t.Errorf("Second write to subtree %x", s.Prefix) default: t.Errorf("Unknown state for subtree %x: %s", s.Prefix, state) } // After this write completes, subsequent reads will see the subtree // being written now: m.EXPECT().GetSubtree(s.Prefix).AnyTimes().Do(func(id []byte) { t.Logf("read again %x", id) }).Return(s, nil) t.Logf("write %x -> %#v", s.Prefix, s) } } // Now write the same value to the same node multiple times. // We should see many reads, but only the first call to SetNodeHash should // result in an actual write being flushed through to storage. for i := 0; i < 10; i++ { c := NewLogSubtreeCache(rfc6962.DefaultHasher) nodes := []tree.Node{{ID: id, Hash: []byte("noodled")}} if err := c.SetNodes(nodes, getSubtrees(m)); err != nil { t.Fatalf("%d: failed to set node hash: %v", i, err) } tiles, err := c.UpdatedTiles() if err != nil { t.Fatalf("%d: failed to get updated tiles: %v", i, err) } if i > 0 && len(tiles) > 0 { t.Fatalf("unexpected updated tiles on write attempt %d", i) } store(tiles) } for k, v := range expectedSetIDs { switch v { case "expected": t.Errorf("Subtree %x remains unset", k) case "met": // default: t.Errorf("Unknown state for subtree %x: %s", k, v) } } } trillian-1.6.1/storage/cache/suffix.go000066400000000000000000000111761466362047600177230ustar00rootroot00000000000000// 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 cache import ( "encoding/base64" "errors" "fmt" ) // key is used to look up variable length suffix values in the map. type key struct { // The depth of this entry in bits (must be > 0). depth uint8 // The value of the path at this depth. value byte } var ( // emptySuffix is a reusable suffix of zero bits. To avoid special cases // there is a single byte path attached to it and there is no way to create // a suffix with a nil or empty path. emptySuffix = newSuffix(0, []byte{0}) // fromRaw maps a bit length and single byte path to a suffix. fromRaw = make(map[key]*suffix) // fromString maps a base64 encoded string representation to a suffix. fromString = make(map[string]*suffix) ) // suffix represents the tail of a NodeID. It is the path within the subtree. // The portion of the path that extends beyond the subtree is not part of this suffix. // We keep a cache of the suffix values use by log trees, which will have a // depth between 1 and 8 bits. These are reused to avoid constant reallocation // and base64 conversion overhead. // // TODO(pavelkalinnikov, v2): This type is specific to SubtreeProto. Move it. type suffix struct { // bits is the number of bits in the node ID suffix. bits uint8 // path is the suffix itself. path []byte // asString is the string representation of the suffix. asString string } // newSuffix creates a new suffix. The primary use for them is to get their // String value to use as a key so we compute that once up front. // // TODO(pavelkalinnikov): Mask the last byte of path. func newSuffix(bits uint8, path []byte) *suffix { // Use a shared value for a short suffix if we have one, they're immutable. if bits <= 8 { if sfx, ok := fromRaw[key{depth: bits, value: path[0]}]; ok { return sfx } } r := make([]byte, 1, len(path)+1) r[0] = bits r = append(r, path...) s := base64.StdEncoding.EncodeToString(r) return &suffix{bits: bits, path: r[1:], asString: s} } // Bits returns the number of significant bits in the suffix path. func (s suffix) Bits() uint8 { return s.bits } // Path returns a copy of the suffix path. func (s suffix) Path() []byte { return append(make([]byte, 0, len(s.path)), s.path...) } // String returns a string that represents suffix. // This is a base64 encoding of the following format: // [ 1 byte for depth || path bytes ] func (s suffix) String() string { return s.asString } // parseSuffix converts a suffix string back into a suffix. func parseSuffix(s string) (*suffix, error) { if sfx, ok := fromString[s]; ok { // Matches a precalculated value, use that. return sfx, nil } b, err := base64.StdEncoding.DecodeString(s) if err != nil { return nil, err } if len(b) == 0 { return nil, errors.New("empty bytes") } bits, b := b[0], b[1:] if got, want := len(b), bytesForBits(int(bits)); got != want { return nil, fmt.Errorf("unexpected length %d, need %d", got, want) } return newSuffix(bits, b), nil } // bytesForBits returns the number of bytes required to store numBits bits. func bytesForBits(numBits int) int { return (numBits + 7) >> 3 } // Precalculate all the one byte suffix values (from depths 1-8) so they can be // reused either on construction or parsing. func init() { path := make([]byte, 1) // There are 8 levels of depth to process. for d := 8; d >= 1; d-- { // And at each depth there's 2^d valid combinations of bits. E.g at // depth 1 there's one valid bit so two possibilities. for i := 0; i < 1< 0 { tree.DeleteTime = timestamppb.New(time.Unix(0, info.DeleteTimeNanos)) if err := tree.DeleteTime.CheckValid(); err != nil { return nil, status.Errorf(codes.Internal, "failed to convert delete time: %v", err) } } return tree, nil } // unmarshalSettings returns the message obtained from tree.StorageSettings. // If tree.StorageSettings is nil no unmarshaling will be attempted; instead the method will return // (nil, nil). func unmarshalSettings(tree *trillian.Tree) (proto.Message, error) { settings := tree.GetStorageSettings() if settings == nil { return nil, nil } any, err := settings.UnmarshalNew() if err != nil { return nil, err } return any, nil } func validateLogStorageConfig(config *spannerpb.LogStorageConfig) error { if config.NumUnseqBuckets < 1 { return status.Errorf(codes.InvalidArgument, "NumUnseqBuckets = %v, want > 0", config.NumUnseqBuckets) } if config.NumMerkleBuckets < 1 || config.NumMerkleBuckets > 256 { return status.Errorf(codes.InvalidArgument, "NumMerkleBuckets = %v, want a number in range [1, 256]", config.NumMerkleBuckets) } return nil } trillian-1.6.1/storage/cloudspanner/doc.go000066400000000000000000000013101466362047600206030ustar00rootroot00000000000000// Copyright 2022 Trillian Authors. 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 cloudspanner contains the Cloud Spanner storage implementation. package cloudspanner trillian-1.6.1/storage/cloudspanner/gen.sh000077500000000000000000000004701466362047600206250ustar00rootroot00000000000000#! /bin/sh cat > spanner.sdl.go <> spanner.sdl.go else base64 -w 76 < spanner.sdl >> spanner.sdl.go fi echo '`' >> spanner.sdl.go trillian-1.6.1/storage/cloudspanner/getdb_test.go000066400000000000000000000105561466362047600221760ustar00rootroot00000000000000// 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 cloudspanner import ( "context" "flag" "fmt" "os" "path" "strings" "sync" "testing" "time" "cloud.google.com/go/spanner" "cloud.google.com/go/spanner/spannertest" "google.golang.org/api/option" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" database "cloud.google.com/go/spanner/admin/database/apiv1" "cloud.google.com/go/spanner/admin/database/apiv1/databasepb" ) // To run cloudspanner tests, // 1) Set the -test_cloud_spanner_database flag // 2) Set application default credentials `gcloud auth application-default login` var cloudDBPath = flag.String("test_cloud_spanner_database", ":memory:", "eg: projects/my-project/instances/my-instance/database/my-db") func GetTestDB(ctx context.Context, t *testing.T) *spanner.Client { t.Helper() switch dbPath := *cloudDBPath; dbPath { case "": t.Skip("-test_cloud_spanner_database flag is unset") return nil case ":memory:": ddl, err := readDDL() if err != nil { t.Fatal(err) } return inMemClient(ctx, t, uniqueDBName("fakeProject", "fakeInstance"), ddl) default: client, err := spanner.NewClient(ctx, *cloudDBPath) if err != nil { t.Fatalf("spanner.NewClient(): %v", err) } return client } } var dbCount struct { sync.Mutex count int } func uniqueDBName(project, instance string) string { // Unique per test binary invocation timestamp := time.Now().UTC().Format("Jan-02-15-04-05") testBinary := strings.ToLower(strings.Replace(path.Base(os.Args[0]), ".test", "", 1)) invocationID := fmt.Sprintf("%s-%s", timestamp, testBinary) dbCount.Lock() defer dbCount.Unlock() dbCount.count++ database := fmt.Sprintf("%s-%d", invocationID, dbCount.count) return fmt.Sprintf("projects/%s/instances/%s/databases/%s", project, instance, database) } func inMemClient(ctx context.Context, t testing.TB, dbName string, statements []string) *spanner.Client { t.Helper() // Don't use SPANNER_EMULATOR_HOST because we need the raw connection for // the database admin client anyway. t.Logf("Using in-memory fake Spanner DB: %s", dbName) srv, err := spannertest.NewServer("localhost:0") if err != nil { t.Fatalf("Starting in-memory fake: %v", err) } t.Cleanup(srv.Close) srv.SetLogger(t.Logf) dialCtx, cancel := context.WithTimeout(ctx, 1*time.Second) defer cancel() conn, err := grpc.DialContext(dialCtx, srv.Addr, grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { t.Fatalf("Dialing in-memory fake: %v", err) } client, err := spanner.NewClient(ctx, dbName, option.WithGRPCConn(conn)) if err != nil { defer func() { if err := conn.Close(); err != nil { t.Errorf("conn.Close(): %v", err) } }() t.Fatalf("Connecting to in-memory fake: %v", err) } t.Cleanup(client.Close) // Set database schema t.Logf("DDL update: %q", statements) adminClient, err := database.NewDatabaseAdminClient(ctx, option.WithGRPCConn(conn)) if err != nil { t.Fatalf("Connecting to in-memory fake DB admin: %v", err) } op, err := adminClient.UpdateDatabaseDdl(ctx, &databasepb.UpdateDatabaseDdlRequest{ Database: dbName, Statements: statements, }) if err != nil { t.Fatalf("Starting DDL update: %v", err) } if err := op.Wait(ctx); err != nil { t.Fatalf("failed DDL update: %v", err) } return client } func cleanTestDB(ctx context.Context, t *testing.T, db *spanner.Client) { if _, err := db.ReadWriteTransaction(ctx, func(ctx context.Context, txn *spanner.ReadWriteTransaction) error { mutations := make([]*spanner.Mutation, 0) for _, table := range []string{ "TreeRoots", "TreeHeads", "SubtreeData", "LeafData", "SequencedLeafData", "Unsequenced", } { mutations = append(mutations, spanner.Delete(table, spanner.AllKeys())) } return txn.BufferWrite(mutations) }); err != nil { t.Fatalf("Failed to clean databse: %v", err) } } trillian-1.6.1/storage/cloudspanner/log_storage.go000066400000000000000000000765001466362047600223600ustar00rootroot00000000000000// Copyright 2018 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 cloudspanner import ( "bytes" "context" "errors" "fmt" "math" "math/rand" "sort" "sync" "time" "cloud.google.com/go/spanner" "github.com/google/trillian" "github.com/google/trillian/storage" "github.com/google/trillian/storage/cache" "github.com/google/trillian/storage/cloudspanner/spannerpb" "github.com/google/trillian/types" "github.com/transparency-dev/merkle/rfc6962" "go.opencensus.io/trace" "golang.org/x/sync/semaphore" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/timestamppb" "k8s.io/klog/v2" ) const ( leafDataTbl = "LeafData" seqDataByMerkleHashIdx = "SequenceByMerkleHash" seqDataTbl = "SequencedLeafData" unseqTable = "Unsequenced" // t.TreeType: 1 = Log, 3 = PreorderedLog. // t.TreeState: 1 = Active, 5 = Draining. getActiveLogIDsSQL = `SELECT t.TreeID FROM TreeRoots t WHERE (t.TreeType = 1 OR t.TreeType = 3) AND (t.TreeState = 1 OR t.TreeState = 5) AND t.Deleted=false` ) // LogStorageOptions are tuning, experiments and workarounds that can be used. type LogStorageOptions struct { TreeStorageOptions // DequeueAcrossMerkleBuckets controls whether DequeueLeaves will only dequeue // from within the chosen Time+Merkle bucket, or whether it will attempt to // continue reading from contiguous Merkle buckets until a sufficient number // of leaves have been dequeued, or the entire Time bucket has been read. DequeueAcrossMerkleBuckets bool // DequeueAcrossMerkleBucketsRangeFraction specifies the fraction of Merkle // keyspace to dequeue from when using multi-bucket-dequeue. DequeueAcrossMerkleBucketsRangeFraction float64 } var ( // Spanner DB columns: colLeafIdentityHash = "LeafIdentityHash" colLeafValue = "LeafValue" colExtraData = "ExtraData" colMerkleLeafHash = "MerkleLeafHash" colSequenceNumber = "SequenceNumber" colQueueTimestampNanos = "QueueTimestampNanos" ) type leafDataCols struct { TreeID int64 LeafIdentityHash []byte LeafValue []byte ExtraData []byte QueueTimestampNanos int64 } type sequencedLeafDataCols struct { TreeID int64 SequenceNumber int64 LeafIdentityHash []byte MerkleLeafHash []byte IntegrateTimestampNanos int64 } type unsequencedCols struct { TreeID int64 Bucket int64 QueueTimestampNanos int64 MerkleLeafHash []byte LeafIdentityHash []byte } // NewLogStorage initialises and returns a new LogStorage. func NewLogStorage(client *spanner.Client) storage.LogStorage { return NewLogStorageWithOpts(client, LogStorageOptions{}) } // NewLogStorageWithOpts initialises and returns a new LogStorage. // The opts parameter can be used to enable custom workarounds. func NewLogStorageWithOpts(client *spanner.Client, opts LogStorageOptions) storage.LogStorage { if got := opts.DequeueAcrossMerkleBucketsRangeFraction; got <= 0 || got > 1.0 { opts.DequeueAcrossMerkleBucketsRangeFraction = 1.0 } return &logStorage{ ts: newTreeStorageWithOpts(client, opts.TreeStorageOptions), // This number is taken from the maximum number of in-flight // transaction in the mutation pool. Add a field to opts if we decide to // adopt this strategy. writeSem: semaphore.NewWeighted(128), opts: opts, } } // logStorage provides a Cloud Spanner backed trillian.LogStorage implementation. // See third_party/golang/trillian/storage/log_storage.go for more details. type logStorage struct { // ts provides the merkle-tree level primitives which are built upon by this // logStorage. ts *treeStorage // writeSem controls how many concurrent writes QueueLeaves/AddSequencedLeaves will do. writeSem *semaphore.Weighted // Additional options applied to this logStorage opts LogStorageOptions } func (ls *logStorage) CheckDatabaseAccessible(ctx context.Context) error { return checkDatabaseAccessible(ctx, ls.ts.client) } func (ls *logStorage) readOnlyTX() *spanner.ReadOnlyTransaction { var staleness spanner.TimestampBound if ls.opts.ReadOnlyStaleness > 0 { staleness = spanner.ExactStaleness(ls.opts.ReadOnlyStaleness) } else { staleness = spanner.StrongRead() } return ls.ts.client.ReadOnlyTransaction().WithTimestampBound(staleness) } func (ls *logStorage) GetActiveLogIDs(ctx context.Context) ([]int64, error) { ids := []int64{} // We have to use SQL as Read() doesn't work against an index. stmt := spanner.NewStatement(getActiveLogIDsSQL) rows := ls.readOnlyTX().Query(ctx, stmt) if err := rows.Do(func(r *spanner.Row) error { var id int64 if err := r.Columns(&id); err != nil { return err } ids = append(ids, id) return nil }); err != nil { klog.Warningf("GetActiveLogIDs: %v", err) return nil, fmt.Errorf("problem executing getActiveLogIDsSQL: %v", err) } return ids, nil } func newLogCache(tree *trillian.Tree) (*cache.SubtreeCache, error) { return cache.NewLogSubtreeCache(rfc6962.DefaultHasher), nil } func (ls *logStorage) begin(ctx context.Context, tree *trillian.Tree, readonly bool, stx spanRead) (*logTX, error) { tx, err := ls.ts.begin(ctx, tree, newLogCache, stx) if err != nil { return nil, err } ltx := &logTX{ ls: ls, dequeued: make(map[string]*QueuedEntry), treeTX: tx, } // Needed to generate ErrTreeNeedsInit in SnapshotForTree and other methods. if err := ltx.getLatestRoot(ctx); err == storage.ErrTreeNeedsInit { return ltx, err } else if err != nil { defer func() { if err := tx.Close(); err != nil { klog.Errorf("conn.Close(): %v", err) } }() return nil, err } return ltx, nil } func (ls *logStorage) BeginForTree(ctx context.Context, treeID int64) (storage.LogTreeTX, error) { return nil, ErrNotImplemented } func (ls *logStorage) ReadWriteTransaction(ctx context.Context, tree *trillian.Tree, f storage.LogTXFunc) error { _, err := ls.ts.client.ReadWriteTransaction(ctx, func(ctx context.Context, stx *spanner.ReadWriteTransaction) error { tx, err := ls.begin(ctx, tree, false /* readonly */, stx) if err != nil && err != storage.ErrTreeNeedsInit { return err } if err := f(ctx, tx); err != nil { return err } return tx.flushSubtrees(ctx) }) return err } func (ls *logStorage) SnapshotForTree(ctx context.Context, tree *trillian.Tree) (storage.ReadOnlyLogTreeTX, error) { return ls.begin(ctx, tree, true /* readonly */, ls.ts.client.ReadOnlyTransaction()) } func (ls *logStorage) QueueLeaves(ctx context.Context, tree *trillian.Tree, leaves []*trillian.LogLeaf, qTimestamp time.Time) ([]*trillian.QueuedLogLeaf, error) { _, treeConfig, err := ls.ts.getTreeAndConfig(ctx, tree) if err != nil { return nil, err } config, ok := treeConfig.(*spannerpb.LogStorageConfig) if !ok { return nil, status.Errorf(codes.Internal, "got unexpected config type for Log operation: %T", treeConfig) } now := time.Now().UTC().Unix() bucketPrefix := (now % config.NumUnseqBuckets) << 8 results := make([]*trillian.QueuedLogLeaf, len(leaves)) writeDupes := make(map[string][]int) qTS := qTimestamp.UnixNano() var wg sync.WaitGroup for i, l := range leaves { wg.Add(1) // Capture values of i and l for later reference in the MutationResultFunc below. i := i l := l go func() { defer wg.Done() // The insert of the leafdata and the unsequenced work item must happen atomically. m1, err := spanner.InsertStruct(leafDataTbl, leafDataCols{ TreeID: tree.TreeId, LeafIdentityHash: l.LeafIdentityHash, LeafValue: l.LeafValue, ExtraData: l.ExtraData, QueueTimestampNanos: qTS, }) if err != nil { results[i] = &trillian.QueuedLogLeaf{Status: status.Convert(err).Proto()} return } b := bucketPrefix | int64(l.MerkleLeafHash[0]) m2, err := spanner.InsertStruct(unseqTable, unsequencedCols{ TreeID: tree.TreeId, Bucket: b, QueueTimestampNanos: qTS, MerkleLeafHash: l.MerkleLeafHash, LeafIdentityHash: l.LeafIdentityHash, }) if err != nil { results[i] = &trillian.QueuedLogLeaf{Status: status.Convert(err).Proto()} return } _, err = ls.ts.client.Apply(ctx, []*spanner.Mutation{m1, m2}) if spanner.ErrCode(err) == codes.AlreadyExists { k := string(l.LeafIdentityHash) writeDupes[k] = append(writeDupes[k], i) } else if err != nil { results[i] = &trillian.QueuedLogLeaf{Status: status.Convert(err).Proto()} } else { results[i] = &trillian.QueuedLogLeaf{Leaf: l} // implicit OK status } }() } // Wait for all of our mutations to apply (or fail): wg.Wait() // Finally, read back any leaves which failed with an already exists error // when we tried to insert them: err = ls.readDupeLeaves(ctx, tree.TreeId, writeDupes, results) if err != nil { return nil, err } return results, nil } func (ls *logStorage) AddSequencedLeaves(ctx context.Context, tree *trillian.Tree, leaves []*trillian.LogLeaf, ts time.Time) ([]*trillian.QueuedLogLeaf, error) { ctx, span := trace.StartSpan(ctx, "AddSequencedLeaves") defer span.End() okProto := status.New(codes.OK, "OK").Proto() _, span = trace.StartSpan(ctx, "insert") defer span.End() res := make([]*trillian.QueuedLogLeaf, len(leaves)) errs := make(chan error, 1) var wg sync.WaitGroup for i, l := range leaves { l.QueueTimestamp = timestamppb.New(ts) if err := l.QueueTimestamp.CheckValid(); err != nil { return nil, fmt.Errorf("got invalid queue timestamp: %w", err) } // Capture the values for later reference in the MutationResultFunc below. i, l := i, l res[i] = &trillian.QueuedLogLeaf{Status: okProto} wg.Add(1) var err error // The insert of the LeafData and SequencedLeafData must happen atomically. m1, err := spanner.InsertStruct(leafDataTbl, leafDataCols{ TreeID: tree.TreeId, LeafIdentityHash: l.LeafIdentityHash, LeafValue: l.LeafValue, ExtraData: l.ExtraData, QueueTimestampNanos: ts.UnixNano(), }) if err != nil { return nil, err } m2, err := spanner.InsertStruct(seqDataTbl, sequencedLeafDataCols{ TreeID: tree.TreeId, SequenceNumber: l.LeafIndex, LeafIdentityHash: l.LeafIdentityHash, MerkleLeafHash: l.MerkleLeafHash, IntegrateTimestampNanos: 0, }) if err != nil { return nil, err } m := []*spanner.Mutation{m1, m2} doneFunc := func(err error) { defer wg.Done() if err != nil { // If failed because of a duplicate insert, set the status correspondingly. if status.Code(err) == codes.AlreadyExists { klog.Infof("Found already exists: index=%v, id=%v", l.LeafIndex, l.LeafIdentityHash) res[i].Status = status.New(codes.FailedPrecondition, "conflicting LeafIndex or LeafIdentityHash").Proto() return } select { case errs <- err: default: // Skip this error, we only need one. } } } if err := ls.writeSem.Acquire(ctx, 1); err != nil { doneFunc(err) } else { go func() { defer ls.writeSem.Release(1) doneFunc(func() error { _, err := ls.ts.client.Apply(ctx, m) return err }()) }() } } span.End() // Wait for all of our mutations to apply (or fail). _, span = trace.StartSpan(ctx, "wait") wg.Wait() span.End() // Check if any failed, and return the first error if so. select { case err := <-errs: return nil, err default: // No error. } return res, nil } // readDupeLeaves reads the leaves whose ids are passed as keys in the dupes map, // and stores them in results. func (ls *logStorage) readDupeLeaves(ctx context.Context, logID int64, dupes map[string][]int, results []*trillian.QueuedLogLeaf) error { numDupes := len(dupes) if numDupes == 0 { return nil } klog.V(2).Infof("dupe rowsToRead: %v", numDupes) ids := make([][]byte, 0, numDupes) for k := range dupes { ids = append(ids, []byte(k)) } dupesRead := 0 tx := ls.ts.client.Single() err := readLeaves(ctx, tx, logID, ids, func(l *trillian.LogLeaf) { klog.V(2).Infof("Found already exists dupe: %v", l) dupesRead++ indices := dupes[string(l.LeafIdentityHash)] klog.V(2).Infof("Indices %v", indices) if len(indices) == 0 { klog.Warningf("Logic error: Spanner returned a leaf %x, but it matched no requested index", l.LeafIdentityHash) return } for _, i := range indices { leaf := l results[i] = &trillian.QueuedLogLeaf{ Leaf: leaf, Status: status.Newf(codes.AlreadyExists, "leaf already exists: %v", l.LeafIdentityHash).Proto(), } } }) tx.Close() if err != nil { return err } if got, want := dupesRead, numDupes; got != want { return fmt.Errorf("read unexpected number of dupe rows %d, want %d", got, want) } return nil } // logTX is a concrete implementation of the Trillian storage.LogStorage // interface. type logTX struct { // treeTX embeds the merkle-tree level transactional actions. *treeTX // logStorage is the logStorage which begat this logTX. ls *logStorage // numSequenced holds the number of leaves sequenced by this transaction. numSequenced int64 // dequeued is a map of LeafIdentityHash to QueuedEntry containing entries for // everything dequeued by this transaction. // This is required to recover the primary key for the unsequenced entry in // UpdateSequencedLeaves. dequeued map[string]*QueuedEntry } func (tx *logTX) getLogStorageConfig() *spannerpb.LogStorageConfig { return tx.config.(*spannerpb.LogStorageConfig) } // LatestSignedLogRoot returns the freshest SignedLogRoot for this log at the // time the transaction was started. func (tx *logTX) LatestSignedLogRoot(ctx context.Context) (*trillian.SignedLogRoot, error) { currentSTH, err := tx.currentSTH(ctx) if err != nil { return nil, err } writeRev, err := tx.writeRev(ctx) if err != nil { return nil, err } if got, want := currentSTH.TreeRevision+1, writeRev; got != want { return nil, fmt.Errorf("inconsistency: currentSTH.TreeRevision+1 (%d) != writeRev (%d)", got, want) } // Put logRoot back together. Fortunately LogRoot has a deterministic serialization. logRoot, err := (&types.LogRootV1{ TimestampNanos: uint64(currentSTH.TsNanos), RootHash: currentSTH.RootHash, TreeSize: uint64(currentSTH.TreeSize), Metadata: currentSTH.Metadata, }).MarshalBinary() if err != nil { return nil, err } // We already read the latest root as part of starting the transaction (in // order to calculate the writeRevision), so we just return that data here: return &trillian.SignedLogRoot{LogRoot: logRoot}, nil } // StoreSignedLogRoot stores the provided root. // This method will return an error if the caller attempts to store more than // one root per log for a given tree size. func (tx *logTX) StoreSignedLogRoot(ctx context.Context, root *trillian.SignedLogRoot) error { writeRev, err := tx.writeRev(ctx) if err == storage.ErrTreeNeedsInit { writeRev = 0 } else if err != nil { return err } var logRoot types.LogRootV1 if err := logRoot.UnmarshalBinary(root.LogRoot); err != nil { klog.Warningf("Failed to parse log root: %x %v", root.LogRoot, err) return err } m := spanner.Insert( "TreeHeads", []string{ "TreeID", "TimestampNanos", "TreeSize", "RootHash", "RootSignature", "TreeRevision", "TreeMetadata", }, []interface{}{ int64(tx.treeID), int64(logRoot.TimestampNanos), int64(logRoot.TreeSize), logRoot.RootHash, []byte{}, writeRev, logRoot.Metadata, }) stx, ok := tx.stx.(*spanner.ReadWriteTransaction) if !ok { return ErrWrongTXType } return stx.BufferWrite([]*spanner.Mutation{m}) } func readLeaves(ctx context.Context, stx *spanner.ReadOnlyTransaction, logID int64, ids [][]byte, f func(*trillian.LogLeaf)) error { leafTable := leafDataTbl cols := []string{colLeafIdentityHash, colLeafValue, colExtraData, colQueueTimestampNanos} keys := make([]spanner.KeySet, 0) for _, l := range ids { keys = append(keys, spanner.Key{logID, l}) } rows := stx.Read(ctx, leafTable, spanner.KeySets(keys...), cols) return rows.Do(func(r *spanner.Row) error { var l trillian.LogLeaf var qTimestamp int64 if err := r.Columns(&l.LeafIdentityHash, &l.LeafValue, &l.ExtraData, &qTimestamp); err != nil { return err } l.QueueTimestamp = timestamppb.New(time.Unix(0, qTimestamp)) if err := l.QueueTimestamp.CheckValid(); err != nil { return fmt.Errorf("got invalid queue timestamp: %w", err) } f(&l) return nil }) } // DequeueLeaves removes [0, limit) leaves from the to-be-sequenced queue. // The leaves returned are not guaranteed to be in any particular order. // The caller should assign sequence numbers and pass the updated leaves as // arguments to the UpdateSequencedLeaves method. // // The LogLeaf structs returned by this method will not be fully populated; // only the LeafIdentityHash and MerkleLeafHash fields will contain data, this // should be sufficient for assigning sequence numbers with this storage impl. // // TODO(al): cutoff is currently ignored. func (tx *logTX) DequeueLeaves(ctx context.Context, limit int, cutoff time.Time) ([]*trillian.LogLeaf, error) { if limit <= 0 { return nil, fmt.Errorf("limit should be > 0, got %d", limit) } // Special case pre-ordered logs. if tx.treeType == trillian.TreeType_PREORDERED_LOG { sth, err := tx.currentSTH(ctx) if err != nil { return nil, err } return tx.GetLeavesByRange(ctx, sth.TreeSize, int64(limit)) } // Decide which bucket(s) to dequeue from. // The high 8 bits of the bucket key is a time based ring - at any given // moment, FEs queueing entries will be adding them to different buckets // than we're dequeuing from here - the low 8 bits are the first byte of the // merkle hash of the entry. now := time.Now().UTC() cfg := tx.getLogStorageConfig() // Select a prefix that is likley to be on a different span server to spread load. prefix := int64((((now.Unix() + cfg.NumUnseqBuckets/2) % cfg.NumUnseqBuckets) << 8)) // Choose a starting point in the merkle prefix range, and calculate the // start/limit of the merkle range we'll dequeue from. // It seems to be much better to tune for keeping this range small, and allow // the signer to run multiple times per second than try to dequeue a large batch // which spans a large number of merkle prefixes. const suffixBuckets = 0x100 suffixStart := rand.Int63n(suffixBuckets) suffixFraction := float64(cfg.NumMerkleBuckets) / float64(suffixBuckets) if tx.ls.opts.DequeueAcrossMerkleBuckets { suffixFraction = tx.ls.opts.DequeueAcrossMerkleBucketsRangeFraction } suffixEnd := suffixStart + int64(math.Ceil(suffixBuckets*suffixFraction)) keysets := []spanner.KeySet{} if suffixEnd < suffixBuckets { keysets = append(keysets, spanner.KeyRange{ Start: spanner.Key{tx.treeID, prefix | suffixStart}, End: spanner.Key{tx.treeID, prefix | suffixEnd}, Kind: spanner.ClosedClosed, }) } else { // The range is too big and wraps around, overflowing a byte value, so we'll // start the second range at 0 and end at the upper limit modulo suffixBuckets: suffixEnd %= suffixBuckets keysets = append(keysets, spanner.KeyRange{ Start: spanner.Key{tx.treeID, prefix | suffixStart}, End: spanner.Key{tx.treeID, prefix | suffixBuckets - 1}, Kind: spanner.ClosedClosed, }, spanner.KeyRange{ Start: spanner.Key{tx.treeID, prefix}, // XXX: When suffixFraction = 1, this produces an overlapping range at suffixStart End: spanner.Key{tx.treeID, prefix | suffixEnd}, Kind: spanner.ClosedClosed, }) } errBreak := errors.New("break") ret := make([]*trillian.LogLeaf, 0, limit) if err := tx.stx.Read(ctx, unseqTable, spanner.KeySets(keysets...), []string{"Bucket", colQueueTimestampNanos, colMerkleLeafHash, colLeafIdentityHash}, ).Do(func(r *spanner.Row) error { var l trillian.LogLeaf var qe QueuedEntry if err := r.Columns(&qe.bucket, &qe.timestamp, &l.MerkleLeafHash, &l.LeafIdentityHash); err != nil { return err } l.QueueTimestamp = timestamppb.New(time.Unix(0, qe.timestamp)) if err := l.QueueTimestamp.CheckValid(); err != nil { return fmt.Errorf("got invalid queue timestamp: %w", err) } k := string(l.LeafIdentityHash) if tx.dequeued[k] != nil { // dupe, user probably called DequeueLeaves more than once. return nil } ret = append(ret, &l) qe.leaf = &l tx.dequeued[k] = &qe // If we've already got enough leaves, don't wrap around for any further reads. if len(ret) >= limit { return errBreak } return nil }); err != nil && err != errBreak { return nil, err } return ret, nil } // UpdateSequencedLeaves stores the sequence numbers assigned to the leaves, // and integrates them into the tree. func (tx *logTX) UpdateSequencedLeaves(ctx context.Context, leaves []*trillian.LogLeaf) error { stx, ok := tx.stx.(*spanner.ReadWriteTransaction) if !ok { return ErrWrongTXType } // We need the latest root to know what the next sequence number to use below is. currentSTH, err := tx.currentSTH(ctx) if err != nil { return err } for _, l := range leaves { if got, want := l.LeafIndex, currentSTH.TreeSize+tx.numSequenced; got != want { return fmt.Errorf("attempting to assign non-sequential leaf with sequence %d, want %d", got, want) } qe, ok := tx.dequeued[string(l.LeafIdentityHash)] if !ok { return fmt.Errorf("attempting to assign unknown merkleleafhash %v", l.MerkleLeafHash) } if err := l.IntegrateTimestamp.CheckValid(); err != nil { return fmt.Errorf("got invalid integrate timestamp: %w", err) } iTimestamp := l.IntegrateTimestamp.AsTime() // Add the sequence mapping... m1, err := spanner.InsertStruct(seqDataTbl, sequencedLeafDataCols{ TreeID: tx.treeID, SequenceNumber: l.LeafIndex, LeafIdentityHash: l.LeafIdentityHash, MerkleLeafHash: l.MerkleLeafHash, IntegrateTimestampNanos: iTimestamp.UnixNano(), }) if err != nil { return err } m2 := spanner.Delete(unseqTable, spanner.Key{tx.treeID, qe.bucket, qe.timestamp, l.MerkleLeafHash}) tx.numSequenced++ if err := stx.BufferWrite([]*spanner.Mutation{m1, m2}); err != nil { return fmt.Errorf("bufferwrite(): %v", err) } } return nil } // leafmap is a map of LogLeaf by sequence number which knows how to populate // itself directly from Spanner Rows. type leafmap map[int64]*trillian.LogLeaf // addFullRow appends the leaf data in row to the array func (l leafmap) addFullRow(seqLeaves map[string]sequencedLeafDataCols) func(r *spanner.Row) error { return func(r *spanner.Row) error { var leafData leafDataCols if err := r.ToStruct(&leafData); err != nil { return err } seqLeaf, ok := seqLeaves[string(leafData.LeafIdentityHash)] if !ok { return fmt.Errorf("LeafIdentityHash %x not found in SequencedLeafData", leafData.LeafIdentityHash) } leaf := &trillian.LogLeaf{ MerkleLeafHash: seqLeaf.MerkleLeafHash, LeafValue: leafData.LeafValue, ExtraData: leafData.ExtraData, LeafIndex: seqLeaf.SequenceNumber, LeafIdentityHash: leafData.LeafIdentityHash, } leaf.QueueTimestamp = timestamppb.New(time.Unix(0, leafData.QueueTimestampNanos)) if err := leaf.QueueTimestamp.CheckValid(); err != nil { return fmt.Errorf("got invalid queue timestamp: %w", err) } leaf.IntegrateTimestamp = timestamppb.New(time.Unix(0, seqLeaf.IntegrateTimestampNanos)) if err := leaf.IntegrateTimestamp.CheckValid(); err != nil { return fmt.Errorf("got invalid integrate timestamp: %w", err) } l[seqLeaf.SequenceNumber] = leaf return nil } } // leavesByHash is a map of []LogLeaf (keyed by value hash) which knows how to // populate itself from Spanner Rows. type leavesByHash map[string][]*trillian.LogLeaf // addRow adds the contents of the Spanner Row to this map. func (b leavesByHash) addRow(r *spanner.Row) error { var h []byte var v []byte var ed []byte var qTimestamp int64 if err := r.Columns(&h, &v, &ed, &qTimestamp); err != nil { return err } queueTimestamp := timestamppb.New(time.Unix(0, qTimestamp)) if err := queueTimestamp.CheckValid(); err != nil { return fmt.Errorf("got invalid queue timestamp: %w", err) } leaves, ok := b[string(h)] if !ok { return fmt.Errorf("inconsistency: unexpected leafValueHash %v", h) } for i := range leaves { if got, want := leaves[i].LeafIdentityHash, h; !bytes.Equal(got, want) { return fmt.Errorf("inconsistency: unexpected leafvaluehash %v, want %v", got, want) } leaves[i].LeafValue = v leaves[i].ExtraData = ed leaves[i].QueueTimestamp = queueTimestamp } return nil } // populateLeafData populates the partial LogLeaf structs held in the passed in // map of LeafIdentityHash to []LogLeaf by reading the remaining LogLeaf data from // Spanner. // The value of byHash is an []LogLeaf because the underlying leaf data could // be sequenced into multiple tree leaves if the log allows duplication. func (tx *logTX) populateLeafData(ctx context.Context, byHash leavesByHash) error { keySet := make([]spanner.KeySet, 0, len(byHash)) for k := range byHash { keySet = append(keySet, spanner.Key{tx.treeID, []byte(k)}) } cols := []string{colLeafIdentityHash, colLeafValue, colExtraData, colQueueTimestampNanos} rows := tx.stx.Read(ctx, leafDataTbl, spanner.KeySets(keySet...), cols) return rows.Do(byHash.addRow) } func validateRange(start, count, treeSize int64) error { if count <= 0 { return status.Errorf(codes.InvalidArgument, "invalid count %d", count) } if start < 0 { return status.Errorf(codes.InvalidArgument, "invalid start %d", start) } if treeSize >= 0 && start >= treeSize { return status.Errorf(codes.OutOfRange, "start index %d beyond tree size %d", start, treeSize) } return nil } // GetLeavesByRange returns the leaves corresponding to the given index range. func (tx *logTX) GetLeavesByRange(ctx context.Context, start, count int64) ([]*trillian.LogLeaf, error) { // We need the latest root to validate the indices are within range. currentSTH, err := tx.currentSTH(ctx) if err != nil { return nil, err } xsize := currentSTH.TreeSize if tx.treeType == trillian.TreeType_PREORDERED_LOG { xsize = -1 // Allow requesting entries beyond the tree size. } if err := validateRange(start, count, xsize); err != nil { return nil, err } xend := start + count if tx.treeType != trillian.TreeType_PREORDERED_LOG && xend > xsize { xend = xsize count = xend - start } // TODO: replace with INNER JOIN when spannertest supports JOINs // https://github.com/googleapis/google-cloud-go/tree/master/spanner/spannertest stmt := spanner.NewStatement( `SELECT TreeID, SequenceNumber, LeafIdentityHash, MerkleLeafHash, IntegrateTimestampNanos FROM SequencedLeafData WHERE TreeID = @tree_id AND SequenceNumber >= @start AND SequenceNumber < @xend`) stmt.Params["tree_id"] = tx.treeID stmt.Params["start"] = start stmt.Params["xend"] = xend seqLeaves := make(map[string]sequencedLeafDataCols) if err := tx.stx.Query(ctx, stmt).Do(func(r *spanner.Row) error { var seqLeaf sequencedLeafDataCols if err := r.ToStruct(&seqLeaf); err != nil { return err } seqLeaves[string(seqLeaf.LeafIdentityHash)] = seqLeaf return nil }); err != nil { return nil, err } idHashes := make([][]byte, 0, len(seqLeaves)) for _, l := range seqLeaves { idHashes = append(idHashes, l.LeafIdentityHash) } stmt = spanner.NewStatement( `SELECT TreeID, LeafIdentityHash, LeafValue, ExtraData, QueueTimestampNanos FROM LeafData WHERE TreeID = @tree_id AND LeafIdentityHash IN UNNEST(@id_hashes)`) stmt.Params["tree_id"] = tx.treeID stmt.Params["id_hashes"] = idHashes // Results need to be returned in order [start, end), all of which // should be available (as we restricted xend/count to TreeSize). leaves := make(leafmap) if err := tx.stx.Query(ctx, stmt). Do(leaves.addFullRow(seqLeaves)); err != nil { return nil, err } if got := int64(len(leaves)); got > count { return nil, fmt.Errorf("unexpected number of leaves %d, want <= %d", got, count) } ret := make([]*trillian.LogLeaf, 0, count) for i := start; i < (start + count); i++ { l, ok := leaves[i] if !ok { if i < int64(currentSTH.TreeSize) { return nil, fmt.Errorf("missing expected index %d", i) } break } ret = append(ret, l) } return ret, nil } // leafSlice is a slice of LogLeaf which knows how to populate itself from // Spanner Rows. type leafSlice []*trillian.LogLeaf // addRow appends the leaf data in Row to the array. func (l *leafSlice) addRow(r *spanner.Row) error { var ( s int64 mh, lh []byte ) if err := r.Columns(&s, &mh, &lh); err != nil { return err } leaf := trillian.LogLeaf{ LeafIndex: s, MerkleLeafHash: mh, LeafIdentityHash: lh, } *l = append(*l, &leaf) return nil } // getUsingIndex returns a slice containing the LogLeaf structs corresponding // to the requested keys. // The entries in key are used in constructing a primary key (treeID, keyElem) // for the specified Spanner index. // If bySeq is true, the returned slice will be order by LogLeaf.LeafIndex. func (tx *logTX) getUsingIndex(ctx context.Context, idx string, keys [][]byte, bySeq bool) ([]*trillian.LogLeaf, error) { keySet := make([]spanner.KeySet, 0, len(keys)) for _, k := range keys { keySet = append(keySet, spanner.Key{tx.treeID, k}) } leaves := make(leafSlice, 0, len(keys)) cols := []string{colSequenceNumber, colMerkleLeafHash, colLeafIdentityHash} rows := tx.stx.ReadUsingIndex(ctx, seqDataTbl, idx, spanner.KeySets(keySet...), cols) if err := rows.Do(leaves.addRow); err != nil { return nil, err } byHash := make(leavesByHash) for i := range leaves { k := string(leaves[i].LeafIdentityHash) byHash[k] = append(byHash[k], leaves[i]) } // Now we can fetch & combine the actual leaf data: if err := tx.populateLeafData(ctx, byHash); err != nil { return nil, err } if bySeq { sort.Sort(byIndex(leaves)) } return leaves, nil } // GetLeavesByHash returns the leaves corresponding to the given merkle hashes. // Any unknown hashes will simply be ignored, and the caller should inspect the // returned leaves to determine whether this has occurred. // TODO(al): Currently, this method does not populate the IntegrateTimestamp // // member of the returned leaves. We should convert this method to use SQL // rather than denormalising IntegrateTimestampNanos into the index too. func (tx *logTX) GetLeavesByHash(ctx context.Context, hashes [][]byte, bySeq bool) ([]*trillian.LogLeaf, error) { return tx.getUsingIndex(ctx, seqDataByMerkleHashIdx, hashes, bySeq) } // QueuedEntry represents a leaf which was dequeued. // It's used to store some extra info which is necessary for rebuilding the // leaf's primary key when it's passed back in to UpdateSequencedLeaves. type QueuedEntry struct { // leaf is partially populated with the Merkle and LeafValue hashes only. leaf *trillian.LogLeaf bucket int64 timestamp int64 } // LogLeaf sorting boilerplate below. type byIndex []*trillian.LogLeaf func (b byIndex) Len() int { return len(b) } func (b byIndex) Swap(i, j int) { b[i], b[j] = b[j], b[i] } func (b byIndex) Less(i, j int) bool { return b[i].LeafIndex < b[j].LeafIndex } trillian-1.6.1/storage/cloudspanner/log_storage_test.go000066400000000000000000000021131466362047600234040ustar00rootroot00000000000000// Copyright 2020 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 cloudspanner import ( "context" "testing" "github.com/google/trillian/integration/storagetest" "github.com/google/trillian/storage" ) func TestLogSuite(t *testing.T) { ctx := context.Background() db := GetTestDB(ctx, t) storageFactory := func(context.Context, *testing.T) (storage.LogStorage, storage.AdminStorage) { t.Cleanup(func() { cleanTestDB(ctx, t, db) }) return NewLogStorage(db), NewAdminStorage(db) } storagetest.RunLogStorageTests(t, storageFactory) } trillian-1.6.1/storage/cloudspanner/sdl.go000066400000000000000000000022201466362047600206210ustar00rootroot00000000000000// Copyright 2020 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 cloudspanner import ( "encoding/base64" "cloud.google.com/go/spanner/spansql" ) //go:generate sh gen.sh // readDDL returns a list of DDL statements from the database schema. func readDDL() ([]string, error) { ddlString, err := base64.StdEncoding.DecodeString(base64DDL) if err != nil { return nil, err } ddl, err := spansql.ParseDDL("spanner.sdl.go", string(ddlString)) if err != nil { return nil, err } stmts := make([]string, 0, len(ddl.List)) for _, s := range ddl.List { stmts = append(stmts, s.SQL()) } return stmts, nil } trillian-1.6.1/storage/cloudspanner/sdl_test.go000066400000000000000000000013411466362047600216630ustar00rootroot00000000000000// Copyright 2020 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 cloudspanner import "testing" func TestDDL(t *testing.T) { _, err := readDDL() if err != nil { t.Fatal(err) } } trillian-1.6.1/storage/cloudspanner/spanner.sdl000066400000000000000000000050171466362047600216710ustar00rootroot00000000000000-- CloudSpanner has a strong recommendation to restrict all data between -- top-level table rows to less than a few GiB: -- | As a rule of thumb, the size of every set of related rows in a hierarchy -- | of parent-child tables should be less than a few GiB. A set of related -- | rows in a hierarchy of parent-child tables is defined as: (a single row -- | of a table at the root of a database hierarchy) + (all rows of that table's -- | descendent tables that share the row's primary key) + (all rows of -- | interleaved indexes that share the row's primary key). -- | (https://cloud.google.com/spanner/docs/schema-and-data-model) -- so we don't interleave our tables. CREATE TABLE TreeRoots( TreeID INT64 NOT NULL, TreeState INT64 NOT NULL, TreeType INT64 NOT NULL, TreeInfo BYTES(2097152) NOT NULL, Deleted BOOL NOT NULL, DeleteTimeMillis INT64, ) PRIMARY KEY(TreeID); CREATE INDEX TreeRootsByDeleted ON TreeRoots (Deleted); CREATE TABLE TreeHeads( TreeID INT64 NOT NULL, TimestampNanos INT64 NOT NULL, TreeSize INT64 NOT NULL, RootHash BYTES(256) NOT NULL, RootSignature BYTES(1024) NOT NULL, TreeRevision INT64 NOT NULL, TreeMetadata BYTES(2097152), ) PRIMARY KEY(TreeID, TreeRevision DESC); CREATE TABLE SubtreeData( TreeID INT64 NOT NULL, SubtreeID BYTES(256) NOT NULL, Revision INT64 NOT NULL, Subtree BYTES(MAX) NOT NULL ) PRIMARY KEY(TreeID, SubtreeID, Revision DESC); CREATE TABLE LeafData( TreeID INT64 NOT NULL, LeafIdentityHash BYTES(256) NOT NULL, LeafValue BYTES(MAX) NOT NULL, ExtraData BYTES(MAX), QueueTimestampNanos INT64 NOT NULL, ) PRIMARY KEY(TreeID, LeafIdentityHash); CREATE TABLE SequencedLeafData( TreeID INT64 NOT NULL, SequenceNumber INT64 NOT NULL, LeafIdentityHash BYTES(256) NOT NULL, MerkleLeafHash BYTES(256) NOT NULL, IntegrateTimestampNanos INT64 NOT NULL, ) PRIMARY KEY(TreeID, SequenceNumber); CREATE INDEX SequenceByMerkleHash ON SequencedLeafData(TreeID, MerkleLeafHash) STORING(LeafIdentityHash); CREATE TABLE Unsequenced( TreeID INT64 NOT NULL, Bucket INT64 NOT NULL, QueueTimestampNanos INT64 NOT NULL, MerkleLeafHash BYTES(256) NOT NULL, LeafIdentityHash BYTES(256) NOT NULL, ) PRIMARY KEY (TreeID, Bucket, QueueTimestampNanos, MerkleLeafHash); trillian-1.6.1/storage/cloudspanner/spanner.sdl.go000066400000000000000000000070051466362047600222740ustar00rootroot00000000000000// Code generated by storage/cloudspanner/gen.sh DO NOT EDIT. package cloudspanner const base64DDL = ` LS0gQ2xvdWRTcGFubmVyIGhhcyBhIHN0cm9uZyByZWNvbW1lbmRhdGlvbiB0byByZXN0cmljdCBh bGwgZGF0YSBiZXR3ZWVuCi0tIHRvcC1sZXZlbCB0YWJsZSByb3dzIHRvIGxlc3MgdGhhbiBhIGZl dyBHaUI6Ci0tICB8IEFzIGEgcnVsZSBvZiB0aHVtYiwgdGhlIHNpemUgb2YgZXZlcnkgc2V0IG9m IHJlbGF0ZWQgcm93cyBpbiBhIGhpZXJhcmNoeQotLSAgfCBvZiBwYXJlbnQtY2hpbGQgdGFibGVz IHNob3VsZCBiZSBsZXNzIHRoYW4gYSBmZXcgR2lCLiBBIHNldCBvZiByZWxhdGVkCi0tICB8IHJv d3MgaW4gYSBoaWVyYXJjaHkgb2YgcGFyZW50LWNoaWxkIHRhYmxlcyBpcyBkZWZpbmVkIGFzOiAo YSBzaW5nbGUgcm93Ci0tICB8IG9mIGEgdGFibGUgYXQgdGhlIHJvb3Qgb2YgYSBkYXRhYmFzZSBo aWVyYXJjaHkpICsgKGFsbCByb3dzIG9mIHRoYXQgdGFibGUncwotLSAgfCBkZXNjZW5kZW50IHRh YmxlcyB0aGF0IHNoYXJlIHRoZSByb3cncyBwcmltYXJ5IGtleSkgKyAoYWxsIHJvd3Mgb2YKLS0g IHwgaW50ZXJsZWF2ZWQgaW5kZXhlcyB0aGF0IHNoYXJlIHRoZSByb3cncyBwcmltYXJ5IGtleSku Ci0tICB8IChodHRwczovL2Nsb3VkLmdvb2dsZS5jb20vc3Bhbm5lci9kb2NzL3NjaGVtYS1hbmQt ZGF0YS1tb2RlbCkKLS0gc28gd2UgZG9uJ3QgaW50ZXJsZWF2ZSBvdXIgdGFibGVzLgoKQ1JFQVRF IFRBQkxFIFRyZWVSb290cygKICBUcmVlSUQgICAgICAgICAgICAgICAgSU5UNjQgTk9UIE5VTEws CiAgVHJlZVN0YXRlICAgICAgICAgICAgIElOVDY0IE5PVCBOVUxMLAogIFRyZWVUeXBlICAgICAg ICAgICAgICBJTlQ2NCBOT1QgTlVMTCwKICBUcmVlSW5mbyAgICAgICAgICAgICAgQllURVMoMjA5 NzE1MikgTk9UIE5VTEwsCiAgRGVsZXRlZCAgICAgICAgICAgICAgIEJPT0wgTk9UIE5VTEwsCiAg RGVsZXRlVGltZU1pbGxpcyAgICAgIElOVDY0LAopIFBSSU1BUlkgS0VZKFRyZWVJRCk7CgpDUkVB VEUgSU5ERVggVHJlZVJvb3RzQnlEZWxldGVkCiAgT04gVHJlZVJvb3RzIChEZWxldGVkKTsKCkNS RUFURSBUQUJMRSBUcmVlSGVhZHMoCiAgVHJlZUlEICAgICAgICAgICAgICAgICAgSU5UNjQgTk9U IE5VTEwsCiAgVGltZXN0YW1wTmFub3MgICAgICAgICAgSU5UNjQgTk9UIE5VTEwsCiAgVHJlZVNp emUgICAgICAgICAgICAgICAgSU5UNjQgTk9UIE5VTEwsCiAgUm9vdEhhc2ggICAgICAgICAgICAg ICAgQllURVMoMjU2KSBOT1QgTlVMTCwKICBSb290U2lnbmF0dXJlICAgICAgICAgICBCWVRFUygx MDI0KSBOT1QgTlVMTCwKICBUcmVlUmV2aXNpb24gICAgICAgICAgICBJTlQ2NCBOT1QgTlVMTCwK ICBUcmVlTWV0YWRhdGEgICAgICAgICAgICBCWVRFUygyMDk3MTUyKSwKKSBQUklNQVJZIEtFWShU cmVlSUQsIFRyZWVSZXZpc2lvbiBERVNDKTsKCkNSRUFURSBUQUJMRSBTdWJ0cmVlRGF0YSgKICBU cmVlSUQgICAgICBJTlQ2NCBOT1QgTlVMTCwKICBTdWJ0cmVlSUQgICBCWVRFUygyNTYpIE5PVCBO VUxMLAogIFJldmlzaW9uICAgIElOVDY0IE5PVCBOVUxMLAogIFN1YnRyZWUgICAgIEJZVEVTKE1B WCkgTk9UIE5VTEwKKSBQUklNQVJZIEtFWShUcmVlSUQsIFN1YnRyZWVJRCwgUmV2aXNpb24gREVT Qyk7CgpDUkVBVEUgVEFCTEUgTGVhZkRhdGEoCiAgVHJlZUlEICAgICAgICAgICAgICBJTlQ2NCBO T1QgTlVMTCwKICBMZWFmSWRlbnRpdHlIYXNoICAgIEJZVEVTKDI1NikgTk9UIE5VTEwsCiAgTGVh ZlZhbHVlICAgICAgICAgICBCWVRFUyhNQVgpIE5PVCBOVUxMLAogIEV4dHJhRGF0YSAgICAgICAg ICAgQllURVMoTUFYKSwKICBRdWV1ZVRpbWVzdGFtcE5hbm9zIElOVDY0IE5PVCBOVUxMLAopIFBS SU1BUlkgS0VZKFRyZWVJRCwgTGVhZklkZW50aXR5SGFzaCk7CgpDUkVBVEUgVEFCTEUgU2VxdWVu Y2VkTGVhZkRhdGEoCiAgVHJlZUlEICAgICAgICAgICAgICAgICAgSU5UNjQgTk9UIE5VTEwsCiAg U2VxdWVuY2VOdW1iZXIgICAgICAgICAgSU5UNjQgTk9UIE5VTEwsCiAgTGVhZklkZW50aXR5SGFz aCAgICAgICAgQllURVMoMjU2KSBOT1QgTlVMTCwKICBNZXJrbGVMZWFmSGFzaCAgICAgICAgICBC WVRFUygyNTYpIE5PVCBOVUxMLAogIEludGVncmF0ZVRpbWVzdGFtcE5hbm9zIElOVDY0IE5PVCBO VUxMLAopIFBSSU1BUlkgS0VZKFRyZWVJRCwgU2VxdWVuY2VOdW1iZXIpOwoKQ1JFQVRFIElOREVY IFNlcXVlbmNlQnlNZXJrbGVIYXNoCiAgT04gU2VxdWVuY2VkTGVhZkRhdGEoVHJlZUlELCBNZXJr bGVMZWFmSGFzaCkKICBTVE9SSU5HKExlYWZJZGVudGl0eUhhc2gpOwoKQ1JFQVRFIFRBQkxFIFVu c2VxdWVuY2VkKAogIFRyZWVJRCAgICAgICAgICAgICAgICAgSU5UNjQgTk9UIE5VTEwsCiAgQnVj a2V0ICAgICAgICAgICAgICAgICBJTlQ2NCBOT1QgTlVMTCwKICBRdWV1ZVRpbWVzdGFtcE5hbm9z ICAgIElOVDY0IE5PVCBOVUxMLAogIE1lcmtsZUxlYWZIYXNoICAgICAgICAgQllURVMoMjU2KSBO T1QgTlVMTCwKICBMZWFmSWRlbnRpdHlIYXNoICAgICAgIEJZVEVTKDI1NikgTk9UIE5VTEwsCikg UFJJTUFSWSBLRVkgKFRyZWVJRCwgQnVja2V0LCBRdWV1ZVRpbWVzdGFtcE5hbm9zLCBNZXJrbGVM ZWFmSGFzaCk7Cg== ` trillian-1.6.1/storage/cloudspanner/spannerpb/000077500000000000000000000000001466362047600215045ustar00rootroot00000000000000trillian-1.6.1/storage/cloudspanner/spannerpb/gen.go000066400000000000000000000015061466362047600226060ustar00rootroot00000000000000// Copyright 2018 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 spannerpb contains the generated protobuf code for the Spanner // storage implementation. package spannerpb //go:generate protoc -I=. -I=../../../third_party/googleapis --go_out=paths=source_relative:. spanner.proto trillian-1.6.1/storage/cloudspanner/spannerpb/spanner.pb.go000066400000000000000000001047341466362047600241120ustar00rootroot00000000000000// Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.34.2 // protoc v3.20.1 // source: spanner.proto package spannerpb import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" anypb "google.golang.org/protobuf/types/known/anypb" reflect "reflect" sync "sync" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // State of the Tree. // Mirrors trillian.TreeState. type TreeState int32 const ( TreeState_UNKNOWN_TREE_STATE TreeState = 0 TreeState_ACTIVE TreeState = 1 TreeState_FROZEN TreeState = 2 ) // Enum value maps for TreeState. var ( TreeState_name = map[int32]string{ 0: "UNKNOWN_TREE_STATE", 1: "ACTIVE", 2: "FROZEN", } TreeState_value = map[string]int32{ "UNKNOWN_TREE_STATE": 0, "ACTIVE": 1, "FROZEN": 2, } ) func (x TreeState) Enum() *TreeState { p := new(TreeState) *p = x return p } func (x TreeState) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (TreeState) Descriptor() protoreflect.EnumDescriptor { return file_spanner_proto_enumTypes[0].Descriptor() } func (TreeState) Type() protoreflect.EnumType { return &file_spanner_proto_enumTypes[0] } func (x TreeState) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use TreeState.Descriptor instead. func (TreeState) EnumDescriptor() ([]byte, []int) { return file_spanner_proto_rawDescGZIP(), []int{0} } // Type of the Tree. // Mirrors trillian.TreeType. type TreeType int32 const ( TreeType_UNKNOWN TreeType = 0 TreeType_LOG TreeType = 1 TreeType_PREORDERED_LOG TreeType = 3 ) // Enum value maps for TreeType. var ( TreeType_name = map[int32]string{ 0: "UNKNOWN", 1: "LOG", 3: "PREORDERED_LOG", } TreeType_value = map[string]int32{ "UNKNOWN": 0, "LOG": 1, "PREORDERED_LOG": 3, } ) func (x TreeType) Enum() *TreeType { p := new(TreeType) *p = x return p } func (x TreeType) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (TreeType) Descriptor() protoreflect.EnumDescriptor { return file_spanner_proto_enumTypes[1].Descriptor() } func (TreeType) Type() protoreflect.EnumType { return &file_spanner_proto_enumTypes[1] } func (x TreeType) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use TreeType.Descriptor instead. func (TreeType) EnumDescriptor() ([]byte, []int) { return file_spanner_proto_rawDescGZIP(), []int{1} } // Defines the preimage protection used for tree leaves / nodes. // Eg, RFC6962 dictates a 0x00 prefix for leaves and 0x01 for nodes. // Mirrors trillian.HashStrategy. type HashStrategy int32 const ( HashStrategy_UNKNOWN_HASH_STRATEGY HashStrategy = 0 HashStrategy_RFC_6962 HashStrategy = 1 HashStrategy_TEST_MAP_HASHER HashStrategy = 2 HashStrategy_OBJECT_RFC6962_SHA256 HashStrategy = 3 HashStrategy_CONIKS_SHA512_256 HashStrategy = 4 HashStrategy_CONIKS_SHA256 HashStrategy = 5 ) // Enum value maps for HashStrategy. var ( HashStrategy_name = map[int32]string{ 0: "UNKNOWN_HASH_STRATEGY", 1: "RFC_6962", 2: "TEST_MAP_HASHER", 3: "OBJECT_RFC6962_SHA256", 4: "CONIKS_SHA512_256", 5: "CONIKS_SHA256", } HashStrategy_value = map[string]int32{ "UNKNOWN_HASH_STRATEGY": 0, "RFC_6962": 1, "TEST_MAP_HASHER": 2, "OBJECT_RFC6962_SHA256": 3, "CONIKS_SHA512_256": 4, "CONIKS_SHA256": 5, } ) func (x HashStrategy) Enum() *HashStrategy { p := new(HashStrategy) *p = x return p } func (x HashStrategy) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (HashStrategy) Descriptor() protoreflect.EnumDescriptor { return file_spanner_proto_enumTypes[2].Descriptor() } func (HashStrategy) Type() protoreflect.EnumType { return &file_spanner_proto_enumTypes[2] } func (x HashStrategy) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use HashStrategy.Descriptor instead. func (HashStrategy) EnumDescriptor() ([]byte, []int) { return file_spanner_proto_rawDescGZIP(), []int{2} } // Supported hash algorithms. // The numbering space is the same as for TLS, given in RFC 5246 s7.4.1.4.1. See // http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-18. // Mirrors trillian.HashAlgorithm. type HashAlgorithm int32 const ( // No hash algorithm is used. HashAlgorithm_NONE HashAlgorithm = 0 // SHA256 is used. HashAlgorithm_SHA256 HashAlgorithm = 4 ) // Enum value maps for HashAlgorithm. var ( HashAlgorithm_name = map[int32]string{ 0: "NONE", 4: "SHA256", } HashAlgorithm_value = map[string]int32{ "NONE": 0, "SHA256": 4, } ) func (x HashAlgorithm) Enum() *HashAlgorithm { p := new(HashAlgorithm) *p = x return p } func (x HashAlgorithm) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (HashAlgorithm) Descriptor() protoreflect.EnumDescriptor { return file_spanner_proto_enumTypes[3].Descriptor() } func (HashAlgorithm) Type() protoreflect.EnumType { return &file_spanner_proto_enumTypes[3] } func (x HashAlgorithm) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use HashAlgorithm.Descriptor instead. func (HashAlgorithm) EnumDescriptor() ([]byte, []int) { return file_spanner_proto_rawDescGZIP(), []int{3} } // Supported signature algorithms. // The numbering space is the same as for TLS, given in RFC 5246 s7.4.1.4.1. See // http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-16. // Mirrors trillian.SignatureAlgorithm. type SignatureAlgorithm int32 const ( // Anonymous signature scheme. SignatureAlgorithm_ANONYMOUS SignatureAlgorithm = 0 // RSA signature scheme. SignatureAlgorithm_RSA SignatureAlgorithm = 1 // ECDSA signature scheme. SignatureAlgorithm_ECDSA SignatureAlgorithm = 3 ) // Enum value maps for SignatureAlgorithm. var ( SignatureAlgorithm_name = map[int32]string{ 0: "ANONYMOUS", 1: "RSA", 3: "ECDSA", } SignatureAlgorithm_value = map[string]int32{ "ANONYMOUS": 0, "RSA": 1, "ECDSA": 3, } ) func (x SignatureAlgorithm) Enum() *SignatureAlgorithm { p := new(SignatureAlgorithm) *p = x return p } func (x SignatureAlgorithm) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (SignatureAlgorithm) Descriptor() protoreflect.EnumDescriptor { return file_spanner_proto_enumTypes[4].Descriptor() } func (SignatureAlgorithm) Type() protoreflect.EnumType { return &file_spanner_proto_enumTypes[4] } func (x SignatureAlgorithm) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use SignatureAlgorithm.Descriptor instead. func (SignatureAlgorithm) EnumDescriptor() ([]byte, []int) { return file_spanner_proto_rawDescGZIP(), []int{4} } // LogStorageConfig holds settings which tune the storage implementation for // a given log tree. type LogStorageConfig struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // num_unseq_buckets defines the length of the unsequenced time ring buffer. // This value must *never* be reduced for any provisioned tree. // // This value should be >= 1, and there's probably not much benefit in // raising it past about 4. // TODO(al): test what the effects of various values are here. NumUnseqBuckets int64 `protobuf:"varint,1,opt,name=num_unseq_buckets,json=numUnseqBuckets,proto3" json:"num_unseq_buckets,omitempty"` // num_merkle_buckets defines the number of individual buckets below each // unsequenced ring bucket. // This value may be changed at any time (so long as you understand the // impact it'll have on integration performace!) // // This value must lie in the range [1..256] NumMerkleBuckets int64 `protobuf:"varint,2,opt,name=num_merkle_buckets,json=numMerkleBuckets,proto3" json:"num_merkle_buckets,omitempty"` } func (x *LogStorageConfig) Reset() { *x = LogStorageConfig{} if protoimpl.UnsafeEnabled { mi := &file_spanner_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *LogStorageConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*LogStorageConfig) ProtoMessage() {} func (x *LogStorageConfig) ProtoReflect() protoreflect.Message { mi := &file_spanner_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use LogStorageConfig.ProtoReflect.Descriptor instead. func (*LogStorageConfig) Descriptor() ([]byte, []int) { return file_spanner_proto_rawDescGZIP(), []int{0} } func (x *LogStorageConfig) GetNumUnseqBuckets() int64 { if x != nil { return x.NumUnseqBuckets } return 0 } func (x *LogStorageConfig) GetNumMerkleBuckets() int64 { if x != nil { return x.NumMerkleBuckets } return 0 } // MapStorageConfig holds settings which tune the storage implementation for // a given map tree. type MapStorageConfig struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *MapStorageConfig) Reset() { *x = MapStorageConfig{} if protoimpl.UnsafeEnabled { mi := &file_spanner_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *MapStorageConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*MapStorageConfig) ProtoMessage() {} func (x *MapStorageConfig) ProtoReflect() protoreflect.Message { mi := &file_spanner_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MapStorageConfig.ProtoReflect.Descriptor instead. func (*MapStorageConfig) Descriptor() ([]byte, []int) { return file_spanner_proto_rawDescGZIP(), []int{1} } // TreeInfo stores information about a Trillian tree. type TreeInfo struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // tree_id is the ID of the tree, and is used as a primary key. TreeId int64 `protobuf:"varint,1,opt,name=tree_id,json=treeId,proto3" json:"tree_id,omitempty"` // key_id identifies the private key associated with this tree. KeyId int64 `protobuf:"varint,2,opt,name=key_id,json=keyId,proto3" json:"key_id,omitempty"` // name is a short name for this tree. Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` // description is a short free form text describing the tree. Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty"` // tree_type identifies whether this is a Log or a Map tree. TreeType TreeType `protobuf:"varint,5,opt,name=tree_type,json=treeType,proto3,enum=spannerpb.TreeType" json:"tree_type,omitempty"` // tree_state is the state of the tree. TreeState TreeState `protobuf:"varint,8,opt,name=tree_state,json=treeState,proto3,enum=spannerpb.TreeState" json:"tree_state,omitempty"` // hash_strategy is the hashing strategy used by the tree. HashStrategy HashStrategy `protobuf:"varint,9,opt,name=hash_strategy,json=hashStrategy,proto3,enum=spannerpb.HashStrategy" json:"hash_strategy,omitempty"` // hash_algorithm is the hash algorithm used by the tree. HashAlgorithm HashAlgorithm `protobuf:"varint,10,opt,name=hash_algorithm,json=hashAlgorithm,proto3,enum=spannerpb.HashAlgorithm" json:"hash_algorithm,omitempty"` // signature_algorithm is the signature algorithm used by the tree. SignatureAlgorithm SignatureAlgorithm `protobuf:"varint,11,opt,name=signature_algorithm,json=signatureAlgorithm,proto3,enum=spannerpb.SignatureAlgorithm" json:"signature_algorithm,omitempty"` // create_time_nanos is the creation timestamp of the tree, in nanos since // epoch. CreateTimeNanos int64 `protobuf:"varint,13,opt,name=create_time_nanos,json=createTimeNanos,proto3" json:"create_time_nanos,omitempty"` // update_time_nanos is the last update time of the tree, in nanos since // epoch. UpdateTimeNanos int64 `protobuf:"varint,14,opt,name=update_time_nanos,json=updateTimeNanos,proto3" json:"update_time_nanos,omitempty"` // private_key should be used to generate signatures for this tree. PrivateKey *anypb.Any `protobuf:"bytes,15,opt,name=private_key,json=privateKey,proto3" json:"private_key,omitempty"` // public_key_der should be used to verify signatures produced by this tree. // It is the key in DER-encoded PKIX form. PublicKeyDer []byte `protobuf:"bytes,16,opt,name=public_key_der,json=publicKeyDer,proto3" json:"public_key_der,omitempty"` // config contains the log or map specific tree configuration. // // Types that are assignable to StorageConfig: // // *TreeInfo_LogStorageConfig // *TreeInfo_MapStorageConfig StorageConfig isTreeInfo_StorageConfig `protobuf_oneof:"storage_config"` // max_root_duration_millis is the interval after which a new signed root is // produced even if there have been no submission. If zero, this behavior is // disabled. MaxRootDurationMillis int64 `protobuf:"varint,17,opt,name=max_root_duration_millis,json=maxRootDurationMillis,proto3" json:"max_root_duration_millis,omitempty"` // If true the tree was soft deleted. Deleted bool `protobuf:"varint,18,opt,name=deleted,proto3" json:"deleted,omitempty"` // Time of tree deletion, if any. DeleteTimeNanos int64 `protobuf:"varint,19,opt,name=delete_time_nanos,json=deleteTimeNanos,proto3" json:"delete_time_nanos,omitempty"` } func (x *TreeInfo) Reset() { *x = TreeInfo{} if protoimpl.UnsafeEnabled { mi := &file_spanner_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *TreeInfo) String() string { return protoimpl.X.MessageStringOf(x) } func (*TreeInfo) ProtoMessage() {} func (x *TreeInfo) ProtoReflect() protoreflect.Message { mi := &file_spanner_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use TreeInfo.ProtoReflect.Descriptor instead. func (*TreeInfo) Descriptor() ([]byte, []int) { return file_spanner_proto_rawDescGZIP(), []int{2} } func (x *TreeInfo) GetTreeId() int64 { if x != nil { return x.TreeId } return 0 } func (x *TreeInfo) GetKeyId() int64 { if x != nil { return x.KeyId } return 0 } func (x *TreeInfo) GetName() string { if x != nil { return x.Name } return "" } func (x *TreeInfo) GetDescription() string { if x != nil { return x.Description } return "" } func (x *TreeInfo) GetTreeType() TreeType { if x != nil { return x.TreeType } return TreeType_UNKNOWN } func (x *TreeInfo) GetTreeState() TreeState { if x != nil { return x.TreeState } return TreeState_UNKNOWN_TREE_STATE } func (x *TreeInfo) GetHashStrategy() HashStrategy { if x != nil { return x.HashStrategy } return HashStrategy_UNKNOWN_HASH_STRATEGY } func (x *TreeInfo) GetHashAlgorithm() HashAlgorithm { if x != nil { return x.HashAlgorithm } return HashAlgorithm_NONE } func (x *TreeInfo) GetSignatureAlgorithm() SignatureAlgorithm { if x != nil { return x.SignatureAlgorithm } return SignatureAlgorithm_ANONYMOUS } func (x *TreeInfo) GetCreateTimeNanos() int64 { if x != nil { return x.CreateTimeNanos } return 0 } func (x *TreeInfo) GetUpdateTimeNanos() int64 { if x != nil { return x.UpdateTimeNanos } return 0 } func (x *TreeInfo) GetPrivateKey() *anypb.Any { if x != nil { return x.PrivateKey } return nil } func (x *TreeInfo) GetPublicKeyDer() []byte { if x != nil { return x.PublicKeyDer } return nil } func (m *TreeInfo) GetStorageConfig() isTreeInfo_StorageConfig { if m != nil { return m.StorageConfig } return nil } func (x *TreeInfo) GetLogStorageConfig() *LogStorageConfig { if x, ok := x.GetStorageConfig().(*TreeInfo_LogStorageConfig); ok { return x.LogStorageConfig } return nil } func (x *TreeInfo) GetMapStorageConfig() *MapStorageConfig { if x, ok := x.GetStorageConfig().(*TreeInfo_MapStorageConfig); ok { return x.MapStorageConfig } return nil } func (x *TreeInfo) GetMaxRootDurationMillis() int64 { if x != nil { return x.MaxRootDurationMillis } return 0 } func (x *TreeInfo) GetDeleted() bool { if x != nil { return x.Deleted } return false } func (x *TreeInfo) GetDeleteTimeNanos() int64 { if x != nil { return x.DeleteTimeNanos } return 0 } type isTreeInfo_StorageConfig interface { isTreeInfo_StorageConfig() } type TreeInfo_LogStorageConfig struct { LogStorageConfig *LogStorageConfig `protobuf:"bytes,6,opt,name=log_storage_config,json=logStorageConfig,proto3,oneof"` } type TreeInfo_MapStorageConfig struct { MapStorageConfig *MapStorageConfig `protobuf:"bytes,7,opt,name=map_storage_config,json=mapStorageConfig,proto3,oneof"` } func (*TreeInfo_LogStorageConfig) isTreeInfo_StorageConfig() {} func (*TreeInfo_MapStorageConfig) isTreeInfo_StorageConfig() {} // TreeHead is the storage format for Trillian's commitment to a particular // tree state. type TreeHead struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // tree_id identifies the tree this TreeHead is built from. TreeId int64 `protobuf:"varint,1,opt,name=tree_id,json=treeId,proto3" json:"tree_id,omitempty"` // ts_nanos is the nanosecond resolution timestamp at which the // TreeHead was created. TsNanos int64 `protobuf:"varint,2,opt,name=ts_nanos,json=tsNanos,proto3" json:"ts_nanos,omitempty"` // tree_size is the number of entries in the tree. TreeSize int64 `protobuf:"varint,3,opt,name=tree_size,json=treeSize,proto3" json:"tree_size,omitempty"` // root_hash is the root of the tree. RootHash []byte `protobuf:"bytes,4,opt,name=root_hash,json=rootHash,proto3" json:"root_hash,omitempty"` // signature holds the raw digital signature across the serialized log_root // (not present) represented by the data in this TreeHead. Signature []byte `protobuf:"bytes,10,opt,name=signature,proto3" json:"signature,omitempty"` // tree_revision identifies the revision at which the TreeHead was created. TreeRevision int64 `protobuf:"varint,6,opt,name=tree_revision,json=treeRevision,proto3" json:"tree_revision,omitempty"` Metadata []byte `protobuf:"bytes,9,opt,name=metadata,proto3" json:"metadata,omitempty"` } func (x *TreeHead) Reset() { *x = TreeHead{} if protoimpl.UnsafeEnabled { mi := &file_spanner_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *TreeHead) String() string { return protoimpl.X.MessageStringOf(x) } func (*TreeHead) ProtoMessage() {} func (x *TreeHead) ProtoReflect() protoreflect.Message { mi := &file_spanner_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use TreeHead.ProtoReflect.Descriptor instead. func (*TreeHead) Descriptor() ([]byte, []int) { return file_spanner_proto_rawDescGZIP(), []int{3} } func (x *TreeHead) GetTreeId() int64 { if x != nil { return x.TreeId } return 0 } func (x *TreeHead) GetTsNanos() int64 { if x != nil { return x.TsNanos } return 0 } func (x *TreeHead) GetTreeSize() int64 { if x != nil { return x.TreeSize } return 0 } func (x *TreeHead) GetRootHash() []byte { if x != nil { return x.RootHash } return nil } func (x *TreeHead) GetSignature() []byte { if x != nil { return x.Signature } return nil } func (x *TreeHead) GetTreeRevision() int64 { if x != nil { return x.TreeRevision } return 0 } func (x *TreeHead) GetMetadata() []byte { if x != nil { return x.Metadata } return nil } var File_spanner_proto protoreflect.FileDescriptor var file_spanner_proto_rawDesc = []byte{ 0x0a, 0x0d, 0x73, 0x70, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, 0x73, 0x70, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x70, 0x62, 0x1a, 0x19, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x6c, 0x0a, 0x10, 0x4c, 0x6f, 0x67, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2a, 0x0a, 0x11, 0x6e, 0x75, 0x6d, 0x5f, 0x75, 0x6e, 0x73, 0x65, 0x71, 0x5f, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x6e, 0x75, 0x6d, 0x55, 0x6e, 0x73, 0x65, 0x71, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x6e, 0x75, 0x6d, 0x5f, 0x6d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x5f, 0x62, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x6e, 0x75, 0x6d, 0x4d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x22, 0x12, 0x0a, 0x10, 0x4d, 0x61, 0x70, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x8c, 0x07, 0x0a, 0x08, 0x54, 0x72, 0x65, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x74, 0x72, 0x65, 0x65, 0x49, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x30, 0x0a, 0x09, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x13, 0x2e, 0x73, 0x70, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x08, 0x74, 0x72, 0x65, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x33, 0x0a, 0x0a, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x73, 0x70, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x09, 0x74, 0x72, 0x65, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x3c, 0x0a, 0x0d, 0x68, 0x61, 0x73, 0x68, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x73, 0x70, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0c, 0x68, 0x61, 0x73, 0x68, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x3f, 0x0a, 0x0e, 0x68, 0x61, 0x73, 0x68, 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x73, 0x70, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x52, 0x0d, 0x68, 0x61, 0x73, 0x68, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x12, 0x4e, 0x0a, 0x13, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x73, 0x70, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x52, 0x12, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x4e, 0x61, 0x6e, 0x6f, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x4e, 0x61, 0x6e, 0x6f, 0x73, 0x12, 0x35, 0x0a, 0x0b, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x0a, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x64, 0x65, 0x72, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x44, 0x65, 0x72, 0x12, 0x4b, 0x0a, 0x12, 0x6c, 0x6f, 0x67, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x73, 0x70, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x67, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x48, 0x00, 0x52, 0x10, 0x6c, 0x6f, 0x67, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x4b, 0x0a, 0x12, 0x6d, 0x61, 0x70, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x73, 0x70, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x4d, 0x61, 0x70, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x48, 0x00, 0x52, 0x10, 0x6d, 0x61, 0x70, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x37, 0x0a, 0x18, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x18, 0x11, 0x20, 0x01, 0x28, 0x03, 0x52, 0x15, 0x6d, 0x61, 0x78, 0x52, 0x6f, 0x6f, 0x74, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x12, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, 0x2a, 0x0a, 0x11, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x18, 0x13, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x4e, 0x61, 0x6e, 0x6f, 0x73, 0x42, 0x10, 0x0a, 0x0e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4a, 0x04, 0x08, 0x0c, 0x10, 0x0d, 0x22, 0xe9, 0x01, 0x0a, 0x08, 0x54, 0x72, 0x65, 0x65, 0x48, 0x65, 0x61, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x74, 0x72, 0x65, 0x65, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x73, 0x5f, 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x74, 0x73, 0x4e, 0x61, 0x6e, 0x6f, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x74, 0x72, 0x65, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x72, 0x6f, 0x6f, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x74, 0x72, 0x65, 0x65, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x4a, 0x04, 0x08, 0x08, 0x10, 0x09, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x2a, 0x3b, 0x0a, 0x09, 0x54, 0x72, 0x65, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x16, 0x0a, 0x12, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x54, 0x52, 0x45, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x46, 0x52, 0x4f, 0x5a, 0x45, 0x4e, 0x10, 0x02, 0x2a, 0x3f, 0x0a, 0x08, 0x54, 0x72, 0x65, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4c, 0x4f, 0x47, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x50, 0x52, 0x45, 0x4f, 0x52, 0x44, 0x45, 0x52, 0x45, 0x44, 0x5f, 0x4c, 0x4f, 0x47, 0x10, 0x03, 0x22, 0x04, 0x08, 0x02, 0x10, 0x02, 0x2a, 0x03, 0x4d, 0x41, 0x50, 0x2a, 0x91, 0x01, 0x0a, 0x0c, 0x48, 0x61, 0x73, 0x68, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x19, 0x0a, 0x15, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x5f, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x46, 0x43, 0x5f, 0x36, 0x39, 0x36, 0x32, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x54, 0x45, 0x53, 0x54, 0x5f, 0x4d, 0x41, 0x50, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x45, 0x52, 0x10, 0x02, 0x12, 0x19, 0x0a, 0x15, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x52, 0x46, 0x43, 0x36, 0x39, 0x36, 0x32, 0x5f, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x10, 0x03, 0x12, 0x15, 0x0a, 0x11, 0x43, 0x4f, 0x4e, 0x49, 0x4b, 0x53, 0x5f, 0x53, 0x48, 0x41, 0x35, 0x31, 0x32, 0x5f, 0x32, 0x35, 0x36, 0x10, 0x04, 0x12, 0x11, 0x0a, 0x0d, 0x43, 0x4f, 0x4e, 0x49, 0x4b, 0x53, 0x5f, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x10, 0x05, 0x2a, 0x25, 0x0a, 0x0d, 0x48, 0x61, 0x73, 0x68, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x10, 0x04, 0x2a, 0x37, 0x0a, 0x12, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x12, 0x0d, 0x0a, 0x09, 0x41, 0x4e, 0x4f, 0x4e, 0x59, 0x4d, 0x4f, 0x55, 0x53, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x52, 0x53, 0x41, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x43, 0x44, 0x53, 0x41, 0x10, 0x03, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x73, 0x70, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x2f, 0x73, 0x70, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( file_spanner_proto_rawDescOnce sync.Once file_spanner_proto_rawDescData = file_spanner_proto_rawDesc ) func file_spanner_proto_rawDescGZIP() []byte { file_spanner_proto_rawDescOnce.Do(func() { file_spanner_proto_rawDescData = protoimpl.X.CompressGZIP(file_spanner_proto_rawDescData) }) return file_spanner_proto_rawDescData } var file_spanner_proto_enumTypes = make([]protoimpl.EnumInfo, 5) var file_spanner_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_spanner_proto_goTypes = []any{ (TreeState)(0), // 0: spannerpb.TreeState (TreeType)(0), // 1: spannerpb.TreeType (HashStrategy)(0), // 2: spannerpb.HashStrategy (HashAlgorithm)(0), // 3: spannerpb.HashAlgorithm (SignatureAlgorithm)(0), // 4: spannerpb.SignatureAlgorithm (*LogStorageConfig)(nil), // 5: spannerpb.LogStorageConfig (*MapStorageConfig)(nil), // 6: spannerpb.MapStorageConfig (*TreeInfo)(nil), // 7: spannerpb.TreeInfo (*TreeHead)(nil), // 8: spannerpb.TreeHead (*anypb.Any)(nil), // 9: google.protobuf.Any } var file_spanner_proto_depIdxs = []int32{ 1, // 0: spannerpb.TreeInfo.tree_type:type_name -> spannerpb.TreeType 0, // 1: spannerpb.TreeInfo.tree_state:type_name -> spannerpb.TreeState 2, // 2: spannerpb.TreeInfo.hash_strategy:type_name -> spannerpb.HashStrategy 3, // 3: spannerpb.TreeInfo.hash_algorithm:type_name -> spannerpb.HashAlgorithm 4, // 4: spannerpb.TreeInfo.signature_algorithm:type_name -> spannerpb.SignatureAlgorithm 9, // 5: spannerpb.TreeInfo.private_key:type_name -> google.protobuf.Any 5, // 6: spannerpb.TreeInfo.log_storage_config:type_name -> spannerpb.LogStorageConfig 6, // 7: spannerpb.TreeInfo.map_storage_config:type_name -> spannerpb.MapStorageConfig 8, // [8:8] is the sub-list for method output_type 8, // [8:8] is the sub-list for method input_type 8, // [8:8] is the sub-list for extension type_name 8, // [8:8] is the sub-list for extension extendee 0, // [0:8] is the sub-list for field type_name } func init() { file_spanner_proto_init() } func file_spanner_proto_init() { if File_spanner_proto != nil { return } if !protoimpl.UnsafeEnabled { file_spanner_proto_msgTypes[0].Exporter = func(v any, i int) any { switch v := v.(*LogStorageConfig); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_spanner_proto_msgTypes[1].Exporter = func(v any, i int) any { switch v := v.(*MapStorageConfig); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_spanner_proto_msgTypes[2].Exporter = func(v any, i int) any { switch v := v.(*TreeInfo); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_spanner_proto_msgTypes[3].Exporter = func(v any, i int) any { switch v := v.(*TreeHead); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } } file_spanner_proto_msgTypes[2].OneofWrappers = []any{ (*TreeInfo_LogStorageConfig)(nil), (*TreeInfo_MapStorageConfig)(nil), } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_spanner_proto_rawDesc, NumEnums: 5, NumMessages: 4, NumExtensions: 0, NumServices: 0, }, GoTypes: file_spanner_proto_goTypes, DependencyIndexes: file_spanner_proto_depIdxs, EnumInfos: file_spanner_proto_enumTypes, MessageInfos: file_spanner_proto_msgTypes, }.Build() File_spanner_proto = out.File file_spanner_proto_rawDesc = nil file_spanner_proto_goTypes = nil file_spanner_proto_depIdxs = nil } trillian-1.6.1/storage/cloudspanner/spannerpb/spanner.proto000066400000000000000000000124471466362047600242470ustar00rootroot00000000000000syntax = "proto3"; option go_package = "github.com/google/trillian/storage/cloudspanner/spannerpb"; package spannerpb; import "google/protobuf/any.proto"; // State of the Tree. // Mirrors trillian.TreeState. enum TreeState { UNKNOWN_TREE_STATE = 0; ACTIVE = 1; FROZEN = 2; } // Type of the Tree. // Mirrors trillian.TreeType. enum TreeType { UNKNOWN = 0; LOG = 1; PREORDERED_LOG = 3; reserved 2; reserved "MAP"; } // Defines the preimage protection used for tree leaves / nodes. // Eg, RFC6962 dictates a 0x00 prefix for leaves and 0x01 for nodes. // Mirrors trillian.HashStrategy. enum HashStrategy { UNKNOWN_HASH_STRATEGY = 0; RFC_6962 = 1; TEST_MAP_HASHER = 2; OBJECT_RFC6962_SHA256 = 3; CONIKS_SHA512_256 = 4; CONIKS_SHA256 = 5; } // Supported hash algorithms. // The numbering space is the same as for TLS, given in RFC 5246 s7.4.1.4.1. See // http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-18. // Mirrors trillian.HashAlgorithm. enum HashAlgorithm { // No hash algorithm is used. NONE = 0; // SHA256 is used. SHA256 = 4; } // Supported signature algorithms. // The numbering space is the same as for TLS, given in RFC 5246 s7.4.1.4.1. See // http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-16. // Mirrors trillian.SignatureAlgorithm. enum SignatureAlgorithm { // Anonymous signature scheme. ANONYMOUS = 0; // RSA signature scheme. RSA = 1; // ECDSA signature scheme. ECDSA = 3; } // LogStorageConfig holds settings which tune the storage implementation for // a given log tree. message LogStorageConfig { // num_unseq_buckets defines the length of the unsequenced time ring buffer. // This value must *never* be reduced for any provisioned tree. // // This value should be >= 1, and there's probably not much benefit in // raising it past about 4. // TODO(al): test what the effects of various values are here. int64 num_unseq_buckets = 1; // num_merkle_buckets defines the number of individual buckets below each // unsequenced ring bucket. // This value may be changed at any time (so long as you understand the // impact it'll have on integration performace!) // // This value must lie in the range [1..256] int64 num_merkle_buckets = 2; } // MapStorageConfig holds settings which tune the storage implementation for // a given map tree. message MapStorageConfig {} // TreeInfo stores information about a Trillian tree. message TreeInfo { // tree_id is the ID of the tree, and is used as a primary key. int64 tree_id = 1; // key_id identifies the private key associated with this tree. int64 key_id = 2; // name is a short name for this tree. string name = 3; // description is a short free form text describing the tree. string description = 4; // tree_type identifies whether this is a Log or a Map tree. TreeType tree_type = 5; // tree_state is the state of the tree. TreeState tree_state = 8; // hash_strategy is the hashing strategy used by the tree. HashStrategy hash_strategy = 9; // hash_algorithm is the hash algorithm used by the tree. HashAlgorithm hash_algorithm = 10; // signature_algorithm is the signature algorithm used by the tree. SignatureAlgorithm signature_algorithm = 11; reserved 12; // create_time_nanos is the creation timestamp of the tree, in nanos since // epoch. int64 create_time_nanos = 13; // update_time_nanos is the last update time of the tree, in nanos since // epoch. int64 update_time_nanos = 14; // private_key should be used to generate signatures for this tree. google.protobuf.Any private_key = 15; // public_key_der should be used to verify signatures produced by this tree. // It is the key in DER-encoded PKIX form. bytes public_key_der = 16; // config contains the log or map specific tree configuration. oneof storage_config { LogStorageConfig log_storage_config = 6; MapStorageConfig map_storage_config = 7; } // max_root_duration_millis is the interval after which a new signed root is // produced even if there have been no submission. If zero, this behavior is // disabled. int64 max_root_duration_millis = 17; // If true the tree was soft deleted. bool deleted = 18; // Time of tree deletion, if any. int64 delete_time_nanos = 19; } // TreeHead is the storage format for Trillian's commitment to a particular // tree state. message TreeHead { // tree_id identifies the tree this TreeHead is built from. int64 tree_id = 1; // ts_nanos is the nanosecond resolution timestamp at which the // TreeHead was created. int64 ts_nanos = 2; // tree_size is the number of entries in the tree. int64 tree_size = 3; // root_hash is the root of the tree. bytes root_hash = 4; reserved 5; // Deleted: old spannerpb.DigitallySigned reserved 8; // signature holds the raw digital signature across the serialized log_root // (not present) represented by the data in this TreeHead. bytes signature = 10; // tree_revision identifies the revision at which the TreeHead was created. int64 tree_revision = 6; // metadata is a blob of opaque data specific to the personality layer that an // application associates with each TreeHead, and which must be covered by the // tree head signature. Only used for Maps at present. reserved 7; bytes metadata = 9; } trillian-1.6.1/storage/cloudspanner/storage_provider.go000066400000000000000000000143631466362047600234300ustar00rootroot00000000000000// Copyright 2018 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 cloudspanner import ( "bytes" "compress/gzip" "context" "encoding/base64" "flag" "io" "sync" "time" "cloud.google.com/go/spanner" "github.com/google/trillian/monitoring" "github.com/google/trillian/storage" "google.golang.org/api/option" "k8s.io/klog/v2" ) var ( csURI = flag.String("cloudspanner_uri", "", "Connection URI for CloudSpanner database") csNumChannels = flag.Int("cloudspanner_num_channels", 0, "Number of gRPC channels to use to talk to CloudSpanner.") csSessionMaxOpened = flag.Uint64("cloudspanner_max_open_sessions", 0, "Max open sessions.") csSessionMinOpened = flag.Uint64("cloudspanner_min_open_sessions", 0, "Min open sessions.") csSessionMaxIdle = flag.Uint64("cloudspanner_max_idle_sessions", 0, "Max idle sessions.") _ = flag.Float64("cloudspanner_write_sessions", 0, "DEPRECATED. This flag is unused and will be removed in the future. Fraction of write capable sessions to maintain.") csSessionHCWorkers = flag.Int("cloudspanner_num_healthcheckers", 0, "Number of health check workers for Spanner session pool.") csSessionHCInterval = flag.Duration("cloudspanner_healthcheck_interval", 0, "Interval betweek pinging sessions.") csSessionTrackHandles = flag.Bool("cloudspanner_track_session_handles", false, "determines whether the session pool will keep track of the stacktrace of the goroutines that take sessions from the pool.") csDequeueAcrossMerkleBucketsFraction = flag.Float64("cloudspanner_dequeue_bucket_fraction", 0.75, "Fraction of merkle keyspace to dequeue from, set to zero to disable.") csReadOnlyStaleness = flag.Duration("cloudspanner_readonly_staleness", time.Minute, "How far in the past to perform readonly operations. Within limits, raising this should help to increase performance/reduce latency.") _ = flag.Uint64("cloudspanner_max_burst_sessions", 0, "No longer used") csMu sync.RWMutex csStorageInstance *cloudSpannerProvider warnOnce sync.Once ) func init() { if err := storage.RegisterProvider("cloud_spanner", newCloudSpannerStorageProvider); err != nil { panic(err) } } func warn() { warnOnce.Do(func() { w := `H4sIAAAAAAAA/4xUsW7rMAzc8xUE2lE41B2sWlzsZ3TwoKEIgkQZ64mLsxga8vUPlG3FrZ2Hd1Ng3onHE5UDPQOEmVnwjCGhjyLC8RLcPgfhIvmwot8/CaHF9CMdOthdGmKvdSQQET85TqxJtKzjgnd4mYaFilDIlmhsnKql977mZSqzYcLy5K/zCUX66sbtNOAwteTXiVph5m4nigGzzUH7e3+a3XIRf5PFyhQQEV6UXLeY8nL292gyujlMIlIbdcUwet9ieBx/snWIOXkievPenyOMiDjnjOHj+MMJhjZfFBFpHF+AcQkGpr9f1nz02YoKcPXed5nvjHG2DGtB//7gGwHCq69HIPMBGa7hIYi3mPlOBOhf/Z8eMBmAdNVjZKlCFuiQgK19Y1YKrXDT5KWX7ohVC+cArnUKwGAF/rwvk6CrVhJ1DuDDF9igfVtEuFf8U2M0MXW4wf28pBy/4yOuOaLZw2+Qa76m5PpSFy+5N0usbnyr66+AjY7cx3eKz5VHrZpFlqL6nJa82+gI/H3Vh+TKm9Fmib7I5GXSvcStTQrndxwIw4dQvpak00DGpKvbnVgIxXk4kD31oLnTSkgkxchmJ01Vnj7lQLZFXrV532bpfqLJbTzqfygXrLHkh/CoP5Hq13DXJYuV3fD/DcRbm+5f7s1tvNj/RLBD9T6vNbi9dYpT05QTKsV1B+Ut4m8AAAD///IJ0vhIBgAA` wd, _ := base64.StdEncoding.DecodeString(w) b := bytes.NewReader(wd) r, _ := gzip.NewReader(b) if err := r.Close(); err != nil { // No need to exit, it's an unlikely error and doesn't affect operation. klog.Warningf("Close()=%v", err) } t, _ := io.ReadAll(r) klog.Warningf("WARNING\n%s\nCloudspanner is an experimental storage implementation, and only supports Logs currently.", string(t)) }) } type cloudSpannerProvider struct { client *spanner.Client } func configFromFlags() spanner.ClientConfig { r := spanner.ClientConfig{} setUint64IfNotDefault(&r.SessionPoolConfig.MaxOpened, *csSessionMaxOpened) setUint64IfNotDefault(&r.SessionPoolConfig.MinOpened, *csSessionMinOpened) setUint64IfNotDefault(&r.SessionPoolConfig.MaxIdle, *csSessionMaxIdle) setIntIfNotDefault(&r.SessionPoolConfig.HealthCheckWorkers, *csSessionHCWorkers) r.SessionPoolConfig.TrackSessionHandles = *csSessionTrackHandles r.SessionPoolConfig.HealthCheckInterval = *csSessionHCInterval return r } func optionsFromFlags() []option.ClientOption { opts := []option.ClientOption{} if numConns := *csNumChannels; numConns != 0 { opts = append(opts, option.WithGRPCConnectionPool(numConns)) } return opts } func newCloudSpannerStorageProvider(_ monitoring.MetricFactory) (storage.Provider, error) { csMu.Lock() defer csMu.Unlock() if csStorageInstance != nil { return csStorageInstance, nil } client, err := spanner.NewClientWithConfig(context.TODO(), *csURI, configFromFlags(), optionsFromFlags()...) if err != nil { return nil, err } csStorageInstance = &cloudSpannerProvider{ client: client, } return csStorageInstance, nil } // LogStorage builds and returns a new storage.LogStorage using CloudSpanner. func (s *cloudSpannerProvider) LogStorage() storage.LogStorage { warn() opts := LogStorageOptions{} frac := *csDequeueAcrossMerkleBucketsFraction if frac > 1.0 { frac = 1.0 } if frac > 0 { opts.DequeueAcrossMerkleBuckets = true opts.DequeueAcrossMerkleBucketsRangeFraction = frac } if *csReadOnlyStaleness > 0 { opts.ReadOnlyStaleness = *csReadOnlyStaleness } return NewLogStorageWithOpts(s.client, opts) } // AdminStorage builds and returns a new storage.AdminStorage using CloudSpanner. func (s *cloudSpannerProvider) AdminStorage() storage.AdminStorage { warn() return NewAdminStorage(s.client) } // Close shuts down this provider. Calls to the other methods will fail // after this. func (s *cloudSpannerProvider) Close() error { s.client.Close() return nil } func setIntIfNotDefault(t *int, v int) { if v != 0 { *t = v } } func setUint64IfNotDefault(t *uint64, v uint64) { if v != 0 { *t = v } } trillian-1.6.1/storage/cloudspanner/tree_storage.go000066400000000000000000000314051466362047600225310ustar00rootroot00000000000000// Copyright 2018 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 cloudspanner import ( "bytes" "context" "errors" "fmt" "sync" "time" "cloud.google.com/go/spanner" "github.com/google/trillian" "github.com/google/trillian/storage" "github.com/google/trillian/storage/cache" "github.com/google/trillian/storage/cloudspanner/spannerpb" "github.com/google/trillian/storage/storagepb" "github.com/google/trillian/storage/tree" "github.com/transparency-dev/merkle/compact" "golang.org/x/sync/errgroup" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/proto" "k8s.io/klog/v2" ) var ( // ErrNotFound is returned when a read/lookup fails because there was no such // item. ErrNotFound = status.Errorf(codes.NotFound, "not found") // ErrNotImplemented is returned by any interface methods which have not been // implemented yet. ErrNotImplemented = errors.New("not implemented") // ErrTransactionClosed is returned by interface methods when an operation is // attempted on a transaction whose Commit or Close methods have // previously been called. ErrTransactionClosed = errors.New("transaction is closed") // ErrWrongTXType is returned when, somehow, a write operation is attempted // with a read-only transaction. This should not even be possible. ErrWrongTXType = errors.New("mutating method called on read-only transaction") ) const ( subtreeTbl = "SubtreeData" colSubtree = "Subtree" colSubtreeID = "SubtreeID" colTreeID = "TreeID" colRevision = "Revision" ) // treeStorage provides a shared base for the concrete CloudSpanner-backed // implementation of the Trillian storage.LogStorage and storage.MapStorage // interfaces. type treeStorage struct { admin storage.AdminStorage opts TreeStorageOptions client *spanner.Client } // TreeStorageOptions holds various levers for configuring the tree storage instance. type TreeStorageOptions struct { // ReadOnlyStaleness controls how far in the past a read-only snapshot // transaction will read. // This is intended to allow Spanner to use local replicas for read requests // to help with performance. // See https://cloud.google.com/spanner/docs/timestamp-bounds for more details. ReadOnlyStaleness time.Duration } func newTreeStorageWithOpts(client *spanner.Client, opts TreeStorageOptions) *treeStorage { return &treeStorage{client: client, admin: nil, opts: opts} } type spanRead interface { Query(context.Context, spanner.Statement) *spanner.RowIterator Read(ctx context.Context, table string, keys spanner.KeySet, columns []string) *spanner.RowIterator ReadUsingIndex(ctx context.Context, table, index string, keys spanner.KeySet, columns []string) *spanner.RowIterator ReadRow(ctx context.Context, table string, key spanner.Key, columns []string) (*spanner.Row, error) ReadWithOptions(ctx context.Context, table string, keys spanner.KeySet, columns []string, opts *spanner.ReadOptions) (ri *spanner.RowIterator) } // latestSTH reads and returns the newest STH. func (t *treeStorage) latestSTH(ctx context.Context, stx spanRead, treeID int64) (*spannerpb.TreeHead, error) { query := spanner.NewStatement( "SELECT TreeID, TimestampNanos, TreeSize, RootHash, RootSignature, TreeRevision, TreeMetadata FROM TreeHeads" + " WHERE TreeID = @tree_id" + " ORDER BY TreeRevision DESC " + " LIMIT 1") query.Params["tree_id"] = treeID var th *spannerpb.TreeHead rows := stx.Query(ctx, query) defer rows.Stop() err := rows.Do(func(r *spanner.Row) error { tth := &spannerpb.TreeHead{} if err := r.Columns(&tth.TreeId, &tth.TsNanos, &tth.TreeSize, &tth.RootHash, &tth.Signature, &tth.TreeRevision, &tth.Metadata); err != nil { return err } th = tth return nil }) if err != nil { return nil, err } if th == nil { klog.Warningf("no head found for treeID %v", treeID) return nil, storage.ErrTreeNeedsInit } return th, nil } type newCacheFn func(*trillian.Tree) (*cache.SubtreeCache, error) func (t *treeStorage) getTreeAndConfig(ctx context.Context, tree *trillian.Tree) (*trillian.Tree, proto.Message, error) { config, err := unmarshalSettings(tree) if err != nil { return nil, nil, err } return tree, config, nil } // begin returns a newly started tree transaction for the specified tree. func (t *treeStorage) begin(ctx context.Context, tree *trillian.Tree, newCache newCacheFn, stx spanRead) (*treeTX, error) { tree, config, err := t.getTreeAndConfig(ctx, tree) if err != nil { return nil, err } subtreeCache, err := newCache(tree) if err != nil { return nil, err } treeTX := &treeTX{ treeID: tree.TreeId, treeType: tree.TreeType, ts: t, stx: stx, cache: subtreeCache, config: config, _writeRev: -1, } return treeTX, nil } // getLatestRoot populates this TX with the newest tree root visible (when // taking read-staleness into account) by this transaction. func (t *treeTX) getLatestRoot(ctx context.Context) error { t.getLatestRootOnce.Do(func() { t._currentSTH, t._currentSTHErr = t.ts.latestSTH(ctx, t.stx, t.treeID) if t._currentSTH != nil { t._writeRev = t._currentSTH.TreeRevision + 1 } }) return t._currentSTHErr } // treeTX is a concrete implementation of the part of storage.LogTreeTX // interface formerly known as storage.TreeTX. type treeTX struct { treeID int64 treeType trillian.TreeType ts *treeStorage // mu guards the nil setting/checking of stx as part of the open checking. mu sync.RWMutex // stx is the underlying Spanner transaction in which all operations will be // performed. stx spanRead // config holds the StorageSettings proto acquired from the trillian.Tree. // Varies according to tree_type (LogStorageConfig vs MapStorageConfig). config proto.Message // currentSTH holds a copy of the latest known STH at the time the // transaction was started, or nil if there was no STH. _currentSTH *spannerpb.TreeHead _currentSTHErr error // writeRev is the tree revision at which any writes will be made. _writeRev int64 cache *cache.SubtreeCache getLatestRootOnce sync.Once } func (t *treeTX) currentSTH(ctx context.Context) (*spannerpb.TreeHead, error) { if err := t.getLatestRoot(ctx); err != nil { return nil, err } return t._currentSTH, nil } func (t *treeTX) writeRev(ctx context.Context) (int64, error) { if err := t.getLatestRoot(ctx); err == storage.ErrTreeNeedsInit { return 0, nil } else if err != nil { return -1, fmt.Errorf("writeRev(): %v", err) } return t._writeRev, nil } // storeSubtrees adds buffered writes to the in-flight transaction to store the // passed in subtrees. func (t *treeTX) storeSubtrees(ctx context.Context, sts []*storagepb.SubtreeProto) error { stx, ok := t.stx.(*spanner.ReadWriteTransaction) if !ok { return ErrWrongTXType } for _, st := range sts { if st == nil { continue } stBytes, err := proto.Marshal(st) if err != nil { return err } m := spanner.Insert( subtreeTbl, []string{colTreeID, colSubtreeID, colRevision, colSubtree}, []interface{}{t.treeID, st.Prefix, t._writeRev, stBytes}, ) if err := stx.BufferWrite([]*spanner.Mutation{m}); err != nil { return err } } return nil } func (t *treeTX) flushSubtrees(ctx context.Context) error { tiles, err := t.cache.UpdatedTiles() if err != nil { return err } return t.storeSubtrees(ctx, tiles) } // Commit attempts to apply all actions perfomed to the underlying Spanner // transaction. If this call returns an error, any values READ via this // transaction MUST NOT be used. // On return from the call, this transaction will be in a closed state. func (t *treeTX) Commit(ctx context.Context) error { t.mu.Lock() defer func() { t.stx = nil t.mu.Unlock() }() if t.stx == nil { return ErrTransactionClosed } switch stx := t.stx.(type) { case *spanner.ReadOnlyTransaction: klog.V(1).Infof("Closed readonly tx %p", stx) stx.Close() return nil case *spanner.ReadWriteTransaction: return t.flushSubtrees(ctx) default: return fmt.Errorf("internal error: unknown transaction type %T", stx) } } // Close aborts any operations perfomed on the underlying Spanner transaction. // On return from the call, this transaction will be in a closed state. func (t *treeTX) Close() error { t.mu.Lock() defer t.mu.Unlock() if t.stx == nil { return ErrTransactionClosed } if stx, ok := t.stx.(*spanner.ReadOnlyTransaction); ok { klog.V(1).Infof("Closed snapshot %p", stx) stx.Close() } return nil } // readRevision returns the tree revision at which the currently visible (taking // into account read-staleness) STH was stored. func (t *treeTX) readRevision(ctx context.Context) (int64, error) { sth, err := t.currentSTH(ctx) if err != nil { return -1, err } return sth.TreeRevision, nil } // getSubtree retrieves the most recent subtree specified by id at (or below) // the requested revision. // If no such subtree exists it returns nil. func (t *treeTX) getSubtree(ctx context.Context, rev int64, id []byte) (p *storagepb.SubtreeProto, e error) { var ret *storagepb.SubtreeProto stmt := spanner.NewStatement( "SELECT Revision, Subtree FROM SubtreeData" + " WHERE TreeID = @tree_id" + " AND SubtreeID = @subtree_id" + " AND Revision <= @revision" + " ORDER BY Revision DESC" + " LIMIT 1") stmt.Params["tree_id"] = t.treeID stmt.Params["subtree_id"] = id stmt.Params["revision"] = rev rows := t.stx.Query(ctx, stmt) err := rows.Do(func(r *spanner.Row) error { if ret != nil { return nil } var rRev int64 var st storagepb.SubtreeProto stBytes := make([]byte, 1<<20) if err := r.Columns(&rRev, &stBytes); err != nil { return err } if err := proto.Unmarshal(stBytes, &st); err != nil { return err } if rRev > rev { return fmt.Errorf("got subtree with too new a revision %d, want %d", rRev, rev) } if got, want := id, st.Prefix; !bytes.Equal(got, want) { return fmt.Errorf("got subtree with prefix %v, wanted %v", got, want) } if got, want := rRev, rev; got > rev { return fmt.Errorf("got subtree rev %d, wanted <= %d", got, want) } ret = &st // If this is a subtree with a zero-length prefix, we'll need to create an // empty Prefix field: if st.Prefix == nil && len(id) == 0 { st.Prefix = []byte{} } return nil }) return ret, err } // GetMerkleNodes returns the requested set of nodes at, or before, the // transaction read revision. func (t *treeTX) GetMerkleNodes(ctx context.Context, ids []compact.NodeID) ([]tree.Node, error) { t.mu.RLock() defer t.mu.RUnlock() if t.stx == nil { return nil, ErrTransactionClosed } rev, err := t.readRevision(ctx) if err != nil { return nil, fmt.Errorf("failed to get read revision: %v", err) } return t.cache.GetNodes(ids, t.getSubtreesAtRev(ctx, rev)) } // getSubtreesAtRev returns a GetSubtreesFunc which reads at the passed in rev. func (t *treeTX) getSubtreesAtRev(ctx context.Context, rev int64) cache.GetSubtreesFunc { return func(ids [][]byte) ([]*storagepb.SubtreeProto, error) { // Request the various subtrees in parallel. // c will carry any retrieved subtrees c := make(chan *storagepb.SubtreeProto, len(ids)) // Spawn goroutines for each request g, gctx := errgroup.WithContext(ctx) for _, id := range ids { id := id g.Go(func() error { st, err := t.getSubtree(gctx, rev, id) if err != nil { return err } c <- st return nil }) } if err := g.Wait(); err != nil { return nil, err } close(c) // Now wait for the goroutines to signal their completion, and collect // the results. ret := make([]*storagepb.SubtreeProto, 0, len(ids)) for st := range c { if st != nil { ret = append(ret, st) } } return ret, nil } } // SetMerkleNodes stores the provided merkle nodes at the writeRevision of the // transaction. func (t *treeTX) SetMerkleNodes(ctx context.Context, nodes []tree.Node) error { t.mu.RLock() defer t.mu.RUnlock() if t.stx == nil { return ErrTransactionClosed } writeRev, err := t.writeRev(ctx) if err != nil { return err } return t.cache.SetNodes(nodes, t.getSubtreesAtRev(ctx, writeRev-1)) } func checkDatabaseAccessible(ctx context.Context, client *spanner.Client) error { stmt := spanner.NewStatement("SELECT 1") // We don't care about freshness here, being able to read *something* is enough rows := client.Single().Query(ctx, stmt) defer rows.Stop() return rows.Do(func(row *spanner.Row) error { return nil }) } trillian-1.6.1/storage/crdb/000077500000000000000000000000001466362047600157315ustar00rootroot00000000000000trillian-1.6.1/storage/crdb/common_test.go000066400000000000000000000046401466362047600206130ustar00rootroot00000000000000// Copyright 2022 Trillian Authors // // 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 crdb import ( "context" "database/sql" "flag" "os" "sync" "testing" "github.com/cockroachdb/cockroach-go/v2/testserver" "k8s.io/klog/v2" "github.com/google/trillian/storage/testdb" ) // testDBs holds a set of test databases, one per test. var testDBs sync.Map type testDBHandle struct { db *sql.DB done func(context.Context) } func (db *testDBHandle) GetDB() *sql.DB { return db.db } func TestMain(m *testing.M) { flag.Parse() ts, err := testserver.NewTestServer(testserver.CustomVersionOpt("v22.2.7")) if err != nil { klog.Exitf("Failed to start test server: %v", err) } defer ts.Stop() // reset the test server URL path. By default cockroach sets it // to point to a default database, we don't want that. dburl := ts.PGURL() dburl.Path = "/" // Set the environment variable for the test server if err := os.Setenv(testdb.CockroachDBURIEnv, dburl.String()); err != nil { klog.Exitf("Failed to SetEnv CockroachDBURIEnv: %v", err) } if !testdb.CockroachDBAvailable() { klog.Errorf("CockroachDB not available, skipping all CockroachDB storage tests") return } status := m.Run() // Clean up databases testDBs.Range(func(key, value interface{}) bool { testName := key.(string) klog.Infof("Cleaning up database for test %s", testName) db := value.(*testDBHandle) // TODO(jaosorior): Set a timeout instead of using Background db.done(context.Background()) return true }) os.Exit(status) } // This is used to identify a database from the map func getDBID(t *testing.T) string { t.Helper() return t.Name() } func openTestDBOrDie(t *testing.T) *testDBHandle { t.Helper() db, done, err := testdb.NewTrillianDB(context.TODO(), testdb.DriverCockroachDB) if err != nil { panic(err) } handle := &testDBHandle{ db: db, done: done, } testDBs.Store(getDBID(t), handle) return handle } trillian-1.6.1/storage/crdb/drop_storage.sql000066400000000000000000000004301466362047600211370ustar00rootroot00000000000000-- Caution - this removes all tables in our schema DROP TABLE IF EXISTS Unsequenced; DROP TABLE IF EXISTS Subtree; DROP TABLE IF EXISTS SequencedLeafData; DROP TABLE IF EXISTS TreeHead; DROP TABLE IF EXISTS LeafData; DROP TABLE IF EXISTS TreeControl; DROP TABLE IF EXISTS Trees; trillian-1.6.1/storage/crdb/errors.go000066400000000000000000000022321466362047600175730ustar00rootroot00000000000000// Copyright 2021 Trillian Authors // // 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 crdb import ( "github.com/lib/pq" ) var uniqueViolationErrorCode = pq.ErrorCode("23505") // crdbToGRPC converts some types of CockroachDB errors to GRPC errors. This gives // clients more signal when the operation can be retried. func crdbToGRPC(err error) error { _, ok := err.(*pq.Error) if !ok { return err } // TODO(jaosorior): Do we have a crdb equivalent for a deadlock // error code? return err } func isDuplicateErr(err error) bool { switch err := err.(type) { case *pq.Error: return err.Code == uniqueViolationErrorCode default: return false } } trillian-1.6.1/storage/crdb/log_storage.go000066400000000000000000000746001466362047600205740ustar00rootroot00000000000000// Copyright 2016 Trillian Authors // // 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 crdb import ( "bytes" "context" "database/sql" "errors" "fmt" "sort" "strconv" "sync" "time" "github.com/transparency-dev/merkle/compact" "github.com/transparency-dev/merkle/rfc6962" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/timestamppb" "k8s.io/klog/v2" "github.com/google/trillian" "github.com/google/trillian/monitoring" "github.com/google/trillian/storage" "github.com/google/trillian/storage/cache" "github.com/google/trillian/storage/tree" "github.com/google/trillian/types" ) const ( valuesPlaceholder5 = "($1,$2,$3,$4,$5)" insertLeafDataSQL = "INSERT INTO LeafData(TreeId,LeafIdentityHash,LeafValue,ExtraData,QueueTimestampNanos) VALUES" + valuesPlaceholder5 insertSequencedLeafSQL = "INSERT INTO SequencedLeafData(TreeId,LeafIdentityHash,MerkleLeafHash,SequenceNumber,IntegrateTimestampNanos) VALUES" selectNonDeletedTreeIDByTypeAndStateSQL = ` SELECT TreeId FROM Trees WHERE TreeType IN($1,$2) AND TreeState IN($3,$4) AND (Deleted IS NULL OR Deleted = 'false')` selectLatestSignedLogRootSQL = `SELECT TreeHeadTimestamp,TreeSize,RootHash,TreeRevision,RootSignature FROM TreeHead WHERE TreeId=$1 ORDER BY TreeHeadTimestamp DESC LIMIT 1` selectLeavesByRangeSQL = `SELECT s.MerkleLeafHash,l.LeafIdentityHash,l.LeafValue,s.SequenceNumber,l.ExtraData,l.QueueTimestampNanos,s.IntegrateTimestampNanos FROM LeafData l,SequencedLeafData s WHERE l.LeafIdentityHash = s.LeafIdentityHash AND s.SequenceNumber >= $1 AND s.SequenceNumber < $2 AND l.TreeId = $3 AND s.TreeId = l.TreeId` + orderBySequenceNumberSQL // These statements need to be expanded to provide the correct number of parameter placeholders. // Note that this uses the MySQL-specific marker syntax here, but is eventually replaced with // the postgres syntax in getStmt. selectLeavesByMerkleHashSQL = `SELECT s.MerkleLeafHash,l.LeafIdentityHash,l.LeafValue,s.SequenceNumber,l.ExtraData,l.QueueTimestampNanos,s.IntegrateTimestampNanos FROM LeafData l,SequencedLeafData s WHERE l.LeafIdentityHash = s.LeafIdentityHash AND s.MerkleLeafHash IN (` + placeholderSQL + `) AND l.TreeId = ? AND s.TreeId = l.TreeId` // TODO(#1548): rework the code so the dummy hash isn't needed (e.g. this assumes hash size is 32) dummyMerkleLeafHash = "00000000000000000000000000000000" // This statement returns a dummy Merkle leaf hash value (which must be // of the right size) so that its signature matches that of the other // leaf-selection statements. // Note that this uses the MySQL-specific marker syntax here, but is eventually replaced with // the postgres syntax in getStmt. selectLeavesByLeafIdentityHashSQL = `SELECT '` + dummyMerkleLeafHash + `',l.LeafIdentityHash,l.LeafValue,-1,l.ExtraData,l.QueueTimestampNanos,s.IntegrateTimestampNanos FROM LeafData l LEFT JOIN SequencedLeafData s ON (l.LeafIdentityHash = s.LeafIdentityHash AND l.TreeID = s.TreeID) WHERE l.LeafIdentityHash IN (` + placeholderSQL + `) AND l.TreeId = ?` // Same as above except with leaves ordered by sequence so we only incur this cost when necessary orderBySequenceNumberSQL = " ORDER BY s.SequenceNumber" selectLeavesByMerkleHashOrderedBySequenceSQL = selectLeavesByMerkleHashSQL + orderBySequenceNumberSQL logIDLabel = "logid" ) var ( once sync.Once queuedCounter monitoring.Counter queuedDupCounter monitoring.Counter dequeuedCounter monitoring.Counter queueLatency monitoring.Histogram queueInsertLatency monitoring.Histogram queueReadLatency monitoring.Histogram queueInsertLeafLatency monitoring.Histogram queueInsertEntryLatency monitoring.Histogram dequeueLatency monitoring.Histogram dequeueSelectLatency monitoring.Histogram dequeueRemoveLatency monitoring.Histogram ) func createMetrics(mf monitoring.MetricFactory) { queuedCounter = mf.NewCounter("crdb_queued_leaves", "Number of leaves queued", logIDLabel) queuedDupCounter = mf.NewCounter("crdb_queued_dup_leaves", "Number of duplicate leaves queued", logIDLabel) dequeuedCounter = mf.NewCounter("crdb_dequeued_leaves", "Number of leaves dequeued", logIDLabel) queueLatency = mf.NewHistogram("crdb_queue_leaves_latency", "Latency of queue leaves operation in seconds", logIDLabel) queueInsertLatency = mf.NewHistogram("crdb_queue_leaves_latency_insert", "Latency of insertion part of queue leaves operation in seconds", logIDLabel) queueReadLatency = mf.NewHistogram("crdb_queue_leaves_latency_read_dups", "Latency of read-duplicates part of queue leaves operation in seconds", logIDLabel) queueInsertLeafLatency = mf.NewHistogram("crdb_queue_leaf_latency_leaf", "Latency of insert-leaf part of queue (single) leaf operation in seconds", logIDLabel) queueInsertEntryLatency = mf.NewHistogram("crdb_queue_leaf_latency_entry", "Latency of insert-entry part of queue (single) leaf operation in seconds", logIDLabel) dequeueLatency = mf.NewHistogram("crdb_dequeue_leaves_latency", "Latency of dequeue leaves operation in seconds", logIDLabel) dequeueSelectLatency = mf.NewHistogram("crdb_dequeue_leaves_latency_select", "Latency of selection part of dequeue leaves operation in seconds", logIDLabel) dequeueRemoveLatency = mf.NewHistogram("crdb_dequeue_leaves_latency_remove", "Latency of removal part of dequeue leaves operation in seconds", logIDLabel) } func labelForTX(t *logTreeTX) string { return strconv.FormatInt(t.treeID, 10) } func observe(hist monitoring.Histogram, duration time.Duration, label string) { hist.Observe(duration.Seconds(), label) } type crdbLogStorage struct { *crdbTreeStorage admin storage.AdminStorage metricFactory monitoring.MetricFactory } // NewLogStorage creates a storage.LogStorage instance for the specified CockroachDB URL. // It assumes storage.AdminStorage is backed by the same CockroachDB database as well. func NewLogStorage(db *sql.DB, mf monitoring.MetricFactory) storage.LogStorage { if mf == nil { mf = monitoring.InertMetricFactory{} } return &crdbLogStorage{ admin: NewSQLAdminStorage(db), crdbTreeStorage: newTreeStorage(db), metricFactory: mf, } } func (m *crdbLogStorage) CheckDatabaseAccessible(ctx context.Context) error { return m.db.PingContext(ctx) } func (m *crdbLogStorage) getLeavesByMerkleHashStmt(ctx context.Context, num int, orderBySequence bool) (*sql.Stmt, error) { if orderBySequence { return m.getStmt(ctx, selectLeavesByMerkleHashOrderedBySequenceSQL, num, "?", "?") } return m.getStmt(ctx, selectLeavesByMerkleHashSQL, num, "?", "?") } func (m *crdbLogStorage) getLeavesByLeafIdentityHashStmt(ctx context.Context, num int) (*sql.Stmt, error) { return m.getStmt(ctx, selectLeavesByLeafIdentityHashSQL, num, "?", "?") } func (m *crdbLogStorage) GetActiveLogIDs(ctx context.Context) ([]int64, error) { // Include logs that are DRAINING in the active list as we're still // integrating leaves into them. rows, err := m.db.QueryContext( ctx, selectNonDeletedTreeIDByTypeAndStateSQL, trillian.TreeType_LOG.String(), trillian.TreeType_PREORDERED_LOG.String(), trillian.TreeState_ACTIVE.String(), trillian.TreeState_DRAINING.String()) if err != nil { return nil, err } defer func() { if err := rows.Close(); err != nil { klog.Errorf("rows.Close(): %v", err) } }() ids := []int64{} for rows.Next() { var treeID int64 if err := rows.Scan(&treeID); err != nil { return nil, err } ids = append(ids, treeID) } return ids, rows.Err() } func (m *crdbLogStorage) beginInternal(ctx context.Context, tree *trillian.Tree) (*logTreeTX, error) { once.Do(func() { createMetrics(m.metricFactory) }) stCache := cache.NewLogSubtreeCache(rfc6962.DefaultHasher) ttx, err := m.beginTreeTx(ctx, tree, rfc6962.DefaultHasher.Size(), stCache) if err != nil && err != storage.ErrTreeNeedsInit { return nil, err } ltx := &logTreeTX{ treeTX: ttx, ls: m, dequeued: make(map[string]dequeuedLeaf), } ltx.slr, ltx.readRev, err = ltx.fetchLatestRoot(ctx) if err == storage.ErrTreeNeedsInit { ltx.treeTX.writeRevision = 0 return ltx, err } else if err != nil { if err := ttx.Close(); err != nil { klog.Errorf("ttx.Close(): %v", err) } return nil, err } if err := ltx.root.UnmarshalBinary(ltx.slr.LogRoot); err != nil { if err := ttx.Close(); err != nil { klog.Errorf("ttx.Close(): %v", err) } return nil, err } ltx.treeTX.writeRevision = ltx.readRev + 1 return ltx, nil } // TODO(pavelkalinnikov): This and many other methods of this storage // implementation can leak a specific sql.ErrTxDone all the way to the client, // if the transaction is rolled back as a result of a canceled context. It must // return "generic" errors, and only log the specific ones for debugging. func (m *crdbLogStorage) ReadWriteTransaction(ctx context.Context, tree *trillian.Tree, f storage.LogTXFunc) error { tx, err := m.beginInternal(ctx, tree) if err != nil && err != storage.ErrTreeNeedsInit { return err } defer func() { if err := tx.Close(); err != nil { klog.Errorf("tx.Close(): %v", err) } }() if err := f(ctx, tx); err != nil { return err } return tx.Commit(ctx) } func (m *crdbLogStorage) AddSequencedLeaves(ctx context.Context, tree *trillian.Tree, leaves []*trillian.LogLeaf, timestamp time.Time) ([]*trillian.QueuedLogLeaf, error) { tx, err := m.beginInternal(ctx, tree) if tx != nil { // Ensure we don't leak the transaction. For example if we get an // ErrTreeNeedsInit from beginInternal() or if AddSequencedLeaves fails // below. defer func() { if err := tx.Close(); err != nil { klog.Errorf("tx.Close(): %v", err) } }() } if err != nil { return nil, err } res, err := tx.AddSequencedLeaves(ctx, leaves, timestamp) if err != nil { return nil, err } if err := tx.Commit(ctx); err != nil { return nil, err } return res, nil } func (m *crdbLogStorage) SnapshotForTree(ctx context.Context, tree *trillian.Tree) (storage.ReadOnlyLogTreeTX, error) { tx, err := m.beginInternal(ctx, tree) if err != nil && err != storage.ErrTreeNeedsInit { return nil, err } return tx, err } func (m *crdbLogStorage) QueueLeaves(ctx context.Context, tree *trillian.Tree, leaves []*trillian.LogLeaf, queueTimestamp time.Time) ([]*trillian.QueuedLogLeaf, error) { tx, err := m.beginInternal(ctx, tree) if tx != nil { // Ensure we don't leak the transaction. For example if we get an // ErrTreeNeedsInit from beginInternal() or if QueueLeaves fails // below. defer func() { if err := tx.Close(); err != nil { klog.Errorf("tx.Close(): %v", err) } }() } if err != nil { return nil, err } existing, err := tx.QueueLeaves(ctx, leaves, queueTimestamp) if err != nil { return nil, err } if err := tx.Commit(ctx); err != nil { return nil, err } ret := make([]*trillian.QueuedLogLeaf, len(leaves)) for i, e := range existing { if e != nil { ret[i] = &trillian.QueuedLogLeaf{ Leaf: e, Status: status.Newf(codes.AlreadyExists, "leaf already exists: %v", e.LeafIdentityHash).Proto(), } continue } ret[i] = &trillian.QueuedLogLeaf{Leaf: leaves[i]} } return ret, nil } type logTreeTX struct { treeTX ls *crdbLogStorage root types.LogRootV1 readRev int64 slr *trillian.SignedLogRoot dequeued map[string]dequeuedLeaf } // GetMerkleNodes returns the requested nodes at the read revision. func (t *logTreeTX) GetMerkleNodes(ctx context.Context, ids []compact.NodeID) ([]tree.Node, error) { t.treeTX.mu.Lock() defer t.treeTX.mu.Unlock() return t.subtreeCache.GetNodes(ids, t.getSubtreesAtRev(ctx, t.readRev)) } func (t *logTreeTX) DequeueLeaves(ctx context.Context, limit int, cutoffTime time.Time) ([]*trillian.LogLeaf, error) { t.treeTX.mu.Lock() defer t.treeTX.mu.Unlock() if t.treeType == trillian.TreeType_PREORDERED_LOG { // TODO(pavelkalinnikov): Optimize this by fetching only the required // fields of LogLeaf. We can avoid joining with LeafData table here. return t.getLeavesByRangeInternal(ctx, int64(t.root.TreeSize), int64(limit)) } start := time.Now() stx, err := t.tx.PrepareContext(ctx, selectQueuedLeavesSQL) if err != nil { klog.Warningf("Failed to prepare dequeue select: %s", err) return nil, err } defer func() { if err := stx.Close(); err != nil { klog.Errorf("stx.Close(): %v", err) } }() leaves := make([]*trillian.LogLeaf, 0, limit) rows, err := stx.QueryContext(ctx, t.treeID, cutoffTime.UnixNano(), limit) if err != nil { klog.Warningf("Failed to select rows for work: %s", err) return nil, err } defer func() { if err := rows.Close(); err != nil { klog.Errorf("rows.Close(): %v", err) } }() for rows.Next() { leaf, dqInfo, err := t.dequeueLeaf(rows) if err != nil { klog.Warningf("Error dequeuing leaf: %v", err) return nil, err } if len(leaf.LeafIdentityHash) != t.hashSizeBytes { return nil, errors.New("dequeued a leaf with incorrect hash size") } k := string(leaf.LeafIdentityHash) if _, ok := t.dequeued[k]; ok { // dupe, user probably called DequeueLeaves more than once. continue } t.dequeued[k] = dqInfo leaves = append(leaves, leaf) } if rows.Err() != nil { return nil, rows.Err() } label := labelForTX(t) observe(dequeueSelectLatency, time.Since(start), label) observe(dequeueLatency, time.Since(start), label) dequeuedCounter.Add(float64(len(leaves)), label) return leaves, nil } // sortLeavesForInsert returns a slice containing the passed in leaves sorted // by LeafIdentityHash, and paired with their original positions. // QueueLeaves and AddSequencedLeaves use this to make the order that LeafData // row locks are acquired deterministic and reduce the chance of deadlocks. func sortLeavesForInsert(leaves []*trillian.LogLeaf) []leafAndPosition { ordLeaves := make([]leafAndPosition, len(leaves)) for i, leaf := range leaves { ordLeaves[i] = leafAndPosition{leaf: leaf, idx: i} } sort.Sort(byLeafIdentityHashWithPosition(ordLeaves)) return ordLeaves } func (t *logTreeTX) QueueLeaves(ctx context.Context, leaves []*trillian.LogLeaf, queueTimestamp time.Time) ([]*trillian.LogLeaf, error) { t.treeTX.mu.Lock() defer t.treeTX.mu.Unlock() // Don't accept batches if any of the leaves are invalid. for _, leaf := range leaves { if len(leaf.LeafIdentityHash) != t.hashSizeBytes { return nil, fmt.Errorf("queued leaf must have a leaf ID hash of length %d", t.hashSizeBytes) } leaf.QueueTimestamp = timestamppb.New(queueTimestamp) if err := leaf.QueueTimestamp.CheckValid(); err != nil { return nil, fmt.Errorf("got invalid queue timestamp: %w", err) } } start := time.Now() label := labelForTX(t) ordLeaves := sortLeavesForInsert(leaves) existingCount := 0 existingLeaves := make([]*trillian.LogLeaf, len(leaves)) // CockroachDB/Postgres will cancel a transaction if an insert // statement is run with a duplicate key. This is not ideal for // QueueLeaves, as we want to detect these errors in-code // and return them as AlreadyExists errors and add metrics. // Thus, we use a SAVEPOINT and rollback on duplicates. const savepoint = "SAVEPOINT QueueLeaves" if _, err := t.tx.ExecContext(ctx, savepoint); err != nil { klog.Errorf("Error adding savepoint: %s", err) return nil, crdbToGRPC(err) } for _, ol := range ordLeaves { i, leaf := ol.idx, ol.leaf leafStart := time.Now() if err := leaf.QueueTimestamp.CheckValid(); err != nil { return nil, fmt.Errorf("got invalid queue timestamp: %w", err) } qTimestamp := leaf.QueueTimestamp.AsTime() if _, err := t.tx.ExecContext(ctx, savepoint); err != nil { klog.Errorf("Error updating savepoint: %s", err) return nil, crdbToGRPC(err) } _, err := t.tx.ExecContext(ctx, insertLeafDataSQL, t.treeID, leaf.LeafIdentityHash, leaf.LeafValue, leaf.ExtraData, qTimestamp.UnixNano()) insertDuration := time.Since(leafStart) observe(queueInsertLeafLatency, insertDuration, label) if isDuplicateErr(err) { // Remember the duplicate leaf, using the requested leaf for now. existingLeaves[i] = leaf existingCount++ queuedDupCounter.Inc(label) // Note: one must roll back since there are side-effects in the transaction // in crdb/postgres if _, err := t.tx.ExecContext(ctx, "ROLLBACK TO "+savepoint); err != nil { klog.Errorf("Error rolling back to savepoint: %s", err) return nil, crdbToGRPC(err) } continue } if err != nil { klog.Warningf("Error inserting %d into LeafData: %s", i, err) return nil, crdbToGRPC(err) } // Create the work queue entry args := []interface{}{ t.treeID, leaf.LeafIdentityHash, leaf.MerkleLeafHash, } args = append(args, queueArgs(t.treeID, leaf.LeafIdentityHash, qTimestamp)...) _, err = t.tx.ExecContext( ctx, insertUnsequencedEntrySQL, args..., ) if err != nil { klog.Warningf("Error inserting into Unsequenced: %s", err) return nil, crdbToGRPC(err) } leafDuration := time.Since(leafStart) observe(queueInsertEntryLatency, (leafDuration - insertDuration), label) } insertDuration := time.Since(start) observe(queueInsertLatency, insertDuration, label) queuedCounter.Add(float64(len(leaves)), label) if _, err := t.tx.ExecContext(ctx, "RELEASE "+savepoint); err != nil { klog.Errorf("Error releasing savepoint: %s", err) return nil, crdbToGRPC(err) } if existingCount == 0 { return existingLeaves, nil } // For existing leaves, we need to retrieve the contents. First collate the desired LeafIdentityHash values. var toRetrieve [][]byte for _, existing := range existingLeaves { if existing != nil { toRetrieve = append(toRetrieve, existing.LeafIdentityHash) } } results, err := t.getLeafDataByIdentityHash(ctx, toRetrieve) if err != nil { return nil, fmt.Errorf("failed to retrieve existing leaves: %v", err) } if len(results) != len(toRetrieve) { return nil, fmt.Errorf("failed to retrieve all existing leaves: got %d, want %d", len(results), len(toRetrieve)) } // Replace the requested leaves with the actual leaves. for i, requested := range existingLeaves { if requested == nil { continue } found := false for _, result := range results { if bytes.Equal(result.LeafIdentityHash, requested.LeafIdentityHash) { existingLeaves[i] = result found = true break } } if !found { return nil, fmt.Errorf("failed to find existing leaf for hash %x", requested.LeafIdentityHash) } } totalDuration := time.Since(start) readDuration := totalDuration - insertDuration observe(queueReadLatency, readDuration, label) observe(queueLatency, totalDuration, label) return existingLeaves, nil } func (t *logTreeTX) AddSequencedLeaves(ctx context.Context, leaves []*trillian.LogLeaf, timestamp time.Time) ([]*trillian.QueuedLogLeaf, error) { t.treeTX.mu.Lock() defer t.treeTX.mu.Unlock() res := make([]*trillian.QueuedLogLeaf, len(leaves)) ok := status.New(codes.OK, "OK").Proto() // Leaves in this transaction are inserted in two tables. For each leaf, if // one of the two inserts fails, we remove the side effect by rolling back to // a savepoint installed before the first insert of the two. const savepoint = "SAVEPOINT AddSequencedLeaves" if _, err := t.tx.ExecContext(ctx, savepoint); err != nil { klog.Errorf("Error adding savepoint: %s", err) return nil, crdbToGRPC(err) } // TODO(pavelkalinnikov): Consider performance implication of executing this // extra SAVEPOINT, especially for 1-entry batches. Optimize if necessary. // Note: LeafData inserts are presumably protected from deadlocks due to // sorting, but the order of the corresponding SequencedLeafData inserts // becomes indeterministic. However, in a typical case when leaves are // supplied in contiguous non-intersecting batches, the chance of having // circular dependencies between transactions is significantly lower. ordLeaves := sortLeavesForInsert(leaves) for _, ol := range ordLeaves { i, leaf := ol.idx, ol.leaf // This should fail on insert, but catch it early. if got, want := len(leaf.LeafIdentityHash), t.hashSizeBytes; got != want { return nil, status.Errorf(codes.FailedPrecondition, "leaves[%d] has incorrect hash size %d, want %d", i, got, want) } if _, err := t.tx.ExecContext(ctx, savepoint); err != nil { klog.Errorf("Error updating savepoint: %s", err) return nil, crdbToGRPC(err) } res[i] = &trillian.QueuedLogLeaf{Status: ok} // TODO(pavelkalinnikov): Measure latencies. _, err := t.tx.ExecContext(ctx, insertLeafDataSQL, t.treeID, leaf.LeafIdentityHash, leaf.LeafValue, leaf.ExtraData, timestamp.UnixNano()) // TODO(pavelkalinnikov): Detach PREORDERED_LOG integration latency metric. // TODO(pavelkalinnikov): Support opting out from duplicates detection. if isDuplicateErr(err) { res[i].Status = status.New(codes.FailedPrecondition, "conflicting LeafIdentityHash").Proto() // Note: one must roll back since there are side-effects in the transaction // in crdb/postgres if _, err := t.tx.ExecContext(ctx, "ROLLBACK TO "+savepoint); err != nil { klog.Errorf("Error rolling back to savepoint: %s", err) return nil, crdbToGRPC(err) } continue } else if err != nil { klog.Errorf("Error inserting leaves[%d] into LeafData: %s", i, err) return nil, crdbToGRPC(err) } _, err = t.tx.ExecContext(ctx, insertSequencedLeafSQL+valuesPlaceholder5, t.treeID, leaf.LeafIdentityHash, leaf.MerkleLeafHash, leaf.LeafIndex, 0) // TODO(pavelkalinnikov): Update IntegrateTimestamp on integrating the leaf. if isDuplicateErr(err) { res[i].Status = status.New(codes.FailedPrecondition, "conflicting LeafIndex").Proto() if _, err := t.tx.ExecContext(ctx, "ROLLBACK TO "+savepoint); err != nil { klog.Errorf("Error rolling back to savepoint: %s", err) return nil, crdbToGRPC(err) } } else if err != nil { klog.Errorf("Error inserting leaves[%d] into SequencedLeafData: %s", i, err) return nil, crdbToGRPC(err) } // TODO(pavelkalinnikov): Load LeafData for conflicting entries. } if _, err := t.tx.ExecContext(ctx, "RELEASE "+savepoint); err != nil { klog.Errorf("Error releasing savepoint: %s", err) return nil, crdbToGRPC(err) } return res, nil } func (t *logTreeTX) GetLeavesByRange(ctx context.Context, start, count int64) ([]*trillian.LogLeaf, error) { t.treeTX.mu.Lock() defer t.treeTX.mu.Unlock() return t.getLeavesByRangeInternal(ctx, start, count) } func (t *logTreeTX) getLeavesByRangeInternal(ctx context.Context, start, count int64) ([]*trillian.LogLeaf, error) { if count <= 0 { return nil, status.Errorf(codes.InvalidArgument, "invalid count %d, want > 0", count) } if start < 0 { return nil, status.Errorf(codes.InvalidArgument, "invalid start %d, want >= 0", start) } if t.treeType == trillian.TreeType_LOG { treeSize := int64(t.root.TreeSize) if treeSize <= 0 { return nil, status.Errorf(codes.OutOfRange, "empty tree") } else if start >= treeSize { return nil, status.Errorf(codes.OutOfRange, "invalid start %d, want < TreeSize(%d)", start, treeSize) } // Ensure no entries queried/returned beyond the tree. if maxCount := treeSize - start; count > maxCount { count = maxCount } } // TODO(pavelkalinnikov): Further clip `count` to a safe upper bound like 64k. args := []interface{}{start, start + count, t.treeID} rows, err := t.tx.QueryContext(ctx, selectLeavesByRangeSQL, args...) if err != nil { klog.Warningf("Failed to get leaves by range: %s", err) return nil, err } defer func() { if err := rows.Close(); err != nil { klog.Errorf("rows.Close(): %v", err) } }() ret := make([]*trillian.LogLeaf, 0, count) for wantIndex := start; rows.Next(); wantIndex++ { leaf := &trillian.LogLeaf{} var qTimestamp, iTimestamp int64 if err := rows.Scan( &leaf.MerkleLeafHash, &leaf.LeafIdentityHash, &leaf.LeafValue, &leaf.LeafIndex, &leaf.ExtraData, &qTimestamp, &iTimestamp); err != nil { klog.Warningf("Failed to scan merkle leaves: %s", err) return nil, err } if leaf.LeafIndex != wantIndex { if wantIndex < int64(t.root.TreeSize) { return nil, fmt.Errorf("got unexpected index %d, want %d", leaf.LeafIndex, wantIndex) } break } leaf.QueueTimestamp = timestamppb.New(time.Unix(0, qTimestamp)) if err := leaf.QueueTimestamp.CheckValid(); err != nil { return nil, fmt.Errorf("got invalid queue timestamp: %w", err) } leaf.IntegrateTimestamp = timestamppb.New(time.Unix(0, iTimestamp)) if err := leaf.IntegrateTimestamp.CheckValid(); err != nil { return nil, fmt.Errorf("got invalid integrate timestamp: %w", err) } ret = append(ret, leaf) } if err := rows.Err(); err != nil { klog.Warningf("Failed to read returned leaves: %s", err) return nil, err } return ret, nil } func (t *logTreeTX) GetLeavesByHash(ctx context.Context, leafHashes [][]byte, orderBySequence bool) ([]*trillian.LogLeaf, error) { t.treeTX.mu.Lock() defer t.treeTX.mu.Unlock() tmpl, err := t.ls.getLeavesByMerkleHashStmt(ctx, len(leafHashes), orderBySequence) if err != nil { return nil, err } return t.getLeavesByHashInternal(ctx, leafHashes, tmpl, "merkle") } // getLeafDataByIdentityHash retrieves leaf data by LeafIdentityHash, returned // as a slice of LogLeaf objects for convenience. However, note that the // returned LogLeaf objects will not have a valid MerkleLeafHash, LeafIndex, or IntegrateTimestamp. func (t *logTreeTX) getLeafDataByIdentityHash(ctx context.Context, leafHashes [][]byte) ([]*trillian.LogLeaf, error) { tmpl, err := t.ls.getLeavesByLeafIdentityHashStmt(ctx, len(leafHashes)) if err != nil { return nil, err } return t.getLeavesByHashInternal(ctx, leafHashes, tmpl, "leaf-identity") } func (t *logTreeTX) LatestSignedLogRoot(ctx context.Context) (*trillian.SignedLogRoot, error) { t.treeTX.mu.Lock() defer t.treeTX.mu.Unlock() if t.slr == nil { return nil, storage.ErrTreeNeedsInit } return t.slr, nil } // fetchLatestRoot reads the latest root and the revision from the DB. func (t *logTreeTX) fetchLatestRoot(ctx context.Context) (*trillian.SignedLogRoot, int64, error) { var timestamp, treeSize, treeRevision int64 var rootHash, rootSignatureBytes []byte if err := t.tx.QueryRowContext( ctx, selectLatestSignedLogRootSQL, t.treeID).Scan( ×tamp, &treeSize, &rootHash, &treeRevision, &rootSignatureBytes, ); err == sql.ErrNoRows { // It's possible there are no roots for this tree yet return nil, 0, storage.ErrTreeNeedsInit } // Put logRoot back together. Fortunately LogRoot has a deterministic serialization. logRoot, err := (&types.LogRootV1{ RootHash: rootHash, TimestampNanos: uint64(timestamp), TreeSize: uint64(treeSize), }).MarshalBinary() if err != nil { return nil, 0, err } return &trillian.SignedLogRoot{LogRoot: logRoot}, treeRevision, nil } func (t *logTreeTX) StoreSignedLogRoot(ctx context.Context, root *trillian.SignedLogRoot) error { t.treeTX.mu.Lock() defer t.treeTX.mu.Unlock() var logRoot types.LogRootV1 if err := logRoot.UnmarshalBinary(root.LogRoot); err != nil { klog.Warningf("Failed to parse log root: %x %v", root.LogRoot, err) return err } if len(logRoot.Metadata) != 0 { return fmt.Errorf("unimplemented: crdb storage does not support log root metadata") } res, err := t.tx.ExecContext( ctx, insertTreeHeadSQL, t.treeID, logRoot.TimestampNanos, logRoot.TreeSize, logRoot.RootHash, t.treeTX.writeRevision, []byte{}) if err != nil { klog.Warningf("Failed to store signed root: %s", err) } return checkResultOkAndRowCountIs(res, err, 1) } func (t *logTreeTX) getLeavesByHashInternal(ctx context.Context, leafHashes [][]byte, tmpl *sql.Stmt, desc string) ([]*trillian.LogLeaf, error) { stx := t.tx.StmtContext(ctx, tmpl) defer func() { if err := stx.Close(); err != nil { klog.Errorf("stx.Close(): %v", err) } }() var args []interface{} for _, hash := range leafHashes { args = append(args, []byte(hash)) } args = append(args, t.treeID) rows, err := stx.QueryContext(ctx, args...) if err != nil { klog.Warningf("Query() %s hash = %v", desc, err) return nil, err } defer func() { if err := rows.Close(); err != nil { klog.Errorf("rows.Close(): %v", err) } }() // The tree could include duplicates so we don't know how many results will be returned var ret []*trillian.LogLeaf for rows.Next() { leaf := &trillian.LogLeaf{} // We might be using a LEFT JOIN in our statement, so leaves which are // queued but not yet integrated will have a NULL IntegrateTimestamp // when there's no corresponding entry in SequencedLeafData, even though // the table definition forbids that, so we use a nullable type here and // check its validity below. var integrateTS sql.NullInt64 var queueTS int64 if err := rows.Scan(&leaf.MerkleLeafHash, &leaf.LeafIdentityHash, &leaf.LeafValue, &leaf.LeafIndex, &leaf.ExtraData, &queueTS, &integrateTS); err != nil { klog.Warningf("LogID: %d Scan() %s = %s", t.treeID, desc, err) return nil, err } leaf.QueueTimestamp = timestamppb.New(time.Unix(0, queueTS)) if err := leaf.QueueTimestamp.CheckValid(); err != nil { return nil, fmt.Errorf("got invalid queue timestamp: %w", err) } if integrateTS.Valid { leaf.IntegrateTimestamp = timestamppb.New(time.Unix(0, integrateTS.Int64)) if err := leaf.IntegrateTimestamp.CheckValid(); err != nil { return nil, fmt.Errorf("got invalid integrate timestamp: %w", err) } } if got, want := len(leaf.MerkleLeafHash), t.hashSizeBytes; got != want { return nil, fmt.Errorf("LogID: %d Scanned leaf %s does not have hash length %d, got %d", t.treeID, desc, want, got) } ret = append(ret, leaf) } if err := rows.Err(); err != nil { klog.Warningf("Failed to read returned leaves: %s", err) return nil, err } return ret, nil } // leafAndPosition records original position before sort. type leafAndPosition struct { leaf *trillian.LogLeaf idx int } // byLeafIdentityHashWithPosition allows sorting (as above), but where we need // to remember the original position type byLeafIdentityHashWithPosition []leafAndPosition func (l byLeafIdentityHashWithPosition) Len() int { return len(l) } func (l byLeafIdentityHashWithPosition) Swap(i, j int) { l[i], l[j] = l[j], l[i] } func (l byLeafIdentityHashWithPosition) Less(i, j int) bool { return bytes.Compare(l[i].leaf.LeafIdentityHash, l[j].leaf.LeafIdentityHash) == -1 } trillian-1.6.1/storage/crdb/log_storage_test.go000066400000000000000000000663071466362047600216400ustar00rootroot00000000000000// Copyright 2016 Trillian Authors // // 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 crdb import ( "bytes" "context" "crypto/sha256" "database/sql" "fmt" "sort" "testing" "time" "github.com/google/go-cmp/cmp" "github.com/google/trillian" "github.com/google/trillian/integration/storagetest" "github.com/google/trillian/storage" "github.com/google/trillian/storage/testonly" "github.com/google/trillian/types" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/timestamppb" _ "github.com/lib/pq" // Register the Postgres driver. ) // Must be 32 bytes to match sha256 length if it was a real hash var ( dummyHash = []byte("hashxxxxhashxxxxhashxxxxhashxxxx") dummyRawHash = []byte("xxxxhashxxxxhashxxxxhashxxxxhash") dummyHash2 = []byte("HASHxxxxhashxxxxhashxxxxhashxxxx") ) // Time we will queue all leaves at var fakeQueueTime = time.Date(2016, 11, 10, 15, 16, 27, 0, time.UTC) // Time we will integrate all leaves at var fakeIntegrateTime = time.Date(2016, 11, 10, 15, 16, 30, 0, time.UTC) // Time we'll request for guard cutoff in tests that don't test this (should include all above) var fakeDequeueCutoffTime = time.Date(2016, 11, 10, 15, 16, 30, 0, time.UTC) // Used for tests involving extra data var someExtraData = []byte("Some extra data") const ( leavesToInsert = 5 sequenceNumber int64 = 237 ) // Tests that access the db should each use a distinct log ID to prevent lock contention when // run in parallel or race conditions / unexpected interactions. Tests that pass should hold // no locks afterwards. func createFakeLeaf(ctx context.Context, db *sql.DB, logID int64, rawHash, hash, data, extraData []byte, seq int64, t *testing.T) *trillian.LogLeaf { t.Helper() queuedAtNanos := fakeQueueTime.UnixNano() integratedAtNanos := fakeIntegrateTime.UnixNano() _, err := db.ExecContext(ctx, "INSERT INTO LeafData(TreeId, LeafIdentityHash, LeafValue, ExtraData, QueueTimestampNanos) VALUES($1,$2,$3,$4,$5)", logID, rawHash, data, extraData, queuedAtNanos) _, err2 := db.ExecContext(ctx, "INSERT INTO SequencedLeafData(TreeId, SequenceNumber, LeafIdentityHash, MerkleLeafHash, IntegrateTimestampNanos) VALUES($1,$2,$3,$4,$5)", logID, seq, rawHash, hash, integratedAtNanos) if err != nil || err2 != nil { t.Fatalf("Failed to create test leaves: %v %v", err, err2) } queueTimestamp := timestamppb.New(fakeQueueTime) integrateTimestamp := timestamppb.New(fakeIntegrateTime) return &trillian.LogLeaf{ MerkleLeafHash: hash, LeafValue: data, ExtraData: extraData, LeafIndex: seq, LeafIdentityHash: rawHash, QueueTimestamp: queueTimestamp, IntegrateTimestamp: integrateTimestamp, } } func checkLeafContents(leaf *trillian.LogLeaf, seq int64, rawHash, hash, data, extraData []byte, t *testing.T) { t.Helper() if got, want := leaf.MerkleLeafHash, hash; !bytes.Equal(got, want) { t.Fatalf("Wrong leaf hash in returned leaf got\n%v\nwant:\n%v", got, want) } if got, want := leaf.LeafIdentityHash, rawHash; !bytes.Equal(got, want) { t.Fatalf("Wrong raw leaf hash in returned leaf got\n%v\nwant:\n%v", got, want) } if got, want := seq, leaf.LeafIndex; got != want { t.Fatalf("Bad sequence number in returned leaf got: %d, want:%d", got, want) } if got, want := leaf.LeafValue, data; !bytes.Equal(got, want) { t.Fatalf("Unxpected data in returned leaf. got:\n%v\nwant:\n%v", got, want) } if got, want := leaf.ExtraData, extraData; !bytes.Equal(got, want) { t.Fatalf("Unxpected data in returned leaf. got:\n%v\nwant:\n%v", got, want) } iTime := leaf.IntegrateTimestamp.AsTime() if got, want := iTime.UnixNano(), fakeIntegrateTime.UnixNano(); got != want { t.Errorf("Wrong IntegrateTimestamp: got %v, want %v", got, want) } } func TestLogSuite(t *testing.T) { t.Parallel() storageFactory := func(context.Context, *testing.T) (storage.LogStorage, storage.AdminStorage) { handle := openTestDBOrDie(t) return NewLogStorage(handle.db, nil), NewSQLAdminStorage(handle.db) } storagetest.RunLogStorageTests(t, storageFactory) } func TestQueueDuplicateLeaf(t *testing.T) { t.Parallel() ctx := context.Background() handle := openTestDBOrDie(t) as := NewSQLAdminStorage(handle.db) tree := mustCreateTree(ctx, t, as, testonly.LogTree) s := NewLogStorage(handle.db, nil) mustSignAndStoreLogRoot(ctx, t, s, tree, 0) count := 15 leaves := createTestLeaves(int64(count), 10) leaves2 := createTestLeaves(int64(count), 12) leaves3 := createTestLeaves(3, 100) // Note that tests accumulate queued leaves on top of each other. tests := []struct { desc string leaves []*trillian.LogLeaf want []*trillian.LogLeaf }{ { desc: "[10, 11, 12, ...]", leaves: leaves, want: make([]*trillian.LogLeaf, count), }, { desc: "[12, 13, 14, ...] so first (count-2) are duplicates", leaves: leaves2, want: append(leaves[2:], nil, nil), }, { desc: "[10, 100, 11, 101, 102] so [dup, new, dup, new, dup]", leaves: []*trillian.LogLeaf{leaves[0], leaves3[0], leaves[1], leaves3[1], leaves[2]}, want: []*trillian.LogLeaf{leaves[0], nil, leaves[1], nil, leaves[2]}, }, } for _, test := range tests { // NOTE(jaosorior): These tests can't be parallelized as they // depend on each others' leaves. t.Run(test.desc, func(t *testing.T) { existing, err := s.QueueLeaves(ctx, tree, test.leaves, fakeQueueTime) if err != nil { t.Fatalf("Failed to queue leaves: %v", err) } if len(existing) != len(test.want) { t.Fatalf("|QueueLeaves()|=%d; want %d", len(existing), len(test.want)) } for i, want := range test.want { got := existing[i] if want == nil { if got.Status != nil { t.Errorf("QueueLeaves()[%d].Code: %v; want %v", i, got, want) } return } if got == nil { t.Fatalf("QueueLeaves()[%d]=nil; want non-nil", i) } else if !bytes.Equal(got.Leaf.LeafIdentityHash, want.LeafIdentityHash) { t.Fatalf("QueueLeaves()[%d].LeafIdentityHash=%x; want %x", i, got.Leaf.LeafIdentityHash, want.LeafIdentityHash) } } }) } } func TestQueueLeaves(t *testing.T) { t.Parallel() ctx := context.Background() handle := openTestDBOrDie(t) as := NewSQLAdminStorage(handle.db) tree := mustCreateTree(ctx, t, as, testonly.LogTree) s := NewLogStorage(handle.db, nil) mustSignAndStoreLogRoot(ctx, t, s, tree, 0) leaves := createTestLeaves(leavesToInsert, 20) if _, err := s.QueueLeaves(ctx, tree, leaves, fakeQueueTime); err != nil { t.Fatalf("Failed to queue leaves: %v", err) } // Should see the leaves in the database. There is no API to read from the unsequenced data. var count int if err := handle.db.QueryRowContext(ctx, "SELECT COUNT(*) FROM Unsequenced WHERE TreeID=$1", tree.TreeId).Scan(&count); err != nil { t.Fatalf("Could not query row count: %v", err) } if leavesToInsert != count { t.Fatalf("Expected %d unsequenced rows but got: %d", leavesToInsert, count) } // Additional check on timestamp being set correctly in the database var queueTimestamp int64 if err := handle.db.QueryRowContext(ctx, "SELECT DISTINCT QueueTimestampNanos FROM Unsequenced WHERE TreeID=$1", tree.TreeId).Scan(&queueTimestamp); err != nil { t.Fatalf("Could not query timestamp: %v", err) } if got, want := queueTimestamp, fakeQueueTime.UnixNano(); got != want { t.Fatalf("Incorrect queue timestamp got: %d want: %d", got, want) } } func TestQueueLeavesDuplicateBigBatch(t *testing.T) { t.Parallel() ctx := context.Background() handle := openTestDBOrDie(t) as := NewSQLAdminStorage(handle.db) tree := mustCreateTree(ctx, t, as, testonly.LogTree) s := NewLogStorage(handle.db, nil) mustSignAndStoreLogRoot(ctx, t, s, tree, 0) const leafCount = 999 + 1 leaves := createTestLeaves(leafCount, 20) if _, err := s.QueueLeaves(ctx, tree, leaves, fakeQueueTime); err != nil { t.Fatalf("Failed to queue leaves: %v", err) } if _, err := s.QueueLeaves(ctx, tree, leaves, fakeQueueTime); err != nil { t.Fatalf("Failed to queue leaves: %v", err) } // Should see the leaves in the database. There is no API to read from the unsequenced data. var count int if err := handle.db.QueryRowContext(ctx, "SELECT COUNT(*) FROM Unsequenced WHERE TreeID=$1", tree.TreeId).Scan(&count); err != nil { t.Fatalf("Could not query row count: %v", err) } if leafCount != count { t.Fatalf("Expected %d unsequenced rows but got: %d", leafCount, count) } } // ----------------------------------------------------------------------------- func TestDequeueLeavesHaveQueueTimestamp(t *testing.T) { t.Parallel() ctx := context.Background() handle := openTestDBOrDie(t) as := NewSQLAdminStorage(handle.db) tree := mustCreateTree(ctx, t, as, testonly.LogTree) s := NewLogStorage(handle.db, nil) mustSignAndStoreLogRoot(ctx, t, s, tree, 0) leaves := createTestLeaves(leavesToInsert, 20) if _, err := s.QueueLeaves(ctx, tree, leaves, fakeDequeueCutoffTime); err != nil { t.Fatalf("Failed to queue leaves: %v", err) } { // Now try to dequeue them runLogTX(s, tree, t, func(ctx context.Context, tx2 storage.LogTreeTX) error { leaves2, err := tx2.DequeueLeaves(ctx, 99, fakeDequeueCutoffTime) if err != nil { t.Fatalf("Failed to dequeue leaves: %v", err) } if len(leaves2) != leavesToInsert { t.Fatalf("Dequeued %d leaves but expected to get %d", len(leaves2), leavesToInsert) } ensureLeavesHaveQueueTimestamp(t, leaves2, fakeDequeueCutoffTime) return nil }) } } // Queues leaves and attempts to dequeue before the guard cutoff allows it. This should // return nothing. Then retry with an inclusive guard cutoff and ensure the leaves // are returned. func TestDequeueLeavesGuardInterval(t *testing.T) { t.Parallel() ctx := context.Background() handle := openTestDBOrDie(t) as := NewSQLAdminStorage(handle.db) tree := mustCreateTree(ctx, t, as, testonly.LogTree) s := NewLogStorage(handle.db, nil) mustSignAndStoreLogRoot(ctx, t, s, tree, 0) leaves := createTestLeaves(leavesToInsert, 20) if _, err := s.QueueLeaves(ctx, tree, leaves, fakeQueueTime); err != nil { t.Fatalf("Failed to queue leaves: %v", err) } { // Now try to dequeue them using a cutoff that means we should get none runLogTX(s, tree, t, func(ctx context.Context, tx2 storage.LogTreeTX) error { leaves2, err := tx2.DequeueLeaves(ctx, 99, fakeQueueTime.Add(-time.Second)) if err != nil { t.Fatalf("Failed to dequeue leaves: %v", err) } if len(leaves2) != 0 { t.Fatalf("Dequeued %d leaves when they all should be in guard interval", len(leaves2)) } // Try to dequeue again using a cutoff that should include them leaves2, err = tx2.DequeueLeaves(ctx, 99, fakeQueueTime.Add(time.Second)) if err != nil { t.Fatalf("Failed to dequeue leaves: %v", err) } if len(leaves2) != leavesToInsert { t.Fatalf("Dequeued %d leaves but expected to get %d", len(leaves2), leavesToInsert) } ensureAllLeavesDistinct(leaves2, t) return nil }) } } func TestDequeueLeavesTimeOrdering(t *testing.T) { t.Parallel() // Queue two small batches of leaves at different timestamps. Do two separate dequeue // transactions and make sure the returned leaves are respecting the time ordering of the // queue. ctx := context.Background() handle := openTestDBOrDie(t) as := NewSQLAdminStorage(handle.db) tree := mustCreateTree(ctx, t, as, testonly.LogTree) s := NewLogStorage(handle.db, nil) mustSignAndStoreLogRoot(ctx, t, s, tree, 0) batchSize := 2 leaves := createTestLeaves(int64(batchSize), 0) leaves2 := createTestLeaves(int64(batchSize), int64(batchSize)) if _, err := s.QueueLeaves(ctx, tree, leaves, fakeQueueTime); err != nil { t.Fatalf("QueueLeaves(1st batch) = %v", err) } // These are one second earlier so should be dequeued first if _, err := s.QueueLeaves(ctx, tree, leaves2, fakeQueueTime.Add(-time.Second)); err != nil { t.Fatalf("QueueLeaves(2nd batch) = %v", err) } { // Now try to dequeue two leaves and we should get the second batch runLogTX(s, tree, t, func(ctx context.Context, tx2 storage.LogTreeTX) error { dequeue1, err := tx2.DequeueLeaves(ctx, batchSize, fakeQueueTime) if err != nil { t.Fatalf("DequeueLeaves(1st) = %v", err) } if got, want := len(dequeue1), batchSize; got != want { t.Fatalf("Dequeue count mismatch (1st) got: %d, want: %d", got, want) } ensureAllLeavesDistinct(dequeue1, t) // Ensure this is the second batch queued by comparing leaf hashes (must be distinct as // the leaf data was). if !leafInBatch(dequeue1[0], leaves2) || !leafInBatch(dequeue1[1], leaves2) { t.Fatalf("Got leaf from wrong batch (1st dequeue): %v", dequeue1) } iTimestamp := timestamppb.Now() for i, l := range dequeue1 { l.IntegrateTimestamp = iTimestamp l.LeafIndex = int64(i) } if err := tx2.UpdateSequencedLeaves(ctx, dequeue1); err != nil { t.Fatalf("UpdateSequencedLeaves(): %v", err) } return nil }) // Try to dequeue again and we should get the batch that was queued first, though at a later time runLogTX(s, tree, t, func(ctx context.Context, tx3 storage.LogTreeTX) error { dequeue2, err := tx3.DequeueLeaves(ctx, batchSize, fakeQueueTime) if err != nil { t.Fatalf("DequeueLeaves(2nd) = %v", err) } if got, want := len(dequeue2), batchSize; got != want { t.Fatalf("Dequeue count mismatch (2nd) got: %d, want: %d", got, want) } ensureAllLeavesDistinct(dequeue2, t) // Ensure this is the first batch by comparing leaf hashes. if !leafInBatch(dequeue2[0], leaves) || !leafInBatch(dequeue2[1], leaves) { t.Fatalf("Got leaf from wrong batch (2nd dequeue): %v", dequeue2) } return nil }) } } func TestGetLeavesByHashNotPresent(t *testing.T) { t.Parallel() ctx := context.Background() handle := openTestDBOrDie(t) as := NewSQLAdminStorage(handle.db) tree := mustCreateTree(ctx, t, as, testonly.LogTree) s := NewLogStorage(handle.db, nil) runLogTX(s, tree, t, func(ctx context.Context, tx storage.LogTreeTX) error { hashes := [][]byte{[]byte("thisdoesn'texist")} leaves, err := tx.GetLeavesByHash(ctx, hashes, false) if err != nil { t.Fatalf("Error getting leaves by hash: %v", err) } if len(leaves) != 0 { t.Fatalf("Expected no leaves returned but got %d", len(leaves)) } return nil }) } func TestGetLeavesByHash(t *testing.T) { t.Parallel() ctx := context.Background() // Create fake leaf as if it had been sequenced handle := openTestDBOrDie(t) as := NewSQLAdminStorage(handle.db) tree := mustCreateTree(ctx, t, as, testonly.LogTree) s := NewLogStorage(handle.db, nil) data := []byte("some data") createFakeLeaf(ctx, handle.db, tree.TreeId, dummyRawHash, dummyHash, data, someExtraData, sequenceNumber, t) runLogTX(s, tree, t, func(ctx context.Context, tx storage.LogTreeTX) error { hashes := [][]byte{dummyHash} leaves, err := tx.GetLeavesByHash(ctx, hashes, false) if err != nil { t.Fatalf("Unexpected error getting leaf by hash: %v", err) } if len(leaves) != 1 { t.Fatalf("Got %d leaves but expected one", len(leaves)) } checkLeafContents(leaves[0], sequenceNumber, dummyRawHash, dummyHash, data, someExtraData, t) return nil }) } func TestGetLeavesByHashBigBatch(t *testing.T) { t.Parallel() ctx := context.Background() // Create fake leaf as if it had been sequenced handle := openTestDBOrDie(t) as := NewSQLAdminStorage(handle.db) tree := mustCreateTree(ctx, t, as, testonly.LogTree) s := NewLogStorage(handle.db, nil) const leafCount = 999 + 1 hashes := make([][]byte, leafCount) for i := 0; i < leafCount; i++ { data := []byte(fmt.Sprintf("data %d", i)) hash := sha256.Sum256(data) hashes[i] = hash[:] createFakeLeaf(ctx, handle.db, tree.TreeId, hash[:], hash[:], data, someExtraData, sequenceNumber+int64(i), t) } runLogTX(s, tree, t, func(ctx context.Context, tx storage.LogTreeTX) error { leaves, err := tx.GetLeavesByHash(ctx, hashes, false) if err != nil { t.Fatalf("Unexpected error getting leaf by hash: %v", err) } if got, want := len(leaves), leafCount; got != want { t.Fatalf("Got %d leaves, expected %d", got, want) } return nil }) } func TestGetLeafDataByIdentityHash(t *testing.T) { t.Parallel() ctx := context.Background() // Create fake leaf as if it had been sequenced handle := openTestDBOrDie(t) as := NewSQLAdminStorage(handle.db) tree := mustCreateTree(ctx, t, as, testonly.LogTree) s := NewLogStorage(handle.db, nil) data := []byte("some data") leaf := createFakeLeaf(ctx, handle.db, tree.TreeId, dummyRawHash, dummyHash, data, someExtraData, sequenceNumber, t) leaf.LeafIndex = -1 leaf.MerkleLeafHash = []byte(dummyMerkleLeafHash) leaf2 := createFakeLeaf(ctx, handle.db, tree.TreeId, dummyHash2, dummyHash2, data, someExtraData, sequenceNumber+1, t) leaf2.LeafIndex = -1 leaf2.MerkleLeafHash = []byte(dummyMerkleLeafHash) tests := []struct { hashes [][]byte want []*trillian.LogLeaf }{ { hashes: [][]byte{dummyRawHash}, want: []*trillian.LogLeaf{leaf}, }, { hashes: [][]byte{{0x01, 0x02}}, }, { hashes: [][]byte{ dummyRawHash, {0x01, 0x02}, dummyHash2, {0x01, 0x02}, }, // Note: leaves not necessarily returned in order requested. want: []*trillian.LogLeaf{leaf2, leaf}, }, } for i, test := range tests { test := test t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { t.Parallel() runLogTX(s, tree, t, func(ctx context.Context, tx storage.LogTreeTX) error { leaves, err := tx.(*logTreeTX).getLeafDataByIdentityHash(ctx, test.hashes) if err != nil { t.Fatalf("getLeavesByIdentityHash(_) = (_,%v); want (_,nil)", err) } if len(leaves) != len(test.want) { t.Fatalf("getLeavesByIdentityHash(_) = (|%d|,nil); want (|%d|,nil)", len(leaves), len(test.want)) } leavesEquivalent(t, leaves, test.want) return nil }) }) } } func leavesEquivalent(t *testing.T, gotLeaves, wantLeaves []*trillian.LogLeaf) { t.Helper() want := make(map[string]*trillian.LogLeaf) for _, w := range wantLeaves { k := sha256.Sum256([]byte(w.String())) want[string(k[:])] = w } got := make(map[string]*trillian.LogLeaf) for _, g := range gotLeaves { k := sha256.Sum256([]byte(g.String())) got[string(k[:])] = g } if diff := cmp.Diff(want, got, cmp.Comparer(proto.Equal)); diff != "" { t.Errorf("leaves not equivalent: diff -want,+got:\n%v", diff) } } // ----------------------------------------------------------------------------- func TestLatestSignedRootNoneWritten(t *testing.T) { t.Parallel() ctx := context.Background() handle := openTestDBOrDie(t) as := NewSQLAdminStorage(handle.db) tree := mustCreateTree(ctx, t, as, testonly.LogTree) s := NewLogStorage(handle.db, nil) tx, err := s.SnapshotForTree(ctx, tree) if err != storage.ErrTreeNeedsInit { t.Fatalf("SnapshotForTree gave %v, want %v", err, storage.ErrTreeNeedsInit) } commit(ctx, tx, t) } func SignLogRoot(root *types.LogRootV1) (*trillian.SignedLogRoot, error) { logRoot, err := root.MarshalBinary() if err != nil { return nil, err } return &trillian.SignedLogRoot{LogRoot: logRoot}, nil } func TestLatestSignedLogRoot(t *testing.T) { t.Parallel() ctx := context.Background() handle := openTestDBOrDie(t) as := NewSQLAdminStorage(handle.db) tree := mustCreateTree(ctx, t, as, testonly.LogTree) s := NewLogStorage(handle.db, nil) root, err := SignLogRoot(&types.LogRootV1{ TimestampNanos: 98765, TreeSize: 16, RootHash: []byte(dummyHash), }) if err != nil { t.Fatalf("SignLogRoot(): %v", err) } runLogTX(s, tree, t, func(ctx context.Context, tx storage.LogTreeTX) error { if err := tx.StoreSignedLogRoot(ctx, root); err != nil { t.Fatalf("Failed to store signed root: %v", err) } return nil }) { runLogTX(s, tree, t, func(ctx context.Context, tx2 storage.LogTreeTX) error { root2, err := tx2.LatestSignedLogRoot(ctx) if err != nil { t.Fatalf("Failed to read back new log root: %v", err) } if !proto.Equal(root, root2) { t.Fatalf("Root round trip failed: <%v> and: <%v>", root, root2) } return nil }) } } func TestDuplicateSignedLogRoot(t *testing.T) { t.Parallel() ctx := context.Background() handle := openTestDBOrDie(t) as := NewSQLAdminStorage(handle.db) tree := mustCreateTree(ctx, t, as, testonly.LogTree) s := NewLogStorage(handle.db, nil) root, err := SignLogRoot(&types.LogRootV1{ TimestampNanos: 98765, TreeSize: 16, RootHash: []byte(dummyHash), }) if err != nil { t.Fatalf("SignLogRoot(): %v", err) } runLogTX(s, tree, t, func(ctx context.Context, tx storage.LogTreeTX) error { if err := tx.StoreSignedLogRoot(ctx, root); err != nil { t.Fatalf("Failed to store signed root: %v", err) } // Shouldn't be able to do it again if err := tx.StoreSignedLogRoot(ctx, root); err == nil { t.Fatal("Allowed duplicate signed root") } return nil }) } func TestLogRootUpdate(t *testing.T) { t.Parallel() ctx := context.Background() // Write two roots for a log and make sure the one with the newest timestamp supersedes handle := openTestDBOrDie(t) as := NewSQLAdminStorage(handle.db) tree := mustCreateTree(ctx, t, as, testonly.LogTree) s := NewLogStorage(handle.db, nil) root, err := SignLogRoot(&types.LogRootV1{ TimestampNanos: 98765, TreeSize: 16, RootHash: []byte(dummyHash), }) if err != nil { t.Fatalf("SignLogRoot(): %v", err) } root2, err := SignLogRoot(&types.LogRootV1{ TimestampNanos: 98766, TreeSize: 16, RootHash: []byte(dummyHash), }) if err != nil { t.Fatalf("SignLogRoot(): %v", err) } runLogTX(s, tree, t, func(ctx context.Context, tx storage.LogTreeTX) error { return tx.StoreSignedLogRoot(ctx, root) }) runLogTX(s, tree, t, func(ctx context.Context, tx storage.LogTreeTX) error { return tx.StoreSignedLogRoot(ctx, root2) }) runLogTX(s, tree, t, func(ctx context.Context, tx2 storage.LogTreeTX) error { root3, err := tx2.LatestSignedLogRoot(ctx) if err != nil { t.Fatalf("Failed to read back new log root: %v", err) } if !proto.Equal(root2, root3) { t.Fatalf("Root round trip failed: <%v> and: <%v>", root, root2) } return nil }) } func TestGetActiveLogIDs(t *testing.T) { t.Parallel() ctx := context.Background() handle := openTestDBOrDie(t) admin := NewSQLAdminStorage(handle.db) // Create a few test trees log1 := proto.Clone(testonly.LogTree).(*trillian.Tree) log2 := proto.Clone(testonly.LogTree).(*trillian.Tree) log3 := proto.Clone(testonly.PreorderedLogTree).(*trillian.Tree) drainingLog := proto.Clone(testonly.LogTree).(*trillian.Tree) frozenLog := proto.Clone(testonly.LogTree).(*trillian.Tree) deletedLog := proto.Clone(testonly.LogTree).(*trillian.Tree) for _, tree := range []**trillian.Tree{&log1, &log2, &log3, &drainingLog, &frozenLog, &deletedLog} { newTree, err := storage.CreateTree(ctx, admin, *tree) if err != nil { t.Fatalf("CreateTree(%+v) returned err = %v", tree, err) } *tree = newTree } // FROZEN is not a valid initial state, so we have to update it separately. if _, err := storage.UpdateTree(ctx, admin, frozenLog.TreeId, func(t *trillian.Tree) { t.TreeState = trillian.TreeState_FROZEN }); err != nil { t.Fatalf("UpdateTree() returned err = %v", err) } // DRAINING is not a valid initial state, so we have to update it separately. if _, err := storage.UpdateTree(ctx, admin, drainingLog.TreeId, func(t *trillian.Tree) { t.TreeState = trillian.TreeState_DRAINING }); err != nil { t.Fatalf("UpdateTree() returned err = %v", err) } // Update deleted trees accordingly updateDeletedStmt, err := handle.db.PrepareContext(ctx, "UPDATE Trees SET Deleted = $1 WHERE TreeId = $2") if err != nil { t.Fatalf("PrepareContext() returned err = %v", err) } defer func() { if err := updateDeletedStmt.Close(); err != nil { t.Errorf("updateDeletedStmt.Close(): %v", err) } }() for _, treeID := range []int64{deletedLog.TreeId} { if _, err := updateDeletedStmt.ExecContext(ctx, true, treeID); err != nil { t.Fatalf("ExecContext(%v) returned err = %v", treeID, err) } } s := NewLogStorage(handle.db, nil) got, err := s.GetActiveLogIDs(ctx) if err != nil { t.Fatalf("GetActiveLogIDs() returns err = %v", err) } want := []int64{log1.TreeId, log2.TreeId, log3.TreeId, drainingLog.TreeId} sort.Slice(got, func(i, j int) bool { return got[i] < got[j] }) sort.Slice(want, func(i, j int) bool { return want[i] < want[j] }) if diff := cmp.Diff(got, want); diff != "" { t.Errorf("post-GetActiveLogIDs diff (-got +want):\n%v", diff) } } func TestGetActiveLogIDsEmpty(t *testing.T) { t.Parallel() ctx := context.Background() handle := openTestDBOrDie(t) s := NewLogStorage(handle.db, nil) ids, err := s.GetActiveLogIDs(ctx) if err != nil { t.Fatalf("GetActiveLogIDs() = (_, %v), want = (_, nil)", err) } if got, want := len(ids), 0; got != want { t.Errorf("GetActiveLogIDs(): got %v IDs, want = %v", got, want) } } func ensureAllLeavesDistinct(leaves []*trillian.LogLeaf, t *testing.T) { t.Helper() // All the leaf value hashes should be distinct because the leaves were created with distinct // leaf data. If only we had maps with slices as keys or sets or pretty much any kind of usable // data structures we could do this properly. for i := range leaves { for j := range leaves { if i != j && bytes.Equal(leaves[i].LeafIdentityHash, leaves[j].LeafIdentityHash) { t.Fatalf("Unexpectedly got a duplicate leaf hash: %v %v", leaves[i].LeafIdentityHash, leaves[j].LeafIdentityHash) } } } } func ensureLeavesHaveQueueTimestamp(t *testing.T, leaves []*trillian.LogLeaf, want time.Time) { t.Helper() for _, leaf := range leaves { gotQTimestamp := leaf.QueueTimestamp.AsTime() if got, want := gotQTimestamp.UnixNano(), want.UnixNano(); got != want { t.Errorf("Got leaf with QueueTimestampNanos = %v, want %v: %v", got, want, leaf) } } } // Creates some test leaves with predictable data func createTestLeaves(n, startSeq int64) []*trillian.LogLeaf { var leaves []*trillian.LogLeaf for l := int64(0); l < n; l++ { lv := fmt.Sprintf("Leaf %d", l+startSeq) h := sha256.New() h.Write([]byte(lv)) leafHash := h.Sum(nil) leaf := &trillian.LogLeaf{ LeafIdentityHash: leafHash, MerkleLeafHash: leafHash, LeafValue: []byte(lv), ExtraData: []byte(fmt.Sprintf("Extra %d", l)), LeafIndex: int64(startSeq + l), } leaves = append(leaves, leaf) } return leaves } // Convenience methods to avoid copying out "if err != nil { blah }" all over the place func runLogTX(s storage.LogStorage, tree *trillian.Tree, t *testing.T, f storage.LogTXFunc) { t.Helper() if err := s.ReadWriteTransaction(context.Background(), tree, f); err != nil { t.Fatalf("Failed to run log tx: %v", err) } } type committableTX interface { Commit(ctx context.Context) error } func commit(ctx context.Context, tx committableTX, t *testing.T) { t.Helper() if err := tx.Commit(ctx); err != nil { t.Errorf("Failed to commit tx: %v", err) } } func leafInBatch(leaf *trillian.LogLeaf, batch []*trillian.LogLeaf) bool { for _, bl := range batch { if bytes.Equal(bl.LeafIdentityHash, leaf.LeafIdentityHash) { return true } } return false } trillian-1.6.1/storage/crdb/pg.go000066400000000000000000000015351466362047600166720ustar00rootroot00000000000000// Copyright 2022 Trillian Authors // // 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 crdb import ( "fmt" "strings" ) func fromMySQLToPGPreparedStatement(sql string) string { counter := 1 for strings.Contains(sql, "?") { pgmarker := fmt.Sprintf("$%d", counter) sql = strings.Replace(sql, "?", pgmarker, 1) counter++ } return sql } trillian-1.6.1/storage/crdb/provider.go000066400000000000000000000056541466362047600201240ustar00rootroot00000000000000// Copyright 2018 Trillian Authors // // 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 crdb import ( "database/sql" "flag" "sync" "github.com/google/trillian/monitoring" "github.com/google/trillian/storage" "k8s.io/klog/v2" _ "github.com/cockroachdb/cockroach-go/v2/crdb/crdbpgx" // crdb retries and postgres interface _ "github.com/lib/pq" // Register the Postgres driver. ) const ( // StorageProviderName is the name of the storage provider. StorageProviderName = "crdb" ) var ( crdbURI = flag.String("crdb_uri", "postgresql://root@localhost:26257?sslmode=disable", "Connection URI for CockroachDB database") maxConns = flag.Int("crdb_max_conns", 0, "Maximum connections to the database") maxIdle = flag.Int("crdb_max_idle_conns", -1, "Maximum idle database connections in the connection pool") crdbErr error crdbHandle *sql.DB crdbStorageInstance *crdbProvider dbConnMu sync.Mutex ) // GetDatabase returns the database handle for the provider. func GetDatabase() (*sql.DB, error) { dbConnMu.Lock() defer dbConnMu.Unlock() return getCRDBDatabaseLocked() } func init() { if err := storage.RegisterProvider(StorageProviderName, newCRDBStorageProvider); err != nil { klog.Fatalf("Failed to register storage provider crdb: %v", err) } } type crdbProvider struct { db *sql.DB mf monitoring.MetricFactory } func newCRDBStorageProvider(mf monitoring.MetricFactory) (storage.Provider, error) { dbConnMu.Lock() defer dbConnMu.Unlock() if crdbStorageInstance == nil { db, err := getCRDBDatabaseLocked() if err != nil { return nil, err } crdbStorageInstance = &crdbProvider{ db: db, mf: mf, } } return crdbStorageInstance, nil } // Lazy initializes the database connection handle and returns the instance. // Requires lock to be held. func getCRDBDatabaseLocked() (*sql.DB, error) { if crdbHandle != nil || crdbErr != nil { return crdbHandle, crdbErr } db, err := OpenDB(*crdbURI) if err != nil { crdbErr = err return nil, err } if *maxConns > 0 { db.SetMaxOpenConns(*maxConns) } if *maxIdle >= 0 { db.SetMaxIdleConns(*maxIdle) } crdbHandle, crdbErr = db, nil return db, nil } func (p *crdbProvider) Close() error { return p.db.Close() } func (p *crdbProvider) LogStorage() storage.LogStorage { return NewLogStorage(p.db, p.mf) } func (p *crdbProvider) AdminStorage() storage.AdminStorage { return NewSQLAdminStorage(p.db) } trillian-1.6.1/storage/crdb/provider_test.go000066400000000000000000000027241466362047600211560ustar00rootroot00000000000000// Copyright 2018 Trillian Authors // // 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 crdb import ( "flag" "testing" "github.com/google/trillian/storage" "github.com/google/trillian/testonly/flagsaver" ) func TestCockroachDBStorageProviderErrorPersistence(t *testing.T) { t.Parallel() defer flagsaver.Save().MustRestore() if err := flag.Set("crdb_uri", "&bogus*:::?"); err != nil { t.Errorf("Failed to set flag: %v", err) } // First call: This should fail due to the Database URL being garbage. _, err1 := storage.NewProvider(StorageProviderName, nil) if err1 == nil { t.Fatalf("Expected 'storage.NewProvider' to fail") } // Second call: This should fail with the same error. _, err2 := storage.NewProvider(StorageProviderName, nil) if err2 == nil { t.Fatalf("Expected second call to 'storage.NewProvider' to fail") } if err2 != err1 { t.Fatalf("Expected second call to 'storage.NewProvider' to fail with %q, instead got: %q", err1, err2) } } trillian-1.6.1/storage/crdb/queue.go000066400000000000000000000114151466362047600174060ustar00rootroot00000000000000// Copyright 2017 Trillian Authors // // 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 crdb import ( "context" "database/sql" "errors" "fmt" "time" "github.com/google/trillian" "google.golang.org/protobuf/types/known/timestamppb" "k8s.io/klog/v2" ) const ( // If this statement ORDER BY clause is changed refer to the comment in removeSequencedLeaves selectQueuedLeavesSQL = `SELECT LeafIdentityHash,MerkleLeafHash,QueueTimestampNanos FROM Unsequenced WHERE TreeID=$1 AND Bucket=0 AND QueueTimestampNanos<=$2 ORDER BY QueueTimestampNanos,LeafIdentityHash ASC LIMIT $3` insertUnsequencedEntrySQL = `INSERT INTO Unsequenced(TreeId,Bucket,LeafIdentityHash,MerkleLeafHash,QueueTimestampNanos) VALUES($1,0,$2,$3,$4)` deleteUnsequencedSQL = "DELETE FROM Unsequenced WHERE TreeId=$1 AND Bucket=0 AND QueueTimestampNanos=$2 AND LeafIdentityHash=$3" ) type dequeuedLeaf struct { queueTimestampNanos int64 leafIdentityHash []byte } func dequeueInfo(leafIDHash []byte, queueTimestamp int64) dequeuedLeaf { return dequeuedLeaf{queueTimestampNanos: queueTimestamp, leafIdentityHash: leafIDHash} } func (t *logTreeTX) dequeueLeaf(rows *sql.Rows) (*trillian.LogLeaf, dequeuedLeaf, error) { var leafIDHash []byte var merkleHash []byte var queueTimestamp int64 err := rows.Scan(&leafIDHash, &merkleHash, &queueTimestamp) if err != nil { klog.Warningf("Error scanning work rows: %s", err) return nil, dequeuedLeaf{}, err } // Note: the LeafData and ExtraData being nil here is OK as this is only used by the // sequencer. The sequencer only writes to the SequencedLeafData table and the client // supplied data was already written to LeafData as part of queueing the leaf. queueTimestampProto := timestamppb.New(time.Unix(0, queueTimestamp)) if err := queueTimestampProto.CheckValid(); err != nil { return nil, dequeuedLeaf{}, fmt.Errorf("got invalid queue timestamp: %w", err) } leaf := &trillian.LogLeaf{ LeafIdentityHash: leafIDHash, MerkleLeafHash: merkleHash, QueueTimestamp: queueTimestampProto, } return leaf, dequeueInfo(leafIDHash, queueTimestamp), nil } func queueArgs(_ int64, _ []byte, queueTimestamp time.Time) []interface{} { return []interface{}{queueTimestamp.UnixNano()} } func (t *logTreeTX) UpdateSequencedLeaves(ctx context.Context, leaves []*trillian.LogLeaf) error { dequeuedLeaves := make([]dequeuedLeaf, 0, len(leaves)) for _, leaf := range leaves { // This should fail on insert but catch it early if len(leaf.LeafIdentityHash) != t.hashSizeBytes { return errors.New("sequenced leaf has incorrect hash size") } if err := leaf.IntegrateTimestamp.CheckValid(); err != nil { return fmt.Errorf("got invalid integrate timestamp: %w", err) } iTimestamp := leaf.IntegrateTimestamp.AsTime() _, err := t.tx.ExecContext( ctx, insertSequencedLeafSQL+valuesPlaceholder5, t.treeID, leaf.LeafIdentityHash, leaf.MerkleLeafHash, leaf.LeafIndex, iTimestamp.UnixNano()) if err != nil { klog.Warningf("Failed to update sequenced leaves: %s", err) return err } qe, ok := t.dequeued[string(leaf.LeafIdentityHash)] if !ok { return fmt.Errorf("attempting to update leaf that wasn't dequeued. IdentityHash: %x", leaf.LeafIdentityHash) } dequeuedLeaves = append(dequeuedLeaves, qe) } return t.removeSequencedLeaves(ctx, dequeuedLeaves) } // removeSequencedLeaves removes the passed in leaves slice (which may be // modified as part of the operation). func (t *logTreeTX) removeSequencedLeaves(ctx context.Context, leaves []dequeuedLeaf) error { start := time.Now() // Don't need to re-sort because the query ordered by leaf hash. If that changes because // the query is expensive then the sort will need to be done here. See comment in // QueueLeaves. stx, err := t.tx.PrepareContext(ctx, deleteUnsequencedSQL) if err != nil { klog.Warningf("Failed to prep delete statement for sequenced work: %v", err) return err } defer func() { if err := stx.Close(); err != nil { klog.Errorf("stx.Close(): %v", err) } }() for _, dql := range leaves { result, err := stx.ExecContext(ctx, t.treeID, dql.queueTimestampNanos, dql.leafIdentityHash) err = checkResultOkAndRowCountIs(result, err, int64(1)) if err != nil { return err } } observe(dequeueRemoveLatency, time.Since(start), labelForTX(t)) return nil } trillian-1.6.1/storage/crdb/schema/000077500000000000000000000000001466362047600171715ustar00rootroot00000000000000trillian-1.6.1/storage/crdb/schema/storage.sql000066400000000000000000000143441466362047600213640ustar00rootroot00000000000000-- CockroachDB version of the tree schema -- --------------------------------------------- -- Tree stuff here -- --------------------------------------------- CREATE TYPE tree_state AS ENUM ('ACTIVE', 'FROZEN', 'DRAINING'); CREATE TYPE tree_type AS ENUM ('LOG', 'PREORDERED_LOG'); CREATE TYPE tree_hash_strategy AS ENUM ('RFC6962_SHA256'); CREATE TYPE tree_hash_algorithm AS ENUM ('SHA256'); CREATE TYPE tree_signature_algorithm AS ENUM ('ECDSA', 'RSA', 'ED25519'); -- Tree parameters should not be changed after creation. Doing so can -- render the data in the tree unusable or inconsistent. CREATE TABLE IF NOT EXISTS Trees( TreeId BIGINT NOT NULL, TreeState tree_state NOT NULL, TreeType tree_type NOT NULL, HashStrategy tree_hash_strategy NOT NULL, HashAlgorithm tree_hash_algorithm NOT NULL, SignatureAlgorithm tree_signature_algorithm NOT NULL, DisplayName VARCHAR(20), Description VARCHAR(200), CreateTimeMillis BIGINT NOT NULL, UpdateTimeMillis BIGINT NOT NULL, MaxRootDurationMillis BIGINT NOT NULL, PrivateKey BYTES NOT NULL, PublicKey BYTES NOT NULL, Deleted BOOLEAN, DeleteTimeMillis BIGINT, PRIMARY KEY(TreeId) ); -- This table contains tree parameters that can be changed at runtime such as for -- administrative purposes. CREATE TABLE IF NOT EXISTS TreeControl( TreeId BIGINT NOT NULL, SigningEnabled BOOLEAN NOT NULL, SequencingEnabled BOOLEAN NOT NULL, SequenceIntervalSeconds INTEGER NOT NULL, PRIMARY KEY(TreeId), FOREIGN KEY(TreeId) REFERENCES Trees(TreeId) ON DELETE CASCADE ); CREATE TABLE IF NOT EXISTS Subtree( TreeId BIGINT NOT NULL, SubtreeId BYTES NOT NULL, Nodes BYTES NOT NULL, SubtreeRevision INTEGER NOT NULL, PRIMARY KEY(TreeId, SubtreeId, SubtreeRevision), FOREIGN KEY(TreeId) REFERENCES Trees(TreeId) ON DELETE CASCADE ); -- The TreeRevisionIdx is used to enforce that there is only one STH at any -- tree revision CREATE TABLE IF NOT EXISTS TreeHead( TreeId BIGINT NOT NULL, TreeHeadTimestamp BIGINT, TreeSize BIGINT, RootHash BYTES NOT NULL, RootSignature BYTES NOT NULL, TreeRevision BIGINT, PRIMARY KEY(TreeId, TreeHeadTimestamp), FOREIGN KEY(TreeId) REFERENCES Trees(TreeId) ON DELETE CASCADE ); CREATE UNIQUE INDEX TreeHeadRevisionIdx ON TreeHead(TreeId, TreeRevision); -- --------------------------------------------- -- Log specific stuff here -- --------------------------------------------- -- Creating index at same time as table allows some storage engines to better -- optimize physical storage layout. Most engines allow multiple nulls in a -- unique index but some may not. -- A leaf that has not been sequenced has a row in this table. If duplicate leaves -- are allowed they will all reference this row. CREATE TABLE IF NOT EXISTS LeafData( TreeId BIGINT NOT NULL, -- This is a personality specific has of some subset of the leaf data. -- It's only purpose is to allow Trillian to identify duplicate entries in -- the context of the personality. LeafIdentityHash BYTES NOT NULL, -- This is the data stored in the leaf for example in CT it contains a DER encoded -- X.509 certificate but is application dependent LeafValue BYTES NOT NULL, -- This is extra data that the application can associate with the leaf should it wish to. -- This data is not included in signing and hashing. ExtraData BYTES, -- The timestamp from when this leaf data was first queued for inclusion. QueueTimestampNanos BIGINT NOT NULL, PRIMARY KEY(TreeId, LeafIdentityHash), FOREIGN KEY(TreeId) REFERENCES Trees(TreeId) ON DELETE CASCADE ); -- When a leaf is sequenced a row is added to this table. If logs allow duplicates then -- multiple rows will exist with different sequence numbers. The signed timestamp -- will be communicated via the unsequenced table as this might need to be unique, depending -- on the log parameters and we can't insert into this table until we have the sequence number -- which is not available at the time we queue the entry. We need both hashes because the -- LeafData table is keyed by the raw data hash. CREATE TABLE IF NOT EXISTS SequencedLeafData( TreeId BIGINT NOT NULL, SequenceNumber BIGINT NOT NULL, -- This is a personality specific has of some subset of the leaf data. -- It's only purpose is to allow Trillian to identify duplicate entries in -- the context of the personality. LeafIdentityHash BYTES NOT NULL, -- This is a MerkleLeafHash as defined by the treehasher that the log uses. For example for -- CT this hash will include the leaf prefix byte as well as the leaf data. MerkleLeafHash BYTES NOT NULL, IntegrateTimestampNanos BIGINT NOT NULL, PRIMARY KEY(TreeId, SequenceNumber), FOREIGN KEY(TreeId) REFERENCES Trees(TreeId) ON DELETE CASCADE, FOREIGN KEY(TreeId, LeafIdentityHash) REFERENCES LeafData(TreeId, LeafIdentityHash) ON DELETE CASCADE ); CREATE INDEX SequencedLeafMerkleIdx ON SequencedLeafData(TreeId, MerkleLeafHash); CREATE TABLE IF NOT EXISTS Unsequenced( TreeId BIGINT NOT NULL, -- The bucket field is to allow the use of time based ring bucketed schemes if desired. If -- unused this should be set to zero for all entries. Bucket INTEGER NOT NULL, -- This is a personality specific hash of some subset of the leaf data. -- It's only purpose is to allow Trillian to identify duplicate entries in -- the context of the personality. LeafIdentityHash BYTES NOT NULL, -- This is a MerkleLeafHash as defined by the treehasher that the log uses. For example for -- CT this hash will include the leaf prefix byte as well as the leaf data. MerkleLeafHash BYTES NOT NULL, QueueTimestampNanos BIGINT NOT NULL, -- This is a SHA256 hash of the TreeID, LeafIdentityHash and QueueTimestampNanos. It is used -- for batched deletes from the table when trillian_log_server and trillian_log_signer are -- built with the batched_queue tag. QueueID BYTES DEFAULT NULL UNIQUE, PRIMARY KEY (TreeId, Bucket, QueueTimestampNanos, LeafIdentityHash) ); trillian-1.6.1/storage/crdb/sql.go000066400000000000000000000074021466362047600170620ustar00rootroot00000000000000// Copyright 2018 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 crdb import ( "database/sql" "fmt" "time" "github.com/google/trillian" "google.golang.org/protobuf/types/known/durationpb" "google.golang.org/protobuf/types/known/timestamppb" ) // ToMillisSinceEpoch converts a timestamp into milliseconds since epoch func toMillisSinceEpoch(t time.Time) int64 { return t.UnixNano() / 1000000 } // FromMillisSinceEpoch converts func fromMillisSinceEpoch(ts int64) time.Time { return time.Unix(0, ts*1000000) } // SetNullStringIfValid assigns src to dest if src is Valid. func setNullStringIfValid(src sql.NullString, dest *string) { if src.Valid { *dest = src.String } } // row defines a common interface between sql.Row and sql.Rows(!) type row interface { Scan(dest ...interface{}) error } // ReadTree takes a sql row and returns a tree func readTree(r row) (*trillian.Tree, error) { tree := &trillian.Tree{} // Enums and Datetimes need an extra conversion step var treeState, treeType, hashStrategy, hashAlgorithm, signatureAlgorithm string var createMillis, updateMillis, maxRootDurationMillis int64 var displayName, description sql.NullString var privateKey, publicKey []byte var deleted sql.NullBool var deleteMillis sql.NullInt64 err := r.Scan( &tree.TreeId, &treeState, &treeType, &hashStrategy, &hashAlgorithm, &signatureAlgorithm, &displayName, &description, &createMillis, &updateMillis, &privateKey, &publicKey, &maxRootDurationMillis, &deleted, &deleteMillis, ) if err != nil { return nil, err } setNullStringIfValid(displayName, &tree.DisplayName) setNullStringIfValid(description, &tree.Description) // Convert all things! if ts, ok := trillian.TreeState_value[treeState]; ok { tree.TreeState = trillian.TreeState(ts) } else { return nil, fmt.Errorf("unknown TreeState: %v", treeState) } if tt, ok := trillian.TreeType_value[treeType]; ok { tree.TreeType = trillian.TreeType(tt) } else { return nil, fmt.Errorf("unknown TreeType: %v", treeType) } if hashStrategy != "RFC6962_SHA256" { return nil, fmt.Errorf("unknown HashStrategy: %v", hashStrategy) } // Let's make sure we didn't mismatch any of the casts above ok := tree.TreeState.String() == treeState && tree.TreeType.String() == treeType if !ok { return nil, fmt.Errorf( "mismatched enum: tree = %v, enums = [%v, %v, %v, %v, %v]", tree, treeState, treeType, hashStrategy, hashAlgorithm, signatureAlgorithm) } tree.CreateTime = timestamppb.New(fromMillisSinceEpoch(createMillis)) if err := tree.CreateTime.CheckValid(); err != nil { return nil, fmt.Errorf("failed to parse create time: %w", err) } tree.UpdateTime = timestamppb.New(fromMillisSinceEpoch(updateMillis)) if err := tree.UpdateTime.CheckValid(); err != nil { return nil, fmt.Errorf("failed to parse update time: %w", err) } tree.MaxRootDuration = durationpb.New(time.Duration(maxRootDurationMillis * int64(time.Millisecond))) tree.Deleted = deleted.Valid && deleted.Bool if tree.Deleted && deleteMillis.Valid { tree.DeleteTime = timestamppb.New(fromMillisSinceEpoch(deleteMillis.Int64)) if err := tree.DeleteTime.CheckValid(); err != nil { return nil, fmt.Errorf("failed to parse delete time: %w", err) } } return tree, nil } trillian-1.6.1/storage/crdb/sqladminstorage.go000066400000000000000000000271641466362047600214670ustar00rootroot00000000000000// Copyright 2017 Trillian Authors // // 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 crdb import ( "context" "database/sql" "fmt" "sync" "time" "github.com/google/trillian" "github.com/google/trillian/storage" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/timestamppb" "k8s.io/klog/v2" ) const ( defaultSequenceIntervalSeconds = 60 nonDeletedWhere = " WHERE (Deleted IS NULL OR Deleted = 'false')" selectTrees = ` SELECT TreeId, TreeState, TreeType, HashStrategy, HashAlgorithm, SignatureAlgorithm, DisplayName, Description, CreateTimeMillis, UpdateTimeMillis, PrivateKey, PublicKey, MaxRootDurationMillis, Deleted, DeleteTimeMillis FROM Trees` selectNonDeletedTrees = selectTrees + nonDeletedWhere selectTreeByID = selectTrees + " WHERE TreeId = $1" updateTreeSQL = `UPDATE Trees SET TreeState = $1, TreeType = $2, DisplayName = $3, Description = $4, UpdateTimeMillis = $5, MaxRootDurationMillis = $6, PrivateKey = $7 WHERE TreeId = $8` ) // NewSQLAdminStorage returns a SQL storage.AdminStorage implementation backed by DB. // Should work for MySQL and CockroachDB func NewSQLAdminStorage(db *sql.DB) storage.AdminStorage { return &sqlAdminStorage{db} } // sqlAdminStorage implements storage.AdminStorage type sqlAdminStorage struct { db *sql.DB } func (s *sqlAdminStorage) Snapshot(ctx context.Context) (storage.ReadOnlyAdminTX, error) { return s.beginInternal(ctx) } func (s *sqlAdminStorage) beginInternal(ctx context.Context) (storage.AdminTX, error) { tx, err := s.db.BeginTx(ctx, nil /* opts */) if err != nil { return nil, err } return &adminTX{tx: tx}, nil } func (s *sqlAdminStorage) ReadWriteTransaction(ctx context.Context, f storage.AdminTXFunc) error { tx, err := s.beginInternal(ctx) if err != nil { return err } defer func() { if err := tx.Close(); err != nil { klog.Errorf("tx.Close(): %v", err) } }() if err := f(ctx, tx); err != nil { return err } return tx.Commit() } func (s *sqlAdminStorage) CheckDatabaseAccessible(ctx context.Context) error { return s.db.PingContext(ctx) } type adminTX struct { tx *sql.Tx // mu guards reads/writes on closed, which happen on Commit/Close methods. // // We don't check closed on methods apart from the ones above, as we trust tx // to keep tabs on its state, and hence fail to do queries after closed. mu sync.RWMutex closed bool } func (t *adminTX) Commit() error { t.mu.Lock() defer t.mu.Unlock() t.closed = true return t.tx.Commit() } func (t *adminTX) Close() error { t.mu.Lock() defer t.mu.Unlock() if t.closed { return nil } t.closed = true return t.tx.Rollback() } func (t *adminTX) GetTree(ctx context.Context, treeID int64) (*trillian.Tree, error) { stmt, err := t.tx.PrepareContext(ctx, selectTreeByID) if err != nil { return nil, err } defer func() { if err := stmt.Close(); err != nil { klog.Errorf("stmt.Close(): %v", err) } }() // GetTree is an entry point for most RPCs, let's provide somewhat nicer error messages. tree, err := readTree(stmt.QueryRowContext(ctx, treeID)) switch { case err == sql.ErrNoRows: // ErrNoRows doesn't provide useful information, so we don't forward it. return nil, status.Errorf(codes.NotFound, "tree %v not found", treeID) case err != nil: return nil, fmt.Errorf("error reading tree %v: %v", treeID, err) } return tree, nil } func (t *adminTX) ListTrees(ctx context.Context, includeDeleted bool) ([]*trillian.Tree, error) { var query string if includeDeleted { query = selectTrees } else { query = selectNonDeletedTrees } stmt, err := t.tx.PrepareContext(ctx, query) if err != nil { return nil, err } defer func() { if err := stmt.Close(); err != nil { klog.Errorf("stmt.Close(): %v", err) } }() rows, err := stmt.QueryContext(ctx) if err != nil { return nil, err } defer func() { if err := rows.Close(); err != nil { klog.Errorf("rows.Close(): %v", err) } }() trees := []*trillian.Tree{} for rows.Next() { tree, err := readTree(rows) if err != nil { return nil, err } trees = append(trees, tree) } return trees, nil } func (t *adminTX) CreateTree(ctx context.Context, tree *trillian.Tree) (*trillian.Tree, error) { if err := storage.ValidateTreeForCreation(ctx, tree); err != nil { return nil, err } if err := validateStorageSettings(tree); err != nil { return nil, err } id, err := storage.NewTreeID() if err != nil { return nil, err } // Use the time truncated-to-millis throughout, as that's what's stored. nowMillis := toMillisSinceEpoch(time.Now()) now := fromMillisSinceEpoch(nowMillis) newTree := proto.Clone(tree).(*trillian.Tree) newTree.TreeId = id newTree.CreateTime = timestamppb.New(now) if err := newTree.CreateTime.CheckValid(); err != nil { return nil, fmt.Errorf("failed to build create time: %w", err) } newTree.UpdateTime = timestamppb.New(now) if err := newTree.UpdateTime.CheckValid(); err != nil { return nil, fmt.Errorf("failed to build update time: %w", err) } if err := newTree.MaxRootDuration.CheckValid(); err != nil { return nil, fmt.Errorf("could not parse MaxRootDuration: %w", err) } rootDuration := newTree.MaxRootDuration.AsDuration() insertTreeStmt, err := t.tx.PrepareContext( ctx, `INSERT INTO Trees( TreeId, TreeState, TreeType, HashStrategy, HashAlgorithm, SignatureAlgorithm, DisplayName, Description, CreateTimeMillis, UpdateTimeMillis, PrivateKey, PublicKey, MaxRootDurationMillis) VALUES($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)`) if err != nil { return nil, err } defer func() { if err := insertTreeStmt.Close(); err != nil { klog.Errorf("insertTreeStmt.Close(): %v", err) } }() _, err = insertTreeStmt.ExecContext( ctx, newTree.TreeId, newTree.TreeState.String(), newTree.TreeType.String(), "RFC6962_SHA256", // Unused, filling in for backward compatibility. "SHA256", // Unused, filling in for backward compatibility. "ECDSA", // Unused, filling in for backward compatibility. newTree.DisplayName, newTree.Description, nowMillis, nowMillis, []byte{}, // Unused, filling in for backward compatibility. []byte{}, // Unused, filling in for backward compatibility. rootDuration/time.Millisecond, ) if err != nil { return nil, err } // MySQL silently truncates data when running in non-strict mode. // We shouldn't be using non-strict modes, but let's guard against it // anyway. if _, err := t.GetTree(ctx, newTree.TreeId); err != nil { // GetTree will fail for truncated enums (they get recorded as // empty strings, which will not match any known value). return nil, fmt.Errorf("enum truncated: %v", err) } insertControlStmt, err := t.tx.PrepareContext( ctx, `INSERT INTO TreeControl( TreeId, SigningEnabled, SequencingEnabled, SequenceIntervalSeconds) VALUES($1, $2, $3, $4)`) if err != nil { return nil, err } defer func() { if err := insertControlStmt.Close(); err != nil { klog.Errorf("insertControlStmt.Close(): %v", err) } }() _, err = insertControlStmt.ExecContext( ctx, newTree.TreeId, true, /* SigningEnabled */ true, /* SequencingEnabled */ defaultSequenceIntervalSeconds, ) if err != nil { return nil, err } return newTree, nil } func (t *adminTX) UpdateTree(ctx context.Context, treeID int64, updateFunc func(*trillian.Tree)) (*trillian.Tree, error) { tree, err := t.GetTree(ctx, treeID) if err != nil { return nil, err } beforeUpdate := proto.Clone(tree).(*trillian.Tree) updateFunc(tree) if err := storage.ValidateTreeForUpdate(ctx, beforeUpdate, tree); err != nil { return nil, err } if err := validateStorageSettings(tree); err != nil { return nil, err } // TODO(pavelkalinnikov): When switching TreeType from PREORDERED_LOG to LOG, // ensure all entries in SequencedLeafData are integrated. // Use the time truncated-to-millis throughout, as that's what's stored. nowMillis := toMillisSinceEpoch(time.Now()) now := fromMillisSinceEpoch(nowMillis) tree.UpdateTime = timestamppb.New(now) if err != nil { return nil, fmt.Errorf("failed to build update time: %v", err) } if err := tree.MaxRootDuration.CheckValid(); err != nil { return nil, fmt.Errorf("could not parse MaxRootDuration: %w", err) } rootDuration := tree.MaxRootDuration.AsDuration() stmt, err := t.tx.PrepareContext(ctx, updateTreeSQL) if err != nil { return nil, err } defer func() { if err := stmt.Close(); err != nil { klog.Errorf("stmt.Close(): %v", err) } }() if _, err = stmt.ExecContext( ctx, tree.TreeState.String(), tree.TreeType.String(), tree.DisplayName, tree.Description, nowMillis, rootDuration/time.Millisecond, []byte{}, // Unused, filling in for backward compatibility. tree.TreeId); err != nil { return nil, err } return tree, nil } func (t *adminTX) SoftDeleteTree(ctx context.Context, treeID int64) (*trillian.Tree, error) { return t.updateDeleted(ctx, treeID, true /* deleted */, toMillisSinceEpoch(time.Now()) /* deleteTimeMillis */) } func (t *adminTX) UndeleteTree(ctx context.Context, treeID int64) (*trillian.Tree, error) { return t.updateDeleted(ctx, treeID, false /* deleted */, nil /* deleteTimeMillis */) } // updateDeleted updates the Deleted and DeleteTimeMillis fields of the specified tree. // deleteTimeMillis must be either an int64 (in millis since epoch) or nil. func (t *adminTX) updateDeleted(ctx context.Context, treeID int64, deleted bool, deleteTimeMillis interface{}) (*trillian.Tree, error) { if err := validateDeleted(ctx, t.tx, treeID, !deleted); err != nil { return nil, err } if _, err := t.tx.ExecContext( ctx, "UPDATE Trees SET Deleted = $1, DeleteTimeMillis = $2 WHERE TreeId = $3", deleted, deleteTimeMillis, treeID); err != nil { return nil, err } return t.GetTree(ctx, treeID) } func (t *adminTX) HardDeleteTree(ctx context.Context, treeID int64) error { if err := validateDeleted(ctx, t.tx, treeID, true /* wantDeleted */); err != nil { return err } // TreeControl didn't have "ON DELETE CASCADE" on previous versions, so let's hit it explicitly if _, err := t.tx.ExecContext(ctx, "DELETE FROM TreeControl WHERE TreeId = $1", treeID); err != nil { return err } _, err := t.tx.ExecContext(ctx, "DELETE FROM Trees WHERE TreeId = $1", treeID) return err } func validateDeleted(ctx context.Context, tx *sql.Tx, treeID int64, wantDeleted bool) error { var nullDeleted sql.NullBool switch err := tx.QueryRowContext(ctx, "SELECT Deleted FROM Trees WHERE TreeId = $1", treeID).Scan(&nullDeleted); { case err == sql.ErrNoRows: return status.Errorf(codes.NotFound, "tree %v not found", treeID) case err != nil: return err } switch deleted := nullDeleted.Valid && nullDeleted.Bool; { case wantDeleted && !deleted: return status.Errorf(codes.FailedPrecondition, "tree %v is not soft deleted", treeID) case !wantDeleted && deleted: return status.Errorf(codes.FailedPrecondition, "tree %v already soft deleted", treeID) } return nil } func validateStorageSettings(tree *trillian.Tree) error { if tree.StorageSettings != nil { return fmt.Errorf("storage_settings not supported, but got %v", tree.StorageSettings) } return nil } trillian-1.6.1/storage/crdb/sqladminstorage_test.go000066400000000000000000000165361466362047600225270ustar00rootroot00000000000000// Copyright 2017 Trillian Authors // // 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 crdb import ( "context" "database/sql" "fmt" "testing" "github.com/google/trillian" "github.com/google/trillian/storage" "github.com/google/trillian/storage/testonly" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/anypb" ) const selectTreeControlByID = "SELECT SigningEnabled, SequencingEnabled, SequenceIntervalSeconds FROM TreeControl WHERE TreeId = $1" func TestCRDBAdminStorage(t *testing.T) { t.Parallel() tester := &testonly.AdminStorageTester{NewAdminStorage: func() storage.AdminStorage { handle := openTestDBOrDie(t) return NewSQLAdminStorage(handle.db) }} tester.RunAllTests(t) } func TestAdminTX_CreateTree_InitializesStorageStructures(t *testing.T) { t.Parallel() handle := openTestDBOrDie(t) s := NewSQLAdminStorage(handle.db) ctx := context.Background() tree, err := storage.CreateTree(ctx, s, testonly.LogTree) if err != nil { t.Fatalf("CreateTree() failed: %v", err) } // Check if TreeControl is correctly written. var signingEnabled, sequencingEnabled bool var sequenceIntervalSeconds int if err := handle.db.QueryRowContext(ctx, selectTreeControlByID, tree.TreeId).Scan(&signingEnabled, &sequencingEnabled, &sequenceIntervalSeconds); err != nil { t.Fatalf("Failed to read TreeControl: %v", err) } // We don't mind about specific values, defaults change, but let's check // that important numbers are not zeroed. if sequenceIntervalSeconds <= 0 { t.Errorf("sequenceIntervalSeconds = %v, want > 0", sequenceIntervalSeconds) } } func TestCreateTreeInvalidStates(t *testing.T) { t.Parallel() handle := openTestDBOrDie(t) s := NewSQLAdminStorage(handle.db) ctx := context.Background() states := []trillian.TreeState{trillian.TreeState_DRAINING, trillian.TreeState_FROZEN} for _, state := range states { inTree := proto.Clone(testonly.LogTree).(*trillian.Tree) inTree.TreeState = state if _, err := storage.CreateTree(ctx, s, inTree); err == nil { t.Errorf("CreateTree() state: %v got: nil want: err", state) } } } func TestAdminTX_TreeWithNulls(t *testing.T) { t.Parallel() handle := openTestDBOrDie(t) s := NewSQLAdminStorage(handle.db) ctx := context.Background() // Setup: create a tree and set all nullable columns to null. // Some columns have to be manually updated, as it's not possible to set // some proto fields to nil. tree, err := storage.CreateTree(ctx, s, testonly.LogTree) if err != nil { t.Fatalf("CreateTree() failed: %v", err) } treeID := tree.TreeId if err := setNulls(ctx, handle.db, treeID); err != nil { t.Fatalf("setNulls() = %v, want = nil", err) } tests := []struct { desc string fn storage.AdminTXFunc }{ { desc: "GetTree", fn: func(ctx context.Context, tx storage.AdminTX) error { _, err := tx.GetTree(ctx, treeID) return err }, }, { desc: "ListTrees", fn: func(ctx context.Context, tx storage.AdminTX) error { trees, err := tx.ListTrees(ctx, false /* includeDeleted */) if err != nil { return err } for _, tree := range trees { if tree.TreeId == treeID { return nil } } return fmt.Errorf("ID not found: %v", treeID) }, }, } for _, test := range tests { if err := s.ReadWriteTransaction(ctx, test.fn); err != nil { t.Errorf("%v: err = %v, want = nil", test.desc, err) } } } func TestAdminTX_StorageSettingsNotSupported(t *testing.T) { t.Parallel() handle := openTestDBOrDie(t) s := NewSQLAdminStorage(handle.db) ctx := context.Background() settings, err := anypb.New(&trillian.Tree{}) if err != nil { t.Fatalf("Error marshaling proto: %v", err) } tests := []struct { desc string // fn attempts to either create or update a tree with a non-nil, valid Any proto // on Tree.StorageSettings. It's expected to return an error. fn func(storage.AdminStorage) error }{ { desc: "CreateTree", fn: func(s storage.AdminStorage) error { tree := proto.Clone(testonly.LogTree).(*trillian.Tree) tree.StorageSettings = settings _, err := storage.CreateTree(ctx, s, tree) return err }, }, { desc: "UpdateTree", fn: func(s storage.AdminStorage) error { tree, err := storage.CreateTree(ctx, s, testonly.LogTree) if err != nil { t.Fatalf("CreateTree() failed with err = %v", err) } _, err = storage.UpdateTree(ctx, s, tree.TreeId, func(tree *trillian.Tree) { tree.StorageSettings = settings }) return err }, }, } for _, test := range tests { if err := test.fn(s); err == nil { t.Errorf("%v: err = nil, want non-nil", test.desc) } } } func TestAdminTX_HardDeleteTree(t *testing.T) { t.Parallel() handle := openTestDBOrDie(t) s := NewSQLAdminStorage(handle.db) ctx := context.Background() tree, err := storage.CreateTree(ctx, s, testonly.LogTree) if err != nil { t.Fatalf("CreateTree() returned err = %v", err) } if err := s.ReadWriteTransaction(ctx, func(ctx context.Context, tx storage.AdminTX) error { if _, err := tx.SoftDeleteTree(ctx, tree.TreeId); err != nil { return err } return tx.HardDeleteTree(ctx, tree.TreeId) }); err != nil { t.Fatalf("ReadWriteTransaction() returned err = %v", err) } // Unlike the HardDelete tests on AdminStorageTester, here we have the chance to poke inside the // database and check that the rows are gone, so let's do just that. // If there's no record on Trees, then there can be no record in any of the dependent tables. var name string if err := handle.db.QueryRowContext(ctx, "SELECT DisplayName FROM Trees WHERE TreeId = $1", tree.TreeId).Scan(&name); err != sql.ErrNoRows { t.Errorf("QueryRowContext() returned err = %v, want = %v", err, sql.ErrNoRows) } } func TestCheckDatabaseAccessible_Fails(t *testing.T) { t.Parallel() ctx := context.Background() // Pass in a closed database to provoke a failure. handle := openTestDBOrDie(t) s := NewSQLAdminStorage(handle.db) id := getDBID(t) rawdbhandle, loaded := testDBs.LoadAndDelete(id) if !loaded { t.Fatalf("Failed to load test DB handle for id %v", id) } dbhandle, ok := rawdbhandle.(*testDBHandle) if !ok { t.Fatalf("Failed to cast test DB handle for id %v", id) } dbhandle.done(context.Background()) if err := s.CheckDatabaseAccessible(ctx); err == nil { t.Error("TestCheckDatabaseAccessible_Fails got: nil, want: err") } } func TestCheckDatabaseAccessible_OK(t *testing.T) { t.Parallel() handle := openTestDBOrDie(t) s := NewSQLAdminStorage(handle.db) ctx := context.Background() if err := s.CheckDatabaseAccessible(ctx); err != nil { t.Errorf("TestCheckDatabaseAccessible_OK got: %v, want: nil", err) } } func setNulls(ctx context.Context, db *sql.DB, treeID int64) error { stmt, err := db.PrepareContext(ctx, "UPDATE Trees SET DisplayName = NULL, Description = NULL WHERE TreeId = $1") if err != nil { return err } defer func() { _ = stmt.Close() }() _, err = stmt.ExecContext(ctx, treeID) return err } trillian-1.6.1/storage/crdb/storage_test.go000066400000000000000000000174131466362047600207710ustar00rootroot00000000000000// Copyright 2016 Trillian Authors // // 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 crdb import ( "bytes" "context" "crypto" "crypto/sha256" "fmt" "testing" "github.com/google/trillian" "github.com/google/trillian/storage" storageto "github.com/google/trillian/storage/testonly" stree "github.com/google/trillian/storage/tree" "github.com/google/trillian/types" "github.com/transparency-dev/merkle/compact" "github.com/transparency-dev/merkle/rfc6962" "k8s.io/klog/v2" ) func TestNodeRoundTrip(t *testing.T) { t.Parallel() nodes := createSomeNodes(256) nodeIDs := make([]compact.NodeID, len(nodes)) for i := range nodes { nodeIDs[i] = nodes[i].ID } for _, tc := range []struct { desc string store []stree.Node read []compact.NodeID want []stree.Node wantErr bool }{ {desc: "store-4-read-4", store: nodes[:4], read: nodeIDs[:4], want: nodes[:4]}, {desc: "store-4-read-1", store: nodes[:4], read: nodeIDs[:1], want: nodes[:1]}, {desc: "store-2-read-4", store: nodes[:2], read: nodeIDs[:4], want: nodes[:2]}, {desc: "store-none-read-all", store: nil, read: nodeIDs, wantErr: true}, {desc: "store-all-read-all", store: nodes, read: nodeIDs, want: nodes}, {desc: "store-all-read-none", store: nodes, read: nil, want: nil}, } { tc := tc t.Run(tc.desc, func(t *testing.T) { t.Parallel() ctx := context.Background() handle := openTestDBOrDie(t) as := NewSQLAdminStorage(handle.db) tree := mustCreateTree(ctx, t, as, storageto.LogTree) s := NewLogStorage(handle.db, nil) const writeRev = int64(100) runLogTX(s, tree, t, func(ctx context.Context, tx storage.LogTreeTX) error { forceWriteRevision(writeRev, tx) if err := tx.SetMerkleNodes(ctx, tc.store); err != nil { t.Fatalf("Failed to store nodes: %s", err) } return storeLogRoot(ctx, tx, uint64(len(tc.store)), uint64(writeRev), []byte{1, 2, 3}) }) runLogTX(s, tree, t, func(ctx context.Context, tx storage.LogTreeTX) error { readNodes, err := tx.GetMerkleNodes(ctx, tc.read) if err != nil && !tc.wantErr { t.Fatalf("Failed to retrieve nodes: %s", err) } else if err == nil && tc.wantErr { t.Fatal("Retrieving nodes succeeded unexpectedly") } if err := nodesAreEqual(readNodes, tc.want); err != nil { t.Fatalf("Read back different nodes from the ones stored: %s", err) } return nil }) }) } } // This test ensures that node writes cross subtree boundaries so this edge case in the subtree // cache gets exercised. Any tree size > 256 will do this. func TestLogNodeRoundTripMultiSubtree(t *testing.T) { t.Parallel() ctx := context.Background() handle := openTestDBOrDie(t) as := NewSQLAdminStorage(handle.db) tree := mustCreateTree(ctx, t, as, storageto.LogTree) s := NewLogStorage(handle.db, nil) const writeRev = int64(100) const size = 871 nodesToStore, err := createLogNodesForTreeAtSize(t, size, writeRev) if err != nil { t.Fatalf("failed to create test tree: %v", err) } nodeIDsToRead := make([]compact.NodeID, len(nodesToStore)) for i := range nodesToStore { nodeIDsToRead[i] = nodesToStore[i].ID } { runLogTX(s, tree, t, func(ctx context.Context, tx storage.LogTreeTX) error { forceWriteRevision(writeRev, tx) if err := tx.SetMerkleNodes(ctx, nodesToStore); err != nil { t.Fatalf("Failed to store nodes: %s", err) } return storeLogRoot(ctx, tx, uint64(size), uint64(writeRev), []byte{1, 2, 3}) }) } { runLogTX(s, tree, t, func(ctx context.Context, tx storage.LogTreeTX) error { readNodes, err := tx.GetMerkleNodes(ctx, nodeIDsToRead) if err != nil { t.Fatalf("Failed to retrieve nodes: %s", err) } if err := nodesAreEqual(readNodes, nodesToStore); err != nil { missing, extra := diffNodes(readNodes, nodesToStore) for _, n := range missing { t.Errorf("Missing: %v", n.ID) } for _, n := range extra { t.Errorf("Extra : %v", n.ID) } t.Fatalf("Read back different nodes from the ones stored: %s", err) } return nil }) } } func forceWriteRevision(rev int64, tx storage.LogTreeTX) { mtx, ok := tx.(*logTreeTX) if !ok { panic(nil) } mtx.treeTX.writeRevision = rev } func createSomeNodes(count int) []stree.Node { r := make([]stree.Node, count) for i := range r { r[i].ID = compact.NewNodeID(0, uint64(i)) h := sha256.Sum256([]byte{byte(i)}) r[i].Hash = h[:] klog.V(3).Infof("Node to store: %v", r[i].ID) } return r } func createLogNodesForTreeAtSize(t *testing.T, ts, rev int64) ([]stree.Node, error) { hasher := rfc6962.New(crypto.SHA256) fact := compact.RangeFactory{Hash: hasher.HashChildren} cr := fact.NewEmptyRange(0) nodeMap := make(map[compact.NodeID][]byte) store := func(id compact.NodeID, hash []byte) { nodeMap[id] = hash } for l := 0; l < int(ts); l++ { hash := hasher.HashLeaf([]byte(fmt.Sprintf("Leaf %d", l))) // Store the new leaf node, and all new perfect nodes. if err := cr.Append(hash, store); err != nil { return nil, err } } // Unroll the map, which has deduped the updates for us and retained the latest nodes := make([]stree.Node, 0, len(nodeMap)) for id, hash := range nodeMap { nodes = append(nodes, stree.Node{ID: id, Hash: hash}) } return nodes, nil } // TODO(pavelkalinnikov): Allow nodes to be out of order. func nodesAreEqual(lhs, rhs []stree.Node) error { if ls, rs := len(lhs), len(rhs); ls != rs { return fmt.Errorf("different number of nodes, %d vs %d", ls, rs) } for i := range lhs { if l, r := lhs[i].ID, rhs[i].ID; l != r { return fmt.Errorf("NodeIDs are not the same,\nlhs = %v,\nrhs = %v", l, r) } if l, r := lhs[i].Hash, rhs[i].Hash; !bytes.Equal(l, r) { return fmt.Errorf("Hashes are not the same for %v,\nlhs = %v,\nrhs = %v", lhs[i].ID, l, r) } } return nil } func diffNodes(got, want []stree.Node) ([]stree.Node, []stree.Node) { var missing []stree.Node gotMap := make(map[compact.NodeID]stree.Node) for _, n := range got { gotMap[n.ID] = n } for _, n := range want { _, ok := gotMap[n.ID] if !ok { missing = append(missing, n) } delete(gotMap, n.ID) } // Unpack the extra nodes to return both as slices extra := make([]stree.Node, 0, len(gotMap)) for _, v := range gotMap { extra = append(extra, v) } return missing, extra } func mustSignAndStoreLogRoot(ctx context.Context, t *testing.T, l storage.LogStorage, tree *trillian.Tree, treeSize uint64) { t.Helper() if err := l.ReadWriteTransaction(ctx, tree, func(ctx context.Context, tx storage.LogTreeTX) error { return storeLogRoot(ctx, tx, treeSize, 0, []byte{0}) }); err != nil { t.Fatalf("ReadWriteTransaction: %v", err) } } func storeLogRoot(ctx context.Context, tx storage.LogTreeTX, size, rev uint64, hash []byte) error { logRoot, err := (&types.LogRootV1{TreeSize: size, RootHash: hash}).MarshalBinary() if err != nil { return fmt.Errorf("error marshaling new LogRoot: %v", err) } root := &trillian.SignedLogRoot{LogRoot: logRoot} if err := tx.StoreSignedLogRoot(ctx, root); err != nil { return fmt.Errorf("error storing new SignedLogRoot: %v", err) } return nil } // mustCreateTree creates the specified tree using AdminStorage. func mustCreateTree(ctx context.Context, t *testing.T, s storage.AdminStorage, tree *trillian.Tree) *trillian.Tree { t.Helper() tree, err := storage.CreateTree(ctx, s, tree) if err != nil { t.Fatalf("storage.CreateTree(): %v", err) } return tree } trillian-1.6.1/storage/crdb/tree_storage.go000066400000000000000000000262441466362047600207530ustar00rootroot00000000000000// Copyright 2016 Trillian Authors // // 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 crdb provides a CockroachDB-based storage layer implementation. package crdb import ( "context" "database/sql" "encoding/base64" "fmt" "runtime/debug" "strings" "sync" "github.com/google/trillian" "github.com/google/trillian/storage/cache" "github.com/google/trillian/storage/storagepb" "github.com/google/trillian/storage/tree" "google.golang.org/protobuf/proto" "k8s.io/klog/v2" ) // These statements are fixed const ( insertSubtreeMultiSQL = `INSERT INTO Subtree(TreeId, SubtreeId, Nodes, SubtreeRevision) ` + placeholderSQL // NOTE(jaosorior): While using the `ON CONFLICT DO NOTHING` clause // simplifies the StoreSignedLogRoot logic; it may lead to an // unnintuitive error message when trying to insert a duplicate. insertTreeHeadSQL = `INSERT INTO TreeHead(TreeId,TreeHeadTimestamp,TreeSize,RootHash,TreeRevision,RootSignature) VALUES($1,$2,$3,$4,$5,$6) ON CONFLICT DO NOTHING` selectSubtreeSQL = ` SELECT x.SubtreeId, x.MaxRevision, Subtree.Nodes FROM ( SELECT n.TreeId, n.SubtreeId, max(n.SubtreeRevision) AS MaxRevision FROM Subtree n WHERE n.SubtreeId IN (` + placeholderSQL + `) AND n.TreeId = ? AND n.SubtreeRevision <= ? GROUP BY n.TreeId, n.SubtreeId ) AS x INNER JOIN Subtree ON Subtree.SubtreeId = x.SubtreeId AND Subtree.SubtreeRevision = x.MaxRevision AND Subtree.TreeId = x.TreeId AND Subtree.TreeId = ?` placeholderSQL = "" ) // crdbTreeStorage contains common functionality for log/map storage type crdbTreeStorage struct { db *sql.DB // Must hold the mutex before manipulating the statement map. Sharing a lock because // it only needs to be held while the statements are built, not while they execute and // this will be a short time. These maps are from the number of placeholder '?' // in the query to the statement that should be used. statementMutex sync.Mutex statements map[string]map[int]*sql.Stmt } // OpenDB opens a database connection to the specified database. func OpenDB(dbURL string) (*sql.DB, error) { db, err := sql.Open("postgres", dbURL) if err != nil { klog.Warningf("Failed to open CRDB database: %v", err) return nil, err } // TODO(jaosorior): Set up retry logic so we don't immediately fail // if the database hasn't started yet. This is useful when deployed // in Kubernetes if err := db.Ping(); err != nil { klog.Warningf("failed verifying database connection: %v", err) return nil, err } return db, nil } func newTreeStorage(db *sql.DB) *crdbTreeStorage { return &crdbTreeStorage{ db: db, statements: make(map[string]map[int]*sql.Stmt), } } // expandPlaceholderSQL expands an sql statement by adding a specified number of '?' // placeholder slots. At most one placeholder will be expanded. func expandPlaceholderSQL(sql string, num int, first, rest string) string { if num <= 0 { panic(fmt.Errorf("trying to expand SQL placeholder with <= 0 parameters: %s", sql)) } parameters := first + strings.Repeat(","+rest, num-1) return fromMySQLToPGPreparedStatement(strings.Replace(sql, placeholderSQL, parameters, 1)) } // getStmt creates and caches sql.Stmt structs based on the passed in statement // and number of bound arguments. // TODO(al,martin): consider pulling this all out as a separate unit for reuse // elsewhere. func (m *crdbTreeStorage) getStmt(ctx context.Context, statement string, num int, first, rest string) (*sql.Stmt, error) { m.statementMutex.Lock() defer m.statementMutex.Unlock() if m.statements[statement] != nil { if m.statements[statement][num] != nil { // TODO(al,martin): we'll possibly need to expire Stmts from the cache, // e.g. when DB connections break etc. return m.statements[statement][num], nil } } else { m.statements[statement] = make(map[int]*sql.Stmt) } s, err := m.db.PrepareContext(ctx, expandPlaceholderSQL(statement, num, first, rest)) if err != nil { klog.Warningf("Failed to prepare statement %d: %s", num, err) return nil, err } m.statements[statement][num] = s return s, nil } func (m *crdbTreeStorage) getSubtreeStmt(ctx context.Context, num int) (*sql.Stmt, error) { return m.getStmt(ctx, selectSubtreeSQL, num, "?", "?") } func (m *crdbTreeStorage) setSubtreeStmt(ctx context.Context, num int) (*sql.Stmt, error) { return m.getStmt(ctx, insertSubtreeMultiSQL, num, "VALUES(?, ?, ?, ?)", "(?, ?, ?, ?)") } func (m *crdbTreeStorage) beginTreeTx(ctx context.Context, tree *trillian.Tree, hashSizeBytes int, subtreeCache *cache.SubtreeCache) (treeTX, error) { t, err := m.db.BeginTx(ctx, nil /* opts */) if err != nil { klog.Warningf("Could not start tree TX: %s", err) return treeTX{}, err } return treeTX{ tx: t, mu: &sync.Mutex{}, ts: m, treeID: tree.TreeId, treeType: tree.TreeType, hashSizeBytes: hashSizeBytes, subtreeCache: subtreeCache, writeRevision: -1, }, nil } type treeTX struct { // mu ensures that tx can only be used for one query/exec at a time. mu *sync.Mutex closed bool tx *sql.Tx ts *crdbTreeStorage treeID int64 treeType trillian.TreeType hashSizeBytes int subtreeCache *cache.SubtreeCache writeRevision int64 } func (t *treeTX) getSubtrees(ctx context.Context, treeRevision int64, ids [][]byte) ([]*storagepb.SubtreeProto, error) { klog.V(2).Infof("getSubtrees(len(ids)=%d)", len(ids)) klog.V(4).Infof("getSubtrees(") if len(ids) == 0 { return nil, nil } tmpl, err := t.ts.getSubtreeStmt(ctx, len(ids)) if err != nil { return nil, err } stx := t.tx.StmtContext(ctx, tmpl) defer func() { if err := stx.Close(); err != nil { klog.Errorf("stx.Close(): %v", err) } }() args := make([]interface{}, 0, len(ids)+3) // populate args with ids. for _, id := range ids { klog.V(4).Infof(" id: %x", id) args = append(args, id) } args = append(args, t.treeID) args = append(args, treeRevision) args = append(args, t.treeID) rows, err := stx.QueryContext(ctx, args...) if err != nil { klog.Warningf("Failed to get merkle subtrees: %s", err) return nil, err } defer func() { if err := rows.Close(); err != nil { klog.Errorf("rows.Close(): %v", err) } }() if rows.Err() != nil { // Nothing from the DB klog.Warningf("Nothing from DB: %s", rows.Err()) return nil, rows.Err() } ret := make([]*storagepb.SubtreeProto, 0, len(ids)) for rows.Next() { var subtreeIDBytes []byte var subtreeRev int64 var nodesRaw []byte if err := rows.Scan(&subtreeIDBytes, &subtreeRev, &nodesRaw); err != nil { klog.Warningf("Failed to scan merkle subtree: %s", err) return nil, err } var subtree storagepb.SubtreeProto if err := proto.Unmarshal(nodesRaw, &subtree); err != nil { klog.Warningf("Failed to unmarshal SubtreeProto: %s", err) return nil, err } if subtree.Prefix == nil { subtree.Prefix = []byte{} } ret = append(ret, &subtree) if klog.V(4).Enabled() { klog.Infof(" subtree: NID: %x, prefix: %x, depth: %d", subtreeIDBytes, subtree.Prefix, subtree.Depth) for k, v := range subtree.Leaves { b, err := base64.StdEncoding.DecodeString(k) if err != nil { klog.Errorf("base64.DecodeString(%v): %v", k, err) } klog.Infof(" %x: %x", b, v) } } } // The InternalNodes cache is possibly nil here, but the SubtreeCache (which called // this method) will re-populate it. return ret, nil } func (t *treeTX) storeSubtrees(ctx context.Context, subtrees []*storagepb.SubtreeProto) error { klog.V(2).Infof("storeSubtrees(len(subtrees)=%d)", len(subtrees)) if klog.V(4).Enabled() { klog.Infof("storeSubtrees(") for _, s := range subtrees { klog.Infof(" prefix: %x, depth: %d", s.Prefix, s.Depth) for k, v := range s.Leaves { b, err := base64.StdEncoding.DecodeString(k) if err != nil { klog.Errorf("base64.DecodeString(%v): %v", k, err) } klog.Infof(" %x: %x", b, v) } } } if len(subtrees) == 0 { return nil } // TODO(al): probably need to be able to batch this in the case where we have // a really large number of subtrees to store. args := make([]interface{}, 0, len(subtrees)) for _, s := range subtrees { s := s if s.Prefix == nil { panic(fmt.Errorf("nil prefix on %v", s)) } subtreeBytes, err := proto.Marshal(s) if err != nil { return err } args = append(args, t.treeID) args = append(args, s.Prefix) args = append(args, subtreeBytes) args = append(args, t.writeRevision) } tmpl, err := t.ts.setSubtreeStmt(ctx, len(subtrees)) if err != nil { return err } stx := t.tx.StmtContext(ctx, tmpl) defer func() { if err := stx.Close(); err != nil { klog.Errorf("stx.Close(): %v", err) } }() r, err := stx.ExecContext(ctx, args...) if err != nil { klog.Warningf("Failed to set merkle subtrees: %s", err) return err } _, _ = r.RowsAffected() return nil } func checkResultOkAndRowCountIs(res sql.Result, err error, count int64) error { // The Exec() might have just failed if err != nil { return crdbToGRPC(err) } // Otherwise we have to look at the result of the operation rowsAffected, rowsError := res.RowsAffected() if rowsError != nil { return crdbToGRPC(rowsError) } if rowsAffected != count { return fmt.Errorf("expected %d row(s) to be affected but saw: %d", count, rowsAffected) } return nil } // getSubtreesAtRev returns a GetSubtreesFunc which reads at the passed in rev. func (t *treeTX) getSubtreesAtRev(ctx context.Context, rev int64) cache.GetSubtreesFunc { return func(ids [][]byte) ([]*storagepb.SubtreeProto, error) { return t.getSubtrees(ctx, rev, ids) } } func (t *treeTX) SetMerkleNodes(ctx context.Context, nodes []tree.Node) error { t.mu.Lock() defer t.mu.Unlock() rev := t.writeRevision - 1 return t.subtreeCache.SetNodes(nodes, t.getSubtreesAtRev(ctx, rev)) } func (t *treeTX) Commit(ctx context.Context) error { t.mu.Lock() defer t.mu.Unlock() if t.writeRevision > -1 { tiles, err := t.subtreeCache.UpdatedTiles() if err != nil { klog.Warningf("SubtreeCache updated tiles error: %v", err) return err } if err := t.storeSubtrees(ctx, tiles); err != nil { klog.Warningf("TX commit flush error: %v", err) return err } } t.closed = true if err := t.tx.Commit(); err != nil { klog.Warningf("TX commit error: %s, stack:\n%s", err, string(debug.Stack())) return err } return nil } func (t *treeTX) rollbackInternal() error { t.closed = true if err := t.tx.Rollback(); err != nil { klog.Warningf("TX rollback error: %s, stack:\n%s", err, string(debug.Stack())) return err } return nil } func (t *treeTX) Close() error { t.mu.Lock() defer t.mu.Unlock() if t.closed { return nil } err := t.rollbackInternal() if err != nil { klog.Warningf("Rollback error on Close(): %v", err) } return err } trillian-1.6.1/storage/gen.go000066400000000000000000000020061466362047600161150ustar00rootroot00000000000000// 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 storage provides general interfaces to Trillian storage layers. package storage //go:generate mockgen -self_package github.com/google/trillian/storage -package storage -destination mock_storage.go -imports=trillian=github.com/google/trillian,storagepb=github.com/google/trillian/storage/storagepb github.com/google/trillian/storage AdminStorage,AdminTX,LogStorage,LogTreeTX,ReadOnlyAdminTX,ReadOnlyLogTreeTX trillian-1.6.1/storage/log_storage.go000066400000000000000000000206021466362047600176530ustar00rootroot00000000000000// 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 storage import ( "context" "time" "github.com/google/trillian" "github.com/google/trillian/storage/tree" "github.com/transparency-dev/merkle/compact" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) // ErrTreeNeedsInit is returned when calling methods on an uninitialised tree. var ErrTreeNeedsInit = status.Error(codes.FailedPrecondition, "tree needs initialising") // ReadOnlyLogTreeTX provides a read-only view into the Log data. // A ReadOnlyLogTreeTX can only read from the tree specified in its creation. type ReadOnlyLogTreeTX interface { // Commit applies the operations performed to the underlying storage. It must // be called before any reads from storage are considered consistent. Commit(context.Context) error // Close rolls back the transaction if it wasn't committed or closed // previously. Resources are cleaned up regardless of the success, and the // transaction should not be used after it. Close() error // GetMerkleNodes returns tree nodes by their IDs, in the requested order. GetMerkleNodes(ctx context.Context, ids []compact.NodeID) ([]tree.Node, error) // GetLeavesByRange returns leaf data for a range of indexes. The returned // slice is a contiguous prefix of leaves in [start, start+count) ordered by // LeafIndex. It will be shorter than `count` if the requested range has // missing entries (e.g., it extends beyond the size of a LOG tree), or // `count` is too big to handle in one go. // For PREORDERED_LOG trees, *must* return leaves beyond the tree size if // they are stored, in order to allow integrating them into the tree. GetLeavesByRange(ctx context.Context, start, count int64) ([]*trillian.LogLeaf, error) // GetLeavesByHash looks up sequenced leaf metadata and data by their Merkle leaf hash. If the // tree permits duplicate leaves callers must be prepared to handle multiple results with the // same hash but different sequence numbers. If orderBySequence is true then the returned data // will be in ascending sequence number order. GetLeavesByHash(ctx context.Context, leafHashes [][]byte, orderBySequence bool) ([]*trillian.LogLeaf, error) // LatestSignedLogRoot returns the most recent SignedLogRoot, if any. LatestSignedLogRoot(ctx context.Context) (*trillian.SignedLogRoot, error) } // LogTreeTX is the transactional interface for reading/updating a Log. // After a call to Commit or Close implementations must be in a clean state and have // released any resources owned by the LogTreeTX. // A LogTreeTX can only modify the tree specified in its creation. type LogTreeTX interface { ReadOnlyLogTreeTX // SetMerkleNodes writes the nodes, at the write revision. // // TODO(pavelkalinnikov): Use tiles instead, here and in GetMerkleNodes. SetMerkleNodes(ctx context.Context, nodes []tree.Node) error // StoreSignedLogRoot stores a freshly created SignedLogRoot. StoreSignedLogRoot(ctx context.Context, root *trillian.SignedLogRoot) error // DequeueLeaves returns between [0, limit] leaves to be integrated to the // tree. // // For LOG trees: // - The leaves are taken from the queue. // - If the Tx is rolled back, they become available for dequeueing again. // // For PREORDERED_LOG trees: // - The leaves are taken from the head of as yet un-integrated part of the // sequenced entries, immediately following the current SignedLogRoot tree // size. // - The operation is a no-op with regards to the sequenced entries. // // Leaves queued more recently than the cutoff time will not be returned. // This allows for guard intervals to be configured, and (in case of // PREORDERED_LOG trees) avoiding contention between log signer and writers // appending new entries. // // This method is not required to return fully populated LogLeaf structures, // but it *must* include MerkleLeafHash, QueueTimestamp, and LeafIndex (for // PREORDERED_LOG trees). Storage implementations might apply optimizations // employing this property. Consult the call sites of this method to be sure. DequeueLeaves(ctx context.Context, limit int, cutoff time.Time) ([]*trillian.LogLeaf, error) // UpdateSequencedLeaves associates the leaves with the sequence numbers // assigned to them. UpdateSequencedLeaves(ctx context.Context, leaves []*trillian.LogLeaf) error } // ReadOnlyLogStorage represents a narrowed read-only view into a LogStorage. type ReadOnlyLogStorage interface { // CheckDatabaseAccessible returns nil if the database is accessible, or an // error otherwise. CheckDatabaseAccessible(context.Context) error // GetActiveLogIDs returns a list of the IDs of all the logs that are // configured in storage and are eligible to have entries sequenced. GetActiveLogIDs(ctx context.Context) ([]int64, error) // SnapshotForTree starts a read-only transaction for the specified treeID. // Commit must be called when the caller is finished with the returned object, // and values read through it should only be propagated if Commit returns // without error. SnapshotForTree(ctx context.Context, tree *trillian.Tree) (ReadOnlyLogTreeTX, error) } // LogTXFunc is the func signature for passing into ReadWriteTransaction. type LogTXFunc func(context.Context, LogTreeTX) error // LogStorage should be implemented by concrete storage mechanisms which want to support Logs. type LogStorage interface { ReadOnlyLogStorage // ReadWriteTransaction starts a RW transaction on the underlying storage, and // calls f with it. // If f fails and returns an error, the storage implementation may optionally // retry with a new transaction, and f MUST NOT keep state across calls. ReadWriteTransaction(ctx context.Context, tree *trillian.Tree, f LogTXFunc) error // QueueLeaves enqueues leaves for later integration into the tree. // If error is nil, the returned slice of leaves will be the same size as the // input, and each entry will hold a passed-in leaf struct and a Status // representing the outcome for that particular leaf: // * a status of OK indicates that the leaf was successfully queued. // * a status of AlreadyExists indicates that the leaf was a duplicate, in this case // the returned leaf data is that of the original. // Other status values may be returned in error cases. // // Duplicates are only reported if the underlying tree does not permit duplicates, and are // considered duplicate if their leaf.LeafIdentityHash matches. QueueLeaves(ctx context.Context, tree *trillian.Tree, leaves []*trillian.LogLeaf, queueTimestamp time.Time) ([]*trillian.QueuedLogLeaf, error) // AddSequencedLeaves stores the `leaves` and associates them with the log // positions according to their `LeafIndex` field. The indices must be // contiguous. // // If error is nil, the returned slice is the same size as the input, entries // correspond to the `leaves` in the same order. Each entry describes the // result of adding the corresponding leaf. // // Possible `QueuedLogLeaf.status` values with their semantics: // - OK: The leaf has been successfully stored. // - AlreadyExists: The storage already contains an identical leaf at the // specified `LeafIndex`. That leaf is returned in `QueuedLogLeaf.leaf`. // - FailedPrecondition: There is another leaf with the same `LeafIndex`, // but a different value. That leaf is returned in `QueuedLogLeaf.leaf`. // - OutOfRange: The leaf can not be stored at the specified `LeafIndex`. // For example, the storage might not support non-sequential writes. // - Internal, etc: A storage-specific error. // // TODO(pavelkalinnikov): Make returning the resulting/conflicting leaves // optional. Channel these options to the top-level Log API. // TODO(pavelkalinnikov): Not checking values of the occupied indices might // be a good optimization. Could also be optional. AddSequencedLeaves(ctx context.Context, tree *trillian.Tree, leaves []*trillian.LogLeaf, timestamp time.Time) ([]*trillian.QueuedLogLeaf, error) } trillian-1.6.1/storage/memory/000077500000000000000000000000001466362047600163275ustar00rootroot00000000000000trillian-1.6.1/storage/memory/admin_storage.go000066400000000000000000000113001466362047600214650ustar00rootroot00000000000000// 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 memory import ( "context" "fmt" "sync" "time" "github.com/google/trillian" "github.com/google/trillian/storage" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/timestamppb" "k8s.io/klog/v2" ) // NewAdminStorage returns a storage.AdminStorage implementation backed by // TreeStorage. func NewAdminStorage(ms *TreeStorage) storage.AdminStorage { return &memoryAdminStorage{ms} } // memoryAdminStorage implements storage.AdminStorage type memoryAdminStorage struct { ms *TreeStorage } func (s *memoryAdminStorage) Snapshot(ctx context.Context) (storage.ReadOnlyAdminTX, error) { return &adminTX{ms: s.ms}, nil } func (s *memoryAdminStorage) ReadWriteTransaction(ctx context.Context, f storage.AdminTXFunc) error { tx := &adminTX{ms: s.ms} defer func() { if err := tx.Close(); err != nil { klog.Errorf("tx.Close(): %v", err) } }() if err := f(ctx, tx); err != nil { return err } return tx.Commit() } func (s *memoryAdminStorage) CheckDatabaseAccessible(ctx context.Context) error { return nil } type adminTX struct { ms *TreeStorage // mu guards reads/writes on closed, which happen on Commit/Close methods. // // We don't check closed on methods apart from the ones above, as we trust tx // to keep tabs on its state, and hence fail to do queries after closed. mu sync.RWMutex closed bool } func (t *adminTX) Commit() error { // TODO(al): The admin implementation isn't transactional. return t.Close() } func (t *adminTX) Close() error { // TODO(al): The admin implementation isn't transactional. t.mu.Lock() defer t.mu.Unlock() t.closed = true return nil } func (t *adminTX) GetTree(ctx context.Context, treeID int64) (*trillian.Tree, error) { tree := t.ms.getTree(treeID) if tree == nil { return nil, fmt.Errorf("no such treeID %d", treeID) } tree.RLock() defer tree.RUnlock() return tree.meta, nil } func (t *adminTX) ListTrees(ctx context.Context, includeDeleted bool) ([]*trillian.Tree, error) { t.ms.mu.RLock() defer t.ms.mu.RUnlock() var ret []*trillian.Tree for _, v := range t.ms.trees { ret = append(ret, v.meta) } return ret, nil } func (t *adminTX) CreateTree(ctx context.Context, tr *trillian.Tree) (*trillian.Tree, error) { if err := storage.ValidateTreeForCreation(ctx, tr); err != nil { return nil, err } if err := validateStorageSettings(tr); err != nil { return nil, err } id, err := storage.NewTreeID() if err != nil { return nil, err } now := time.Now() meta := proto.Clone(tr).(*trillian.Tree) meta.TreeId = id meta.CreateTime = timestamppb.New(now) if err := meta.CreateTime.CheckValid(); err != nil { return nil, err } meta.UpdateTime = timestamppb.New(now) if err := meta.UpdateTime.CheckValid(); err != nil { return nil, err } t.ms.mu.Lock() defer t.ms.mu.Unlock() t.ms.trees[id] = newTree(meta) klog.V(1).Infof("trees: %v", t.ms.trees) return meta, nil } func (t *adminTX) UpdateTree(ctx context.Context, treeID int64, updateFunc func(*trillian.Tree)) (*trillian.Tree, error) { mTree := t.ms.getTree(treeID) mTree.mu.Lock() defer mTree.mu.Unlock() tree := mTree.meta beforeUpdate := proto.Clone(tree).(*trillian.Tree) updateFunc(tree) if err := storage.ValidateTreeForUpdate(ctx, beforeUpdate, tree); err != nil { return nil, err } if err := validateStorageSettings(tree); err != nil { return nil, err } tree.UpdateTime = timestamppb.New(time.Now()) if err := tree.UpdateTime.CheckValid(); err != nil { return nil, err } return tree, nil } func (t *adminTX) SoftDeleteTree(ctx context.Context, treeID int64) (*trillian.Tree, error) { return nil, fmt.Errorf("method not supported: SoftDeleteTree") } func (t *adminTX) HardDeleteTree(ctx context.Context, treeID int64) error { return fmt.Errorf("method not supported: HardDeleteTree") } func (t *adminTX) UndeleteTree(ctx context.Context, treeID int64) (*trillian.Tree, error) { return nil, fmt.Errorf("method not supported: UndeleteTree") } func validateStorageSettings(tree *trillian.Tree) error { if tree.StorageSettings != nil { return fmt.Errorf("storage_settings not supported, but got %v", tree.StorageSettings) } return nil } trillian-1.6.1/storage/memory/doc.go000066400000000000000000000027411466362047600174270ustar00rootroot00000000000000// 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 memory provides a simple in-process implementation of the tree- and // log-storage interfaces. // // This implementation is intended SOLELY for use in integration tests which // exercise properties of the higher levels of Trillian componened - e.g. // an integration test which ensures that the Trillian Log is able to correctly // handle a tree which contains duplicate leaves. // // The storage implementation is based on a BTree, which provides an ordered // key-value space which can be used to store arbitrary items, as well as // scan ranges of keys in order. // // The implementation does provide transaction-like semantics for the // LogStorage interface, although conflict is avoided by each writable // transaction exclusively locking the tree until it's committed or // rolled-back. // // Currently, the Admin Storage does not honor transactional semantics. package memory trillian-1.6.1/storage/memory/log_storage.go000066400000000000000000000274201466362047600211700ustar00rootroot00000000000000// 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 memory import ( "container/list" "context" "fmt" "strconv" "sync" "time" "github.com/google/btree" "github.com/google/trillian" "github.com/google/trillian/monitoring" "github.com/google/trillian/storage" "github.com/google/trillian/storage/cache" stree "github.com/google/trillian/storage/tree" "github.com/google/trillian/types" "github.com/transparency-dev/merkle/compact" "github.com/transparency-dev/merkle/rfc6962" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "k8s.io/klog/v2" ) const logIDLabel = "logid" var ( once sync.Once queuedCounter monitoring.Counter dequeuedCounter monitoring.Counter ) func createMetrics(mf monitoring.MetricFactory) { queuedCounter = mf.NewCounter("mem_queued_leaves", "Number of leaves queued", logIDLabel) dequeuedCounter = mf.NewCounter("mem_dequeued_leaves", "Number of leaves dequeued", logIDLabel) } func labelForTX(t *logTreeTX) string { return strconv.FormatInt(t.treeID, 10) } // unseqKey formats a key for use in a tree's BTree store. // The associated Item value will be a list of unsequenced entries. func unseqKey(treeID int64) btree.Item { return &kv{k: fmt.Sprintf("/%d/unseq", treeID)} } // seqLeafKey formats a key for use in a tree's BTree store. // The associated Item value will be the leaf at the given sequence number. func seqLeafKey(treeID, seq int64) btree.Item { return &kv{k: fmt.Sprintf("/%d/seq/%020d", treeID, seq)} } // hashToSeqKey formats a key for use in a tree's BTree store. // The associated Item value will be the sequence number for the leaf with // the given hash. func hashToSeqKey(treeID int64) btree.Item { return &kv{k: fmt.Sprintf("/%d/h2s", treeID)} } // sthKey formats a key for use in a tree's BTree store. // The associated Item value will be the STH with the given timestamp. func sthKey(treeID int64, timestamp uint64) btree.Item { return &kv{k: fmt.Sprintf("/%d/sth/%020d", treeID, timestamp)} } // revKey formats a key for use in a tree's BTree store. The associated Item // value will be the revision number for the given timestamp. func revKey(treeID int64, timestamp uint64) btree.Item { return &kv{k: fmt.Sprintf("/%d/rev/%020d", treeID, timestamp)} } type memoryLogStorage struct { *TreeStorage metricFactory monitoring.MetricFactory } // NewLogStorage creates an in-memory LogStorage instance. func NewLogStorage(ts *TreeStorage, mf monitoring.MetricFactory) storage.LogStorage { if mf == nil { mf = monitoring.InertMetricFactory{} } ret := &memoryLogStorage{ TreeStorage: ts, metricFactory: mf, } return ret } func (m *memoryLogStorage) CheckDatabaseAccessible(ctx context.Context) error { return nil } // GetActiveLogIDs returns the IDs of all logs that are currently in a state // that requires sequencing (e.g. ACTIVE, DRAINING). func (m *memoryLogStorage) GetActiveLogIDs(ctx context.Context) ([]int64, error) { m.mu.RLock() defer m.mu.RUnlock() var ret []int64 for id, tree := range m.trees { if tree.meta.GetDeleted() { continue } switch tree.meta.GetTreeType() { case trillian.TreeType_LOG, trillian.TreeType_PREORDERED_LOG: switch tree.meta.GetTreeState() { case trillian.TreeState_ACTIVE, trillian.TreeState_DRAINING: ret = append(ret, id) } } } return ret, nil } func (m *memoryLogStorage) beginInternal(ctx context.Context, tree *trillian.Tree, readonly bool) (*logTreeTX, error) { once.Do(func() { createMetrics(m.metricFactory) }) stCache := cache.NewLogSubtreeCache(rfc6962.DefaultHasher) ttx, err := m.TreeStorage.beginTreeTX(ctx, tree.TreeId, rfc6962.DefaultHasher.Size(), stCache, readonly) if err != nil { return nil, err } ltx := &logTreeTX{ treeTX: ttx, ls: m, } var rev int64 ltx.slr, rev, err = ltx.fetchLatestRoot(ctx) if err == storage.ErrTreeNeedsInit { return ltx, err } else if err != nil { if err := ttx.Close(); err != nil { klog.Errorf("ttx.Close(): %v", err) } return nil, err } if err := ltx.root.UnmarshalBinary(ltx.slr.LogRoot); err != nil { if err := ttx.Close(); err != nil { klog.Errorf("ttx.Close(): %v", err) } return nil, err } ltx.treeTX.writeRevision = rev + 1 return ltx, nil } func (m *memoryLogStorage) ReadWriteTransaction(ctx context.Context, tree *trillian.Tree, f storage.LogTXFunc) error { tx, err := m.beginInternal(ctx, tree, false /* readonly */) if err != nil && err != storage.ErrTreeNeedsInit { return err } defer func() { if err := tx.Close(); err != nil { klog.Errorf("tx.Close(): %v", err) } }() if err := f(ctx, tx); err != nil { return err } return tx.Commit(ctx) } func (m *memoryLogStorage) AddSequencedLeaves(ctx context.Context, tree *trillian.Tree, leaves []*trillian.LogLeaf, timestamp time.Time) ([]*trillian.QueuedLogLeaf, error) { return nil, status.Errorf(codes.Unimplemented, "AddSequencedLeaves is not implemented") } func (m *memoryLogStorage) SnapshotForTree(ctx context.Context, tree *trillian.Tree) (storage.ReadOnlyLogTreeTX, error) { tx, err := m.beginInternal(ctx, tree, true /* readonly */) if err != nil { return nil, err } return tx, err } func (m *memoryLogStorage) QueueLeaves(ctx context.Context, tree *trillian.Tree, leaves []*trillian.LogLeaf, queueTimestamp time.Time) ([]*trillian.QueuedLogLeaf, error) { tx, err := m.beginInternal(ctx, tree, false /* readonly */) if tx != nil { // Ensure we don't leak the transaction. For example if we get an // ErrTreeNeedsInit from beginInternal() or if QueueLeaves fails // below. defer func() { if err := tx.Close(); err != nil { klog.Errorf("tx.Close(): %v", err) } }() } if err != nil { return nil, err } existing, err := tx.QueueLeaves(ctx, leaves, queueTimestamp) if err != nil { return nil, err } if err := tx.Commit(ctx); err != nil { return nil, err } ret := make([]*trillian.QueuedLogLeaf, len(leaves)) for i, e := range existing { if e != nil { ret[i] = &trillian.QueuedLogLeaf{ Leaf: e, Status: status.Newf(codes.AlreadyExists, "leaf already exists: %v", e.LeafIdentityHash).Proto(), } continue } ret[i] = &trillian.QueuedLogLeaf{Leaf: leaves[i]} } return ret, nil } type logTreeTX struct { treeTX ls *memoryLogStorage root types.LogRootV1 slr *trillian.SignedLogRoot } // GetMerkleNodes returns the requested nodes at (or below) the read revision. func (t *logTreeTX) GetMerkleNodes(ctx context.Context, ids []compact.NodeID) ([]stree.Node, error) { rev := t.treeTX.writeRevision - 1 return t.treeTX.subtreeCache.GetNodes(ids, t.treeTX.getSubtreesAtRev(ctx, rev)) } func (t *logTreeTX) DequeueLeaves(ctx context.Context, limit int, cutoffTime time.Time) ([]*trillian.LogLeaf, error) { leaves := make([]*trillian.LogLeaf, 0, limit) q := t.tx.Get(unseqKey(t.treeID)).(*kv).v.(*list.List) e := q.Front() for i := 0; i < limit && e != nil; i++ { // TODO(al): consider cutoffTime leaves = append(leaves, e.Value.(*trillian.LogLeaf)) e = e.Next() } dequeuedCounter.Add(float64(len(leaves)), labelForTX(t)) return leaves, nil } func (t *logTreeTX) QueueLeaves(ctx context.Context, leaves []*trillian.LogLeaf, queueTimestamp time.Time) ([]*trillian.LogLeaf, error) { // Don't accept batches if any of the leaves are invalid. for _, leaf := range leaves { if len(leaf.LeafIdentityHash) != t.hashSizeBytes { return nil, fmt.Errorf("queued leaf must have a leaf ID hash of length %d", t.hashSizeBytes) } } queuedCounter.Add(float64(len(leaves)), labelForTX(t)) // No deduping in this storage! k := unseqKey(t.treeID) q := t.tx.Get(k).(*kv).v.(*list.List) for _, l := range leaves { q.PushBack(l) } return make([]*trillian.LogLeaf, len(leaves)), nil } func (t *logTreeTX) AddSequencedLeaves(ctx context.Context, leaves []*trillian.LogLeaf, timestamp time.Time) ([]*trillian.QueuedLogLeaf, error) { return nil, status.Errorf(codes.Unimplemented, "AddSequencedLeaves is not implemented") } func (t *logTreeTX) GetLeavesByRange(ctx context.Context, start, count int64) ([]*trillian.LogLeaf, error) { ret := make([]*trillian.LogLeaf, 0, count) for i := int64(0); i < count; i++ { leaf := t.tx.Get(seqLeafKey(t.treeID, start+i)) if leaf != nil { ret = append(ret, leaf.(*kv).v.(*trillian.LogLeaf)) } } return ret, nil } func (t *logTreeTX) GetLeavesByHash(ctx context.Context, leafHashes [][]byte, orderBySequence bool) ([]*trillian.LogLeaf, error) { m := t.tx.Get(hashToSeqKey(t.treeID)).(*kv).v.(map[string][]int64) ret := make([]*trillian.LogLeaf, 0, len(leafHashes)) for _, hash := range leafHashes { seq, ok := m[string(hash)] if !ok { continue } for _, s := range seq { l := t.tx.Get(seqLeafKey(t.treeID, s)) if l == nil { continue } ret = append(ret, l.(*kv).v.(*trillian.LogLeaf)) } } return ret, nil } func (t *logTreeTX) LatestSignedLogRoot(ctx context.Context) (*trillian.SignedLogRoot, error) { return t.slr, nil } // fetchLatestRoot reads the latest SignedLogRoot from the DB and returns it. func (t *logTreeTX) fetchLatestRoot(ctx context.Context) (*trillian.SignedLogRoot, int64, error) { r := t.tx.Get(sthKey(t.treeID, t.tree.currentSTH)) if r == nil { return nil, 0, storage.ErrTreeNeedsInit } sth := r.(*kv).v.(*trillian.SignedLogRoot) r = t.tx.Get(revKey(t.treeID, t.tree.currentSTH)) if r == nil { return nil, 0, storage.ErrTreeNeedsInit } rev := r.(*kv).v.(int64) return sth, rev, nil } func (t *logTreeTX) StoreSignedLogRoot(ctx context.Context, slr *trillian.SignedLogRoot) error { var root types.LogRootV1 if err := root.UnmarshalBinary(slr.LogRoot); err != nil { return err } k := sthKey(t.treeID, root.TimestampNanos) k.(*kv).v = slr t.tx.ReplaceOrInsert(k) k = revKey(t.treeID, root.TimestampNanos) k.(*kv).v = t.treeTX.writeRevision t.tx.ReplaceOrInsert(k) // TODO(alcutter): this breaks the transactional model if root.TimestampNanos > t.tree.currentSTH { t.tree.currentSTH = root.TimestampNanos } return nil } func (t *logTreeTX) UpdateSequencedLeaves(ctx context.Context, leaves []*trillian.LogLeaf) error { countByMerkleHash := make(map[string]int) for _, leaf := range leaves { // This should fail on insert but catch it early if got, want := len(leaf.LeafIdentityHash), t.hashSizeBytes; got != want { return fmt.Errorf("sequenced leaf has incorrect hash size: got %v, want %v", got, want) } mh := string(leaf.MerkleLeafHash) countByMerkleHash[mh]++ // insert sequenced leaf: k := seqLeafKey(t.treeID, leaf.LeafIndex) k.(*kv).v = leaf t.tx.ReplaceOrInsert(k) // update merkle-to-seq mapping: m := t.tx.Get(hashToSeqKey(t.treeID)) l := m.(*kv).v.(map[string][]int64)[string(leaf.MerkleLeafHash)] l = append(l, leaf.LeafIndex) m.(*kv).v.(map[string][]int64)[string(leaf.MerkleLeafHash)] = l } q := t.tx.Get(unseqKey(t.treeID)).(*kv).v.(*list.List) toRemove := make([]*list.Element, 0, q.Len()) for e := q.Front(); e != nil && len(countByMerkleHash) > 0; e = e.Next() { h := e.Value.(*trillian.LogLeaf).MerkleLeafHash mh := string(h) if countByMerkleHash[mh] > 0 { countByMerkleHash[mh]-- toRemove = append(toRemove, e) if countByMerkleHash[mh] == 0 { delete(countByMerkleHash, mh) } } } for _, e := range toRemove { q.Remove(e) } if unknown := len(countByMerkleHash); unknown != 0 { return fmt.Errorf("attempted to update %d unknown leaves: %x", unknown, countByMerkleHash) } return nil } trillian-1.6.1/storage/memory/provider.go000066400000000000000000000025411466362047600205120ustar00rootroot00000000000000// Copyright 2018 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 memory import ( "github.com/google/trillian/monitoring" "github.com/google/trillian/storage" "k8s.io/klog/v2" ) func init() { if err := storage.RegisterProvider("memory", newMemoryStorageProvider); err != nil { klog.Fatalf("Failed to register storage provider memory: %v", err) } } type memProvider struct { mf monitoring.MetricFactory ts *TreeStorage } func newMemoryStorageProvider(mf monitoring.MetricFactory) (storage.Provider, error) { return &memProvider{ mf: mf, ts: NewTreeStorage(), }, nil } func (s *memProvider) LogStorage() storage.LogStorage { return NewLogStorage(s.ts, s.mf) } func (s *memProvider) AdminStorage() storage.AdminStorage { return NewAdminStorage(s.ts) } func (s *memProvider) Close() error { return nil } trillian-1.6.1/storage/memory/provider_test.go000066400000000000000000000032661466362047600215560ustar00rootroot00000000000000// Copyright 2018 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 memory import ( "testing" "github.com/google/trillian/storage" ) func TestMemoryStorageProvider(t *testing.T) { sp, err := storage.NewProvider("memory", nil) if err != nil { t.Fatalf("Got an unexpected error: %v", err) } if sp == nil { t.Fatal("Got a nil storage provider.") } } func TestMemoryStorageProviderLogStorage(t *testing.T) { sp, err := storage.NewProvider("memory", nil) if err != nil { t.Fatalf("Got an unexpected error: %v", err) } ls := sp.LogStorage() if ls == nil { t.Fatal("Got a nil log storage interface.") } } func TestMemoryStorageProviderAdminStorage(t *testing.T) { sp, err := storage.NewProvider("memory", nil) if err != nil { t.Fatalf("Got an unexpected error: %v", err) } as := sp.AdminStorage() if as == nil { t.Fatal("Got a nil admin storage interface.") } } func TestMemoryStorageProviderClose(t *testing.T) { sp, err := storage.NewProvider("memory", nil) if err != nil { t.Fatalf("Got an unexpected error: %v", err) } err = sp.Close() if err != nil { t.Fatalf("Failed to close the memory storage provider: %v", err) } } trillian-1.6.1/storage/memory/tree_debug.go000066400000000000000000000033301466362047600207620ustar00rootroot00000000000000// 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 memory import ( "github.com/google/btree" "github.com/google/trillian/storage" "github.com/google/trillian/storage/storagepb" "k8s.io/klog/v2" ) // This file contains utilities that are not part of the Storage API contracts but may // be useful for development or debugging. // Dump ascends the tree, logging the items contained. func Dump(t *btree.BTree) { t.Ascend(func(i btree.Item) bool { klog.Infof("%#v", i) return true }) } // DumpSubtrees will traverse the BTree and execute a callback on each subtree proto // that it contains. The traversal will be 'in order' according to the BTree keys, which // may not be useful at the application level. func DumpSubtrees(ls storage.LogStorage, treeID int64, callback func(string, *storagepb.SubtreeProto)) { m := ls.(*memoryLogStorage) tree := m.trees[treeID] pi := subtreeKey(treeID, 0, []byte{}) tree.store.AscendGreaterOrEqual(pi, func(bi btree.Item) bool { i := bi.(*kv) if _, ok := i.v.(*storagepb.SubtreeProto); !ok { // Then we've finished iterating over subtrees return false } callback(i.k, i.v.(*storagepb.SubtreeProto)) return true }) } trillian-1.6.1/storage/memory/tree_storage.go000066400000000000000000000146451466362047600213530ustar00rootroot00000000000000// 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 memory import ( "container/list" "context" "fmt" "strings" "sync" "github.com/google/btree" "github.com/google/trillian" "github.com/google/trillian/storage/cache" "github.com/google/trillian/storage/storagepb" stree "github.com/google/trillian/storage/tree" "google.golang.org/protobuf/proto" "k8s.io/klog/v2" ) const degree = 8 // subtreeKey formats a key for use in a tree's BTree store. The associated // Item value will be the SubtreeProto with the given prefix. func subtreeKey(treeID, rev int64, prefix []byte) btree.Item { return &kv{k: fmt.Sprintf("/%d/subtree/%x/%d", treeID, prefix, rev)} } // tree stores all data for a given treeID type tree struct { // mu protects access to all tree members. mu sync.RWMutex // store is a key-value representation of a Trillian tree storage. // The keyspace is partitioned off into various prefixes for the different // 'tables' of things stored in there. // e.g. subtree protos are stored with a key returned by subtreeKey() above. // // Other prefixes are used by Log/Map Storage. // // See the various key formatting functions for details of what is stored // under the formatted keys. // // store uses a BTree so that we can have a defined ordering over things // (such as sequenced leaves), while still accessing by key. store *btree.BTree // currentSTH is the timestamp of the current STH. currentSTH uint64 meta *trillian.Tree } func (t *tree) Lock() { t.mu.Lock() } func (t *tree) Unlock() { t.mu.Unlock() } func (t *tree) RLock() { t.mu.RLock() } func (t *tree) RUnlock() { t.mu.RUnlock() } // TreeStorage is shared between the memoryLog and (forthcoming) memoryMap- // Storage implementations, and contains functionality which is common to both, type TreeStorage struct { // mu only protects access to the trees map. mu sync.RWMutex trees map[int64]*tree } // NewTreeStorage returns a new instance of the in-memory tree storage database. func NewTreeStorage() *TreeStorage { return &TreeStorage{ trees: make(map[int64]*tree), } } // getTree returns the tree associated with id, or nil if no such tree exists. func (m *TreeStorage) getTree(id int64) *tree { m.mu.RLock() defer m.mu.RUnlock() return m.trees[id] } // kv is a simple key->value type which implements btree's Item interface. type kv struct { k string v interface{} } // Less than by k's string key func (a kv) Less(b btree.Item) bool { return strings.Compare(a.k, b.(*kv).k) < 0 } // newTree creates and initializes a tree struct. func newTree(t *trillian.Tree) *tree { ret := &tree{ store: btree.New(degree), meta: proto.Clone(t).(*trillian.Tree), } k := unseqKey(t.TreeId) k.(*kv).v = list.New() ret.store.ReplaceOrInsert(k) k = hashToSeqKey(t.TreeId) k.(*kv).v = make(map[string][]int64) ret.store.ReplaceOrInsert(k) return ret } func (m *TreeStorage) beginTreeTX(ctx context.Context, treeID int64, hashSizeBytes int, cache *cache.SubtreeCache, readonly bool) (treeTX, error) { tree := m.getTree(treeID) // Lock the tree for the duration of the TX. // It will be unlocked by a call to Commit or Close. var unlock func() if readonly { tree.RLock() unlock = tree.RUnlock } else { tree.Lock() unlock = tree.Unlock } return treeTX{ ts: m, tx: tree.store.Clone(), tree: tree, treeID: treeID, hashSizeBytes: hashSizeBytes, subtreeCache: cache, writeRevision: -1, unlock: unlock, }, nil } type treeTX struct { closed bool tx *btree.BTree ts *TreeStorage tree *tree treeID int64 hashSizeBytes int subtreeCache *cache.SubtreeCache writeRevision int64 unlock func() } func (t *treeTX) getSubtrees(ctx context.Context, treeRevision int64, ids [][]byte) ([]*storagepb.SubtreeProto, error) { if len(ids) == 0 { return nil, nil } ret := make([]*storagepb.SubtreeProto, 0, len(ids)) for _, id := range ids { // Look for a nodeID at or below treeRevision: for r := treeRevision; r >= 0; r-- { s := t.tx.Get(subtreeKey(t.treeID, r, id)) if s == nil { continue } // Return a copy of the proto to protect against the caller modifying the stored one. p := s.(*kv).v.(*storagepb.SubtreeProto) v := proto.Clone(p).(*storagepb.SubtreeProto) ret = append(ret, v) break } } // The InternalNodes cache is possibly nil here, but the SubtreeCache (which called // this method) will re-populate it. return ret, nil } func (t *treeTX) storeSubtrees(ctx context.Context, subtrees []*storagepb.SubtreeProto) error { if len(subtrees) == 0 { klog.Warning("attempted to store 0 subtrees...") return nil } for _, s := range subtrees { s := s if s.Prefix == nil { panic(fmt.Errorf("nil prefix on %v", s)) } k := subtreeKey(t.treeID, t.writeRevision, s.Prefix) k.(*kv).v = s t.tx.ReplaceOrInsert(k) } return nil } // getSubtreesAtRev returns a GetSubtreesFunc which reads at the passed in rev. func (t *treeTX) getSubtreesAtRev(ctx context.Context, rev int64) cache.GetSubtreesFunc { return func(ids [][]byte) ([]*storagepb.SubtreeProto, error) { return t.getSubtrees(ctx, rev, ids) } } func (t *treeTX) SetMerkleNodes(ctx context.Context, nodes []stree.Node) error { rev := t.writeRevision - 1 return t.subtreeCache.SetNodes(nodes, t.getSubtreesAtRev(ctx, rev)) } func (t *treeTX) Commit(ctx context.Context) error { defer t.unlock() if t.writeRevision > -1 { tiles, err := t.subtreeCache.UpdatedTiles() if err != nil { klog.Warningf("SubtreeCache updated tiles error: %v", err) return err } if err := t.storeSubtrees(ctx, tiles); err != nil { klog.Warningf("TX commit flush error: %v", err) return err } } t.closed = true // update the shared view of the tree post TX: t.tree.store = t.tx return nil } func (t *treeTX) Close() error { if t.closed { return nil } defer t.unlock() t.closed = true return nil } trillian-1.6.1/storage/mock_storage.go000066400000000000000000000642561466362047600200400ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/google/trillian/storage (interfaces: AdminStorage,AdminTX,LogStorage,LogTreeTX,ReadOnlyAdminTX,ReadOnlyLogTreeTX) // Package storage is a generated GoMock package. package storage import ( context "context" reflect "reflect" time "time" gomock "github.com/golang/mock/gomock" trillian "github.com/google/trillian" tree "github.com/google/trillian/storage/tree" compact "github.com/transparency-dev/merkle/compact" ) // MockAdminStorage is a mock of AdminStorage interface. type MockAdminStorage struct { ctrl *gomock.Controller recorder *MockAdminStorageMockRecorder } // MockAdminStorageMockRecorder is the mock recorder for MockAdminStorage. type MockAdminStorageMockRecorder struct { mock *MockAdminStorage } // NewMockAdminStorage creates a new mock instance. func NewMockAdminStorage(ctrl *gomock.Controller) *MockAdminStorage { mock := &MockAdminStorage{ctrl: ctrl} mock.recorder = &MockAdminStorageMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockAdminStorage) EXPECT() *MockAdminStorageMockRecorder { return m.recorder } // CheckDatabaseAccessible mocks base method. func (m *MockAdminStorage) CheckDatabaseAccessible(arg0 context.Context) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CheckDatabaseAccessible", arg0) ret0, _ := ret[0].(error) return ret0 } // CheckDatabaseAccessible indicates an expected call of CheckDatabaseAccessible. func (mr *MockAdminStorageMockRecorder) CheckDatabaseAccessible(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CheckDatabaseAccessible", reflect.TypeOf((*MockAdminStorage)(nil).CheckDatabaseAccessible), arg0) } // ReadWriteTransaction mocks base method. func (m *MockAdminStorage) ReadWriteTransaction(arg0 context.Context, arg1 AdminTXFunc) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReadWriteTransaction", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // ReadWriteTransaction indicates an expected call of ReadWriteTransaction. func (mr *MockAdminStorageMockRecorder) ReadWriteTransaction(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadWriteTransaction", reflect.TypeOf((*MockAdminStorage)(nil).ReadWriteTransaction), arg0, arg1) } // Snapshot mocks base method. func (m *MockAdminStorage) Snapshot(arg0 context.Context) (ReadOnlyAdminTX, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Snapshot", arg0) ret0, _ := ret[0].(ReadOnlyAdminTX) ret1, _ := ret[1].(error) return ret0, ret1 } // Snapshot indicates an expected call of Snapshot. func (mr *MockAdminStorageMockRecorder) Snapshot(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Snapshot", reflect.TypeOf((*MockAdminStorage)(nil).Snapshot), arg0) } // MockAdminTX is a mock of AdminTX interface. type MockAdminTX struct { ctrl *gomock.Controller recorder *MockAdminTXMockRecorder } // MockAdminTXMockRecorder is the mock recorder for MockAdminTX. type MockAdminTXMockRecorder struct { mock *MockAdminTX } // NewMockAdminTX creates a new mock instance. func NewMockAdminTX(ctrl *gomock.Controller) *MockAdminTX { mock := &MockAdminTX{ctrl: ctrl} mock.recorder = &MockAdminTXMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockAdminTX) EXPECT() *MockAdminTXMockRecorder { return m.recorder } // Close mocks base method. func (m *MockAdminTX) Close() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Close") ret0, _ := ret[0].(error) return ret0 } // Close indicates an expected call of Close. func (mr *MockAdminTXMockRecorder) Close() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockAdminTX)(nil).Close)) } // Commit mocks base method. func (m *MockAdminTX) Commit() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Commit") ret0, _ := ret[0].(error) return ret0 } // Commit indicates an expected call of Commit. func (mr *MockAdminTXMockRecorder) Commit() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Commit", reflect.TypeOf((*MockAdminTX)(nil).Commit)) } // CreateTree mocks base method. func (m *MockAdminTX) CreateTree(arg0 context.Context, arg1 *trillian.Tree) (*trillian.Tree, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateTree", arg0, arg1) ret0, _ := ret[0].(*trillian.Tree) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateTree indicates an expected call of CreateTree. func (mr *MockAdminTXMockRecorder) CreateTree(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateTree", reflect.TypeOf((*MockAdminTX)(nil).CreateTree), arg0, arg1) } // GetTree mocks base method. func (m *MockAdminTX) GetTree(arg0 context.Context, arg1 int64) (*trillian.Tree, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTree", arg0, arg1) ret0, _ := ret[0].(*trillian.Tree) ret1, _ := ret[1].(error) return ret0, ret1 } // GetTree indicates an expected call of GetTree. func (mr *MockAdminTXMockRecorder) GetTree(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTree", reflect.TypeOf((*MockAdminTX)(nil).GetTree), arg0, arg1) } // HardDeleteTree mocks base method. func (m *MockAdminTX) HardDeleteTree(arg0 context.Context, arg1 int64) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "HardDeleteTree", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // HardDeleteTree indicates an expected call of HardDeleteTree. func (mr *MockAdminTXMockRecorder) HardDeleteTree(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HardDeleteTree", reflect.TypeOf((*MockAdminTX)(nil).HardDeleteTree), arg0, arg1) } // ListTrees mocks base method. func (m *MockAdminTX) ListTrees(arg0 context.Context, arg1 bool) ([]*trillian.Tree, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListTrees", arg0, arg1) ret0, _ := ret[0].([]*trillian.Tree) ret1, _ := ret[1].(error) return ret0, ret1 } // ListTrees indicates an expected call of ListTrees. func (mr *MockAdminTXMockRecorder) ListTrees(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListTrees", reflect.TypeOf((*MockAdminTX)(nil).ListTrees), arg0, arg1) } // SoftDeleteTree mocks base method. func (m *MockAdminTX) SoftDeleteTree(arg0 context.Context, arg1 int64) (*trillian.Tree, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SoftDeleteTree", arg0, arg1) ret0, _ := ret[0].(*trillian.Tree) ret1, _ := ret[1].(error) return ret0, ret1 } // SoftDeleteTree indicates an expected call of SoftDeleteTree. func (mr *MockAdminTXMockRecorder) SoftDeleteTree(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SoftDeleteTree", reflect.TypeOf((*MockAdminTX)(nil).SoftDeleteTree), arg0, arg1) } // UndeleteTree mocks base method. func (m *MockAdminTX) UndeleteTree(arg0 context.Context, arg1 int64) (*trillian.Tree, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UndeleteTree", arg0, arg1) ret0, _ := ret[0].(*trillian.Tree) ret1, _ := ret[1].(error) return ret0, ret1 } // UndeleteTree indicates an expected call of UndeleteTree. func (mr *MockAdminTXMockRecorder) UndeleteTree(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UndeleteTree", reflect.TypeOf((*MockAdminTX)(nil).UndeleteTree), arg0, arg1) } // UpdateTree mocks base method. func (m *MockAdminTX) UpdateTree(arg0 context.Context, arg1 int64, arg2 func(*trillian.Tree)) (*trillian.Tree, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateTree", arg0, arg1, arg2) ret0, _ := ret[0].(*trillian.Tree) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateTree indicates an expected call of UpdateTree. func (mr *MockAdminTXMockRecorder) UpdateTree(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateTree", reflect.TypeOf((*MockAdminTX)(nil).UpdateTree), arg0, arg1, arg2) } // MockLogStorage is a mock of LogStorage interface. type MockLogStorage struct { ctrl *gomock.Controller recorder *MockLogStorageMockRecorder } // MockLogStorageMockRecorder is the mock recorder for MockLogStorage. type MockLogStorageMockRecorder struct { mock *MockLogStorage } // NewMockLogStorage creates a new mock instance. func NewMockLogStorage(ctrl *gomock.Controller) *MockLogStorage { mock := &MockLogStorage{ctrl: ctrl} mock.recorder = &MockLogStorageMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockLogStorage) EXPECT() *MockLogStorageMockRecorder { return m.recorder } // AddSequencedLeaves mocks base method. func (m *MockLogStorage) AddSequencedLeaves(arg0 context.Context, arg1 *trillian.Tree, arg2 []*trillian.LogLeaf, arg3 time.Time) ([]*trillian.QueuedLogLeaf, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddSequencedLeaves", arg0, arg1, arg2, arg3) ret0, _ := ret[0].([]*trillian.QueuedLogLeaf) ret1, _ := ret[1].(error) return ret0, ret1 } // AddSequencedLeaves indicates an expected call of AddSequencedLeaves. func (mr *MockLogStorageMockRecorder) AddSequencedLeaves(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddSequencedLeaves", reflect.TypeOf((*MockLogStorage)(nil).AddSequencedLeaves), arg0, arg1, arg2, arg3) } // CheckDatabaseAccessible mocks base method. func (m *MockLogStorage) CheckDatabaseAccessible(arg0 context.Context) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CheckDatabaseAccessible", arg0) ret0, _ := ret[0].(error) return ret0 } // CheckDatabaseAccessible indicates an expected call of CheckDatabaseAccessible. func (mr *MockLogStorageMockRecorder) CheckDatabaseAccessible(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CheckDatabaseAccessible", reflect.TypeOf((*MockLogStorage)(nil).CheckDatabaseAccessible), arg0) } // GetActiveLogIDs mocks base method. func (m *MockLogStorage) GetActiveLogIDs(arg0 context.Context) ([]int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetActiveLogIDs", arg0) ret0, _ := ret[0].([]int64) ret1, _ := ret[1].(error) return ret0, ret1 } // GetActiveLogIDs indicates an expected call of GetActiveLogIDs. func (mr *MockLogStorageMockRecorder) GetActiveLogIDs(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetActiveLogIDs", reflect.TypeOf((*MockLogStorage)(nil).GetActiveLogIDs), arg0) } // QueueLeaves mocks base method. func (m *MockLogStorage) QueueLeaves(arg0 context.Context, arg1 *trillian.Tree, arg2 []*trillian.LogLeaf, arg3 time.Time) ([]*trillian.QueuedLogLeaf, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "QueueLeaves", arg0, arg1, arg2, arg3) ret0, _ := ret[0].([]*trillian.QueuedLogLeaf) ret1, _ := ret[1].(error) return ret0, ret1 } // QueueLeaves indicates an expected call of QueueLeaves. func (mr *MockLogStorageMockRecorder) QueueLeaves(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueueLeaves", reflect.TypeOf((*MockLogStorage)(nil).QueueLeaves), arg0, arg1, arg2, arg3) } // ReadWriteTransaction mocks base method. func (m *MockLogStorage) ReadWriteTransaction(arg0 context.Context, arg1 *trillian.Tree, arg2 LogTXFunc) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReadWriteTransaction", arg0, arg1, arg2) ret0, _ := ret[0].(error) return ret0 } // ReadWriteTransaction indicates an expected call of ReadWriteTransaction. func (mr *MockLogStorageMockRecorder) ReadWriteTransaction(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadWriteTransaction", reflect.TypeOf((*MockLogStorage)(nil).ReadWriteTransaction), arg0, arg1, arg2) } // SnapshotForTree mocks base method. func (m *MockLogStorage) SnapshotForTree(arg0 context.Context, arg1 *trillian.Tree) (ReadOnlyLogTreeTX, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SnapshotForTree", arg0, arg1) ret0, _ := ret[0].(ReadOnlyLogTreeTX) ret1, _ := ret[1].(error) return ret0, ret1 } // SnapshotForTree indicates an expected call of SnapshotForTree. func (mr *MockLogStorageMockRecorder) SnapshotForTree(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SnapshotForTree", reflect.TypeOf((*MockLogStorage)(nil).SnapshotForTree), arg0, arg1) } // MockLogTreeTX is a mock of LogTreeTX interface. type MockLogTreeTX struct { ctrl *gomock.Controller recorder *MockLogTreeTXMockRecorder } // MockLogTreeTXMockRecorder is the mock recorder for MockLogTreeTX. type MockLogTreeTXMockRecorder struct { mock *MockLogTreeTX } // NewMockLogTreeTX creates a new mock instance. func NewMockLogTreeTX(ctrl *gomock.Controller) *MockLogTreeTX { mock := &MockLogTreeTX{ctrl: ctrl} mock.recorder = &MockLogTreeTXMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockLogTreeTX) EXPECT() *MockLogTreeTXMockRecorder { return m.recorder } // Close mocks base method. func (m *MockLogTreeTX) Close() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Close") ret0, _ := ret[0].(error) return ret0 } // Close indicates an expected call of Close. func (mr *MockLogTreeTXMockRecorder) Close() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockLogTreeTX)(nil).Close)) } // Commit mocks base method. func (m *MockLogTreeTX) Commit(arg0 context.Context) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Commit", arg0) ret0, _ := ret[0].(error) return ret0 } // Commit indicates an expected call of Commit. func (mr *MockLogTreeTXMockRecorder) Commit(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Commit", reflect.TypeOf((*MockLogTreeTX)(nil).Commit), arg0) } // DequeueLeaves mocks base method. func (m *MockLogTreeTX) DequeueLeaves(arg0 context.Context, arg1 int, arg2 time.Time) ([]*trillian.LogLeaf, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DequeueLeaves", arg0, arg1, arg2) ret0, _ := ret[0].([]*trillian.LogLeaf) ret1, _ := ret[1].(error) return ret0, ret1 } // DequeueLeaves indicates an expected call of DequeueLeaves. func (mr *MockLogTreeTXMockRecorder) DequeueLeaves(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DequeueLeaves", reflect.TypeOf((*MockLogTreeTX)(nil).DequeueLeaves), arg0, arg1, arg2) } // GetLeavesByHash mocks base method. func (m *MockLogTreeTX) GetLeavesByHash(arg0 context.Context, arg1 [][]byte, arg2 bool) ([]*trillian.LogLeaf, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetLeavesByHash", arg0, arg1, arg2) ret0, _ := ret[0].([]*trillian.LogLeaf) ret1, _ := ret[1].(error) return ret0, ret1 } // GetLeavesByHash indicates an expected call of GetLeavesByHash. func (mr *MockLogTreeTXMockRecorder) GetLeavesByHash(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLeavesByHash", reflect.TypeOf((*MockLogTreeTX)(nil).GetLeavesByHash), arg0, arg1, arg2) } // GetLeavesByRange mocks base method. func (m *MockLogTreeTX) GetLeavesByRange(arg0 context.Context, arg1, arg2 int64) ([]*trillian.LogLeaf, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetLeavesByRange", arg0, arg1, arg2) ret0, _ := ret[0].([]*trillian.LogLeaf) ret1, _ := ret[1].(error) return ret0, ret1 } // GetLeavesByRange indicates an expected call of GetLeavesByRange. func (mr *MockLogTreeTXMockRecorder) GetLeavesByRange(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLeavesByRange", reflect.TypeOf((*MockLogTreeTX)(nil).GetLeavesByRange), arg0, arg1, arg2) } // GetMerkleNodes mocks base method. func (m *MockLogTreeTX) GetMerkleNodes(arg0 context.Context, arg1 []compact.NodeID) ([]tree.Node, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetMerkleNodes", arg0, arg1) ret0, _ := ret[0].([]tree.Node) ret1, _ := ret[1].(error) return ret0, ret1 } // GetMerkleNodes indicates an expected call of GetMerkleNodes. func (mr *MockLogTreeTXMockRecorder) GetMerkleNodes(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMerkleNodes", reflect.TypeOf((*MockLogTreeTX)(nil).GetMerkleNodes), arg0, arg1) } // LatestSignedLogRoot mocks base method. func (m *MockLogTreeTX) LatestSignedLogRoot(arg0 context.Context) (*trillian.SignedLogRoot, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "LatestSignedLogRoot", arg0) ret0, _ := ret[0].(*trillian.SignedLogRoot) ret1, _ := ret[1].(error) return ret0, ret1 } // LatestSignedLogRoot indicates an expected call of LatestSignedLogRoot. func (mr *MockLogTreeTXMockRecorder) LatestSignedLogRoot(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LatestSignedLogRoot", reflect.TypeOf((*MockLogTreeTX)(nil).LatestSignedLogRoot), arg0) } // SetMerkleNodes mocks base method. func (m *MockLogTreeTX) SetMerkleNodes(arg0 context.Context, arg1 []tree.Node) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SetMerkleNodes", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // SetMerkleNodes indicates an expected call of SetMerkleNodes. func (mr *MockLogTreeTXMockRecorder) SetMerkleNodes(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetMerkleNodes", reflect.TypeOf((*MockLogTreeTX)(nil).SetMerkleNodes), arg0, arg1) } // StoreSignedLogRoot mocks base method. func (m *MockLogTreeTX) StoreSignedLogRoot(arg0 context.Context, arg1 *trillian.SignedLogRoot) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "StoreSignedLogRoot", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // StoreSignedLogRoot indicates an expected call of StoreSignedLogRoot. func (mr *MockLogTreeTXMockRecorder) StoreSignedLogRoot(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StoreSignedLogRoot", reflect.TypeOf((*MockLogTreeTX)(nil).StoreSignedLogRoot), arg0, arg1) } // UpdateSequencedLeaves mocks base method. func (m *MockLogTreeTX) UpdateSequencedLeaves(arg0 context.Context, arg1 []*trillian.LogLeaf) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateSequencedLeaves", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // UpdateSequencedLeaves indicates an expected call of UpdateSequencedLeaves. func (mr *MockLogTreeTXMockRecorder) UpdateSequencedLeaves(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateSequencedLeaves", reflect.TypeOf((*MockLogTreeTX)(nil).UpdateSequencedLeaves), arg0, arg1) } // MockReadOnlyAdminTX is a mock of ReadOnlyAdminTX interface. type MockReadOnlyAdminTX struct { ctrl *gomock.Controller recorder *MockReadOnlyAdminTXMockRecorder } // MockReadOnlyAdminTXMockRecorder is the mock recorder for MockReadOnlyAdminTX. type MockReadOnlyAdminTXMockRecorder struct { mock *MockReadOnlyAdminTX } // NewMockReadOnlyAdminTX creates a new mock instance. func NewMockReadOnlyAdminTX(ctrl *gomock.Controller) *MockReadOnlyAdminTX { mock := &MockReadOnlyAdminTX{ctrl: ctrl} mock.recorder = &MockReadOnlyAdminTXMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockReadOnlyAdminTX) EXPECT() *MockReadOnlyAdminTXMockRecorder { return m.recorder } // Close mocks base method. func (m *MockReadOnlyAdminTX) Close() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Close") ret0, _ := ret[0].(error) return ret0 } // Close indicates an expected call of Close. func (mr *MockReadOnlyAdminTXMockRecorder) Close() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockReadOnlyAdminTX)(nil).Close)) } // Commit mocks base method. func (m *MockReadOnlyAdminTX) Commit() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Commit") ret0, _ := ret[0].(error) return ret0 } // Commit indicates an expected call of Commit. func (mr *MockReadOnlyAdminTXMockRecorder) Commit() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Commit", reflect.TypeOf((*MockReadOnlyAdminTX)(nil).Commit)) } // GetTree mocks base method. func (m *MockReadOnlyAdminTX) GetTree(arg0 context.Context, arg1 int64) (*trillian.Tree, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTree", arg0, arg1) ret0, _ := ret[0].(*trillian.Tree) ret1, _ := ret[1].(error) return ret0, ret1 } // GetTree indicates an expected call of GetTree. func (mr *MockReadOnlyAdminTXMockRecorder) GetTree(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTree", reflect.TypeOf((*MockReadOnlyAdminTX)(nil).GetTree), arg0, arg1) } // ListTrees mocks base method. func (m *MockReadOnlyAdminTX) ListTrees(arg0 context.Context, arg1 bool) ([]*trillian.Tree, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListTrees", arg0, arg1) ret0, _ := ret[0].([]*trillian.Tree) ret1, _ := ret[1].(error) return ret0, ret1 } // ListTrees indicates an expected call of ListTrees. func (mr *MockReadOnlyAdminTXMockRecorder) ListTrees(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListTrees", reflect.TypeOf((*MockReadOnlyAdminTX)(nil).ListTrees), arg0, arg1) } // MockReadOnlyLogTreeTX is a mock of ReadOnlyLogTreeTX interface. type MockReadOnlyLogTreeTX struct { ctrl *gomock.Controller recorder *MockReadOnlyLogTreeTXMockRecorder } // MockReadOnlyLogTreeTXMockRecorder is the mock recorder for MockReadOnlyLogTreeTX. type MockReadOnlyLogTreeTXMockRecorder struct { mock *MockReadOnlyLogTreeTX } // NewMockReadOnlyLogTreeTX creates a new mock instance. func NewMockReadOnlyLogTreeTX(ctrl *gomock.Controller) *MockReadOnlyLogTreeTX { mock := &MockReadOnlyLogTreeTX{ctrl: ctrl} mock.recorder = &MockReadOnlyLogTreeTXMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockReadOnlyLogTreeTX) EXPECT() *MockReadOnlyLogTreeTXMockRecorder { return m.recorder } // Close mocks base method. func (m *MockReadOnlyLogTreeTX) Close() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Close") ret0, _ := ret[0].(error) return ret0 } // Close indicates an expected call of Close. func (mr *MockReadOnlyLogTreeTXMockRecorder) Close() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockReadOnlyLogTreeTX)(nil).Close)) } // Commit mocks base method. func (m *MockReadOnlyLogTreeTX) Commit(arg0 context.Context) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Commit", arg0) ret0, _ := ret[0].(error) return ret0 } // Commit indicates an expected call of Commit. func (mr *MockReadOnlyLogTreeTXMockRecorder) Commit(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Commit", reflect.TypeOf((*MockReadOnlyLogTreeTX)(nil).Commit), arg0) } // GetLeavesByHash mocks base method. func (m *MockReadOnlyLogTreeTX) GetLeavesByHash(arg0 context.Context, arg1 [][]byte, arg2 bool) ([]*trillian.LogLeaf, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetLeavesByHash", arg0, arg1, arg2) ret0, _ := ret[0].([]*trillian.LogLeaf) ret1, _ := ret[1].(error) return ret0, ret1 } // GetLeavesByHash indicates an expected call of GetLeavesByHash. func (mr *MockReadOnlyLogTreeTXMockRecorder) GetLeavesByHash(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLeavesByHash", reflect.TypeOf((*MockReadOnlyLogTreeTX)(nil).GetLeavesByHash), arg0, arg1, arg2) } // GetLeavesByRange mocks base method. func (m *MockReadOnlyLogTreeTX) GetLeavesByRange(arg0 context.Context, arg1, arg2 int64) ([]*trillian.LogLeaf, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetLeavesByRange", arg0, arg1, arg2) ret0, _ := ret[0].([]*trillian.LogLeaf) ret1, _ := ret[1].(error) return ret0, ret1 } // GetLeavesByRange indicates an expected call of GetLeavesByRange. func (mr *MockReadOnlyLogTreeTXMockRecorder) GetLeavesByRange(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLeavesByRange", reflect.TypeOf((*MockReadOnlyLogTreeTX)(nil).GetLeavesByRange), arg0, arg1, arg2) } // GetMerkleNodes mocks base method. func (m *MockReadOnlyLogTreeTX) GetMerkleNodes(arg0 context.Context, arg1 []compact.NodeID) ([]tree.Node, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetMerkleNodes", arg0, arg1) ret0, _ := ret[0].([]tree.Node) ret1, _ := ret[1].(error) return ret0, ret1 } // GetMerkleNodes indicates an expected call of GetMerkleNodes. func (mr *MockReadOnlyLogTreeTXMockRecorder) GetMerkleNodes(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMerkleNodes", reflect.TypeOf((*MockReadOnlyLogTreeTX)(nil).GetMerkleNodes), arg0, arg1) } // LatestSignedLogRoot mocks base method. func (m *MockReadOnlyLogTreeTX) LatestSignedLogRoot(arg0 context.Context) (*trillian.SignedLogRoot, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "LatestSignedLogRoot", arg0) ret0, _ := ret[0].(*trillian.SignedLogRoot) ret1, _ := ret[1].(error) return ret0, ret1 } // LatestSignedLogRoot indicates an expected call of LatestSignedLogRoot. func (mr *MockReadOnlyLogTreeTXMockRecorder) LatestSignedLogRoot(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LatestSignedLogRoot", reflect.TypeOf((*MockReadOnlyLogTreeTX)(nil).LatestSignedLogRoot), arg0) } trillian-1.6.1/storage/mysql/000077500000000000000000000000001466362047600161645ustar00rootroot00000000000000trillian-1.6.1/storage/mysql/admin_storage.go000066400000000000000000000341171466362047600213350ustar00rootroot00000000000000// 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 mysql import ( "bytes" "context" "database/sql" "encoding/gob" "fmt" "sync" "time" "github.com/google/trillian" "github.com/google/trillian/storage" "github.com/google/trillian/storage/mysql/mysqlpb" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/anypb" "google.golang.org/protobuf/types/known/timestamppb" "k8s.io/klog/v2" ) const ( defaultSequenceIntervalSeconds = 60 nonDeletedWhere = " WHERE (Deleted IS NULL OR Deleted = 'false')" selectTrees = ` SELECT TreeId, TreeState, TreeType, HashStrategy, HashAlgorithm, SignatureAlgorithm, DisplayName, Description, CreateTimeMillis, UpdateTimeMillis, PrivateKey, -- Unused PublicKey, -- Used to store StorageSettings MaxRootDurationMillis, Deleted, DeleteTimeMillis FROM Trees` selectNonDeletedTrees = selectTrees + nonDeletedWhere selectTreeByID = selectTrees + " WHERE TreeId = ?" updateTreeSQL = `UPDATE Trees SET TreeState = ?, TreeType = ?, DisplayName = ?, Description = ?, UpdateTimeMillis = ?, MaxRootDurationMillis = ?, PrivateKey = ? WHERE TreeId = ?` ) // NewAdminStorage returns a MySQL storage.AdminStorage implementation backed by DB. func NewAdminStorage(db *sql.DB) *mysqlAdminStorage { return &mysqlAdminStorage{db} } // mysqlAdminStorage implements storage.AdminStorage type mysqlAdminStorage struct { db *sql.DB } func (s *mysqlAdminStorage) Snapshot(ctx context.Context) (storage.ReadOnlyAdminTX, error) { return s.beginInternal(ctx) } func (s *mysqlAdminStorage) beginInternal(ctx context.Context) (storage.AdminTX, error) { tx, err := s.db.BeginTx(ctx, nil /* opts */) if err != nil { return nil, err } return &adminTX{tx: tx}, nil } func (s *mysqlAdminStorage) ReadWriteTransaction(ctx context.Context, f storage.AdminTXFunc) error { tx, err := s.beginInternal(ctx) if err != nil { return err } defer func() { if err := tx.Close(); err != nil { klog.Errorf("tx.Close(): %v", err) } }() if err := f(ctx, tx); err != nil { return err } return tx.Commit() } func (s *mysqlAdminStorage) CheckDatabaseAccessible(ctx context.Context) error { return s.db.PingContext(ctx) } type adminTX struct { tx *sql.Tx // mu guards reads/writes on closed, which happen on Commit/Close methods. // // We don't check closed on methods apart from the ones above, as we trust tx // to keep tabs on its state, and hence fail to do queries after closed. mu sync.RWMutex closed bool } func (t *adminTX) Commit() error { t.mu.Lock() defer t.mu.Unlock() t.closed = true return t.tx.Commit() } func (t *adminTX) Close() error { t.mu.Lock() defer t.mu.Unlock() if t.closed { return nil } t.closed = true return t.tx.Rollback() } func (t *adminTX) GetTree(ctx context.Context, treeID int64) (*trillian.Tree, error) { stmt, err := t.tx.PrepareContext(ctx, selectTreeByID) if err != nil { return nil, err } defer func() { if err := stmt.Close(); err != nil { klog.Errorf("stmt.Close(): %v", err) } }() // GetTree is an entry point for most RPCs, let's provide somewhat nicer error messages. tree, err := readTree(stmt.QueryRowContext(ctx, treeID)) switch { case err == sql.ErrNoRows: // ErrNoRows doesn't provide useful information, so we don't forward it. return nil, status.Errorf(codes.NotFound, "tree %v not found", treeID) case err != nil: return nil, fmt.Errorf("error reading tree %v: %v", treeID, err) } return tree, nil } func (t *adminTX) ListTrees(ctx context.Context, includeDeleted bool) ([]*trillian.Tree, error) { var query string if includeDeleted { query = selectTrees } else { query = selectNonDeletedTrees } stmt, err := t.tx.PrepareContext(ctx, query) if err != nil { return nil, err } defer func() { if err := stmt.Close(); err != nil { klog.Errorf("stmt.Close(): %v", err) } }() rows, err := stmt.QueryContext(ctx) if err != nil { return nil, err } defer func() { if err := rows.Close(); err != nil { klog.Errorf("rows.Close(): %v", err) } }() trees := []*trillian.Tree{} for rows.Next() { tree, err := readTree(rows) if err != nil { return nil, err } trees = append(trees, tree) } return trees, nil } func (t *adminTX) CreateTree(ctx context.Context, tree *trillian.Tree) (*trillian.Tree, error) { if err := storage.ValidateTreeForCreation(ctx, tree); err != nil { return nil, err } if err := validateStorageSettings(tree); err != nil { return nil, err } id, err := storage.NewTreeID() if err != nil { return nil, err } // Use the time truncated-to-millis throughout, as that's what's stored. nowMillis := toMillisSinceEpoch(time.Now()) now := fromMillisSinceEpoch(nowMillis) newTree := proto.Clone(tree).(*trillian.Tree) newTree.TreeId = id newTree.CreateTime = timestamppb.New(now) if err := newTree.CreateTime.CheckValid(); err != nil { return nil, fmt.Errorf("failed to build create time: %w", err) } newTree.UpdateTime = timestamppb.New(now) if err := newTree.UpdateTime.CheckValid(); err != nil { return nil, fmt.Errorf("failed to build update time: %w", err) } if err := newTree.MaxRootDuration.CheckValid(); err != nil { return nil, fmt.Errorf("could not parse MaxRootDuration: %w", err) } rootDuration := newTree.MaxRootDuration.AsDuration() // When creating a new tree we automatically add StorageSettings to allow us to // determine that this tree can support newer storage features. When reading // trees that do not have this StorageSettings populated, it must be assumed that // the tree was created with the oldest settings. // The gist of this code is super simple: create a new StorageSettings with the most // modern defaults if the created tree does not have one, and then create a struct that // represents this to store in the DB. Unfortunately because this involves anypb, struct // copies, marshalling, and proper error handling this turns into a scary amount of code. if tree.StorageSettings != nil { newTree.StorageSettings = proto.Clone(tree.StorageSettings).(*anypb.Any) } else { o := &mysqlpb.StorageOptions{ SubtreeRevisions: false, // Default behaviour for new trees is to skip writing subtree revisions. } a, err := anypb.New(o) if err != nil { return nil, fmt.Errorf("failed to create new StorageOptions: %v", err) } newTree.StorageSettings = a } o := &mysqlpb.StorageOptions{} if err := anypb.UnmarshalTo(newTree.StorageSettings, o, proto.UnmarshalOptions{}); err != nil { return nil, fmt.Errorf("failed to unmarshal StorageOptions: %v", err) } ss := storageSettings{ Revisioned: o.SubtreeRevisions, } buff := &bytes.Buffer{} enc := gob.NewEncoder(buff) if err := enc.Encode(ss); err != nil { return nil, fmt.Errorf("failed to encode storageSettings: %v", err) } insertTreeStmt, err := t.tx.PrepareContext( ctx, `INSERT INTO Trees( TreeId, TreeState, TreeType, HashStrategy, HashAlgorithm, SignatureAlgorithm, DisplayName, Description, CreateTimeMillis, UpdateTimeMillis, PrivateKey, -- Unused PublicKey, -- Used to store StorageSettings MaxRootDurationMillis) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`) if err != nil { return nil, err } defer func() { if err := insertTreeStmt.Close(); err != nil { klog.Errorf("insertTreeStmt.Close(): %v", err) } }() _, err = insertTreeStmt.ExecContext( ctx, newTree.TreeId, newTree.TreeState.String(), newTree.TreeType.String(), "RFC6962_SHA256", // Unused, filling in for backward compatibility. "SHA256", // Unused, filling in for backward compatibility. "ECDSA", // Unused, filling in for backward compatibility. newTree.DisplayName, newTree.Description, nowMillis, nowMillis, []byte{}, // PrivateKey: Unused, filling in for backward compatibility. buff.Bytes(), // Using the otherwise unused PublicKey for storing StorageSettings. rootDuration/time.Millisecond, ) if err != nil { return nil, err } // MySQL silently truncates data when running in non-strict mode. // We shouldn't be using non-strict modes, but let's guard against it // anyway. if _, err := t.GetTree(ctx, newTree.TreeId); err != nil { // GetTree will fail for truncated enums (they get recorded as // empty strings, which will not match any known value). return nil, fmt.Errorf("enum truncated: %v", err) } insertControlStmt, err := t.tx.PrepareContext( ctx, `INSERT INTO TreeControl( TreeId, SigningEnabled, SequencingEnabled, SequenceIntervalSeconds) VALUES(?, ?, ?, ?)`) if err != nil { return nil, err } defer func() { if err := insertControlStmt.Close(); err != nil { klog.Errorf("insertControlStmt.Close(): %v", err) } }() _, err = insertControlStmt.ExecContext( ctx, newTree.TreeId, true, /* SigningEnabled */ true, /* SequencingEnabled */ defaultSequenceIntervalSeconds, ) if err != nil { return nil, err } return newTree, nil } func (t *adminTX) UpdateTree(ctx context.Context, treeID int64, updateFunc func(*trillian.Tree)) (*trillian.Tree, error) { tree, err := t.GetTree(ctx, treeID) if err != nil { return nil, err } beforeUpdate := proto.Clone(tree).(*trillian.Tree) updateFunc(tree) if err := storage.ValidateTreeForUpdate(ctx, beforeUpdate, tree); err != nil { return nil, err } if err := validateStorageSettings(tree); err != nil { return nil, err } // TODO(pavelkalinnikov): When switching TreeType from PREORDERED_LOG to LOG, // ensure all entries in SequencedLeafData are integrated. // Use the time truncated-to-millis throughout, as that's what's stored. nowMillis := toMillisSinceEpoch(time.Now()) now := fromMillisSinceEpoch(nowMillis) tree.UpdateTime = timestamppb.New(now) if err != nil { return nil, fmt.Errorf("failed to build update time: %v", err) } if err := tree.MaxRootDuration.CheckValid(); err != nil { return nil, fmt.Errorf("could not parse MaxRootDuration: %w", err) } rootDuration := tree.MaxRootDuration.AsDuration() stmt, err := t.tx.PrepareContext(ctx, updateTreeSQL) if err != nil { return nil, err } defer func() { if err := stmt.Close(); err != nil { klog.Errorf("stmt.Close(): %v", err) } }() if _, err = stmt.ExecContext( ctx, tree.TreeState.String(), tree.TreeType.String(), tree.DisplayName, tree.Description, nowMillis, rootDuration/time.Millisecond, []byte{}, // PrivateKey: Unused, filling in for backward compatibility. // PublicKey should not be updated with any storageSettings here without // a lot of thought put into it. At the moment storageSettings are inferred // when reading the tree, even if no value is stored in the database. tree.TreeId); err != nil { return nil, err } return tree, nil } func (t *adminTX) SoftDeleteTree(ctx context.Context, treeID int64) (*trillian.Tree, error) { return t.updateDeleted(ctx, treeID, true /* deleted */, toMillisSinceEpoch(time.Now()) /* deleteTimeMillis */) } func (t *adminTX) UndeleteTree(ctx context.Context, treeID int64) (*trillian.Tree, error) { return t.updateDeleted(ctx, treeID, false /* deleted */, nil /* deleteTimeMillis */) } // updateDeleted updates the Deleted and DeleteTimeMillis fields of the specified tree. // deleteTimeMillis must be either an int64 (in millis since epoch) or nil. func (t *adminTX) updateDeleted(ctx context.Context, treeID int64, deleted bool, deleteTimeMillis interface{}) (*trillian.Tree, error) { if err := validateDeleted(ctx, t.tx, treeID, !deleted); err != nil { return nil, err } if _, err := t.tx.ExecContext( ctx, "UPDATE Trees SET Deleted = ?, DeleteTimeMillis = ? WHERE TreeId = ?", deleted, deleteTimeMillis, treeID); err != nil { return nil, err } return t.GetTree(ctx, treeID) } func (t *adminTX) HardDeleteTree(ctx context.Context, treeID int64) error { if err := validateDeleted(ctx, t.tx, treeID, true /* wantDeleted */); err != nil { return err } // TreeControl didn't have "ON DELETE CASCADE" on previous versions, so let's hit it explicitly if _, err := t.tx.ExecContext(ctx, "DELETE FROM TreeControl WHERE TreeId = ?", treeID); err != nil { return err } _, err := t.tx.ExecContext(ctx, "DELETE FROM Trees WHERE TreeId = ?", treeID) return err } func validateDeleted(ctx context.Context, tx *sql.Tx, treeID int64, wantDeleted bool) error { var nullDeleted sql.NullBool switch err := tx.QueryRowContext(ctx, "SELECT Deleted FROM Trees WHERE TreeId = ?", treeID).Scan(&nullDeleted); { case err == sql.ErrNoRows: return status.Errorf(codes.NotFound, "tree %v not found", treeID) case err != nil: return err } switch deleted := nullDeleted.Valid && nullDeleted.Bool; { case wantDeleted && !deleted: return status.Errorf(codes.FailedPrecondition, "tree %v is not soft deleted", treeID) case !wantDeleted && deleted: return status.Errorf(codes.FailedPrecondition, "tree %v already soft deleted", treeID) } return nil } func validateStorageSettings(tree *trillian.Tree) error { if tree.StorageSettings.MessageIs(&mysqlpb.StorageOptions{}) { return nil } if tree.StorageSettings == nil { // No storage settings is OK, we'll just use the defaults for new trees return nil } return fmt.Errorf("storage_settings must be nil or mysqlpb.StorageOptions, but got %v", tree.StorageSettings) } // storageSettings allows us to persist storage settings to the DB. // It is a tempting trap to use protos for this, but the way they encode // makes it impossible to tell the difference between no value ever written // and a value that was written with the default values for each field. // Using an explicit struct and gob encoding allows us to tell the difference. type storageSettings struct { Revisioned bool } trillian-1.6.1/storage/mysql/admin_storage_test.go000066400000000000000000000235541466362047600223770ustar00rootroot00000000000000// 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 mysql import ( "bytes" "context" "database/sql" "encoding/gob" "fmt" "testing" "github.com/google/trillian" "github.com/google/trillian/storage" "github.com/google/trillian/storage/mysql/mysqlpb" "github.com/google/trillian/storage/testonly" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/anypb" ) const selectTreeControlByID = "SELECT SigningEnabled, SequencingEnabled, SequenceIntervalSeconds FROM TreeControl WHERE TreeId = ?" func TestMysqlAdminStorage(t *testing.T) { tester := &testonly.AdminStorageTester{NewAdminStorage: func() storage.AdminStorage { cleanTestDB(DB) return NewAdminStorage(DB) }} tester.RunAllTests(t) } func TestAdminTX_CreateTree_InitializesStorageStructures(t *testing.T) { cleanTestDB(DB) s := NewAdminStorage(DB) ctx := context.Background() tree, err := storage.CreateTree(ctx, s, testonly.LogTree) if err != nil { t.Fatalf("CreateTree() failed: %v", err) } // Check if TreeControl is correctly written. var signingEnabled, sequencingEnabled bool var sequenceIntervalSeconds int if err := DB.QueryRowContext(ctx, selectTreeControlByID, tree.TreeId).Scan(&signingEnabled, &sequencingEnabled, &sequenceIntervalSeconds); err != nil { t.Fatalf("Failed to read TreeControl: %v", err) } // We don't mind about specific values, defaults change, but let's check // that important numbers are not zeroed. if sequenceIntervalSeconds <= 0 { t.Errorf("sequenceIntervalSeconds = %v, want > 0", sequenceIntervalSeconds) } } func TestCreateTreeInvalidStates(t *testing.T) { cleanTestDB(DB) s := NewAdminStorage(DB) ctx := context.Background() states := []trillian.TreeState{trillian.TreeState_DRAINING, trillian.TreeState_FROZEN} for _, state := range states { inTree := proto.Clone(testonly.LogTree).(*trillian.Tree) inTree.TreeState = state if _, err := storage.CreateTree(ctx, s, inTree); err == nil { t.Errorf("CreateTree() state: %v got: nil want: err", state) } } } func TestAdminTX_TreeWithNulls(t *testing.T) { cleanTestDB(DB) s := NewAdminStorage(DB) ctx := context.Background() // Setup: create a tree and set all nullable columns to null. // Some columns have to be manually updated, as it's not possible to set // some proto fields to nil. tree, err := storage.CreateTree(ctx, s, testonly.LogTree) if err != nil { t.Fatalf("CreateTree() failed: %v", err) } treeID := tree.TreeId if err := setNulls(ctx, DB, treeID); err != nil { t.Fatalf("setNulls() = %v, want = nil", err) } tests := []struct { desc string fn storage.AdminTXFunc }{ { desc: "GetTree", fn: func(ctx context.Context, tx storage.AdminTX) error { _, err := tx.GetTree(ctx, treeID) return err }, }, { desc: "ListTrees", fn: func(ctx context.Context, tx storage.AdminTX) error { trees, err := tx.ListTrees(ctx, false /* includeDeleted */) if err != nil { return err } for _, tree := range trees { if tree.TreeId == treeID { return nil } } return fmt.Errorf("ID not found: %v", treeID) }, }, } for _, test := range tests { if err := s.ReadWriteTransaction(ctx, test.fn); err != nil { t.Errorf("%v: err = %v, want = nil", test.desc, err) } } } func TestAdminTX_StorageSettings(t *testing.T) { cleanTestDB(DB) s := NewAdminStorage(DB) ctx := context.Background() badSettings, err := anypb.New(&trillian.Tree{}) if err != nil { t.Fatalf("Error marshaling proto: %v", err) } goodSettings, err := anypb.New(&mysqlpb.StorageOptions{}) if err != nil { t.Fatalf("Error marshaling proto: %v", err) } tests := []struct { desc string // fn attempts to either create or update a tree with a non-nil, valid Any proto // on Tree.StorageSettings. It's expected to return an error. fn func(storage.AdminStorage) error wantErr bool }{ { desc: "CreateTree Bad Settings", fn: func(s storage.AdminStorage) error { tree := proto.Clone(testonly.LogTree).(*trillian.Tree) tree.StorageSettings = badSettings _, err := storage.CreateTree(ctx, s, tree) return err }, wantErr: true, }, { desc: "CreateTree nil Settings", fn: func(s storage.AdminStorage) error { tree := proto.Clone(testonly.LogTree).(*trillian.Tree) tree.StorageSettings = nil _, err := storage.CreateTree(ctx, s, tree) return err }, wantErr: false, }, { desc: "CreateTree StorageOptions Settings", fn: func(s storage.AdminStorage) error { tree := proto.Clone(testonly.LogTree).(*trillian.Tree) tree.StorageSettings = goodSettings _, err := storage.CreateTree(ctx, s, tree) return err }, wantErr: false, }, { desc: "UpdateTree", fn: func(s storage.AdminStorage) error { tree, err := storage.CreateTree(ctx, s, testonly.LogTree) if err != nil { t.Fatalf("CreateTree() failed with err = %v", err) } _, err = storage.UpdateTree(ctx, s, tree.TreeId, func(tree *trillian.Tree) { tree.StorageSettings = badSettings }) return err }, wantErr: true, }, } for _, test := range tests { if err := test.fn(s); (err != nil) != test.wantErr { t.Errorf("err: %v, wantErr = %v", err, test.wantErr) } } } // Test reading variants of trees that could have been created by old versions // of Trillian to check we infer the correct storage options. func TestAdminTX_GetTreeLegacies(t *testing.T) { cleanTestDB(DB) s := NewAdminStorage(DB) ctx := context.Background() serializedStorageSettings := func(revisioned bool) []byte { ss := storageSettings{ Revisioned: revisioned, } buff := &bytes.Buffer{} enc := gob.NewEncoder(buff) if err := enc.Encode(ss); err != nil { t.Fatalf("failed to encode storageSettings: %v", err) } return buff.Bytes() } tests := []struct { desc string key []byte wantRevisioned bool }{ { desc: "No data", key: []byte{}, wantRevisioned: true, }, { desc: "Public key", key: []byte("trustmethatthisisapublickey"), wantRevisioned: true, }, { desc: "StorageOptions revisioned", key: serializedStorageSettings(true), wantRevisioned: true, }, { desc: "StorageOptions revisionless", key: serializedStorageSettings(false), wantRevisioned: false, }, } for _, tC := range tests { // Create a tree with default settings, and then reach into the DB to override // whatever was written into the persisted settings to align with the test case. tree, err := storage.CreateTree(ctx, s, testonly.LogTree) if err != nil { t.Fatal(err) } // We are reaching really into the internals here, but it's the only way to set up // archival state. Going through the Create/Update methods will change the storage // options. tx, err := s.db.BeginTx(ctx, nil /* opts */) if err != nil { t.Fatal(err) } if _, err := tx.Exec("UPDATE Trees SET PublicKey = ? WHERE TreeId = ?", tC.key, tree.TreeId); err != nil { t.Fatal(err) } if err := tx.Commit(); err != nil { t.Fatal(err) } readTree, err := storage.GetTree(ctx, s, tree.TreeId) if err != nil { t.Fatal(err) } o := &mysqlpb.StorageOptions{} if err := anypb.UnmarshalTo(readTree.StorageSettings, o, proto.UnmarshalOptions{}); err != nil { t.Fatal(err) } if got, want := o.SubtreeRevisions, tC.wantRevisioned; got != want { t.Errorf("%s SubtreeRevisions: got %t, wanted %t", tC.desc, got, want) } } } func TestAdminTX_HardDeleteTree(t *testing.T) { cleanTestDB(DB) s := NewAdminStorage(DB) ctx := context.Background() tree, err := storage.CreateTree(ctx, s, testonly.LogTree) if err != nil { t.Fatalf("CreateTree() returned err = %v", err) } if err := s.ReadWriteTransaction(ctx, func(ctx context.Context, tx storage.AdminTX) error { if _, err := tx.SoftDeleteTree(ctx, tree.TreeId); err != nil { return err } return tx.HardDeleteTree(ctx, tree.TreeId) }); err != nil { t.Fatalf("ReadWriteTransaction() returned err = %v", err) } // Unlike the HardDelete tests on AdminStorageTester, here we have the chance to poke inside the // database and check that the rows are gone, so let's do just that. // If there's no record on Trees, then there can be no record in any of the dependent tables. var name string if err := DB.QueryRowContext(ctx, "SELECT DisplayName FROM Trees WHERE TreeId = ?", tree.TreeId).Scan(&name); err != sql.ErrNoRows { t.Errorf("QueryRowContext() returned err = %v, want = %v", err, sql.ErrNoRows) } } func TestCheckDatabaseAccessible_Fails(t *testing.T) { ctx := context.Background() // Pass in a closed database to provoke a failure. db, done := openTestDBOrDie() cleanTestDB(db) s := NewAdminStorage(db) done(ctx) if err := s.CheckDatabaseAccessible(ctx); err == nil { t.Error("TestCheckDatabaseAccessible_Fails got: nil, want: err") } } func TestCheckDatabaseAccessible_OK(t *testing.T) { cleanTestDB(DB) s := NewAdminStorage(DB) ctx := context.Background() if err := s.CheckDatabaseAccessible(ctx); err != nil { t.Errorf("TestCheckDatabaseAccessible_OK got: %v, want: nil", err) } } func setNulls(ctx context.Context, db *sql.DB, treeID int64) error { stmt, err := db.PrepareContext(ctx, "UPDATE Trees SET DisplayName = NULL, Description = NULL WHERE TreeId = ?") if err != nil { return err } defer func() { _ = stmt.Close() }() _, err = stmt.ExecContext(ctx, treeID) return err } trillian-1.6.1/storage/mysql/drop_storage.sql000066400000000000000000000004301466362047600213720ustar00rootroot00000000000000-- Caution - this removes all tables in our schema DROP TABLE IF EXISTS Unsequenced; DROP TABLE IF EXISTS Subtree; DROP TABLE IF EXISTS SequencedLeafData; DROP TABLE IF EXISTS TreeHead; DROP TABLE IF EXISTS LeafData; DROP TABLE IF EXISTS TreeControl; DROP TABLE IF EXISTS Trees; trillian-1.6.1/storage/mysql/errors.go000066400000000000000000000026531466362047600200350ustar00rootroot00000000000000// Copyright 2021 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 mysql import ( "github.com/go-sql-driver/mysql" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) const ( // ER_DUP_ENTRY: Error returned by driver when inserting a duplicate row. errNumDuplicate = 1062 // ER_LOCK_DEADLOCK: Error returned when there was a deadlock. errNumDeadlock = 1213 ) // mysqlToGRPC converts some types of MySQL errors to GRPC errors. This gives // clients more signal when the operation can be retried. func mysqlToGRPC(err error) error { mysqlErr, ok := err.(*mysql.MySQLError) if !ok { return err } if mysqlErr.Number == errNumDeadlock { return status.Errorf(codes.Aborted, "MySQL: %v", mysqlErr) } return err } func isDuplicateErr(err error) bool { switch err := err.(type) { case *mysql.MySQLError: return err.Number == errNumDuplicate default: return false } } trillian-1.6.1/storage/mysql/log_storage.go000066400000000000000000000723451466362047600210330ustar00rootroot00000000000000// 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 mysql import ( "bytes" "context" "database/sql" "errors" "fmt" "sort" "strconv" "sync" "time" "github.com/google/trillian" "github.com/google/trillian/monitoring" "github.com/google/trillian/storage" "github.com/google/trillian/storage/cache" "github.com/google/trillian/storage/tree" "github.com/google/trillian/types" "github.com/transparency-dev/merkle/compact" "github.com/transparency-dev/merkle/rfc6962" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/timestamppb" "k8s.io/klog/v2" ) const ( valuesPlaceholder5 = "(?,?,?,?,?)" insertLeafDataSQL = "INSERT INTO LeafData(TreeId,LeafIdentityHash,LeafValue,ExtraData,QueueTimestampNanos) VALUES" + valuesPlaceholder5 insertSequencedLeafSQL = "INSERT INTO SequencedLeafData(TreeId,LeafIdentityHash,MerkleLeafHash,SequenceNumber,IntegrateTimestampNanos) VALUES" selectNonDeletedTreeIDByTypeAndStateSQL = ` SELECT TreeId FROM Trees WHERE TreeType IN(?,?) AND TreeState IN(?,?) AND (Deleted IS NULL OR Deleted = 'false')` selectLatestSignedLogRootSQL = `SELECT TreeHeadTimestamp,TreeSize,RootHash,TreeRevision,RootSignature FROM TreeHead WHERE TreeId=? ORDER BY TreeHeadTimestamp DESC LIMIT 1` selectLeavesByRangeSQL = `SELECT s.MerkleLeafHash,l.LeafIdentityHash,l.LeafValue,s.SequenceNumber,l.ExtraData,l.QueueTimestampNanos,s.IntegrateTimestampNanos FROM LeafData l,SequencedLeafData s WHERE l.LeafIdentityHash = s.LeafIdentityHash AND s.SequenceNumber >= ? AND s.SequenceNumber < ? AND l.TreeId = ? AND s.TreeId = l.TreeId` + orderBySequenceNumberSQL // These statements need to be expanded to provide the correct number of parameter placeholders. selectLeavesByMerkleHashSQL = `SELECT s.MerkleLeafHash,l.LeafIdentityHash,l.LeafValue,s.SequenceNumber,l.ExtraData,l.QueueTimestampNanos,s.IntegrateTimestampNanos FROM LeafData l,SequencedLeafData s WHERE l.LeafIdentityHash = s.LeafIdentityHash AND s.MerkleLeafHash IN (` + placeholderSQL + `) AND l.TreeId = ? AND s.TreeId = l.TreeId` // TODO(#1548): rework the code so the dummy hash isn't needed (e.g. this assumes hash size is 32) dummyMerkleLeafHash = "00000000000000000000000000000000" // This statement returns a dummy Merkle leaf hash value (which must be // of the right size) so that its signature matches that of the other // leaf-selection statements. selectLeavesByLeafIdentityHashSQL = `SELECT '` + dummyMerkleLeafHash + `',l.LeafIdentityHash,l.LeafValue,-1,l.ExtraData,l.QueueTimestampNanos,s.IntegrateTimestampNanos FROM LeafData l LEFT JOIN SequencedLeafData s ON (l.LeafIdentityHash = s.LeafIdentityHash AND l.TreeID = s.TreeID) WHERE l.LeafIdentityHash IN (` + placeholderSQL + `) AND l.TreeId = ?` // Same as above except with leaves ordered by sequence so we only incur this cost when necessary orderBySequenceNumberSQL = " ORDER BY s.SequenceNumber" selectLeavesByMerkleHashOrderedBySequenceSQL = selectLeavesByMerkleHashSQL + orderBySequenceNumberSQL logIDLabel = "logid" ) var ( once sync.Once queuedCounter monitoring.Counter queuedDupCounter monitoring.Counter dequeuedCounter monitoring.Counter queueLatency monitoring.Histogram queueInsertLatency monitoring.Histogram queueReadLatency monitoring.Histogram queueInsertLeafLatency monitoring.Histogram queueInsertEntryLatency monitoring.Histogram dequeueLatency monitoring.Histogram dequeueSelectLatency monitoring.Histogram dequeueRemoveLatency monitoring.Histogram ) func createMetrics(mf monitoring.MetricFactory) { queuedCounter = mf.NewCounter("mysql_queued_leaves", "Number of leaves queued", logIDLabel) queuedDupCounter = mf.NewCounter("mysql_queued_dup_leaves", "Number of duplicate leaves queued", logIDLabel) dequeuedCounter = mf.NewCounter("mysql_dequeued_leaves", "Number of leaves dequeued", logIDLabel) queueLatency = mf.NewHistogram("mysql_queue_leaves_latency", "Latency of queue leaves operation in seconds", logIDLabel) queueInsertLatency = mf.NewHistogram("mysql_queue_leaves_latency_insert", "Latency of insertion part of queue leaves operation in seconds", logIDLabel) queueReadLatency = mf.NewHistogram("mysql_queue_leaves_latency_read_dups", "Latency of read-duplicates part of queue leaves operation in seconds", logIDLabel) queueInsertLeafLatency = mf.NewHistogram("mysql_queue_leaf_latency_leaf", "Latency of insert-leaf part of queue (single) leaf operation in seconds", logIDLabel) queueInsertEntryLatency = mf.NewHistogram("mysql_queue_leaf_latency_entry", "Latency of insert-entry part of queue (single) leaf operation in seconds", logIDLabel) dequeueLatency = mf.NewHistogram("mysql_dequeue_leaves_latency", "Latency of dequeue leaves operation in seconds", logIDLabel) dequeueSelectLatency = mf.NewHistogram("mysql_dequeue_leaves_latency_select", "Latency of selection part of dequeue leaves operation in seconds", logIDLabel) dequeueRemoveLatency = mf.NewHistogram("mysql_dequeue_leaves_latency_remove", "Latency of removal part of dequeue leaves operation in seconds", logIDLabel) } func labelForTX(t *logTreeTX) string { return strconv.FormatInt(t.treeID, 10) } func observe(hist monitoring.Histogram, duration time.Duration, label string) { hist.Observe(duration.Seconds(), label) } type mySQLLogStorage struct { *mySQLTreeStorage admin storage.AdminStorage metricFactory monitoring.MetricFactory } // NewLogStorage creates a storage.LogStorage instance for the specified MySQL URL. // It assumes storage.AdminStorage is backed by the same MySQL database as well. func NewLogStorage(db *sql.DB, mf monitoring.MetricFactory) storage.LogStorage { if mf == nil { mf = monitoring.InertMetricFactory{} } return &mySQLLogStorage{ admin: NewAdminStorage(db), mySQLTreeStorage: newTreeStorage(db), metricFactory: mf, } } func (m *mySQLLogStorage) CheckDatabaseAccessible(ctx context.Context) error { return m.db.PingContext(ctx) } func (m *mySQLLogStorage) getLeavesByMerkleHashStmt(ctx context.Context, num int, orderBySequence bool) (*sql.Stmt, error) { if orderBySequence { return m.getStmt(ctx, selectLeavesByMerkleHashOrderedBySequenceSQL, num, "?", "?") } return m.getStmt(ctx, selectLeavesByMerkleHashSQL, num, "?", "?") } func (m *mySQLLogStorage) getLeavesByLeafIdentityHashStmt(ctx context.Context, num int) (*sql.Stmt, error) { return m.getStmt(ctx, selectLeavesByLeafIdentityHashSQL, num, "?", "?") } func (m *mySQLLogStorage) GetActiveLogIDs(ctx context.Context) ([]int64, error) { // Include logs that are DRAINING in the active list as we're still // integrating leaves into them. rows, err := m.db.QueryContext( ctx, selectNonDeletedTreeIDByTypeAndStateSQL, trillian.TreeType_LOG.String(), trillian.TreeType_PREORDERED_LOG.String(), trillian.TreeState_ACTIVE.String(), trillian.TreeState_DRAINING.String()) if err != nil { return nil, err } defer func() { if err := rows.Close(); err != nil { klog.Errorf("rows.Close(): %v", err) } }() ids := []int64{} for rows.Next() { var treeID int64 if err := rows.Scan(&treeID); err != nil { return nil, err } ids = append(ids, treeID) } return ids, rows.Err() } func (m *mySQLLogStorage) beginInternal(ctx context.Context, tree *trillian.Tree) (*logTreeTX, error) { once.Do(func() { createMetrics(m.metricFactory) }) stCache := cache.NewLogSubtreeCache(rfc6962.DefaultHasher) ttx, err := m.beginTreeTx(ctx, tree, rfc6962.DefaultHasher.Size(), stCache) if err != nil && err != storage.ErrTreeNeedsInit { return nil, err } ltx := &logTreeTX{ treeTX: ttx, ls: m, dequeued: make(map[string]dequeuedLeaf), } ltx.slr, ltx.readRev, err = ltx.fetchLatestRoot(ctx) if err == storage.ErrTreeNeedsInit { ltx.treeTX.writeRevision = 0 return ltx, err } else if err != nil { if err := ttx.Close(); err != nil { klog.Errorf("ttx.Close(): %v", err) } return nil, err } if err := ltx.root.UnmarshalBinary(ltx.slr.LogRoot); err != nil { if err := ttx.Close(); err != nil { klog.Errorf("ttx.Close(): %v", err) } return nil, err } ltx.treeTX.writeRevision = ltx.readRev + 1 return ltx, nil } // TODO(pavelkalinnikov): This and many other methods of this storage // implementation can leak a specific sql.ErrTxDone all the way to the client, // if the transaction is rolled back as a result of a canceled context. It must // return "generic" errors, and only log the specific ones for debugging. func (m *mySQLLogStorage) ReadWriteTransaction(ctx context.Context, tree *trillian.Tree, f storage.LogTXFunc) error { tx, err := m.beginInternal(ctx, tree) if err != nil && err != storage.ErrTreeNeedsInit { return err } defer func() { if err := tx.Close(); err != nil { klog.Errorf("tx.Close(): %v", err) } }() if err := f(ctx, tx); err != nil { return err } return tx.Commit(ctx) } func (m *mySQLLogStorage) AddSequencedLeaves(ctx context.Context, tree *trillian.Tree, leaves []*trillian.LogLeaf, timestamp time.Time) ([]*trillian.QueuedLogLeaf, error) { tx, err := m.beginInternal(ctx, tree) if tx != nil { // Ensure we don't leak the transaction. For example if we get an // ErrTreeNeedsInit from beginInternal() or if AddSequencedLeaves fails // below. defer func() { if err := tx.Close(); err != nil { klog.Errorf("tx.Close(): %v", err) } }() } if err != nil { return nil, err } res, err := tx.AddSequencedLeaves(ctx, leaves, timestamp) if err != nil { return nil, err } if err := tx.Commit(ctx); err != nil { return nil, err } return res, nil } func (m *mySQLLogStorage) SnapshotForTree(ctx context.Context, tree *trillian.Tree) (storage.ReadOnlyLogTreeTX, error) { tx, err := m.beginInternal(ctx, tree) if err != nil && err != storage.ErrTreeNeedsInit { return nil, err } return tx, err } func (m *mySQLLogStorage) QueueLeaves(ctx context.Context, tree *trillian.Tree, leaves []*trillian.LogLeaf, queueTimestamp time.Time) ([]*trillian.QueuedLogLeaf, error) { tx, err := m.beginInternal(ctx, tree) if tx != nil { // Ensure we don't leak the transaction. For example if we get an // ErrTreeNeedsInit from beginInternal() or if QueueLeaves fails // below. defer func() { if err := tx.Close(); err != nil { klog.Errorf("tx.Close(): %v", err) } }() } if err != nil { return nil, err } existing, err := tx.QueueLeaves(ctx, leaves, queueTimestamp) if err != nil { return nil, err } if err := tx.Commit(ctx); err != nil { return nil, err } ret := make([]*trillian.QueuedLogLeaf, len(leaves)) for i, e := range existing { if e != nil { ret[i] = &trillian.QueuedLogLeaf{ Leaf: e, Status: status.Newf(codes.AlreadyExists, "leaf already exists: %v", e.LeafIdentityHash).Proto(), } continue } ret[i] = &trillian.QueuedLogLeaf{Leaf: leaves[i]} } return ret, nil } type logTreeTX struct { treeTX ls *mySQLLogStorage root types.LogRootV1 readRev int64 slr *trillian.SignedLogRoot dequeued map[string]dequeuedLeaf } // GetMerkleNodes returns the requested nodes at the read revision. func (t *logTreeTX) GetMerkleNodes(ctx context.Context, ids []compact.NodeID) ([]tree.Node, error) { t.treeTX.mu.Lock() defer t.treeTX.mu.Unlock() return t.subtreeCache.GetNodes(ids, t.getSubtreesAtRev(ctx, t.readRev)) } func (t *logTreeTX) DequeueLeaves(ctx context.Context, limit int, cutoffTime time.Time) ([]*trillian.LogLeaf, error) { t.treeTX.mu.Lock() defer t.treeTX.mu.Unlock() if t.treeType == trillian.TreeType_PREORDERED_LOG { // TODO(pavelkalinnikov): Optimize this by fetching only the required // fields of LogLeaf. We can avoid joining with LeafData table here. return t.getLeavesByRangeInternal(ctx, int64(t.root.TreeSize), int64(limit)) } start := time.Now() stx, err := t.tx.PrepareContext(ctx, selectQueuedLeavesSQL) if err != nil { klog.Warningf("Failed to prepare dequeue select: %s", err) return nil, err } defer func() { if err := stx.Close(); err != nil { klog.Errorf("stx.Close(): %v", err) } }() leaves := make([]*trillian.LogLeaf, 0, limit) rows, err := stx.QueryContext(ctx, t.treeID, cutoffTime.UnixNano(), limit) if err != nil { klog.Warningf("Failed to select rows for work: %s", err) return nil, err } defer func() { if err := rows.Close(); err != nil { klog.Errorf("rows.Close(): %v", err) } }() for rows.Next() { leaf, dqInfo, err := t.dequeueLeaf(rows) if err != nil { klog.Warningf("Error dequeuing leaf: %v", err) return nil, err } if len(leaf.LeafIdentityHash) != t.hashSizeBytes { return nil, errors.New("dequeued a leaf with incorrect hash size") } k := string(leaf.LeafIdentityHash) if _, ok := t.dequeued[k]; ok { // dupe, user probably called DequeueLeaves more than once. continue } t.dequeued[k] = dqInfo leaves = append(leaves, leaf) } if rows.Err() != nil { return nil, rows.Err() } label := labelForTX(t) observe(dequeueSelectLatency, time.Since(start), label) observe(dequeueLatency, time.Since(start), label) dequeuedCounter.Add(float64(len(leaves)), label) return leaves, nil } // sortLeavesForInsert returns a slice containing the passed in leaves sorted // by LeafIdentityHash, and paired with their original positions. // QueueLeaves and AddSequencedLeaves use this to make the order that LeafData // row locks are acquired deterministic and reduce the chance of deadlocks. func sortLeavesForInsert(leaves []*trillian.LogLeaf) []leafAndPosition { ordLeaves := make([]leafAndPosition, len(leaves)) for i, leaf := range leaves { ordLeaves[i] = leafAndPosition{leaf: leaf, idx: i} } sort.Sort(byLeafIdentityHashWithPosition(ordLeaves)) return ordLeaves } func (t *logTreeTX) QueueLeaves(ctx context.Context, leaves []*trillian.LogLeaf, queueTimestamp time.Time) ([]*trillian.LogLeaf, error) { t.treeTX.mu.Lock() defer t.treeTX.mu.Unlock() // Don't accept batches if any of the leaves are invalid. for _, leaf := range leaves { if len(leaf.LeafIdentityHash) != t.hashSizeBytes { return nil, fmt.Errorf("queued leaf must have a leaf ID hash of length %d", t.hashSizeBytes) } leaf.QueueTimestamp = timestamppb.New(queueTimestamp) if err := leaf.QueueTimestamp.CheckValid(); err != nil { return nil, fmt.Errorf("got invalid queue timestamp: %w", err) } } start := time.Now() label := labelForTX(t) ordLeaves := sortLeavesForInsert(leaves) existingCount := 0 existingLeaves := make([]*trillian.LogLeaf, len(leaves)) for _, ol := range ordLeaves { i, leaf := ol.idx, ol.leaf leafStart := time.Now() if err := leaf.QueueTimestamp.CheckValid(); err != nil { return nil, fmt.Errorf("got invalid queue timestamp: %w", err) } qTimestamp := leaf.QueueTimestamp.AsTime() _, err := t.tx.ExecContext(ctx, insertLeafDataSQL, t.treeID, leaf.LeafIdentityHash, leaf.LeafValue, leaf.ExtraData, qTimestamp.UnixNano()) insertDuration := time.Since(leafStart) observe(queueInsertLeafLatency, insertDuration, label) if isDuplicateErr(err) { // Remember the duplicate leaf, using the requested leaf for now. existingLeaves[i] = leaf existingCount++ queuedDupCounter.Inc(label) continue } if err != nil { klog.Warningf("Error inserting %d into LeafData: %s", i, err) return nil, mysqlToGRPC(err) } // Create the work queue entry args := []interface{}{ t.treeID, leaf.LeafIdentityHash, leaf.MerkleLeafHash, } args = append(args, queueArgs(t.treeID, leaf.LeafIdentityHash, qTimestamp)...) _, err = t.tx.ExecContext( ctx, insertUnsequencedEntrySQL, args..., ) if err != nil { klog.Warningf("Error inserting into Unsequenced: %s", err) return nil, mysqlToGRPC(err) } leafDuration := time.Since(leafStart) observe(queueInsertEntryLatency, (leafDuration - insertDuration), label) } insertDuration := time.Since(start) observe(queueInsertLatency, insertDuration, label) queuedCounter.Add(float64(len(leaves)), label) if existingCount == 0 { return existingLeaves, nil } // For existing leaves, we need to retrieve the contents. First collate the desired LeafIdentityHash values // We deduplicate the hashes to address https://github.com/google/trillian/issues/3603 but will be mapped // back to the existingLeaves slice below uniqueLeafMap := make(map[string]struct{}, len(existingLeaves)) var toRetrieve [][]byte for _, existing := range existingLeaves { if existing != nil { key := string(existing.LeafIdentityHash) if _, ok := uniqueLeafMap[key]; !ok { uniqueLeafMap[key] = struct{}{} toRetrieve = append(toRetrieve, existing.LeafIdentityHash) } } } results, err := t.getLeafDataByIdentityHash(ctx, toRetrieve) if err != nil { return nil, fmt.Errorf("failed to retrieve existing leaves: %v", err) } if len(results) != len(toRetrieve) { return nil, fmt.Errorf("failed to retrieve all existing leaves: got %d, want %d", len(results), len(toRetrieve)) } // Replace the requested leaves with the actual leaves. for i, requested := range existingLeaves { if requested == nil { continue } found := false for _, result := range results { if bytes.Equal(result.LeafIdentityHash, requested.LeafIdentityHash) { existingLeaves[i] = result found = true break } } if !found { return nil, fmt.Errorf("failed to find existing leaf for hash %x", requested.LeafIdentityHash) } } totalDuration := time.Since(start) readDuration := totalDuration - insertDuration observe(queueReadLatency, readDuration, label) observe(queueLatency, totalDuration, label) return existingLeaves, nil } func (t *logTreeTX) AddSequencedLeaves(ctx context.Context, leaves []*trillian.LogLeaf, timestamp time.Time) ([]*trillian.QueuedLogLeaf, error) { t.treeTX.mu.Lock() defer t.treeTX.mu.Unlock() res := make([]*trillian.QueuedLogLeaf, len(leaves)) ok := status.New(codes.OK, "OK").Proto() // Leaves in this transaction are inserted in two tables. For each leaf, if // one of the two inserts fails, we remove the side effect by rolling back to // a savepoint installed before the first insert of the two. const savepoint = "SAVEPOINT AddSequencedLeaves" if _, err := t.tx.ExecContext(ctx, savepoint); err != nil { klog.Errorf("Error adding savepoint: %s", err) return nil, mysqlToGRPC(err) } // TODO(pavelkalinnikov): Consider performance implication of executing this // extra SAVEPOINT, especially for 1-entry batches. Optimize if necessary. // Note: LeafData inserts are presumably protected from deadlocks due to // sorting, but the order of the corresponding SequencedLeafData inserts // becomes indeterministic. However, in a typical case when leaves are // supplied in contiguous non-intersecting batches, the chance of having // circular dependencies between transactions is significantly lower. ordLeaves := sortLeavesForInsert(leaves) for _, ol := range ordLeaves { i, leaf := ol.idx, ol.leaf // This should fail on insert, but catch it early. if got, want := len(leaf.LeafIdentityHash), t.hashSizeBytes; got != want { return nil, status.Errorf(codes.FailedPrecondition, "leaves[%d] has incorrect hash size %d, want %d", i, got, want) } if _, err := t.tx.ExecContext(ctx, savepoint); err != nil { klog.Errorf("Error updating savepoint: %s", err) return nil, mysqlToGRPC(err) } res[i] = &trillian.QueuedLogLeaf{Status: ok} // TODO(pavelkalinnikov): Measure latencies. _, err := t.tx.ExecContext(ctx, insertLeafDataSQL, t.treeID, leaf.LeafIdentityHash, leaf.LeafValue, leaf.ExtraData, timestamp.UnixNano()) // TODO(pavelkalinnikov): Detach PREORDERED_LOG integration latency metric. // TODO(pavelkalinnikov): Support opting out from duplicates detection. if isDuplicateErr(err) { res[i].Status = status.New(codes.FailedPrecondition, "conflicting LeafIdentityHash").Proto() // Note: No rolling back to savepoint because there is no side effect. continue } else if err != nil { klog.Errorf("Error inserting leaves[%d] into LeafData: %s", i, err) return nil, mysqlToGRPC(err) } _, err = t.tx.ExecContext(ctx, insertSequencedLeafSQL+valuesPlaceholder5, t.treeID, leaf.LeafIdentityHash, leaf.MerkleLeafHash, leaf.LeafIndex, 0) // TODO(pavelkalinnikov): Update IntegrateTimestamp on integrating the leaf. if isDuplicateErr(err) { res[i].Status = status.New(codes.FailedPrecondition, "conflicting LeafIndex").Proto() if _, err := t.tx.ExecContext(ctx, "ROLLBACK TO "+savepoint); err != nil { klog.Errorf("Error rolling back to savepoint: %s", err) return nil, mysqlToGRPC(err) } } else if err != nil { klog.Errorf("Error inserting leaves[%d] into SequencedLeafData: %s", i, err) return nil, mysqlToGRPC(err) } // TODO(pavelkalinnikov): Load LeafData for conflicting entries. } if _, err := t.tx.ExecContext(ctx, "RELEASE "+savepoint); err != nil { klog.Errorf("Error releasing savepoint: %s", err) return nil, mysqlToGRPC(err) } return res, nil } func (t *logTreeTX) GetLeavesByRange(ctx context.Context, start, count int64) ([]*trillian.LogLeaf, error) { t.treeTX.mu.Lock() defer t.treeTX.mu.Unlock() return t.getLeavesByRangeInternal(ctx, start, count) } func (t *logTreeTX) getLeavesByRangeInternal(ctx context.Context, start, count int64) ([]*trillian.LogLeaf, error) { if count <= 0 { return nil, status.Errorf(codes.InvalidArgument, "invalid count %d, want > 0", count) } if start < 0 { return nil, status.Errorf(codes.InvalidArgument, "invalid start %d, want >= 0", start) } if t.treeType == trillian.TreeType_LOG { treeSize := int64(t.root.TreeSize) if treeSize <= 0 { return nil, status.Errorf(codes.OutOfRange, "empty tree") } else if start >= treeSize { return nil, status.Errorf(codes.OutOfRange, "invalid start %d, want < TreeSize(%d)", start, treeSize) } // Ensure no entries queried/returned beyond the tree. if maxCount := treeSize - start; count > maxCount { count = maxCount } } // TODO(pavelkalinnikov): Further clip `count` to a safe upper bound like 64k. args := []interface{}{start, start + count, t.treeID} rows, err := t.tx.QueryContext(ctx, selectLeavesByRangeSQL, args...) if err != nil { klog.Warningf("Failed to get leaves by range: %s", err) return nil, err } defer func() { if err := rows.Close(); err != nil { klog.Errorf("rows.Close(): %v", err) } }() ret := make([]*trillian.LogLeaf, 0, count) for wantIndex := start; rows.Next(); wantIndex++ { leaf := &trillian.LogLeaf{} var qTimestamp, iTimestamp int64 if err := rows.Scan( &leaf.MerkleLeafHash, &leaf.LeafIdentityHash, &leaf.LeafValue, &leaf.LeafIndex, &leaf.ExtraData, &qTimestamp, &iTimestamp); err != nil { klog.Warningf("Failed to scan merkle leaves: %s", err) return nil, err } if leaf.LeafIndex != wantIndex { if wantIndex < int64(t.root.TreeSize) { return nil, fmt.Errorf("got unexpected index %d, want %d", leaf.LeafIndex, wantIndex) } break } leaf.QueueTimestamp = timestamppb.New(time.Unix(0, qTimestamp)) if err := leaf.QueueTimestamp.CheckValid(); err != nil { return nil, fmt.Errorf("got invalid queue timestamp: %w", err) } leaf.IntegrateTimestamp = timestamppb.New(time.Unix(0, iTimestamp)) if err := leaf.IntegrateTimestamp.CheckValid(); err != nil { return nil, fmt.Errorf("got invalid integrate timestamp: %w", err) } ret = append(ret, leaf) } if err := rows.Err(); err != nil { klog.Warningf("Failed to read returned leaves: %s", err) return nil, err } return ret, nil } func (t *logTreeTX) GetLeavesByHash(ctx context.Context, leafHashes [][]byte, orderBySequence bool) ([]*trillian.LogLeaf, error) { t.treeTX.mu.Lock() defer t.treeTX.mu.Unlock() tmpl, err := t.ls.getLeavesByMerkleHashStmt(ctx, len(leafHashes), orderBySequence) if err != nil { return nil, err } return t.getLeavesByHashInternal(ctx, leafHashes, tmpl, "merkle") } // getLeafDataByIdentityHash retrieves leaf data by LeafIdentityHash, returned // as a slice of LogLeaf objects for convenience. However, note that the // returned LogLeaf objects will not have a valid MerkleLeafHash, LeafIndex, or IntegrateTimestamp. func (t *logTreeTX) getLeafDataByIdentityHash(ctx context.Context, leafHashes [][]byte) ([]*trillian.LogLeaf, error) { tmpl, err := t.ls.getLeavesByLeafIdentityHashStmt(ctx, len(leafHashes)) if err != nil { return nil, err } return t.getLeavesByHashInternal(ctx, leafHashes, tmpl, "leaf-identity") } func (t *logTreeTX) LatestSignedLogRoot(ctx context.Context) (*trillian.SignedLogRoot, error) { t.treeTX.mu.Lock() defer t.treeTX.mu.Unlock() if t.slr == nil { return nil, storage.ErrTreeNeedsInit } return t.slr, nil } // fetchLatestRoot reads the latest root and the revision from the DB. func (t *logTreeTX) fetchLatestRoot(ctx context.Context) (*trillian.SignedLogRoot, int64, error) { var timestamp, treeSize, treeRevision int64 var rootHash, rootSignatureBytes []byte if err := t.tx.QueryRowContext( ctx, selectLatestSignedLogRootSQL, t.treeID).Scan( ×tamp, &treeSize, &rootHash, &treeRevision, &rootSignatureBytes, ); err == sql.ErrNoRows { // It's possible there are no roots for this tree yet return nil, 0, storage.ErrTreeNeedsInit } // Put logRoot back together. Fortunately LogRoot has a deterministic serialization. logRoot, err := (&types.LogRootV1{ RootHash: rootHash, TimestampNanos: uint64(timestamp), TreeSize: uint64(treeSize), }).MarshalBinary() if err != nil { return nil, 0, err } return &trillian.SignedLogRoot{LogRoot: logRoot}, treeRevision, nil } func (t *logTreeTX) StoreSignedLogRoot(ctx context.Context, root *trillian.SignedLogRoot) error { t.treeTX.mu.Lock() defer t.treeTX.mu.Unlock() var logRoot types.LogRootV1 if err := logRoot.UnmarshalBinary(root.LogRoot); err != nil { klog.Warningf("Failed to parse log root: %x %v", root.LogRoot, err) return err } if len(logRoot.Metadata) != 0 { return fmt.Errorf("unimplemented: mysql storage does not support log root metadata") } res, err := t.tx.ExecContext( ctx, insertTreeHeadSQL, t.treeID, logRoot.TimestampNanos, logRoot.TreeSize, logRoot.RootHash, t.treeTX.writeRevision, []byte{}) if err != nil { klog.Warningf("Failed to store signed root: %s", err) } return checkResultOkAndRowCountIs(res, err, 1) } func (t *logTreeTX) getLeavesByHashInternal(ctx context.Context, leafHashes [][]byte, tmpl *sql.Stmt, desc string) ([]*trillian.LogLeaf, error) { stx := t.tx.StmtContext(ctx, tmpl) defer func() { if err := stx.Close(); err != nil { klog.Errorf("stx.Close(): %v", err) } }() var args []interface{} for _, hash := range leafHashes { args = append(args, []byte(hash)) } args = append(args, t.treeID) rows, err := stx.QueryContext(ctx, args...) if err != nil { klog.Warningf("Query() %s hash = %v", desc, err) return nil, err } defer func() { if err := rows.Close(); err != nil { klog.Errorf("rows.Close(): %v", err) } }() // The tree could include duplicates so we don't know how many results will be returned var ret []*trillian.LogLeaf for rows.Next() { leaf := &trillian.LogLeaf{} // We might be using a LEFT JOIN in our statement, so leaves which are // queued but not yet integrated will have a NULL IntegrateTimestamp // when there's no corresponding entry in SequencedLeafData, even though // the table definition forbids that, so we use a nullable type here and // check its validity below. var integrateTS sql.NullInt64 var queueTS int64 if err := rows.Scan(&leaf.MerkleLeafHash, &leaf.LeafIdentityHash, &leaf.LeafValue, &leaf.LeafIndex, &leaf.ExtraData, &queueTS, &integrateTS); err != nil { klog.Warningf("LogID: %d Scan() %s = %s", t.treeID, desc, err) return nil, err } leaf.QueueTimestamp = timestamppb.New(time.Unix(0, queueTS)) if err := leaf.QueueTimestamp.CheckValid(); err != nil { return nil, fmt.Errorf("got invalid queue timestamp: %w", err) } if integrateTS.Valid { leaf.IntegrateTimestamp = timestamppb.New(time.Unix(0, integrateTS.Int64)) if err := leaf.IntegrateTimestamp.CheckValid(); err != nil { return nil, fmt.Errorf("got invalid integrate timestamp: %w", err) } } if got, want := len(leaf.MerkleLeafHash), t.hashSizeBytes; got != want { return nil, fmt.Errorf("LogID: %d Scanned leaf %s does not have hash length %d, got %d", t.treeID, desc, want, got) } ret = append(ret, leaf) } if err := rows.Err(); err != nil { klog.Warningf("Failed to read returned leaves: %s", err) return nil, err } return ret, nil } // leafAndPosition records original position before sort. type leafAndPosition struct { leaf *trillian.LogLeaf idx int } // byLeafIdentityHashWithPosition allows sorting (as above), but where we need // to remember the original position type byLeafIdentityHashWithPosition []leafAndPosition func (l byLeafIdentityHashWithPosition) Len() int { return len(l) } func (l byLeafIdentityHashWithPosition) Swap(i, j int) { l[i], l[j] = l[j], l[i] } func (l byLeafIdentityHashWithPosition) Less(i, j int) bool { return bytes.Compare(l[i].leaf.LeafIdentityHash, l[j].leaf.LeafIdentityHash) == -1 } trillian-1.6.1/storage/mysql/log_storage_test.go000066400000000000000000000656301466362047600220710ustar00rootroot00000000000000// 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 mysql import ( "bytes" "context" "crypto/sha256" "database/sql" "fmt" "sort" "testing" "time" "github.com/google/go-cmp/cmp" "github.com/google/trillian" "github.com/google/trillian/integration/storagetest" "github.com/google/trillian/storage" "github.com/google/trillian/storage/testonly" "github.com/google/trillian/types" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/timestamppb" "k8s.io/klog/v2" _ "github.com/go-sql-driver/mysql" ) var allTables = []string{"Unsequenced", "TreeHead", "SequencedLeafData", "LeafData", "Subtree", "TreeControl", "Trees"} // Must be 32 bytes to match sha256 length if it was a real hash var ( dummyHash = []byte("hashxxxxhashxxxxhashxxxxhashxxxx") dummyRawHash = []byte("xxxxhashxxxxhashxxxxhashxxxxhash") dummyHash2 = []byte("HASHxxxxhashxxxxhashxxxxhashxxxx") ) // Time we will queue all leaves at var fakeQueueTime = time.Date(2016, 11, 10, 15, 16, 27, 0, time.UTC) // Time we will integrate all leaves at var fakeIntegrateTime = time.Date(2016, 11, 10, 15, 16, 30, 0, time.UTC) // Time we'll request for guard cutoff in tests that don't test this (should include all above) var fakeDequeueCutoffTime = time.Date(2016, 11, 10, 15, 16, 30, 0, time.UTC) // Used for tests involving extra data var someExtraData = []byte("Some extra data") const ( leavesToInsert = 5 sequenceNumber int64 = 237 ) // Tests that access the db should each use a distinct log ID to prevent lock contention when // run in parallel or race conditions / unexpected interactions. Tests that pass should hold // no locks afterwards. func createFakeLeaf(ctx context.Context, db *sql.DB, logID int64, rawHash, hash, data, extraData []byte, seq int64, t *testing.T) *trillian.LogLeaf { t.Helper() queuedAtNanos := fakeQueueTime.UnixNano() integratedAtNanos := fakeIntegrateTime.UnixNano() _, err := db.ExecContext(ctx, "INSERT INTO LeafData(TreeId, LeafIdentityHash, LeafValue, ExtraData, QueueTimestampNanos) VALUES(?,?,?,?,?)", logID, rawHash, data, extraData, queuedAtNanos) _, err2 := db.ExecContext(ctx, "INSERT INTO SequencedLeafData(TreeId, SequenceNumber, LeafIdentityHash, MerkleLeafHash, IntegrateTimestampNanos) VALUES(?,?,?,?,?)", logID, seq, rawHash, hash, integratedAtNanos) if err != nil || err2 != nil { t.Fatalf("Failed to create test leaves: %v %v", err, err2) } queueTimestamp := timestamppb.New(fakeQueueTime) integrateTimestamp := timestamppb.New(fakeIntegrateTime) return &trillian.LogLeaf{ MerkleLeafHash: hash, LeafValue: data, ExtraData: extraData, LeafIndex: seq, LeafIdentityHash: rawHash, QueueTimestamp: queueTimestamp, IntegrateTimestamp: integrateTimestamp, } } func checkLeafContents(leaf *trillian.LogLeaf, seq int64, rawHash, hash, data, extraData []byte, t *testing.T) { t.Helper() if got, want := leaf.MerkleLeafHash, hash; !bytes.Equal(got, want) { t.Fatalf("Wrong leaf hash in returned leaf got\n%v\nwant:\n%v", got, want) } if got, want := leaf.LeafIdentityHash, rawHash; !bytes.Equal(got, want) { t.Fatalf("Wrong raw leaf hash in returned leaf got\n%v\nwant:\n%v", got, want) } if got, want := seq, leaf.LeafIndex; got != want { t.Fatalf("Bad sequence number in returned leaf got: %d, want:%d", got, want) } if got, want := leaf.LeafValue, data; !bytes.Equal(got, want) { t.Fatalf("Unxpected data in returned leaf. got:\n%v\nwant:\n%v", got, want) } if got, want := leaf.ExtraData, extraData; !bytes.Equal(got, want) { t.Fatalf("Unxpected data in returned leaf. got:\n%v\nwant:\n%v", got, want) } iTime := leaf.IntegrateTimestamp.AsTime() if got, want := iTime.UnixNano(), fakeIntegrateTime.UnixNano(); got != want { t.Errorf("Wrong IntegrateTimestamp: got %v, want %v", got, want) } } func TestLogSuite(t *testing.T) { storageFactory := func(context.Context, *testing.T) (storage.LogStorage, storage.AdminStorage) { t.Cleanup(func() { cleanTestDB(DB) }) return NewLogStorage(DB, nil), NewAdminStorage(DB) } storagetest.RunLogStorageTests(t, storageFactory) } func TestQueueDuplicateLeaf(t *testing.T) { ctx := context.Background() cleanTestDB(DB) as := NewAdminStorage(DB) tree := mustCreateTree(ctx, t, as, testonly.LogTree) s := NewLogStorage(DB, nil) mustSignAndStoreLogRoot(ctx, t, s, tree, 0) count := 15 leaves := createTestLeaves(int64(count), 10) leaves2 := createTestLeaves(int64(count), 12) leaves3 := createTestLeaves(3, 100) leaves4 := createTestLeaves(3, 105) // Note that tests accumulate queued leaves on top of each other. tests := []struct { desc string leaves []*trillian.LogLeaf want []*trillian.LogLeaf }{ { desc: "[10, 11, 12, ...]", leaves: leaves, want: make([]*trillian.LogLeaf, count), }, { desc: "[12, 13, 14, ...] so first (count-2) are duplicates", leaves: leaves2, want: append(leaves[2:], nil, nil), }, { desc: "[10, 100, 11, 101, 102] so [dup, new, dup, new, dup]", leaves: []*trillian.LogLeaf{leaves[0], leaves3[0], leaves[1], leaves3[1], leaves[2]}, want: []*trillian.LogLeaf{leaves[0], nil, leaves[1], nil, leaves[2]}, }, { // we explictly reuse tests that have already been integrated to test issue 3603 desc: "[100, 100, 106, 101, 107]", leaves: []*trillian.LogLeaf{leaves3[0], leaves3[0], leaves4[1], leaves3[1], leaves4[2]}, want: []*trillian.LogLeaf{leaves3[0], leaves3[0], leaves4[1], leaves3[1], leaves4[2]}, }, } for _, test := range tests { t.Run(test.desc, func(t *testing.T) { existing, err := s.QueueLeaves(ctx, tree, test.leaves, fakeQueueTime) if err != nil { t.Fatalf("Failed to queue leaves: %v", err) } if len(existing) != len(test.want) { t.Fatalf("|QueueLeaves()|=%d; want %d", len(existing), len(test.want)) } for i, want := range test.want { got := existing[i] if want == nil { if got.Status != nil { t.Errorf("QueueLeaves()[%d].Code: %v; want %v", i, got, want) } return } if got == nil { t.Fatalf("QueueLeaves()[%d]=nil; want non-nil", i) } else if !bytes.Equal(got.Leaf.LeafIdentityHash, want.LeafIdentityHash) { t.Fatalf("QueueLeaves()[%d].LeafIdentityHash=%x; want %x", i, got.Leaf.LeafIdentityHash, want.LeafIdentityHash) } } }) } } func TestQueueLeaves(t *testing.T) { ctx := context.Background() cleanTestDB(DB) as := NewAdminStorage(DB) tree := mustCreateTree(ctx, t, as, testonly.LogTree) s := NewLogStorage(DB, nil) mustSignAndStoreLogRoot(ctx, t, s, tree, 0) leaves := createTestLeaves(leavesToInsert, 20) if _, err := s.QueueLeaves(ctx, tree, leaves, fakeQueueTime); err != nil { t.Fatalf("Failed to queue leaves: %v", err) } // Should see the leaves in the database. There is no API to read from the unsequenced data. var count int if err := DB.QueryRowContext(ctx, "SELECT COUNT(*) FROM Unsequenced WHERE TreeID=?", tree.TreeId).Scan(&count); err != nil { t.Fatalf("Could not query row count: %v", err) } if leavesToInsert != count { t.Fatalf("Expected %d unsequenced rows but got: %d", leavesToInsert, count) } // Additional check on timestamp being set correctly in the database var queueTimestamp int64 if err := DB.QueryRowContext(ctx, "SELECT DISTINCT QueueTimestampNanos FROM Unsequenced WHERE TreeID=?", tree.TreeId).Scan(&queueTimestamp); err != nil { t.Fatalf("Could not query timestamp: %v", err) } if got, want := queueTimestamp, fakeQueueTime.UnixNano(); got != want { t.Fatalf("Incorrect queue timestamp got: %d want: %d", got, want) } } func TestQueueLeavesDuplicateBigBatch(t *testing.T) { t.Skip("Known Issue: https://github.com/google/trillian/issues/1845") ctx := context.Background() cleanTestDB(DB) as := NewAdminStorage(DB) tree := mustCreateTree(ctx, t, as, testonly.LogTree) s := NewLogStorage(DB, nil) mustSignAndStoreLogRoot(ctx, t, s, tree, 0) const leafCount = 999 + 1 leaves := createTestLeaves(leafCount, 20) if _, err := s.QueueLeaves(ctx, tree, leaves, fakeQueueTime); err != nil { t.Fatalf("Failed to queue leaves: %v", err) } if _, err := s.QueueLeaves(ctx, tree, leaves, fakeQueueTime); err != nil { t.Fatalf("Failed to queue leaves: %v", err) } // Should see the leaves in the database. There is no API to read from the unsequenced data. var count int if err := DB.QueryRowContext(ctx, "SELECT COUNT(*) FROM Unsequenced WHERE TreeID=?", tree.TreeId).Scan(&count); err != nil { t.Fatalf("Could not query row count: %v", err) } if leafCount != count { t.Fatalf("Expected %d unsequenced rows but got: %d", leafCount, count) } } // ----------------------------------------------------------------------------- func TestDequeueLeavesHaveQueueTimestamp(t *testing.T) { ctx := context.Background() cleanTestDB(DB) as := NewAdminStorage(DB) tree := mustCreateTree(ctx, t, as, testonly.LogTree) s := NewLogStorage(DB, nil) mustSignAndStoreLogRoot(ctx, t, s, tree, 0) leaves := createTestLeaves(leavesToInsert, 20) if _, err := s.QueueLeaves(ctx, tree, leaves, fakeDequeueCutoffTime); err != nil { t.Fatalf("Failed to queue leaves: %v", err) } { // Now try to dequeue them runLogTX(s, tree, t, func(ctx context.Context, tx2 storage.LogTreeTX) error { leaves2, err := tx2.DequeueLeaves(ctx, 99, fakeDequeueCutoffTime) if err != nil { t.Fatalf("Failed to dequeue leaves: %v", err) } if len(leaves2) != leavesToInsert { t.Fatalf("Dequeued %d leaves but expected to get %d", len(leaves2), leavesToInsert) } ensureLeavesHaveQueueTimestamp(t, leaves2, fakeDequeueCutoffTime) return nil }) } } // Queues leaves and attempts to dequeue before the guard cutoff allows it. This should // return nothing. Then retry with an inclusive guard cutoff and ensure the leaves // are returned. func TestDequeueLeavesGuardInterval(t *testing.T) { ctx := context.Background() cleanTestDB(DB) as := NewAdminStorage(DB) tree := mustCreateTree(ctx, t, as, testonly.LogTree) s := NewLogStorage(DB, nil) mustSignAndStoreLogRoot(ctx, t, s, tree, 0) leaves := createTestLeaves(leavesToInsert, 20) if _, err := s.QueueLeaves(ctx, tree, leaves, fakeQueueTime); err != nil { t.Fatalf("Failed to queue leaves: %v", err) } { // Now try to dequeue them using a cutoff that means we should get none runLogTX(s, tree, t, func(ctx context.Context, tx2 storage.LogTreeTX) error { leaves2, err := tx2.DequeueLeaves(ctx, 99, fakeQueueTime.Add(-time.Second)) if err != nil { t.Fatalf("Failed to dequeue leaves: %v", err) } if len(leaves2) != 0 { t.Fatalf("Dequeued %d leaves when they all should be in guard interval", len(leaves2)) } // Try to dequeue again using a cutoff that should include them leaves2, err = tx2.DequeueLeaves(ctx, 99, fakeQueueTime.Add(time.Second)) if err != nil { t.Fatalf("Failed to dequeue leaves: %v", err) } if len(leaves2) != leavesToInsert { t.Fatalf("Dequeued %d leaves but expected to get %d", len(leaves2), leavesToInsert) } ensureAllLeavesDistinct(leaves2, t) return nil }) } } func TestDequeueLeavesTimeOrdering(t *testing.T) { // Queue two small batches of leaves at different timestamps. Do two separate dequeue // transactions and make sure the returned leaves are respecting the time ordering of the // queue. ctx := context.Background() cleanTestDB(DB) as := NewAdminStorage(DB) tree := mustCreateTree(ctx, t, as, testonly.LogTree) s := NewLogStorage(DB, nil) mustSignAndStoreLogRoot(ctx, t, s, tree, 0) batchSize := 2 leaves := createTestLeaves(int64(batchSize), 0) leaves2 := createTestLeaves(int64(batchSize), int64(batchSize)) if _, err := s.QueueLeaves(ctx, tree, leaves, fakeQueueTime); err != nil { t.Fatalf("QueueLeaves(1st batch) = %v", err) } // These are one second earlier so should be dequeued first if _, err := s.QueueLeaves(ctx, tree, leaves2, fakeQueueTime.Add(-time.Second)); err != nil { t.Fatalf("QueueLeaves(2nd batch) = %v", err) } { // Now try to dequeue two leaves and we should get the second batch runLogTX(s, tree, t, func(ctx context.Context, tx2 storage.LogTreeTX) error { dequeue1, err := tx2.DequeueLeaves(ctx, batchSize, fakeQueueTime) if err != nil { t.Fatalf("DequeueLeaves(1st) = %v", err) } if got, want := len(dequeue1), batchSize; got != want { t.Fatalf("Dequeue count mismatch (1st) got: %d, want: %d", got, want) } ensureAllLeavesDistinct(dequeue1, t) // Ensure this is the second batch queued by comparing leaf hashes (must be distinct as // the leaf data was). if !leafInBatch(dequeue1[0], leaves2) || !leafInBatch(dequeue1[1], leaves2) { t.Fatalf("Got leaf from wrong batch (1st dequeue): %v", dequeue1) } iTimestamp := timestamppb.Now() for i, l := range dequeue1 { l.IntegrateTimestamp = iTimestamp l.LeafIndex = int64(i) } if err := tx2.UpdateSequencedLeaves(ctx, dequeue1); err != nil { t.Fatalf("UpdateSequencedLeaves(): %v", err) } return nil }) // Try to dequeue again and we should get the batch that was queued first, though at a later time runLogTX(s, tree, t, func(ctx context.Context, tx3 storage.LogTreeTX) error { dequeue2, err := tx3.DequeueLeaves(ctx, batchSize, fakeQueueTime) if err != nil { t.Fatalf("DequeueLeaves(2nd) = %v", err) } if got, want := len(dequeue2), batchSize; got != want { t.Fatalf("Dequeue count mismatch (2nd) got: %d, want: %d", got, want) } ensureAllLeavesDistinct(dequeue2, t) // Ensure this is the first batch by comparing leaf hashes. if !leafInBatch(dequeue2[0], leaves) || !leafInBatch(dequeue2[1], leaves) { t.Fatalf("Got leaf from wrong batch (2nd dequeue): %v", dequeue2) } return nil }) } } func TestGetLeavesByHashNotPresent(t *testing.T) { ctx := context.Background() cleanTestDB(DB) as := NewAdminStorage(DB) tree := mustCreateTree(ctx, t, as, testonly.LogTree) s := NewLogStorage(DB, nil) runLogTX(s, tree, t, func(ctx context.Context, tx storage.LogTreeTX) error { hashes := [][]byte{[]byte("thisdoesn'texist")} leaves, err := tx.GetLeavesByHash(ctx, hashes, false) if err != nil { t.Fatalf("Error getting leaves by hash: %v", err) } if len(leaves) != 0 { t.Fatalf("Expected no leaves returned but got %d", len(leaves)) } return nil }) } func TestGetLeavesByHash(t *testing.T) { ctx := context.Background() // Create fake leaf as if it had been sequenced cleanTestDB(DB) as := NewAdminStorage(DB) tree := mustCreateTree(ctx, t, as, testonly.LogTree) s := NewLogStorage(DB, nil) data := []byte("some data") createFakeLeaf(ctx, DB, tree.TreeId, dummyRawHash, dummyHash, data, someExtraData, sequenceNumber, t) runLogTX(s, tree, t, func(ctx context.Context, tx storage.LogTreeTX) error { hashes := [][]byte{dummyHash} leaves, err := tx.GetLeavesByHash(ctx, hashes, false) if err != nil { t.Fatalf("Unexpected error getting leaf by hash: %v", err) } if len(leaves) != 1 { t.Fatalf("Got %d leaves but expected one", len(leaves)) } checkLeafContents(leaves[0], sequenceNumber, dummyRawHash, dummyHash, data, someExtraData, t) return nil }) } func TestGetLeavesByHashBigBatch(t *testing.T) { t.Skip("Known Issue: https://github.com/google/trillian/issues/1845") ctx := context.Background() // Create fake leaf as if it had been sequenced cleanTestDB(DB) as := NewAdminStorage(DB) tree := mustCreateTree(ctx, t, as, testonly.LogTree) s := NewLogStorage(DB, nil) const leafCount = 999 + 1 hashes := make([][]byte, leafCount) for i := 0; i < leafCount; i++ { data := []byte(fmt.Sprintf("data %d", i)) hash := sha256.Sum256(data) hashes[i] = hash[:] createFakeLeaf(ctx, DB, tree.TreeId, hash[:], hash[:], data, someExtraData, sequenceNumber+int64(i), t) } runLogTX(s, tree, t, func(ctx context.Context, tx storage.LogTreeTX) error { leaves, err := tx.GetLeavesByHash(ctx, hashes, false) if err != nil { t.Fatalf("Unexpected error getting leaf by hash: %v", err) } if got, want := len(leaves), leafCount; got != want { t.Fatalf("Got %d leaves, expected %d", got, want) } return nil }) } func TestGetLeafDataByIdentityHash(t *testing.T) { ctx := context.Background() // Create fake leaf as if it had been sequenced cleanTestDB(DB) as := NewAdminStorage(DB) tree := mustCreateTree(ctx, t, as, testonly.LogTree) s := NewLogStorage(DB, nil) data := []byte("some data") leaf := createFakeLeaf(ctx, DB, tree.TreeId, dummyRawHash, dummyHash, data, someExtraData, sequenceNumber, t) leaf.LeafIndex = -1 leaf.MerkleLeafHash = []byte(dummyMerkleLeafHash) leaf2 := createFakeLeaf(ctx, DB, tree.TreeId, dummyHash2, dummyHash2, data, someExtraData, sequenceNumber+1, t) leaf2.LeafIndex = -1 leaf2.MerkleLeafHash = []byte(dummyMerkleLeafHash) tests := []struct { hashes [][]byte want []*trillian.LogLeaf }{ { hashes: [][]byte{dummyRawHash}, want: []*trillian.LogLeaf{leaf}, }, { hashes: [][]byte{{0x01, 0x02}}, }, { hashes: [][]byte{ dummyRawHash, {0x01, 0x02}, dummyHash2, {0x01, 0x02}, }, // Note: leaves not necessarily returned in order requested. want: []*trillian.LogLeaf{leaf2, leaf}, }, } for i, test := range tests { t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { runLogTX(s, tree, t, func(ctx context.Context, tx storage.LogTreeTX) error { leaves, err := tx.(*logTreeTX).getLeafDataByIdentityHash(ctx, test.hashes) if err != nil { t.Fatalf("getLeavesByIdentityHash(_) = (_,%v); want (_,nil)", err) } if len(leaves) != len(test.want) { t.Fatalf("getLeavesByIdentityHash(_) = (|%d|,nil); want (|%d|,nil)", len(leaves), len(test.want)) } leavesEquivalent(t, leaves, test.want) return nil }) }) } } func leavesEquivalent(t *testing.T, gotLeaves, wantLeaves []*trillian.LogLeaf) { t.Helper() want := make(map[string]*trillian.LogLeaf) for _, w := range wantLeaves { k := sha256.Sum256([]byte(w.String())) want[string(k[:])] = w } got := make(map[string]*trillian.LogLeaf) for _, g := range gotLeaves { k := sha256.Sum256([]byte(g.String())) got[string(k[:])] = g } if diff := cmp.Diff(want, got, cmp.Comparer(proto.Equal)); diff != "" { t.Errorf("leaves not equivalent: diff -want,+got:\n%v", diff) } } // ----------------------------------------------------------------------------- func TestLatestSignedRootNoneWritten(t *testing.T) { ctx := context.Background() cleanTestDB(DB) as := NewAdminStorage(DB) tree := mustCreateTree(ctx, t, as, testonly.LogTree) s := NewLogStorage(DB, nil) tx, err := s.SnapshotForTree(ctx, tree) if err != storage.ErrTreeNeedsInit { t.Fatalf("SnapshotForTree gave %v, want %v", err, storage.ErrTreeNeedsInit) } commit(ctx, tx, t) } func SignLogRoot(root *types.LogRootV1) (*trillian.SignedLogRoot, error) { logRoot, err := root.MarshalBinary() if err != nil { return nil, err } return &trillian.SignedLogRoot{LogRoot: logRoot}, nil } func TestLatestSignedLogRoot(t *testing.T) { ctx := context.Background() cleanTestDB(DB) as := NewAdminStorage(DB) tree := mustCreateTree(ctx, t, as, testonly.LogTree) s := NewLogStorage(DB, nil) root, err := SignLogRoot(&types.LogRootV1{ TimestampNanos: 98765, TreeSize: 16, RootHash: []byte(dummyHash), }) if err != nil { t.Fatalf("SignLogRoot(): %v", err) } runLogTX(s, tree, t, func(ctx context.Context, tx storage.LogTreeTX) error { if err := tx.StoreSignedLogRoot(ctx, root); err != nil { t.Fatalf("Failed to store signed root: %v", err) } return nil }) { runLogTX(s, tree, t, func(ctx context.Context, tx2 storage.LogTreeTX) error { root2, err := tx2.LatestSignedLogRoot(ctx) if err != nil { t.Fatalf("Failed to read back new log root: %v", err) } if !proto.Equal(root, root2) { t.Fatalf("Root round trip failed: <%v> and: <%v>", root, root2) } return nil }) } } func TestDuplicateSignedLogRoot(t *testing.T) { ctx := context.Background() cleanTestDB(DB) as := NewAdminStorage(DB) tree := mustCreateTree(ctx, t, as, testonly.LogTree) s := NewLogStorage(DB, nil) root, err := SignLogRoot(&types.LogRootV1{ TimestampNanos: 98765, TreeSize: 16, RootHash: []byte(dummyHash), }) if err != nil { t.Fatalf("SignLogRoot(): %v", err) } runLogTX(s, tree, t, func(ctx context.Context, tx storage.LogTreeTX) error { if err := tx.StoreSignedLogRoot(ctx, root); err != nil { t.Fatalf("Failed to store signed root: %v", err) } // Shouldn't be able to do it again if err := tx.StoreSignedLogRoot(ctx, root); err == nil { t.Fatal("Allowed duplicate signed root") } return nil }) } func TestLogRootUpdate(t *testing.T) { ctx := context.Background() // Write two roots for a log and make sure the one with the newest timestamp supersedes cleanTestDB(DB) as := NewAdminStorage(DB) tree := mustCreateTree(ctx, t, as, testonly.LogTree) s := NewLogStorage(DB, nil) root, err := SignLogRoot(&types.LogRootV1{ TimestampNanos: 98765, TreeSize: 16, RootHash: []byte(dummyHash), }) if err != nil { t.Fatalf("SignLogRoot(): %v", err) } root2, err := SignLogRoot(&types.LogRootV1{ TimestampNanos: 98766, TreeSize: 16, RootHash: []byte(dummyHash), }) if err != nil { t.Fatalf("SignLogRoot(): %v", err) } runLogTX(s, tree, t, func(ctx context.Context, tx storage.LogTreeTX) error { return tx.StoreSignedLogRoot(ctx, root) }) runLogTX(s, tree, t, func(ctx context.Context, tx storage.LogTreeTX) error { return tx.StoreSignedLogRoot(ctx, root2) }) runLogTX(s, tree, t, func(ctx context.Context, tx2 storage.LogTreeTX) error { root3, err := tx2.LatestSignedLogRoot(ctx) if err != nil { t.Fatalf("Failed to read back new log root: %v", err) } if !proto.Equal(root2, root3) { t.Fatalf("Root round trip failed: <%v> and: <%v>", root, root2) } return nil }) } func TestGetActiveLogIDs(t *testing.T) { ctx := context.Background() cleanTestDB(DB) admin := NewAdminStorage(DB) // Create a few test trees log1 := proto.Clone(testonly.LogTree).(*trillian.Tree) log2 := proto.Clone(testonly.LogTree).(*trillian.Tree) log3 := proto.Clone(testonly.PreorderedLogTree).(*trillian.Tree) drainingLog := proto.Clone(testonly.LogTree).(*trillian.Tree) frozenLog := proto.Clone(testonly.LogTree).(*trillian.Tree) deletedLog := proto.Clone(testonly.LogTree).(*trillian.Tree) for _, tree := range []**trillian.Tree{&log1, &log2, &log3, &drainingLog, &frozenLog, &deletedLog} { newTree, err := storage.CreateTree(ctx, admin, *tree) if err != nil { t.Fatalf("CreateTree(%+v) returned err = %v", tree, err) } *tree = newTree } // FROZEN is not a valid initial state, so we have to update it separately. if _, err := storage.UpdateTree(ctx, admin, frozenLog.TreeId, func(t *trillian.Tree) { t.TreeState = trillian.TreeState_FROZEN }); err != nil { t.Fatalf("UpdateTree() returned err = %v", err) } // DRAINING is not a valid initial state, so we have to update it separately. if _, err := storage.UpdateTree(ctx, admin, drainingLog.TreeId, func(t *trillian.Tree) { t.TreeState = trillian.TreeState_DRAINING }); err != nil { t.Fatalf("UpdateTree() returned err = %v", err) } // Update deleted trees accordingly updateDeletedStmt, err := DB.PrepareContext(ctx, "UPDATE Trees SET Deleted = ? WHERE TreeId = ?") if err != nil { t.Fatalf("PrepareContext() returned err = %v", err) } defer func() { if err := updateDeletedStmt.Close(); err != nil { klog.Errorf("updateDeletedStmt.Close(): %v", err) } }() for _, treeID := range []int64{deletedLog.TreeId} { if _, err := updateDeletedStmt.ExecContext(ctx, true, treeID); err != nil { t.Fatalf("ExecContext(%v) returned err = %v", treeID, err) } } s := NewLogStorage(DB, nil) got, err := s.GetActiveLogIDs(ctx) if err != nil { t.Fatalf("GetActiveLogIDs() returns err = %v", err) } want := []int64{log1.TreeId, log2.TreeId, log3.TreeId, drainingLog.TreeId} sort.Slice(got, func(i, j int) bool { return got[i] < got[j] }) sort.Slice(want, func(i, j int) bool { return want[i] < want[j] }) if diff := cmp.Diff(got, want); diff != "" { t.Errorf("post-GetActiveLogIDs diff (-got +want):\n%v", diff) } } func TestGetActiveLogIDsEmpty(t *testing.T) { ctx := context.Background() cleanTestDB(DB) s := NewLogStorage(DB, nil) ids, err := s.GetActiveLogIDs(ctx) if err != nil { t.Fatalf("GetActiveLogIDs() = (_, %v), want = (_, nil)", err) } if got, want := len(ids), 0; got != want { t.Errorf("GetActiveLogIDs(): got %v IDs, want = %v", got, want) } } func ensureAllLeavesDistinct(leaves []*trillian.LogLeaf, t *testing.T) { t.Helper() // All the leaf value hashes should be distinct because the leaves were created with distinct // leaf data. If only we had maps with slices as keys or sets or pretty much any kind of usable // data structures we could do this properly. for i := range leaves { for j := range leaves { if i != j && bytes.Equal(leaves[i].LeafIdentityHash, leaves[j].LeafIdentityHash) { t.Fatalf("Unexpectedly got a duplicate leaf hash: %v %v", leaves[i].LeafIdentityHash, leaves[j].LeafIdentityHash) } } } } func ensureLeavesHaveQueueTimestamp(t *testing.T, leaves []*trillian.LogLeaf, want time.Time) { t.Helper() for _, leaf := range leaves { gotQTimestamp := leaf.QueueTimestamp.AsTime() if got, want := gotQTimestamp.UnixNano(), want.UnixNano(); got != want { t.Errorf("Got leaf with QueueTimestampNanos = %v, want %v: %v", got, want, leaf) } } } // Creates some test leaves with predictable data func createTestLeaves(n, startSeq int64) []*trillian.LogLeaf { var leaves []*trillian.LogLeaf for l := int64(0); l < n; l++ { lv := fmt.Sprintf("Leaf %d", l+startSeq) h := sha256.New() h.Write([]byte(lv)) leafHash := h.Sum(nil) leaf := &trillian.LogLeaf{ LeafIdentityHash: leafHash, MerkleLeafHash: leafHash, LeafValue: []byte(lv), ExtraData: []byte(fmt.Sprintf("Extra %d", l)), LeafIndex: int64(startSeq + l), } leaves = append(leaves, leaf) } return leaves } // Convenience methods to avoid copying out "if err != nil { blah }" all over the place func runLogTX(s storage.LogStorage, tree *trillian.Tree, t *testing.T, f storage.LogTXFunc) { t.Helper() if err := s.ReadWriteTransaction(context.Background(), tree, f); err != nil { t.Fatalf("Failed to run log tx: %v", err) } } type committableTX interface { Commit(ctx context.Context) error } func commit(ctx context.Context, tx committableTX, t *testing.T) { t.Helper() if err := tx.Commit(ctx); err != nil { t.Errorf("Failed to commit tx: %v", err) } } func leafInBatch(leaf *trillian.LogLeaf, batch []*trillian.LogLeaf) bool { for _, bl := range batch { if bytes.Equal(bl.LeafIdentityHash, leaf.LeafIdentityHash) { return true } } return false } trillian-1.6.1/storage/mysql/mysqlpb/000077500000000000000000000000001466362047600176535ustar00rootroot00000000000000trillian-1.6.1/storage/mysql/mysqlpb/gen.go000066400000000000000000000014171466362047600207560ustar00rootroot00000000000000// Copyright 2023 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 mysqlpb contains protobuf definitions used by the mysql implementation. package mysqlpb //go:generate protoc -I=. --go_out=paths=source_relative:. options.proto trillian-1.6.1/storage/mysql/mysqlpb/options.pb.go000066400000000000000000000126101466362047600222750ustar00rootroot00000000000000// Copyright 2023 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. // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.34.2 // protoc v3.20.1 // source: options.proto package mysqlpb import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // StorageOptions contains configuration parameters for MySQL implementation // of the storage backend. This is envisioned only to be used for changes that // would be breaking, but need to support old behaviour for backwards compatibility. type StorageOptions struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // subtreeRevisions being explicitly set to false will skip writing subtree revisions. // https://github.com/google/trillian/pull/3201 SubtreeRevisions bool `protobuf:"varint,1,opt,name=subtreeRevisions,proto3" json:"subtreeRevisions,omitempty"` } func (x *StorageOptions) Reset() { *x = StorageOptions{} if protoimpl.UnsafeEnabled { mi := &file_options_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *StorageOptions) String() string { return protoimpl.X.MessageStringOf(x) } func (*StorageOptions) ProtoMessage() {} func (x *StorageOptions) ProtoReflect() protoreflect.Message { mi := &file_options_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use StorageOptions.ProtoReflect.Descriptor instead. func (*StorageOptions) Descriptor() ([]byte, []int) { return file_options_proto_rawDescGZIP(), []int{0} } func (x *StorageOptions) GetSubtreeRevisions() bool { if x != nil { return x.SubtreeRevisions } return false } var File_options_proto protoreflect.FileDescriptor var file_options_proto_rawDesc = []byte{ 0x0a, 0x0d, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x6d, 0x79, 0x73, 0x71, 0x6c, 0x70, 0x62, 0x22, 0x3c, 0x0a, 0x0e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2a, 0x0a, 0x10, 0x73, 0x75, 0x62, 0x74, 0x72, 0x65, 0x65, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x73, 0x75, 0x62, 0x74, 0x72, 0x65, 0x65, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x32, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, 0x6d, 0x79, 0x73, 0x71, 0x6c, 0x2f, 0x6d, 0x79, 0x73, 0x71, 0x6c, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( file_options_proto_rawDescOnce sync.Once file_options_proto_rawDescData = file_options_proto_rawDesc ) func file_options_proto_rawDescGZIP() []byte { file_options_proto_rawDescOnce.Do(func() { file_options_proto_rawDescData = protoimpl.X.CompressGZIP(file_options_proto_rawDescData) }) return file_options_proto_rawDescData } var file_options_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_options_proto_goTypes = []any{ (*StorageOptions)(nil), // 0: mysqlpb.StorageOptions } var file_options_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type 0, // [0:0] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_options_proto_init() } func file_options_proto_init() { if File_options_proto != nil { return } if !protoimpl.UnsafeEnabled { file_options_proto_msgTypes[0].Exporter = func(v any, i int) any { switch v := v.(*StorageOptions); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_options_proto_rawDesc, NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_options_proto_goTypes, DependencyIndexes: file_options_proto_depIdxs, MessageInfos: file_options_proto_msgTypes, }.Build() File_options_proto = out.File file_options_proto_rawDesc = nil file_options_proto_goTypes = nil file_options_proto_depIdxs = nil } trillian-1.6.1/storage/mysql/mysqlpb/options.proto000066400000000000000000000022121466362047600224300ustar00rootroot00000000000000// Copyright 2023 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. syntax = "proto3"; option go_package = "github.com/google/trillian/storage/mysql/mysqlpb"; package mysqlpb; // StorageOptions contains configuration parameters for MySQL implementation // of the storage backend. This is envisioned only to be used for changes that // would be breaking, but need to support old behaviour for backwards compatibility. message StorageOptions { // subtreeRevisions being explicitly set to false will skip writing subtree revisions. // https://github.com/google/trillian/pull/3201 bool subtreeRevisions = 1; } trillian-1.6.1/storage/mysql/provider.go000066400000000000000000000077461466362047600203630ustar00rootroot00000000000000// Copyright 2018 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 mysql import ( "crypto/tls" "crypto/x509" "database/sql" "errors" "flag" "os" "sync" "github.com/google/trillian/monitoring" "github.com/google/trillian/storage" "k8s.io/klog/v2" // Load MySQL driver "github.com/go-sql-driver/mysql" ) var ( mySQLURI = flag.String("mysql_uri", "test:zaphod@tcp(127.0.0.1:3306)/test", "Connection URI for MySQL database") maxConns = flag.Int("mysql_max_conns", 0, "Maximum connections to the database") maxIdle = flag.Int("mysql_max_idle_conns", -1, "Maximum idle database connections in the connection pool") mySQLTLSCA = flag.String("mysql_tls_ca", "", "Path to the CA certificate file for MySQL TLS connection ") mySQLServerName = flag.String("mysql_server_name", "", "Name of the MySQL server to be used as the Server Name in the TLS configuration") mysqlMu sync.Mutex mysqlErr error mysqlDB *sql.DB mysqlStorageInstance *mysqlProvider ) // GetDatabase returns an instance of MySQL database, or creates one. // // TODO(pavelkalinnikov): Make the dependency of MySQL quota provider from // MySQL storage provider explicit. func GetDatabase() (*sql.DB, error) { mysqlMu.Lock() defer mysqlMu.Unlock() return getMySQLDatabaseLocked() } func init() { if err := storage.RegisterProvider("mysql", newMySQLStorageProvider); err != nil { klog.Fatalf("Failed to register storage provider mysql: %v", err) } } type mysqlProvider struct { db *sql.DB mf monitoring.MetricFactory } func newMySQLStorageProvider(mf monitoring.MetricFactory) (storage.Provider, error) { mysqlMu.Lock() defer mysqlMu.Unlock() if mysqlStorageInstance == nil { db, err := getMySQLDatabaseLocked() if err != nil { return nil, err } mysqlStorageInstance = &mysqlProvider{ db: db, mf: mf, } } return mysqlStorageInstance, nil } // getMySQLDatabaseLocked returns an instance of MySQL database, or creates // one. Requires mysqlMu to be locked. func getMySQLDatabaseLocked() (*sql.DB, error) { if mysqlDB != nil || mysqlErr != nil { return mysqlDB, mysqlErr } dsn := *mySQLURI if *mySQLTLSCA != "" { if err := registerMySQLTLSConfig(); err != nil { return nil, err } dsn += "?tls=custom" } db, err := OpenDB(dsn) if err != nil { mysqlErr = err return nil, err } if *maxConns > 0 { db.SetMaxOpenConns(*maxConns) } if *maxIdle >= 0 { db.SetMaxIdleConns(*maxIdle) } mysqlDB, mysqlErr = db, nil return db, nil } func (s *mysqlProvider) LogStorage() storage.LogStorage { return NewLogStorage(s.db, s.mf) } func (s *mysqlProvider) AdminStorage() storage.AdminStorage { return NewAdminStorage(s.db) } func (s *mysqlProvider) Close() error { return s.db.Close() } // registerMySQLTLSConfig registers a custom TLS config for MySQL using a provided CA certificate and optional server name. // Returns an error if the CA certificate can't be read or added to the root cert pool, or when the registration of the TLS config fails. func registerMySQLTLSConfig() error { if *mySQLTLSCA == "" { return nil } rootCertPool := x509.NewCertPool() pem, err := os.ReadFile(*mySQLTLSCA) if err != nil { return err } if ok := rootCertPool.AppendCertsFromPEM(pem); !ok { return errors.New("failed to append PEM") } tlsConfig := &tls.Config{ RootCAs: rootCertPool, } if *mySQLServerName != "" { tlsConfig.ServerName = *mySQLServerName } return mysql.RegisterTLSConfig("custom", tlsConfig) } trillian-1.6.1/storage/mysql/provider_test.go000066400000000000000000000026711466362047600214120ustar00rootroot00000000000000// Copyright 2018 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 mysql import ( "flag" "testing" "github.com/google/trillian/storage" "github.com/google/trillian/testonly/flagsaver" ) func TestMySQLStorageProviderErrorPersistence(t *testing.T) { defer flagsaver.Save().MustRestore() if err := flag.Set("mysql_uri", "&bogus*:::?"); err != nil { t.Errorf("Failed to set flag: %v", err) } // First call: This should fail due to the Database URL being garbage. _, err1 := storage.NewProvider("mysql", nil) if err1 == nil { t.Fatalf("Expected 'storage.NewProvider' to fail") } // Second call: This should fail with the same error. _, err2 := storage.NewProvider("mysql", nil) if err2 == nil { t.Fatalf("Expected second call to 'storage.NewProvider' to fail") } if err2 != err1 { t.Fatalf("Expected second call to 'storage.NewProvider' to fail with %q, instead got: %q", err1, err2) } } trillian-1.6.1/storage/mysql/queue.go000066400000000000000000000115101466362047600176350ustar00rootroot00000000000000//go:build !batched_queue // +build !batched_queue // 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 mysql import ( "context" "database/sql" "errors" "fmt" "time" "github.com/google/trillian" "google.golang.org/protobuf/types/known/timestamppb" "k8s.io/klog/v2" ) const ( // If this statement ORDER BY clause is changed refer to the comment in removeSequencedLeaves selectQueuedLeavesSQL = `SELECT LeafIdentityHash,MerkleLeafHash,QueueTimestampNanos FROM Unsequenced WHERE TreeID=? AND Bucket=0 AND QueueTimestampNanos<=? ORDER BY QueueTimestampNanos,LeafIdentityHash ASC LIMIT ?` insertUnsequencedEntrySQL = `INSERT INTO Unsequenced(TreeId,Bucket,LeafIdentityHash,MerkleLeafHash,QueueTimestampNanos) VALUES(?,0,?,?,?)` deleteUnsequencedSQL = "DELETE FROM Unsequenced WHERE TreeId=? AND Bucket=0 AND QueueTimestampNanos=? AND LeafIdentityHash=?" ) type dequeuedLeaf struct { queueTimestampNanos int64 leafIdentityHash []byte } func dequeueInfo(leafIDHash []byte, queueTimestamp int64) dequeuedLeaf { return dequeuedLeaf{queueTimestampNanos: queueTimestamp, leafIdentityHash: leafIDHash} } func (t *logTreeTX) dequeueLeaf(rows *sql.Rows) (*trillian.LogLeaf, dequeuedLeaf, error) { var leafIDHash []byte var merkleHash []byte var queueTimestamp int64 err := rows.Scan(&leafIDHash, &merkleHash, &queueTimestamp) if err != nil { klog.Warningf("Error scanning work rows: %s", err) return nil, dequeuedLeaf{}, err } // Note: the LeafData and ExtraData being nil here is OK as this is only used by the // sequencer. The sequencer only writes to the SequencedLeafData table and the client // supplied data was already written to LeafData as part of queueing the leaf. queueTimestampProto := timestamppb.New(time.Unix(0, queueTimestamp)) if err := queueTimestampProto.CheckValid(); err != nil { return nil, dequeuedLeaf{}, fmt.Errorf("got invalid queue timestamp: %w", err) } leaf := &trillian.LogLeaf{ LeafIdentityHash: leafIDHash, MerkleLeafHash: merkleHash, QueueTimestamp: queueTimestampProto, } return leaf, dequeueInfo(leafIDHash, queueTimestamp), nil } func queueArgs(_ int64, _ []byte, queueTimestamp time.Time) []interface{} { return []interface{}{queueTimestamp.UnixNano()} } func (t *logTreeTX) UpdateSequencedLeaves(ctx context.Context, leaves []*trillian.LogLeaf) error { dequeuedLeaves := make([]dequeuedLeaf, 0, len(leaves)) for _, leaf := range leaves { // This should fail on insert but catch it early if len(leaf.LeafIdentityHash) != t.hashSizeBytes { return errors.New("sequenced leaf has incorrect hash size") } if err := leaf.IntegrateTimestamp.CheckValid(); err != nil { return fmt.Errorf("got invalid integrate timestamp: %w", err) } iTimestamp := leaf.IntegrateTimestamp.AsTime() _, err := t.tx.ExecContext( ctx, insertSequencedLeafSQL+valuesPlaceholder5, t.treeID, leaf.LeafIdentityHash, leaf.MerkleLeafHash, leaf.LeafIndex, iTimestamp.UnixNano()) if err != nil { klog.Warningf("Failed to update sequenced leaves: %s", err) return err } qe, ok := t.dequeued[string(leaf.LeafIdentityHash)] if !ok { return fmt.Errorf("attempting to update leaf that wasn't dequeued. IdentityHash: %x", leaf.LeafIdentityHash) } dequeuedLeaves = append(dequeuedLeaves, qe) } return t.removeSequencedLeaves(ctx, dequeuedLeaves) } // removeSequencedLeaves removes the passed in leaves slice (which may be // modified as part of the operation). func (t *logTreeTX) removeSequencedLeaves(ctx context.Context, leaves []dequeuedLeaf) error { start := time.Now() // Don't need to re-sort because the query ordered by leaf hash. If that changes because // the query is expensive then the sort will need to be done here. See comment in // QueueLeaves. stx, err := t.tx.PrepareContext(ctx, deleteUnsequencedSQL) if err != nil { klog.Warningf("Failed to prep delete statement for sequenced work: %v", err) return err } defer func() { if err := stx.Close(); err != nil { klog.Errorf("stx.Close(): %v", err) } }() for _, dql := range leaves { result, err := stx.ExecContext(ctx, t.treeID, dql.queueTimestampNanos, dql.leafIdentityHash) err = checkResultOkAndRowCountIs(result, err, int64(1)) if err != nil { return err } } observe(dequeueRemoveLatency, time.Since(start), labelForTX(t)) return nil } trillian-1.6.1/storage/mysql/queue_batching.go000066400000000000000000000124071466362047600215020ustar00rootroot00000000000000//go:build batched_queue // +build batched_queue // 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 mysql import ( "context" "crypto/sha256" "database/sql" "encoding/binary" "fmt" "strings" "time" "github.com/google/trillian" "google.golang.org/protobuf/types/known/timestamppb" "k8s.io/klog/v2" ) const ( // If this statement ORDER BY clause is changed refer to the comment in removeSequencedLeaves selectQueuedLeavesSQL = `SELECT LeafIdentityHash,MerkleLeafHash,QueueTimestampNanos,QueueID FROM Unsequenced WHERE TreeID=? AND Bucket=0 AND QueueTimestampNanos<=? ORDER BY QueueTimestampNanos,LeafIdentityHash ASC LIMIT ?` insertUnsequencedEntrySQL = `INSERT INTO Unsequenced(TreeId,Bucket,LeafIdentityHash,MerkleLeafHash,QueueTimestampNanos,QueueID) VALUES(?,0,?,?,?,?)` deleteUnsequencedSQL = "DELETE FROM Unsequenced WHERE QueueID IN ()" ) type dequeuedLeaf []byte func dequeueInfo(_ []byte, queueID []byte) dequeuedLeaf { return dequeuedLeaf(queueID) } func (t *logTreeTX) dequeueLeaf(rows *sql.Rows) (*trillian.LogLeaf, dequeuedLeaf, error) { var leafIDHash []byte var merkleHash []byte var queueTimestamp int64 var queueID []byte err := rows.Scan(&leafIDHash, &merkleHash, &queueTimestamp, &queueID) if err != nil { klog.Warningf("Error scanning work rows: %s", err) return nil, nil, err } queueTimestampProto := timestamppb.New(time.Unix(0, queueTimestamp)) if err := queueTimestampProto.CheckValid(); err != nil { return nil, dequeuedLeaf{}, fmt.Errorf("got invalid queue timestamp: %w", err) } // Note: the LeafData and ExtraData being nil here is OK as this is only used by the // sequencer. The sequencer only writes to the SequencedLeafData table and the client // supplied data was already written to LeafData as part of queueing the leaf. leaf := &trillian.LogLeaf{ LeafIdentityHash: leafIDHash, MerkleLeafHash: merkleHash, QueueTimestamp: queueTimestampProto, } return leaf, dequeueInfo(leafIDHash, queueID), nil } func generateQueueID(treeID int64, leafIdentityHash []byte, timestamp int64) []byte { h := sha256.New() b := make([]byte, 10) binary.PutVarint(b, treeID) h.Write(b) b = make([]byte, 10) binary.PutVarint(b, timestamp) h.Write(b) h.Write(leafIdentityHash) return h.Sum(nil) } func queueArgs(treeID int64, identityHash []byte, queueTimestamp time.Time) []interface{} { timestamp := queueTimestamp.UnixNano() return []interface{}{timestamp, generateQueueID(treeID, identityHash, timestamp)} } func (t *logTreeTX) UpdateSequencedLeaves(ctx context.Context, leaves []*trillian.LogLeaf) error { querySuffix := []string{} args := []interface{}{} dequeuedLeaves := make([]dequeuedLeaf, 0, len(leaves)) for _, leaf := range leaves { if err := leaf.IntegrateTimestamp.CheckValid(); err != nil { return fmt.Errorf("got invalid integrate timestamp: %w", err) } iTimestamp := leaf.IntegrateTimestamp.AsTime() querySuffix = append(querySuffix, valuesPlaceholder5) args = append(args, t.treeID, leaf.LeafIdentityHash, leaf.MerkleLeafHash, leaf.LeafIndex, iTimestamp.UnixNano()) qe, ok := t.dequeued[string(leaf.LeafIdentityHash)] if !ok { return fmt.Errorf("attempting to update leaf that wasn't dequeued. IdentityHash: %x", leaf.LeafIdentityHash) } dequeuedLeaves = append(dequeuedLeaves, qe) } result, err := t.tx.ExecContext(ctx, insertSequencedLeafSQL+strings.Join(querySuffix, ","), args...) if err != nil { klog.Warningf("Failed to update sequenced leaves: %s", err) } if err := checkResultOkAndRowCountIs(result, err, int64(len(leaves))); err != nil { return err } return t.removeSequencedLeaves(ctx, dequeuedLeaves) } func (m *mySQLLogStorage) getDeleteUnsequencedStmt(ctx context.Context, num int) (*sql.Stmt, error) { return m.getStmt(ctx, deleteUnsequencedSQL, num, "?", "?") } // removeSequencedLeaves removes the passed in leaves slice (which may be // modified as part of the operation). func (t *logTreeTX) removeSequencedLeaves(ctx context.Context, queueIDs []dequeuedLeaf) error { // Don't need to re-sort because the query ordered by leaf hash. If that changes because // the query is expensive then the sort will need to be done here. See comment in // QueueLeaves. tmpl, err := t.ls.getDeleteUnsequencedStmt(ctx, len(queueIDs)) if err != nil { klog.Warningf("Failed to get delete statement for sequenced work: %s", err) return err } stx := t.tx.StmtContext(ctx, tmpl) args := make([]interface{}, len(queueIDs)) for i, q := range queueIDs { args[i] = []byte(q) } result, err := stx.ExecContext(ctx, args...) if err != nil { // Error is handled by checkResultOkAndRowCountIs() below klog.Warningf("Failed to delete sequenced work: %s", err) } return checkResultOkAndRowCountIs(result, err, int64(len(queueIDs))) } trillian-1.6.1/storage/mysql/schema/000077500000000000000000000000001466362047600174245ustar00rootroot00000000000000trillian-1.6.1/storage/mysql/schema/storage.sql000066400000000000000000000145151466362047600216170ustar00rootroot00000000000000# MySQL / MariaDB version of the tree schema -- --------------------------------------------- -- Tree stuff here -- --------------------------------------------- -- Tree parameters should not be changed after creation. Doing so can -- render the data in the tree unusable or inconsistent. CREATE TABLE IF NOT EXISTS Trees( TreeId BIGINT NOT NULL, TreeState ENUM('ACTIVE', 'FROZEN', 'DRAINING') NOT NULL, TreeType ENUM('LOG', 'MAP', 'PREORDERED_LOG') NOT NULL, HashStrategy ENUM('RFC6962_SHA256', 'TEST_MAP_HASHER', 'OBJECT_RFC6962_SHA256', 'CONIKS_SHA512_256', 'CONIKS_SHA256') NOT NULL, HashAlgorithm ENUM('SHA256') NOT NULL, SignatureAlgorithm ENUM('ECDSA', 'RSA', 'ED25519') NOT NULL, DisplayName VARCHAR(20), Description VARCHAR(200), CreateTimeMillis BIGINT NOT NULL, UpdateTimeMillis BIGINT NOT NULL, MaxRootDurationMillis BIGINT NOT NULL, PrivateKey MEDIUMBLOB NOT NULL, -- Unused. PublicKey MEDIUMBLOB NOT NULL, -- This is now used to store settings. Deleted BOOLEAN, DeleteTimeMillis BIGINT, PRIMARY KEY(TreeId) ); -- This table contains tree parameters that can be changed at runtime such as for -- administrative purposes. CREATE TABLE IF NOT EXISTS TreeControl( TreeId BIGINT NOT NULL, SigningEnabled BOOLEAN NOT NULL, SequencingEnabled BOOLEAN NOT NULL, SequenceIntervalSeconds INTEGER NOT NULL, PRIMARY KEY(TreeId), FOREIGN KEY(TreeId) REFERENCES Trees(TreeId) ON DELETE CASCADE ); CREATE TABLE IF NOT EXISTS Subtree( TreeId BIGINT NOT NULL, SubtreeId VARBINARY(255) NOT NULL, Nodes MEDIUMBLOB NOT NULL, SubtreeRevision INTEGER NOT NULL, -- Key columns must be in ASC order in order to benefit from group-by/min-max -- optimization in MySQL. PRIMARY KEY(TreeId, SubtreeId, SubtreeRevision), FOREIGN KEY(TreeId) REFERENCES Trees(TreeId) ON DELETE CASCADE ); -- The TreeRevisionIdx is used to enforce that there is only one STH at any -- tree revision CREATE TABLE IF NOT EXISTS TreeHead( TreeId BIGINT NOT NULL, TreeHeadTimestamp BIGINT, TreeSize BIGINT, RootHash VARBINARY(255) NOT NULL, RootSignature VARBINARY(1024) NOT NULL, TreeRevision BIGINT, PRIMARY KEY(TreeId, TreeHeadTimestamp), FOREIGN KEY(TreeId) REFERENCES Trees(TreeId) ON DELETE CASCADE ); CREATE UNIQUE INDEX TreeHeadRevisionIdx ON TreeHead(TreeId, TreeRevision); -- --------------------------------------------- -- Log specific stuff here -- --------------------------------------------- -- Creating index at same time as table allows some storage engines to better -- optimize physical storage layout. Most engines allow multiple nulls in a -- unique index but some may not. -- A leaf that has not been sequenced has a row in this table. If duplicate leaves -- are allowed they will all reference this row. CREATE TABLE IF NOT EXISTS LeafData( TreeId BIGINT NOT NULL, -- This is a personality specific has of some subset of the leaf data. -- It's only purpose is to allow Trillian to identify duplicate entries in -- the context of the personality. LeafIdentityHash VARBINARY(255) NOT NULL, -- This is the data stored in the leaf for example in CT it contains a DER encoded -- X.509 certificate but is application dependent LeafValue LONGBLOB NOT NULL, -- This is extra data that the application can associate with the leaf should it wish to. -- This data is not included in signing and hashing. ExtraData LONGBLOB, -- The timestamp from when this leaf data was first queued for inclusion. QueueTimestampNanos BIGINT NOT NULL, PRIMARY KEY(TreeId, LeafIdentityHash), FOREIGN KEY(TreeId) REFERENCES Trees(TreeId) ON DELETE CASCADE ); -- When a leaf is sequenced a row is added to this table. If logs allow duplicates then -- multiple rows will exist with different sequence numbers. The signed timestamp -- will be communicated via the unsequenced table as this might need to be unique, depending -- on the log parameters and we can't insert into this table until we have the sequence number -- which is not available at the time we queue the entry. We need both hashes because the -- LeafData table is keyed by the raw data hash. CREATE TABLE IF NOT EXISTS SequencedLeafData( TreeId BIGINT NOT NULL, SequenceNumber BIGINT UNSIGNED NOT NULL, -- This is a personality specific has of some subset of the leaf data. -- It's only purpose is to allow Trillian to identify duplicate entries in -- the context of the personality. LeafIdentityHash VARBINARY(255) NOT NULL, -- This is a MerkleLeafHash as defined by the treehasher that the log uses. For example for -- CT this hash will include the leaf prefix byte as well as the leaf data. MerkleLeafHash VARBINARY(255) NOT NULL, IntegrateTimestampNanos BIGINT NOT NULL, PRIMARY KEY(TreeId, SequenceNumber), FOREIGN KEY(TreeId) REFERENCES Trees(TreeId) ON DELETE CASCADE, FOREIGN KEY(TreeId, LeafIdentityHash) REFERENCES LeafData(TreeId, LeafIdentityHash) ON DELETE CASCADE ); CREATE INDEX SequencedLeafMerkleIdx ON SequencedLeafData(TreeId, MerkleLeafHash); CREATE TABLE IF NOT EXISTS Unsequenced( TreeId BIGINT NOT NULL, -- The bucket field is to allow the use of time based ring bucketed schemes if desired. If -- unused this should be set to zero for all entries. Bucket INTEGER NOT NULL, -- This is a personality specific hash of some subset of the leaf data. -- It's only purpose is to allow Trillian to identify duplicate entries in -- the context of the personality. LeafIdentityHash VARBINARY(255) NOT NULL, -- This is a MerkleLeafHash as defined by the treehasher that the log uses. For example for -- CT this hash will include the leaf prefix byte as well as the leaf data. MerkleLeafHash VARBINARY(255) NOT NULL, QueueTimestampNanos BIGINT NOT NULL, -- This is a SHA256 hash of the TreeID, LeafIdentityHash and QueueTimestampNanos. It is used -- for batched deletes from the table when trillian_log_server and trillian_log_signer are -- built with the batched_queue tag. QueueID VARBINARY(32) DEFAULT NULL UNIQUE, PRIMARY KEY (TreeId, Bucket, QueueTimestampNanos, LeafIdentityHash) ); trillian-1.6.1/storage/mysql/sql.go000066400000000000000000000114201466362047600173100ustar00rootroot00000000000000// Copyright 2018 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 mysql import ( "bytes" "database/sql" "encoding/gob" "fmt" "time" "github.com/google/trillian" "github.com/google/trillian/storage/mysql/mysqlpb" "google.golang.org/protobuf/types/known/anypb" "google.golang.org/protobuf/types/known/durationpb" "google.golang.org/protobuf/types/known/timestamppb" ) // toMillisSinceEpoch converts a timestamp into milliseconds since epoch func toMillisSinceEpoch(t time.Time) int64 { return t.UnixNano() / 1000000 } // fromMillisSinceEpoch converts func fromMillisSinceEpoch(ts int64) time.Time { return time.Unix(0, ts*1000000) } // setNullStringIfValid assigns src to dest if src is Valid. func setNullStringIfValid(src sql.NullString, dest *string) { if src.Valid { *dest = src.String } } // row defines a common interface between sql.Row and sql.Rows(!) type row interface { Scan(dest ...interface{}) error } // readTree takes a sql row and returns a tree func readTree(r row) (*trillian.Tree, error) { tree := &trillian.Tree{} // Enums and Datetimes need an extra conversion step var treeState, treeType, hashStrategy, hashAlgorithm, signatureAlgorithm string var createMillis, updateMillis, maxRootDurationMillis int64 var displayName, description sql.NullString var privateKey, publicKey []byte var deleted sql.NullBool var deleteMillis sql.NullInt64 err := r.Scan( &tree.TreeId, &treeState, &treeType, &hashStrategy, &hashAlgorithm, &signatureAlgorithm, &displayName, &description, &createMillis, &updateMillis, &privateKey, &publicKey, &maxRootDurationMillis, &deleted, &deleteMillis, ) if err != nil { return nil, err } setNullStringIfValid(displayName, &tree.DisplayName) setNullStringIfValid(description, &tree.Description) // Convert all things! if ts, ok := trillian.TreeState_value[treeState]; ok { tree.TreeState = trillian.TreeState(ts) } else { return nil, fmt.Errorf("unknown TreeState: %v", treeState) } if tt, ok := trillian.TreeType_value[treeType]; ok { tree.TreeType = trillian.TreeType(tt) } else { return nil, fmt.Errorf("unknown TreeType: %v", treeType) } if hashStrategy != "RFC6962_SHA256" { return nil, fmt.Errorf("unknown HashStrategy: %v", hashStrategy) } // Let's make sure we didn't mismatch any of the casts above ok := tree.TreeState.String() == treeState && tree.TreeType.String() == treeType if !ok { return nil, fmt.Errorf( "mismatched enum: tree = %v, enums = [%v, %v, %v, %v, %v]", tree, treeState, treeType, hashStrategy, hashAlgorithm, signatureAlgorithm) } tree.CreateTime = timestamppb.New(fromMillisSinceEpoch(createMillis)) if err := tree.CreateTime.CheckValid(); err != nil { return nil, fmt.Errorf("failed to parse create time: %w", err) } tree.UpdateTime = timestamppb.New(fromMillisSinceEpoch(updateMillis)) if err := tree.UpdateTime.CheckValid(); err != nil { return nil, fmt.Errorf("failed to parse update time: %w", err) } tree.MaxRootDuration = durationpb.New(time.Duration(maxRootDurationMillis * int64(time.Millisecond))) tree.Deleted = deleted.Valid && deleted.Bool if tree.Deleted && deleteMillis.Valid { tree.DeleteTime = timestamppb.New(fromMillisSinceEpoch(deleteMillis.Int64)) if err := tree.DeleteTime.CheckValid(); err != nil { return nil, fmt.Errorf("failed to parse delete time: %w", err) } } // We're going to try to interpret PublicKey as storageSettings, but it could be a // public key from a really old tree, or an empty column from a tree created in the // period between Trillian key material being removed and this column being used for // storing settings. buff := bytes.NewBuffer(publicKey) dec := gob.NewDecoder(buff) ss := &storageSettings{} var o *mysqlpb.StorageOptions if err := dec.Decode(ss); err != nil { // If there are no storageSettings then this tree was created before settings // were supported, and thus we have to populate the settings with the oldest // settings for features. o = &mysqlpb.StorageOptions{ SubtreeRevisions: true, } } else { o = &mysqlpb.StorageOptions{ SubtreeRevisions: ss.Revisioned, } } tree.StorageSettings, err = anypb.New(o) if err != nil { return nil, fmt.Errorf("failed to put StorageSettings into tree: %w", err) } return tree, nil } trillian-1.6.1/storage/mysql/storage_test.go000066400000000000000000000250201466362047600212150ustar00rootroot00000000000000// 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 mysql import ( "bytes" "context" "crypto" "crypto/sha256" "database/sql" "errors" "flag" "fmt" "os" "testing" "time" "github.com/google/trillian" "github.com/google/trillian/storage" "github.com/google/trillian/storage/mysql/mysqlpb" "github.com/google/trillian/storage/testdb" storageto "github.com/google/trillian/storage/testonly" stree "github.com/google/trillian/storage/tree" "github.com/google/trillian/types" "github.com/transparency-dev/merkle/compact" "github.com/transparency-dev/merkle/rfc6962" "google.golang.org/protobuf/types/known/anypb" "google.golang.org/protobuf/types/known/durationpb" "k8s.io/klog/v2" ) var ( // LogTree is a valid, LOG-type trillian.Tree for tests. // This tree is configured to write revisions for each subtree. // This matches the legacy behaviour before revisions were removed. RevisionedLogTree = &trillian.Tree{ TreeState: trillian.TreeState_ACTIVE, TreeType: trillian.TreeType_LOG, DisplayName: "Llamas Log", Description: "Registry of publicly-owned llamas", MaxRootDuration: durationpb.New(0 * time.Millisecond), StorageSettings: mustCreateRevisionedStorage(), } ) func mustCreateRevisionedStorage() *anypb.Any { o := &mysqlpb.StorageOptions{ SubtreeRevisions: true, } a, err := anypb.New(o) if err != nil { panic(err) } return a } func TestNodeRoundTrip(t *testing.T) { nodes := createSomeNodes(256) nodeIDs := make([]compact.NodeID, len(nodes)) for i := range nodes { nodeIDs[i] = nodes[i].ID } for _, tc := range []struct { desc string store []stree.Node read []compact.NodeID want []stree.Node wantErr bool }{ {desc: "store-4-read-4", store: nodes[:4], read: nodeIDs[:4], want: nodes[:4]}, {desc: "store-4-read-1", store: nodes[:4], read: nodeIDs[:1], want: nodes[:1]}, {desc: "store-2-read-4", store: nodes[:2], read: nodeIDs[:4], want: nodes[:2]}, {desc: "store-none-read-all", store: nil, read: nodeIDs, wantErr: true}, {desc: "store-all-read-all", store: nodes, read: nodeIDs, want: nodes}, {desc: "store-all-read-none", store: nodes, read: nil, want: nil}, } { testbody := func(treeDef *trillian.Tree) { ctx := context.Background() cleanTestDB(DB) as := NewAdminStorage(DB) tree := mustCreateTree(ctx, t, as, treeDef) s := NewLogStorage(DB, nil) const writeRev = int64(100) runLogTX(s, tree, t, func(ctx context.Context, tx storage.LogTreeTX) error { forceWriteRevision(writeRev, tx) if err := tx.SetMerkleNodes(ctx, tc.store); err != nil { t.Fatalf("Failed to store nodes: %s", err) } return storeLogRoot(ctx, tx, uint64(len(tc.store)), uint64(writeRev), []byte{1, 2, 3}) }) runLogTX(s, tree, t, func(ctx context.Context, tx storage.LogTreeTX) error { readNodes, err := tx.GetMerkleNodes(ctx, tc.read) if err != nil && !tc.wantErr { t.Fatalf("Failed to retrieve nodes: %s", err) } else if err == nil && tc.wantErr { t.Fatal("Retrieving nodes succeeded unexpectedly") } if err := nodesAreEqual(readNodes, tc.want); err != nil { t.Fatalf("Read back different nodes from the ones stored: %s", err) } return nil }) } t.Run(tc.desc+"-norevisions", func(t *testing.T) { testbody(storageto.LogTree) }) t.Run(tc.desc+"-revisions", func(t *testing.T) { testbody(RevisionedLogTree) }) } } // This test ensures that node writes cross subtree boundaries so this edge case in the subtree // cache gets exercised. Any tree size > 256 will do this. func TestLogNodeRoundTripMultiSubtree(t *testing.T) { testCases := []struct { desc string tree *trillian.Tree }{ { desc: "Revisionless", tree: storageto.LogTree, }, { desc: "Revisions", tree: RevisionedLogTree, }, } for _, tC := range testCases { t.Run(tC.desc, func(t *testing.T) { ctx := context.Background() cleanTestDB(DB) as := NewAdminStorage(DB) tree := mustCreateTree(ctx, t, as, tC.tree) s := NewLogStorage(DB, nil) const writeRev = int64(100) const size = 871 nodesToStore, err := createLogNodesForTreeAtSize(t, size, writeRev) if err != nil { t.Fatalf("failed to create test tree: %v", err) } nodeIDsToRead := make([]compact.NodeID, len(nodesToStore)) for i := range nodesToStore { nodeIDsToRead[i] = nodesToStore[i].ID } { runLogTX(s, tree, t, func(ctx context.Context, tx storage.LogTreeTX) error { forceWriteRevision(writeRev, tx) if err := tx.SetMerkleNodes(ctx, nodesToStore); err != nil { t.Fatalf("Failed to store nodes: %s", err) } return storeLogRoot(ctx, tx, uint64(size), uint64(writeRev), []byte{1, 2, 3}) }) } { runLogTX(s, tree, t, func(ctx context.Context, tx storage.LogTreeTX) error { readNodes, err := tx.GetMerkleNodes(ctx, nodeIDsToRead) if err != nil { t.Fatalf("Failed to retrieve nodes: %s", err) } if err := nodesAreEqual(readNodes, nodesToStore); err != nil { missing, extra := diffNodes(readNodes, nodesToStore) for _, n := range missing { t.Errorf("Missing: %v", n.ID) } for _, n := range extra { t.Errorf("Extra : %v", n.ID) } t.Fatalf("Read back different nodes from the ones stored: %s", err) } return nil }) } }) } } func forceWriteRevision(rev int64, tx storage.LogTreeTX) { mtx, ok := tx.(*logTreeTX) if !ok { panic(nil) } mtx.treeTX.writeRevision = rev } func createSomeNodes(count int) []stree.Node { r := make([]stree.Node, count) for i := range r { r[i].ID = compact.NewNodeID(0, uint64(i)) h := sha256.Sum256([]byte{byte(i)}) r[i].Hash = h[:] klog.V(3).Infof("Node to store: %v", r[i].ID) } return r } func createLogNodesForTreeAtSize(t *testing.T, ts, rev int64) ([]stree.Node, error) { hasher := rfc6962.New(crypto.SHA256) fact := compact.RangeFactory{Hash: hasher.HashChildren} cr := fact.NewEmptyRange(0) nodeMap := make(map[compact.NodeID][]byte) store := func(id compact.NodeID, hash []byte) { nodeMap[id] = hash } for l := 0; l < int(ts); l++ { hash := hasher.HashLeaf([]byte(fmt.Sprintf("Leaf %d", l))) // Store the new leaf node, and all new perfect nodes. if err := cr.Append(hash, store); err != nil { return nil, err } } // Unroll the map, which has deduped the updates for us and retained the latest nodes := make([]stree.Node, 0, len(nodeMap)) for id, hash := range nodeMap { nodes = append(nodes, stree.Node{ID: id, Hash: hash}) } return nodes, nil } // TODO(pavelkalinnikov): Allow nodes to be out of order. func nodesAreEqual(lhs, rhs []stree.Node) error { if ls, rs := len(lhs), len(rhs); ls != rs { return fmt.Errorf("different number of nodes, %d vs %d", ls, rs) } for i := range lhs { if l, r := lhs[i].ID, rhs[i].ID; l != r { return fmt.Errorf("NodeIDs are not the same,\nlhs = %v,\nrhs = %v", l, r) } if l, r := lhs[i].Hash, rhs[i].Hash; !bytes.Equal(l, r) { return fmt.Errorf("Hashes are not the same for %v,\nlhs = %v,\nrhs = %v", lhs[i].ID, l, r) } } return nil } func diffNodes(got, want []stree.Node) ([]stree.Node, []stree.Node) { var missing []stree.Node gotMap := make(map[compact.NodeID]stree.Node) for _, n := range got { gotMap[n.ID] = n } for _, n := range want { _, ok := gotMap[n.ID] if !ok { missing = append(missing, n) } delete(gotMap, n.ID) } // Unpack the extra nodes to return both as slices extra := make([]stree.Node, 0, len(gotMap)) for _, v := range gotMap { extra = append(extra, v) } return missing, extra } func openTestDBOrDie() (*sql.DB, func(context.Context)) { db, done, err := testdb.NewTrillianDB(context.TODO(), testdb.DriverMySQL) if err != nil { panic(err) } return db, done } // cleanTestDB deletes all the entries in the database. func cleanTestDB(db *sql.DB) { for _, table := range allTables { if _, err := db.ExecContext(context.TODO(), fmt.Sprintf("DELETE FROM %s", table)); err != nil { panic(fmt.Sprintf("Failed to delete rows in %s: %v", table, err)) } } } func getVersion(db *sql.DB) (string, error) { rows, err := db.QueryContext(context.TODO(), "SELECT @@GLOBAL.version") if err != nil { return "", fmt.Errorf("getVersion: failed to perform query: %v", err) } defer func() { _ = rows.Close() }() if !rows.Next() { return "", errors.New("getVersion: cursor has no rows") } var v string if err := rows.Scan(&v); err != nil { return "", err } if rows.Next() { return "", errors.New("getVersion: too many rows returned") } return v, nil } func mustSignAndStoreLogRoot(ctx context.Context, t *testing.T, l storage.LogStorage, tree *trillian.Tree, treeSize uint64) { t.Helper() if err := l.ReadWriteTransaction(ctx, tree, func(ctx context.Context, tx storage.LogTreeTX) error { return storeLogRoot(ctx, tx, treeSize, 0, []byte{0}) }); err != nil { t.Fatalf("ReadWriteTransaction: %v", err) } } func storeLogRoot(ctx context.Context, tx storage.LogTreeTX, size, rev uint64, hash []byte) error { logRoot, err := (&types.LogRootV1{TreeSize: size, RootHash: hash}).MarshalBinary() if err != nil { return fmt.Errorf("error marshaling new LogRoot: %v", err) } root := &trillian.SignedLogRoot{LogRoot: logRoot} if err := tx.StoreSignedLogRoot(ctx, root); err != nil { return fmt.Errorf("error storing new SignedLogRoot: %v", err) } return nil } // mustCreateTree creates the specified tree using AdminStorage. func mustCreateTree(ctx context.Context, t *testing.T, s storage.AdminStorage, tree *trillian.Tree) *trillian.Tree { t.Helper() tree, err := storage.CreateTree(ctx, s, tree) if err != nil { t.Fatalf("storage.CreateTree(): %v", err) } return tree } // DB is the database used for tests. It's initialized and closed by TestMain(). var DB *sql.DB func TestMain(m *testing.M) { flag.Parse() if !testdb.MySQLAvailable() { klog.Errorf("MySQL not available, skipping all MySQL storage tests") return } var done func(context.Context) DB, done = openTestDBOrDie() if v, err := getVersion(DB); err == nil { klog.Infof("MySQL version '%v'", v) } status := m.Run() done(context.Background()) os.Exit(status) } trillian-1.6.1/storage/mysql/tree_storage.go000066400000000000000000000304611466362047600212020ustar00rootroot00000000000000// 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 mysql provides a MySQL-based storage layer implementation. package mysql import ( "context" "database/sql" "encoding/base64" "fmt" "runtime/debug" "strings" "sync" "github.com/google/trillian" "github.com/google/trillian/storage/cache" "github.com/google/trillian/storage/mysql/mysqlpb" "github.com/google/trillian/storage/storagepb" "github.com/google/trillian/storage/tree" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/anypb" "k8s.io/klog/v2" ) // These statements are fixed const ( insertSubtreeMultiSQL = `INSERT INTO Subtree(TreeId, SubtreeId, Nodes, SubtreeRevision) ` + placeholderSQL + ` ON DUPLICATE KEY UPDATE Nodes=VALUES(Nodes)` insertTreeHeadSQL = `INSERT INTO TreeHead(TreeId,TreeHeadTimestamp,TreeSize,RootHash,TreeRevision,RootSignature) VALUES(?,?,?,?,?,?)` selectSubtreeSQL = ` SELECT x.SubtreeId, Subtree.Nodes FROM ( SELECT n.TreeId, n.SubtreeId, max(n.SubtreeRevision) AS MaxRevision FROM Subtree n WHERE n.SubtreeId IN (` + placeholderSQL + `) AND n.TreeId = ? AND n.SubtreeRevision <= ? GROUP BY n.TreeId, n.SubtreeId ) AS x INNER JOIN Subtree ON Subtree.SubtreeId = x.SubtreeId AND Subtree.SubtreeRevision = x.MaxRevision AND Subtree.TreeId = x.TreeId AND Subtree.TreeId = ?` selectSubtreeSQLNoRev = ` SELECT SubtreeId, Subtree.Nodes FROM Subtree WHERE Subtree.TreeId = ? AND SubtreeId IN (` + placeholderSQL + `)` placeholderSQL = "" ) // mySQLTreeStorage is shared between the mySQLLog- and (forthcoming) mySQLMap- // Storage implementations, and contains functionality which is common to both, type mySQLTreeStorage struct { db *sql.DB // Must hold the mutex before manipulating the statement map. Sharing a lock because // it only needs to be held while the statements are built, not while they execute and // this will be a short time. These maps are from the number of placeholder '?' // in the query to the statement that should be used. statementMutex sync.Mutex statements map[string]map[int]*sql.Stmt } // OpenDB opens a database connection for all MySQL-based storage implementations. func OpenDB(dbURL string) (*sql.DB, error) { db, err := sql.Open("mysql", dbURL) if err != nil { // Don't log uri as it could contain credentials klog.Warningf("Could not open MySQL database, check config: %s", err) return nil, err } if _, err := db.ExecContext(context.TODO(), "SET sql_mode = 'STRICT_ALL_TABLES'"); err != nil { klog.Warningf("Failed to set strict mode on mysql db: %s", err) return nil, err } return db, nil } func newTreeStorage(db *sql.DB) *mySQLTreeStorage { return &mySQLTreeStorage{ db: db, statements: make(map[string]map[int]*sql.Stmt), } } // expandPlaceholderSQL expands an sql statement by adding a specified number of '?' // placeholder slots. At most one placeholder will be expanded. func expandPlaceholderSQL(sql string, num int, first, rest string) string { if num <= 0 { panic(fmt.Errorf("trying to expand SQL placeholder with <= 0 parameters: %s", sql)) } parameters := first + strings.Repeat(","+rest, num-1) return strings.Replace(sql, placeholderSQL, parameters, 1) } // getStmt creates and caches sql.Stmt structs based on the passed in statement // and number of bound arguments. // TODO(al,martin): consider pulling this all out as a separate unit for reuse // elsewhere. func (m *mySQLTreeStorage) getStmt(ctx context.Context, statement string, num int, first, rest string) (*sql.Stmt, error) { m.statementMutex.Lock() defer m.statementMutex.Unlock() if m.statements[statement] != nil { if m.statements[statement][num] != nil { // TODO(al,martin): we'll possibly need to expire Stmts from the cache, // e.g. when DB connections break etc. return m.statements[statement][num], nil } } else { m.statements[statement] = make(map[int]*sql.Stmt) } s, err := m.db.PrepareContext(ctx, expandPlaceholderSQL(statement, num, first, rest)) if err != nil { klog.Warningf("Failed to prepare statement %d: %s", num, err) return nil, err } m.statements[statement][num] = s return s, nil } func (m *mySQLTreeStorage) getSubtreeStmt(ctx context.Context, subtreeRevs bool, num int) (*sql.Stmt, error) { if subtreeRevs { return m.getStmt(ctx, selectSubtreeSQL, num, "?", "?") } else { return m.getStmt(ctx, selectSubtreeSQLNoRev, num, "?", "?") } } func (m *mySQLTreeStorage) setSubtreeStmt(ctx context.Context, num int) (*sql.Stmt, error) { return m.getStmt(ctx, insertSubtreeMultiSQL, num, "VALUES(?, ?, ?, ?)", "(?, ?, ?, ?)") } func (m *mySQLTreeStorage) beginTreeTx(ctx context.Context, tree *trillian.Tree, hashSizeBytes int, subtreeCache *cache.SubtreeCache) (treeTX, error) { t, err := m.db.BeginTx(ctx, nil /* opts */) if err != nil { klog.Warningf("Could not start tree TX: %s", err) return treeTX{}, err } var subtreeRevisions bool o := &mysqlpb.StorageOptions{} if err := anypb.UnmarshalTo(tree.StorageSettings, o, proto.UnmarshalOptions{}); err != nil { return treeTX{}, fmt.Errorf("failed to unmarshal StorageSettings: %v", err) } subtreeRevisions = o.SubtreeRevisions return treeTX{ tx: t, mu: &sync.Mutex{}, ts: m, treeID: tree.TreeId, treeType: tree.TreeType, hashSizeBytes: hashSizeBytes, subtreeCache: subtreeCache, writeRevision: -1, subtreeRevs: subtreeRevisions, }, nil } type treeTX struct { // mu ensures that tx can only be used for one query/exec at a time. mu *sync.Mutex closed bool tx *sql.Tx ts *mySQLTreeStorage treeID int64 treeType trillian.TreeType hashSizeBytes int subtreeCache *cache.SubtreeCache writeRevision int64 subtreeRevs bool } func (t *treeTX) getSubtrees(ctx context.Context, treeRevision int64, ids [][]byte) ([]*storagepb.SubtreeProto, error) { klog.V(2).Infof("getSubtrees(len(ids)=%d)", len(ids)) klog.V(4).Infof("getSubtrees(") if len(ids) == 0 { return nil, nil } tmpl, err := t.ts.getSubtreeStmt(ctx, t.subtreeRevs, len(ids)) if err != nil { return nil, err } stx := t.tx.StmtContext(ctx, tmpl) defer func() { if err := stx.Close(); err != nil { klog.Errorf("stx.Close(): %v", err) } }() var args []interface{} if t.subtreeRevs { args = make([]interface{}, 0, len(ids)+3) // populate args with ids. for _, id := range ids { klog.V(4).Infof(" id: %x", id) args = append(args, id) } args = append(args, t.treeID) args = append(args, treeRevision) args = append(args, t.treeID) } else { args = make([]interface{}, 0, len(ids)+1) args = append(args, t.treeID) // populate args with ids. for _, id := range ids { klog.V(4).Infof(" id: %x", id) args = append(args, id) } } rows, err := stx.QueryContext(ctx, args...) if err != nil { klog.Warningf("Failed to get merkle subtrees: %s", err) return nil, err } defer func() { if err := rows.Close(); err != nil { klog.Errorf("rows.Close(): %v", err) } }() if rows.Err() != nil { // Nothing from the DB klog.Warningf("Nothing from DB: %s", rows.Err()) return nil, rows.Err() } ret := make([]*storagepb.SubtreeProto, 0, len(ids)) for rows.Next() { var subtreeIDBytes []byte var nodesRaw []byte if err := rows.Scan(&subtreeIDBytes, &nodesRaw); err != nil { klog.Warningf("Failed to scan merkle subtree: %s", err) return nil, err } var subtree storagepb.SubtreeProto if err := proto.Unmarshal(nodesRaw, &subtree); err != nil { klog.Warningf("Failed to unmarshal SubtreeProto: %s", err) return nil, err } if subtree.Prefix == nil { subtree.Prefix = []byte{} } ret = append(ret, &subtree) if klog.V(4).Enabled() { klog.Infof(" subtree: NID: %x, prefix: %x, depth: %d", subtreeIDBytes, subtree.Prefix, subtree.Depth) for k, v := range subtree.Leaves { b, err := base64.StdEncoding.DecodeString(k) if err != nil { klog.Errorf("base64.DecodeString(%v): %v", k, err) } klog.Infof(" %x: %x", b, v) } } } if err := rows.Err(); err != nil { return nil, err } // The InternalNodes cache is possibly nil here, but the SubtreeCache (which called // this method) will re-populate it. return ret, nil } func (t *treeTX) storeSubtrees(ctx context.Context, subtrees []*storagepb.SubtreeProto) error { klog.V(2).Infof("storeSubtrees(len(subtrees)=%d)", len(subtrees)) if klog.V(4).Enabled() { klog.Infof("storeSubtrees(") for _, s := range subtrees { klog.Infof(" prefix: %x, depth: %d", s.Prefix, s.Depth) for k, v := range s.Leaves { b, err := base64.StdEncoding.DecodeString(k) if err != nil { klog.Errorf("base64.DecodeString(%v): %v", k, err) } klog.Infof(" %x: %x", b, v) } } } if len(subtrees) == 0 { return nil } // TODO(al): probably need to be able to batch this in the case where we have // a really large number of subtrees to store. args := make([]interface{}, 0, len(subtrees)) // If not using subtree revisions then default value of 0 is fine. There is no // significance to this value, other than it cannot be NULL in the DB. var subtreeRev int64 if t.subtreeRevs { // We're using subtree revisions, so ensure we write at the correct revision subtreeRev = t.writeRevision } for _, s := range subtrees { s := s if s.Prefix == nil { panic(fmt.Errorf("nil prefix on %v", s)) } subtreeBytes, err := proto.Marshal(s) if err != nil { return err } args = append(args, t.treeID) args = append(args, s.Prefix) args = append(args, subtreeBytes) args = append(args, subtreeRev) } tmpl, err := t.ts.setSubtreeStmt(ctx, len(subtrees)) if err != nil { return err } stx := t.tx.StmtContext(ctx, tmpl) defer func() { if err := stx.Close(); err != nil { klog.Errorf("stx.Close(): %v", err) } }() r, err := stx.ExecContext(ctx, args...) if err != nil { klog.Warningf("Failed to set merkle subtrees: %s", err) return err } _, _ = r.RowsAffected() return nil } func checkResultOkAndRowCountIs(res sql.Result, err error, count int64) error { // The Exec() might have just failed if err != nil { return mysqlToGRPC(err) } // Otherwise we have to look at the result of the operation rowsAffected, rowsError := res.RowsAffected() if rowsError != nil { return mysqlToGRPC(rowsError) } if rowsAffected != count { return fmt.Errorf("expected %d row(s) to be affected but saw: %d", count, rowsAffected) } return nil } // getSubtreesAtRev returns a GetSubtreesFunc which reads at the passed in rev. func (t *treeTX) getSubtreesAtRev(ctx context.Context, rev int64) cache.GetSubtreesFunc { return func(ids [][]byte) ([]*storagepb.SubtreeProto, error) { return t.getSubtrees(ctx, rev, ids) } } func (t *treeTX) SetMerkleNodes(ctx context.Context, nodes []tree.Node) error { t.mu.Lock() defer t.mu.Unlock() rev := t.writeRevision - 1 return t.subtreeCache.SetNodes(nodes, t.getSubtreesAtRev(ctx, rev)) } func (t *treeTX) Commit(ctx context.Context) error { t.mu.Lock() defer t.mu.Unlock() if t.writeRevision > -1 { tiles, err := t.subtreeCache.UpdatedTiles() if err != nil { klog.Warningf("SubtreeCache updated tiles error: %v", err) return err } if err := t.storeSubtrees(ctx, tiles); err != nil { klog.Warningf("TX commit flush error: %v", err) return err } } t.closed = true if err := t.tx.Commit(); err != nil { klog.Warningf("TX commit error: %s, stack:\n%s", err, string(debug.Stack())) return err } return nil } func (t *treeTX) rollbackInternal() error { t.closed = true if err := t.tx.Rollback(); err != nil { klog.Warningf("TX rollback error: %s, stack:\n%s", err, string(debug.Stack())) return err } return nil } func (t *treeTX) Close() error { t.mu.Lock() defer t.mu.Unlock() if t.closed { return nil } err := t.rollbackInternal() if err != nil { klog.Warningf("Rollback error on Close(): %v", err) } return err } trillian-1.6.1/storage/provider.go000066400000000000000000000042001466362047600171740ustar00rootroot00000000000000// Copyright 2018 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 storage import ( "fmt" "sync" "github.com/google/trillian/monitoring" ) // NewProviderFunc is the signature of a function which can be registered to // provide instances of storage providers. type NewProviderFunc func(monitoring.MetricFactory) (Provider, error) var ( spMu sync.RWMutex spByName = make(map[string]NewProviderFunc) ) // RegisterProvider registers the given storage Provider. func RegisterProvider(name string, sp NewProviderFunc) error { spMu.Lock() defer spMu.Unlock() _, exists := spByName[name] if exists { return fmt.Errorf("storage provider %v already registered", name) } spByName[name] = sp return nil } // NewProvider returns a new Provider instance of the type specified by name. func NewProvider(name string, mf monitoring.MetricFactory) (Provider, error) { spMu.RLock() defer spMu.RUnlock() sp := spByName[name] if sp == nil { return nil, fmt.Errorf("no such storage provider %v", name) } return sp(mf) } // Providers returns a slice of all registered storage provider names. func Providers() []string { spMu.RLock() defer spMu.RUnlock() r := []string{} for k := range spByName { r = append(r, k) } return r } // Provider is an interface which allows Trillian binaries to use different // storage implementations. type Provider interface { // LogStorage creates and returns a LogStorage implementation. LogStorage() LogStorage // AdminStorage creates and returns a AdminStorage implementation. AdminStorage() AdminStorage // Close closes the underlying storage. Close() error } trillian-1.6.1/storage/provider_test.go000066400000000000000000000046601466362047600202450ustar00rootroot00000000000000// Copyright 2018 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 storage import ( "testing" "github.com/google/trillian/monitoring" ) type provider struct{} func (p *provider) LogStorage() LogStorage { return nil } func (p *provider) AdminStorage() AdminStorage { return nil } func (p *provider) Close() error { return nil } func TestProviderRegistration(t *testing.T) { for _, test := range []struct { desc string reg bool wantErr bool }{ { desc: "works", reg: true, wantErr: false, }, { desc: "unknown provider", reg: false, wantErr: true, }, } { t.Run(test.desc, func(t *testing.T) { called := false name := test.desc if test.reg { if err := RegisterProvider(name, func(_ monitoring.MetricFactory) (Provider, error) { called = true return &provider{}, nil }); err != nil { t.Error(err) } } _, err := NewProvider(name, nil) if err != nil && !test.wantErr { t.Fatalf("NewProvider = %v, want no error", err) } if err == nil && test.wantErr { t.Fatalf("NewProvider = no error, want error") } if !called && !test.wantErr { t.Fatal("Registered storage provider was not called") } }) } } func TestProviders(t *testing.T) { if err := RegisterProvider("a", func(_ monitoring.MetricFactory) (Provider, error) { return &provider{}, nil }); err != nil { t.Error(err) } if err := RegisterProvider("b", func(_ monitoring.MetricFactory) (Provider, error) { return &provider{}, nil }); err != nil { t.Error(err) } sp := Providers() if got, want := len(sp), 2; got < want { t.Fatalf("Got %d names, want at least %d", got, want) } a := 0 b := 0 for _, n := range sp { if n == "a" { a++ } if n == "b" { b++ } } if a != 1 { t.Errorf("Providers() gave %d 'a', want 1", a) } if b != 1 { t.Errorf("Providers() gave %d 'b', want 1", b) } } trillian-1.6.1/storage/storagepb/000077500000000000000000000000001466362047600170055ustar00rootroot00000000000000trillian-1.6.1/storage/storagepb/gen.go000066400000000000000000000022451466362047600201100ustar00rootroot00000000000000// 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 storagepb contains protobuf definitions used by various storage // implementations. // // TODO(pavelkalinnikov, v2): SubtreeProto is used as: // // a) database storage unit in multiple storage implementations; // b) data exchange format between storage and application layers; // c) nodes index data structure. // // We should change it so that: // // a) individual storage implementations define their own formats; // b) data structures are defined in the application layer. package storagepb //go:generate protoc -I=. --go_out=paths=source_relative:. storage.proto trillian-1.6.1/storage/storagepb/storage.pb.go000066400000000000000000000213741466362047600214070ustar00rootroot00000000000000// 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. // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.34.2 // protoc v3.20.1 // source: storage.proto package storagepb import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // SubtreeProto contains nodes of a subtree. type SubtreeProto struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // subtree's prefix (must be a multiple of 8 bits) Prefix []byte `protobuf:"bytes,1,opt,name=prefix,proto3" json:"prefix,omitempty"` // subtree's depth Depth int32 `protobuf:"varint,2,opt,name=depth,proto3" json:"depth,omitempty"` // map of suffix (within subtree) to subtree-leaf node hash Leaves map[string][]byte `protobuf:"bytes,4,rep,name=leaves,proto3" json:"leaves,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // Map of suffix (within subtree) to subtree-internal node hash. // This structure is usually used in RAM as a cache, the internal nodes of // the subtree are not generally stored. However internal nodes are stored for // partially filled log subtrees. InternalNodes map[string][]byte `protobuf:"bytes,5,rep,name=internal_nodes,json=internalNodes,proto3" json:"internal_nodes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // Used as a crosscheck on the internal node map by recording its expected // size after loading and repopulation. InternalNodeCount uint32 `protobuf:"varint,6,opt,name=internal_node_count,json=internalNodeCount,proto3" json:"internal_node_count,omitempty"` } func (x *SubtreeProto) Reset() { *x = SubtreeProto{} if protoimpl.UnsafeEnabled { mi := &file_storage_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *SubtreeProto) String() string { return protoimpl.X.MessageStringOf(x) } func (*SubtreeProto) ProtoMessage() {} func (x *SubtreeProto) ProtoReflect() protoreflect.Message { mi := &file_storage_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SubtreeProto.ProtoReflect.Descriptor instead. func (*SubtreeProto) Descriptor() ([]byte, []int) { return file_storage_proto_rawDescGZIP(), []int{0} } func (x *SubtreeProto) GetPrefix() []byte { if x != nil { return x.Prefix } return nil } func (x *SubtreeProto) GetDepth() int32 { if x != nil { return x.Depth } return 0 } func (x *SubtreeProto) GetLeaves() map[string][]byte { if x != nil { return x.Leaves } return nil } func (x *SubtreeProto) GetInternalNodes() map[string][]byte { if x != nil { return x.InternalNodes } return nil } func (x *SubtreeProto) GetInternalNodeCount() uint32 { if x != nil { return x.InternalNodeCount } return 0 } var File_storage_proto protoreflect.FileDescriptor var file_storage_proto_rawDesc = []byte{ 0x0a, 0x0d, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x70, 0x62, 0x22, 0xff, 0x02, 0x0a, 0x0c, 0x53, 0x75, 0x62, 0x74, 0x72, 0x65, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x14, 0x0a, 0x05, 0x64, 0x65, 0x70, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x64, 0x65, 0x70, 0x74, 0x68, 0x12, 0x3b, 0x0a, 0x06, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x70, 0x62, 0x2e, 0x53, 0x75, 0x62, 0x74, 0x72, 0x65, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x12, 0x51, 0x0a, 0x0e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x70, 0x62, 0x2e, 0x53, 0x75, 0x62, 0x74, 0x72, 0x65, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x11, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x40, 0x0a, 0x12, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x42, 0x2e, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( file_storage_proto_rawDescOnce sync.Once file_storage_proto_rawDescData = file_storage_proto_rawDesc ) func file_storage_proto_rawDescGZIP() []byte { file_storage_proto_rawDescOnce.Do(func() { file_storage_proto_rawDescData = protoimpl.X.CompressGZIP(file_storage_proto_rawDescData) }) return file_storage_proto_rawDescData } var file_storage_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_storage_proto_goTypes = []any{ (*SubtreeProto)(nil), // 0: storagepb.SubtreeProto nil, // 1: storagepb.SubtreeProto.LeavesEntry nil, // 2: storagepb.SubtreeProto.InternalNodesEntry } var file_storage_proto_depIdxs = []int32{ 1, // 0: storagepb.SubtreeProto.leaves:type_name -> storagepb.SubtreeProto.LeavesEntry 2, // 1: storagepb.SubtreeProto.internal_nodes:type_name -> storagepb.SubtreeProto.InternalNodesEntry 2, // [2:2] is the sub-list for method output_type 2, // [2:2] is the sub-list for method input_type 2, // [2:2] is the sub-list for extension type_name 2, // [2:2] is the sub-list for extension extendee 0, // [0:2] is the sub-list for field type_name } func init() { file_storage_proto_init() } func file_storage_proto_init() { if File_storage_proto != nil { return } if !protoimpl.UnsafeEnabled { file_storage_proto_msgTypes[0].Exporter = func(v any, i int) any { switch v := v.(*SubtreeProto); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_storage_proto_rawDesc, NumEnums: 0, NumMessages: 3, NumExtensions: 0, NumServices: 0, }, GoTypes: file_storage_proto_goTypes, DependencyIndexes: file_storage_proto_depIdxs, MessageInfos: file_storage_proto_msgTypes, }.Build() File_storage_proto = out.File file_storage_proto_rawDesc = nil file_storage_proto_goTypes = nil file_storage_proto_depIdxs = nil } trillian-1.6.1/storage/storagepb/storage.proto000066400000000000000000000030451466362047600215400ustar00rootroot00000000000000// 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. syntax = "proto3"; option go_package = "github.com/google/trillian/storage/storagepb"; package storagepb; // This file contains protos used only by storage. They are not exported via any // of our public APIs. // SubtreeProto contains nodes of a subtree. message SubtreeProto { // subtree's prefix (must be a multiple of 8 bits) bytes prefix = 1; // subtree's depth int32 depth = 2; // map of suffix (within subtree) to subtree-leaf node hash map leaves = 4; // Map of suffix (within subtree) to subtree-internal node hash. // This structure is usually used in RAM as a cache, the internal nodes of // the subtree are not generally stored. However internal nodes are stored for // partially filled log subtrees. map internal_nodes = 5; // Used as a crosscheck on the internal node map by recording its expected // size after loading and repopulation. uint32 internal_node_count = 6; reserved 3; } trillian-1.6.1/storage/testdb/000077500000000000000000000000001466362047600163045ustar00rootroot00000000000000trillian-1.6.1/storage/testdb/testdb.go000066400000000000000000000210761466362047600201260ustar00rootroot00000000000000// 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 testdb creates new databases for tests. package testdb import ( "bytes" "context" "database/sql" "fmt" "log" "net/url" "os" "strings" "testing" "time" "github.com/google/trillian/testonly" "golang.org/x/sys/unix" "k8s.io/klog/v2" _ "github.com/go-sql-driver/mysql" // mysql driver _ "github.com/lib/pq" // postgres driver ) const ( // MySQLURIEnv is the name of the ENV variable checked for the test MySQL // instance URI to use. The value must have a trailing slash. MySQLURIEnv = "TEST_MYSQL_URI" // Note: sql.Open requires the URI to end with a slash. defaultTestMySQLURI = "root@tcp(127.0.0.1)/" // CockroachDBURIEnv is the name of the ENV variable checked for the test CockroachDB // instance URI to use. The value must have a trailing slash. CockroachDBURIEnv = "TEST_COCKROACHDB_URI" defaultTestCockroachDBURI = "postgres://root@localhost:26257/?sslmode=disable" ) type storageDriverInfo struct { sqlDriverName string schema string uriFunc func(paths ...string) string } var ( trillianMySQLSchema = testonly.RelativeToPackage("../mysql/schema/storage.sql") trillianCRDBSchema = testonly.RelativeToPackage("../crdb/schema/storage.sql") ) // DriverName is the name of a database driver. type DriverName string const ( // DriverMySQL is the identifier for the MySQL storage driver. DriverMySQL DriverName = "mysql" // DriverCockroachDB is the identifier for the CockroachDB storage driver. DriverCockroachDB DriverName = "cockroachdb" ) var driverMapping = map[DriverName]storageDriverInfo{ DriverMySQL: { sqlDriverName: "mysql", schema: trillianMySQLSchema, uriFunc: mysqlURI, }, DriverCockroachDB: { sqlDriverName: "postgres", schema: trillianCRDBSchema, uriFunc: crdbURI, }, } // mysqlURI returns the MySQL connection URI to use for tests. It returns the // value in the ENV variable defined by MySQLURIEnv. If the value is empty, // returns defaultTestMySQLURI. // // We use an ENV variable, rather than a flag, for flexibility. Only a subset // of the tests in this repo require a database and import this package. With a // flag, it would be necessary to distinguish "go test" invocations that need a // database, and those that don't. ENV allows to "blanket apply" this setting. func mysqlURI(dbRef ...string) string { var stringurl string if e := os.Getenv(MySQLURIEnv); len(e) > 0 { stringurl = e } else { stringurl = defaultTestMySQLURI } for _, ref := range dbRef { separator := "/" if strings.HasSuffix(stringurl, "/") { separator = "" } stringurl = strings.Join([]string{stringurl, ref}, separator) } return stringurl } // crdbURI returns the CockroachDB connection URI to use for tests. It returns the // value in the ENV variable defined by CockroachDBURIEnv. If the value is empty, // returns defaultTestCockroachDBURI. func crdbURI(dbRef ...string) string { var uri *url.URL if e := os.Getenv(CockroachDBURIEnv); len(e) > 0 { uri = getURL(e) } else { uri = getURL(defaultTestCockroachDBURI) } return addPathToURI(uri, dbRef...) } func addPathToURI(uri *url.URL, paths ...string) string { if len(paths) > 0 { for _, ref := range paths { currentPaths := uri.Path // If the path is the root path, we don't want to append a slash. if currentPaths == "/" { currentPaths = "" } uri.Path = strings.Join([]string{currentPaths, ref}, "/") } } return uri.String() } func getURL(unparsedurl string) *url.URL { //nolint:errcheck // We're not expecting an error here. u, _ := url.Parse(unparsedurl) return u } // MySQLAvailable indicates whether the configured MySQL database is available. func MySQLAvailable() bool { return dbAvailable(DriverMySQL) } // CockroachDBAvailable indicates whether the configured CockroachDB database is available. func CockroachDBAvailable() bool { return dbAvailable(DriverCockroachDB) } func dbAvailable(driver DriverName) bool { driverName := driverMapping[driver].sqlDriverName uri := driverMapping[driver].uriFunc() db, err := sql.Open(driverName, uri) if err != nil { log.Printf("sql.Open(): %v", err) return false } defer func() { if err := db.Close(); err != nil { log.Printf("db.Close(): %v", err) } }() if err := db.Ping(); err != nil { log.Printf("db.Ping(): %v", err) return false } return true } // SetFDLimit sets the soft limit on the maximum number of open file descriptors. // See http://man7.org/linux/man-pages/man2/setrlimit.2.html func SetFDLimit(uLimit uint64) error { var rLimit unix.Rlimit if err := unix.Getrlimit(unix.RLIMIT_NOFILE, &rLimit); err != nil { return err } if uLimit > rLimit.Max { return fmt.Errorf("Could not set FD limit to %v. Must be less than the hard limit %v", uLimit, rLimit.Max) } rLimit.Cur = uLimit return unix.Setrlimit(unix.RLIMIT_NOFILE, &rLimit) } // newEmptyDB creates a new, empty database. // It returns the database handle and a clean-up function, or an error. // The returned clean-up function should be called once the caller is finished // using the DB, the caller should not continue to use the returned DB after // calling this function as it may, for example, delete the underlying // instance. func newEmptyDB(ctx context.Context, driver DriverName) (*sql.DB, func(context.Context), error) { if err := SetFDLimit(2048); err != nil { return nil, nil, err } inf, gotinf := driverMapping[driver] if !gotinf { return nil, nil, fmt.Errorf("unknown driver %q", driver) } db, err := sql.Open(inf.sqlDriverName, inf.uriFunc()) if err != nil { return nil, nil, err } // Create a randomly-named database and then connect using the new name. name := fmt.Sprintf("trl_%v", time.Now().UnixNano()) stmt := fmt.Sprintf("CREATE DATABASE %v", name) if _, err := db.ExecContext(ctx, stmt); err != nil { return nil, nil, fmt.Errorf("error running statement %q: %v", stmt, err) } if err := db.Close(); err != nil { return nil, nil, fmt.Errorf("failed to close DB: %v", err) } uri := inf.uriFunc(name) db, err = sql.Open(inf.sqlDriverName, uri) if err != nil { return nil, nil, err } done := func(ctx context.Context) { defer func() { if err := db.Close(); err != nil { klog.Errorf("db.Close(): %v", err) } }() if _, err := db.ExecContext(ctx, fmt.Sprintf("DROP DATABASE %v", name)); err != nil { klog.Warningf("Failed to drop test database %q: %v", name, err) } } return db, done, db.Ping() } // NewTrillianDB creates an empty database with the Trillian schema. The database name is randomly // generated. // NewTrillianDB is equivalent to Default().NewTrillianDB(ctx). func NewTrillianDB(ctx context.Context, driver DriverName) (*sql.DB, func(context.Context), error) { db, done, err := newEmptyDB(ctx, driver) if err != nil { return nil, nil, err } schema := driverMapping[driver].schema sqlBytes, err := os.ReadFile(schema) if err != nil { return nil, nil, err } for _, stmt := range strings.Split(sanitize(string(sqlBytes)), ";") { stmt = strings.TrimSpace(stmt) if stmt == "" { continue } if _, err := db.ExecContext(ctx, stmt); err != nil { return nil, nil, fmt.Errorf("error running statement %q: %v", stmt, err) } } return db, done, nil } func sanitize(script string) string { buf := &bytes.Buffer{} for _, line := range strings.Split(string(script), "\n") { line = strings.TrimSpace(line) if line == "" || line[0] == '#' || strings.Index(line, "--") == 0 { continue // skip empty lines and comments } buf.WriteString(line) buf.WriteString("\n") } return buf.String() } // SkipIfNoMySQL is a test helper that skips tests that require a local MySQL. func SkipIfNoMySQL(t *testing.T) { t.Helper() if !MySQLAvailable() { t.Skip("Skipping test as MySQL not available") } t.Logf("Test MySQL available at %q", mysqlURI()) } // SkipIfNoCockroachDB is a test helper that skips tests that require a local CockroachDB. func SkipIfNoCockroachDB(t *testing.T) { t.Helper() if !CockroachDBAvailable() { t.Skip("Skipping test as CockroachDB not available") } t.Logf("Test CockroachDB available at %q", crdbURI()) } trillian-1.6.1/storage/testdb/testdb_test.go000066400000000000000000000015311466362047600211570ustar00rootroot00000000000000// Copyright 2018 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 testdb import ( "testing" _ "k8s.io/klog/v2" ) func TestMySQLWarning(t *testing.T) { if !MySQLAvailable() { t.Error("Deliberate test failure as a reminder that all storage-related tests are being skipped due to absent MySQL") } } trillian-1.6.1/storage/testonly/000077500000000000000000000000001466362047600167005ustar00rootroot00000000000000trillian-1.6.1/storage/testonly/admin_storage_tester.go000066400000000000000000000475731466362047600234510ustar00rootroot00000000000000// 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 testonly import ( "context" "fmt" "reflect" "sort" "testing" "time" "github.com/google/go-cmp/cmp" "github.com/google/trillian" "github.com/google/trillian/storage" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/durationpb" ) var ( // LogTree is a valid, LOG-type trillian.Tree for tests. LogTree = &trillian.Tree{ TreeState: trillian.TreeState_ACTIVE, TreeType: trillian.TreeType_LOG, DisplayName: "Llamas Log", Description: "Registry of publicly-owned llamas", MaxRootDuration: durationpb.New(0 * time.Millisecond), } // PreorderedLogTree is a valid, PREORDERED_LOG-type trillian.Tree for tests. PreorderedLogTree = &trillian.Tree{ TreeState: trillian.TreeState_ACTIVE, TreeType: trillian.TreeType_PREORDERED_LOG, DisplayName: "Pre-ordered Log", Description: "Mirror registry of publicly-owned llamas", MaxRootDuration: durationpb.New(0 * time.Millisecond), } ) // AdminStorageTester runs a suite of tests against AdminStorage implementations. type AdminStorageTester struct { // NewAdminStorage returns an AdminStorage instance pointing to a clean // test database. NewAdminStorage func() storage.AdminStorage } // RunAllTests runs all AdminStorage tests. func (tester *AdminStorageTester) RunAllTests(t *testing.T) { t.Run("TestCreateTree", tester.TestCreateTree) t.Run("TestUpdateTree", tester.TestUpdateTree) t.Run("TestListTrees", tester.TestListTrees) t.Run("TestSoftDeleteTree", tester.TestSoftDeleteTree) t.Run("TestSoftDeleteTreeErrors", tester.TestSoftDeleteTreeErrors) t.Run("TestHardDeleteTree", tester.TestHardDeleteTree) t.Run("TestHardDeleteTreeErrors", tester.TestHardDeleteTreeErrors) t.Run("TestUndeleteTree", tester.TestUndeleteTree) t.Run("TestUndeleteTreeErrors", tester.TestUndeleteTreeErrors) t.Run("TestAdminTXReadWriteTransaction", tester.TestAdminTXReadWriteTransaction) } // TestCreateTree tests AdminStorage Tree creation. func (tester *AdminStorageTester) TestCreateTree(t *testing.T) { // Check that validation runs, but leave details to the validation // tests. invalidTree := proto.Clone(LogTree).(*trillian.Tree) invalidTree.TreeType = trillian.TreeType_UNKNOWN_TREE_TYPE validTree1 := proto.Clone(LogTree).(*trillian.Tree) validTree3 := proto.Clone(PreorderedLogTree).(*trillian.Tree) validTreeWithoutOptionals := proto.Clone(LogTree).(*trillian.Tree) validTreeWithoutOptionals.DisplayName = "" validTreeWithoutOptionals.Description = "" tests := []struct { desc string tree *trillian.Tree wantErr bool }{ { desc: "invalidTree", tree: invalidTree, wantErr: true, }, { desc: "validTree1", tree: validTree1, }, { desc: "validTree3", tree: validTree3, }, { desc: "validTreeWithoutOptionals", tree: validTreeWithoutOptionals, }, } ctx := context.Background() s := tester.NewAdminStorage() for _, test := range tests { func() { // Test CreateTree up to the tx commit newTree, err := storage.CreateTree(ctx, s, test.tree) if hasErr := err != nil; hasErr != test.wantErr { t.Errorf("%v: CreateTree() = (_, %v), wantErr = %v", test.desc, err, test.wantErr) return } else if hasErr { // Tested above return } createTime := newTree.CreateTime updateTime := newTree.UpdateTime if err := createTime.CheckValid(); err != nil { t.Errorf("%v: CreateTime malformed after creation: %v", test.desc, newTree) return } switch { case newTree.TreeId == 0: t.Errorf("%v: TreeID not returned from creation: %v", test.desc, newTree) return case !reflect.DeepEqual(createTime, updateTime): t.Errorf("%v: CreateTime != UpdateTime: %v", test.desc, newTree) return } wantTree := proto.Clone(test.tree).(*trillian.Tree) wantTree.TreeId = newTree.TreeId wantTree.CreateTime = createTime wantTree.UpdateTime = updateTime // Ignore storage_settings changes (OK to vary between implementations) wantTree.StorageSettings = newTree.StorageSettings if !proto.Equal(newTree, wantTree) { diff := cmp.Diff(newTree, &wantTree) t.Errorf("%v: post-CreateTree diff:\n%v", test.desc, diff) return } if err := assertStoredTree(ctx, s, newTree); err != nil { t.Errorf("%v: %v", test.desc, err) } }() } } // TestUpdateTree tests AdminStorage Tree updates. func (tester *AdminStorageTester) TestUpdateTree(t *testing.T) { ctx := context.Background() s := tester.NewAdminStorage() unrelatedTree := makeTreeOrFail(ctx, s, spec{Tree: PreorderedLogTree}, t.Fatalf) referenceLog := proto.Clone(LogTree).(*trillian.Tree) validLog := proto.Clone(referenceLog).(*trillian.Tree) validLog.TreeState = trillian.TreeState_FROZEN validLog.DisplayName = "Frozen Tree" validLog.Description = "A Frozen Tree" validLogFunc := func(tree *trillian.Tree) { tree.TreeState = validLog.TreeState tree.DisplayName = validLog.DisplayName tree.Description = validLog.Description } validLogWithoutOptionalsFunc := func(tree *trillian.Tree) { tree.DisplayName = "" tree.Description = "" } validLogWithoutOptionals := proto.Clone(referenceLog).(*trillian.Tree) validLogWithoutOptionalsFunc(validLogWithoutOptionals) invalidLogFunc := func(tree *trillian.Tree) { tree.TreeState = trillian.TreeState_UNKNOWN_TREE_STATE } readonlyChangedFunc := func(tree *trillian.Tree) { tree.TreeType = trillian.TreeType_PREORDERED_LOG } // Test for an unknown tree outside the loop: it makes the test logic simpler if _, err := storage.UpdateTree(ctx, s, -1, func(tree *trillian.Tree) {}); err == nil { t.Error("UpdateTree() for treeID -1 returned nil err") } tests := []struct { desc string create, want *trillian.Tree updateFunc func(*trillian.Tree) wantErr bool }{ { desc: "validLog", create: referenceLog, updateFunc: validLogFunc, want: validLog, }, { desc: "validLogWithoutOptionals", create: referenceLog, updateFunc: validLogWithoutOptionalsFunc, want: validLogWithoutOptionals, }, { desc: "invalidLog", create: referenceLog, updateFunc: invalidLogFunc, wantErr: true, }, { desc: "readonlyChanged", create: referenceLog, updateFunc: readonlyChangedFunc, wantErr: true, }, } for _, test := range tests { createdTree, err := storage.CreateTree(ctx, s, test.create) if err != nil { t.Errorf("CreateTree() = (_, %v), want = (_, nil)", err) continue } updatedTree, err := storage.UpdateTree(ctx, s, createdTree.TreeId, test.updateFunc) if hasErr := err != nil; hasErr != test.wantErr { t.Errorf("%v: UpdateTree() = (_, %v), wantErr = %v", test.desc, err, test.wantErr) continue } else if hasErr { continue } if createdTree.TreeId != updatedTree.TreeId { t.Errorf("%v: TreeId = %v, want = %v", test.desc, updatedTree.TreeId, createdTree.TreeId) } if !proto.Equal(createdTree.CreateTime, updatedTree.CreateTime) { t.Errorf("%v: CreateTime = %v, want = %v", test.desc, updatedTree.CreateTime, createdTree.CreateTime) } if err := createdTree.UpdateTime.CheckValid(); err != nil { t.Errorf("%v: createdTree.UpdateTime malformed: %v", test.desc, err) } if err := updatedTree.UpdateTime.CheckValid(); err != nil { t.Errorf("%v: updatedTree.UpdateTime malformed: %v", test.desc, err) } createUpdateTime := createdTree.UpdateTime.AsTime() updatedUpdateTime := updatedTree.UpdateTime.AsTime() if createUpdateTime.After(updatedUpdateTime) { t.Errorf("%v: UpdateTime = %v, want >= %v", test.desc, updatedTree.UpdateTime, createdTree.UpdateTime) } // Copy storage-generated values to want before comparing wantTree := tweakedCopy(test.want, func(t *trillian.Tree) { t.TreeId = updatedTree.TreeId t.CreateTime = updatedTree.CreateTime t.UpdateTime = updatedTree.UpdateTime // Ignore storage_settings changes (OK to vary between implementations) t.StorageSettings = updatedTree.StorageSettings }) if !proto.Equal(updatedTree, wantTree) { diff := cmp.Diff(updatedTree, wantTree) t.Errorf("%v: updatedTree doesn't match wantTree:\n%s", test.desc, diff) } if err := assertStoredTree(ctx, s, updatedTree); err != nil { t.Errorf("%v: %v", test.desc, err) } if err := assertStoredTree(ctx, s, unrelatedTree); err != nil { t.Errorf("%v: %v", test.desc, err) } } } // TestListTrees tests ListTrees. func (tester *AdminStorageTester) TestListTrees(t *testing.T) { ctx := context.Background() s := tester.NewAdminStorage() run := func(desc string, includeDeleted bool, wantTrees []*trillian.Tree) { if err := storage.RunInAdminSnapshot(ctx, s, func(tx storage.ReadOnlyAdminTX) error { if err := runListTreesTest(ctx, tx, includeDeleted, wantTrees); err != nil { t.Errorf("%v: %v", desc, err) } // Always return nil, as we're reporting errors independently above. return nil }); err != nil { // Capture Begin() / Commit() errors t.Errorf("%v: RunInAdminSnapshot() returned err = %v", desc, err) } } // Do a first pass with an empty DB run("empty", false /* includeDeleted */, nil /* wantTrees */) run("emptyDeleted", true /* includeDeleted */, nil /* wantTrees */) // Add some trees and do another pass activeLog := makeTreeOrFail(ctx, s, spec{Tree: LogTree}, t.Fatalf) frozenLog := makeTreeOrFail(ctx, s, spec{Tree: LogTree, Frozen: true}, t.Fatalf) deletedLog := makeTreeOrFail(ctx, s, spec{Tree: LogTree, Deleted: true}, t.Fatalf) run("multipleTrees", false /* includeDeleted */, []*trillian.Tree{activeLog, frozenLog}) run("multipleTreesDeleted", true /* includeDeleted */, []*trillian.Tree{activeLog, frozenLog, deletedLog}) } func runListTreesTest(ctx context.Context, tx storage.ReadOnlyAdminTX, includeDeleted bool, wantTrees []*trillian.Tree) error { got, err := tx.ListTrees(ctx, includeDeleted) if err != nil { return fmt.Errorf("ListTrees() returned err = %v", err) } if len(got) != len(wantTrees) { return fmt.Errorf("ListTrees() returned %v trees, want = %v", len(got), len(wantTrees)) } want := wantTrees sort.Slice(got, func(i, j int) bool { return got[i].TreeId < got[j].TreeId }) sort.Slice(want, func(i, j int) bool { return want[i].TreeId < want[j].TreeId }) for i, wantTree := range want { // Ignore storage_settings changes (OK to vary between implementations) wantTree.StorageSettings = got[i].StorageSettings if !proto.Equal(got[i], wantTree) { return fmt.Errorf("post-ListTrees() diff (-got +want):\n%v", cmp.Diff(got, want, cmp.Comparer(proto.Equal))) } } return nil } // TestSoftDeleteTree tests success scenarios of SoftDeleteTree. func (tester *AdminStorageTester) TestSoftDeleteTree(t *testing.T) { ctx := context.Background() s := tester.NewAdminStorage() logTree := makeTreeOrFail(ctx, s, spec{Tree: LogTree}, t.Fatalf) tests := []struct { desc string tree *trillian.Tree }{ {desc: "logTree", tree: logTree}, } for _, test := range tests { deletedTree, err := storage.SoftDeleteTree(ctx, s, test.tree.TreeId) if err != nil { t.Errorf("%v: SoftDeleteTree() returned err = %v", test.desc, err) continue } if deletedTree.GetDeleteTime().GetSeconds() == 0 { t.Errorf("%v: tree.DeleteTime = %v, want > 0", test.desc, deletedTree.DeleteTime) } wantTree := proto.Clone(test.tree).(*trillian.Tree) wantTree.Deleted = true wantTree.DeleteTime = deletedTree.DeleteTime // Ignore storage_settings changes (OK to vary between implementations) wantTree.StorageSettings = deletedTree.StorageSettings if got, want := deletedTree, wantTree; !proto.Equal(got, want) { t.Errorf("%v: post-SoftDeleteTree diff (-got +want):\n%v", test.desc, cmp.Diff(got, want, cmp.Comparer(proto.Equal))) } if err := assertStoredTree(ctx, s, deletedTree); err != nil { t.Errorf("%v: %v", test.desc, err) } } } // TestSoftDeleteTreeErrors tests error scenarios of SoftDeleteTree. func (tester *AdminStorageTester) TestSoftDeleteTreeErrors(t *testing.T) { ctx := context.Background() s := tester.NewAdminStorage() softDeleted := makeTreeOrFail(ctx, s, spec{Tree: LogTree, Deleted: true}, t.Fatalf) tests := []struct { desc string treeID int64 wantCode codes.Code }{ {desc: "unknownTree", treeID: 12345, wantCode: codes.NotFound}, {desc: "alreadyDeleted", treeID: softDeleted.TreeId, wantCode: codes.FailedPrecondition}, } for _, test := range tests { if _, err := storage.SoftDeleteTree(ctx, s, test.treeID); status.Code(err) != test.wantCode { t.Errorf("%v: SoftDeleteTree() returned err = %v, wantCode = %s", test.desc, err, test.wantCode) } } } // TestHardDeleteTree tests success scenarios of HardDeleteTree. func (tester *AdminStorageTester) TestHardDeleteTree(t *testing.T) { ctx := context.Background() s := tester.NewAdminStorage() logTree := makeTreeOrFail(ctx, s, spec{Tree: LogTree, Deleted: true}, t.Fatalf) frozenTree := makeTreeOrFail(ctx, s, spec{Tree: LogTree, Deleted: true, Frozen: true}, t.Fatalf) tests := []struct { desc string treeID int64 }{ {desc: "logTree", treeID: logTree.TreeId}, {desc: "frozenTree", treeID: frozenTree.TreeId}, } for _, test := range tests { if err := storage.HardDeleteTree(ctx, s, test.treeID); err != nil { t.Errorf("%v: HardDeleteTree() returned err = %v", test.desc, err) continue } } } // TestHardDeleteTreeErrors tests error scenarios of HardDeleteTree. func (tester *AdminStorageTester) TestHardDeleteTreeErrors(t *testing.T) { ctx := context.Background() s := tester.NewAdminStorage() activeTree := makeTreeOrFail(ctx, s, spec{Tree: LogTree}, t.Fatalf) tests := []struct { desc string treeID int64 wantCode codes.Code }{ {desc: "unknownTree", treeID: 12345, wantCode: codes.NotFound}, {desc: "activeTree", treeID: activeTree.TreeId, wantCode: codes.FailedPrecondition}, } for _, test := range tests { if err := storage.HardDeleteTree(ctx, s, test.treeID); status.Code(err) != test.wantCode { t.Errorf("%v: HardDeleteTree() returned err = %v, wantCode = %s", test.desc, err, test.wantCode) } } } // TestUndeleteTree tests success scenarios of UndeleteTree. func (tester *AdminStorageTester) TestUndeleteTree(t *testing.T) { ctx := context.Background() s := tester.NewAdminStorage() activeDeleted := makeTreeOrFail(ctx, s, spec{Tree: LogTree, Deleted: true}, t.Fatalf) frozenDeleted := makeTreeOrFail(ctx, s, spec{Tree: LogTree, Frozen: true, Deleted: true}, t.Fatalf) tests := []struct { desc string tree *trillian.Tree }{ {desc: "activeTree", tree: activeDeleted}, {desc: "frozenTree", tree: frozenDeleted}, } for _, test := range tests { tree, err := storage.UndeleteTree(ctx, s, test.tree.TreeId) if err != nil { t.Errorf("%v: UndeleteTree() returned err = %v", test.desc, err) continue } want := proto.Clone(test.tree).(*trillian.Tree) want.Deleted = false want.DeleteTime = nil if got := tree; !proto.Equal(got, want) { t.Errorf("%v: post-UndeleteTree diff (-got +want):\n%v", test.desc, cmp.Diff(got, want)) } if err := assertStoredTree(ctx, s, tree); err != nil { t.Errorf("%v: %v", test.desc, err) } } } // TestUndeleteTreeErrors tests error scenarios of UndeleteTree. func (tester *AdminStorageTester) TestUndeleteTreeErrors(t *testing.T) { ctx := context.Background() s := tester.NewAdminStorage() activeTree := makeTreeOrFail(ctx, s, spec{Tree: LogTree}, t.Fatalf) tests := []struct { desc string treeID int64 wantCode codes.Code }{ {desc: "unknownTree", treeID: 12345, wantCode: codes.NotFound}, {desc: "activeTree", treeID: activeTree.TreeId, wantCode: codes.FailedPrecondition}, } for _, test := range tests { if _, err := storage.UndeleteTree(ctx, s, test.treeID); status.Code(err) != test.wantCode { t.Errorf("%v: UndeleteTree() returned err = %v, wantCode = %s", test.desc, err, test.wantCode) } } } // TestAdminTXReadWriteTransaction tests the ReadWriteTransaction method on AdminStorage. func (tester *AdminStorageTester) TestAdminTXReadWriteTransaction(t *testing.T) { tests := []struct { wantCommit bool }{ {wantCommit: true}, {wantCommit: false}, } ctx := context.Background() s := tester.NewAdminStorage() var tree *trillian.Tree for i, test := range tests { t.Run(fmt.Sprintf("%+v", test), func(t *testing.T) { err := s.ReadWriteTransaction(ctx, func(ctx context.Context, tx storage.AdminTX) error { var err error tree, err = tx.CreateTree(ctx, LogTree) if err != nil { t.Fatalf("%v: CreateTree() = (_, %v), want = (_, nil)", i, err) } if !test.wantCommit { return fmt.Errorf("no commit %d", i) } return nil }) if (err != nil && test.wantCommit) || (err == nil && !test.wantCommit) { t.Fatalf("%v: ReadWriteTransaction() = (_, %v), want = (_, nil)", i, err) } tx2, err := s.Snapshot(ctx) if err != nil { t.Fatalf("%v: Snapshot() = (_, %v), want = (_, nil)", i, err) } defer func() { if err := tx2.Close(); err != nil { t.Errorf("tx2.Close(): %v", err) } }() _, err = tx2.GetTree(ctx, tree.TreeId) if hasErr := err != nil; !test.wantCommit != hasErr { t.Errorf("%v: GetTree() = (_, %v), but wantCommit = %v", i, err, test.wantCommit) } // Multiple Close() calls are fine too if err := tx2.Close(); err != nil { t.Errorf("%v: Close() = %v, want = nil", i, err) return } }) } } // assertStoredTree verifies that "want" is equal to the tree stored under its ID. func assertStoredTree(ctx context.Context, s storage.AdminStorage, want *trillian.Tree) error { got, err := storage.GetTree(ctx, s, want.TreeId) if err != nil { return fmt.Errorf("GetTree() returned err = %v", err) } // Ignore storage_settings changes (OK to vary between implementations) want.StorageSettings = got.StorageSettings if !proto.Equal(got, want) { return fmt.Errorf("post-GetTree() diff (-got +want):\n%v", cmp.Diff(got, want, cmp.Comparer(proto.Equal))) } return nil } type spec struct { Tree *trillian.Tree Frozen, Deleted bool } // makeTreeOrFail delegates to makeTree. If makeTree returns a non-nil error, failFn is called. func makeTreeOrFail(ctx context.Context, s storage.AdminStorage, spec spec, failFn func(string, ...interface{})) *trillian.Tree { tree, err := makeTree(ctx, s, spec) if err != nil { failFn("makeTree() returned err = %v", err) return nil } return tree } // makeTree creates a tree and updates it to Frozen and/or Deleted, according to "spec". func makeTree(ctx context.Context, s storage.AdminStorage, spec spec) (*trillian.Tree, error) { tree := proto.Clone(spec.Tree).(*trillian.Tree) var err error tree, err = storage.CreateTree(ctx, s, tree) if err != nil { return nil, err } if spec.Frozen { tree, err = storage.UpdateTree(ctx, s, tree.TreeId, func(t *trillian.Tree) { t.TreeState = trillian.TreeState_FROZEN }) if err != nil { return nil, err } } if spec.Deleted { tree, err = storage.SoftDeleteTree(ctx, s, tree.TreeId) if err != nil { return nil, err } } // Sanity checks if spec.Frozen && tree.TreeState != trillian.TreeState_FROZEN { return nil, fmt.Errorf("makeTree(): TreeState = %s, want = %s", tree.TreeState, trillian.TreeState_FROZEN) } if tree.Deleted != spec.Deleted { return nil, fmt.Errorf("makeTree(): Deleted = %v, want = %v", tree.Deleted, spec.Deleted) } return tree, nil } func tweakedCopy(tree *trillian.Tree, modFn func(t *trillian.Tree)) *trillian.Tree { newTree := proto.Clone(tree).(*trillian.Tree) modFn(newTree) return newTree } trillian-1.6.1/storage/testonly/doc.go000066400000000000000000000013111466362047600177700ustar00rootroot00000000000000// Copyright 2022 Trillian Authors. 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 utilities and helpers to use in the storage tests. package testonly trillian-1.6.1/storage/testonly/fake_node_reader.go000066400000000000000000000150401466362047600224640ustar00rootroot00000000000000// 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 testonly import ( "bytes" "context" "fmt" "github.com/google/trillian/storage/tree" "github.com/transparency-dev/merkle/compact" "github.com/transparency-dev/merkle/rfc6962" "k8s.io/klog/v2" ) // This is a fake implementation of a NodeReader intended for use in testing Merkle path code. // Building node sets for tests by hand is onerous and error prone, especially when trying // to test code reading from multiple tree revisions. It cannot live in the main testonly // package as this creates import cycles. // FakeNodeReader is an implementation of storage.NodeReader that's preloaded with a set of // NodeID -> Node mappings and will return only those. Requesting any other nodes results in // an error. For use in tests only, does not implement any other storage APIs. type FakeNodeReader struct { nodeMap map[compact.NodeID]tree.Node } // NewFakeNodeReader creates and returns a FakeNodeReader with the supplied nodes // assuming that all the nodes are at a specified tree revision. All the node IDs // must be distinct. func NewFakeNodeReader(nodes []tree.Node) *FakeNodeReader { nodeMap := make(map[compact.NodeID]tree.Node) for _, node := range nodes { id := node.ID if _, ok := nodeMap[id]; ok { // Duplicate mapping - the test data is invalid so don't continue. klog.Fatalf("NewFakeNodeReader duplicate mapping for: %+v in:\n%v", id, nodes) } nodeMap[id] = node } return &FakeNodeReader{nodeMap: nodeMap} } // GetMerkleNodes implements the corresponding NodeReader API. func (f FakeNodeReader) GetMerkleNodes(ids []compact.NodeID) ([]tree.Node, error) { nodes := make([]tree.Node, 0, len(ids)) for _, id := range ids { node, ok := f.nodeMap[id] if !ok { return nil, fmt.Errorf("GetMerkleNodes() unknown node ID: %v", id) } nodes = append(nodes, node) } return nodes, nil } func (f FakeNodeReader) hasID(id compact.NodeID) bool { _, ok := f.nodeMap[id] return ok } // MultiFakeNodeReader can provide nodes at multiple revisions. It delegates to a number of // FakeNodeReaders, each set up to handle one revision. type MultiFakeNodeReader struct { readers []FakeNodeReader } // LeafBatch describes a set of leaves to be loaded into a MultiFakeNodeReader via a compact // merkle tree. As each batch is added to the tree a set of node updates are collected // and recorded in a FakeNodeReader for that revision. The expected root should be the // result of calling CurrentRoot() on the compact Merkle tree encoded by hex.EncodeToString(). type LeafBatch struct { TreeRevision int64 Leaves []string ExpectedRoot []byte } // NewMultiFakeNodeReader creates a MultiFakeNodeReader delegating to a number of FakeNodeReaders func NewMultiFakeNodeReader(readers []FakeNodeReader) *MultiFakeNodeReader { return &MultiFakeNodeReader{readers: readers} } // NewMultiFakeNodeReaderFromLeaves uses a compact Merkle tree to set up the nodes at various // revisions. It collates all node updates from a batch of leaf data into one FakeNodeReader. // This has the advantage of not needing to manually create all the data structures but the // disadvantage is that a bug in the compact tree could be reflected in test using this // code. To help guard against this we check the tree root hash after each batch has been // processed. The supplied batches should be in ascending order of tree revision. func NewMultiFakeNodeReaderFromLeaves(batches []LeafBatch) *MultiFakeNodeReader { hasher := rfc6962.DefaultHasher fact := compact.RangeFactory{Hash: hasher.HashChildren} cr := fact.NewEmptyRange(0) readers := make([]FakeNodeReader, 0, len(batches)) lastBatchRevision := int64(0) for _, batch := range batches { if batch.TreeRevision <= lastBatchRevision { klog.Fatalf("Batches out of order revision: %d, last: %d in:\n%v", batch.TreeRevision, lastBatchRevision, batches) } lastBatchRevision = batch.TreeRevision nodeMap := make(map[compact.NodeID][]byte) store := func(id compact.NodeID, hash []byte) { nodeMap[id] = hash } for _, leaf := range batch.Leaves { hash := hasher.HashLeaf([]byte(leaf)) // Store the new leaf node, and all new perfect nodes. if err := cr.Append(hash, store); err != nil { panic(fmt.Errorf("Append: %v", err)) } } // TODO(pavelkalinnikov): Use testing.T.Fatalf instead of panics. root, err := cr.GetRootHash(nil) if err != nil { panic(fmt.Errorf("GetRootHash: %v", err)) } if cr.End() == 0 { root = hasher.EmptyRoot() } // Sanity check the tree root hash against the one we expect to see. if got, want := root, batch.ExpectedRoot; !bytes.Equal(got, want) { panic(fmt.Errorf("NewMultiFakeNodeReaderFromLeaves() got root: %x, want: %x (%v)", got, want, batch)) } // Unroll the update map to []tree.Node to retain the most recent node update within // the batch for each ID. Use that to create a new FakeNodeReader. nodes := make([]tree.Node, 0, len(nodeMap)) for id, hash := range nodeMap { nodes = append(nodes, tree.Node{ID: id, Hash: hash}) } readers = append(readers, *NewFakeNodeReader(nodes)) } return NewMultiFakeNodeReader(readers) } func (m MultiFakeNodeReader) readerForNodeID(id compact.NodeID) *FakeNodeReader { // Work backwards and use the first reader where the node is present. for i := len(m.readers) - 1; i >= 0; i-- { if m.readers[i].hasID(id) { return &m.readers[i] } } return nil } // GetMerkleNodes implements the corresponding NodeReader API. func (m MultiFakeNodeReader) GetMerkleNodes(ctx context.Context, ids []compact.NodeID) ([]tree.Node, error) { // Find the correct reader for the supplied tree revision. This must be done for each node // as earlier revisions may still be relevant nodes := make([]tree.Node, 0, len(ids)) for _, id := range ids { reader := m.readerForNodeID(id) if reader == nil { return nil, fmt.Errorf("want nodeID %v, but no reader has it\n%v", id, m) } node, err := reader.GetMerkleNodes([]compact.NodeID{id}) if err != nil { return nil, err } nodes = append(nodes, node[0]) } return nodes, nil } trillian-1.6.1/storage/testonly/matchers.go000066400000000000000000000026241466362047600210410ustar00rootroot00000000000000// 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 testonly import ( "fmt" "reflect" "sort" "github.com/golang/mock/gomock" "github.com/google/trillian/storage/tree" ) // NodeSet returns a matcher that expects the given set of nodes. func NodeSet(nodes []tree.Node) gomock.Matcher { return nodeSet(sorted(nodes)) } type nodeSet []tree.Node // Matches implements the gomock.Matcher API. func (n nodeSet) Matches(x interface{}) bool { nodes, ok := x.([]tree.Node) if !ok { return false } return reflect.DeepEqual(sorted(nodes), []tree.Node(n)) } func (n nodeSet) String() string { return fmt.Sprintf("equivalent to %v", []tree.Node(n)) } func sorted(n []tree.Node) []tree.Node { sort.Slice(n, func(i, j int) bool { return n[i].ID.Level < n[j].ID.Level || (n[i].ID.Level == n[j].ID.Level && n[i].ID.Index < n[j].ID.Index) }) return n } trillian-1.6.1/storage/testonly/transaction.go000066400000000000000000000115311466362047600215550ustar00rootroot00000000000000// Copyright 2018 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 ( "context" "errors" "time" "github.com/google/trillian" "github.com/google/trillian/storage" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "k8s.io/klog/v2" ) // RunOnLogTX is a helper for mocking out the LogStorage.ReadWriteTransaction method. func RunOnLogTX(tx storage.LogTreeTX) func(ctx context.Context, treeID int64, f storage.LogTXFunc) error { return func(ctx context.Context, _ int64, f storage.LogTXFunc) error { defer func() { if err := tx.Close(); err != nil { klog.Errorf("tx.Close(): %v", err) } }() if err := f(ctx, tx); err != nil { return err } return tx.Commit(ctx) } } // RunOnAdminTX is a helper for mocking out the AdminStorage.ReadWriteTransaction method. func RunOnAdminTX(tx storage.AdminTX) func(ctx context.Context, f storage.AdminTXFunc) error { return func(ctx context.Context, f storage.AdminTXFunc) error { defer func() { if err := tx.Close(); err != nil { klog.Errorf("tx.Close(): %v", err) } }() if err := f(ctx, tx); err != nil { return err } return tx.Commit() } } // ErrNotImplemented is returned by unimplemented methods on the storage fakes. var ErrNotImplemented = errors.New("not implemented") // FakeLogStorage is a LogStorage implementation which is used for testing. type FakeLogStorage struct { TX storage.LogTreeTX ReadOnlyTX storage.ReadOnlyLogTreeTX TXErr error QueueLeavesErr error AddSequencedLeavesErr error } // GetActiveLogIDs implements LogStorage.GetActiveLogIDs. func (f *FakeLogStorage) GetActiveLogIDs(ctx context.Context) ([]int64, error) { return nil, ErrNotImplemented } // SnapshotForTree implements LogStorage.SnapshotForTree func (f *FakeLogStorage) SnapshotForTree(ctx context.Context, _ *trillian.Tree) (storage.ReadOnlyLogTreeTX, error) { return f.ReadOnlyTX, f.TXErr } // ReadWriteTransaction implements LogStorage.ReadWriteTransaction func (f *FakeLogStorage) ReadWriteTransaction(ctx context.Context, tree *trillian.Tree, fn storage.LogTXFunc) error { if f.TXErr != nil { return f.TXErr } return RunOnLogTX(f.TX)(ctx, tree.TreeId, fn) } // QueueLeaves implements LogStorage.QueueLeaves. func (f *FakeLogStorage) QueueLeaves(ctx context.Context, tree *trillian.Tree, leaves []*trillian.LogLeaf, queueTimestamp time.Time) ([]*trillian.QueuedLogLeaf, error) { if f.QueueLeavesErr != nil { return nil, f.QueueLeavesErr } return make([]*trillian.QueuedLogLeaf, len(leaves)), nil } // AddSequencedLeaves implements LogStorage.AddSequencedLeaves. func (f *FakeLogStorage) AddSequencedLeaves(ctx context.Context, tree *trillian.Tree, leaves []*trillian.LogLeaf, timestamp time.Time) ([]*trillian.QueuedLogLeaf, error) { if f.AddSequencedLeavesErr != nil { return nil, f.AddSequencedLeavesErr } res := make([]*trillian.QueuedLogLeaf, len(leaves)) for i := range res { res[i] = &trillian.QueuedLogLeaf{Status: status.New(codes.OK, "OK").Proto()} } return res, nil } // CheckDatabaseAccessible implements LogStorage.CheckDatabaseAccessible func (f *FakeLogStorage) CheckDatabaseAccessible(ctx context.Context) error { return nil } // FakeAdminStorage is a AdminStorage implementation which is used for testing. type FakeAdminStorage struct { TX []storage.AdminTX ReadOnlyTX []storage.ReadOnlyAdminTX TXErr []error SnapshotErr []error } // Begin implements AdminStorage.Begin func (f *FakeAdminStorage) Begin(ctx context.Context) (storage.AdminTX, error) { return nil, ErrNotImplemented } // Snapshot implements AdminStorage.Snapshot func (f *FakeAdminStorage) Snapshot(ctx context.Context) (storage.ReadOnlyAdminTX, error) { if len(f.SnapshotErr) > 0 { e := f.SnapshotErr[0] f.SnapshotErr = f.SnapshotErr[1:] return nil, e } r := f.ReadOnlyTX[0] f.ReadOnlyTX = f.ReadOnlyTX[1:] return r, nil } // ReadWriteTransaction implements AdminStorage.ReadWriteTransaction func (f *FakeAdminStorage) ReadWriteTransaction(ctx context.Context, fn storage.AdminTXFunc) error { if len(f.TXErr) > 0 { e := f.TXErr[0] f.TXErr = f.TXErr[1:] return e } t := f.TX[0] f.TX = f.TX[1:] return RunOnAdminTX(t)(ctx, fn) } // CheckDatabaseAccessible implements AdminStorage.CheckDatabaseAccessible func (f *FakeAdminStorage) CheckDatabaseAccessible(ctx context.Context) error { return nil } trillian-1.6.1/storage/tree/000077500000000000000000000000001466362047600157565ustar00rootroot00000000000000trillian-1.6.1/storage/tree/node.go000066400000000000000000000015251466362047600172350ustar00rootroot00000000000000// 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 tree defines types that help navigating a tree in storage. package tree import "github.com/transparency-dev/merkle/compact" // Node represents a single node in a Merkle tree. type Node struct { ID compact.NodeID Hash []byte } trillian-1.6.1/storage/tree_id.go000066400000000000000000000016001466362047600167560ustar00rootroot00000000000000// 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 storage import ( "crypto/rand" "math" "math/big" ) // NewTreeID generates a random, positive, non-zero tree ID. func NewTreeID() (int64, error) { id, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt64)) if err != nil { return 0, err } return id.Int64() + 1, nil } trillian-1.6.1/storage/tree_id_test.go000066400000000000000000000021371466362047600200230ustar00rootroot00000000000000// 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 storage import "testing" func TestNewTreeID(t *testing.T) { // Grab a few IDs, check that they're not zero and not repeating. ids := make(map[int64]bool) for i := 0; i < 5; i++ { id, err := NewTreeID() switch _, ok := ids[id]; { case err != nil: t.Errorf("%v: NewTreeID() = (_ %v), want = (_, nil)", i, err) case ok: t.Errorf("%v: NewTreeID() return repeated ID: %v", i, id) case id <= 0: t.Errorf("%v: NewTreeID() = %v, want > 0", i, id) default: ids[id] = true } } } trillian-1.6.1/storage/tree_validation.go000066400000000000000000000116751466362047600205310ustar00rootroot00000000000000// 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 storage import ( "context" "github.com/google/trillian" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/proto" ) // ValidateTreeForCreation returns nil if tree is valid for insertion, error // otherwise. // See the documentation on trillian.Tree for reference on which values are // valid. func ValidateTreeForCreation(ctx context.Context, tree *trillian.Tree) error { switch { case tree == nil: return status.Error(codes.InvalidArgument, "a tree is required") case tree.TreeState != trillian.TreeState_ACTIVE: return status.Errorf(codes.InvalidArgument, "invalid tree_state: %s", tree.TreeState) case tree.TreeType == trillian.TreeType_UNKNOWN_TREE_TYPE: return status.Errorf(codes.InvalidArgument, "invalid tree_type: %s", tree.TreeType) case tree.Deleted: return status.Errorf(codes.InvalidArgument, "invalid deleted: %v", tree.Deleted) case tree.DeleteTime != nil: return status.Errorf(codes.InvalidArgument, "invalid delete_time: %+v (must be nil)", tree.DeleteTime) } return validateMutableTreeFields(ctx, tree) } // validateTreeTypeUpdate returns nil iff oldTree.TreeType can be updated to // newTree.TreeType. The tree type is changeable only if the Tree is and // remains in the FROZEN state. // At the moment only PREORDERED_LOG->LOG type transition is permitted. func validateTreeTypeUpdate(oldTree, newTree *trillian.Tree) error { const prefix = "can't change tree_type" const wantState = trillian.TreeState_FROZEN if oldState := oldTree.TreeState; oldState != wantState { return status.Errorf(codes.InvalidArgument, "%s: tree_state=%v, want %v", prefix, oldState, wantState) } else if newTree.TreeState != wantState { return status.Errorf(codes.InvalidArgument, "%s: tree_state should stay %v", prefix, wantState) } if oldTree.TreeType != trillian.TreeType_PREORDERED_LOG || newTree.TreeType != trillian.TreeType_LOG { return status.Errorf(codes.InvalidArgument, "%s: %v->%v", prefix, oldTree.TreeType, newTree.TreeType) } return nil } // ValidateTreeForUpdate returns nil if newTree is valid for update, error // otherwise. // The newTree is compared to the storedTree to determine if readonly fields // have been changed. It's assumed that storage-generated fields, such as // update_time, have not yet changed when this method is called. // See the documentation on trillian.Tree for reference on which fields may be // changed and what is considered valid for each of them. func ValidateTreeForUpdate(ctx context.Context, storedTree, newTree *trillian.Tree) error { // Check that readonly fields didn't change switch { case storedTree.TreeId != newTree.TreeId: return status.Error(codes.InvalidArgument, "readonly field changed: tree_id") case storedTree.TreeType != newTree.TreeType: if err := validateTreeTypeUpdate(storedTree, newTree); err != nil { return err } case !proto.Equal(storedTree.CreateTime, newTree.CreateTime): return status.Error(codes.InvalidArgument, "readonly field changed: create_time") case !proto.Equal(storedTree.UpdateTime, newTree.UpdateTime): return status.Error(codes.InvalidArgument, "readonly field changed: update_time") case storedTree.Deleted != newTree.Deleted: return status.Error(codes.InvalidArgument, "readonly field changed: deleted") case !proto.Equal(storedTree.DeleteTime, newTree.DeleteTime): return status.Error(codes.InvalidArgument, "readonly field changed: delete_time") } return validateMutableTreeFields(ctx, newTree) } func validateMutableTreeFields(ctx context.Context, tree *trillian.Tree) error { if tree.TreeState == trillian.TreeState_UNKNOWN_TREE_STATE { return status.Errorf(codes.InvalidArgument, "invalid tree_state: %v", tree.TreeState) } if err := tree.MaxRootDuration.CheckValid(); err != nil { return status.Errorf(codes.InvalidArgument, "max_root_duration malformed: %v", err) } else if duration := tree.MaxRootDuration.AsDuration(); duration < 0 { return status.Errorf(codes.InvalidArgument, "max_root_duration negative: %v", tree.MaxRootDuration) } // Implementations may vary, so let's assume storage_settings is mutable. // Other than checking that it's a valid Any there isn't much to do at this layer, though. if tree.StorageSettings != nil { _, err := tree.StorageSettings.UnmarshalNew() if err != nil { return status.Errorf(codes.InvalidArgument, "invalid storage_settings: %v", err) } } return nil } trillian-1.6.1/storage/tree_validation_test.go000066400000000000000000000164121466362047600215620ustar00rootroot00000000000000// 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 storage import ( "context" "testing" "time" "github.com/google/trillian" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/anypb" "google.golang.org/protobuf/types/known/durationpb" "google.golang.org/protobuf/types/known/timestamppb" ) func TestValidateTreeForCreation(t *testing.T) { ctx := context.Background() valid1 := newTree() valid2 := newTree() valid2.TreeType = trillian.TreeType_PREORDERED_LOG invalidState1 := newTree() invalidState1.TreeState = trillian.TreeState_UNKNOWN_TREE_STATE invalidState2 := newTree() invalidState2.TreeState = trillian.TreeState_FROZEN invalidType := newTree() invalidType.TreeType = trillian.TreeType_UNKNOWN_TREE_TYPE invalidSettings := newTree() invalidSettings.StorageSettings = &anypb.Any{Value: []byte("foobar")} // As long as settings is a valid proto, the type doesn't matter for this test. settings, err := anypb.New(&trillian.Tree{}) if err != nil { t.Fatalf("Error marshaling proto: %v", err) } validSettings := newTree() validSettings.StorageSettings = settings nilRootDuration := newTree() nilRootDuration.MaxRootDuration = nil invalidRootDuration := newTree() invalidRootDuration.MaxRootDuration = durationpb.New(-1 * time.Second) deletedTree := newTree() deletedTree.Deleted = true deleteTimeTree := newTree() deleteTimeTree.DeleteTime = timestamppb.Now() tests := []struct { desc string tree *trillian.Tree wantErr bool }{ { desc: "valid1", tree: valid1, }, { desc: "valid2", tree: valid2, }, { desc: "nilTree", tree: nil, wantErr: true, }, { desc: "invalidState1", tree: invalidState1, wantErr: true, }, { desc: "invalidState2", tree: invalidState2, wantErr: true, }, { desc: "invalidType", tree: invalidType, wantErr: true, }, { desc: "invalidSettings", tree: invalidSettings, wantErr: true, }, { desc: "validSettings", tree: validSettings, }, { desc: "nilRootDuration", tree: nilRootDuration, wantErr: true, }, { desc: "invalidRootDuration", tree: invalidRootDuration, wantErr: true, }, { desc: "deletedTree", tree: deletedTree, wantErr: true, }, { desc: "deleteTimeTree", tree: deleteTimeTree, wantErr: true, }, } for _, test := range tests { err := ValidateTreeForCreation(ctx, test.tree) switch hasErr := err != nil; { case hasErr != test.wantErr: t.Errorf("%v: ValidateTreeForCreation() = %v, wantErr = %v", test.desc, err, test.wantErr) case hasErr && status.Code(err) != codes.InvalidArgument: t.Errorf("%v: ValidateTreeForCreation() = %v, wantCode = %v", test.desc, err, codes.InvalidArgument) } } } func TestValidateTreeForUpdate(t *testing.T) { ctx := context.Background() tests := []struct { desc string treeState trillian.TreeState treeType trillian.TreeType updatefn func(*trillian.Tree) wantErr bool }{ { desc: "valid", updatefn: func(tree *trillian.Tree) { tree.TreeState = trillian.TreeState_FROZEN tree.DisplayName = "Frozen Tree" tree.Description = "A Frozen Tree" }, }, { desc: "noop", updatefn: func(tree *trillian.Tree) {}, }, { desc: "validSettings", updatefn: func(tree *trillian.Tree) { // As long as settings is a valid proto, the type doesn't matter for this test. settings, err := anypb.New(&trillian.Tree{}) if err != nil { t.Fatalf("Error marshaling proto: %v", err) } tree.StorageSettings = settings }, }, { desc: "invalidSettings", updatefn: func(tree *trillian.Tree) { tree.StorageSettings = &anypb.Any{Value: []byte("foobar")} }, wantErr: true, }, { desc: "validRootDuration", updatefn: func(tree *trillian.Tree) { tree.MaxRootDuration = durationpb.New(200 * time.Millisecond) }, }, { desc: "invalidRootDuration", updatefn: func(tree *trillian.Tree) { tree.MaxRootDuration = durationpb.New(-200 * time.Millisecond) }, wantErr: true, }, // Changes on readonly fields { desc: "TreeId", updatefn: func(tree *trillian.Tree) { tree.TreeId++ }, wantErr: true, }, { desc: "TreeType", updatefn: func(tree *trillian.Tree) { tree.TreeType = trillian.TreeType_PREORDERED_LOG }, wantErr: true, }, { desc: "TreeTypeFromPreorderedLogToLog", treeType: trillian.TreeType_PREORDERED_LOG, updatefn: func(tree *trillian.Tree) { tree.TreeType = trillian.TreeType_LOG }, wantErr: true, }, { desc: "TreeTypeFromFrozenPreorderedLogToLog", treeState: trillian.TreeState_FROZEN, treeType: trillian.TreeType_PREORDERED_LOG, updatefn: func(tree *trillian.Tree) { tree.TreeType = trillian.TreeType_LOG }, }, { desc: "TreeTypeFromFrozenPreorderedLogToActiveLog", treeState: trillian.TreeState_FROZEN, treeType: trillian.TreeType_PREORDERED_LOG, updatefn: func(tree *trillian.Tree) { tree.TreeState = trillian.TreeState_ACTIVE tree.TreeType = trillian.TreeType_LOG }, wantErr: true, }, { desc: "CreateTime", updatefn: func(tree *trillian.Tree) { tree.CreateTime = timestamppb.New(time.Now()) }, wantErr: true, }, { desc: "UpdateTime", updatefn: func(tree *trillian.Tree) { tree.UpdateTime = timestamppb.New(time.Now()) }, wantErr: true, }, { desc: "Deleted", updatefn: func(tree *trillian.Tree) { tree.Deleted = !tree.Deleted }, wantErr: true, }, { desc: "DeleteTime", updatefn: func(tree *trillian.Tree) { tree.DeleteTime = timestamppb.Now() }, wantErr: true, }, } for _, test := range tests { tree := newTree() if test.treeType != trillian.TreeType_UNKNOWN_TREE_TYPE { tree.TreeType = test.treeType } if test.treeState != trillian.TreeState_UNKNOWN_TREE_STATE { tree.TreeState = test.treeState } baseTree := proto.Clone(tree).(*trillian.Tree) test.updatefn(tree) err := ValidateTreeForUpdate(ctx, baseTree, tree) switch hasErr := err != nil; { case hasErr != test.wantErr: t.Errorf("%v: ValidateTreeForUpdate() = %v, wantErr = %v", test.desc, err, test.wantErr) case hasErr && status.Code(err) != codes.InvalidArgument: t.Errorf("%v: ValidateTreeForUpdate() = %v, wantCode = %d", test.desc, err, codes.InvalidArgument) } } } // newTree returns a valid log tree for tests. func newTree() *trillian.Tree { return &trillian.Tree{ TreeState: trillian.TreeState_ACTIVE, TreeType: trillian.TreeType_LOG, DisplayName: "Llamas Log", Description: "Registry of publicly-owned llamas", MaxRootDuration: durationpb.New(1000 * time.Millisecond), } } trillian-1.6.1/testdata/000077500000000000000000000000001466362047600151645ustar00rootroot00000000000000trillian-1.6.1/testdata/Makefile000066400000000000000000000016571466362047600166350ustar00rootroot00000000000000all: pubkeys # The following private keys are never regenerated. SERVER_PRIVKEYS=log-rpc-server.privkey.pem map-rpc-server.privkey.pem # Corresponding passwords: LOG_RPC_PWD=towel MAP_RPC_PWD=towel # Server public keys are derived from the corresponding private keys. SERVER_PUBKEYS=$(subst .privkey,.pubkey,$(SERVER_PRIVKEYS)) # Build public keys from private keys pubkeys: $(SERVER_PUBKEYS) map-rpc-server.pubkey.pem: map-rpc-server.privkey.pem openssl ec -in $< -pubout -out $@ -passin pass:$(MAP_RPC_PWD) log-rpc-server.pubkey.pem: log-rpc-server.privkey.pem openssl ec -in $< -pubout -out $@ -passin pass:$(LOG_RPC_PWD) clean: rm -f $(SERVER_PUBKEYS) # The newkey target creates a fresh private key; should never be needed. newkey: fresh.privkey.pem fresh.privkey.pem: openssl ecparam -genkey -name prime256v1 -noout -out $@.unencrypted openssl ec -in $@.unencrypted -out $@ -des # Prompts for password rm -f $@.unencrypted trillian-1.6.1/testdata/README.md000066400000000000000000000012431466362047600164430ustar00rootroot00000000000000Directory Contents ================== This directory holds data files for testing; **under no circumstances should these files be used in production**. Some of the data files are generated from other data files; the [`Makefile`](Makefile) has commands for doing this, but the generated files are checked in for convenience. Trillian Server Keys -------------------- Files of the form `*-server.privkey.pem` hold private keys for Trillian servers, with the corresponding public keys stored in `*-server.pubkey.pem`. The following sets of files are available: - `log-rpc-server`: Log RPC server; password `towel`. - `map-rpc-server`: Map RPC server; password `towel`. trillian-1.6.1/testdata/log-rpc-server-pkcs11.privkey.pem000066400000000000000000000016241466362047600233310ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALv+1bCpRdklo1+e m+eqJp40ySeEq6wKzICJ2sfG+4v63nhcUIcnCwHfAiVBniYzBvyYL8pFiEKoQIES gIB/9vSbeP8BK4Q8vJg6vP9oSzfA/TYhlAsaaKGaMyTtP+i40K5fvLV9ONfcZ5VD TpT8R4/BkULFOhzo1YGuLQ3dVWyzAgMBAAECgYEAiYSMREJOud1yeMvJN9mbjjcG vkWqXps3pP8ybtj7wRWuD1VYAsVoo+HB7Abn7QF+pt+cddYPKtNw+/nQzi7NMjhW NwIFcEEmOSpSVw25MAM+zpuElYVO2lO6Ia4NHJZuqiJwx+Wi3APFihl+W1tlTgSH BKX8rjKeK/iBWFE4MIECQQDff325M8quRzRHCnVpIZRkv9ylSRgmUe2UCSo/7B4G Qn4QYUj9+V9EWdoZ0KTGMdP+gNXezUr7/l6OVAuX+V2DAkEA11WiY05eziQU8DJv gowDXxDC4oa1eBzxOmh+FUAVcEzzv5aqAETouFd6MKUmmx1EF+EbTKWoeBWfcmWu gSo9EQJAB+3I8woQVbB2/VI0qh8j2ZMo68A1GB7t88ODeTMSOZygRkp9QW1YYN9j KmlJfzKDV9MQQ1A7KcdzaU83/hM9qwJAV3SPLFfzBzP+UQOa6GFnKSAMlqXxY+QV UQyJUKYjTuXiBUjyqINUJTJaDRG2GT+MnUoupTWOs57MkFCdzBXUkQJAetn5wutd S/VBEKndWeEiC5fFBN60P2HRmQBKxs4ZLl0ASJayN8u249hlT25bUhlJUZxmcmBj rPtdVGQSPWCzKQ== -----END PRIVATE KEY----- trillian-1.6.1/testdata/log-rpc-server-pkcs11.pubkey.pem000066400000000000000000000004201466362047600231300ustar00rootroot00000000000000-----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC7/tWwqUXZJaNfnpvnqiaeNMkn hKusCsyAidrHxvuL+t54XFCHJwsB3wIlQZ4mMwb8mC/KRYhCqECBEoCAf/b0m3j/ ASuEPLyYOrz/aEs3wP02IZQLGmihmjMk7T/ouNCuX7y1fTjX3GeVQ06U/EePwZFC xToc6NWBri0N3VVsswIDAQAB -----END PUBLIC KEY----- trillian-1.6.1/testdata/log-rpc-server.privkey.pem000066400000000000000000000006511466362047600222300ustar00rootroot00000000000000# This key is for test purposes only and must not be used for # production under any circumstances. The key password is # 'towel' -----BEGIN EC PRIVATE KEY----- Proc-Type: 4,ENCRYPTED DEK-Info: DES-CBC,D95ECC664FF4BDEC Xy3zzHFwlFwjE8L1NCngJAFbu3zFf4IbBOCsz6Fa790utVNdulZncNCl2FMK3U2T sdoiTW8ymO+qgwcNrqvPVmjFRBtkN0Pn5lgbWhN/aK3TlS9IYJ/EShbMUzjgVzie S9+/31whWcH/FLeLJx4cBzvhgCtfquwA+s5ojeLYYsk= -----END EC PRIVATE KEY----- trillian-1.6.1/testdata/log-rpc-server.pubkey.pem000066400000000000000000000002621466362047600220340ustar00rootroot00000000000000-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEywnWicNEQ8bn3GXcGpA+tiU4VL70 Ws9xezgQPrg96YGsFrF6KYG68iqyHDlQ+4FWuKfGKXHn3ooVtB/pfawb5Q== -----END PUBLIC KEY----- trillian-1.6.1/testdata/map-rpc-server.privkey.pem000066400000000000000000000006561466362047600222310ustar00rootroot00000000000000# This key is for test purposes only and must not be used for # production under any circumstances. The key password is # 'towel' -----BEGIN EC PRIVATE KEY----- Proc-Type: 4,ENCRYPTED DEK-Info: DES-EDE3-CBC,796E67895D90AE50 eG4d+n9ApGwRXzIexusU63n4jqNzaYmDcvociaopfEJxslEQHR1IbpSAYUPrz64i CJGMfD7r3gsIk/qh0/HlOtX9IacfLvWivy13cU+EOxIUx37GVGOzwytN4umUxERT TN9t+pCsFLfhEpoGrIsOcmf654xPkWSWpOJVjJl0UxI= -----END EC PRIVATE KEY----- trillian-1.6.1/testdata/map-rpc-server.pubkey.pem000066400000000000000000000002621466362047600220300ustar00rootroot00000000000000-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE4NnK7W1bxddfR/49j1QnVHhbfiin ScaprQIdjQY6lx+2eCgTpbcICFudIC2OsggM8jwCCWeR85aUSY5xTFv5Sg== -----END PUBLIC KEY----- trillian-1.6.1/testdata/pkcs11-conf.json000066400000000000000000000001611466362047600201020ustar00rootroot00000000000000{ "tokenLabel": "log", "pin": "1234", "publicKeyPath": "testdata/log-rpc-server-pkcs11.pubkey.pem" } trillian-1.6.1/testonly/000077500000000000000000000000001466362047600152345ustar00rootroot00000000000000trillian-1.6.1/testonly/doc.go000066400000000000000000000020721466362047600163310ustar00rootroot00000000000000// 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 testonly contains code and data that should only be used by tests. Production code MUST NOT depend on anything in this package. This will be enforced by tools where possible. As an example PEM encoded test certificates and helper functions to decode them are suitable candidates for being placed in testonly. However, nothing specific to a particular application should be added at this level. Do not add CT specific test data for example. */ package testonly trillian-1.6.1/testonly/encoding.go000066400000000000000000000025731466362047600173600ustar00rootroot00000000000000// 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 testonly import ( "encoding/base64" "encoding/hex" "time" "google.golang.org/protobuf/types/known/timestamppb" ) // MustDecodeBase64 expects a base 64 encoded string input and panics if it cannot be decoded func MustDecodeBase64(b64 string) []byte { r, err := base64.StdEncoding.DecodeString(b64) if err != nil { panic(r) } return r } // MustHexDecode decodes its input string from hex and panics if this fails func MustHexDecode(b string) []byte { r, err := hex.DecodeString(b) if err != nil { panic(err) } return r } // MustToTimestampProto converts t to a Timestamp protobuf, or panics if this fails. func MustToTimestampProto(t time.Time) *timestamppb.Timestamp { ts := timestamppb.New(t) if err := ts.CheckValid(); err != nil { panic(err) } return ts } trillian-1.6.1/testonly/errors.go000066400000000000000000000020161466362047600170760ustar00rootroot00000000000000// 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 testonly import ( "strings" "testing" ) // EnsureErrorContains checks that an error contains a specific substring and fails a // test with a fatal if it does not or the error was nil. func EnsureErrorContains(t *testing.T, err error, s string) { if err == nil { t.Fatalf("%s operation unexpectedly succeeded", s) } if !strings.Contains(err.Error(), s) { t.Fatalf("Got the wrong type of error: %v", err) } } trillian-1.6.1/testonly/flagsaver/000077500000000000000000000000001466362047600172065ustar00rootroot00000000000000trillian-1.6.1/testonly/flagsaver/flagsaver.go000066400000000000000000000042401466362047600215070ustar00rootroot00000000000000// 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 flagsaver provides a simple way to save and restore flag values. // TODO(RJPercival): Move this to its own GitHub project. // // Example: // // func TestFoo(t *testing.T) { // defer flagsaver.Save().Restore() // // Test code that changes flags // } // flags are reset to their original values here. package flagsaver import ( "flag" "strings" "k8s.io/klog/v2" ) // Stash holds flag values so that they can be restored at the end of a test. type Stash struct { flags map[string]string } // Restore sets all non-hidden flags to the values they had when the Stash was created. func (s *Stash) Restore() error { for name, value := range s.flags { if err := flag.Set(name, value); err != nil { return err } } return nil } // Save returns a Stash that captures the current value of all non-hidden flags. func Save() *Stash { s := Stash{ flags: make(map[string]string, flag.NFlag()), } // Exclude the go test related flags. Also exclude log_backtrace_at because // while it may have an empty value it can't be set to one without an // error. flag.VisitAll(func(f *flag.Flag) { if !strings.HasPrefix(f.Name, "test.") && f.Name != "log_backtrace_at" { s.flags[f.Name] = f.Value.String() } }) return &s } // MustRestore calls Restore and exits on failure. It can be used in a defer for // tests. If Restore fails then the flags may be in an arbitrary // state that could cause subsequent tests to misbehave. func (s *Stash) MustRestore() { if err := s.Restore(); err != nil { klog.Fatalf("MustRestore(): failed to restore flags: %v %v", err, *s) } } trillian-1.6.1/testonly/flagsaver/flagsaver_test.go000066400000000000000000000063641466362047600225570ustar00rootroot00000000000000// 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 flagsaver import ( "flag" "testing" "time" _ "k8s.io/klog/v2" ) var ( _ = flag.Int("int_flag", 123, "test integer flag") _ = flag.String("str_flag", "foo", "test string flag") _ = flag.Duration("duration_flag", 5*time.Second, "test duration flag") ) // TestRestore checks that flags are saved and restore correctly. // Checks are performed on flags with both their default values and with explicit values set. // Only a subset of the possible flag types are currently tested. func TestRestore(t *testing.T) { tests := []struct { desc string // flag is the name of the flag to save and restore. flag string // oldValue is the value the flag should have when saved. If empty, this indicates the flag should have its default value. oldValue string // newValue is the value the flag should have just before being restored to oldValue. newValue string }{ { desc: "RestoreDefaultIntValue", flag: "int_flag", newValue: "666", }, { desc: "RestoreDefaultStrValue", flag: "str_flag", newValue: "baz", }, { desc: "RestoreDefaultDurationValue", flag: "duration_flag", newValue: "1m0s", }, { desc: "RestoreSetIntValue", flag: "int_flag", oldValue: "555", newValue: "666", }, { desc: "RestoreSetStrValue", flag: "str_flag", oldValue: "bar", newValue: "baz", }, { desc: "RestoreSetDurationValue", flag: "duration_flag", oldValue: "10s", newValue: "1m0s", }, } for _, test := range tests { f := flag.Lookup(test.flag) if f == nil { t.Errorf("%v: flag.Lookup(%q) = nil, want not nil", test.desc, test.flag) continue } if test.oldValue != "" { if err := flag.Set(test.flag, test.oldValue); err != nil { t.Errorf("%v: flag.Set(%q, %q) = %q, want nil", test.desc, test.flag, test.oldValue, err) continue } } else { // Use the default value of the flag as the oldValue if none was set. test.oldValue = f.DefValue } func() { defer Save().MustRestore() // If the Set() fails the value won't have been updated but some of the // test cases set the same value so it's safer to have this check. if err := flag.Set(test.flag, test.newValue); err != nil { t.Errorf("%v: flag.Set(%q) = %q, want nil", test.desc, test.flag, err) } if gotValue := f.Value.String(); gotValue != test.newValue { t.Errorf("%v: flag.Lookup(%q).Value.String() = %q, want %q", test.desc, test.flag, gotValue, test.newValue) } }() if gotValue := f.Value.String(); gotValue != test.oldValue { t.Errorf("%v: flag.Lookup(%q).Value.String() = %q, want %q", test.desc, test.flag, gotValue, test.oldValue) } } } trillian-1.6.1/testonly/hasher.go000066400000000000000000000027061466362047600170420ustar00rootroot00000000000000// 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 testonly // This file implements the hashing functions that are part of a Trillian // personality. import "crypto/sha256" // HashKey converts a map key into a map index using SHA256. // This preserves tests that precomputed indexes based on SHA256. func HashKey(key string) []byte { h := sha256.New() h.Write([]byte(key)) return h.Sum(nil) } // TransparentHash returns a key that can be visually inspected. // This supports testing where it was nice to see what the key was. func TransparentHash(key string) []byte { const prefixLen = 8 if prefixLen+len(key) > sha256.Size { panic("key too long") } b := make([]byte, sha256.Size) // Put some hashed bytes before the key so that key values are // a bit more widely distributed around the Merkle tree. h := HashKey(key) copy(b[0:prefixLen], h[0:prefixLen]) copy(b[prefixLen:], key) return b } trillian-1.6.1/testonly/integration/000077500000000000000000000000001466362047600175575ustar00rootroot00000000000000trillian-1.6.1/testonly/integration/etcd/000077500000000000000000000000001466362047600204765ustar00rootroot00000000000000trillian-1.6.1/testonly/integration/etcd/etcd.go000066400000000000000000000060261466362047600217500ustar00rootroot00000000000000// 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 etcd contains a helper to start an embedded etcd server. package etcd import ( "errors" "fmt" "net" "net/url" "os" "strings" "time" clientv3 "go.etcd.io/etcd/client/v3" "go.etcd.io/etcd/server/v3/embed" ) const ( // MaxEtcdStartAttempts is the max number of start attempts made before it fails. MaxEtcdStartAttempts = 3 defaultTimeout = 5 * time.Second tempDirPrefix = "etcdquota-test-" ) // StartEtcd returns a started, ready to use embedded etcd, along with a client and a cleanup // function (that must be defer-called). There's no need to defer-close etcd of client, cleanup // closes both. // // A temp directory and random ports are used to setup etcd. func StartEtcd() (e *embed.Etcd, c *clientv3.Client, cleanup func(), err error) { var dir string dir, err = os.MkdirTemp("", tempDirPrefix) if err != nil { return } cleanup = func() { if c != nil { _ = c.Close() } if e != nil { e.Close() } _ = os.RemoveAll(dir) } for i := 0; i < MaxEtcdStartAttempts; i++ { e, err = tryStartEtcd(dir) if err == nil { break } if strings.Contains(err.Error(), "address already in use") { continue } cleanup() return } if e == nil { cleanup() err = errors.New("failed to start etcd: too many attempts") return } select { case <-e.Server.ReadyNotify(): // OK case <-time.After(defaultTimeout): cleanup() err = errors.New("timed out waiting for etcd to start") return } c, err = clientv3.New(clientv3.Config{ Endpoints: []string{e.Config().ListenClientUrls[0].String()}, DialTimeout: defaultTimeout, }) if err != nil { cleanup() } return } func tryStartEtcd(dir string) (*embed.Etcd, error) { p1, err := net.Listen("tcp", "localhost:0") if err != nil { return nil, err } if err := p1.Close(); err != nil { return nil, err } p2, err := net.Listen("tcp", "localhost:0") if err != nil { return nil, err } if err := p2.Close(); err != nil { return nil, err } // OK to ignore err, it'll error below if parsing fails clientURL, _ := url.Parse("http://" + p1.Addr().String()) peerURL, _ := url.Parse("http://" + p2.Addr().String()) cfg := embed.NewConfig() cfg.Dir = dir cfg.ListenClientUrls = []url.URL{*clientURL} cfg.AdvertiseClientUrls = []url.URL{*clientURL} cfg.ListenPeerUrls = []url.URL{*peerURL} cfg.AdvertisePeerUrls = []url.URL{*peerURL} cfg.InitialCluster = fmt.Sprintf("default=%v", peerURL) cfg.Logger = "zap" return embed.StartEtcd(cfg) } trillian-1.6.1/testonly/integration/logenv.go000066400000000000000000000154241466362047600214060ustar00rootroot00000000000000// 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 integration provides test-only code for performing integrated // tests of Trillian functionality. package integration import ( "context" "database/sql" "net" "sync" "time" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "k8s.io/klog/v2" "github.com/google/trillian" "github.com/google/trillian/extension" "github.com/google/trillian/log" "github.com/google/trillian/quota" "github.com/google/trillian/server" "github.com/google/trillian/server/admin" "github.com/google/trillian/server/interceptor" "github.com/google/trillian/storage/mysql" "github.com/google/trillian/storage/testdb" "github.com/google/trillian/util/clock" _ "github.com/go-sql-driver/mysql" // Load MySQL driver ) var ( sequencerWindow = time.Duration(0) batchSize = 50 // SequencerInterval is the time between runs of the sequencer. SequencerInterval = 500 * time.Millisecond timeSource = clock.System ) // LogEnv is a test environment that contains both a log server and a connection to it. type LogEnv struct { registry extension.Registry pendingTasks *sync.WaitGroup grpcServer *grpc.Server adminServer *admin.Server logServer *server.TrillianLogRPCServer LogOperation log.Operation Sequencer *log.OperationManager sequencerCancel context.CancelFunc ClientConn *grpc.ClientConn // TODO(gbelvin): Deprecate. Address string Log trillian.TrillianLogClient Admin trillian.TrillianAdminClient DB *sql.DB dbDone func(context.Context) } // NewLogEnv creates a fresh DB, log server, and client. The numSequencers parameter // indicates how many sequencers to run in parallel; if numSequencers is zero a // manually-controlled test sequencer is used. // // Deprecated: Use NewLogEnvWithGRPCOptions instead // // TODO(Martin2112): Remove this constructor, it is only used by tests and // can be replaced by one of the others. func NewLogEnv(ctx context.Context, numSequencers int, _ string) (*LogEnv, error) { return NewLogEnvWithGRPCOptions(ctx, numSequencers, nil, nil) } // NewLogEnvWithGRPCOptions creates a fresh DB, log server, and client. The // numSequencers parameter indicates how many sequencers to run in parallel; // if numSequencers is zero a manually-controlled test sequencer is used. // Additional grpc.ServerOption and grpc.DialOption values can be provided. func NewLogEnvWithGRPCOptions(ctx context.Context, numSequencers int, serverOpts []grpc.ServerOption, clientOpts []grpc.DialOption) (*LogEnv, error) { // TODO(jaosorior): Make this configurable for Cockroach or MySQL db, done, err := testdb.NewTrillianDB(ctx, testdb.DriverMySQL) if err != nil { return nil, err } registry := extension.Registry{ AdminStorage: mysql.NewAdminStorage(db), LogStorage: mysql.NewLogStorage(db, nil), QuotaManager: quota.Noop(), } ret, err := NewLogEnvWithRegistryAndGRPCOptions(ctx, numSequencers, registry, serverOpts, clientOpts) if err != nil { if err := db.Close(); err != nil { return nil, err } return nil, err } ret.DB = db ret.dbDone = done return ret, nil } // NewLogEnvWithRegistry uses the passed in Registry to create a log server, // and client. The numSequencers parameter indicates how many sequencers to // run in parallel; if numSequencers is zero a manually-controlled test // sequencer is used. func NewLogEnvWithRegistry(ctx context.Context, numSequencers int, registry extension.Registry) (*LogEnv, error) { return NewLogEnvWithRegistryAndGRPCOptions(ctx, numSequencers, registry, nil, nil) } // NewLogEnvWithRegistryAndGRPCOptions works the same way as NewLogEnv, but allows callers to also set additional grpc.ServerOption and grpc.DialOption values. func NewLogEnvWithRegistryAndGRPCOptions(ctx context.Context, numSequencers int, registry extension.Registry, serverOpts []grpc.ServerOption, clientOpts []grpc.DialOption) (*LogEnv, error) { // Create the GRPC Server. serverOpts = append(serverOpts, grpc.UnaryInterceptor(interceptor.ErrorWrapper)) grpcServer := grpc.NewServer(serverOpts...) // Setup the Admin Server. adminServer := admin.New(registry, nil) trillian.RegisterTrillianAdminServer(grpcServer, adminServer) // Setup the Log Server. logServer := server.NewTrillianLogRPCServer(registry, timeSource) trillian.RegisterTrillianLogServer(grpcServer, logServer) // Create Sequencer. sequencerManager := log.NewSequencerManager(registry, sequencerWindow) var wg sync.WaitGroup var sequencerTask *log.OperationManager ctx, cancel := context.WithCancel(ctx) info := log.OperationInfo{ Registry: registry, BatchSize: batchSize, NumWorkers: numSequencers, RunInterval: SequencerInterval, TimeSource: timeSource, } // Start a live sequencer in a goroutine. sequencerTask = log.NewOperationManager(info, sequencerManager) wg.Add(1) go func(wg *sync.WaitGroup, om *log.OperationManager) { defer wg.Done() om.OperationLoop(ctx) }(&wg, sequencerTask) // Listen and start server. addr, lis, err := listen() if err != nil { cancel() return nil, err } wg.Add(1) go func(wg *sync.WaitGroup, grpcServer *grpc.Server, lis net.Listener) { defer wg.Done() if err := grpcServer.Serve(lis); err != nil { klog.Errorf("gRPC server stopped: %v", err) klog.Flush() } }(&wg, grpcServer, lis) // Connect to the server. if clientOpts == nil { clientOpts = []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())} } cc, err := grpc.Dial(addr, clientOpts...) if err != nil { cancel() return nil, err } return &LogEnv{ registry: registry, pendingTasks: &wg, grpcServer: grpcServer, adminServer: adminServer, logServer: logServer, Address: addr, ClientConn: cc, Log: trillian.NewTrillianLogClient(cc), Admin: trillian.NewTrillianAdminClient(cc), LogOperation: sequencerManager, Sequencer: sequencerTask, sequencerCancel: cancel, }, nil } // Close shuts down the server. func (env *LogEnv) Close() { if env.sequencerCancel != nil { env.sequencerCancel() } if err := env.ClientConn.Close(); err != nil { klog.Errorf("ClientConn.Close(): %v", err) } env.grpcServer.GracefulStop() env.pendingTasks.Wait() if env.dbDone != nil { env.dbDone(context.TODO()) } } trillian-1.6.1/testonly/integration/port.go000066400000000000000000000021401466362047600210670ustar00rootroot00000000000000// 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 integration import ( "fmt" "net" ) // listen opens a random high numbered port for listening. func listen() (string, net.Listener, error) { lis, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { return "", nil, fmt.Errorf("failed to listen: %v", err) } _, port, err := net.SplitHostPort(lis.Addr().String()) if err != nil { return "", nil, fmt.Errorf("unrecognized format for listen address %v: %v", lis.Addr().String(), err) } addr := "localhost:" + port return addr, lis, nil } trillian-1.6.1/testonly/integration/registry.go000066400000000000000000000027171466362047600217650ustar00rootroot00000000000000// 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 integration import ( "context" "github.com/google/trillian/extension" "github.com/google/trillian/quota/mysqlqm" "github.com/google/trillian/storage/mysql" "github.com/google/trillian/storage/testdb" ) // NewRegistryForTests returns an extension.Registry for integration tests. // Callers should call the returned cleanup function when they're finished // with the registry and its contents. func NewRegistryForTests(ctx context.Context, driver testdb.DriverName) (extension.Registry, func(context.Context), error) { db, done, err := testdb.NewTrillianDB(ctx, driver) if err != nil { return extension.Registry{}, nil, err } return extension.Registry{ AdminStorage: mysql.NewAdminStorage(db), LogStorage: mysql.NewLogStorage(db, nil), QuotaManager: &mysqlqm.QuotaManager{DB: db, MaxUnsequencedRows: mysqlqm.DefaultMaxUnsequenced}, }, done, nil } trillian-1.6.1/testonly/keys.go000066400000000000000000000027431466362047600165440ustar00rootroot00000000000000// 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 testonly // KEYS IN THIS FILE ARE ONLY FOR TESTING. They must not be used by production code. // DemoPrivateKeyPass is the password for DemoPrivateKey const DemoPrivateKeyPass string = "towel" // DemoPrivateKey is the private key itself; must only be used for testing purposes const DemoPrivateKey string = ` -----BEGIN EC PRIVATE KEY----- Proc-Type: 4,ENCRYPTED DEK-Info: DES-CBC,B71ECAB011EB4E8F +6cz455aVRHFX5UsxplyGvFXMcmuMH0My/nOWNmYCL+bX2PnHdsv3dRgpgPRHTWt IPI6kVHv0g2uV5zW8nRqacmikBFA40CIKp0SjRmi1CtfchzuqXQ3q40rFwCjeuiz t48+aoeFsfU6NnL5sP8mbFlPze+o7lovgAWEqHEcebU= -----END EC PRIVATE KEY-----` // DemoPublicKey is the public key that corresponds to DemoPrivateKey. const DemoPublicKey string = ` -----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEsAVg3YB0tOFf3DdC2YHPL2WiuCNR 1iywqGjjtu2dAdWktWqgRO4NTqPJXUggSQL3nvOupHB4WZFZ4j3QhtmWRg== -----END PUBLIC KEY-----` trillian-1.6.1/testonly/marshalling.go000066400000000000000000000030571466362047600200710ustar00rootroot00000000000000// 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 testonly import ( "log" "testing" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/anypb" ) // MustMarshalAnyNoT is used to Marshal proto messages into the // protobuf.ptypes.anypb.Any used throughout the Trillian API and in // storage. Use if testing.T not available. Failure to marshal will // fail the test suite. func MustMarshalAnyNoT(in proto.Message) []byte { protoBytes, err := proto.Marshal(in) if err != nil { log.Fatalf("failed to marshal %v as 'bytes': err %v", in, err) } return protoBytes } // MustMarshalAny is used in tests to Marshal proto messages into the // protobuf.ptypes.anypb.Any used in the Trillian API and in storage. // Failure to marshal will fail the test but the suite will continue. func MustMarshalAny(t *testing.T, in proto.Message) *anypb.Any { t.Helper() anything, err := anypb.New(in) if err != nil { t.Fatalf("failed to marshal %v as 'any': err %v", in, err) } return anything } trillian-1.6.1/testonly/matchers/000077500000000000000000000000001466362047600170425ustar00rootroot00000000000000trillian-1.6.1/testonly/matchers/atleast.go000066400000000000000000000023611466362047600210300ustar00rootroot00000000000000// 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 matchers contains additional gomock matchers. package matchers import ( "fmt" "github.com/golang/mock/gomock" ) // AtLeast returns a matcher that requires a number >= n. func AtLeast(n int) gomock.Matcher { return &atLeastMatcher{n} } type atLeastMatcher struct { num int } // Matches tests whether a supplied value, which must be of an int type is // at least the value the AtLeast matcher expects. If so then it returns true. func (m atLeastMatcher) Matches(x interface{}) bool { if x, ok := x.(int); ok { return x >= m.num } return false } func (m atLeastMatcher) String() string { return fmt.Sprintf("at least %v", m.num) } trillian-1.6.1/testonly/matchers/atleast_test.go000066400000000000000000000020541466362047600220660ustar00rootroot00000000000000// 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 matchers import ( "testing" _ "k8s.io/klog/v2" ) func TestAtLeast(t *testing.T) { tests := []struct { num, actual int want bool }{ {num: 5, actual: 3}, {num: 5, actual: 5, want: true}, {num: 5, actual: 10, want: true}, } for _, test := range tests { if got := AtLeast(test.num).Matches(test.actual); got != test.want { t.Errorf("AtLeast(%v).Matches(%v) = %v, want = %v", test.num, test.actual, got, test.want) } } } trillian-1.6.1/testonly/matchers/proto.go000066400000000000000000000022431466362047600205350ustar00rootroot00000000000000// 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 matchers import ( "fmt" "github.com/golang/mock/gomock" "google.golang.org/protobuf/proto" ) type protoEqual struct { msg proto.Message } // ProtoEqual returns a matcher that compares protobuf messages. func ProtoEqual(m proto.Message) gomock.Matcher { return protoEqual{msg: m} } // Matches implements the gomock.Matcher API. func (pe protoEqual) Matches(msg interface{}) bool { m, ok := msg.(proto.Message) if !ok { return false } return proto.Equal(m, pe.msg) } func (pe protoEqual) String() string { return fmt.Sprintf("is equal to %s", pe.msg) } trillian-1.6.1/testonly/matchers/proto_test.go000066400000000000000000000024021466362047600215710ustar00rootroot00000000000000// 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 matchers import ( "testing" "github.com/google/trillian" "google.golang.org/protobuf/proto" _ "k8s.io/klog/v2" ) func TestProtoEquals(t *testing.T) { for _, tc := range []struct { a, b proto.Message equal bool }{ {a: &trillian.SignedLogRoot{}, b: &trillian.SignedLogRoot{}, equal: true}, {a: &trillian.SignedLogRoot{}, b: &trillian.SignedLogRoot{LogRoot: []byte{}}, equal: true}, {a: &trillian.SignedLogRoot{}, b: &trillian.SignedLogRoot{LogRoot: []byte{0x01}}, equal: false}, } { if got, want := ProtoEqual(tc.a).Matches(tc.b), tc.equal; got != want { t.Errorf("ProtoEqual(%s).Matches(%s) = %v, want %v", tc.a, tc.b, got, want) } } } trillian-1.6.1/testonly/mdm/000077500000000000000000000000001466362047600160115ustar00rootroot00000000000000trillian-1.6.1/testonly/mdm/mdm.go000066400000000000000000000124501466362047600171170ustar00rootroot00000000000000// 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 mdm provides test-only code for checking the merge delay of a // Trillian log. package mdm import ( "context" crand "crypto/rand" "fmt" "math/rand" "strconv" "sync" "time" "github.com/google/trillian" "github.com/google/trillian/client" "github.com/google/trillian/monitoring" "github.com/google/trillian/types" "k8s.io/klog/v2" ) // MergeDelayMonitor submits leaves to a Log and measures merge delay. type MergeDelayMonitor struct { client []*client.LogClient logID int64 opts MergeDelayOptions } // MergeDelayOptions holds the parameters for a MergeDelayMonitor. type MergeDelayOptions struct { ParallelAdds int LeafSize int NewLeafChance int // percentage EmitInterval time.Duration Deadline time.Duration MinMergeDelay time.Duration MetricFactory monitoring.MetricFactory } // NewMonitor creates a MergeDelayMonitor instance for the given log ID, accessed // via the cl client. func NewMonitor(ctx context.Context, logID int64, cl trillian.TrillianLogClient, adminCl trillian.TrillianAdminClient, opts MergeDelayOptions) (*MergeDelayMonitor, error) { if opts.MetricFactory == nil { opts.MetricFactory = monitoring.InertMetricFactory{} } if opts.EmitInterval <= 0 { opts.EmitInterval = 10 * time.Second } if opts.Deadline <= 0 { opts.Deadline = 60 * time.Second } metricsOnce.Do(func() { initMetrics(opts.MetricFactory) }) tree, err := adminCl.GetTree(ctx, &trillian.GetTreeRequest{TreeId: logID}) if err != nil { return nil, fmt.Errorf("failed to get tree %d: %v", logID, err) } verifier, err := client.NewLogVerifierFromTree(tree) if err != nil { return nil, fmt.Errorf("failed to build verifier: %v", err) } clients := make([]*client.LogClient, opts.ParallelAdds) for i := 0; i < opts.ParallelAdds; i++ { clients[i] = client.New(logID, cl, verifier, types.LogRootV1{}) clients[i].MinMergeDelay = opts.MinMergeDelay } mdm := MergeDelayMonitor{logID: logID, opts: opts, client: clients} return &mdm, nil } // Monitor runs merge delay monitoring until its context is cancelled or an error occurs. func (m *MergeDelayMonitor) Monitor(ctx context.Context) error { klog.Infof("starting %d parallel monitor instances", m.opts.ParallelAdds) errs := make(chan error, m.opts.ParallelAdds) var wg sync.WaitGroup wg.Add(m.opts.ParallelAdds) for i := 0; i < m.opts.ParallelAdds; i++ { go func(i int) { defer wg.Done() if err := m.monitor(ctx, i); err != nil { errs <- err } }(i) } ticker := time.NewTicker(m.opts.EmitInterval) defer ticker.Stop() klog.V(1).Infof("start stats ticker every %v", m.opts.EmitInterval) go func(c <-chan time.Time) { for range c { countT, totalT := m.Stats(true) if countT > 0 { klog.Infof("new leaves: %d in %f secs, average %f secs", countT, totalT, totalT/float64(countT)) } countF, totalF := m.Stats(false) if countF > 0 { klog.Infof("dup leaves: %d in %f secs, average %f secs", countF, totalF, totalF/float64(countF)) } } }(ticker.C) wg.Wait() close(errs) var lastErr error for err := range errs { klog.Errorf("monitor failure: %v", err) lastErr = err } return lastErr } func (m *MergeDelayMonitor) monitor(ctx context.Context, idx int) error { logIDLabel := strconv.FormatInt(m.logID, 10) data := make([]byte, m.opts.LeafSize) createNew := true // Always need a new leaf to start with for { if rand.Intn(100) < m.opts.NewLeafChance { createNew = true } if createNew { if _, err := crand.Read(data); err != nil { return fmt.Errorf("Read(): %v", err) } } // Add the leaf data and wait for its inclusion. klog.V(1).Infof("[%d] submit new=%t leaf and wait for inclusion (within %v)", idx, createNew, m.opts.Deadline) cctx, cancel := context.WithTimeout(ctx, m.opts.Deadline) start := time.Now() err := m.client[idx].AddLeaf(cctx, data) cancel() if err != nil { return fmt.Errorf("failed to QueueLeaf: %v", err) } mergeDelay := time.Since(start) mergeDelayDist.Observe(mergeDelay.Seconds(), logIDLabel, newLeafLabel[createNew]) klog.V(1).Infof("[%d] merge delay for new=%t leaf = %v", idx, createNew, mergeDelay) select { case <-ctx.Done(): return nil default: } createNew = false } } // Stats returns the total count of requests and the total elapsed time across // all invocations. func (m *MergeDelayMonitor) Stats(newLeaf bool) (uint64, float64) { logIDLabel := strconv.FormatInt(m.logID, 10) return mergeDelayDist.Info(logIDLabel, newLeafLabel[newLeaf]) } var ( metricsOnce sync.Once mergeDelayDist monitoring.Histogram newLeafLabel = map[bool]string{true: "true", false: "false"} ) func initMetrics(mf monitoring.MetricFactory) { mergeDelayDist = mf.NewHistogram("merge_delay", "Merge delay for submitted leaves", "log_id", "new_leaf") } trillian-1.6.1/testonly/mdm/mdmtest/000077500000000000000000000000001466362047600174665ustar00rootroot00000000000000trillian-1.6.1/testonly/mdm/mdmtest/main.go000066400000000000000000000113671466362047600207510ustar00rootroot00000000000000// 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. // The mdmtest binary runs merge delay tests against a Trillian Log. package main import ( "context" "flag" "fmt" "net/http" "time" "github.com/google/trillian" "github.com/google/trillian/client" "github.com/google/trillian/monitoring" "github.com/google/trillian/monitoring/prometheus" "github.com/google/trillian/testonly/mdm" "github.com/google/trillian/util" "github.com/prometheus/client_golang/prometheus/promhttp" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "google.golang.org/protobuf/types/known/durationpb" "k8s.io/klog/v2" ) var ( logID = flag.Int64("log_id", 0, "Log ID to test against; ephemeral tree used if zero") rpcServer = flag.String("rpc_server", "", "Trillian log server address:port") adminServer = flag.String("admin_server", "", "Trillian admin server address:port (defaults to --rpc_server value)") metricsEndpoint = flag.String("metrics_endpoint", "", "Endpoint for serving metrics; if left empty, metrics will not be exposed") leafSize = flag.Uint("leaf_size", 500, "Size of leaf values") newLeafChance = flag.Uint("new_leaf_chance", 50, "Percentage chance of using a new leaf for each submission") checkers = flag.Int("checkers", 3, "Number of parallel checker goroutines to run") emitInterval = flag.Duration("emit_interval", 10*time.Second, "How often to output the summary info") deadline = flag.Duration("deadline", 60*time.Second, "Deadline for single add+get-proof operation") minMergeDelay = flag.Duration("min_merge_delay", 3*time.Second, "Minimum merge delay; don't check for inclusion until this interval has passed") ) func main() { flag.Parse() ctx := context.Background() if err := innerMain(ctx); err != nil { klog.Exit(err) } } func innerMain(ctx context.Context) error { var mf monitoring.MetricFactory if *metricsEndpoint != "" { mf = prometheus.MetricFactory{} http.Handle("/metrics", promhttp.Handler()) server := http.Server{Addr: *metricsEndpoint, Handler: nil} klog.Infof("Serving metrics at %v", *metricsEndpoint) go func() { err := server.ListenAndServe() klog.Warningf("Metrics server exited: %v", err) }() } dialOpts := []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())} c, err := grpc.Dial(*rpcServer, dialOpts...) if err != nil { klog.Exitf("Failed to create log client conn: %v", err) } cl := trillian.NewTrillianLogClient(c) ac := c if len(*adminServer) > 0 { ac, err = grpc.Dial(*adminServer, dialOpts...) if err != nil { klog.Exitf("Failed to create admin client conn: %v", err) } } adminCl := trillian.NewTrillianAdminClient(ac) if *logID <= 0 { // No logID provided, so create an ephemeral tree to test against. req := trillian.CreateTreeRequest{ Tree: &trillian.Tree{ TreeState: trillian.TreeState_ACTIVE, TreeType: trillian.TreeType_LOG, DisplayName: fmt.Sprintf("mdmtest-%d", time.Now().UnixNano()/int64(time.Second)), Description: "Transient tree for mdmtest", MaxRootDuration: durationpb.New(time.Second * 3600), }, } tree, err := client.CreateAndInitTree(ctx, &req, adminCl, cl) if err != nil { klog.Exitf("failed to create ephemeral tree: %v", err) } *logID = tree.TreeId klog.Infof("testing against ephemeral tree %d", *logID) defer func() { req := &trillian.DeleteTreeRequest{TreeId: *logID} klog.Infof("Soft-delete transient Trillian Log with TreeID=%d", *logID) if _, err := adminCl.DeleteTree(ctx, req); err != nil { klog.Errorf("failed to DeleteTree(%d): %v", *logID, err) } }() } opts := mdm.MergeDelayOptions{ ParallelAdds: *checkers, LeafSize: int(*leafSize), NewLeafChance: int(*newLeafChance), EmitInterval: *emitInterval, Deadline: *deadline, MinMergeDelay: *minMergeDelay, MetricFactory: mf, } monitor, err := mdm.NewMonitor(ctx, *logID, cl, adminCl, opts) if err != nil { return fmt.Errorf("failed to build merge delay monitor: %v", err) } cctx, cancel := context.WithCancel(ctx) defer cancel() go util.AwaitSignal(ctx, cancel) if err := monitor.Monitor(cctx); err != nil { return fmt.Errorf("merge delay monitoring failed: %v", err) } return nil } trillian-1.6.1/testonly/mock_server.go000066400000000000000000000043571466362047600201130ustar00rootroot00000000000000// Copyright 2018 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 ( "net" "github.com/golang/mock/gomock" "github.com/google/trillian" "github.com/google/trillian/testonly/tmock" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" ) // MockServer implements the TrillianAdminServer, and TrillianLogServer. type MockServer struct { Admin *tmock.MockTrillianAdminServer Log *tmock.MockTrillianLogServer AdminClient trillian.TrillianAdminClient LogClient trillian.TrillianLogClient Addr string } // NewMockServer starts a server on a random port. // Returns the started server, the listener it's using for connection and a // close function that must be defer-called on the scope the server is meant to // stop. func NewMockServer(ctrl *gomock.Controller) (*MockServer, func(), error) { grpcServer := grpc.NewServer() logServer := tmock.NewMockTrillianLogServer(ctrl) adminServer := tmock.NewMockTrillianAdminServer(ctrl) trillian.RegisterTrillianLogServer(grpcServer, logServer) trillian.RegisterTrillianAdminServer(grpcServer, adminServer) lis, err := net.Listen("tcp", ":0") if err != nil { return nil, nil, err } go func() { _ = grpcServer.Serve(lis) }() cc, err := grpc.Dial(lis.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { grpcServer.Stop() _ = lis.Close() return nil, nil, err } stopFn := func() { _ = cc.Close() grpcServer.Stop() _ = lis.Close() } return &MockServer{ Log: logServer, Admin: adminServer, LogClient: trillian.NewTrillianLogClient(cc), AdminClient: trillian.NewTrillianAdminClient(cc), Addr: lis.Addr().String(), }, stopFn, nil } trillian-1.6.1/testonly/paths.go000066400000000000000000000023471466362047600167100ustar00rootroot00000000000000// 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 testonly import ( "path" "path/filepath" "runtime" ) // RelativeToPackage returns the input path p as an absolute path, resolved relative to the caller's package. // The working directory for Go tests is the dir of the test file. Using "plain" relative paths in test // utilities is, therefore, brittle, as the directory structure may change depending on where the tests are placed. func RelativeToPackage(p string) string { _, file, _, ok := runtime.Caller(1) if !ok { panic("cannot get caller information") } absPath, err := filepath.Abs(filepath.Join(path.Dir(file), p)) if err != nil { panic(err) } return absPath } trillian-1.6.1/testonly/setup/000077500000000000000000000000001466362047600163745ustar00rootroot00000000000000trillian-1.6.1/testonly/setup/doc.go000066400000000000000000000013031466362047600174650ustar00rootroot00000000000000// Copyright 2022 Trillian Authors. 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 setup contains test-only code that's useful for setting up tests. package setup trillian-1.6.1/testonly/setup/files.go000066400000000000000000000023701466362047600200270ustar00rootroot00000000000000// Copyright 2018 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 setup import ( "os" "testing" ) // TempFile creates a temporary file to be used in a test, and returns its // *os.File handler, as well as a cleanup function. func TempFile(t *testing.T, prefix string) (*os.File, func()) { t.Helper() file, err := os.CreateTemp("", prefix) if err != nil { t.Fatalf("Failed to generate a temporary file: %v", err) } cleanup := func() { if err := os.Remove(file.Name()); err != nil { t.Fatalf("Failed to remove temporary file '%s': %v", file.Name(), err) } if err := file.Close(); err != nil { t.Fatalf("Failed to close temporary file handler '%v': %v", file, err) } } return file, cleanup } trillian-1.6.1/testonly/setup/flags.go000066400000000000000000000016051466362047600200210ustar00rootroot00000000000000// Copyright 2018 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 setup import ( "flag" "testing" ) // SetFlag updates a flag value, failing the test if something goes wrong. func SetFlag(t *testing.T, name, value string) { t.Helper() if err := flag.Set(name, value); err != nil { t.Errorf("failed to set the -%s flag: %v", name, err) } } trillian-1.6.1/testonly/setup/tls.go000066400000000000000000000036131466362047600175300ustar00rootroot00000000000000// Copyright 2018 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 setup import ( "crypto/rand" "crypto/rsa" "crypto/tls" "crypto/x509" "encoding/pem" "math/big" "testing" "time" ) // NewTLSCertificate returns a random TLS Certificate for testing. func NewTLSCertificate(t *testing.T) tls.Certificate { t.Helper() priv, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { t.Errorf("failed to generate RSA key: %s", err) } privBytes := x509.MarshalPKCS1PrivateKey(priv) key := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: privBytes}) // Generate public certificate. serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) if err != nil { t.Errorf("failed to generate serial number: %s", err) } template := x509.Certificate{ SerialNumber: serialNumber, NotBefore: time.Time{}, NotAfter: time.Now().Add(time.Hour), DNSNames: []string{"localhost"}, } pubBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) if err != nil { t.Errorf("failed to generate TLS public certificate: %s", err) } crt := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: pubBytes}) c, err := tls.X509KeyPair(crt, key) if err != nil { t.Errorf("failed to parse the public/private key pair: %s", err) } return c } trillian-1.6.1/testonly/signer.go000066400000000000000000000032231466362047600170520ustar00rootroot00000000000000// 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 testonly import ( "crypto" "io" ) // signerStub returns a fixed signature and error, no matter the input. // It implements crypto.Signer. type signerStub struct { publicKey crypto.PublicKey signature []byte err error } // Public returns the public key associated with the signer that this stub is based on. func (s *signerStub) Public() crypto.PublicKey { return s.publicKey } // Sign will return the signature or error that the signerStub was created to provide. func (s *signerStub) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) { return s.signature, s.err } // NewSignerWithErr creates a signer that always returns err when Sign() is called. func NewSignerWithErr(pubKey crypto.PublicKey, err error) crypto.Signer { return &signerStub{ publicKey: pubKey, err: err, } } // NewSignerWithFixedSig creates a signer that always return sig when Sign() is called. func NewSignerWithFixedSig(pubKey crypto.PublicKey, sig []byte) crypto.Signer { return &signerStub{ publicKey: pubKey, signature: sig, } } trillian-1.6.1/testonly/tmock/000077500000000000000000000000001466362047600163515ustar00rootroot00000000000000trillian-1.6.1/testonly/tmock/mock_admin_server.go000066400000000000000000000115051466362047600223710ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/google/trillian (interfaces: TrillianAdminServer) // Package tmock is a generated GoMock package. package tmock import ( context "context" reflect "reflect" gomock "github.com/golang/mock/gomock" trillian "github.com/google/trillian" ) // MockTrillianAdminServer is a mock of TrillianAdminServer interface. type MockTrillianAdminServer struct { ctrl *gomock.Controller recorder *MockTrillianAdminServerMockRecorder } // MockTrillianAdminServerMockRecorder is the mock recorder for MockTrillianAdminServer. type MockTrillianAdminServerMockRecorder struct { mock *MockTrillianAdminServer } // NewMockTrillianAdminServer creates a new mock instance. func NewMockTrillianAdminServer(ctrl *gomock.Controller) *MockTrillianAdminServer { mock := &MockTrillianAdminServer{ctrl: ctrl} mock.recorder = &MockTrillianAdminServerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockTrillianAdminServer) EXPECT() *MockTrillianAdminServerMockRecorder { return m.recorder } // CreateTree mocks base method. func (m *MockTrillianAdminServer) CreateTree(arg0 context.Context, arg1 *trillian.CreateTreeRequest) (*trillian.Tree, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateTree", arg0, arg1) ret0, _ := ret[0].(*trillian.Tree) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateTree indicates an expected call of CreateTree. func (mr *MockTrillianAdminServerMockRecorder) CreateTree(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateTree", reflect.TypeOf((*MockTrillianAdminServer)(nil).CreateTree), arg0, arg1) } // DeleteTree mocks base method. func (m *MockTrillianAdminServer) DeleteTree(arg0 context.Context, arg1 *trillian.DeleteTreeRequest) (*trillian.Tree, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteTree", arg0, arg1) ret0, _ := ret[0].(*trillian.Tree) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteTree indicates an expected call of DeleteTree. func (mr *MockTrillianAdminServerMockRecorder) DeleteTree(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteTree", reflect.TypeOf((*MockTrillianAdminServer)(nil).DeleteTree), arg0, arg1) } // GetTree mocks base method. func (m *MockTrillianAdminServer) GetTree(arg0 context.Context, arg1 *trillian.GetTreeRequest) (*trillian.Tree, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetTree", arg0, arg1) ret0, _ := ret[0].(*trillian.Tree) ret1, _ := ret[1].(error) return ret0, ret1 } // GetTree indicates an expected call of GetTree. func (mr *MockTrillianAdminServerMockRecorder) GetTree(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTree", reflect.TypeOf((*MockTrillianAdminServer)(nil).GetTree), arg0, arg1) } // ListTrees mocks base method. func (m *MockTrillianAdminServer) ListTrees(arg0 context.Context, arg1 *trillian.ListTreesRequest) (*trillian.ListTreesResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListTrees", arg0, arg1) ret0, _ := ret[0].(*trillian.ListTreesResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // ListTrees indicates an expected call of ListTrees. func (mr *MockTrillianAdminServerMockRecorder) ListTrees(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListTrees", reflect.TypeOf((*MockTrillianAdminServer)(nil).ListTrees), arg0, arg1) } // UndeleteTree mocks base method. func (m *MockTrillianAdminServer) UndeleteTree(arg0 context.Context, arg1 *trillian.UndeleteTreeRequest) (*trillian.Tree, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UndeleteTree", arg0, arg1) ret0, _ := ret[0].(*trillian.Tree) ret1, _ := ret[1].(error) return ret0, ret1 } // UndeleteTree indicates an expected call of UndeleteTree. func (mr *MockTrillianAdminServerMockRecorder) UndeleteTree(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UndeleteTree", reflect.TypeOf((*MockTrillianAdminServer)(nil).UndeleteTree), arg0, arg1) } // UpdateTree mocks base method. func (m *MockTrillianAdminServer) UpdateTree(arg0 context.Context, arg1 *trillian.UpdateTreeRequest) (*trillian.Tree, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateTree", arg0, arg1) ret0, _ := ret[0].(*trillian.Tree) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateTree indicates an expected call of UpdateTree. func (mr *MockTrillianAdminServerMockRecorder) UpdateTree(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateTree", reflect.TypeOf((*MockTrillianAdminServer)(nil).UpdateTree), arg0, arg1) } trillian-1.6.1/testonly/tmock/mock_log_server.go000066400000000000000000000167071466362047600220730ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/google/trillian (interfaces: TrillianLogServer) // Package tmock is a generated GoMock package. package tmock import ( context "context" reflect "reflect" gomock "github.com/golang/mock/gomock" trillian "github.com/google/trillian" ) // MockTrillianLogServer is a mock of TrillianLogServer interface. type MockTrillianLogServer struct { ctrl *gomock.Controller recorder *MockTrillianLogServerMockRecorder } // MockTrillianLogServerMockRecorder is the mock recorder for MockTrillianLogServer. type MockTrillianLogServerMockRecorder struct { mock *MockTrillianLogServer } // NewMockTrillianLogServer creates a new mock instance. func NewMockTrillianLogServer(ctrl *gomock.Controller) *MockTrillianLogServer { mock := &MockTrillianLogServer{ctrl: ctrl} mock.recorder = &MockTrillianLogServerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockTrillianLogServer) EXPECT() *MockTrillianLogServerMockRecorder { return m.recorder } // AddSequencedLeaves mocks base method. func (m *MockTrillianLogServer) AddSequencedLeaves(arg0 context.Context, arg1 *trillian.AddSequencedLeavesRequest) (*trillian.AddSequencedLeavesResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddSequencedLeaves", arg0, arg1) ret0, _ := ret[0].(*trillian.AddSequencedLeavesResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // AddSequencedLeaves indicates an expected call of AddSequencedLeaves. func (mr *MockTrillianLogServerMockRecorder) AddSequencedLeaves(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddSequencedLeaves", reflect.TypeOf((*MockTrillianLogServer)(nil).AddSequencedLeaves), arg0, arg1) } // GetConsistencyProof mocks base method. func (m *MockTrillianLogServer) GetConsistencyProof(arg0 context.Context, arg1 *trillian.GetConsistencyProofRequest) (*trillian.GetConsistencyProofResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetConsistencyProof", arg0, arg1) ret0, _ := ret[0].(*trillian.GetConsistencyProofResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetConsistencyProof indicates an expected call of GetConsistencyProof. func (mr *MockTrillianLogServerMockRecorder) GetConsistencyProof(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetConsistencyProof", reflect.TypeOf((*MockTrillianLogServer)(nil).GetConsistencyProof), arg0, arg1) } // GetEntryAndProof mocks base method. func (m *MockTrillianLogServer) GetEntryAndProof(arg0 context.Context, arg1 *trillian.GetEntryAndProofRequest) (*trillian.GetEntryAndProofResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetEntryAndProof", arg0, arg1) ret0, _ := ret[0].(*trillian.GetEntryAndProofResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetEntryAndProof indicates an expected call of GetEntryAndProof. func (mr *MockTrillianLogServerMockRecorder) GetEntryAndProof(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEntryAndProof", reflect.TypeOf((*MockTrillianLogServer)(nil).GetEntryAndProof), arg0, arg1) } // GetInclusionProof mocks base method. func (m *MockTrillianLogServer) GetInclusionProof(arg0 context.Context, arg1 *trillian.GetInclusionProofRequest) (*trillian.GetInclusionProofResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetInclusionProof", arg0, arg1) ret0, _ := ret[0].(*trillian.GetInclusionProofResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetInclusionProof indicates an expected call of GetInclusionProof. func (mr *MockTrillianLogServerMockRecorder) GetInclusionProof(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetInclusionProof", reflect.TypeOf((*MockTrillianLogServer)(nil).GetInclusionProof), arg0, arg1) } // GetInclusionProofByHash mocks base method. func (m *MockTrillianLogServer) GetInclusionProofByHash(arg0 context.Context, arg1 *trillian.GetInclusionProofByHashRequest) (*trillian.GetInclusionProofByHashResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetInclusionProofByHash", arg0, arg1) ret0, _ := ret[0].(*trillian.GetInclusionProofByHashResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetInclusionProofByHash indicates an expected call of GetInclusionProofByHash. func (mr *MockTrillianLogServerMockRecorder) GetInclusionProofByHash(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetInclusionProofByHash", reflect.TypeOf((*MockTrillianLogServer)(nil).GetInclusionProofByHash), arg0, arg1) } // GetLatestSignedLogRoot mocks base method. func (m *MockTrillianLogServer) GetLatestSignedLogRoot(arg0 context.Context, arg1 *trillian.GetLatestSignedLogRootRequest) (*trillian.GetLatestSignedLogRootResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetLatestSignedLogRoot", arg0, arg1) ret0, _ := ret[0].(*trillian.GetLatestSignedLogRootResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetLatestSignedLogRoot indicates an expected call of GetLatestSignedLogRoot. func (mr *MockTrillianLogServerMockRecorder) GetLatestSignedLogRoot(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLatestSignedLogRoot", reflect.TypeOf((*MockTrillianLogServer)(nil).GetLatestSignedLogRoot), arg0, arg1) } // GetLeavesByRange mocks base method. func (m *MockTrillianLogServer) GetLeavesByRange(arg0 context.Context, arg1 *trillian.GetLeavesByRangeRequest) (*trillian.GetLeavesByRangeResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetLeavesByRange", arg0, arg1) ret0, _ := ret[0].(*trillian.GetLeavesByRangeResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetLeavesByRange indicates an expected call of GetLeavesByRange. func (mr *MockTrillianLogServerMockRecorder) GetLeavesByRange(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLeavesByRange", reflect.TypeOf((*MockTrillianLogServer)(nil).GetLeavesByRange), arg0, arg1) } // InitLog mocks base method. func (m *MockTrillianLogServer) InitLog(arg0 context.Context, arg1 *trillian.InitLogRequest) (*trillian.InitLogResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InitLog", arg0, arg1) ret0, _ := ret[0].(*trillian.InitLogResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // InitLog indicates an expected call of InitLog. func (mr *MockTrillianLogServerMockRecorder) InitLog(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InitLog", reflect.TypeOf((*MockTrillianLogServer)(nil).InitLog), arg0, arg1) } // QueueLeaf mocks base method. func (m *MockTrillianLogServer) QueueLeaf(arg0 context.Context, arg1 *trillian.QueueLeafRequest) (*trillian.QueueLeafResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "QueueLeaf", arg0, arg1) ret0, _ := ret[0].(*trillian.QueueLeafResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // QueueLeaf indicates an expected call of QueueLeaf. func (mr *MockTrillianLogServerMockRecorder) QueueLeaf(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueueLeaf", reflect.TypeOf((*MockTrillianLogServer)(nil).QueueLeaf), arg0, arg1) } trillian-1.6.1/third_party/000077500000000000000000000000001466362047600157045ustar00rootroot00000000000000trillian-1.6.1/third_party/googleapis/000077500000000000000000000000001466362047600200355ustar00rootroot00000000000000trillian-1.6.1/third_party/googleapis/README.md000066400000000000000000000024751466362047600213240ustar00rootroot00000000000000## Purpose This directory is manually vendored in order to take the required proto definitions from `googleapis`. The principle is that the dependency should be hermetic and lightweight. Previously, the instructions were to manually clone the whole googleapis repository at head, into a known location. ## Updating The required files were determined by manual inspection of the codebase and closing the transitive dependencies by checking the imports of the files. Should the upstream repository be significantly refactored then it is possible that further files may need to be imported if an update is performed. The files here were cloned at commit `c81bb70`. The workflow is simple and documented here for future maintainers: ```sh export GA_VERSION=c81bb701eb53991d6faf74b2656eaf539261a122 mkdir -p google/api wget https://raw.githubusercontent.com/googleapis/googleapis/$GA_VERSION/google/api/annotations.proto -O google/api/annotations.proto wget https://raw.githubusercontent.com/googleapis/googleapis/$GA_VERSION/google/api/http.proto -O google/api/http.proto mkdir -p google/rpc wget https://raw.githubusercontent.com/googleapis/googleapis/$GA_VERSION/google/rpc/code.proto -O google/rpc/code.proto wget https://raw.githubusercontent.com/googleapis/googleapis/$GA_VERSION/google/rpc/status.proto -O google/rpc/status.proto ``` trillian-1.6.1/third_party/googleapis/google/000077500000000000000000000000001466362047600213115ustar00rootroot00000000000000trillian-1.6.1/third_party/googleapis/google/api/000077500000000000000000000000001466362047600220625ustar00rootroot00000000000000trillian-1.6.1/third_party/googleapis/google/api/annotations.proto000066400000000000000000000020251466362047600255030ustar00rootroot00000000000000// Copyright 2015 Google LLC // // 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. syntax = "proto3"; package google.api; import "google/api/http.proto"; import "google/protobuf/descriptor.proto"; option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; option java_multiple_files = true; option java_outer_classname = "AnnotationsProto"; option java_package = "com.google.api"; option objc_class_prefix = "GAPI"; extend google.protobuf.MethodOptions { // See `HttpRule`. HttpRule http = 72295728; } trillian-1.6.1/third_party/googleapis/google/api/http.proto000066400000000000000000000354441466362047600241400ustar00rootroot00000000000000// Copyright 2015 Google LLC // // 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. syntax = "proto3"; package google.api; option cc_enable_arenas = true; option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; option java_multiple_files = true; option java_outer_classname = "HttpProto"; option java_package = "com.google.api"; option objc_class_prefix = "GAPI"; // Defines the HTTP configuration for an API service. It contains a list of // [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method // to one or more HTTP REST API methods. message Http { // A list of HTTP configuration rules that apply to individual API methods. // // **NOTE:** All service configuration rules follow "last one wins" order. repeated HttpRule rules = 1; // When set to true, URL path parameters will be fully URI-decoded except in // cases of single segment matches in reserved expansion, where "%2F" will be // left encoded. // // The default behavior is to not decode RFC 6570 reserved characters in multi // segment matches. bool fully_decode_reserved_expansion = 2; } // # gRPC Transcoding // // gRPC Transcoding is a feature for mapping between a gRPC method and one or // more HTTP REST endpoints. It allows developers to build a single API service // that supports both gRPC APIs and REST APIs. Many systems, including [Google // APIs](https://github.com/googleapis/googleapis), // [Cloud Endpoints](https://cloud.google.com/endpoints), [gRPC // Gateway](https://github.com/grpc-ecosystem/grpc-gateway), // and [Envoy](https://github.com/envoyproxy/envoy) proxy support this feature // and use it for large scale production services. // // `HttpRule` defines the schema of the gRPC/REST mapping. The mapping specifies // how different portions of the gRPC request message are mapped to the URL // path, URL query parameters, and HTTP request body. It also controls how the // gRPC response message is mapped to the HTTP response body. `HttpRule` is // typically specified as an `google.api.http` annotation on the gRPC method. // // Each mapping specifies a URL path template and an HTTP method. The path // template may refer to one or more fields in the gRPC request message, as long // as each field is a non-repeated field with a primitive (non-message) type. // The path template controls how fields of the request message are mapped to // the URL path. // // Example: // // service Messaging { // rpc GetMessage(GetMessageRequest) returns (Message) { // option (google.api.http) = { // get: "/v1/{name=messages/*}" // }; // } // } // message GetMessageRequest { // string name = 1; // Mapped to URL path. // } // message Message { // string text = 1; // The resource content. // } // // This enables an HTTP REST to gRPC mapping as below: // // HTTP | gRPC // -----|----- // `GET /v1/messages/123456` | `GetMessage(name: "messages/123456")` // // Any fields in the request message which are not bound by the path template // automatically become HTTP query parameters if there is no HTTP request body. // For example: // // service Messaging { // rpc GetMessage(GetMessageRequest) returns (Message) { // option (google.api.http) = { // get:"/v1/messages/{message_id}" // }; // } // } // message GetMessageRequest { // message SubMessage { // string subfield = 1; // } // string message_id = 1; // Mapped to URL path. // int64 revision = 2; // Mapped to URL query parameter `revision`. // SubMessage sub = 3; // Mapped to URL query parameter `sub.subfield`. // } // // This enables a HTTP JSON to RPC mapping as below: // // HTTP | gRPC // -----|----- // `GET /v1/messages/123456?revision=2&sub.subfield=foo` | // `GetMessage(message_id: "123456" revision: 2 sub: SubMessage(subfield: // "foo"))` // // Note that fields which are mapped to URL query parameters must have a // primitive type or a repeated primitive type or a non-repeated message type. // In the case of a repeated type, the parameter can be repeated in the URL // as `...?param=A¶m=B`. In the case of a message type, each field of the // message is mapped to a separate parameter, such as // `...?foo.a=A&foo.b=B&foo.c=C`. // // For HTTP methods that allow a request body, the `body` field // specifies the mapping. Consider a REST update method on the // message resource collection: // // service Messaging { // rpc UpdateMessage(UpdateMessageRequest) returns (Message) { // option (google.api.http) = { // patch: "/v1/messages/{message_id}" // body: "message" // }; // } // } // message UpdateMessageRequest { // string message_id = 1; // mapped to the URL // Message message = 2; // mapped to the body // } // // The following HTTP JSON to RPC mapping is enabled, where the // representation of the JSON in the request body is determined by // protos JSON encoding: // // HTTP | gRPC // -----|----- // `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: // "123456" message { text: "Hi!" })` // // The special name `*` can be used in the body mapping to define that // every field not bound by the path template should be mapped to the // request body. This enables the following alternative definition of // the update method: // // service Messaging { // rpc UpdateMessage(Message) returns (Message) { // option (google.api.http) = { // patch: "/v1/messages/{message_id}" // body: "*" // }; // } // } // message Message { // string message_id = 1; // string text = 2; // } // // // The following HTTP JSON to RPC mapping is enabled: // // HTTP | gRPC // -----|----- // `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: // "123456" text: "Hi!")` // // Note that when using `*` in the body mapping, it is not possible to // have HTTP parameters, as all fields not bound by the path end in // the body. This makes this option more rarely used in practice when // defining REST APIs. The common usage of `*` is in custom methods // which don't use the URL at all for transferring data. // // It is possible to define multiple HTTP methods for one RPC by using // the `additional_bindings` option. Example: // // service Messaging { // rpc GetMessage(GetMessageRequest) returns (Message) { // option (google.api.http) = { // get: "/v1/messages/{message_id}" // additional_bindings { // get: "/v1/users/{user_id}/messages/{message_id}" // } // }; // } // } // message GetMessageRequest { // string message_id = 1; // string user_id = 2; // } // // This enables the following two alternative HTTP JSON to RPC mappings: // // HTTP | gRPC // -----|----- // `GET /v1/messages/123456` | `GetMessage(message_id: "123456")` // `GET /v1/users/me/messages/123456` | `GetMessage(user_id: "me" message_id: // "123456")` // // ## Rules for HTTP mapping // // 1. Leaf request fields (recursive expansion nested messages in the request // message) are classified into three categories: // - Fields referred by the path template. They are passed via the URL path. // - Fields referred by the [HttpRule.body][google.api.HttpRule.body]. They are passed via the HTTP // request body. // - All other fields are passed via the URL query parameters, and the // parameter name is the field path in the request message. A repeated // field can be represented as multiple query parameters under the same // name. // 2. If [HttpRule.body][google.api.HttpRule.body] is "*", there is no URL query parameter, all fields // are passed via URL path and HTTP request body. // 3. If [HttpRule.body][google.api.HttpRule.body] is omitted, there is no HTTP request body, all // fields are passed via URL path and URL query parameters. // // ### Path template syntax // // Template = "/" Segments [ Verb ] ; // Segments = Segment { "/" Segment } ; // Segment = "*" | "**" | LITERAL | Variable ; // Variable = "{" FieldPath [ "=" Segments ] "}" ; // FieldPath = IDENT { "." IDENT } ; // Verb = ":" LITERAL ; // // The syntax `*` matches a single URL path segment. The syntax `**` matches // zero or more URL path segments, which must be the last part of the URL path // except the `Verb`. // // The syntax `Variable` matches part of the URL path as specified by its // template. A variable template must not contain other variables. If a variable // matches a single path segment, its template may be omitted, e.g. `{var}` // is equivalent to `{var=*}`. // // The syntax `LITERAL` matches literal text in the URL path. If the `LITERAL` // contains any reserved character, such characters should be percent-encoded // before the matching. // // If a variable contains exactly one path segment, such as `"{var}"` or // `"{var=*}"`, when such a variable is expanded into a URL path on the client // side, all characters except `[-_.~0-9a-zA-Z]` are percent-encoded. The // server side does the reverse decoding. Such variables show up in the // [Discovery // Document](https://developers.google.com/discovery/v1/reference/apis) as // `{var}`. // // If a variable contains multiple path segments, such as `"{var=foo/*}"` // or `"{var=**}"`, when such a variable is expanded into a URL path on the // client side, all characters except `[-_.~/0-9a-zA-Z]` are percent-encoded. // The server side does the reverse decoding, except "%2F" and "%2f" are left // unchanged. Such variables show up in the // [Discovery // Document](https://developers.google.com/discovery/v1/reference/apis) as // `{+var}`. // // ## Using gRPC API Service Configuration // // gRPC API Service Configuration (service config) is a configuration language // for configuring a gRPC service to become a user-facing product. The // service config is simply the YAML representation of the `google.api.Service` // proto message. // // As an alternative to annotating your proto file, you can configure gRPC // transcoding in your service config YAML files. You do this by specifying a // `HttpRule` that maps the gRPC method to a REST endpoint, achieving the same // effect as the proto annotation. This can be particularly useful if you // have a proto that is reused in multiple services. Note that any transcoding // specified in the service config will override any matching transcoding // configuration in the proto. // // Example: // // http: // rules: // # Selects a gRPC method and applies HttpRule to it. // - selector: example.v1.Messaging.GetMessage // get: /v1/messages/{message_id}/{sub.subfield} // // ## Special notes // // When gRPC Transcoding is used to map a gRPC to JSON REST endpoints, the // proto to JSON conversion must follow the [proto3 // specification](https://developers.google.com/protocol-buffers/docs/proto3#json). // // While the single segment variable follows the semantics of // [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 Simple String // Expansion, the multi segment variable **does not** follow RFC 6570 Section // 3.2.3 Reserved Expansion. The reason is that the Reserved Expansion // does not expand special characters like `?` and `#`, which would lead // to invalid URLs. As the result, gRPC Transcoding uses a custom encoding // for multi segment variables. // // The path variables **must not** refer to any repeated or mapped field, // because client libraries are not capable of handling such variable expansion. // // The path variables **must not** capture the leading "/" character. The reason // is that the most common use case "{var}" does not capture the leading "/" // character. For consistency, all path variables must share the same behavior. // // Repeated message fields must not be mapped to URL query parameters, because // no client library can support such complicated mapping. // // If an API needs to use a JSON array for request or response body, it can map // the request or response body to a repeated field. However, some gRPC // Transcoding implementations may not support this feature. message HttpRule { // Selects a method to which this rule applies. // // Refer to [selector][google.api.DocumentationRule.selector] for syntax details. string selector = 1; // Determines the URL pattern is matched by this rules. This pattern can be // used with any of the {get|put|post|delete|patch} methods. A custom method // can be defined using the 'custom' field. oneof pattern { // Maps to HTTP GET. Used for listing and getting information about // resources. string get = 2; // Maps to HTTP PUT. Used for replacing a resource. string put = 3; // Maps to HTTP POST. Used for creating a resource or performing an action. string post = 4; // Maps to HTTP DELETE. Used for deleting a resource. string delete = 5; // Maps to HTTP PATCH. Used for updating a resource. string patch = 6; // The custom pattern is used for specifying an HTTP method that is not // included in the `pattern` field, such as HEAD, or "*" to leave the // HTTP method unspecified for this rule. The wild-card rule is useful // for services that provide content to Web (HTML) clients. CustomHttpPattern custom = 8; } // The name of the request field whose value is mapped to the HTTP request // body, or `*` for mapping all request fields not captured by the path // pattern to the HTTP body, or omitted for not having any HTTP request body. // // NOTE: the referred field must be present at the top-level of the request // message type. string body = 7; // Optional. The name of the response field whose value is mapped to the HTTP // response body. When omitted, the entire response message will be used // as the HTTP response body. // // NOTE: The referred field must be present at the top-level of the response // message type. string response_body = 12; // Additional HTTP bindings for the selector. Nested bindings must // not contain an `additional_bindings` field themselves (that is, // the nesting may only be one level deep). repeated HttpRule additional_bindings = 11; } // A custom pattern is used for defining custom HTTP verb. message CustomHttpPattern { // The name of this custom HTTP verb. string kind = 1; // The path matched by this custom verb. string path = 2; } trillian-1.6.1/third_party/googleapis/google/rpc/000077500000000000000000000000001466362047600220755ustar00rootroot00000000000000trillian-1.6.1/third_party/googleapis/google/rpc/code.proto000066400000000000000000000157251466362047600241060ustar00rootroot00000000000000// Copyright 2020 Google LLC // // 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. syntax = "proto3"; package google.rpc; option go_package = "google.golang.org/genproto/googleapis/rpc/code;code"; option java_multiple_files = true; option java_outer_classname = "CodeProto"; option java_package = "com.google.rpc"; option objc_class_prefix = "RPC"; // The canonical error codes for gRPC APIs. // // // Sometimes multiple error codes may apply. Services should return // the most specific error code that applies. For example, prefer // `OUT_OF_RANGE` over `FAILED_PRECONDITION` if both codes apply. // Similarly prefer `NOT_FOUND` or `ALREADY_EXISTS` over `FAILED_PRECONDITION`. enum Code { // Not an error; returned on success // // HTTP Mapping: 200 OK OK = 0; // The operation was cancelled, typically by the caller. // // HTTP Mapping: 499 Client Closed Request CANCELLED = 1; // Unknown error. For example, this error may be returned when // a `Status` value received from another address space belongs to // an error space that is not known in this address space. Also // errors raised by APIs that do not return enough error information // may be converted to this error. // // HTTP Mapping: 500 Internal Server Error UNKNOWN = 2; // The client specified an invalid argument. Note that this differs // from `FAILED_PRECONDITION`. `INVALID_ARGUMENT` indicates arguments // that are problematic regardless of the state of the system // (e.g., a malformed file name). // // HTTP Mapping: 400 Bad Request INVALID_ARGUMENT = 3; // The deadline expired before the operation could complete. For operations // that change the state of the system, this error may be returned // even if the operation has completed successfully. For example, a // successful response from a server could have been delayed long // enough for the deadline to expire. // // HTTP Mapping: 504 Gateway Timeout DEADLINE_EXCEEDED = 4; // Some requested entity (e.g., file or directory) was not found. // // Note to server developers: if a request is denied for an entire class // of users, such as gradual feature rollout or undocumented whitelist, // `NOT_FOUND` may be used. If a request is denied for some users within // a class of users, such as user-based access control, `PERMISSION_DENIED` // must be used. // // HTTP Mapping: 404 Not Found NOT_FOUND = 5; // The entity that a client attempted to create (e.g., file or directory) // already exists. // // HTTP Mapping: 409 Conflict ALREADY_EXISTS = 6; // The caller does not have permission to execute the specified // operation. `PERMISSION_DENIED` must not be used for rejections // caused by exhausting some resource (use `RESOURCE_EXHAUSTED` // instead for those errors). `PERMISSION_DENIED` must not be // used if the caller can not be identified (use `UNAUTHENTICATED` // instead for those errors). This error code does not imply the // request is valid or the requested entity exists or satisfies // other pre-conditions. // // HTTP Mapping: 403 Forbidden PERMISSION_DENIED = 7; // The request does not have valid authentication credentials for the // operation. // // HTTP Mapping: 401 Unauthorized UNAUTHENTICATED = 16; // Some resource has been exhausted, perhaps a per-user quota, or // perhaps the entire file system is out of space. // // HTTP Mapping: 429 Too Many Requests RESOURCE_EXHAUSTED = 8; // The operation was rejected because the system is not in a state // required for the operation's execution. For example, the directory // to be deleted is non-empty, an rmdir operation is applied to // a non-directory, etc. // // Service implementors can use the following guidelines to decide // between `FAILED_PRECONDITION`, `ABORTED`, and `UNAVAILABLE`: // (a) Use `UNAVAILABLE` if the client can retry just the failing call. // (b) Use `ABORTED` if the client should retry at a higher level // (e.g., when a client-specified test-and-set fails, indicating the // client should restart a read-modify-write sequence). // (c) Use `FAILED_PRECONDITION` if the client should not retry until // the system state has been explicitly fixed. E.g., if an "rmdir" // fails because the directory is non-empty, `FAILED_PRECONDITION` // should be returned since the client should not retry unless // the files are deleted from the directory. // // HTTP Mapping: 400 Bad Request FAILED_PRECONDITION = 9; // The operation was aborted, typically due to a concurrency issue such as // a sequencer check failure or transaction abort. // // See the guidelines above for deciding between `FAILED_PRECONDITION`, // `ABORTED`, and `UNAVAILABLE`. // // HTTP Mapping: 409 Conflict ABORTED = 10; // The operation was attempted past the valid range. E.g., seeking or // reading past end-of-file. // // Unlike `INVALID_ARGUMENT`, this error indicates a problem that may // be fixed if the system state changes. For example, a 32-bit file // system will generate `INVALID_ARGUMENT` if asked to read at an // offset that is not in the range [0,2^32-1], but it will generate // `OUT_OF_RANGE` if asked to read from an offset past the current // file size. // // There is a fair bit of overlap between `FAILED_PRECONDITION` and // `OUT_OF_RANGE`. We recommend using `OUT_OF_RANGE` (the more specific // error) when it applies so that callers who are iterating through // a space can easily look for an `OUT_OF_RANGE` error to detect when // they are done. // // HTTP Mapping: 400 Bad Request OUT_OF_RANGE = 11; // The operation is not implemented or is not supported/enabled in this // service. // // HTTP Mapping: 501 Not Implemented UNIMPLEMENTED = 12; // Internal errors. This means that some invariants expected by the // underlying system have been broken. This error code is reserved // for serious errors. // // HTTP Mapping: 500 Internal Server Error INTERNAL = 13; // The service is currently unavailable. This is most likely a // transient condition, which can be corrected by retrying with // a backoff. Note that it is not always safe to retry // non-idempotent operations. // // See the guidelines above for deciding between `FAILED_PRECONDITION`, // `ABORTED`, and `UNAVAILABLE`. // // HTTP Mapping: 503 Service Unavailable UNAVAILABLE = 14; // Unrecoverable data loss or corruption. // // HTTP Mapping: 500 Internal Server Error DATA_LOSS = 15; } trillian-1.6.1/third_party/googleapis/google/rpc/status.proto000066400000000000000000000036041466362047600245100ustar00rootroot00000000000000// Copyright 2020 Google LLC // // 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. syntax = "proto3"; package google.rpc; import "google/protobuf/any.proto"; option cc_enable_arenas = true; option go_package = "google.golang.org/genproto/googleapis/rpc/status;status"; option java_multiple_files = true; option java_outer_classname = "StatusProto"; option java_package = "com.google.rpc"; option objc_class_prefix = "RPC"; // The `Status` type defines a logical error model that is suitable for // different programming environments, including REST APIs and RPC APIs. It is // used by [gRPC](https://github.com/grpc). Each `Status` message contains // three pieces of data: error code, error message, and error details. // // You can find out more about this error model and how to work with it in the // [API Design Guide](https://cloud.google.com/apis/design/errors). message Status { // The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. int32 code = 1; // A developer-facing error message, which should be in English. Any // user-facing error message should be localized and sent in the // [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. string message = 2; // A list of messages that carry the error details. There is a common set of // message types for APIs to use. repeated google.protobuf.Any details = 3; } trillian-1.6.1/tools/000077500000000000000000000000001466362047600145135ustar00rootroot00000000000000trillian-1.6.1/tools/tools.go000066400000000000000000000026031466362047600162030ustar00rootroot00000000000000// 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. //go:build tools // +build tools // Package tools tracks dependencies on binaries not otherwise referenced in the trillian codebase. // https://github.com/golang/go/wiki/Modules#how-can-i-track-tool-dependencies-for-a-module package tools import ( _ "github.com/apache/beam/sdks/v2/go/cmd/starcgen" _ "github.com/fullstorydev/grpcurl/cmd/grpcurl" _ "github.com/golang/mock/gomock" _ "github.com/golang/mock/mockgen" _ "github.com/google/go-licenses/v2" _ "github.com/pseudomuto/protoc-gen-doc/cmd/protoc-gen-doc" _ "go.etcd.io/etcd/etcdctl/v3" _ "go.etcd.io/etcd/etcdctl/v3/ctlv3/command" _ "go.etcd.io/etcd/v3" _ "golang.org/x/tools/cmd/stringer" _ "google.golang.org/grpc/cmd/protoc-gen-go-grpc" _ "google.golang.org/protobuf/cmd/protoc-gen-go" _ "google.golang.org/protobuf/proto" ) trillian-1.6.1/trees/000077500000000000000000000000001466362047600144755ustar00rootroot00000000000000trillian-1.6.1/trees/gen.go000066400000000000000000000012361466362047600155770ustar00rootroot00000000000000// Copyright 2018 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 trees //go:generate stringer -type=OpType types.go trillian-1.6.1/trees/optype_string.go000066400000000000000000000013301466362047600177270ustar00rootroot00000000000000// Code generated by "stringer -type=OpType types.go"; DO NOT EDIT. package trees import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[Unknown-0] _ = x[Admin-1] _ = x[Query-2] _ = x[QueueLog-3] _ = x[SequenceLog-4] _ = x[UpdateMap-5] } const _OpType_name = "UnknownAdminQueryQueueLogSequenceLogUpdateMap" var _OpType_index = [...]uint8{0, 7, 12, 17, 25, 36, 45} func (i OpType) String() string { if i < 0 || i >= OpType(len(_OpType_index)-1) { return "OpType(" + strconv.FormatInt(int64(i), 10) + ")" } return _OpType_name[_OpType_index[i]:_OpType_index[i+1]] } trillian-1.6.1/trees/trees.go000066400000000000000000000133351466362047600161530ustar00rootroot00000000000000// 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 trees contains utility method for retrieving trees and acquiring objects (hashers, // signers) associated with them. package trees import ( "context" "fmt" "github.com/google/trillian" "github.com/google/trillian/monitoring" "github.com/google/trillian/storage" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) const traceSpanRoot = "/trillian/trees" type treeKey struct{} type accessRule struct { // Tree states are accepted if there is a 'true' value for them in this map. okStates map[trillian.TreeState]bool // Allows the error code to be specified for specific rejected states. rejectCodes map[trillian.TreeState]codes.Code // Tree types are accepted if there is a 'true' value for them in this map. okTypes map[trillian.TreeType]bool } // These rules define the permissible combinations of tree state and type // for each operation type. var rules = map[OpType]accessRule{ Unknown: {}, Admin: { okStates: map[trillian.TreeState]bool{ trillian.TreeState_UNKNOWN_TREE_STATE: true, trillian.TreeState_ACTIVE: true, trillian.TreeState_DRAINING: true, trillian.TreeState_FROZEN: true, }, okTypes: map[trillian.TreeType]bool{ trillian.TreeType_LOG: true, trillian.TreeType_PREORDERED_LOG: true, }, }, Query: { okStates: map[trillian.TreeState]bool{ // Have to allow queries on unknown state so storage can get a chance // to return ErrTreeNeedsInit. trillian.TreeState_UNKNOWN_TREE_STATE: true, trillian.TreeState_ACTIVE: true, trillian.TreeState_DRAINING: true, trillian.TreeState_FROZEN: true, }, okTypes: map[trillian.TreeType]bool{ trillian.TreeType_LOG: true, trillian.TreeType_PREORDERED_LOG: true, }, }, QueueLog: { okStates: map[trillian.TreeState]bool{ trillian.TreeState_ACTIVE: true, }, rejectCodes: map[trillian.TreeState]codes.Code{ trillian.TreeState_DRAINING: codes.PermissionDenied, trillian.TreeState_FROZEN: codes.PermissionDenied, }, okTypes: map[trillian.TreeType]bool{ trillian.TreeType_LOG: true, trillian.TreeType_PREORDERED_LOG: true, }, }, SequenceLog: { okStates: map[trillian.TreeState]bool{ trillian.TreeState_ACTIVE: true, trillian.TreeState_DRAINING: true, }, okTypes: map[trillian.TreeType]bool{ trillian.TreeType_LOG: true, trillian.TreeType_PREORDERED_LOG: true, }, rejectCodes: map[trillian.TreeState]codes.Code{ trillian.TreeState_FROZEN: codes.PermissionDenied, }, }, } // NewContext returns a ctx with the given tree. func NewContext(ctx context.Context, tree *trillian.Tree) context.Context { return context.WithValue(ctx, treeKey{}, tree) } // FromContext returns the tree within ctx if present, together with an indication of whether a // tree was present. func FromContext(ctx context.Context) (*trillian.Tree, bool) { tree, ok := ctx.Value(treeKey{}).(*trillian.Tree) return tree, ok && tree != nil } func validate(o GetOpts, tree *trillian.Tree) error { // Do the special case checks first if len(o.TreeTypes) > 0 && !o.TreeTypes[tree.TreeType] { return status.Errorf(codes.InvalidArgument, "operation not allowed for %s-type trees (wanted one of %v)", tree.TreeType, o.TreeTypes) } // Reject any operation types we don't know about. rule, ok := rules[o.Operation] if !ok { return status.Errorf(codes.Internal, "invalid operation type in GetOpts: %v", o) } // Apply the rule, ensure it allows the tree type and state that we have. if !rule.okTypes[tree.TreeType] || !rule.okStates[tree.TreeState] { // If we have a status code to use it takes precedence, otherwise it's // a generic InvalidArgument code. code, ok := rule.rejectCodes[tree.TreeState] if !ok { code = codes.InvalidArgument } return status.Errorf(code, "operation: %v not allowed for tree type: %v state: %v", o.Operation, tree.TreeType, tree.TreeState) } return nil } // GetTree returns the specified tree, either from the ctx (if present) or read from storage. // The tree will be validated according to GetOpts before returned. Tree state is also considered // (for example, deleted tree will return NotFound errors). func GetTree(ctx context.Context, s storage.AdminStorage, treeID int64, opts GetOpts) (*trillian.Tree, error) { ctx, spanEnd := spanFor(ctx, "GetTree") defer spanEnd() tree, ok := FromContext(ctx) if !ok { var err error tree, err = storage.GetTree(ctx, s, treeID) if err != nil { return nil, err } } if tree.TreeId != treeID { // No operations should span multiple trees. If a tree is already in the context // it had better be the one that we want. If the tree comes back from the DB with // the wrong ID then this checks that too. return nil, status.Errorf(codes.Internal, "got tree %v, want %v", tree.TreeId, treeID) } if err := validate(opts, tree); err != nil { return nil, err } if tree.Deleted { return nil, status.Errorf(codes.NotFound, "tree %v not found", tree.TreeId) } return tree, nil } func spanFor(ctx context.Context, name string) (context.Context, func()) { return monitoring.StartSpan(ctx, fmt.Sprintf("%s.%s", traceSpanRoot, name)) } trillian-1.6.1/trees/trees_test.go000066400000000000000000000177311466362047600172160ustar00rootroot00000000000000// 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 trees import ( "context" "errors" "testing" "github.com/golang/mock/gomock" "github.com/google/go-cmp/cmp" "github.com/google/trillian" "github.com/google/trillian/storage" "github.com/google/trillian/storage/testonly" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/timestamppb" ) func TestFromContext(t *testing.T) { tests := []struct { desc string tree *trillian.Tree }{ {desc: "noTree"}, {desc: "hasTree", tree: testonly.LogTree}, } for _, test := range tests { ctx := NewContext(context.Background(), test.tree) tree, ok := FromContext(ctx) switch wantOK := test.tree != nil; { case ok != wantOK: t.Errorf("%v: FromContext(%v) = (_, %v), want = (_, %v)", test.desc, ctx, ok, wantOK) case ok && !proto.Equal(tree, test.tree): t.Errorf("%v: FromContext(%v) = (%v, nil), want = (%v, nil)", test.desc, ctx, tree, test.tree) case !ok && tree != nil: t.Errorf("%v: FromContext(%v) = (%v, %v), want = (nil, %v)", test.desc, ctx, tree, ok, wantOK) } } } func TestGetTree(t *testing.T) { logTree := proto.Clone(testonly.LogTree).(*trillian.Tree) logTree.TreeId = 1 frozenTree := proto.Clone(testonly.LogTree).(*trillian.Tree) frozenTree.TreeId = 3 frozenTree.TreeState = trillian.TreeState_FROZEN drainingTree := proto.Clone(testonly.LogTree).(*trillian.Tree) drainingTree.TreeId = 3 drainingTree.TreeState = trillian.TreeState_DRAINING softDeletedTree := proto.Clone(testonly.LogTree).(*trillian.Tree) softDeletedTree.Deleted = true softDeletedTree.DeleteTime = timestamppb.Now() tests := []struct { desc string treeID int64 opts GetOpts ctxTree, storageTree, wantTree *trillian.Tree beginErr, getErr, commitErr error wantErr bool code codes.Code }{ { desc: "anyTree", treeID: logTree.TreeId, opts: NewGetOpts(Query), storageTree: logTree, wantTree: logTree, }, { desc: "logTree", treeID: logTree.TreeId, opts: NewGetOpts(Query, trillian.TreeType_LOG), storageTree: logTree, wantTree: logTree, }, { desc: "logTreeButMaybePreordered", treeID: logTree.TreeId, opts: NewGetOpts(Query, trillian.TreeType_LOG, trillian.TreeType_PREORDERED_LOG), storageTree: logTree, wantTree: logTree, }, { desc: "wrongType1", treeID: logTree.TreeId, opts: NewGetOpts(Query, trillian.TreeType_PREORDERED_LOG), storageTree: logTree, wantErr: true, code: codes.InvalidArgument, }, { desc: "adminLog", treeID: logTree.TreeId, opts: NewGetOpts(Admin, trillian.TreeType_LOG), storageTree: logTree, wantTree: logTree, }, { desc: "adminPreordered", treeID: testonly.PreorderedLogTree.TreeId, opts: NewGetOpts(Admin, trillian.TreeType_PREORDERED_LOG), storageTree: testonly.PreorderedLogTree, wantTree: testonly.PreorderedLogTree, }, { desc: "adminFrozen", treeID: frozenTree.TreeId, opts: NewGetOpts(Admin, trillian.TreeType_LOG), storageTree: frozenTree, wantTree: frozenTree, }, { desc: "queryLog", treeID: logTree.TreeId, opts: NewGetOpts(Query, trillian.TreeType_LOG), storageTree: logTree, wantTree: logTree, }, { desc: "queryPreordered", treeID: testonly.PreorderedLogTree.TreeId, opts: NewGetOpts(Query, trillian.TreeType_PREORDERED_LOG), storageTree: testonly.PreorderedLogTree, wantTree: testonly.PreorderedLogTree, }, { desc: "queryFrozen", treeID: frozenTree.TreeId, opts: NewGetOpts(Query, trillian.TreeType_LOG), storageTree: frozenTree, wantTree: frozenTree, }, { desc: "sequenceFrozen", treeID: frozenTree.TreeId, opts: NewGetOpts(SequenceLog, trillian.TreeType_LOG), storageTree: frozenTree, wantTree: frozenTree, wantErr: true, code: codes.PermissionDenied, }, { desc: "queueFrozen", treeID: frozenTree.TreeId, opts: NewGetOpts(QueueLog, trillian.TreeType_LOG), storageTree: frozenTree, wantTree: frozenTree, wantErr: true, code: codes.PermissionDenied, }, { desc: "queryDraining", treeID: drainingTree.TreeId, opts: NewGetOpts(Query, trillian.TreeType_LOG), storageTree: drainingTree, wantTree: drainingTree, }, { desc: "sequenceDraining", treeID: drainingTree.TreeId, opts: NewGetOpts(SequenceLog, trillian.TreeType_LOG), storageTree: drainingTree, wantTree: drainingTree, }, { desc: "queueDraining", treeID: drainingTree.TreeId, opts: NewGetOpts(QueueLog, trillian.TreeType_LOG), storageTree: drainingTree, wantTree: drainingTree, wantErr: true, code: codes.PermissionDenied, }, { desc: "softDeleted", treeID: softDeletedTree.TreeId, opts: NewGetOpts(Query, trillian.TreeType_LOG), storageTree: softDeletedTree, wantErr: true, // Deleted = true makes the tree "invisible" for most RPCs code: codes.NotFound, }, { desc: "treeInCtx", treeID: logTree.TreeId, opts: NewGetOpts(Query, trillian.TreeType_LOG), ctxTree: logTree, wantTree: logTree, }, { desc: "wrongTreeInCtx", treeID: logTree.TreeId, opts: NewGetOpts(Query, trillian.TreeType_LOG), ctxTree: frozenTree, storageTree: logTree, wantTree: logTree, wantErr: true, code: codes.Internal, }, { desc: "beginErr", treeID: logTree.TreeId, opts: NewGetOpts(Query, trillian.TreeType_LOG), beginErr: errors.New("begin err"), wantErr: true, code: codes.Unknown, }, { desc: "getErr", treeID: logTree.TreeId, opts: NewGetOpts(Query, trillian.TreeType_LOG), getErr: errors.New("get err"), wantErr: true, code: codes.Unknown, }, { desc: "commitErr", treeID: logTree.TreeId, opts: NewGetOpts(Query, trillian.TreeType_LOG), commitErr: errors.New("commit err"), wantErr: true, code: codes.Unknown, }, } ctrl := gomock.NewController(t) defer ctrl.Finish() for _, test := range tests { ctx := NewContext(context.Background(), test.ctxTree) admin := storage.NewMockAdminStorage(ctrl) tx := storage.NewMockReadOnlyAdminTX(ctrl) admin.EXPECT().Snapshot(gomock.Any()).MaxTimes(1).Return(tx, test.beginErr) tx.EXPECT().GetTree(gomock.Any(), test.treeID).MaxTimes(1).Return(test.storageTree, test.getErr) tx.EXPECT().Close().MaxTimes(1).Return(nil) tx.EXPECT().Commit().MaxTimes(1).Return(test.commitErr) tree, err := GetTree(ctx, admin, test.treeID, test.opts) if hasErr := err != nil; hasErr != test.wantErr { t.Errorf("%v: GetTree() = (_, %q), wantErr = %v", test.desc, err, test.wantErr) continue } else if hasErr { if status.Code(err) != test.code { t.Errorf("%v: GetTree() = (_, %q), got ErrorCode: %v, want: %v", test.desc, err, status.Code(err), test.code) } continue } if !proto.Equal(tree, test.wantTree) { diff := cmp.Diff(tree, test.wantTree) t.Errorf("%v: post-GetTree diff:\n%v", test.desc, diff) } } } trillian-1.6.1/trees/types.go000066400000000000000000000034061466362047600161730ustar00rootroot00000000000000// Copyright 2018 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 trees import "github.com/google/trillian" // OpType indicates how a tree is to be used and participates in permissions // decisions. type OpType int const ( // Unknown is an access type that will always get rejected. Unknown OpType = iota // Admin access is for general administration purposes. Admin // Query implies access to serve query, typically readonly. Query // QueueLog is log specific - adding entries to the queue. QueueLog // SequenceLog is log specific - integrating entries into the tree. SequenceLog // UpdateMap is map specific - set / update leaves. UpdateMap ) // GetOpts contains validation options for GetTree. type GetOpts struct { // TreeTypes is a set of allowed tree types. If empty, any type is allowed. TreeTypes map[trillian.TreeType]bool // Operation indicates what operation is being performed. Operation OpType } // NewGetOpts creates GetOps that allows the listed set of tree types, and // optionally forces the tree to be readonly. func NewGetOpts(op OpType, types ...trillian.TreeType) GetOpts { m := make(map[trillian.TreeType]bool) for _, t := range types { m[t] = true } return GetOpts{Operation: op, TreeTypes: m} } trillian-1.6.1/trillian.pb.go000066400000000000000000000743361466362047600161350ustar00rootroot00000000000000// 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. // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.34.2 // protoc v3.20.1 // source: trillian.proto package trillian import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" anypb "google.golang.org/protobuf/types/known/anypb" durationpb "google.golang.org/protobuf/types/known/durationpb" timestamppb "google.golang.org/protobuf/types/known/timestamppb" reflect "reflect" sync "sync" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // LogRootFormat specifies the fields that are covered by the // SignedLogRoot signature, as well as their ordering and formats. type LogRootFormat int32 const ( LogRootFormat_LOG_ROOT_FORMAT_UNKNOWN LogRootFormat = 0 LogRootFormat_LOG_ROOT_FORMAT_V1 LogRootFormat = 1 ) // Enum value maps for LogRootFormat. var ( LogRootFormat_name = map[int32]string{ 0: "LOG_ROOT_FORMAT_UNKNOWN", 1: "LOG_ROOT_FORMAT_V1", } LogRootFormat_value = map[string]int32{ "LOG_ROOT_FORMAT_UNKNOWN": 0, "LOG_ROOT_FORMAT_V1": 1, } ) func (x LogRootFormat) Enum() *LogRootFormat { p := new(LogRootFormat) *p = x return p } func (x LogRootFormat) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (LogRootFormat) Descriptor() protoreflect.EnumDescriptor { return file_trillian_proto_enumTypes[0].Descriptor() } func (LogRootFormat) Type() protoreflect.EnumType { return &file_trillian_proto_enumTypes[0] } func (x LogRootFormat) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use LogRootFormat.Descriptor instead. func (LogRootFormat) EnumDescriptor() ([]byte, []int) { return file_trillian_proto_rawDescGZIP(), []int{0} } // Defines the way empty / node / leaf hashes are constructed incorporating // preimage protection, which can be application specific. type HashStrategy int32 const ( // Hash strategy cannot be determined. Included to enable detection of // mismatched proto versions being used. Represents an invalid value. HashStrategy_UNKNOWN_HASH_STRATEGY HashStrategy = 0 // Certificate Transparency strategy: leaf hash prefix = 0x00, node prefix = // 0x01, empty hash is digest([]byte{}), as defined in the specification. HashStrategy_RFC6962_SHA256 HashStrategy = 1 // Sparse Merkle Tree strategy: leaf hash prefix = 0x00, node prefix = 0x01, // empty branch is recursively computed from empty leaf nodes. // NOT secure in a multi tree environment. For testing only. HashStrategy_TEST_MAP_HASHER HashStrategy = 2 // Append-only log strategy where leaf nodes are defined as the ObjectHash. // All other properties are equal to RFC6962_SHA256. HashStrategy_OBJECT_RFC6962_SHA256 HashStrategy = 3 // The CONIKS sparse tree hasher with SHA512_256 as the hash algorithm. HashStrategy_CONIKS_SHA512_256 HashStrategy = 4 // The CONIKS sparse tree hasher with SHA256 as the hash algorithm. HashStrategy_CONIKS_SHA256 HashStrategy = 5 ) // Enum value maps for HashStrategy. var ( HashStrategy_name = map[int32]string{ 0: "UNKNOWN_HASH_STRATEGY", 1: "RFC6962_SHA256", 2: "TEST_MAP_HASHER", 3: "OBJECT_RFC6962_SHA256", 4: "CONIKS_SHA512_256", 5: "CONIKS_SHA256", } HashStrategy_value = map[string]int32{ "UNKNOWN_HASH_STRATEGY": 0, "RFC6962_SHA256": 1, "TEST_MAP_HASHER": 2, "OBJECT_RFC6962_SHA256": 3, "CONIKS_SHA512_256": 4, "CONIKS_SHA256": 5, } ) func (x HashStrategy) Enum() *HashStrategy { p := new(HashStrategy) *p = x return p } func (x HashStrategy) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (HashStrategy) Descriptor() protoreflect.EnumDescriptor { return file_trillian_proto_enumTypes[1].Descriptor() } func (HashStrategy) Type() protoreflect.EnumType { return &file_trillian_proto_enumTypes[1] } func (x HashStrategy) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use HashStrategy.Descriptor instead. func (HashStrategy) EnumDescriptor() ([]byte, []int) { return file_trillian_proto_rawDescGZIP(), []int{1} } // State of the tree. type TreeState int32 const ( // Tree state cannot be determined. Included to enable detection of // mismatched proto versions being used. Represents an invalid value. TreeState_UNKNOWN_TREE_STATE TreeState = 0 // Active trees are able to respond to both read and write requests. TreeState_ACTIVE TreeState = 1 // Frozen trees are only able to respond to read requests, writing to a frozen // tree is forbidden. Trees should not be frozen when there are entries // in the queue that have not yet been integrated. See the DRAINING // state for this case. TreeState_FROZEN TreeState = 2 // Deprecated: now tracked in Tree.deleted. // // Deprecated: Marked as deprecated in trillian.proto. TreeState_DEPRECATED_SOFT_DELETED TreeState = 3 // Deprecated: now tracked in Tree.deleted. // // Deprecated: Marked as deprecated in trillian.proto. TreeState_DEPRECATED_HARD_DELETED TreeState = 4 // A tree that is draining will continue to integrate queued entries. // No new entries should be accepted. TreeState_DRAINING TreeState = 5 ) // Enum value maps for TreeState. var ( TreeState_name = map[int32]string{ 0: "UNKNOWN_TREE_STATE", 1: "ACTIVE", 2: "FROZEN", 3: "DEPRECATED_SOFT_DELETED", 4: "DEPRECATED_HARD_DELETED", 5: "DRAINING", } TreeState_value = map[string]int32{ "UNKNOWN_TREE_STATE": 0, "ACTIVE": 1, "FROZEN": 2, "DEPRECATED_SOFT_DELETED": 3, "DEPRECATED_HARD_DELETED": 4, "DRAINING": 5, } ) func (x TreeState) Enum() *TreeState { p := new(TreeState) *p = x return p } func (x TreeState) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (TreeState) Descriptor() protoreflect.EnumDescriptor { return file_trillian_proto_enumTypes[2].Descriptor() } func (TreeState) Type() protoreflect.EnumType { return &file_trillian_proto_enumTypes[2] } func (x TreeState) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use TreeState.Descriptor instead. func (TreeState) EnumDescriptor() ([]byte, []int) { return file_trillian_proto_rawDescGZIP(), []int{2} } // Type of the tree. type TreeType int32 const ( // Tree type cannot be determined. Included to enable detection of mismatched // proto versions being used. Represents an invalid value. TreeType_UNKNOWN_TREE_TYPE TreeType = 0 // Tree represents a verifiable log. TreeType_LOG TreeType = 1 // Tree represents a verifiable pre-ordered log, i.e., a log whose entries are // placed according to sequence numbers assigned outside of Trillian. TreeType_PREORDERED_LOG TreeType = 3 ) // Enum value maps for TreeType. var ( TreeType_name = map[int32]string{ 0: "UNKNOWN_TREE_TYPE", 1: "LOG", 3: "PREORDERED_LOG", } TreeType_value = map[string]int32{ "UNKNOWN_TREE_TYPE": 0, "LOG": 1, "PREORDERED_LOG": 3, } ) func (x TreeType) Enum() *TreeType { p := new(TreeType) *p = x return p } func (x TreeType) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (TreeType) Descriptor() protoreflect.EnumDescriptor { return file_trillian_proto_enumTypes[3].Descriptor() } func (TreeType) Type() protoreflect.EnumType { return &file_trillian_proto_enumTypes[3] } func (x TreeType) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use TreeType.Descriptor instead. func (TreeType) EnumDescriptor() ([]byte, []int) { return file_trillian_proto_rawDescGZIP(), []int{3} } // Represents a tree. // Readonly attributes are assigned at tree creation, after which they may not // be modified. // // Note: Many APIs within the rest of the code require these objects to // be provided. For safety they should be obtained via Admin API calls and // not created dynamically. type Tree struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // ID of the tree. // Readonly. TreeId int64 `protobuf:"varint,1,opt,name=tree_id,json=treeId,proto3" json:"tree_id,omitempty"` // State of the tree. // Trees are ACTIVE after creation. At any point the tree may transition // between ACTIVE, DRAINING and FROZEN states. TreeState TreeState `protobuf:"varint,2,opt,name=tree_state,json=treeState,proto3,enum=trillian.TreeState" json:"tree_state,omitempty"` // Type of the tree. // Readonly after Tree creation. Exception: Can be switched from // PREORDERED_LOG to LOG if the Tree is and remains in the FROZEN state. TreeType TreeType `protobuf:"varint,3,opt,name=tree_type,json=treeType,proto3,enum=trillian.TreeType" json:"tree_type,omitempty"` // Display name of the tree. // Optional. DisplayName string `protobuf:"bytes,8,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"` // Description of the tree, // Optional. Description string `protobuf:"bytes,9,opt,name=description,proto3" json:"description,omitempty"` // Storage-specific settings. // Varies according to the storage implementation backing Trillian. StorageSettings *anypb.Any `protobuf:"bytes,13,opt,name=storage_settings,json=storageSettings,proto3" json:"storage_settings,omitempty"` // Interval after which a new signed root is produced even if there have been // no submission. If zero, this behavior is disabled. MaxRootDuration *durationpb.Duration `protobuf:"bytes,15,opt,name=max_root_duration,json=maxRootDuration,proto3" json:"max_root_duration,omitempty"` // Time of tree creation. // Readonly. CreateTime *timestamppb.Timestamp `protobuf:"bytes,16,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty"` // Time of last tree update. // Readonly (automatically assigned on updates). UpdateTime *timestamppb.Timestamp `protobuf:"bytes,17,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty"` // If true, the tree has been deleted. // Deleted trees may be undeleted during a certain time window, after which // they're permanently deleted (and unrecoverable). // Readonly. Deleted bool `protobuf:"varint,19,opt,name=deleted,proto3" json:"deleted,omitempty"` // Time of tree deletion, if any. // Readonly. DeleteTime *timestamppb.Timestamp `protobuf:"bytes,20,opt,name=delete_time,json=deleteTime,proto3" json:"delete_time,omitempty"` } func (x *Tree) Reset() { *x = Tree{} if protoimpl.UnsafeEnabled { mi := &file_trillian_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Tree) String() string { return protoimpl.X.MessageStringOf(x) } func (*Tree) ProtoMessage() {} func (x *Tree) ProtoReflect() protoreflect.Message { mi := &file_trillian_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Tree.ProtoReflect.Descriptor instead. func (*Tree) Descriptor() ([]byte, []int) { return file_trillian_proto_rawDescGZIP(), []int{0} } func (x *Tree) GetTreeId() int64 { if x != nil { return x.TreeId } return 0 } func (x *Tree) GetTreeState() TreeState { if x != nil { return x.TreeState } return TreeState_UNKNOWN_TREE_STATE } func (x *Tree) GetTreeType() TreeType { if x != nil { return x.TreeType } return TreeType_UNKNOWN_TREE_TYPE } func (x *Tree) GetDisplayName() string { if x != nil { return x.DisplayName } return "" } func (x *Tree) GetDescription() string { if x != nil { return x.Description } return "" } func (x *Tree) GetStorageSettings() *anypb.Any { if x != nil { return x.StorageSettings } return nil } func (x *Tree) GetMaxRootDuration() *durationpb.Duration { if x != nil { return x.MaxRootDuration } return nil } func (x *Tree) GetCreateTime() *timestamppb.Timestamp { if x != nil { return x.CreateTime } return nil } func (x *Tree) GetUpdateTime() *timestamppb.Timestamp { if x != nil { return x.UpdateTime } return nil } func (x *Tree) GetDeleted() bool { if x != nil { return x.Deleted } return false } func (x *Tree) GetDeleteTime() *timestamppb.Timestamp { if x != nil { return x.DeleteTime } return nil } // SignedLogRoot represents a commitment by a Log to a particular tree. // // Note that the signature itself is no-longer provided by Trillian since // https://github.com/google/trillian/pull/2452 . // This functionality was intended to support a niche-use case but added // significant complexity and was prone to causing confusion and // misunderstanding for personality authors. type SignedLogRoot struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // log_root holds the TLS-serialization of the following structure (described // in RFC5246 notation): // // enum { v1(1), (65535)} Version; // // struct { // uint64 tree_size; // opaque root_hash<0..128>; // uint64 timestamp_nanos; // uint64 revision; // opaque metadata<0..65535>; // } LogRootV1; // // struct { // Version version; // select(version) { // case v1: LogRootV1; // } // } LogRoot; // // A serialized v1 log root will therefore be laid out as: // // +---+---+---+---+---+---+---+---+---+---+---+---+---+---+-....--+ // | ver=1 | tree_size |len| root_hash | // +---+---+---+---+---+---+---+---+---+---+---+---+---+---+-....--+ // // +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ // | timestamp_nanos | revision | // +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ // // +---+---+---+---+---+-....---+ // | len | metadata | // +---+---+---+---+---+-....---+ // // (with all integers encoded big-endian). LogRoot []byte `protobuf:"bytes,8,opt,name=log_root,json=logRoot,proto3" json:"log_root,omitempty"` } func (x *SignedLogRoot) Reset() { *x = SignedLogRoot{} if protoimpl.UnsafeEnabled { mi := &file_trillian_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *SignedLogRoot) String() string { return protoimpl.X.MessageStringOf(x) } func (*SignedLogRoot) ProtoMessage() {} func (x *SignedLogRoot) ProtoReflect() protoreflect.Message { mi := &file_trillian_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SignedLogRoot.ProtoReflect.Descriptor instead. func (*SignedLogRoot) Descriptor() ([]byte, []int) { return file_trillian_proto_rawDescGZIP(), []int{1} } func (x *SignedLogRoot) GetLogRoot() []byte { if x != nil { return x.LogRoot } return nil } // Proof holds a consistency or inclusion proof for a Merkle tree, as returned // by the API. type Proof struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // leaf_index indicates the requested leaf index when this message is used for // a leaf inclusion proof. This field is set to zero when this message is // used for a consistency proof. LeafIndex int64 `protobuf:"varint,1,opt,name=leaf_index,json=leafIndex,proto3" json:"leaf_index,omitempty"` Hashes [][]byte `protobuf:"bytes,3,rep,name=hashes,proto3" json:"hashes,omitempty"` } func (x *Proof) Reset() { *x = Proof{} if protoimpl.UnsafeEnabled { mi := &file_trillian_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Proof) String() string { return protoimpl.X.MessageStringOf(x) } func (*Proof) ProtoMessage() {} func (x *Proof) ProtoReflect() protoreflect.Message { mi := &file_trillian_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Proof.ProtoReflect.Descriptor instead. func (*Proof) Descriptor() ([]byte, []int) { return file_trillian_proto_rawDescGZIP(), []int{2} } func (x *Proof) GetLeafIndex() int64 { if x != nil { return x.LeafIndex } return 0 } func (x *Proof) GetHashes() [][]byte { if x != nil { return x.Hashes } return nil } var File_trillian_proto protoreflect.FileDescriptor var file_trillian_proto_rawDesc = []byte{ 0x0a, 0x0e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x1a, 0x19, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf1, 0x05, 0x0a, 0x04, 0x54, 0x72, 0x65, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x74, 0x72, 0x65, 0x65, 0x49, 0x64, 0x12, 0x32, 0x0a, 0x0a, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x13, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x09, 0x74, 0x72, 0x65, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x2f, 0x0a, 0x09, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x08, 0x74, 0x72, 0x65, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3f, 0x0a, 0x10, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x0f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x45, 0x0a, 0x11, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0f, 0x6d, 0x61, 0x78, 0x52, 0x6f, 0x6f, 0x74, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x3b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x13, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, 0x3b, 0x0a, 0x0b, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x08, 0x4a, 0x04, 0x08, 0x0a, 0x10, 0x0d, 0x4a, 0x04, 0x08, 0x0e, 0x10, 0x0f, 0x4a, 0x04, 0x08, 0x12, 0x10, 0x13, 0x52, 0x1e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x5f, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x52, 0x10, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0e, 0x68, 0x61, 0x73, 0x68, 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x52, 0x0d, 0x68, 0x61, 0x73, 0x68, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0b, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x52, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x52, 0x13, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x52, 0x16, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x73, 0x75, 0x69, 0x74, 0x65, 0x52, 0x1e, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x5f, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x22, 0x9d, 0x01, 0x0a, 0x0d, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x4c, 0x6f, 0x67, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x6c, 0x6f, 0x67, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6c, 0x6f, 0x67, 0x52, 0x6f, 0x6f, 0x74, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x08, 0x4a, 0x04, 0x08, 0x09, 0x10, 0x0a, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x68, 0x69, 0x6e, 0x74, 0x52, 0x06, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x52, 0x12, 0x6c, 0x6f, 0x67, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x09, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x6e, 0x61, 0x6e, 0x6f, 0x73, 0x52, 0x0d, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x22, 0x50, 0x0a, 0x05, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x65, 0x61, 0x66, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x6c, 0x65, 0x61, 0x66, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x06, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x2a, 0x44, 0x0a, 0x0d, 0x4c, 0x6f, 0x67, 0x52, 0x6f, 0x6f, 0x74, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x1b, 0x0a, 0x17, 0x4c, 0x4f, 0x47, 0x5f, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x46, 0x4f, 0x52, 0x4d, 0x41, 0x54, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x4c, 0x4f, 0x47, 0x5f, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x46, 0x4f, 0x52, 0x4d, 0x41, 0x54, 0x5f, 0x56, 0x31, 0x10, 0x01, 0x2a, 0x97, 0x01, 0x0a, 0x0c, 0x48, 0x61, 0x73, 0x68, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x19, 0x0a, 0x15, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x5f, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x52, 0x46, 0x43, 0x36, 0x39, 0x36, 0x32, 0x5f, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x54, 0x45, 0x53, 0x54, 0x5f, 0x4d, 0x41, 0x50, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x45, 0x52, 0x10, 0x02, 0x12, 0x19, 0x0a, 0x15, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x52, 0x46, 0x43, 0x36, 0x39, 0x36, 0x32, 0x5f, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x10, 0x03, 0x12, 0x15, 0x0a, 0x11, 0x43, 0x4f, 0x4e, 0x49, 0x4b, 0x53, 0x5f, 0x53, 0x48, 0x41, 0x35, 0x31, 0x32, 0x5f, 0x32, 0x35, 0x36, 0x10, 0x04, 0x12, 0x11, 0x0a, 0x0d, 0x43, 0x4f, 0x4e, 0x49, 0x4b, 0x53, 0x5f, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x10, 0x05, 0x2a, 0x8b, 0x01, 0x0a, 0x09, 0x54, 0x72, 0x65, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x16, 0x0a, 0x12, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x54, 0x52, 0x45, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x46, 0x52, 0x4f, 0x5a, 0x45, 0x4e, 0x10, 0x02, 0x12, 0x1f, 0x0a, 0x17, 0x44, 0x45, 0x50, 0x52, 0x45, 0x43, 0x41, 0x54, 0x45, 0x44, 0x5f, 0x53, 0x4f, 0x46, 0x54, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x44, 0x10, 0x03, 0x1a, 0x02, 0x08, 0x01, 0x12, 0x1f, 0x0a, 0x17, 0x44, 0x45, 0x50, 0x52, 0x45, 0x43, 0x41, 0x54, 0x45, 0x44, 0x5f, 0x48, 0x41, 0x52, 0x44, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x44, 0x10, 0x04, 0x1a, 0x02, 0x08, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x44, 0x52, 0x41, 0x49, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x05, 0x2a, 0x49, 0x0a, 0x08, 0x54, 0x72, 0x65, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x15, 0x0a, 0x11, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x54, 0x52, 0x45, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4c, 0x4f, 0x47, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x50, 0x52, 0x45, 0x4f, 0x52, 0x44, 0x45, 0x52, 0x45, 0x44, 0x5f, 0x4c, 0x4f, 0x47, 0x10, 0x03, 0x22, 0x04, 0x08, 0x02, 0x10, 0x02, 0x2a, 0x03, 0x4d, 0x41, 0x50, 0x42, 0x48, 0x0a, 0x19, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x42, 0x0d, 0x54, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x1a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( file_trillian_proto_rawDescOnce sync.Once file_trillian_proto_rawDescData = file_trillian_proto_rawDesc ) func file_trillian_proto_rawDescGZIP() []byte { file_trillian_proto_rawDescOnce.Do(func() { file_trillian_proto_rawDescData = protoimpl.X.CompressGZIP(file_trillian_proto_rawDescData) }) return file_trillian_proto_rawDescData } var file_trillian_proto_enumTypes = make([]protoimpl.EnumInfo, 4) var file_trillian_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_trillian_proto_goTypes = []any{ (LogRootFormat)(0), // 0: trillian.LogRootFormat (HashStrategy)(0), // 1: trillian.HashStrategy (TreeState)(0), // 2: trillian.TreeState (TreeType)(0), // 3: trillian.TreeType (*Tree)(nil), // 4: trillian.Tree (*SignedLogRoot)(nil), // 5: trillian.SignedLogRoot (*Proof)(nil), // 6: trillian.Proof (*anypb.Any)(nil), // 7: google.protobuf.Any (*durationpb.Duration)(nil), // 8: google.protobuf.Duration (*timestamppb.Timestamp)(nil), // 9: google.protobuf.Timestamp } var file_trillian_proto_depIdxs = []int32{ 2, // 0: trillian.Tree.tree_state:type_name -> trillian.TreeState 3, // 1: trillian.Tree.tree_type:type_name -> trillian.TreeType 7, // 2: trillian.Tree.storage_settings:type_name -> google.protobuf.Any 8, // 3: trillian.Tree.max_root_duration:type_name -> google.protobuf.Duration 9, // 4: trillian.Tree.create_time:type_name -> google.protobuf.Timestamp 9, // 5: trillian.Tree.update_time:type_name -> google.protobuf.Timestamp 9, // 6: trillian.Tree.delete_time:type_name -> google.protobuf.Timestamp 7, // [7:7] is the sub-list for method output_type 7, // [7:7] is the sub-list for method input_type 7, // [7:7] is the sub-list for extension type_name 7, // [7:7] is the sub-list for extension extendee 0, // [0:7] is the sub-list for field type_name } func init() { file_trillian_proto_init() } func file_trillian_proto_init() { if File_trillian_proto != nil { return } if !protoimpl.UnsafeEnabled { file_trillian_proto_msgTypes[0].Exporter = func(v any, i int) any { switch v := v.(*Tree); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_trillian_proto_msgTypes[1].Exporter = func(v any, i int) any { switch v := v.(*SignedLogRoot); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_trillian_proto_msgTypes[2].Exporter = func(v any, i int) any { switch v := v.(*Proof); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_trillian_proto_rawDesc, NumEnums: 4, NumMessages: 3, NumExtensions: 0, NumServices: 0, }, GoTypes: file_trillian_proto_goTypes, DependencyIndexes: file_trillian_proto_depIdxs, EnumInfos: file_trillian_proto_enumTypes, MessageInfos: file_trillian_proto_msgTypes, }.Build() File_trillian_proto = out.File file_trillian_proto_rawDesc = nil file_trillian_proto_goTypes = nil file_trillian_proto_depIdxs = nil } trillian-1.6.1/trillian.proto000066400000000000000000000174241466362047600162660ustar00rootroot00000000000000// 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. syntax = "proto3"; option java_multiple_files = true; option java_package = "com.google.trillian.proto"; option java_outer_classname = "TrillianProto"; option go_package = "github.com/google/trillian"; package trillian; import "google/protobuf/any.proto"; import "google/protobuf/duration.proto"; import "google/protobuf/timestamp.proto"; // LogRootFormat specifies the fields that are covered by the // SignedLogRoot signature, as well as their ordering and formats. enum LogRootFormat { LOG_ROOT_FORMAT_UNKNOWN = 0; LOG_ROOT_FORMAT_V1 = 1; } // What goes in here? // Things which are exposed through the public trillian APIs. // Defines the way empty / node / leaf hashes are constructed incorporating // preimage protection, which can be application specific. enum HashStrategy { // Hash strategy cannot be determined. Included to enable detection of // mismatched proto versions being used. Represents an invalid value. UNKNOWN_HASH_STRATEGY = 0; // Certificate Transparency strategy: leaf hash prefix = 0x00, node prefix = // 0x01, empty hash is digest([]byte{}), as defined in the specification. RFC6962_SHA256 = 1; // Sparse Merkle Tree strategy: leaf hash prefix = 0x00, node prefix = 0x01, // empty branch is recursively computed from empty leaf nodes. // NOT secure in a multi tree environment. For testing only. TEST_MAP_HASHER = 2; // Append-only log strategy where leaf nodes are defined as the ObjectHash. // All other properties are equal to RFC6962_SHA256. OBJECT_RFC6962_SHA256 = 3; // The CONIKS sparse tree hasher with SHA512_256 as the hash algorithm. CONIKS_SHA512_256 = 4; // The CONIKS sparse tree hasher with SHA256 as the hash algorithm. CONIKS_SHA256 = 5; } // State of the tree. enum TreeState { // Tree state cannot be determined. Included to enable detection of // mismatched proto versions being used. Represents an invalid value. UNKNOWN_TREE_STATE = 0; // Active trees are able to respond to both read and write requests. ACTIVE = 1; // Frozen trees are only able to respond to read requests, writing to a frozen // tree is forbidden. Trees should not be frozen when there are entries // in the queue that have not yet been integrated. See the DRAINING // state for this case. FROZEN = 2; // Deprecated: now tracked in Tree.deleted. DEPRECATED_SOFT_DELETED = 3 [deprecated = true]; // Deprecated: now tracked in Tree.deleted. DEPRECATED_HARD_DELETED = 4 [deprecated = true]; // A tree that is draining will continue to integrate queued entries. // No new entries should be accepted. DRAINING = 5; } // Type of the tree. enum TreeType { // Tree type cannot be determined. Included to enable detection of mismatched // proto versions being used. Represents an invalid value. UNKNOWN_TREE_TYPE = 0; // Tree represents a verifiable log. LOG = 1; // Tree represents a verifiable pre-ordered log, i.e., a log whose entries are // placed according to sequence numbers assigned outside of Trillian. PREORDERED_LOG = 3; reserved 2; reserved "MAP"; } // Represents a tree. // Readonly attributes are assigned at tree creation, after which they may not // be modified. // // Note: Many APIs within the rest of the code require these objects to // be provided. For safety they should be obtained via Admin API calls and // not created dynamically. message Tree { // ID of the tree. // Readonly. int64 tree_id = 1; // State of the tree. // Trees are ACTIVE after creation. At any point the tree may transition // between ACTIVE, DRAINING and FROZEN states. TreeState tree_state = 2; // Type of the tree. // Readonly after Tree creation. Exception: Can be switched from // PREORDERED_LOG to LOG if the Tree is and remains in the FROZEN state. TreeType tree_type = 3; // Display name of the tree. // Optional. string display_name = 8; // Description of the tree, // Optional. string description = 9; // Storage-specific settings. // Varies according to the storage implementation backing Trillian. google.protobuf.Any storage_settings = 13; // Interval after which a new signed root is produced even if there have been // no submission. If zero, this behavior is disabled. google.protobuf.Duration max_root_duration = 15; // Time of tree creation. // Readonly. google.protobuf.Timestamp create_time = 16; // Time of last tree update. // Readonly (automatically assigned on updates). google.protobuf.Timestamp update_time = 17; // If true, the tree has been deleted. // Deleted trees may be undeleted during a certain time window, after which // they're permanently deleted (and unrecoverable). // Readonly. bool deleted = 19; // Time of tree deletion, if any. // Readonly. google.protobuf.Timestamp delete_time = 20; reserved 4 to 7, 10 to 12, 14, 18; reserved "create_time_millis_since_epoch"; reserved "duplicate_policy"; reserved "hash_algorithm"; reserved "hash_strategy"; reserved "private_key"; reserved "public_key"; reserved "signature_algorithm"; reserved "signature_cipher_suite"; reserved "update_time_millis_since_epoch"; } // SignedLogRoot represents a commitment by a Log to a particular tree. // // Note that the signature itself is no-longer provided by Trillian since // https://github.com/google/trillian/pull/2452 . // This functionality was intended to support a niche-use case but added // significant complexity and was prone to causing confusion and // misunderstanding for personality authors. message SignedLogRoot { // log_root holds the TLS-serialization of the following structure (described // in RFC5246 notation): // // enum { v1(1), (65535)} Version; // struct { // uint64 tree_size; // opaque root_hash<0..128>; // uint64 timestamp_nanos; // uint64 revision; // opaque metadata<0..65535>; // } LogRootV1; // struct { // Version version; // select(version) { // case v1: LogRootV1; // } // } LogRoot; // // A serialized v1 log root will therefore be laid out as: // // +---+---+---+---+---+---+---+---+---+---+---+---+---+---+-....--+ // | ver=1 | tree_size |len| root_hash | // +---+---+---+---+---+---+---+---+---+---+---+---+---+---+-....--+ // // +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ // | timestamp_nanos | revision | // +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ // // +---+---+---+---+---+-....---+ // | len | metadata | // +---+---+---+---+---+-....---+ // // (with all integers encoded big-endian). bytes log_root = 8; reserved 1 to 7, 9; reserved "key_hint"; reserved "log_id"; reserved "log_root_signature"; reserved "root_hash"; reserved "signature"; reserved "timestamp_nanos"; reserved "tree_revision"; reserved "tree_size"; } // Proof holds a consistency or inclusion proof for a Merkle tree, as returned // by the API. message Proof { // leaf_index indicates the requested leaf index when this message is used for // a leaf inclusion proof. This field is set to zero when this message is // used for a consistency proof. int64 leaf_index = 1; repeated bytes hashes = 3; reserved 2; reserved "proof_node"; } trillian-1.6.1/trillian_admin_api.pb.go000066400000000000000000000535541466362047600201350ustar00rootroot00000000000000// 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. // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.34.2 // protoc v3.20.1 // source: trillian_admin_api.proto package trillian import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" fieldmaskpb "google.golang.org/protobuf/types/known/fieldmaskpb" reflect "reflect" sync "sync" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // ListTrees request. // No filters or pagination options are provided. type ListTreesRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // If true, deleted trees are included in the response. ShowDeleted bool `protobuf:"varint,1,opt,name=show_deleted,json=showDeleted,proto3" json:"show_deleted,omitempty"` } func (x *ListTreesRequest) Reset() { *x = ListTreesRequest{} if protoimpl.UnsafeEnabled { mi := &file_trillian_admin_api_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ListTreesRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*ListTreesRequest) ProtoMessage() {} func (x *ListTreesRequest) ProtoReflect() protoreflect.Message { mi := &file_trillian_admin_api_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ListTreesRequest.ProtoReflect.Descriptor instead. func (*ListTreesRequest) Descriptor() ([]byte, []int) { return file_trillian_admin_api_proto_rawDescGZIP(), []int{0} } func (x *ListTreesRequest) GetShowDeleted() bool { if x != nil { return x.ShowDeleted } return false } // ListTrees response. // No pagination is provided, all trees the requester has access to are // returned. type ListTreesResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Trees matching the list request filters. Tree []*Tree `protobuf:"bytes,1,rep,name=tree,proto3" json:"tree,omitempty"` } func (x *ListTreesResponse) Reset() { *x = ListTreesResponse{} if protoimpl.UnsafeEnabled { mi := &file_trillian_admin_api_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ListTreesResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*ListTreesResponse) ProtoMessage() {} func (x *ListTreesResponse) ProtoReflect() protoreflect.Message { mi := &file_trillian_admin_api_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ListTreesResponse.ProtoReflect.Descriptor instead. func (*ListTreesResponse) Descriptor() ([]byte, []int) { return file_trillian_admin_api_proto_rawDescGZIP(), []int{1} } func (x *ListTreesResponse) GetTree() []*Tree { if x != nil { return x.Tree } return nil } // GetTree request. type GetTreeRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // ID of the tree to retrieve. TreeId int64 `protobuf:"varint,1,opt,name=tree_id,json=treeId,proto3" json:"tree_id,omitempty"` } func (x *GetTreeRequest) Reset() { *x = GetTreeRequest{} if protoimpl.UnsafeEnabled { mi := &file_trillian_admin_api_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *GetTreeRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*GetTreeRequest) ProtoMessage() {} func (x *GetTreeRequest) ProtoReflect() protoreflect.Message { mi := &file_trillian_admin_api_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GetTreeRequest.ProtoReflect.Descriptor instead. func (*GetTreeRequest) Descriptor() ([]byte, []int) { return file_trillian_admin_api_proto_rawDescGZIP(), []int{2} } func (x *GetTreeRequest) GetTreeId() int64 { if x != nil { return x.TreeId } return 0 } // CreateTree request. type CreateTreeRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Tree to be created. See Tree and CreateTree for more details. Tree *Tree `protobuf:"bytes,1,opt,name=tree,proto3" json:"tree,omitempty"` } func (x *CreateTreeRequest) Reset() { *x = CreateTreeRequest{} if protoimpl.UnsafeEnabled { mi := &file_trillian_admin_api_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *CreateTreeRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*CreateTreeRequest) ProtoMessage() {} func (x *CreateTreeRequest) ProtoReflect() protoreflect.Message { mi := &file_trillian_admin_api_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use CreateTreeRequest.ProtoReflect.Descriptor instead. func (*CreateTreeRequest) Descriptor() ([]byte, []int) { return file_trillian_admin_api_proto_rawDescGZIP(), []int{3} } func (x *CreateTreeRequest) GetTree() *Tree { if x != nil { return x.Tree } return nil } // UpdateTree request. type UpdateTreeRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Tree to be updated. Tree *Tree `protobuf:"bytes,1,opt,name=tree,proto3" json:"tree,omitempty"` // Fields modified by the update request. // For example: "tree_state", "display_name", "description". UpdateMask *fieldmaskpb.FieldMask `protobuf:"bytes,2,opt,name=update_mask,json=updateMask,proto3" json:"update_mask,omitempty"` } func (x *UpdateTreeRequest) Reset() { *x = UpdateTreeRequest{} if protoimpl.UnsafeEnabled { mi := &file_trillian_admin_api_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *UpdateTreeRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*UpdateTreeRequest) ProtoMessage() {} func (x *UpdateTreeRequest) ProtoReflect() protoreflect.Message { mi := &file_trillian_admin_api_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use UpdateTreeRequest.ProtoReflect.Descriptor instead. func (*UpdateTreeRequest) Descriptor() ([]byte, []int) { return file_trillian_admin_api_proto_rawDescGZIP(), []int{4} } func (x *UpdateTreeRequest) GetTree() *Tree { if x != nil { return x.Tree } return nil } func (x *UpdateTreeRequest) GetUpdateMask() *fieldmaskpb.FieldMask { if x != nil { return x.UpdateMask } return nil } // DeleteTree request. type DeleteTreeRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // ID of the tree to delete. TreeId int64 `protobuf:"varint,1,opt,name=tree_id,json=treeId,proto3" json:"tree_id,omitempty"` } func (x *DeleteTreeRequest) Reset() { *x = DeleteTreeRequest{} if protoimpl.UnsafeEnabled { mi := &file_trillian_admin_api_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *DeleteTreeRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*DeleteTreeRequest) ProtoMessage() {} func (x *DeleteTreeRequest) ProtoReflect() protoreflect.Message { mi := &file_trillian_admin_api_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DeleteTreeRequest.ProtoReflect.Descriptor instead. func (*DeleteTreeRequest) Descriptor() ([]byte, []int) { return file_trillian_admin_api_proto_rawDescGZIP(), []int{5} } func (x *DeleteTreeRequest) GetTreeId() int64 { if x != nil { return x.TreeId } return 0 } // UndeleteTree request. type UndeleteTreeRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // ID of the tree to undelete. TreeId int64 `protobuf:"varint,1,opt,name=tree_id,json=treeId,proto3" json:"tree_id,omitempty"` } func (x *UndeleteTreeRequest) Reset() { *x = UndeleteTreeRequest{} if protoimpl.UnsafeEnabled { mi := &file_trillian_admin_api_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *UndeleteTreeRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*UndeleteTreeRequest) ProtoMessage() {} func (x *UndeleteTreeRequest) ProtoReflect() protoreflect.Message { mi := &file_trillian_admin_api_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use UndeleteTreeRequest.ProtoReflect.Descriptor instead. func (*UndeleteTreeRequest) Descriptor() ([]byte, []int) { return file_trillian_admin_api_proto_rawDescGZIP(), []int{6} } func (x *UndeleteTreeRequest) GetTreeId() int64 { if x != nil { return x.TreeId } return 0 } var File_trillian_admin_api_proto protoreflect.FileDescriptor var file_trillian_admin_api_proto_rawDesc = []byte{ 0x0a, 0x18, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x1a, 0x0e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x35, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x72, 0x65, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x68, 0x6f, 0x77, 0x5f, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x73, 0x68, 0x6f, 0x77, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x22, 0x37, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x72, 0x65, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x22, 0x0a, 0x04, 0x74, 0x72, 0x65, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x52, 0x04, 0x74, 0x72, 0x65, 0x65, 0x22, 0x29, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x54, 0x72, 0x65, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x74, 0x72, 0x65, 0x65, 0x49, 0x64, 0x22, 0x47, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x72, 0x65, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x22, 0x0a, 0x04, 0x74, 0x72, 0x65, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x52, 0x04, 0x74, 0x72, 0x65, 0x65, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x22, 0x74, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x72, 0x65, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x22, 0x0a, 0x04, 0x74, 0x72, 0x65, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x52, 0x04, 0x74, 0x72, 0x65, 0x65, 0x12, 0x3b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x73, 0x6b, 0x22, 0x2c, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x72, 0x65, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x74, 0x72, 0x65, 0x65, 0x49, 0x64, 0x22, 0x2e, 0x0a, 0x13, 0x55, 0x6e, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x72, 0x65, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x74, 0x72, 0x65, 0x65, 0x49, 0x64, 0x32, 0x86, 0x03, 0x0a, 0x0d, 0x54, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x12, 0x46, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x72, 0x65, 0x65, 0x73, 0x12, 0x1a, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x72, 0x65, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x72, 0x65, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x35, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x54, 0x72, 0x65, 0x65, 0x12, 0x18, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x72, 0x65, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x22, 0x00, 0x12, 0x3b, 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x72, 0x65, 0x65, 0x12, 0x1b, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x72, 0x65, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x22, 0x00, 0x12, 0x3b, 0x0a, 0x0a, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x72, 0x65, 0x65, 0x12, 0x1b, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x72, 0x65, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x22, 0x00, 0x12, 0x3b, 0x0a, 0x0a, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x72, 0x65, 0x65, 0x12, 0x1b, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x72, 0x65, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x22, 0x00, 0x12, 0x3f, 0x0a, 0x0c, 0x55, 0x6e, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x72, 0x65, 0x65, 0x12, 0x1d, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x55, 0x6e, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x72, 0x65, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x22, 0x00, 0x42, 0x50, 0x0a, 0x19, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x42, 0x15, 0x54, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x41, 0x70, 0x69, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x1a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( file_trillian_admin_api_proto_rawDescOnce sync.Once file_trillian_admin_api_proto_rawDescData = file_trillian_admin_api_proto_rawDesc ) func file_trillian_admin_api_proto_rawDescGZIP() []byte { file_trillian_admin_api_proto_rawDescOnce.Do(func() { file_trillian_admin_api_proto_rawDescData = protoimpl.X.CompressGZIP(file_trillian_admin_api_proto_rawDescData) }) return file_trillian_admin_api_proto_rawDescData } var file_trillian_admin_api_proto_msgTypes = make([]protoimpl.MessageInfo, 7) var file_trillian_admin_api_proto_goTypes = []any{ (*ListTreesRequest)(nil), // 0: trillian.ListTreesRequest (*ListTreesResponse)(nil), // 1: trillian.ListTreesResponse (*GetTreeRequest)(nil), // 2: trillian.GetTreeRequest (*CreateTreeRequest)(nil), // 3: trillian.CreateTreeRequest (*UpdateTreeRequest)(nil), // 4: trillian.UpdateTreeRequest (*DeleteTreeRequest)(nil), // 5: trillian.DeleteTreeRequest (*UndeleteTreeRequest)(nil), // 6: trillian.UndeleteTreeRequest (*Tree)(nil), // 7: trillian.Tree (*fieldmaskpb.FieldMask)(nil), // 8: google.protobuf.FieldMask } var file_trillian_admin_api_proto_depIdxs = []int32{ 7, // 0: trillian.ListTreesResponse.tree:type_name -> trillian.Tree 7, // 1: trillian.CreateTreeRequest.tree:type_name -> trillian.Tree 7, // 2: trillian.UpdateTreeRequest.tree:type_name -> trillian.Tree 8, // 3: trillian.UpdateTreeRequest.update_mask:type_name -> google.protobuf.FieldMask 0, // 4: trillian.TrillianAdmin.ListTrees:input_type -> trillian.ListTreesRequest 2, // 5: trillian.TrillianAdmin.GetTree:input_type -> trillian.GetTreeRequest 3, // 6: trillian.TrillianAdmin.CreateTree:input_type -> trillian.CreateTreeRequest 4, // 7: trillian.TrillianAdmin.UpdateTree:input_type -> trillian.UpdateTreeRequest 5, // 8: trillian.TrillianAdmin.DeleteTree:input_type -> trillian.DeleteTreeRequest 6, // 9: trillian.TrillianAdmin.UndeleteTree:input_type -> trillian.UndeleteTreeRequest 1, // 10: trillian.TrillianAdmin.ListTrees:output_type -> trillian.ListTreesResponse 7, // 11: trillian.TrillianAdmin.GetTree:output_type -> trillian.Tree 7, // 12: trillian.TrillianAdmin.CreateTree:output_type -> trillian.Tree 7, // 13: trillian.TrillianAdmin.UpdateTree:output_type -> trillian.Tree 7, // 14: trillian.TrillianAdmin.DeleteTree:output_type -> trillian.Tree 7, // 15: trillian.TrillianAdmin.UndeleteTree:output_type -> trillian.Tree 10, // [10:16] is the sub-list for method output_type 4, // [4:10] is the sub-list for method input_type 4, // [4:4] is the sub-list for extension type_name 4, // [4:4] is the sub-list for extension extendee 0, // [0:4] is the sub-list for field type_name } func init() { file_trillian_admin_api_proto_init() } func file_trillian_admin_api_proto_init() { if File_trillian_admin_api_proto != nil { return } file_trillian_proto_init() if !protoimpl.UnsafeEnabled { file_trillian_admin_api_proto_msgTypes[0].Exporter = func(v any, i int) any { switch v := v.(*ListTreesRequest); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_trillian_admin_api_proto_msgTypes[1].Exporter = func(v any, i int) any { switch v := v.(*ListTreesResponse); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_trillian_admin_api_proto_msgTypes[2].Exporter = func(v any, i int) any { switch v := v.(*GetTreeRequest); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_trillian_admin_api_proto_msgTypes[3].Exporter = func(v any, i int) any { switch v := v.(*CreateTreeRequest); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_trillian_admin_api_proto_msgTypes[4].Exporter = func(v any, i int) any { switch v := v.(*UpdateTreeRequest); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_trillian_admin_api_proto_msgTypes[5].Exporter = func(v any, i int) any { switch v := v.(*DeleteTreeRequest); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_trillian_admin_api_proto_msgTypes[6].Exporter = func(v any, i int) any { switch v := v.(*UndeleteTreeRequest); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_trillian_admin_api_proto_rawDesc, NumEnums: 0, NumMessages: 7, NumExtensions: 0, NumServices: 1, }, GoTypes: file_trillian_admin_api_proto_goTypes, DependencyIndexes: file_trillian_admin_api_proto_depIdxs, MessageInfos: file_trillian_admin_api_proto_msgTypes, }.Build() File_trillian_admin_api_proto = out.File file_trillian_admin_api_proto_rawDesc = nil file_trillian_admin_api_proto_goTypes = nil file_trillian_admin_api_proto_depIdxs = nil } trillian-1.6.1/trillian_admin_api.proto000066400000000000000000000061541466362047600202650ustar00rootroot00000000000000// 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. syntax = "proto3"; option java_multiple_files = true; option java_package = "com.google.trillian.proto"; option java_outer_classname = "TrillianAdminApiProto"; option go_package = "github.com/google/trillian"; package trillian; import "trillian.proto"; import "google/protobuf/field_mask.proto"; // ListTrees request. // No filters or pagination options are provided. message ListTreesRequest { // If true, deleted trees are included in the response. bool show_deleted = 1; } // ListTrees response. // No pagination is provided, all trees the requester has access to are // returned. message ListTreesResponse { // Trees matching the list request filters. repeated Tree tree = 1; } // GetTree request. message GetTreeRequest { // ID of the tree to retrieve. int64 tree_id = 1; } // CreateTree request. message CreateTreeRequest { // Tree to be created. See Tree and CreateTree for more details. Tree tree = 1; reserved 2; reserved "key_spec"; } // UpdateTree request. message UpdateTreeRequest { // Tree to be updated. Tree tree = 1; // Fields modified by the update request. // For example: "tree_state", "display_name", "description". google.protobuf.FieldMask update_mask = 2; } // DeleteTree request. message DeleteTreeRequest { // ID of the tree to delete. int64 tree_id = 1; } // UndeleteTree request. message UndeleteTreeRequest { // ID of the tree to undelete. int64 tree_id = 1; } // Trillian Administrative interface. // Allows creation and management of Trillian trees. service TrillianAdmin { // Lists all trees the requester has access to. rpc ListTrees(ListTreesRequest) returns (ListTreesResponse) {} // Retrieves a tree by ID. rpc GetTree(GetTreeRequest) returns (Tree) {} // Creates a new tree. // System-generated fields are not required and will be ignored if present, // e.g.: tree_id, create_time and update_time. // Returns the created tree, with all system-generated fields assigned. rpc CreateTree(CreateTreeRequest) returns (Tree) {} // Updates a tree. // See Tree for details. Readonly fields cannot be updated. rpc UpdateTree(UpdateTreeRequest) returns (Tree) {} // Soft-deletes a tree. // A soft-deleted tree may be undeleted for a certain period, after which // it'll be permanently deleted. rpc DeleteTree(DeleteTreeRequest) returns (Tree) {} // Undeletes a soft-deleted a tree. // A soft-deleted tree may be undeleted for a certain period, after which // it'll be permanently deleted. rpc UndeleteTree(UndeleteTreeRequest) returns (Tree) {} } trillian-1.6.1/trillian_admin_api_grpc.pb.go000066400000000000000000000334711466362047600211440ustar00rootroot00000000000000// 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. // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.5.1 // - protoc v3.20.1 // source: trillian_admin_api.proto package trillian import ( context "context" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" ) // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. // Requires gRPC-Go v1.64.0 or later. const _ = grpc.SupportPackageIsVersion9 const ( TrillianAdmin_ListTrees_FullMethodName = "/trillian.TrillianAdmin/ListTrees" TrillianAdmin_GetTree_FullMethodName = "/trillian.TrillianAdmin/GetTree" TrillianAdmin_CreateTree_FullMethodName = "/trillian.TrillianAdmin/CreateTree" TrillianAdmin_UpdateTree_FullMethodName = "/trillian.TrillianAdmin/UpdateTree" TrillianAdmin_DeleteTree_FullMethodName = "/trillian.TrillianAdmin/DeleteTree" TrillianAdmin_UndeleteTree_FullMethodName = "/trillian.TrillianAdmin/UndeleteTree" ) // TrillianAdminClient is the client API for TrillianAdmin service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. // // Trillian Administrative interface. // Allows creation and management of Trillian trees. type TrillianAdminClient interface { // Lists all trees the requester has access to. ListTrees(ctx context.Context, in *ListTreesRequest, opts ...grpc.CallOption) (*ListTreesResponse, error) // Retrieves a tree by ID. GetTree(ctx context.Context, in *GetTreeRequest, opts ...grpc.CallOption) (*Tree, error) // Creates a new tree. // System-generated fields are not required and will be ignored if present, // e.g.: tree_id, create_time and update_time. // Returns the created tree, with all system-generated fields assigned. CreateTree(ctx context.Context, in *CreateTreeRequest, opts ...grpc.CallOption) (*Tree, error) // Updates a tree. // See Tree for details. Readonly fields cannot be updated. UpdateTree(ctx context.Context, in *UpdateTreeRequest, opts ...grpc.CallOption) (*Tree, error) // Soft-deletes a tree. // A soft-deleted tree may be undeleted for a certain period, after which // it'll be permanently deleted. DeleteTree(ctx context.Context, in *DeleteTreeRequest, opts ...grpc.CallOption) (*Tree, error) // Undeletes a soft-deleted a tree. // A soft-deleted tree may be undeleted for a certain period, after which // it'll be permanently deleted. UndeleteTree(ctx context.Context, in *UndeleteTreeRequest, opts ...grpc.CallOption) (*Tree, error) } type trillianAdminClient struct { cc grpc.ClientConnInterface } func NewTrillianAdminClient(cc grpc.ClientConnInterface) TrillianAdminClient { return &trillianAdminClient{cc} } func (c *trillianAdminClient) ListTrees(ctx context.Context, in *ListTreesRequest, opts ...grpc.CallOption) (*ListTreesResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ListTreesResponse) err := c.cc.Invoke(ctx, TrillianAdmin_ListTrees_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *trillianAdminClient) GetTree(ctx context.Context, in *GetTreeRequest, opts ...grpc.CallOption) (*Tree, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(Tree) err := c.cc.Invoke(ctx, TrillianAdmin_GetTree_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *trillianAdminClient) CreateTree(ctx context.Context, in *CreateTreeRequest, opts ...grpc.CallOption) (*Tree, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(Tree) err := c.cc.Invoke(ctx, TrillianAdmin_CreateTree_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *trillianAdminClient) UpdateTree(ctx context.Context, in *UpdateTreeRequest, opts ...grpc.CallOption) (*Tree, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(Tree) err := c.cc.Invoke(ctx, TrillianAdmin_UpdateTree_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *trillianAdminClient) DeleteTree(ctx context.Context, in *DeleteTreeRequest, opts ...grpc.CallOption) (*Tree, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(Tree) err := c.cc.Invoke(ctx, TrillianAdmin_DeleteTree_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *trillianAdminClient) UndeleteTree(ctx context.Context, in *UndeleteTreeRequest, opts ...grpc.CallOption) (*Tree, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(Tree) err := c.cc.Invoke(ctx, TrillianAdmin_UndeleteTree_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } // TrillianAdminServer is the server API for TrillianAdmin service. // All implementations should embed UnimplementedTrillianAdminServer // for forward compatibility. // // Trillian Administrative interface. // Allows creation and management of Trillian trees. type TrillianAdminServer interface { // Lists all trees the requester has access to. ListTrees(context.Context, *ListTreesRequest) (*ListTreesResponse, error) // Retrieves a tree by ID. GetTree(context.Context, *GetTreeRequest) (*Tree, error) // Creates a new tree. // System-generated fields are not required and will be ignored if present, // e.g.: tree_id, create_time and update_time. // Returns the created tree, with all system-generated fields assigned. CreateTree(context.Context, *CreateTreeRequest) (*Tree, error) // Updates a tree. // See Tree for details. Readonly fields cannot be updated. UpdateTree(context.Context, *UpdateTreeRequest) (*Tree, error) // Soft-deletes a tree. // A soft-deleted tree may be undeleted for a certain period, after which // it'll be permanently deleted. DeleteTree(context.Context, *DeleteTreeRequest) (*Tree, error) // Undeletes a soft-deleted a tree. // A soft-deleted tree may be undeleted for a certain period, after which // it'll be permanently deleted. UndeleteTree(context.Context, *UndeleteTreeRequest) (*Tree, error) } // UnimplementedTrillianAdminServer should be embedded to have // forward compatible implementations. // // NOTE: this should be embedded by value instead of pointer to avoid a nil // pointer dereference when methods are called. type UnimplementedTrillianAdminServer struct{} func (UnimplementedTrillianAdminServer) ListTrees(context.Context, *ListTreesRequest) (*ListTreesResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ListTrees not implemented") } func (UnimplementedTrillianAdminServer) GetTree(context.Context, *GetTreeRequest) (*Tree, error) { return nil, status.Errorf(codes.Unimplemented, "method GetTree not implemented") } func (UnimplementedTrillianAdminServer) CreateTree(context.Context, *CreateTreeRequest) (*Tree, error) { return nil, status.Errorf(codes.Unimplemented, "method CreateTree not implemented") } func (UnimplementedTrillianAdminServer) UpdateTree(context.Context, *UpdateTreeRequest) (*Tree, error) { return nil, status.Errorf(codes.Unimplemented, "method UpdateTree not implemented") } func (UnimplementedTrillianAdminServer) DeleteTree(context.Context, *DeleteTreeRequest) (*Tree, error) { return nil, status.Errorf(codes.Unimplemented, "method DeleteTree not implemented") } func (UnimplementedTrillianAdminServer) UndeleteTree(context.Context, *UndeleteTreeRequest) (*Tree, error) { return nil, status.Errorf(codes.Unimplemented, "method UndeleteTree not implemented") } func (UnimplementedTrillianAdminServer) testEmbeddedByValue() {} // UnsafeTrillianAdminServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to TrillianAdminServer will // result in compilation errors. type UnsafeTrillianAdminServer interface { mustEmbedUnimplementedTrillianAdminServer() } func RegisterTrillianAdminServer(s grpc.ServiceRegistrar, srv TrillianAdminServer) { // If the following call pancis, it indicates UnimplementedTrillianAdminServer was // embedded by pointer and is nil. This will cause panics if an // unimplemented method is ever invoked, so we test this at initialization // time to prevent it from happening at runtime later due to I/O. if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { t.testEmbeddedByValue() } s.RegisterService(&TrillianAdmin_ServiceDesc, srv) } func _TrillianAdmin_ListTrees_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ListTreesRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(TrillianAdminServer).ListTrees(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: TrillianAdmin_ListTrees_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(TrillianAdminServer).ListTrees(ctx, req.(*ListTreesRequest)) } return interceptor(ctx, in, info, handler) } func _TrillianAdmin_GetTree_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(GetTreeRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(TrillianAdminServer).GetTree(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: TrillianAdmin_GetTree_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(TrillianAdminServer).GetTree(ctx, req.(*GetTreeRequest)) } return interceptor(ctx, in, info, handler) } func _TrillianAdmin_CreateTree_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(CreateTreeRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(TrillianAdminServer).CreateTree(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: TrillianAdmin_CreateTree_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(TrillianAdminServer).CreateTree(ctx, req.(*CreateTreeRequest)) } return interceptor(ctx, in, info, handler) } func _TrillianAdmin_UpdateTree_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(UpdateTreeRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(TrillianAdminServer).UpdateTree(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: TrillianAdmin_UpdateTree_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(TrillianAdminServer).UpdateTree(ctx, req.(*UpdateTreeRequest)) } return interceptor(ctx, in, info, handler) } func _TrillianAdmin_DeleteTree_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(DeleteTreeRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(TrillianAdminServer).DeleteTree(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: TrillianAdmin_DeleteTree_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(TrillianAdminServer).DeleteTree(ctx, req.(*DeleteTreeRequest)) } return interceptor(ctx, in, info, handler) } func _TrillianAdmin_UndeleteTree_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(UndeleteTreeRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(TrillianAdminServer).UndeleteTree(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: TrillianAdmin_UndeleteTree_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(TrillianAdminServer).UndeleteTree(ctx, req.(*UndeleteTreeRequest)) } return interceptor(ctx, in, info, handler) } // TrillianAdmin_ServiceDesc is the grpc.ServiceDesc for TrillianAdmin service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var TrillianAdmin_ServiceDesc = grpc.ServiceDesc{ ServiceName: "trillian.TrillianAdmin", HandlerType: (*TrillianAdminServer)(nil), Methods: []grpc.MethodDesc{ { MethodName: "ListTrees", Handler: _TrillianAdmin_ListTrees_Handler, }, { MethodName: "GetTree", Handler: _TrillianAdmin_GetTree_Handler, }, { MethodName: "CreateTree", Handler: _TrillianAdmin_CreateTree_Handler, }, { MethodName: "UpdateTree", Handler: _TrillianAdmin_UpdateTree_Handler, }, { MethodName: "DeleteTree", Handler: _TrillianAdmin_DeleteTree_Handler, }, { MethodName: "UndeleteTree", Handler: _TrillianAdmin_UndeleteTree_Handler, }, }, Streams: []grpc.StreamDesc{}, Metadata: "trillian_admin_api.proto", } trillian-1.6.1/trillian_log_api.pb.go000066400000000000000000002375571466362047600176350ustar00rootroot00000000000000// 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. // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.34.2 // protoc v3.20.1 // source: trillian_log_api.proto package trillian import ( status "google.golang.org/genproto/googleapis/rpc/status" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" timestamppb "google.golang.org/protobuf/types/known/timestamppb" reflect "reflect" sync "sync" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // ChargeTo describes the user(s) associated with the request whose quota should // be checked and charged. type ChargeTo struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // user is a list of personality-defined strings. // Trillian will treat them as /User/%{user}/... keys when checking and // charging quota. // If one or more of the specified users has insufficient quota, the // request will be denied. // // As an example, a Certificate Transparency frontend might set the following // user strings when sending a QueueLeaf request to the Trillian log: // - The requesting IP address. // This would limit the number of requests per IP. // - The "intermediate-" for each of the intermediate certificates in // the submitted chain. // This would have the effect of limiting the rate of submissions under // a given intermediate/root. User []string `protobuf:"bytes,1,rep,name=user,proto3" json:"user,omitempty"` } func (x *ChargeTo) Reset() { *x = ChargeTo{} if protoimpl.UnsafeEnabled { mi := &file_trillian_log_api_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ChargeTo) String() string { return protoimpl.X.MessageStringOf(x) } func (*ChargeTo) ProtoMessage() {} func (x *ChargeTo) ProtoReflect() protoreflect.Message { mi := &file_trillian_log_api_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ChargeTo.ProtoReflect.Descriptor instead. func (*ChargeTo) Descriptor() ([]byte, []int) { return file_trillian_log_api_proto_rawDescGZIP(), []int{0} } func (x *ChargeTo) GetUser() []string { if x != nil { return x.User } return nil } type QueueLeafRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields LogId int64 `protobuf:"varint,1,opt,name=log_id,json=logId,proto3" json:"log_id,omitempty"` Leaf *LogLeaf `protobuf:"bytes,2,opt,name=leaf,proto3" json:"leaf,omitempty"` ChargeTo *ChargeTo `protobuf:"bytes,3,opt,name=charge_to,json=chargeTo,proto3" json:"charge_to,omitempty"` } func (x *QueueLeafRequest) Reset() { *x = QueueLeafRequest{} if protoimpl.UnsafeEnabled { mi := &file_trillian_log_api_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *QueueLeafRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*QueueLeafRequest) ProtoMessage() {} func (x *QueueLeafRequest) ProtoReflect() protoreflect.Message { mi := &file_trillian_log_api_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use QueueLeafRequest.ProtoReflect.Descriptor instead. func (*QueueLeafRequest) Descriptor() ([]byte, []int) { return file_trillian_log_api_proto_rawDescGZIP(), []int{1} } func (x *QueueLeafRequest) GetLogId() int64 { if x != nil { return x.LogId } return 0 } func (x *QueueLeafRequest) GetLeaf() *LogLeaf { if x != nil { return x.Leaf } return nil } func (x *QueueLeafRequest) GetChargeTo() *ChargeTo { if x != nil { return x.ChargeTo } return nil } type QueueLeafResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // queued_leaf describes the leaf which is or will be incorporated into the // Log. If the submitted leaf was already present in the Log (as indicated by // its leaf identity hash), then the returned leaf will be the pre-existing // leaf entry rather than the submitted leaf. QueuedLeaf *QueuedLogLeaf `protobuf:"bytes,2,opt,name=queued_leaf,json=queuedLeaf,proto3" json:"queued_leaf,omitempty"` } func (x *QueueLeafResponse) Reset() { *x = QueueLeafResponse{} if protoimpl.UnsafeEnabled { mi := &file_trillian_log_api_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *QueueLeafResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*QueueLeafResponse) ProtoMessage() {} func (x *QueueLeafResponse) ProtoReflect() protoreflect.Message { mi := &file_trillian_log_api_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use QueueLeafResponse.ProtoReflect.Descriptor instead. func (*QueueLeafResponse) Descriptor() ([]byte, []int) { return file_trillian_log_api_proto_rawDescGZIP(), []int{2} } func (x *QueueLeafResponse) GetQueuedLeaf() *QueuedLogLeaf { if x != nil { return x.QueuedLeaf } return nil } type GetInclusionProofRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields LogId int64 `protobuf:"varint,1,opt,name=log_id,json=logId,proto3" json:"log_id,omitempty"` LeafIndex int64 `protobuf:"varint,2,opt,name=leaf_index,json=leafIndex,proto3" json:"leaf_index,omitempty"` TreeSize int64 `protobuf:"varint,3,opt,name=tree_size,json=treeSize,proto3" json:"tree_size,omitempty"` ChargeTo *ChargeTo `protobuf:"bytes,4,opt,name=charge_to,json=chargeTo,proto3" json:"charge_to,omitempty"` } func (x *GetInclusionProofRequest) Reset() { *x = GetInclusionProofRequest{} if protoimpl.UnsafeEnabled { mi := &file_trillian_log_api_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *GetInclusionProofRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*GetInclusionProofRequest) ProtoMessage() {} func (x *GetInclusionProofRequest) ProtoReflect() protoreflect.Message { mi := &file_trillian_log_api_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GetInclusionProofRequest.ProtoReflect.Descriptor instead. func (*GetInclusionProofRequest) Descriptor() ([]byte, []int) { return file_trillian_log_api_proto_rawDescGZIP(), []int{3} } func (x *GetInclusionProofRequest) GetLogId() int64 { if x != nil { return x.LogId } return 0 } func (x *GetInclusionProofRequest) GetLeafIndex() int64 { if x != nil { return x.LeafIndex } return 0 } func (x *GetInclusionProofRequest) GetTreeSize() int64 { if x != nil { return x.TreeSize } return 0 } func (x *GetInclusionProofRequest) GetChargeTo() *ChargeTo { if x != nil { return x.ChargeTo } return nil } type GetInclusionProofResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // The proof field may be empty if the requested tree_size was larger // than that available at the server (e.g. because there is skew between // server instances, and an earlier client request was processed by a // more up-to-date instance). In this case, the signed_log_root // field will indicate the tree size that the server is aware of, and // the proof field will be empty. Proof *Proof `protobuf:"bytes,2,opt,name=proof,proto3" json:"proof,omitempty"` SignedLogRoot *SignedLogRoot `protobuf:"bytes,3,opt,name=signed_log_root,json=signedLogRoot,proto3" json:"signed_log_root,omitempty"` } func (x *GetInclusionProofResponse) Reset() { *x = GetInclusionProofResponse{} if protoimpl.UnsafeEnabled { mi := &file_trillian_log_api_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *GetInclusionProofResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*GetInclusionProofResponse) ProtoMessage() {} func (x *GetInclusionProofResponse) ProtoReflect() protoreflect.Message { mi := &file_trillian_log_api_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GetInclusionProofResponse.ProtoReflect.Descriptor instead. func (*GetInclusionProofResponse) Descriptor() ([]byte, []int) { return file_trillian_log_api_proto_rawDescGZIP(), []int{4} } func (x *GetInclusionProofResponse) GetProof() *Proof { if x != nil { return x.Proof } return nil } func (x *GetInclusionProofResponse) GetSignedLogRoot() *SignedLogRoot { if x != nil { return x.SignedLogRoot } return nil } type GetInclusionProofByHashRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields LogId int64 `protobuf:"varint,1,opt,name=log_id,json=logId,proto3" json:"log_id,omitempty"` // The leaf hash field provides the Merkle tree hash of the leaf entry // to be retrieved. LeafHash []byte `protobuf:"bytes,2,opt,name=leaf_hash,json=leafHash,proto3" json:"leaf_hash,omitempty"` TreeSize int64 `protobuf:"varint,3,opt,name=tree_size,json=treeSize,proto3" json:"tree_size,omitempty"` OrderBySequence bool `protobuf:"varint,4,opt,name=order_by_sequence,json=orderBySequence,proto3" json:"order_by_sequence,omitempty"` ChargeTo *ChargeTo `protobuf:"bytes,5,opt,name=charge_to,json=chargeTo,proto3" json:"charge_to,omitempty"` } func (x *GetInclusionProofByHashRequest) Reset() { *x = GetInclusionProofByHashRequest{} if protoimpl.UnsafeEnabled { mi := &file_trillian_log_api_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *GetInclusionProofByHashRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*GetInclusionProofByHashRequest) ProtoMessage() {} func (x *GetInclusionProofByHashRequest) ProtoReflect() protoreflect.Message { mi := &file_trillian_log_api_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GetInclusionProofByHashRequest.ProtoReflect.Descriptor instead. func (*GetInclusionProofByHashRequest) Descriptor() ([]byte, []int) { return file_trillian_log_api_proto_rawDescGZIP(), []int{5} } func (x *GetInclusionProofByHashRequest) GetLogId() int64 { if x != nil { return x.LogId } return 0 } func (x *GetInclusionProofByHashRequest) GetLeafHash() []byte { if x != nil { return x.LeafHash } return nil } func (x *GetInclusionProofByHashRequest) GetTreeSize() int64 { if x != nil { return x.TreeSize } return 0 } func (x *GetInclusionProofByHashRequest) GetOrderBySequence() bool { if x != nil { return x.OrderBySequence } return false } func (x *GetInclusionProofByHashRequest) GetChargeTo() *ChargeTo { if x != nil { return x.ChargeTo } return nil } type GetInclusionProofByHashResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Logs can potentially contain leaves with duplicate hashes so it's possible // for this to return multiple proofs. If the leaf index for a particular // instance of the requested Merkle leaf hash is beyond the requested tree // size, the corresponding proof entry will be missing. Proof []*Proof `protobuf:"bytes,2,rep,name=proof,proto3" json:"proof,omitempty"` SignedLogRoot *SignedLogRoot `protobuf:"bytes,3,opt,name=signed_log_root,json=signedLogRoot,proto3" json:"signed_log_root,omitempty"` } func (x *GetInclusionProofByHashResponse) Reset() { *x = GetInclusionProofByHashResponse{} if protoimpl.UnsafeEnabled { mi := &file_trillian_log_api_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *GetInclusionProofByHashResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*GetInclusionProofByHashResponse) ProtoMessage() {} func (x *GetInclusionProofByHashResponse) ProtoReflect() protoreflect.Message { mi := &file_trillian_log_api_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GetInclusionProofByHashResponse.ProtoReflect.Descriptor instead. func (*GetInclusionProofByHashResponse) Descriptor() ([]byte, []int) { return file_trillian_log_api_proto_rawDescGZIP(), []int{6} } func (x *GetInclusionProofByHashResponse) GetProof() []*Proof { if x != nil { return x.Proof } return nil } func (x *GetInclusionProofByHashResponse) GetSignedLogRoot() *SignedLogRoot { if x != nil { return x.SignedLogRoot } return nil } type GetConsistencyProofRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields LogId int64 `protobuf:"varint,1,opt,name=log_id,json=logId,proto3" json:"log_id,omitempty"` FirstTreeSize int64 `protobuf:"varint,2,opt,name=first_tree_size,json=firstTreeSize,proto3" json:"first_tree_size,omitempty"` SecondTreeSize int64 `protobuf:"varint,3,opt,name=second_tree_size,json=secondTreeSize,proto3" json:"second_tree_size,omitempty"` ChargeTo *ChargeTo `protobuf:"bytes,4,opt,name=charge_to,json=chargeTo,proto3" json:"charge_to,omitempty"` } func (x *GetConsistencyProofRequest) Reset() { *x = GetConsistencyProofRequest{} if protoimpl.UnsafeEnabled { mi := &file_trillian_log_api_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *GetConsistencyProofRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*GetConsistencyProofRequest) ProtoMessage() {} func (x *GetConsistencyProofRequest) ProtoReflect() protoreflect.Message { mi := &file_trillian_log_api_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GetConsistencyProofRequest.ProtoReflect.Descriptor instead. func (*GetConsistencyProofRequest) Descriptor() ([]byte, []int) { return file_trillian_log_api_proto_rawDescGZIP(), []int{7} } func (x *GetConsistencyProofRequest) GetLogId() int64 { if x != nil { return x.LogId } return 0 } func (x *GetConsistencyProofRequest) GetFirstTreeSize() int64 { if x != nil { return x.FirstTreeSize } return 0 } func (x *GetConsistencyProofRequest) GetSecondTreeSize() int64 { if x != nil { return x.SecondTreeSize } return 0 } func (x *GetConsistencyProofRequest) GetChargeTo() *ChargeTo { if x != nil { return x.ChargeTo } return nil } type GetConsistencyProofResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // The proof field may be empty if the requested tree_size was larger // than that available at the server (e.g. because there is skew between // server instances, and an earlier client request was processed by a // more up-to-date instance). In this case, the signed_log_root // field will indicate the tree size that the server is aware of, and // the proof field will be empty. Proof *Proof `protobuf:"bytes,2,opt,name=proof,proto3" json:"proof,omitempty"` SignedLogRoot *SignedLogRoot `protobuf:"bytes,3,opt,name=signed_log_root,json=signedLogRoot,proto3" json:"signed_log_root,omitempty"` } func (x *GetConsistencyProofResponse) Reset() { *x = GetConsistencyProofResponse{} if protoimpl.UnsafeEnabled { mi := &file_trillian_log_api_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *GetConsistencyProofResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*GetConsistencyProofResponse) ProtoMessage() {} func (x *GetConsistencyProofResponse) ProtoReflect() protoreflect.Message { mi := &file_trillian_log_api_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GetConsistencyProofResponse.ProtoReflect.Descriptor instead. func (*GetConsistencyProofResponse) Descriptor() ([]byte, []int) { return file_trillian_log_api_proto_rawDescGZIP(), []int{8} } func (x *GetConsistencyProofResponse) GetProof() *Proof { if x != nil { return x.Proof } return nil } func (x *GetConsistencyProofResponse) GetSignedLogRoot() *SignedLogRoot { if x != nil { return x.SignedLogRoot } return nil } type GetLatestSignedLogRootRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields LogId int64 `protobuf:"varint,1,opt,name=log_id,json=logId,proto3" json:"log_id,omitempty"` ChargeTo *ChargeTo `protobuf:"bytes,2,opt,name=charge_to,json=chargeTo,proto3" json:"charge_to,omitempty"` // If first_tree_size is non-zero, the response will include a consistency // proof between first_tree_size and the new tree size (if not smaller). FirstTreeSize int64 `protobuf:"varint,3,opt,name=first_tree_size,json=firstTreeSize,proto3" json:"first_tree_size,omitempty"` } func (x *GetLatestSignedLogRootRequest) Reset() { *x = GetLatestSignedLogRootRequest{} if protoimpl.UnsafeEnabled { mi := &file_trillian_log_api_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *GetLatestSignedLogRootRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*GetLatestSignedLogRootRequest) ProtoMessage() {} func (x *GetLatestSignedLogRootRequest) ProtoReflect() protoreflect.Message { mi := &file_trillian_log_api_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GetLatestSignedLogRootRequest.ProtoReflect.Descriptor instead. func (*GetLatestSignedLogRootRequest) Descriptor() ([]byte, []int) { return file_trillian_log_api_proto_rawDescGZIP(), []int{9} } func (x *GetLatestSignedLogRootRequest) GetLogId() int64 { if x != nil { return x.LogId } return 0 } func (x *GetLatestSignedLogRootRequest) GetChargeTo() *ChargeTo { if x != nil { return x.ChargeTo } return nil } func (x *GetLatestSignedLogRootRequest) GetFirstTreeSize() int64 { if x != nil { return x.FirstTreeSize } return 0 } type GetLatestSignedLogRootResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields SignedLogRoot *SignedLogRoot `protobuf:"bytes,2,opt,name=signed_log_root,json=signedLogRoot,proto3" json:"signed_log_root,omitempty"` // proof is filled in with a consistency proof if first_tree_size in // GetLatestSignedLogRootRequest is non-zero (and within the tree size // available at the server). Proof *Proof `protobuf:"bytes,3,opt,name=proof,proto3" json:"proof,omitempty"` } func (x *GetLatestSignedLogRootResponse) Reset() { *x = GetLatestSignedLogRootResponse{} if protoimpl.UnsafeEnabled { mi := &file_trillian_log_api_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *GetLatestSignedLogRootResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*GetLatestSignedLogRootResponse) ProtoMessage() {} func (x *GetLatestSignedLogRootResponse) ProtoReflect() protoreflect.Message { mi := &file_trillian_log_api_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GetLatestSignedLogRootResponse.ProtoReflect.Descriptor instead. func (*GetLatestSignedLogRootResponse) Descriptor() ([]byte, []int) { return file_trillian_log_api_proto_rawDescGZIP(), []int{10} } func (x *GetLatestSignedLogRootResponse) GetSignedLogRoot() *SignedLogRoot { if x != nil { return x.SignedLogRoot } return nil } func (x *GetLatestSignedLogRootResponse) GetProof() *Proof { if x != nil { return x.Proof } return nil } type GetEntryAndProofRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields LogId int64 `protobuf:"varint,1,opt,name=log_id,json=logId,proto3" json:"log_id,omitempty"` LeafIndex int64 `protobuf:"varint,2,opt,name=leaf_index,json=leafIndex,proto3" json:"leaf_index,omitempty"` TreeSize int64 `protobuf:"varint,3,opt,name=tree_size,json=treeSize,proto3" json:"tree_size,omitempty"` ChargeTo *ChargeTo `protobuf:"bytes,4,opt,name=charge_to,json=chargeTo,proto3" json:"charge_to,omitempty"` } func (x *GetEntryAndProofRequest) Reset() { *x = GetEntryAndProofRequest{} if protoimpl.UnsafeEnabled { mi := &file_trillian_log_api_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *GetEntryAndProofRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*GetEntryAndProofRequest) ProtoMessage() {} func (x *GetEntryAndProofRequest) ProtoReflect() protoreflect.Message { mi := &file_trillian_log_api_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GetEntryAndProofRequest.ProtoReflect.Descriptor instead. func (*GetEntryAndProofRequest) Descriptor() ([]byte, []int) { return file_trillian_log_api_proto_rawDescGZIP(), []int{11} } func (x *GetEntryAndProofRequest) GetLogId() int64 { if x != nil { return x.LogId } return 0 } func (x *GetEntryAndProofRequest) GetLeafIndex() int64 { if x != nil { return x.LeafIndex } return 0 } func (x *GetEntryAndProofRequest) GetTreeSize() int64 { if x != nil { return x.TreeSize } return 0 } func (x *GetEntryAndProofRequest) GetChargeTo() *ChargeTo { if x != nil { return x.ChargeTo } return nil } type GetEntryAndProofResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Proof *Proof `protobuf:"bytes,2,opt,name=proof,proto3" json:"proof,omitempty"` Leaf *LogLeaf `protobuf:"bytes,3,opt,name=leaf,proto3" json:"leaf,omitempty"` SignedLogRoot *SignedLogRoot `protobuf:"bytes,4,opt,name=signed_log_root,json=signedLogRoot,proto3" json:"signed_log_root,omitempty"` } func (x *GetEntryAndProofResponse) Reset() { *x = GetEntryAndProofResponse{} if protoimpl.UnsafeEnabled { mi := &file_trillian_log_api_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *GetEntryAndProofResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*GetEntryAndProofResponse) ProtoMessage() {} func (x *GetEntryAndProofResponse) ProtoReflect() protoreflect.Message { mi := &file_trillian_log_api_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GetEntryAndProofResponse.ProtoReflect.Descriptor instead. func (*GetEntryAndProofResponse) Descriptor() ([]byte, []int) { return file_trillian_log_api_proto_rawDescGZIP(), []int{12} } func (x *GetEntryAndProofResponse) GetProof() *Proof { if x != nil { return x.Proof } return nil } func (x *GetEntryAndProofResponse) GetLeaf() *LogLeaf { if x != nil { return x.Leaf } return nil } func (x *GetEntryAndProofResponse) GetSignedLogRoot() *SignedLogRoot { if x != nil { return x.SignedLogRoot } return nil } type InitLogRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields LogId int64 `protobuf:"varint,1,opt,name=log_id,json=logId,proto3" json:"log_id,omitempty"` ChargeTo *ChargeTo `protobuf:"bytes,2,opt,name=charge_to,json=chargeTo,proto3" json:"charge_to,omitempty"` } func (x *InitLogRequest) Reset() { *x = InitLogRequest{} if protoimpl.UnsafeEnabled { mi := &file_trillian_log_api_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *InitLogRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*InitLogRequest) ProtoMessage() {} func (x *InitLogRequest) ProtoReflect() protoreflect.Message { mi := &file_trillian_log_api_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use InitLogRequest.ProtoReflect.Descriptor instead. func (*InitLogRequest) Descriptor() ([]byte, []int) { return file_trillian_log_api_proto_rawDescGZIP(), []int{13} } func (x *InitLogRequest) GetLogId() int64 { if x != nil { return x.LogId } return 0 } func (x *InitLogRequest) GetChargeTo() *ChargeTo { if x != nil { return x.ChargeTo } return nil } type InitLogResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Created *SignedLogRoot `protobuf:"bytes,1,opt,name=created,proto3" json:"created,omitempty"` } func (x *InitLogResponse) Reset() { *x = InitLogResponse{} if protoimpl.UnsafeEnabled { mi := &file_trillian_log_api_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *InitLogResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*InitLogResponse) ProtoMessage() {} func (x *InitLogResponse) ProtoReflect() protoreflect.Message { mi := &file_trillian_log_api_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use InitLogResponse.ProtoReflect.Descriptor instead. func (*InitLogResponse) Descriptor() ([]byte, []int) { return file_trillian_log_api_proto_rawDescGZIP(), []int{14} } func (x *InitLogResponse) GetCreated() *SignedLogRoot { if x != nil { return x.Created } return nil } type AddSequencedLeavesRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields LogId int64 `protobuf:"varint,1,opt,name=log_id,json=logId,proto3" json:"log_id,omitempty"` Leaves []*LogLeaf `protobuf:"bytes,2,rep,name=leaves,proto3" json:"leaves,omitempty"` ChargeTo *ChargeTo `protobuf:"bytes,4,opt,name=charge_to,json=chargeTo,proto3" json:"charge_to,omitempty"` } func (x *AddSequencedLeavesRequest) Reset() { *x = AddSequencedLeavesRequest{} if protoimpl.UnsafeEnabled { mi := &file_trillian_log_api_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *AddSequencedLeavesRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*AddSequencedLeavesRequest) ProtoMessage() {} func (x *AddSequencedLeavesRequest) ProtoReflect() protoreflect.Message { mi := &file_trillian_log_api_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AddSequencedLeavesRequest.ProtoReflect.Descriptor instead. func (*AddSequencedLeavesRequest) Descriptor() ([]byte, []int) { return file_trillian_log_api_proto_rawDescGZIP(), []int{15} } func (x *AddSequencedLeavesRequest) GetLogId() int64 { if x != nil { return x.LogId } return 0 } func (x *AddSequencedLeavesRequest) GetLeaves() []*LogLeaf { if x != nil { return x.Leaves } return nil } func (x *AddSequencedLeavesRequest) GetChargeTo() *ChargeTo { if x != nil { return x.ChargeTo } return nil } type AddSequencedLeavesResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Same number and order as in the corresponding request. Results []*QueuedLogLeaf `protobuf:"bytes,2,rep,name=results,proto3" json:"results,omitempty"` } func (x *AddSequencedLeavesResponse) Reset() { *x = AddSequencedLeavesResponse{} if protoimpl.UnsafeEnabled { mi := &file_trillian_log_api_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *AddSequencedLeavesResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*AddSequencedLeavesResponse) ProtoMessage() {} func (x *AddSequencedLeavesResponse) ProtoReflect() protoreflect.Message { mi := &file_trillian_log_api_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AddSequencedLeavesResponse.ProtoReflect.Descriptor instead. func (*AddSequencedLeavesResponse) Descriptor() ([]byte, []int) { return file_trillian_log_api_proto_rawDescGZIP(), []int{16} } func (x *AddSequencedLeavesResponse) GetResults() []*QueuedLogLeaf { if x != nil { return x.Results } return nil } type GetLeavesByRangeRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields LogId int64 `protobuf:"varint,1,opt,name=log_id,json=logId,proto3" json:"log_id,omitempty"` StartIndex int64 `protobuf:"varint,2,opt,name=start_index,json=startIndex,proto3" json:"start_index,omitempty"` Count int64 `protobuf:"varint,3,opt,name=count,proto3" json:"count,omitempty"` ChargeTo *ChargeTo `protobuf:"bytes,4,opt,name=charge_to,json=chargeTo,proto3" json:"charge_to,omitempty"` } func (x *GetLeavesByRangeRequest) Reset() { *x = GetLeavesByRangeRequest{} if protoimpl.UnsafeEnabled { mi := &file_trillian_log_api_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *GetLeavesByRangeRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*GetLeavesByRangeRequest) ProtoMessage() {} func (x *GetLeavesByRangeRequest) ProtoReflect() protoreflect.Message { mi := &file_trillian_log_api_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GetLeavesByRangeRequest.ProtoReflect.Descriptor instead. func (*GetLeavesByRangeRequest) Descriptor() ([]byte, []int) { return file_trillian_log_api_proto_rawDescGZIP(), []int{17} } func (x *GetLeavesByRangeRequest) GetLogId() int64 { if x != nil { return x.LogId } return 0 } func (x *GetLeavesByRangeRequest) GetStartIndex() int64 { if x != nil { return x.StartIndex } return 0 } func (x *GetLeavesByRangeRequest) GetCount() int64 { if x != nil { return x.Count } return 0 } func (x *GetLeavesByRangeRequest) GetChargeTo() *ChargeTo { if x != nil { return x.ChargeTo } return nil } type GetLeavesByRangeResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Returned log leaves starting from the `start_index` of the request, in // order. There may be fewer than `request.count` leaves returned, if the // requested range extended beyond the size of the tree or if the server opted // to return fewer leaves than requested. Leaves []*LogLeaf `protobuf:"bytes,1,rep,name=leaves,proto3" json:"leaves,omitempty"` SignedLogRoot *SignedLogRoot `protobuf:"bytes,2,opt,name=signed_log_root,json=signedLogRoot,proto3" json:"signed_log_root,omitempty"` } func (x *GetLeavesByRangeResponse) Reset() { *x = GetLeavesByRangeResponse{} if protoimpl.UnsafeEnabled { mi := &file_trillian_log_api_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *GetLeavesByRangeResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*GetLeavesByRangeResponse) ProtoMessage() {} func (x *GetLeavesByRangeResponse) ProtoReflect() protoreflect.Message { mi := &file_trillian_log_api_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GetLeavesByRangeResponse.ProtoReflect.Descriptor instead. func (*GetLeavesByRangeResponse) Descriptor() ([]byte, []int) { return file_trillian_log_api_proto_rawDescGZIP(), []int{18} } func (x *GetLeavesByRangeResponse) GetLeaves() []*LogLeaf { if x != nil { return x.Leaves } return nil } func (x *GetLeavesByRangeResponse) GetSignedLogRoot() *SignedLogRoot { if x != nil { return x.SignedLogRoot } return nil } // QueuedLogLeaf provides the result of submitting an entry to the log. // TODO(pavelkalinnikov): Consider renaming it to AddLogLeafResult or the like. type QueuedLogLeaf struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // The leaf as it was stored by Trillian. Empty unless `status.code` is: // - `google.rpc.OK`: the `leaf` data is the same as in the request. // - `google.rpc.ALREADY_EXISTS` or 'google.rpc.FAILED_PRECONDITION`: the // `leaf` is the conflicting one already in the log. Leaf *LogLeaf `protobuf:"bytes,1,opt,name=leaf,proto3" json:"leaf,omitempty"` // The status of adding the leaf. // - `google.rpc.OK`: successfully added. // - `google.rpc.ALREADY_EXISTS`: the leaf is a duplicate of an already // existing one. Either `leaf_identity_hash` is the same in the `LOG` // mode, or `leaf_index` in the `PREORDERED_LOG`. // - `google.rpc.FAILED_PRECONDITION`: A conflicting entry is already // present in the log, e.g., same `leaf_index` but different `leaf_data`. Status *status.Status `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` } func (x *QueuedLogLeaf) Reset() { *x = QueuedLogLeaf{} if protoimpl.UnsafeEnabled { mi := &file_trillian_log_api_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *QueuedLogLeaf) String() string { return protoimpl.X.MessageStringOf(x) } func (*QueuedLogLeaf) ProtoMessage() {} func (x *QueuedLogLeaf) ProtoReflect() protoreflect.Message { mi := &file_trillian_log_api_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use QueuedLogLeaf.ProtoReflect.Descriptor instead. func (*QueuedLogLeaf) Descriptor() ([]byte, []int) { return file_trillian_log_api_proto_rawDescGZIP(), []int{19} } func (x *QueuedLogLeaf) GetLeaf() *LogLeaf { if x != nil { return x.Leaf } return nil } func (x *QueuedLogLeaf) GetStatus() *status.Status { if x != nil { return x.Status } return nil } // LogLeaf describes a leaf in the Log's Merkle tree, corresponding to a single log entry. // Each leaf has a unique leaf index in the scope of this tree. Clients submitting new // leaf entries should only set the following fields: // - leaf_value // - extra_data (optionally) // - leaf_identity_hash (optionally) // - leaf_index (iff the log is a PREORDERED_LOG) type LogLeaf struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // merkle_leaf_hash holds the Merkle leaf hash over leaf_value. This is // calculated by the Trillian server when leaves are added to the tree, using // the defined hashing algorithm and strategy for the tree; as such, the client // does not need to set it on leaf submissions. MerkleLeafHash []byte `protobuf:"bytes,1,opt,name=merkle_leaf_hash,json=merkleLeafHash,proto3" json:"merkle_leaf_hash,omitempty"` // leaf_value holds the data that forms the value of the Merkle tree leaf. // The client should set this field on all leaf submissions, and is // responsible for ensuring its validity (the Trillian server treats it as an // opaque blob). LeafValue []byte `protobuf:"bytes,2,opt,name=leaf_value,json=leafValue,proto3" json:"leaf_value,omitempty"` // extra_data holds additional data associated with the Merkle tree leaf. // The client may set this data on leaf submissions, and the Trillian server // will return it on subsequent read operations. However, the contents of // this field are not covered by and do not affect the Merkle tree hash // calculations. ExtraData []byte `protobuf:"bytes,3,opt,name=extra_data,json=extraData,proto3" json:"extra_data,omitempty"` // leaf_index indicates the index of this leaf in the Merkle tree. // This field is returned on all read operations, but should only be // set for leaf submissions in PREORDERED_LOG mode (for a normal log // the leaf index is assigned by Trillian when the submitted leaf is // integrated into the Merkle tree). LeafIndex int64 `protobuf:"varint,4,opt,name=leaf_index,json=leafIndex,proto3" json:"leaf_index,omitempty"` // leaf_identity_hash provides a hash value that indicates the client's // concept of which leaf entries should be considered identical. // // This mechanism allows the client personality to indicate that two leaves // should be considered "duplicates" even though their `leaf_value`s differ. // // If this is not set on leaf submissions, the Trillian server will take its // value to be the same as merkle_leaf_hash (and thus only leaves with // identical leaf_value contents will be considered identical). // // For example, in Certificate Transparency each certificate submission is // associated with a submission timestamp, but subsequent submissions of the // same certificate should be considered identical. This is achieved // by setting the leaf identity hash to a hash over (just) the certificate, // whereas the Merkle leaf hash encompasses both the certificate and its // submission time -- allowing duplicate certificates to be detected. // // Continuing the CT example, for a CT mirror personality (which must allow // dupes since the source log could contain them), the part of the // personality which fetches and submits the entries might set // `leaf_identity_hash` to `H(leaf_index||cert)`. // // TODO(pavelkalinnikov): Consider instead using `H(cert)` and allowing // identity hash dupes in `PREORDERED_LOG` mode, for it can later be // upgraded to `LOG` which will need to correctly detect duplicates with // older entries when new ones get queued. LeafIdentityHash []byte `protobuf:"bytes,5,opt,name=leaf_identity_hash,json=leafIdentityHash,proto3" json:"leaf_identity_hash,omitempty"` // queue_timestamp holds the time at which this leaf was queued for // inclusion in the Log, or zero if the entry was submitted without // queuing. Clients should not set this field on submissions. QueueTimestamp *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=queue_timestamp,json=queueTimestamp,proto3" json:"queue_timestamp,omitempty"` // integrate_timestamp holds the time at which this leaf was integrated into // the tree. Clients should not set this field on submissions. IntegrateTimestamp *timestamppb.Timestamp `protobuf:"bytes,7,opt,name=integrate_timestamp,json=integrateTimestamp,proto3" json:"integrate_timestamp,omitempty"` } func (x *LogLeaf) Reset() { *x = LogLeaf{} if protoimpl.UnsafeEnabled { mi := &file_trillian_log_api_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *LogLeaf) String() string { return protoimpl.X.MessageStringOf(x) } func (*LogLeaf) ProtoMessage() {} func (x *LogLeaf) ProtoReflect() protoreflect.Message { mi := &file_trillian_log_api_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use LogLeaf.ProtoReflect.Descriptor instead. func (*LogLeaf) Descriptor() ([]byte, []int) { return file_trillian_log_api_proto_rawDescGZIP(), []int{20} } func (x *LogLeaf) GetMerkleLeafHash() []byte { if x != nil { return x.MerkleLeafHash } return nil } func (x *LogLeaf) GetLeafValue() []byte { if x != nil { return x.LeafValue } return nil } func (x *LogLeaf) GetExtraData() []byte { if x != nil { return x.ExtraData } return nil } func (x *LogLeaf) GetLeafIndex() int64 { if x != nil { return x.LeafIndex } return 0 } func (x *LogLeaf) GetLeafIdentityHash() []byte { if x != nil { return x.LeafIdentityHash } return nil } func (x *LogLeaf) GetQueueTimestamp() *timestamppb.Timestamp { if x != nil { return x.QueueTimestamp } return nil } func (x *LogLeaf) GetIntegrateTimestamp() *timestamppb.Timestamp { if x != nil { return x.IntegrateTimestamp } return nil } var File_trillian_log_api_proto protoreflect.FileDescriptor var file_trillian_log_api_proto_rawDesc = []byte{ 0x0a, 0x16, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x5f, 0x6c, 0x6f, 0x67, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x0e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x1e, 0x0a, 0x08, 0x43, 0x68, 0x61, 0x72, 0x67, 0x65, 0x54, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x81, 0x01, 0x0a, 0x10, 0x51, 0x75, 0x65, 0x75, 0x65, 0x4c, 0x65, 0x61, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6c, 0x6f, 0x67, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x04, 0x6c, 0x65, 0x61, 0x66, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x61, 0x66, 0x52, 0x04, 0x6c, 0x65, 0x61, 0x66, 0x12, 0x2f, 0x0a, 0x09, 0x63, 0x68, 0x61, 0x72, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x43, 0x68, 0x61, 0x72, 0x67, 0x65, 0x54, 0x6f, 0x52, 0x08, 0x63, 0x68, 0x61, 0x72, 0x67, 0x65, 0x54, 0x6f, 0x22, 0x4d, 0x0a, 0x11, 0x51, 0x75, 0x65, 0x75, 0x65, 0x4c, 0x65, 0x61, 0x66, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x0b, 0x71, 0x75, 0x65, 0x75, 0x65, 0x64, 0x5f, 0x6c, 0x65, 0x61, 0x66, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x51, 0x75, 0x65, 0x75, 0x65, 0x64, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x61, 0x66, 0x52, 0x0a, 0x71, 0x75, 0x65, 0x75, 0x65, 0x64, 0x4c, 0x65, 0x61, 0x66, 0x22, 0x9e, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6c, 0x6f, 0x67, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x65, 0x61, 0x66, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x6c, 0x65, 0x61, 0x66, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x74, 0x72, 0x65, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x2f, 0x0a, 0x09, 0x63, 0x68, 0x61, 0x72, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x43, 0x68, 0x61, 0x72, 0x67, 0x65, 0x54, 0x6f, 0x52, 0x08, 0x63, 0x68, 0x61, 0x72, 0x67, 0x65, 0x54, 0x6f, 0x22, 0x83, 0x01, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x12, 0x3f, 0x0a, 0x0f, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x6c, 0x6f, 0x67, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x4c, 0x6f, 0x67, 0x52, 0x6f, 0x6f, 0x74, 0x52, 0x0d, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x4c, 0x6f, 0x67, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0xce, 0x01, 0x0a, 0x1e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x42, 0x79, 0x48, 0x61, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6c, 0x6f, 0x67, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x65, 0x61, 0x66, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x6c, 0x65, 0x61, 0x66, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x74, 0x72, 0x65, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x62, 0x79, 0x5f, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x42, 0x79, 0x53, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x2f, 0x0a, 0x09, 0x63, 0x68, 0x61, 0x72, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x43, 0x68, 0x61, 0x72, 0x67, 0x65, 0x54, 0x6f, 0x52, 0x08, 0x63, 0x68, 0x61, 0x72, 0x67, 0x65, 0x54, 0x6f, 0x22, 0x89, 0x01, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x42, 0x79, 0x48, 0x61, 0x73, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x12, 0x3f, 0x0a, 0x0f, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x6c, 0x6f, 0x67, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x4c, 0x6f, 0x67, 0x52, 0x6f, 0x6f, 0x74, 0x52, 0x0d, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x4c, 0x6f, 0x67, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0xb6, 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6c, 0x6f, 0x67, 0x49, 0x64, 0x12, 0x26, 0x0a, 0x0f, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x66, 0x69, 0x72, 0x73, 0x74, 0x54, 0x72, 0x65, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x54, 0x72, 0x65, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x2f, 0x0a, 0x09, 0x63, 0x68, 0x61, 0x72, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x43, 0x68, 0x61, 0x72, 0x67, 0x65, 0x54, 0x6f, 0x52, 0x08, 0x63, 0x68, 0x61, 0x72, 0x67, 0x65, 0x54, 0x6f, 0x22, 0x85, 0x01, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x12, 0x3f, 0x0a, 0x0f, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x6c, 0x6f, 0x67, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x4c, 0x6f, 0x67, 0x52, 0x6f, 0x6f, 0x74, 0x52, 0x0d, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x4c, 0x6f, 0x67, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x8f, 0x01, 0x0a, 0x1d, 0x47, 0x65, 0x74, 0x4c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x4c, 0x6f, 0x67, 0x52, 0x6f, 0x6f, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6c, 0x6f, 0x67, 0x49, 0x64, 0x12, 0x2f, 0x0a, 0x09, 0x63, 0x68, 0x61, 0x72, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x43, 0x68, 0x61, 0x72, 0x67, 0x65, 0x54, 0x6f, 0x52, 0x08, 0x63, 0x68, 0x61, 0x72, 0x67, 0x65, 0x54, 0x6f, 0x12, 0x26, 0x0a, 0x0f, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x66, 0x69, 0x72, 0x73, 0x74, 0x54, 0x72, 0x65, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x22, 0x88, 0x01, 0x0a, 0x1e, 0x47, 0x65, 0x74, 0x4c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x4c, 0x6f, 0x67, 0x52, 0x6f, 0x6f, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x0f, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x6c, 0x6f, 0x67, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x4c, 0x6f, 0x67, 0x52, 0x6f, 0x6f, 0x74, 0x52, 0x0d, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x4c, 0x6f, 0x67, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x25, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x22, 0x9d, 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x41, 0x6e, 0x64, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6c, 0x6f, 0x67, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x65, 0x61, 0x66, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x6c, 0x65, 0x61, 0x66, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x74, 0x72, 0x65, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x2f, 0x0a, 0x09, 0x63, 0x68, 0x61, 0x72, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x43, 0x68, 0x61, 0x72, 0x67, 0x65, 0x54, 0x6f, 0x52, 0x08, 0x63, 0x68, 0x61, 0x72, 0x67, 0x65, 0x54, 0x6f, 0x22, 0xa9, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x41, 0x6e, 0x64, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x12, 0x25, 0x0a, 0x04, 0x6c, 0x65, 0x61, 0x66, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x61, 0x66, 0x52, 0x04, 0x6c, 0x65, 0x61, 0x66, 0x12, 0x3f, 0x0a, 0x0f, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x6c, 0x6f, 0x67, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x4c, 0x6f, 0x67, 0x52, 0x6f, 0x6f, 0x74, 0x52, 0x0d, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x4c, 0x6f, 0x67, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x58, 0x0a, 0x0e, 0x49, 0x6e, 0x69, 0x74, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6c, 0x6f, 0x67, 0x49, 0x64, 0x12, 0x2f, 0x0a, 0x09, 0x63, 0x68, 0x61, 0x72, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x43, 0x68, 0x61, 0x72, 0x67, 0x65, 0x54, 0x6f, 0x52, 0x08, 0x63, 0x68, 0x61, 0x72, 0x67, 0x65, 0x54, 0x6f, 0x22, 0x44, 0x0a, 0x0f, 0x49, 0x6e, 0x69, 0x74, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x4c, 0x6f, 0x67, 0x52, 0x6f, 0x6f, 0x74, 0x52, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x22, 0x8e, 0x01, 0x0a, 0x19, 0x41, 0x64, 0x64, 0x53, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x64, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6c, 0x6f, 0x67, 0x49, 0x64, 0x12, 0x29, 0x0a, 0x06, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x61, 0x66, 0x52, 0x06, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x09, 0x63, 0x68, 0x61, 0x72, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x43, 0x68, 0x61, 0x72, 0x67, 0x65, 0x54, 0x6f, 0x52, 0x08, 0x63, 0x68, 0x61, 0x72, 0x67, 0x65, 0x54, 0x6f, 0x22, 0x4f, 0x0a, 0x1a, 0x41, 0x64, 0x64, 0x53, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x64, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x51, 0x75, 0x65, 0x75, 0x65, 0x64, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x61, 0x66, 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x22, 0x98, 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x42, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6c, 0x6f, 0x67, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2f, 0x0a, 0x09, 0x63, 0x68, 0x61, 0x72, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x43, 0x68, 0x61, 0x72, 0x67, 0x65, 0x54, 0x6f, 0x52, 0x08, 0x63, 0x68, 0x61, 0x72, 0x67, 0x65, 0x54, 0x6f, 0x22, 0x86, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x42, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x06, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x61, 0x66, 0x52, 0x06, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x12, 0x3f, 0x0a, 0x0f, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x6c, 0x6f, 0x67, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x4c, 0x6f, 0x67, 0x52, 0x6f, 0x6f, 0x74, 0x52, 0x0d, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x4c, 0x6f, 0x67, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x62, 0x0a, 0x0d, 0x51, 0x75, 0x65, 0x75, 0x65, 0x64, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x61, 0x66, 0x12, 0x25, 0x0a, 0x04, 0x6c, 0x65, 0x61, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x61, 0x66, 0x52, 0x04, 0x6c, 0x65, 0x61, 0x66, 0x12, 0x2a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0xd0, 0x02, 0x0a, 0x07, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x61, 0x66, 0x12, 0x28, 0x0a, 0x10, 0x6d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x5f, 0x6c, 0x65, 0x61, 0x66, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x6d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x4c, 0x65, 0x61, 0x66, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x65, 0x61, 0x66, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x6c, 0x65, 0x61, 0x66, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x65, 0x78, 0x74, 0x72, 0x61, 0x44, 0x61, 0x74, 0x61, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x65, 0x61, 0x66, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x6c, 0x65, 0x61, 0x66, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x2c, 0x0a, 0x12, 0x6c, 0x65, 0x61, 0x66, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x6c, 0x65, 0x61, 0x66, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x48, 0x61, 0x73, 0x68, 0x12, 0x43, 0x0a, 0x0f, 0x71, 0x75, 0x65, 0x75, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0e, 0x71, 0x75, 0x65, 0x75, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x4b, 0x0a, 0x13, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x12, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x32, 0xdb, 0x06, 0x0a, 0x0b, 0x54, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x4c, 0x6f, 0x67, 0x12, 0x46, 0x0a, 0x09, 0x51, 0x75, 0x65, 0x75, 0x65, 0x4c, 0x65, 0x61, 0x66, 0x12, 0x1a, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x51, 0x75, 0x65, 0x75, 0x65, 0x4c, 0x65, 0x61, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x51, 0x75, 0x65, 0x75, 0x65, 0x4c, 0x65, 0x61, 0x66, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5e, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x12, 0x22, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x70, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x42, 0x79, 0x48, 0x61, 0x73, 0x68, 0x12, 0x28, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x42, 0x79, 0x48, 0x61, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x42, 0x79, 0x48, 0x61, 0x73, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x64, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x12, 0x24, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6d, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x4c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x4c, 0x6f, 0x67, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x27, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x4c, 0x6f, 0x67, 0x52, 0x6f, 0x6f, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x4c, 0x6f, 0x67, 0x52, 0x6f, 0x6f, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5b, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x41, 0x6e, 0x64, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x12, 0x21, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x41, 0x6e, 0x64, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x41, 0x6e, 0x64, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x40, 0x0a, 0x07, 0x49, 0x6e, 0x69, 0x74, 0x4c, 0x6f, 0x67, 0x12, 0x18, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x61, 0x0a, 0x12, 0x41, 0x64, 0x64, 0x53, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x64, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x12, 0x23, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x41, 0x64, 0x64, 0x53, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x64, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x41, 0x64, 0x64, 0x53, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x64, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5b, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x42, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x21, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x42, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x42, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x4e, 0x0a, 0x19, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x42, 0x13, 0x54, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x4c, 0x6f, 0x67, 0x41, 0x70, 0x69, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x1a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x74, 0x72, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( file_trillian_log_api_proto_rawDescOnce sync.Once file_trillian_log_api_proto_rawDescData = file_trillian_log_api_proto_rawDesc ) func file_trillian_log_api_proto_rawDescGZIP() []byte { file_trillian_log_api_proto_rawDescOnce.Do(func() { file_trillian_log_api_proto_rawDescData = protoimpl.X.CompressGZIP(file_trillian_log_api_proto_rawDescData) }) return file_trillian_log_api_proto_rawDescData } var file_trillian_log_api_proto_msgTypes = make([]protoimpl.MessageInfo, 21) var file_trillian_log_api_proto_goTypes = []any{ (*ChargeTo)(nil), // 0: trillian.ChargeTo (*QueueLeafRequest)(nil), // 1: trillian.QueueLeafRequest (*QueueLeafResponse)(nil), // 2: trillian.QueueLeafResponse (*GetInclusionProofRequest)(nil), // 3: trillian.GetInclusionProofRequest (*GetInclusionProofResponse)(nil), // 4: trillian.GetInclusionProofResponse (*GetInclusionProofByHashRequest)(nil), // 5: trillian.GetInclusionProofByHashRequest (*GetInclusionProofByHashResponse)(nil), // 6: trillian.GetInclusionProofByHashResponse (*GetConsistencyProofRequest)(nil), // 7: trillian.GetConsistencyProofRequest (*GetConsistencyProofResponse)(nil), // 8: trillian.GetConsistencyProofResponse (*GetLatestSignedLogRootRequest)(nil), // 9: trillian.GetLatestSignedLogRootRequest (*GetLatestSignedLogRootResponse)(nil), // 10: trillian.GetLatestSignedLogRootResponse (*GetEntryAndProofRequest)(nil), // 11: trillian.GetEntryAndProofRequest (*GetEntryAndProofResponse)(nil), // 12: trillian.GetEntryAndProofResponse (*InitLogRequest)(nil), // 13: trillian.InitLogRequest (*InitLogResponse)(nil), // 14: trillian.InitLogResponse (*AddSequencedLeavesRequest)(nil), // 15: trillian.AddSequencedLeavesRequest (*AddSequencedLeavesResponse)(nil), // 16: trillian.AddSequencedLeavesResponse (*GetLeavesByRangeRequest)(nil), // 17: trillian.GetLeavesByRangeRequest (*GetLeavesByRangeResponse)(nil), // 18: trillian.GetLeavesByRangeResponse (*QueuedLogLeaf)(nil), // 19: trillian.QueuedLogLeaf (*LogLeaf)(nil), // 20: trillian.LogLeaf (*Proof)(nil), // 21: trillian.Proof (*SignedLogRoot)(nil), // 22: trillian.SignedLogRoot (*status.Status)(nil), // 23: google.rpc.Status (*timestamppb.Timestamp)(nil), // 24: google.protobuf.Timestamp } var file_trillian_log_api_proto_depIdxs = []int32{ 20, // 0: trillian.QueueLeafRequest.leaf:type_name -> trillian.LogLeaf 0, // 1: trillian.QueueLeafRequest.charge_to:type_name -> trillian.ChargeTo 19, // 2: trillian.QueueLeafResponse.queued_leaf:type_name -> trillian.QueuedLogLeaf 0, // 3: trillian.GetInclusionProofRequest.charge_to:type_name -> trillian.ChargeTo 21, // 4: trillian.GetInclusionProofResponse.proof:type_name -> trillian.Proof 22, // 5: trillian.GetInclusionProofResponse.signed_log_root:type_name -> trillian.SignedLogRoot 0, // 6: trillian.GetInclusionProofByHashRequest.charge_to:type_name -> trillian.ChargeTo 21, // 7: trillian.GetInclusionProofByHashResponse.proof:type_name -> trillian.Proof 22, // 8: trillian.GetInclusionProofByHashResponse.signed_log_root:type_name -> trillian.SignedLogRoot 0, // 9: trillian.GetConsistencyProofRequest.charge_to:type_name -> trillian.ChargeTo 21, // 10: trillian.GetConsistencyProofResponse.proof:type_name -> trillian.Proof 22, // 11: trillian.GetConsistencyProofResponse.signed_log_root:type_name -> trillian.SignedLogRoot 0, // 12: trillian.GetLatestSignedLogRootRequest.charge_to:type_name -> trillian.ChargeTo 22, // 13: trillian.GetLatestSignedLogRootResponse.signed_log_root:type_name -> trillian.SignedLogRoot 21, // 14: trillian.GetLatestSignedLogRootResponse.proof:type_name -> trillian.Proof 0, // 15: trillian.GetEntryAndProofRequest.charge_to:type_name -> trillian.ChargeTo 21, // 16: trillian.GetEntryAndProofResponse.proof:type_name -> trillian.Proof 20, // 17: trillian.GetEntryAndProofResponse.leaf:type_name -> trillian.LogLeaf 22, // 18: trillian.GetEntryAndProofResponse.signed_log_root:type_name -> trillian.SignedLogRoot 0, // 19: trillian.InitLogRequest.charge_to:type_name -> trillian.ChargeTo 22, // 20: trillian.InitLogResponse.created:type_name -> trillian.SignedLogRoot 20, // 21: trillian.AddSequencedLeavesRequest.leaves:type_name -> trillian.LogLeaf 0, // 22: trillian.AddSequencedLeavesRequest.charge_to:type_name -> trillian.ChargeTo 19, // 23: trillian.AddSequencedLeavesResponse.results:type_name -> trillian.QueuedLogLeaf 0, // 24: trillian.GetLeavesByRangeRequest.charge_to:type_name -> trillian.ChargeTo 20, // 25: trillian.GetLeavesByRangeResponse.leaves:type_name -> trillian.LogLeaf 22, // 26: trillian.GetLeavesByRangeResponse.signed_log_root:type_name -> trillian.SignedLogRoot 20, // 27: trillian.QueuedLogLeaf.leaf:type_name -> trillian.LogLeaf 23, // 28: trillian.QueuedLogLeaf.status:type_name -> google.rpc.Status 24, // 29: trillian.LogLeaf.queue_timestamp:type_name -> google.protobuf.Timestamp 24, // 30: trillian.LogLeaf.integrate_timestamp:type_name -> google.protobuf.Timestamp 1, // 31: trillian.TrillianLog.QueueLeaf:input_type -> trillian.QueueLeafRequest 3, // 32: trillian.TrillianLog.GetInclusionProof:input_type -> trillian.GetInclusionProofRequest 5, // 33: trillian.TrillianLog.GetInclusionProofByHash:input_type -> trillian.GetInclusionProofByHashRequest 7, // 34: trillian.TrillianLog.GetConsistencyProof:input_type -> trillian.GetConsistencyProofRequest 9, // 35: trillian.TrillianLog.GetLatestSignedLogRoot:input_type -> trillian.GetLatestSignedLogRootRequest 11, // 36: trillian.TrillianLog.GetEntryAndProof:input_type -> trillian.GetEntryAndProofRequest 13, // 37: trillian.TrillianLog.InitLog:input_type -> trillian.InitLogRequest 15, // 38: trillian.TrillianLog.AddSequencedLeaves:input_type -> trillian.AddSequencedLeavesRequest 17, // 39: trillian.TrillianLog.GetLeavesByRange:input_type -> trillian.GetLeavesByRangeRequest 2, // 40: trillian.TrillianLog.QueueLeaf:output_type -> trillian.QueueLeafResponse 4, // 41: trillian.TrillianLog.GetInclusionProof:output_type -> trillian.GetInclusionProofResponse 6, // 42: trillian.TrillianLog.GetInclusionProofByHash:output_type -> trillian.GetInclusionProofByHashResponse 8, // 43: trillian.TrillianLog.GetConsistencyProof:output_type -> trillian.GetConsistencyProofResponse 10, // 44: trillian.TrillianLog.GetLatestSignedLogRoot:output_type -> trillian.GetLatestSignedLogRootResponse 12, // 45: trillian.TrillianLog.GetEntryAndProof:output_type -> trillian.GetEntryAndProofResponse 14, // 46: trillian.TrillianLog.InitLog:output_type -> trillian.InitLogResponse 16, // 47: trillian.TrillianLog.AddSequencedLeaves:output_type -> trillian.AddSequencedLeavesResponse 18, // 48: trillian.TrillianLog.GetLeavesByRange:output_type -> trillian.GetLeavesByRangeResponse 40, // [40:49] is the sub-list for method output_type 31, // [31:40] is the sub-list for method input_type 31, // [31:31] is the sub-list for extension type_name 31, // [31:31] is the sub-list for extension extendee 0, // [0:31] is the sub-list for field type_name } func init() { file_trillian_log_api_proto_init() } func file_trillian_log_api_proto_init() { if File_trillian_log_api_proto != nil { return } file_trillian_proto_init() if !protoimpl.UnsafeEnabled { file_trillian_log_api_proto_msgTypes[0].Exporter = func(v any, i int) any { switch v := v.(*ChargeTo); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_trillian_log_api_proto_msgTypes[1].Exporter = func(v any, i int) any { switch v := v.(*QueueLeafRequest); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_trillian_log_api_proto_msgTypes[2].Exporter = func(v any, i int) any { switch v := v.(*QueueLeafResponse); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_trillian_log_api_proto_msgTypes[3].Exporter = func(v any, i int) any { switch v := v.(*GetInclusionProofRequest); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_trillian_log_api_proto_msgTypes[4].Exporter = func(v any, i int) any { switch v := v.(*GetInclusionProofResponse); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_trillian_log_api_proto_msgTypes[5].Exporter = func(v any, i int) any { switch v := v.(*GetInclusionProofByHashRequest); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_trillian_log_api_proto_msgTypes[6].Exporter = func(v any, i int) any { switch v := v.(*GetInclusionProofByHashResponse); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_trillian_log_api_proto_msgTypes[7].Exporter = func(v any, i int) any { switch v := v.(*GetConsistencyProofRequest); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_trillian_log_api_proto_msgTypes[8].Exporter = func(v any, i int) any { switch v := v.(*GetConsistencyProofResponse); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_trillian_log_api_proto_msgTypes[9].Exporter = func(v any, i int) any { switch v := v.(*GetLatestSignedLogRootRequest); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_trillian_log_api_proto_msgTypes[10].Exporter = func(v any, i int) any { switch v := v.(*GetLatestSignedLogRootResponse); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_trillian_log_api_proto_msgTypes[11].Exporter = func(v any, i int) any { switch v := v.(*GetEntryAndProofRequest); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_trillian_log_api_proto_msgTypes[12].Exporter = func(v any, i int) any { switch v := v.(*GetEntryAndProofResponse); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_trillian_log_api_proto_msgTypes[13].Exporter = func(v any, i int) any { switch v := v.(*InitLogRequest); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_trillian_log_api_proto_msgTypes[14].Exporter = func(v any, i int) any { switch v := v.(*InitLogResponse); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_trillian_log_api_proto_msgTypes[15].Exporter = func(v any, i int) any { switch v := v.(*AddSequencedLeavesRequest); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_trillian_log_api_proto_msgTypes[16].Exporter = func(v any, i int) any { switch v := v.(*AddSequencedLeavesResponse); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_trillian_log_api_proto_msgTypes[17].Exporter = func(v any, i int) any { switch v := v.(*GetLeavesByRangeRequest); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_trillian_log_api_proto_msgTypes[18].Exporter = func(v any, i int) any { switch v := v.(*GetLeavesByRangeResponse); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_trillian_log_api_proto_msgTypes[19].Exporter = func(v any, i int) any { switch v := v.(*QueuedLogLeaf); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_trillian_log_api_proto_msgTypes[20].Exporter = func(v any, i int) any { switch v := v.(*LogLeaf); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_trillian_log_api_proto_rawDesc, NumEnums: 0, NumMessages: 21, NumExtensions: 0, NumServices: 1, }, GoTypes: file_trillian_log_api_proto_goTypes, DependencyIndexes: file_trillian_log_api_proto_depIdxs, MessageInfos: file_trillian_log_api_proto_msgTypes, }.Build() File_trillian_log_api_proto = out.File file_trillian_log_api_proto_rawDesc = nil file_trillian_log_api_proto_goTypes = nil file_trillian_log_api_proto_depIdxs = nil } trillian-1.6.1/trillian_log_api.proto000066400000000000000000000351141466362047600177540ustar00rootroot00000000000000// 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. syntax = "proto3"; package trillian; option go_package = "github.com/google/trillian"; option java_multiple_files = true; option java_outer_classname = "TrillianLogApiProto"; option java_package = "com.google.trillian.proto"; import "google/protobuf/timestamp.proto"; import "google/rpc/status.proto"; import "trillian.proto"; // The TrillianLog service provides access to an append-only Log data structure // as described in the [Verifiable Data // Structures](docs/papers/VerifiableDataStructures.pdf) paper. // // The API supports adding new entries to the Merkle tree for a specific Log // instance (identified by its log_id) in two modes: // - For a normal log, new leaf entries are queued up for subsequent // inclusion in the log, and the leaves are assigned consecutive leaf_index // values as part of that integration process. // - For a 'pre-ordered log', new entries have an already-defined leaf // ordering, and leaves are only integrated into the Merkle tree when a // contiguous range of leaves is available. // // The API also supports read operations to retrieve leaf contents, and to // provide cryptographic proofs of leaf inclusion and of the append-only nature // of the Log. // // Each API request also includes a charge_to field, which allows API users // to provide quota identifiers that should be "charged" for each API request // (and potentially rejected with codes.ResourceExhausted). // // Various operations on the API also allows for 'server skew', which can occur // when different API requests happen to be handled by different server instances // that may not all be up to date. An API request that is relative to a specific // tree size may reach a server instance that is not yet aware of this tree size; // in this case the server will typically return an OK response that contains: // - a signed log root that indicates the tree size that it is aware of // - an empty response otherwise. service TrillianLog { // QueueLeaf adds a single leaf to the queue of pending leaves for a normal // log. rpc QueueLeaf(QueueLeafRequest) returns (QueueLeafResponse) {} // GetInclusionProof returns an inclusion proof for a leaf with a given index // in a particular tree. // // If the requested tree_size is larger than the server is aware of, the // response will include the latest known log root and an empty proof. rpc GetInclusionProof(GetInclusionProofRequest) returns (GetInclusionProofResponse) {} // GetInclusionProofByHash returns an inclusion proof for any leaves that have // the given Merkle hash in a particular tree. // // If any of the leaves that match the given Merkle has have a leaf index that // is beyond the requested tree size, the corresponding proof entry will be empty. rpc GetInclusionProofByHash(GetInclusionProofByHashRequest) returns (GetInclusionProofByHashResponse) {} // GetConsistencyProof returns a consistency proof between different sizes of // a particular tree. // // If the requested tree size is larger than the server is aware of, // the response will include the latest known log root and an empty proof. rpc GetConsistencyProof(GetConsistencyProofRequest) returns (GetConsistencyProofResponse) {} // GetLatestSignedLogRoot returns the latest log root for a given tree, // and optionally also includes a consistency proof from an earlier tree size // to the new size of the tree. // // If the earlier tree size is larger than the server is aware of, // an InvalidArgument error is returned. rpc GetLatestSignedLogRoot(GetLatestSignedLogRootRequest) returns (GetLatestSignedLogRootResponse) {} // GetEntryAndProof returns a log leaf and the corresponding inclusion proof // to a specified tree size, for a given leaf index in a particular tree. // // If the requested tree size is unavailable but the leaf is // in scope for the current tree, the returned proof will be for the // current tree size rather than the requested tree size. rpc GetEntryAndProof(GetEntryAndProofRequest) returns (GetEntryAndProofResponse) {} // InitLog initializes a particular tree, creating the initial signed log // root (which will be of size 0). rpc InitLog(InitLogRequest) returns (InitLogResponse) {} // AddSequencedLeaves adds a batch of leaves with assigned sequence numbers // to a pre-ordered log. The indices of the provided leaves must be contiguous. rpc AddSequencedLeaves(AddSequencedLeavesRequest) returns (AddSequencedLeavesResponse) {} // GetLeavesByRange returns a batch of leaves whose leaf indices are in a // sequential range. rpc GetLeavesByRange(GetLeavesByRangeRequest) returns (GetLeavesByRangeResponse) {} } // ChargeTo describes the user(s) associated with the request whose quota should // be checked and charged. message ChargeTo { // user is a list of personality-defined strings. // Trillian will treat them as /User/%{user}/... keys when checking and // charging quota. // If one or more of the specified users has insufficient quota, the // request will be denied. // // As an example, a Certificate Transparency frontend might set the following // user strings when sending a QueueLeaf request to the Trillian log: // - The requesting IP address. // This would limit the number of requests per IP. // - The "intermediate-" for each of the intermediate certificates in // the submitted chain. // This would have the effect of limiting the rate of submissions under // a given intermediate/root. repeated string user = 1; } message QueueLeafRequest { int64 log_id = 1; LogLeaf leaf = 2; ChargeTo charge_to = 3; } message QueueLeafResponse { // queued_leaf describes the leaf which is or will be incorporated into the // Log. If the submitted leaf was already present in the Log (as indicated by // its leaf identity hash), then the returned leaf will be the pre-existing // leaf entry rather than the submitted leaf. QueuedLogLeaf queued_leaf = 2; } message GetInclusionProofRequest { int64 log_id = 1; int64 leaf_index = 2; int64 tree_size = 3; ChargeTo charge_to = 4; } message GetInclusionProofResponse { // The proof field may be empty if the requested tree_size was larger // than that available at the server (e.g. because there is skew between // server instances, and an earlier client request was processed by a // more up-to-date instance). In this case, the signed_log_root // field will indicate the tree size that the server is aware of, and // the proof field will be empty. Proof proof = 2; SignedLogRoot signed_log_root = 3; } message GetInclusionProofByHashRequest { int64 log_id = 1; // The leaf hash field provides the Merkle tree hash of the leaf entry // to be retrieved. bytes leaf_hash = 2; int64 tree_size = 3; bool order_by_sequence = 4; ChargeTo charge_to = 5; } message GetInclusionProofByHashResponse { // Logs can potentially contain leaves with duplicate hashes so it's possible // for this to return multiple proofs. If the leaf index for a particular // instance of the requested Merkle leaf hash is beyond the requested tree // size, the corresponding proof entry will be missing. repeated Proof proof = 2; SignedLogRoot signed_log_root = 3; } message GetConsistencyProofRequest { int64 log_id = 1; int64 first_tree_size = 2; int64 second_tree_size = 3; ChargeTo charge_to = 4; } message GetConsistencyProofResponse { // The proof field may be empty if the requested tree_size was larger // than that available at the server (e.g. because there is skew between // server instances, and an earlier client request was processed by a // more up-to-date instance). In this case, the signed_log_root // field will indicate the tree size that the server is aware of, and // the proof field will be empty. Proof proof = 2; SignedLogRoot signed_log_root = 3; } message GetLatestSignedLogRootRequest { int64 log_id = 1; ChargeTo charge_to = 2; // If first_tree_size is non-zero, the response will include a consistency // proof between first_tree_size and the new tree size (if not smaller). int64 first_tree_size = 3; } message GetLatestSignedLogRootResponse { SignedLogRoot signed_log_root = 2; // proof is filled in with a consistency proof if first_tree_size in // GetLatestSignedLogRootRequest is non-zero (and within the tree size // available at the server). Proof proof = 3; } message GetEntryAndProofRequest { int64 log_id = 1; int64 leaf_index = 2; int64 tree_size = 3; ChargeTo charge_to = 4; } message GetEntryAndProofResponse { Proof proof = 2; LogLeaf leaf = 3; SignedLogRoot signed_log_root = 4; } message InitLogRequest { int64 log_id = 1; ChargeTo charge_to = 2; } message InitLogResponse { SignedLogRoot created = 1; } message AddSequencedLeavesRequest { int64 log_id = 1; repeated LogLeaf leaves = 2; ChargeTo charge_to = 4; } message AddSequencedLeavesResponse { // Same number and order as in the corresponding request. repeated QueuedLogLeaf results = 2; } message GetLeavesByRangeRequest { int64 log_id = 1; int64 start_index = 2; int64 count = 3; ChargeTo charge_to = 4; } message GetLeavesByRangeResponse { // Returned log leaves starting from the `start_index` of the request, in // order. There may be fewer than `request.count` leaves returned, if the // requested range extended beyond the size of the tree or if the server opted // to return fewer leaves than requested. repeated LogLeaf leaves = 1; SignedLogRoot signed_log_root = 2; } // QueuedLogLeaf provides the result of submitting an entry to the log. // TODO(pavelkalinnikov): Consider renaming it to AddLogLeafResult or the like. message QueuedLogLeaf { // The leaf as it was stored by Trillian. Empty unless `status.code` is: // - `google.rpc.OK`: the `leaf` data is the same as in the request. // - `google.rpc.ALREADY_EXISTS` or 'google.rpc.FAILED_PRECONDITION`: the // `leaf` is the conflicting one already in the log. LogLeaf leaf = 1; // The status of adding the leaf. // - `google.rpc.OK`: successfully added. // - `google.rpc.ALREADY_EXISTS`: the leaf is a duplicate of an already // existing one. Either `leaf_identity_hash` is the same in the `LOG` // mode, or `leaf_index` in the `PREORDERED_LOG`. // - `google.rpc.FAILED_PRECONDITION`: A conflicting entry is already // present in the log, e.g., same `leaf_index` but different `leaf_data`. google.rpc.Status status = 2; } // LogLeaf describes a leaf in the Log's Merkle tree, corresponding to a single log entry. // Each leaf has a unique leaf index in the scope of this tree. Clients submitting new // leaf entries should only set the following fields: // - leaf_value // - extra_data (optionally) // - leaf_identity_hash (optionally) // - leaf_index (iff the log is a PREORDERED_LOG) message LogLeaf { // merkle_leaf_hash holds the Merkle leaf hash over leaf_value. This is // calculated by the Trillian server when leaves are added to the tree, using // the defined hashing algorithm and strategy for the tree; as such, the client // does not need to set it on leaf submissions. bytes merkle_leaf_hash = 1; // leaf_value holds the data that forms the value of the Merkle tree leaf. // The client should set this field on all leaf submissions, and is // responsible for ensuring its validity (the Trillian server treats it as an // opaque blob). bytes leaf_value = 2; // extra_data holds additional data associated with the Merkle tree leaf. // The client may set this data on leaf submissions, and the Trillian server // will return it on subsequent read operations. However, the contents of // this field are not covered by and do not affect the Merkle tree hash // calculations. bytes extra_data = 3; // leaf_index indicates the index of this leaf in the Merkle tree. // This field is returned on all read operations, but should only be // set for leaf submissions in PREORDERED_LOG mode (for a normal log // the leaf index is assigned by Trillian when the submitted leaf is // integrated into the Merkle tree). int64 leaf_index = 4; // leaf_identity_hash provides a hash value that indicates the client's // concept of which leaf entries should be considered identical. // // This mechanism allows the client personality to indicate that two leaves // should be considered "duplicates" even though their `leaf_value`s differ. // // If this is not set on leaf submissions, the Trillian server will take its // value to be the same as merkle_leaf_hash (and thus only leaves with // identical leaf_value contents will be considered identical). // // For example, in Certificate Transparency each certificate submission is // associated with a submission timestamp, but subsequent submissions of the // same certificate should be considered identical. This is achieved // by setting the leaf identity hash to a hash over (just) the certificate, // whereas the Merkle leaf hash encompasses both the certificate and its // submission time -- allowing duplicate certificates to be detected. // // // Continuing the CT example, for a CT mirror personality (which must allow // dupes since the source log could contain them), the part of the // personality which fetches and submits the entries might set // `leaf_identity_hash` to `H(leaf_index||cert)`. // // TODO(pavelkalinnikov): Consider instead using `H(cert)` and allowing // identity hash dupes in `PREORDERED_LOG` mode, for it can later be // upgraded to `LOG` which will need to correctly detect duplicates with // older entries when new ones get queued. bytes leaf_identity_hash = 5; // queue_timestamp holds the time at which this leaf was queued for // inclusion in the Log, or zero if the entry was submitted without // queuing. Clients should not set this field on submissions. google.protobuf.Timestamp queue_timestamp = 6; // integrate_timestamp holds the time at which this leaf was integrated into // the tree. Clients should not set this field on submissions. google.protobuf.Timestamp integrate_timestamp = 7; } trillian-1.6.1/trillian_log_api_grpc.pb.go000066400000000000000000000627661466362047600206460ustar00rootroot00000000000000// 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. // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.5.1 // - protoc v3.20.1 // source: trillian_log_api.proto package trillian import ( context "context" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" ) // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. // Requires gRPC-Go v1.64.0 or later. const _ = grpc.SupportPackageIsVersion9 const ( TrillianLog_QueueLeaf_FullMethodName = "/trillian.TrillianLog/QueueLeaf" TrillianLog_GetInclusionProof_FullMethodName = "/trillian.TrillianLog/GetInclusionProof" TrillianLog_GetInclusionProofByHash_FullMethodName = "/trillian.TrillianLog/GetInclusionProofByHash" TrillianLog_GetConsistencyProof_FullMethodName = "/trillian.TrillianLog/GetConsistencyProof" TrillianLog_GetLatestSignedLogRoot_FullMethodName = "/trillian.TrillianLog/GetLatestSignedLogRoot" TrillianLog_GetEntryAndProof_FullMethodName = "/trillian.TrillianLog/GetEntryAndProof" TrillianLog_InitLog_FullMethodName = "/trillian.TrillianLog/InitLog" TrillianLog_AddSequencedLeaves_FullMethodName = "/trillian.TrillianLog/AddSequencedLeaves" TrillianLog_GetLeavesByRange_FullMethodName = "/trillian.TrillianLog/GetLeavesByRange" ) // TrillianLogClient is the client API for TrillianLog service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. // // The TrillianLog service provides access to an append-only Log data structure // as described in the [Verifiable Data // Structures](docs/papers/VerifiableDataStructures.pdf) paper. // // The API supports adding new entries to the Merkle tree for a specific Log // instance (identified by its log_id) in two modes: // - For a normal log, new leaf entries are queued up for subsequent // inclusion in the log, and the leaves are assigned consecutive leaf_index // values as part of that integration process. // - For a 'pre-ordered log', new entries have an already-defined leaf // ordering, and leaves are only integrated into the Merkle tree when a // contiguous range of leaves is available. // // The API also supports read operations to retrieve leaf contents, and to // provide cryptographic proofs of leaf inclusion and of the append-only nature // of the Log. // // Each API request also includes a charge_to field, which allows API users // to provide quota identifiers that should be "charged" for each API request // (and potentially rejected with codes.ResourceExhausted). // // Various operations on the API also allows for 'server skew', which can occur // when different API requests happen to be handled by different server instances // that may not all be up to date. An API request that is relative to a specific // tree size may reach a server instance that is not yet aware of this tree size; // in this case the server will typically return an OK response that contains: // - a signed log root that indicates the tree size that it is aware of // - an empty response otherwise. type TrillianLogClient interface { // QueueLeaf adds a single leaf to the queue of pending leaves for a normal // log. QueueLeaf(ctx context.Context, in *QueueLeafRequest, opts ...grpc.CallOption) (*QueueLeafResponse, error) // GetInclusionProof returns an inclusion proof for a leaf with a given index // in a particular tree. // // If the requested tree_size is larger than the server is aware of, the // response will include the latest known log root and an empty proof. GetInclusionProof(ctx context.Context, in *GetInclusionProofRequest, opts ...grpc.CallOption) (*GetInclusionProofResponse, error) // GetInclusionProofByHash returns an inclusion proof for any leaves that have // the given Merkle hash in a particular tree. // // If any of the leaves that match the given Merkle has have a leaf index that // is beyond the requested tree size, the corresponding proof entry will be empty. GetInclusionProofByHash(ctx context.Context, in *GetInclusionProofByHashRequest, opts ...grpc.CallOption) (*GetInclusionProofByHashResponse, error) // GetConsistencyProof returns a consistency proof between different sizes of // a particular tree. // // If the requested tree size is larger than the server is aware of, // the response will include the latest known log root and an empty proof. GetConsistencyProof(ctx context.Context, in *GetConsistencyProofRequest, opts ...grpc.CallOption) (*GetConsistencyProofResponse, error) // GetLatestSignedLogRoot returns the latest log root for a given tree, // and optionally also includes a consistency proof from an earlier tree size // to the new size of the tree. // // If the earlier tree size is larger than the server is aware of, // an InvalidArgument error is returned. GetLatestSignedLogRoot(ctx context.Context, in *GetLatestSignedLogRootRequest, opts ...grpc.CallOption) (*GetLatestSignedLogRootResponse, error) // GetEntryAndProof returns a log leaf and the corresponding inclusion proof // to a specified tree size, for a given leaf index in a particular tree. // // If the requested tree size is unavailable but the leaf is // in scope for the current tree, the returned proof will be for the // current tree size rather than the requested tree size. GetEntryAndProof(ctx context.Context, in *GetEntryAndProofRequest, opts ...grpc.CallOption) (*GetEntryAndProofResponse, error) // InitLog initializes a particular tree, creating the initial signed log // root (which will be of size 0). InitLog(ctx context.Context, in *InitLogRequest, opts ...grpc.CallOption) (*InitLogResponse, error) // AddSequencedLeaves adds a batch of leaves with assigned sequence numbers // to a pre-ordered log. The indices of the provided leaves must be contiguous. AddSequencedLeaves(ctx context.Context, in *AddSequencedLeavesRequest, opts ...grpc.CallOption) (*AddSequencedLeavesResponse, error) // GetLeavesByRange returns a batch of leaves whose leaf indices are in a // sequential range. GetLeavesByRange(ctx context.Context, in *GetLeavesByRangeRequest, opts ...grpc.CallOption) (*GetLeavesByRangeResponse, error) } type trillianLogClient struct { cc grpc.ClientConnInterface } func NewTrillianLogClient(cc grpc.ClientConnInterface) TrillianLogClient { return &trillianLogClient{cc} } func (c *trillianLogClient) QueueLeaf(ctx context.Context, in *QueueLeafRequest, opts ...grpc.CallOption) (*QueueLeafResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(QueueLeafResponse) err := c.cc.Invoke(ctx, TrillianLog_QueueLeaf_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *trillianLogClient) GetInclusionProof(ctx context.Context, in *GetInclusionProofRequest, opts ...grpc.CallOption) (*GetInclusionProofResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetInclusionProofResponse) err := c.cc.Invoke(ctx, TrillianLog_GetInclusionProof_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *trillianLogClient) GetInclusionProofByHash(ctx context.Context, in *GetInclusionProofByHashRequest, opts ...grpc.CallOption) (*GetInclusionProofByHashResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetInclusionProofByHashResponse) err := c.cc.Invoke(ctx, TrillianLog_GetInclusionProofByHash_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *trillianLogClient) GetConsistencyProof(ctx context.Context, in *GetConsistencyProofRequest, opts ...grpc.CallOption) (*GetConsistencyProofResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetConsistencyProofResponse) err := c.cc.Invoke(ctx, TrillianLog_GetConsistencyProof_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *trillianLogClient) GetLatestSignedLogRoot(ctx context.Context, in *GetLatestSignedLogRootRequest, opts ...grpc.CallOption) (*GetLatestSignedLogRootResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetLatestSignedLogRootResponse) err := c.cc.Invoke(ctx, TrillianLog_GetLatestSignedLogRoot_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *trillianLogClient) GetEntryAndProof(ctx context.Context, in *GetEntryAndProofRequest, opts ...grpc.CallOption) (*GetEntryAndProofResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetEntryAndProofResponse) err := c.cc.Invoke(ctx, TrillianLog_GetEntryAndProof_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *trillianLogClient) InitLog(ctx context.Context, in *InitLogRequest, opts ...grpc.CallOption) (*InitLogResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(InitLogResponse) err := c.cc.Invoke(ctx, TrillianLog_InitLog_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *trillianLogClient) AddSequencedLeaves(ctx context.Context, in *AddSequencedLeavesRequest, opts ...grpc.CallOption) (*AddSequencedLeavesResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(AddSequencedLeavesResponse) err := c.cc.Invoke(ctx, TrillianLog_AddSequencedLeaves_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *trillianLogClient) GetLeavesByRange(ctx context.Context, in *GetLeavesByRangeRequest, opts ...grpc.CallOption) (*GetLeavesByRangeResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetLeavesByRangeResponse) err := c.cc.Invoke(ctx, TrillianLog_GetLeavesByRange_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } // TrillianLogServer is the server API for TrillianLog service. // All implementations should embed UnimplementedTrillianLogServer // for forward compatibility. // // The TrillianLog service provides access to an append-only Log data structure // as described in the [Verifiable Data // Structures](docs/papers/VerifiableDataStructures.pdf) paper. // // The API supports adding new entries to the Merkle tree for a specific Log // instance (identified by its log_id) in two modes: // - For a normal log, new leaf entries are queued up for subsequent // inclusion in the log, and the leaves are assigned consecutive leaf_index // values as part of that integration process. // - For a 'pre-ordered log', new entries have an already-defined leaf // ordering, and leaves are only integrated into the Merkle tree when a // contiguous range of leaves is available. // // The API also supports read operations to retrieve leaf contents, and to // provide cryptographic proofs of leaf inclusion and of the append-only nature // of the Log. // // Each API request also includes a charge_to field, which allows API users // to provide quota identifiers that should be "charged" for each API request // (and potentially rejected with codes.ResourceExhausted). // // Various operations on the API also allows for 'server skew', which can occur // when different API requests happen to be handled by different server instances // that may not all be up to date. An API request that is relative to a specific // tree size may reach a server instance that is not yet aware of this tree size; // in this case the server will typically return an OK response that contains: // - a signed log root that indicates the tree size that it is aware of // - an empty response otherwise. type TrillianLogServer interface { // QueueLeaf adds a single leaf to the queue of pending leaves for a normal // log. QueueLeaf(context.Context, *QueueLeafRequest) (*QueueLeafResponse, error) // GetInclusionProof returns an inclusion proof for a leaf with a given index // in a particular tree. // // If the requested tree_size is larger than the server is aware of, the // response will include the latest known log root and an empty proof. GetInclusionProof(context.Context, *GetInclusionProofRequest) (*GetInclusionProofResponse, error) // GetInclusionProofByHash returns an inclusion proof for any leaves that have // the given Merkle hash in a particular tree. // // If any of the leaves that match the given Merkle has have a leaf index that // is beyond the requested tree size, the corresponding proof entry will be empty. GetInclusionProofByHash(context.Context, *GetInclusionProofByHashRequest) (*GetInclusionProofByHashResponse, error) // GetConsistencyProof returns a consistency proof between different sizes of // a particular tree. // // If the requested tree size is larger than the server is aware of, // the response will include the latest known log root and an empty proof. GetConsistencyProof(context.Context, *GetConsistencyProofRequest) (*GetConsistencyProofResponse, error) // GetLatestSignedLogRoot returns the latest log root for a given tree, // and optionally also includes a consistency proof from an earlier tree size // to the new size of the tree. // // If the earlier tree size is larger than the server is aware of, // an InvalidArgument error is returned. GetLatestSignedLogRoot(context.Context, *GetLatestSignedLogRootRequest) (*GetLatestSignedLogRootResponse, error) // GetEntryAndProof returns a log leaf and the corresponding inclusion proof // to a specified tree size, for a given leaf index in a particular tree. // // If the requested tree size is unavailable but the leaf is // in scope for the current tree, the returned proof will be for the // current tree size rather than the requested tree size. GetEntryAndProof(context.Context, *GetEntryAndProofRequest) (*GetEntryAndProofResponse, error) // InitLog initializes a particular tree, creating the initial signed log // root (which will be of size 0). InitLog(context.Context, *InitLogRequest) (*InitLogResponse, error) // AddSequencedLeaves adds a batch of leaves with assigned sequence numbers // to a pre-ordered log. The indices of the provided leaves must be contiguous. AddSequencedLeaves(context.Context, *AddSequencedLeavesRequest) (*AddSequencedLeavesResponse, error) // GetLeavesByRange returns a batch of leaves whose leaf indices are in a // sequential range. GetLeavesByRange(context.Context, *GetLeavesByRangeRequest) (*GetLeavesByRangeResponse, error) } // UnimplementedTrillianLogServer should be embedded to have // forward compatible implementations. // // NOTE: this should be embedded by value instead of pointer to avoid a nil // pointer dereference when methods are called. type UnimplementedTrillianLogServer struct{} func (UnimplementedTrillianLogServer) QueueLeaf(context.Context, *QueueLeafRequest) (*QueueLeafResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method QueueLeaf not implemented") } func (UnimplementedTrillianLogServer) GetInclusionProof(context.Context, *GetInclusionProofRequest) (*GetInclusionProofResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetInclusionProof not implemented") } func (UnimplementedTrillianLogServer) GetInclusionProofByHash(context.Context, *GetInclusionProofByHashRequest) (*GetInclusionProofByHashResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetInclusionProofByHash not implemented") } func (UnimplementedTrillianLogServer) GetConsistencyProof(context.Context, *GetConsistencyProofRequest) (*GetConsistencyProofResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetConsistencyProof not implemented") } func (UnimplementedTrillianLogServer) GetLatestSignedLogRoot(context.Context, *GetLatestSignedLogRootRequest) (*GetLatestSignedLogRootResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetLatestSignedLogRoot not implemented") } func (UnimplementedTrillianLogServer) GetEntryAndProof(context.Context, *GetEntryAndProofRequest) (*GetEntryAndProofResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetEntryAndProof not implemented") } func (UnimplementedTrillianLogServer) InitLog(context.Context, *InitLogRequest) (*InitLogResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method InitLog not implemented") } func (UnimplementedTrillianLogServer) AddSequencedLeaves(context.Context, *AddSequencedLeavesRequest) (*AddSequencedLeavesResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method AddSequencedLeaves not implemented") } func (UnimplementedTrillianLogServer) GetLeavesByRange(context.Context, *GetLeavesByRangeRequest) (*GetLeavesByRangeResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetLeavesByRange not implemented") } func (UnimplementedTrillianLogServer) testEmbeddedByValue() {} // UnsafeTrillianLogServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to TrillianLogServer will // result in compilation errors. type UnsafeTrillianLogServer interface { mustEmbedUnimplementedTrillianLogServer() } func RegisterTrillianLogServer(s grpc.ServiceRegistrar, srv TrillianLogServer) { // If the following call pancis, it indicates UnimplementedTrillianLogServer was // embedded by pointer and is nil. This will cause panics if an // unimplemented method is ever invoked, so we test this at initialization // time to prevent it from happening at runtime later due to I/O. if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { t.testEmbeddedByValue() } s.RegisterService(&TrillianLog_ServiceDesc, srv) } func _TrillianLog_QueueLeaf_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(QueueLeafRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(TrillianLogServer).QueueLeaf(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: TrillianLog_QueueLeaf_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(TrillianLogServer).QueueLeaf(ctx, req.(*QueueLeafRequest)) } return interceptor(ctx, in, info, handler) } func _TrillianLog_GetInclusionProof_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(GetInclusionProofRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(TrillianLogServer).GetInclusionProof(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: TrillianLog_GetInclusionProof_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(TrillianLogServer).GetInclusionProof(ctx, req.(*GetInclusionProofRequest)) } return interceptor(ctx, in, info, handler) } func _TrillianLog_GetInclusionProofByHash_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(GetInclusionProofByHashRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(TrillianLogServer).GetInclusionProofByHash(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: TrillianLog_GetInclusionProofByHash_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(TrillianLogServer).GetInclusionProofByHash(ctx, req.(*GetInclusionProofByHashRequest)) } return interceptor(ctx, in, info, handler) } func _TrillianLog_GetConsistencyProof_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(GetConsistencyProofRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(TrillianLogServer).GetConsistencyProof(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: TrillianLog_GetConsistencyProof_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(TrillianLogServer).GetConsistencyProof(ctx, req.(*GetConsistencyProofRequest)) } return interceptor(ctx, in, info, handler) } func _TrillianLog_GetLatestSignedLogRoot_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(GetLatestSignedLogRootRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(TrillianLogServer).GetLatestSignedLogRoot(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: TrillianLog_GetLatestSignedLogRoot_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(TrillianLogServer).GetLatestSignedLogRoot(ctx, req.(*GetLatestSignedLogRootRequest)) } return interceptor(ctx, in, info, handler) } func _TrillianLog_GetEntryAndProof_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(GetEntryAndProofRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(TrillianLogServer).GetEntryAndProof(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: TrillianLog_GetEntryAndProof_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(TrillianLogServer).GetEntryAndProof(ctx, req.(*GetEntryAndProofRequest)) } return interceptor(ctx, in, info, handler) } func _TrillianLog_InitLog_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(InitLogRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(TrillianLogServer).InitLog(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: TrillianLog_InitLog_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(TrillianLogServer).InitLog(ctx, req.(*InitLogRequest)) } return interceptor(ctx, in, info, handler) } func _TrillianLog_AddSequencedLeaves_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(AddSequencedLeavesRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(TrillianLogServer).AddSequencedLeaves(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: TrillianLog_AddSequencedLeaves_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(TrillianLogServer).AddSequencedLeaves(ctx, req.(*AddSequencedLeavesRequest)) } return interceptor(ctx, in, info, handler) } func _TrillianLog_GetLeavesByRange_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(GetLeavesByRangeRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(TrillianLogServer).GetLeavesByRange(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: TrillianLog_GetLeavesByRange_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(TrillianLogServer).GetLeavesByRange(ctx, req.(*GetLeavesByRangeRequest)) } return interceptor(ctx, in, info, handler) } // TrillianLog_ServiceDesc is the grpc.ServiceDesc for TrillianLog service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var TrillianLog_ServiceDesc = grpc.ServiceDesc{ ServiceName: "trillian.TrillianLog", HandlerType: (*TrillianLogServer)(nil), Methods: []grpc.MethodDesc{ { MethodName: "QueueLeaf", Handler: _TrillianLog_QueueLeaf_Handler, }, { MethodName: "GetInclusionProof", Handler: _TrillianLog_GetInclusionProof_Handler, }, { MethodName: "GetInclusionProofByHash", Handler: _TrillianLog_GetInclusionProofByHash_Handler, }, { MethodName: "GetConsistencyProof", Handler: _TrillianLog_GetConsistencyProof_Handler, }, { MethodName: "GetLatestSignedLogRoot", Handler: _TrillianLog_GetLatestSignedLogRoot_Handler, }, { MethodName: "GetEntryAndProof", Handler: _TrillianLog_GetEntryAndProof_Handler, }, { MethodName: "InitLog", Handler: _TrillianLog_InitLog_Handler, }, { MethodName: "AddSequencedLeaves", Handler: _TrillianLog_AddSequencedLeaves_Handler, }, { MethodName: "GetLeavesByRange", Handler: _TrillianLog_GetLeavesByRange_Handler, }, }, Streams: []grpc.StreamDesc{}, Metadata: "trillian_log_api.proto", } trillian-1.6.1/types/000077500000000000000000000000001466362047600145175ustar00rootroot00000000000000trillian-1.6.1/types/internal/000077500000000000000000000000001466362047600163335ustar00rootroot00000000000000trillian-1.6.1/types/internal/tls/000077500000000000000000000000001466362047600171355ustar00rootroot00000000000000trillian-1.6.1/types/internal/tls/tls.go000066400000000000000000000534721466362047600203010ustar00rootroot00000000000000// 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 tls implements functionality for dealing with TLS-encoded data, // as defined in RFC 5246. This includes parsing and generation of TLS-encoded // data, together with utility functions for dealing with the DigitallySigned // TLS type. // N.B. This is copied from https://github.com/google/certificate-transparency-go/tree/master/tls // - DO NOT MAKE CHANGES TO THIS FILE except to sync to the latest from ct-go. package tls import ( "bytes" "encoding/binary" "fmt" "reflect" "strconv" "strings" ) // This file holds utility functions for TLS encoding/decoding data // as per RFC 5246 section 4. // A structuralError suggests that the TLS data is valid, but the Go type // which is receiving it doesn't match. type structuralError struct { field string msg string } func (e structuralError) Error() string { var prefix string if e.field != "" { prefix = e.field + ": " } return "tls: structure error: " + prefix + e.msg } // A syntaxError suggests that the TLS data is invalid. type syntaxError struct { field string msg string } func (e syntaxError) Error() string { var prefix string if e.field != "" { prefix = e.field + ": " } return "tls: syntax error: " + prefix + e.msg } // Uint24 is an unsigned 3-byte integer. type Uint24 uint32 // Enum is an unsigned integer. type Enum uint64 var ( uint8Type = reflect.TypeOf(uint8(0)) uint16Type = reflect.TypeOf(uint16(0)) uint24Type = reflect.TypeOf(Uint24(0)) uint32Type = reflect.TypeOf(uint32(0)) uint64Type = reflect.TypeOf(uint64(0)) enumType = reflect.TypeOf(Enum(0)) ) // Unmarshal parses the TLS-encoded data in b and uses the reflect package to // fill in an arbitrary value pointed at by val. Because Unmarshal uses the // reflect package, the structs being written to must use exported fields // (upper case names). // // The mappings between TLS types and Go types is as follows; some fields // must have tags (to indicate their encoded size). // // TLS Go Required Tags // opaque byte / uint8 // uint8 byte / uint8 // uint16 uint16 // uint24 tls.Uint24 // uint32 uint32 // uint64 uint64 // enum tls.Enum size:S or maxval:N // Type []Type minlen:N,maxlen:M // opaque[N] [N]byte / [N]uint8 // uint8[N] [N]byte / [N]uint8 // struct { } struct { } // select(T) { // case e1: Type *T selector:Field,val:e1 // } // // TLS variants (RFC 5246 s4.6.1) are only supported when the value of the // associated enumeration type is available earlier in the same enclosing // struct, and each possible variant is marked with a selector tag (to // indicate which field selects the variants) and a val tag (to indicate // what value of the selector picks this particular field). // // For example, a TLS structure: // // enum { e1(1), e2(2) } EnumType; // struct { // EnumType sel; // select(sel) { // case e1: uint16 // case e2: uint32 // } data; // } VariantItem; // // would have a corresponding Go type: // // type VariantItem struct { // Sel tls.Enum `tls:"maxval:2"` // Data16 *uint16 `tls:"selector:Sel,val:1"` // Data32 *uint32 `tls:"selector:Sel,val:2"` // } // // TLS fixed-length vectors of types other than opaque or uint8 are not supported. // // For TLS variable-length vectors that are themselves used in other vectors, // create a single-field structure to represent the inner type. For example, for: // // opaque InnerType<1..65535>; // struct { // InnerType inners<1,65535>; // } Something; // // convert to: // // type InnerType struct { // Val []byte `tls:"minlen:1,maxlen:65535"` // } // type Something struct { // Inners []InnerType `tls:"minlen:1,maxlen:65535"` // } // // If the encoded value does not fit in the Go type, Unmarshal returns a parse error. func Unmarshal(b []byte, val interface{}) ([]byte, error) { return UnmarshalWithParams(b, val, "") } // UnmarshalWithParams allows field parameters to be specified for the // top-level element. The form of the params is the same as the field tags. func UnmarshalWithParams(b []byte, val interface{}, params string) ([]byte, error) { info, err := fieldTagToFieldInfo(params, "") if err != nil { return nil, err } // The passed in interface{} is a pointer (to allow the value to be written // to); extract the pointed-to object as a reflect.Value, so parseField // can do various introspection things. v := reflect.ValueOf(val).Elem() offset, err := parseField(v, b, 0, info) if err != nil { return nil, err } return b[offset:], nil } // Return the number of bytes needed to encode values up to (and including) x. func byteCount(x uint64) uint { switch { case x < 0x100: return 1 case x < 0x10000: return 2 case x < 0x1000000: return 3 case x < 0x100000000: return 4 case x < 0x10000000000: return 5 case x < 0x1000000000000: return 6 case x < 0x100000000000000: return 7 default: return 8 } } type fieldInfo struct { count uint // Number of bytes countSet bool minlen uint64 // Only relevant for slices maxlen uint64 // Only relevant for slices selector string // Only relevant for select sub-values val uint64 // Only relevant for select sub-values name string // Used for better error messages } func (i *fieldInfo) fieldName() string { if i == nil { return "" } return i.name } // Given a tag string, return a fieldInfo describing the field. func fieldTagToFieldInfo(str string, name string) (*fieldInfo, error) { var info *fieldInfo // Iterate over clauses in the tag, ignoring any that don't parse properly. for _, part := range strings.Split(str, ",") { switch { case strings.HasPrefix(part, "maxval:"): if v, err := strconv.ParseUint(part[7:], 10, 64); err == nil { info = &fieldInfo{count: byteCount(v), countSet: true} } case strings.HasPrefix(part, "size:"): if sz, err := strconv.ParseUint(part[5:], 10, 32); err == nil { info = &fieldInfo{count: uint(sz), countSet: true} } case strings.HasPrefix(part, "maxlen:"): v, err := strconv.ParseUint(part[7:], 10, 64) if err != nil { continue } if info == nil { info = &fieldInfo{} } info.count = byteCount(v) info.countSet = true info.maxlen = v case strings.HasPrefix(part, "minlen:"): v, err := strconv.ParseUint(part[7:], 10, 64) if err != nil { continue } if info == nil { info = &fieldInfo{} } info.minlen = v case strings.HasPrefix(part, "selector:"): if info == nil { info = &fieldInfo{} } info.selector = part[9:] case strings.HasPrefix(part, "val:"): v, err := strconv.ParseUint(part[4:], 10, 64) if err != nil { continue } if info == nil { info = &fieldInfo{} } info.val = v } } if info != nil { info.name = name if info.selector == "" { if info.count < 1 { return nil, structuralError{name, "field of unknown size in " + str} } else if info.count > 8 { return nil, structuralError{name, "specified size too large in " + str} } else if info.minlen > info.maxlen { return nil, structuralError{name, "specified length range inverted in " + str} } else if info.val > 0 { return nil, structuralError{name, "specified selector value but not field in " + str} } } } else if name != "" { info = &fieldInfo{name: name} } return info, nil } // Check that a value fits into a field described by a fieldInfo structure. func (i fieldInfo) check(val uint64, fldName string) error { if val >= (1 << (8 * i.count)) { return structuralError{fldName, fmt.Sprintf("value %d too large for size", val)} } if i.maxlen != 0 { if val < i.minlen { return structuralError{fldName, fmt.Sprintf("value %d too small for minimum %d", val, i.minlen)} } if val > i.maxlen { return structuralError{fldName, fmt.Sprintf("value %d too large for maximum %d", val, i.maxlen)} } } return nil } // readVarUint reads an big-endian unsigned integer of the given size in // bytes. func readVarUint(data []byte, info *fieldInfo) (uint64, error) { if info == nil || !info.countSet { return 0, structuralError{info.fieldName(), "no field size information available"} } if len(data) < int(info.count) { return 0, syntaxError{info.fieldName(), "truncated variable-length integer"} } var result uint64 for i := uint(0); i < info.count; i++ { result = (result << 8) | uint64(data[i]) } if err := info.check(result, info.name); err != nil { return 0, err } return result, nil } // parseField is the main parsing function. Given a byte slice and an offset // (in bytes) into the data, it will try to parse a suitable ASN.1 value out // and store it in the given Value. func parseField(v reflect.Value, data []byte, initOffset int, info *fieldInfo) (int, error) { offset := initOffset rest := data[offset:] fieldType := v.Type() // First look for known fixed types. switch fieldType { case uint8Type: if len(rest) < 1 { return offset, syntaxError{info.fieldName(), "truncated uint8"} } v.SetUint(uint64(rest[0])) offset++ return offset, nil case uint16Type: if len(rest) < 2 { return offset, syntaxError{info.fieldName(), "truncated uint16"} } v.SetUint(uint64(binary.BigEndian.Uint16(rest))) offset += 2 return offset, nil case uint24Type: if len(rest) < 3 { return offset, syntaxError{info.fieldName(), "truncated uint24"} } v.SetUint(uint64(data[0])<<16 | uint64(data[1])<<8 | uint64(data[2])) offset += 3 return offset, nil case uint32Type: if len(rest) < 4 { return offset, syntaxError{info.fieldName(), "truncated uint32"} } v.SetUint(uint64(binary.BigEndian.Uint32(rest))) offset += 4 return offset, nil case uint64Type: if len(rest) < 8 { return offset, syntaxError{info.fieldName(), "truncated uint64"} } v.SetUint(uint64(binary.BigEndian.Uint64(rest))) offset += 8 return offset, nil } // Now deal with user-defined types. switch v.Kind() { case enumType.Kind(): // Assume that anything of the same kind as Enum is an Enum, so that // users can alias types of their own to Enum. val, err := readVarUint(rest, info) if err != nil { return offset, err } v.SetUint(val) offset += int(info.count) return offset, nil case reflect.Struct: structType := fieldType // TLS includes a select(Enum) {..} construct, where the value of an enum // indicates which variant field is present (like a C union). We require // that the enum value be an earlier field in the same structure (the selector), // and that each of the possible variant destination fields be pointers. // So the Go mapping looks like: // type variantType struct { // Which tls.Enum `tls:"size:1"` // this is the selector // Val1 *type1 `tls:"selector:Which,val:1"` // this is a destination // Val2 *type2 `tls:"selector:Which,val:1"` // this is a destination // } // To deal with this, we track any enum-like fields and their values... enums := make(map[string]uint64) // .. and we track which selector names we've seen (in the destination field tags), // and whether a destination for that selector has been chosen. selectorSeen := make(map[string]bool) for i := 0; i < structType.NumField(); i++ { // Find information about this field. tag := structType.Field(i).Tag.Get("tls") fieldInfo, err := fieldTagToFieldInfo(tag, structType.Field(i).Name) if err != nil { return offset, err } destination := v.Field(i) if fieldInfo.selector != "" { // This is a possible select(Enum) destination, so first check that the referenced // selector field has already been seen earlier in the struct. choice, ok := enums[fieldInfo.selector] if !ok { return offset, structuralError{fieldInfo.name, "selector not seen: " + fieldInfo.selector} } if structType.Field(i).Type.Kind() != reflect.Ptr { return offset, structuralError{fieldInfo.name, "choice field not a pointer type"} } // Is this the first mention of the selector field name? If so, remember it. seen, ok := selectorSeen[fieldInfo.selector] if !ok { selectorSeen[fieldInfo.selector] = false } if choice != fieldInfo.val { // This destination field was not the chosen one, so make it nil (we checked // it was a pointer above). v.Field(i).Set(reflect.Zero(structType.Field(i).Type)) continue } if seen { // We already saw a different destination field receive the value for this // selector value, which indicates a badly annotated structure. return offset, structuralError{fieldInfo.name, "duplicate selector value for " + fieldInfo.selector} } selectorSeen[fieldInfo.selector] = true // Make an object of the pointed-to type and parse into that. v.Field(i).Set(reflect.New(structType.Field(i).Type.Elem())) destination = v.Field(i).Elem() } offset, err = parseField(destination, data, offset, fieldInfo) if err != nil { return offset, err } // Remember any possible tls.Enum values encountered in case they are selectors. if structType.Field(i).Type.Kind() == enumType.Kind() { enums[structType.Field(i).Name] = v.Field(i).Uint() } } // Now we have seen all fields in the structure, check that all select(Enum) {..} selector // fields found a destination to put their data in. for selector, seen := range selectorSeen { if !seen { return offset, syntaxError{info.fieldName(), selector + ": unhandled value for selector"} } } return offset, nil case reflect.Array: datalen := v.Len() if datalen > len(rest) { return offset, syntaxError{info.fieldName(), "truncated array"} } inner := rest[:datalen] offset += datalen if fieldType.Elem().Kind() != reflect.Uint8 { // Only byte/uint8 arrays are supported return offset, structuralError{info.fieldName(), "unsupported array type: " + v.Type().String()} } reflect.Copy(v, reflect.ValueOf(inner)) return offset, nil case reflect.Slice: sliceType := fieldType // Slices represent variable-length vectors, which are prefixed by a length field. // The fieldInfo indicates the size of that length field. varlen, err := readVarUint(rest, info) if err != nil { return offset, err } datalen := int(varlen) offset += int(info.count) rest = rest[info.count:] if datalen > len(rest) { return offset, syntaxError{info.fieldName(), "truncated slice"} } inner := rest[:datalen] offset += datalen if fieldType.Elem().Kind() == reflect.Uint8 { // Fast version for []byte v.Set(reflect.MakeSlice(sliceType, datalen, datalen)) reflect.Copy(v, reflect.ValueOf(inner)) return offset, nil } v.Set(reflect.MakeSlice(sliceType, 0, datalen)) single := reflect.New(sliceType.Elem()) for innerOffset := 0; innerOffset < len(inner); { var err error innerOffset, err = parseField(single.Elem(), inner, innerOffset, nil) if err != nil { return offset, err } v.Set(reflect.Append(v, single.Elem())) } return offset, nil default: return offset, structuralError{info.fieldName(), fmt.Sprintf("unsupported type: %s of kind %s", fieldType, v.Kind())} } } // Marshal returns the TLS encoding of val. func Marshal(val interface{}) ([]byte, error) { return MarshalWithParams(val, "") } // MarshalWithParams returns the TLS encoding of val, and allows field // parameters to be specified for the top-level element. The form // of the params is the same as the field tags. func MarshalWithParams(val interface{}, params string) ([]byte, error) { info, err := fieldTagToFieldInfo(params, "") if err != nil { return nil, err } var out bytes.Buffer v := reflect.ValueOf(val) if err := marshalField(&out, v, info); err != nil { return nil, err } return out.Bytes(), err } func marshalField(out *bytes.Buffer, v reflect.Value, info *fieldInfo) error { var prefix string if info != nil && len(info.name) > 0 { prefix = info.name + ": " } fieldType := v.Type() // First look for known fixed types. switch fieldType { case uint8Type: out.WriteByte(byte(v.Uint())) return nil case uint16Type: scratch := make([]byte, 2) binary.BigEndian.PutUint16(scratch, uint16(v.Uint())) out.Write(scratch) return nil case uint24Type: i := v.Uint() if i > 0xffffff { return structuralError{info.fieldName(), fmt.Sprintf("uint24 overflow %d", i)} } scratch := make([]byte, 4) binary.BigEndian.PutUint32(scratch, uint32(i)) out.Write(scratch[1:]) return nil case uint32Type: scratch := make([]byte, 4) binary.BigEndian.PutUint32(scratch, uint32(v.Uint())) out.Write(scratch) return nil case uint64Type: scratch := make([]byte, 8) binary.BigEndian.PutUint64(scratch, uint64(v.Uint())) out.Write(scratch) return nil } // Now deal with user-defined types. switch v.Kind() { case enumType.Kind(): i := v.Uint() if info == nil { return structuralError{info.fieldName(), "enum field tag missing"} } if err := info.check(i, prefix); err != nil { return err } scratch := make([]byte, 8) binary.BigEndian.PutUint64(scratch, uint64(i)) out.Write(scratch[(8 - info.count):]) return nil case reflect.Struct: structType := fieldType enums := make(map[string]uint64) // Values of any Enum fields // The comment parseField() describes the mapping of the TLS select(Enum) {..} construct; // here we have selector and source (rather than destination) fields. // Track which selector names we've seen (in the source field tags), and whether a source // value for that selector has been processed. selectorSeen := make(map[string]bool) for i := 0; i < structType.NumField(); i++ { // Find information about this field. tag := structType.Field(i).Tag.Get("tls") fieldInfo, err := fieldTagToFieldInfo(tag, structType.Field(i).Name) if err != nil { return err } source := v.Field(i) if fieldInfo.selector != "" { // This field is a possible source for a select(Enum) {..}. First check // the selector field name has been seen. choice, ok := enums[fieldInfo.selector] if !ok { return structuralError{fieldInfo.name, "selector not seen: " + fieldInfo.selector} } if structType.Field(i).Type.Kind() != reflect.Ptr { return structuralError{fieldInfo.name, "choice field not a pointer type"} } // Is this the first mention of the selector field name? If so, remember it. seen, ok := selectorSeen[fieldInfo.selector] if !ok { selectorSeen[fieldInfo.selector] = false } if choice != fieldInfo.val { // This source was not chosen; police that it should be nil. if v.Field(i).Pointer() != uintptr(0) { return structuralError{fieldInfo.name, "unchosen field is non-nil"} } continue } if seen { // We already saw a different source field generate the value for this // selector value, which indicates a badly annotated structure. return structuralError{fieldInfo.name, "duplicate selector value for " + fieldInfo.selector} } selectorSeen[fieldInfo.selector] = true if v.Field(i).Pointer() == uintptr(0) { return structuralError{fieldInfo.name, "chosen field is nil"} } // Marshal from the pointed-to source object. source = v.Field(i).Elem() } var fieldData bytes.Buffer if err := marshalField(&fieldData, source, fieldInfo); err != nil { return err } out.Write(fieldData.Bytes()) // Remember any tls.Enum values encountered in case they are selectors. if structType.Field(i).Type.Kind() == enumType.Kind() { enums[structType.Field(i).Name] = v.Field(i).Uint() } } // Now we have seen all fields in the structure, check that all select(Enum) {..} selector // fields found a source field get get their data from. for selector, seen := range selectorSeen { if !seen { return syntaxError{info.fieldName(), selector + ": unhandled value for selector"} } } return nil case reflect.Array: datalen := v.Len() arrayType := fieldType if arrayType.Elem().Kind() != reflect.Uint8 { // Only byte/uint8 arrays are supported return structuralError{info.fieldName(), "unsupported array type"} } bytes := make([]byte, datalen) for i := 0; i < datalen; i++ { bytes[i] = uint8(v.Index(i).Uint()) } _, err := out.Write(bytes) return err case reflect.Slice: if info == nil { return structuralError{info.fieldName(), "slice field tag missing"} } sliceType := fieldType if sliceType.Elem().Kind() == reflect.Uint8 { // Fast version for []byte: first write the length as info.count bytes. datalen := v.Len() scratch := make([]byte, 8) binary.BigEndian.PutUint64(scratch, uint64(datalen)) out.Write(scratch[(8 - info.count):]) if err := info.check(uint64(datalen), prefix); err != nil { return err } // Then just write the data. bytes := make([]byte, datalen) for i := 0; i < datalen; i++ { bytes[i] = uint8(v.Index(i).Uint()) } _, err := out.Write(bytes) return err } // General version: use a separate Buffer to write the slice entries into. var innerBuf bytes.Buffer for i := 0; i < v.Len(); i++ { if err := marshalField(&innerBuf, v.Index(i), nil); err != nil { return err } } // Now insert (and check) the size. size := uint64(innerBuf.Len()) if err := info.check(size, prefix); err != nil { return err } scratch := make([]byte, 8) binary.BigEndian.PutUint64(scratch, size) out.Write(scratch[(8 - info.count):]) // Then copy the data. _, err := out.Write(innerBuf.Bytes()) return err default: return structuralError{info.fieldName(), fmt.Sprintf("unsupported type: %s of kind %s", fieldType, v.Kind())} } } trillian-1.6.1/types/logroot.go000066400000000000000000000057761466362047600165520ustar00rootroot00000000000000// Copyright 2018 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 types defines serialization and parsing functions for SignedLogRoot // fields. package types import ( "encoding/binary" "fmt" "github.com/google/trillian/types/internal/tls" "github.com/google/trillian" ) // LogRootV1 holds the TLS-deserialization of the following structure // (described in RFC5246 section 4 notation): // // struct { // uint64 tree_size; // opaque root_hash<0..128>; // uint64 timestamp_nanos; // uint64 revision; // opaque metadata<0..65535>; // } LogRootV1; type LogRootV1 struct { // TreeSize is the number of leaves in the log Merkle tree. TreeSize uint64 // RootHash is the hash of the root node of the tree. RootHash []byte `tls:"minlen:0,maxlen:128"` // TimestampNanos is the time in nanoseconds for when this root was created, // counting from the UNIX epoch. TimestampNanos uint64 // Revision is the Merkle tree revision associated with this root. // // Deprecated: Revision is a concept internal to the storage layer. Revision uint64 // Metadata holds additional data associated with this root. Metadata []byte `tls:"minlen:0,maxlen:65535"` } // LogRoot holds the TLS-deserialization of the following structure // (described in RFC5246 section 4 notation): // enum { v1(1), (65535)} Version; // // struct { // Version version; // select(version) { // case v1: LogRootV1; // } // } LogRoot; type LogRoot struct { Version tls.Enum `tls:"size:2"` V1 *LogRootV1 `tls:"selector:Version,val:1"` } // UnmarshalBinary verifies that logRootBytes is a TLS serialized LogRoot, has // the LOG_ROOT_FORMAT_V1 tag, and populates the caller with the deserialized // *LogRootV1. func (l *LogRootV1) UnmarshalBinary(logRootBytes []byte) error { if len(logRootBytes) < 3 { return fmt.Errorf("logRootBytes too short") } if l == nil { return fmt.Errorf("nil log root") } version := binary.BigEndian.Uint16(logRootBytes) if version != uint16(trillian.LogRootFormat_LOG_ROOT_FORMAT_V1) { return fmt.Errorf("invalid LogRoot.Version: %v, want %v", version, trillian.LogRootFormat_LOG_ROOT_FORMAT_V1) } var logRoot LogRoot if _, err := tls.Unmarshal(logRootBytes, &logRoot); err != nil { return err } *l = *logRoot.V1 return nil } // MarshalBinary returns a canonical TLS serialization of LogRoot. func (l *LogRootV1) MarshalBinary() ([]byte, error) { return tls.Marshal(LogRoot{ Version: tls.Enum(trillian.LogRootFormat_LOG_ROOT_FORMAT_V1), V1: l, }) } trillian-1.6.1/types/logroot_test.go000066400000000000000000000046441466362047600176020ustar00rootroot00000000000000// 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 types import ( "encoding" "reflect" "testing" _ "k8s.io/klog/v2" // Don't crash when --logtostderr is supplied ) func TestLogRoot(t *testing.T) { for _, logRoot := range []interface { encoding.BinaryMarshaler encoding.BinaryUnmarshaler }{ &LogRootV1{ RootHash: []byte("foo"), Metadata: []byte{}, }, } { b, err := logRoot.MarshalBinary() if err != nil { t.Errorf("%v MarshalBinary(): %v", logRoot, err) continue } var got LogRootV1 if err := got.UnmarshalBinary(b); err != nil { t.Errorf("UnmarshalBinary(): %v", err) continue } if !reflect.DeepEqual(&got, logRoot) { t.Errorf("serialize/parse round trip failed. got %#v, want %#v", got, logRoot) } } } func TestUnmarshalLogRoot(t *testing.T) { for _, tc := range []struct { logRoot []byte wantErr bool }{ {logRoot: MustMarshalLogRoot(&LogRootV1{})}, { logRoot: func() []byte { b := MustMarshalLogRoot(&LogRootV1{}) b[0] = 1 // Corrupt the version tag. return b }(), wantErr: true, }, { // Correct type, but junk afterwards. logRoot: []byte{0, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5}, wantErr: true, }, { // Incorrect type. logRoot: []byte{0}, wantErr: true, }, {logRoot: []byte("foo"), wantErr: true}, {logRoot: nil, wantErr: true}, } { var got LogRootV1 err := got.UnmarshalBinary(tc.logRoot) if got, want := err != nil, tc.wantErr; got != want { t.Errorf("UnmarshalBinary(): %v, wantErr %v", err, want) } } // Unmarshaling to a nil should throw an error. var nilPtr *LogRootV1 if err := nilPtr.UnmarshalBinary(MustMarshalLogRoot(&LogRootV1{})); err == nil { t.Errorf("nil.UnmarshalBinary(): %v, want err", err) } } func MustMarshalLogRoot(root *LogRootV1) []byte { b, err := root.MarshalBinary() if err != nil { panic(err) } return b } trillian-1.6.1/util/000077500000000000000000000000001466362047600143305ustar00rootroot00000000000000trillian-1.6.1/util/clock/000077500000000000000000000000001466362047600154235ustar00rootroot00000000000000trillian-1.6.1/util/clock/time.go000066400000000000000000000030441466362047600167110ustar00rootroot00000000000000// Copyright 2018 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 clock import ( "context" "time" ) // SleepContext sleeps for at least the specified duration. Returns ctx.Err() // iff the context is done before the deadline. func SleepContext(ctx context.Context, d time.Duration) error { if !sleepImpl(ctx.Done(), d, System) { return ctx.Err() } return nil } // SleepSource sleeps for at least the specified duration, as measured by the // TimeSource. Returns ctx.Err() iff the context is done before the deadline. func SleepSource(ctx context.Context, d time.Duration, s TimeSource) error { if !sleepImpl(ctx.Done(), d, s) { return ctx.Err() } return nil } // sleepImpl sleeps for at least the specified duration, as measured by the // TimeSource. Returns false iff done is closed before the deadline. func sleepImpl(done <-chan struct{}, d time.Duration, s TimeSource) bool { timer := s.NewTimer(d) defer timer.Stop() select { case <-timer.Chan(): return true case <-done: return false } } trillian-1.6.1/util/clock/time_test.go000066400000000000000000000045211466362047600177510ustar00rootroot00000000000000// Copyright 2018 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 clock import ( "context" "fmt" "testing" "time" ) var cases = []struct { dur time.Duration timeout time.Duration cancel bool wantErr error }{ {dur: 0 * time.Second, timeout: time.Second}, {dur: 10 * time.Millisecond, timeout: 200 * time.Millisecond}, {dur: 200 * time.Millisecond, timeout: 10 * time.Millisecond, wantErr: context.DeadlineExceeded}, {dur: 1 * time.Millisecond, timeout: 0, wantErr: context.DeadlineExceeded}, {dur: 10 * time.Millisecond, timeout: 20 * time.Millisecond, cancel: true, wantErr: context.Canceled}, } func TestSleepContext(t *testing.T) { for _, tc := range cases { tc := tc t.Run(fmt.Sprintf("%v:%v", tc.dur, tc.timeout), func(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), tc.timeout) if tc.cancel { cancel() } else { defer cancel() } if got, want := SleepContext(ctx, tc.dur), tc.wantErr; got != want { t.Errorf("SleepContext() returned %v, want %v", got, want) } }) } } func TestSleepSource(t *testing.T) { for _, tc := range cases { tc := tc t.Run(fmt.Sprintf("%v:%v", tc.dur, tc.timeout), func(t *testing.T) { base := time.Now() ts := NewFake(base) ctx, cancel := context.WithCancel(context.Background()) if tc.cancel { cancel() } else { defer cancel() } go func() { // Give SleepSource some time to block. time.Sleep(50 * time.Millisecond) if tc.dur < tc.timeout { ts.Set(base.Add(tc.dur)) } else { ts.Set(base.Add(tc.timeout)) cancel() } }() want := tc.wantErr if want == context.DeadlineExceeded { want = context.Canceled } if got := SleepSource(ctx, tc.dur, ts); got != want { t.Errorf("SleepSource() returned %v, want %v", got, want) } }) } } trillian-1.6.1/util/clock/timer.go000066400000000000000000000035121466362047600170730ustar00rootroot00000000000000// Copyright 2018 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 clock import "time" // Timer represents an event that fires with time passage. // See time.Timer type for intuition on how it works. type Timer interface { // Chan returns a channel which is used to deliver the event. Chan() <-chan time.Time // Stop prevents the Timer from firing. Returns false if the event has // already fired, or the Timer has been stopped. Stop() bool } // systemTimer is a Timer that uses system time. type systemTimer struct { *time.Timer } func (t systemTimer) Chan() <-chan time.Time { return t.C } // fakeTimer implements Timer interface for testing. Event firing is controlled // by FakeTimeSource which creates and owns fakeTimer instances. type fakeTimer struct { ts *FakeTimeSource id int when time.Time ch chan time.Time } func newFakeTimer(ts *FakeTimeSource, id int, when time.Time) *fakeTimer { ch := make(chan time.Time, 1) return &fakeTimer{ts: ts, id: id, when: when, ch: ch} } func (t *fakeTimer) Chan() <-chan time.Time { return t.ch } func (t *fakeTimer) Stop() bool { return t.ts.unsubscribe(t.id) } func (t *fakeTimer) tryFire(now time.Time) bool { if t.when.Before(now) || t.when.Equal(now) { select { case t.ch <- now: return true default: } } return false } trillian-1.6.1/util/clock/timer_test.go000066400000000000000000000057151466362047600201410ustar00rootroot00000000000000// Copyright 2018 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 clock import ( "fmt" "testing" "time" ) func checkNotFiring(t *testing.T, timer Timer) { t.Helper() select { case tm := <-timer.Chan(): t.Errorf("Timer unexpectedly fired at %v", tm) case <-time.After(10 * time.Millisecond): // Give some real time to pass. } } func TestFakeTimerFiresOnce(t *testing.T) { base := time.Date(2018, 12, 12, 18, 0, 0, 0, time.UTC) ts := NewFake(base) timer := ts.NewTimer(10 * time.Millisecond) checkNotFiring(t, timer) newTime := base.Add(9 * time.Millisecond) ts.Set(newTime) checkNotFiring(t, timer) newTime = newTime.Add(1 * time.Millisecond) ts.Set(newTime) got := <-timer.Chan() // Now it should fire. if !got.Equal(newTime) { t.Errorf("Timer fired at %v, want %v", got, newTime) } checkNotFiring(t, timer) // Shouldn't fire any more. } func TestFakeTimerStopBeforeFire(t *testing.T) { base := time.Date(2018, 12, 12, 18, 0, 0, 0, time.UTC) ts := NewFake(base) timer := ts.NewTimer(10 * time.Millisecond) checkNotFiring(t, timer) if !timer.Stop() { t.Error("Stop() returns true, want false") } newTime := base.Add(20 * time.Millisecond) ts.Set(newTime) checkNotFiring(t, timer) // Shouldn't fire because it was stopped. } func TestFakeTimerStopAfterFire(t *testing.T) { for _, drain := range []bool{false, true} { t.Run(fmt.Sprintf("drain:%v", drain), func(t *testing.T) { base := time.Date(2018, 12, 12, 18, 0, 0, 0, time.UTC) ts := NewFake(base) timer := ts.NewTimer(10 * time.Millisecond) ts.Set(base.Add(20 * time.Millisecond)) // Triggers the event. if drain { <-timer.Chan() } if timer.Stop() { t.Error("Stop() returns false, want true") } }) } } func TestManyFakeTimers(t *testing.T) { base := time.Date(2018, 12, 12, 18, 0, 0, 0, time.UTC) ts := NewFake(base) var timers []Timer var times []time.Time for i := 1; i <= 10; i++ { d := time.Duration(i) * time.Second timers = append(timers, ts.NewTimer(d)) times = append(times, base.Add(d)) } check := func(fire int, want time.Time) { for i, timer := range timers { if i != fire { checkNotFiring(t, timer) continue } got := <-timer.Chan() if !got.Equal(want) { t.Errorf("Timer %d fired at %v, want %v", i, got, want) } } } check(-1, time.Time{}) // No firing before. for i, fTime := range times { ts.Set(fTime) check(i, fTime) } check(-1, time.Time{}) // No firing after. } trillian-1.6.1/util/clock/timesource.go000066400000000000000000000071521466362047600201360ustar00rootroot00000000000000// 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 clock contains time utilities, and types that allow mocking system // time in tests. package clock import ( "sync" "time" "k8s.io/klog/v2" ) // System is a default TimeSource that provides system time. var System TimeSource = systemTimeSource{} // TimeSource can provide the current time, or be replaced by a mock in tests // to return specific values. type TimeSource interface { // Now returns the current time as seen by this TimeSource. Now() time.Time // NewTimer creates a timer that fires after the specified duration. NewTimer(d time.Duration) Timer } // SecondsSince returns the time in seconds elapsed since t until now, as // measured by the TimeSource. func SecondsSince(ts TimeSource, t time.Time) float64 { return ts.Now().Sub(t).Seconds() } // systemTimeSource provides the current system local time. type systemTimeSource struct{} // Now returns the true current local time. func (s systemTimeSource) Now() time.Time { return time.Now() } // NewTimer returns a real timer. func (s systemTimeSource) NewTimer(d time.Duration) Timer { return systemTimer{time.NewTimer(d)} } // FakeTimeSource provides time that can be arbitrarily set. For tests only. type FakeTimeSource struct { mu sync.RWMutex now time.Time timers map[int]*fakeTimer nextID int } // NewFake creates a FakeTimeSource instance. func NewFake(t time.Time) *FakeTimeSource { timers := make(map[int]*fakeTimer) return &FakeTimeSource{now: t, timers: timers} } // Now returns the time value this instance contains. func (f *FakeTimeSource) Now() time.Time { f.mu.RLock() defer f.mu.RUnlock() return f.now } // NewTimer returns a fake Timer. func (f *FakeTimeSource) NewTimer(d time.Duration) Timer { f.mu.Lock() defer f.mu.Unlock() id := f.nextID f.nextID++ timer := newFakeTimer(f, id, f.now.Add(d)) f.timers[id] = timer return timer } // unsubscribe removes the Timer with the specified ID if it exists, and // returns the existence bit. func (f *FakeTimeSource) unsubscribe(id int) bool { f.mu.Lock() defer f.mu.Unlock() _, ok := f.timers[id] if ok { delete(f.timers, id) } return ok } // Set updates the time that this instance will report. func (f *FakeTimeSource) Set(t time.Time) { f.mu.Lock() defer f.mu.Unlock() f.now = t for id, timer := range f.timers { if timer.tryFire(t) { delete(f.timers, id) } } } // PredefinedFake is a TimeSource that returns a predefined set of times // computed as base time + delays[i]. Delays don't have to be monotonic. type PredefinedFake struct { Base time.Time Delays []time.Duration Next int } // Now returns the current time, which depends on how many times this method // has already been invoked. Must not be called more than len(delays) times. func (p *PredefinedFake) Now() time.Time { adjustedTime := p.Base.Add(p.Delays[p.Next]) p.Next++ return adjustedTime } // NewTimer creates a timer with the specified delay. Not implemented. func (p *PredefinedFake) NewTimer(d time.Duration) Timer { klog.Exitf("PredefinedFake.NewTimer is not implemented") return nil } trillian-1.6.1/util/clock/timesource_test.go000066400000000000000000000025621466362047600211750ustar00rootroot00000000000000// 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 clock import ( "testing" "time" ) var ( date1 = time.Date(1970, 9, 19, 12, 0, 0, 0, time.UTC) date2 = time.Date(2007, 7, 7, 11, 35, 0, 0, time.UTC) ) func TestFakeTimeSource(t *testing.T) { fake := NewFake(date1) // Check that a FakeTimeSource can be used as a TimeSource. var ts TimeSource = fake if got, want := ts.Now(), date1; got != want { t.Errorf("ts.Now=%v; want %v", got, want) } fake.Set(date2) if got, want := ts.Now(), date2; got != want { t.Errorf("ts.Now=%v; want %v", got, want) } } func TestSecondsSince(t *testing.T) { delta := 8 * time.Second date3 := date2.Add(delta) var ts TimeSource = NewFake(date3) if got, want := SecondsSince(ts, date2), delta.Seconds(); got != want { t.Errorf("SecondsSince=%v; want %v", got, want) } } trillian-1.6.1/util/election/000077500000000000000000000000001466362047600161325ustar00rootroot00000000000000trillian-1.6.1/util/election/doc.go000066400000000000000000000013041466362047600172240ustar00rootroot00000000000000// Copyright 2022 Trillian Authors. 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 election provides a generic interface for election of a leader. package election trillian-1.6.1/util/election/runner.go000066400000000000000000000127511466362047600200000ustar00rootroot00000000000000// 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 election import ( "context" "fmt" "math/rand" "time" "github.com/google/trillian/util/clock" "github.com/google/trillian/util/election2" "k8s.io/klog/v2" ) // Minimum values for configuration intervals. // TODO(pavelkalinnikov): These parameters are specific to the application, so // shouldn't be here. const ( MinPreElectionPause = 10 * time.Millisecond MinMasterHoldInterval = 10 * time.Second ) // RunnerConfig describes the parameters for an election Runner. type RunnerConfig struct { // PreElectionPause is the maximum interval to wait before starting a // mastership election for a particular log. PreElectionPause time.Duration // MasterHoldInterval is the minimum interval to hold mastership for. MasterHoldInterval time.Duration // MasterHoldJitter is the maximum addition to MasterHoldInterval. MasterHoldJitter time.Duration TimeSource clock.TimeSource } // ResignDelay returns a randomized delay of how long to keep mastership for. func (cfg *RunnerConfig) ResignDelay() time.Duration { delay := cfg.MasterHoldInterval if cfg.MasterHoldJitter <= 0 { return delay } add := rand.Int63n(int64(cfg.MasterHoldJitter)) return delay + time.Duration(add) } // fixupRunnerConfig ensures operation parameters have required minimum values. func fixupRunnerConfig(cfg *RunnerConfig) { if cfg.PreElectionPause < MinPreElectionPause { cfg.PreElectionPause = MinPreElectionPause } if cfg.MasterHoldInterval < MinMasterHoldInterval { cfg.MasterHoldInterval = MinMasterHoldInterval } if cfg.MasterHoldJitter < 0 { cfg.MasterHoldJitter = 0 } if cfg.TimeSource == nil { cfg.TimeSource = clock.System } } // Runner controls a continuous election process. type Runner struct { // Allow the user to store a Cancel function with the runner for convenience. Cancel context.CancelFunc id string cfg *RunnerConfig tracker *MasterTracker election election2.Election } // NewRunner builds a new election Runner instance with the given config. On // calling Run(), the provided Election will be continuously monitored, and // mastership changes will be notified to the provided MasterTracker instance. func NewRunner(id string, cfg *RunnerConfig, tracker *MasterTracker, cancel context.CancelFunc, el election2.Election) *Runner { fixupRunnerConfig(cfg) return &Runner{ Cancel: cancel, id: id, cfg: cfg, tracker: tracker, election: el, } } // Run performs a continuous election process. It runs continuously until the // context is canceled or an internal error is encountered. func (er *Runner) Run(ctx context.Context, pending chan<- Resignation) { // Pause for a random interval so that if multiple instances start at the // same time there is less of a thundering herd. pause := rand.Int63n(er.cfg.PreElectionPause.Nanoseconds()) if err := clock.SleepSource(ctx, time.Duration(pause), er.cfg.TimeSource); err != nil { return // The context has been canceled during the sleep. } klog.V(1).Infof("%s: start election-monitoring loop ", er.id) defer func() { klog.Infof("%s: shutdown election-monitoring loop", er.id) if err := er.election.Close(ctx); err != nil { klog.Warningf("%s: election.Close: %v", er.id, err) } }() for { if err := er.beMaster(ctx, pending); err != nil { klog.Errorf("%s: %v", er.id, err) break } } } func (er *Runner) beMaster(ctx context.Context, pending chan<- Resignation) error { klog.V(1).Infof("%s: When I left you, I was but the learner", er.id) if err := er.election.Await(ctx); err != nil { return fmt.Errorf("election.Await() failed: %v", err) } klog.Infof("%s: Now, I am the master", er.id) er.tracker.Set(er.id, true) defer er.tracker.Set(er.id, false) mctx, err := er.election.WithMastership(ctx) if err != nil { return fmt.Errorf("election.WithMastership() failed: %v", err) } timer := er.cfg.TimeSource.NewTimer(er.cfg.ResignDelay()) defer timer.Stop() select { case <-mctx.Done(): // Mastership context is canceled. klog.Errorf("%s: no longer the master!", er.id) return mctx.Err() case <-timer.Chan(): klog.Infof("%s: queue up resignation of mastership", er.id) done := make(chan struct{}) r := Resignation{ID: er.id, er: er, done: done} select { case pending <- r: <-done // Block until acted on. default: klog.Warning("Dropping resignation because operation manager seems to be exiting") } } return nil } // Resignation indicates that a master should explicitly resign mastership, and // call the Execute() method as soon as no master-related activity is ongoing. type Resignation struct { ID string er *Runner done chan<- struct{} } // Execute performs the pending deliberate resignation for an election runner. func (r *Resignation) Execute(ctx context.Context) { defer close(r.done) klog.Infof("%s: deliberately resigning mastership", r.er.id) if err := r.er.election.Resign(ctx); err != nil { klog.Errorf("%s: failed to resign mastership: %v", r.er.id, err) } } trillian-1.6.1/util/election/runner_test.go000066400000000000000000000116241466362047600210350ustar00rootroot00000000000000// 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 election_test import ( "context" "errors" "fmt" "sync" "testing" "time" "github.com/google/trillian/util/clock" "github.com/google/trillian/util/election" to "github.com/google/trillian/util/election2/testonly" ) func TestConfigResignDelay(t *testing.T) { const checks = 1000 for _, tc := range []struct { hold time.Duration jitter time.Duration wantMax time.Duration }{ {hold: 12 * time.Second, jitter: -1 * time.Second, wantMax: 12 * time.Second}, {hold: 12 * time.Second, jitter: 0, wantMax: 12 * time.Second}, {hold: 12 * time.Second, jitter: 5 * time.Second, wantMax: 17 * time.Second}, {hold: 0, jitter: 0, wantMax: 0}, {hold: 0, jitter: 500 * time.Millisecond, wantMax: 500 * time.Millisecond}, {hold: 1 * time.Hour, jitter: 10 * time.Minute, wantMax: 70 * time.Minute}, } { t.Run(fmt.Sprintf("%v:%v", tc.hold, tc.jitter), func(t *testing.T) { cfg := election.RunnerConfig{ MasterHoldInterval: tc.hold, MasterHoldJitter: tc.jitter, } for i := 0; i < checks; i++ { d := cfg.ResignDelay() if min, max := tc.hold, tc.wantMax; d < min || d > max { t.Fatalf("ResignDelay(): %v, want between %v and %v", d, min, max) } } }) } } // TODO(pavelkalinnikov): Reduce flakiness risk in this test by making fewer // time assumptions. func TestElectionRunnerRun(t *testing.T) { const logID = "6962" checkMaster := func(t *testing.T, held []string, wantMaster bool) { t.Helper() if got := len(held) > 0 && held[0] == logID; got != wantMaster { t.Errorf("holding: %v: master=%v, want %v", held, got, wantMaster) } } for _, tc := range []struct { desc string errs to.Errs isMaster bool wantMaster bool loseMaster bool resign bool }{ // Basic cases. {desc: "not-master"}, {desc: "is-master", isMaster: true, wantMaster: true}, {desc: "lose-master", isMaster: true, wantMaster: true, loseMaster: true}, {desc: "resign", isMaster: true, wantMaster: true, resign: true}, // Error cases. {desc: "err-await", errs: to.Errs{Await: errors.New("ErrAwait")}}, {desc: "err-mctx", errs: to.Errs{WithMastership: errors.New("ErrMastership")}}, { desc: "err-mctx-after-master", isMaster: true, errs: to.Errs{WithMastership: errors.New("ErrMastership")}, }, {desc: "err-resign", errs: to.Errs{Resign: errors.New("ErrResign")}}, { desc: "err-resign-after-master", isMaster: true, wantMaster: true, errs: to.Errs{Resign: errors.New("ErrResign")}, }, } { t.Run(tc.desc, func(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() e := to.NewElection() d := to.NewDecorator(e) d.Update(tc.errs) d.BlockAwait(!tc.isMaster) start := time.Now() ts := clock.NewFake(start) tracker := election.NewMasterTracker([]string{logID}, nil) cfg := election.RunnerConfig{TimeSource: ts} er := election.NewRunner(logID, &cfg, tracker, nil, d) resignations := make(chan election.Resignation, 100) var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() er.Run(ctx, resignations) }() // TODO(pavelkalinnikov): This Sleep is necessary to let Run create a // Timer *before* we modify the time. To reduce risk of flakes, // FakeTimeSource should expose timer creation to this test. time.Sleep(100 * time.Millisecond) checkMaster(t, tracker.Held(), false) ts.Set(start.Add(election.MinPreElectionPause)) time.Sleep(100 * time.Millisecond) // Now it *can* become the master. checkMaster(t, tracker.Held(), tc.wantMaster) if tc.loseMaster { d.BlockAwait(true) if err := e.Resign(ctx); err != nil { t.Errorf("Lose mastership: %v", err) } wg.Wait() // Runner exits on unexpected mastership loss. checkMaster(t, tracker.Held(), false) } if tc.resign { d.BlockAwait(true) // Advance fake time so that resignation triggers too, if still master. ts.Set(start.Add(24 * 60 * time.Hour)) time.Sleep(100 * time.Millisecond) for len(resignations) > 0 { r := <-resignations r.Execute(ctx) } time.Sleep(100 * time.Millisecond) } checkMaster(t, tracker.Held(), tc.wantMaster && !tc.loseMaster && !tc.resign) cancel() // If Runner is still running, it should stop now. wg.Wait() // Wait until it stops. checkMaster(t, tracker.Held(), false) }) } } trillian-1.6.1/util/election/tracker.go000066400000000000000000000061761466362047600201260ustar00rootroot00000000000000// 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 election import ( "fmt" "sort" "strings" "sync" "k8s.io/klog/v2" ) // MasterTracker tracks the current mastership state across multiple IDs. type MasterTracker struct { mu sync.RWMutex masterFor map[string]bool masterCount int notify func(id string, isMaster bool) } // NewMasterTracker creates a new MasterTracker instance to track the // mastership status for the given set of IDs. func NewMasterTracker(ids []string, notify func(id string, isMaster bool)) *MasterTracker { mf := make(map[string]bool) for _, id := range ids { mf[id] = false } return &MasterTracker{masterFor: mf, notify: notify} } // Set changes the tracked mastership status for the given ID. This method // should be called exactly once for each state transition. func (mt *MasterTracker) Set(id string, isMaster bool) { mt.mu.Lock() defer mt.mu.Unlock() wasMaster, ok := mt.masterFor[id] if ok && isMaster == wasMaster { klog.Warningf("toggle masterFor[%s] from %v to %v!", id, wasMaster, isMaster) } mt.masterFor[id] = isMaster if isMaster && !wasMaster { mt.masterCount++ } else if !isMaster && wasMaster { mt.masterCount-- } if mt.notify != nil { mt.notify(id, isMaster) } } // Count returns the number of IDs for which we are currently master. func (mt *MasterTracker) Count() int { mt.mu.RLock() defer mt.mu.RUnlock() return mt.masterCount } // Held returns a (sorted) list of the IDs for which we are currently master. func (mt *MasterTracker) Held() []string { mt.mu.RLock() defer mt.mu.RUnlock() ids := make([]string, 0, mt.masterCount) for id := range mt.masterFor { if mt.masterFor[id] { ids = append(ids, id) } } sort.Strings(ids) return ids } // IDs returns a (sorted) list of the IDs that we are currently tracking. func (mt *MasterTracker) IDs() []string { mt.mu.RLock() defer mt.mu.RUnlock() ids := make([]string, 0, len(mt.masterFor)) for id := range mt.masterFor { ids = append(ids, id) } sort.Strings(ids) return ids } // String returns a textual decription of the current mastership status. func (mt *MasterTracker) String() string { return HeldInfo(mt.Held(), mt.IDs()) } // HeldInfo produces a textual description of the set of held IDs, compared to // a complete set of IDs. func HeldInfo(held []string, ids []string) string { result := "" prefix := "" for _, id := range ids { show := strings.Repeat(".", len(id)) for _, h := range held { if h == id { show = id } if h >= id { break } } result += fmt.Sprintf("%s%s", prefix, show) prefix = " " } return result } trillian-1.6.1/util/election/tracker_test.go000066400000000000000000000051111466362047600211510ustar00rootroot00000000000000// 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 election import ( "reflect" "testing" ) type testOperation struct { id string val bool } func TestMasterTracker(t *testing.T) { tests := []struct { ids []string ops []testOperation count int held []string str string }{ { ids: []string{"1", "2", "3"}, ops: []testOperation{{id: "1", val: true}}, count: 1, held: []string{"1"}, str: "1 . .", }, { ids: []string{"1", "20000", "30000"}, ops: []testOperation{{id: "30000", val: true}}, count: 1, held: []string{"30000"}, str: ". ..... 30000", }, { ids: []string{"1", "2", "3"}, ops: []testOperation{ {id: "1", val: true}, {id: "2", val: true}, {id: "3", val: true}, {id: "1", val: false}, }, count: 2, held: []string{"2", "3"}, str: ". 2 3", }, { ids: []string{}, ops: []testOperation{ {id: "1", val: true}, {id: "2", val: true}, {id: "3", val: true}, {id: "1", val: false}, }, count: 2, held: []string{"2", "3"}, str: ". 2 3", }, { ids: []string{"1", "2", "3"}, ops: []testOperation{ {id: "1", val: true}, {id: "1", val: true}, // error: already true }, count: 1, // count still accurate though held: []string{"1"}, str: "1 . .", }, { ids: []string{"1", "2", "3"}, ops: []testOperation{ {id: "1", val: false}, // error: already false }, count: 0, // count still accurate though held: []string{}, str: ". . .", }, } for _, test := range tests { mt := NewMasterTracker(test.ids, nil) for _, op := range test.ops { mt.Set(op.id, op.val) } if got := mt.Count(); got != test.count { t.Errorf("MasterTracker.Count(%+v)=%d; want %d", test.ops, got, test.count) } if got := mt.Held(); !reflect.DeepEqual(got, test.held) { t.Errorf("MasterTracker.Held(%+v)=%v; want %v", test.ops, got, test.held) } if got := mt.String(); got != test.str { t.Errorf("MasterTracker.String(%+v)=%q; want %q", test.ops, got, test.str) } } } trillian-1.6.1/util/election2/000077500000000000000000000000001466362047600162145ustar00rootroot00000000000000trillian-1.6.1/util/election2/election.go000066400000000000000000000102771466362047600203540ustar00rootroot00000000000000// Copyright 2018 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 election2 provides master election tools, and interfaces for // plugging in a custom underlying mechanism. // // There are two important abstractions in this package: instance and resource. // - An instance is a single client of the library. An instance is represented // by an Election object (possibly multiple). // - A resource is something guarded by master election (e.g. a piece of data, // or operation). Each resource has at most one (most of the time; see note // below) master instance which is said to own this resource. A single // instance may own multiple resources (one resource per Election object). // // Note: Sometimes there can be more than 1 instance "believing" to own a // resource. The reason is that the client code operates outside of the // election mechanism (e.g. a distributed consensus group), so mastership // updates can race with the operation. // // TODO(pavelkalinnikov): Merge this package with util/election. package election2 import "context" // Election controls an instance's participation in master election process. // Note: Implementations are not intended to be thread-safe. type Election interface { // Await blocks until the instance captures mastership. Returns immediately // if it is already the master. Returns an error if capturing fails, or the // passed in context is canceled before mastership is captured. If an error // is returned, the instance might still have become the master. Idempotent, // might be useful to retry in case of an error. Await(ctx context.Context) error // WithMastership returns a "mastership context" which remains active until // the instance stops being the master, or the passed in context is canceled. // If the instance is not the master during this call, returns an already // canceled context. In particular, this will happen if WithMastership is // called without a preceding Await. // // The resources used for maintaining the mastership context are released // when the latter gets canceled. This happens when the instance loses // mastership, calls Resign, an error occurs in mastership monitoring, or the // context passed in to WithMastership is explicitly canceled. // // If the passed in ctx is canceled, the instance does not resign mastership. // Use Resign or Close method for that. WithMastership(ctx context.Context) (context.Context, error) // Resign releases mastership for this instance. The instance can be elected // again using Await. Idempotent, might be useful to retry if fails. // // Note: Resign does not guarantee immediate cancelation of the context // returned from WithMastership. However, the latter will happen *eventually* if // resigning is successful. The caller can force mastership context // cancelation by explicitly canceling the context passed in to WithMastership. // // The caller is advised to tear down mastership-related work before invoking // Resign to have best protection against double-master situations. Resign(ctx context.Context) error // Close permanently stops participating in election, and releases the // resources. It does best effort on resigning despite potential cancelation // of the passed in context, so that other instances can overtake mastership // faster. No other method should be called after Close. // // Note: Does not guarantee immediate mastership context cancelation, see // Resign comment for details. Close(ctx context.Context) error } // Factory encapsulates the creation of an Election instance for a resource // with the specified ID. type Factory interface { NewElection(ctx context.Context, resourceID string) (Election, error) } trillian-1.6.1/util/election2/etcd/000077500000000000000000000000001466362047600171335ustar00rootroot00000000000000trillian-1.6.1/util/election2/etcd/election.go000066400000000000000000000126271466362047600212740ustar00rootroot00000000000000// 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 etcd provides an implementation of master election based on etcd. package etcd import ( "context" "fmt" "strings" "github.com/google/trillian/util/election2" clientv3 "go.etcd.io/etcd/client/v3" "go.etcd.io/etcd/client/v3/concurrency" "k8s.io/klog/v2" ) const resignID = "" // Election is an implementation of election2.Election based on etcd. type Election struct { resourceID string instanceID string lockFile string client *clientv3.Client session *concurrency.Session election *concurrency.Election } // Await blocks until the instance captures mastership. func (e *Election) Await(ctx context.Context) error { return e.election.Campaign(ctx, e.instanceID) } // WithMastership returns a "mastership context" which remains active until the // instance stops being the master, or the passed in context is canceled. func (e *Election) WithMastership(ctx context.Context) (context.Context, error) { // Get a channel for notifications of election status (using the cancelable // context so that the monitoring goroutine below and the goroutine started // by WithMastership will reliably terminate). cctx, cancel := context.WithCancel(ctx) etcdRev := e.election.Rev() // The revision at which e became the master. if etcdRev == 0 { // Not even tried to become the master. Return a canceled context. cancel() return cctx, nil } // Was the master once, so watch for latest mastership updates. ch := e.election.Observe(cctx) // Verify that we are still the master, before returning context. select { case <-ctx.Done(): cancel() return nil, ctx.Err() case rsp, ok := <-ch: kv := rsp.Kvs[0] if !ok || kv.CreateRevision != etcdRev || string(kv.Value) == resignID { // Mastership has been overtaken, released, or not capturead at all. cancel() return cctx, nil } } // At this point we have observed confirmation that we are the master; start // a goroutine to monitor for anyone else overtaking us. go func() { defer func() { cancel() klog.Infof("%s: canceled mastership context", e.resourceID) }() for rsp := range ch { kv := rsp.Kvs[0] if kv.CreateRevision != etcdRev { conquerorID := string(kv.Value) // TODO(pavelkalinnikov): conquerorID can be resignID too. Serialize a // protobuf with all mastership details instead of ID string. klog.Warningf("%s: mastership overtaken by %s", e.resourceID, conquerorID) break } else if string(kv.Value) == resignID { klog.Infof("%s: canceling context due to resignation", e.resourceID) break } } }() return cctx, nil } // Resign releases mastership for this instance. The instance can be elected // again using Await. Idempotent, might be useful to retry if fails. func (e *Election) Resign(ctx context.Context) error { // Trigger Observe callers to see the update, and cancel mastership contexts. err := e.election.Proclaim(ctx, resignID) if err == concurrency.ErrElectionNotLeader { return nil // Resigning if not master is a no-op. } else if err != nil { return err } return e.election.Resign(ctx) } // Close resigns and permanently stops participating in election. No other // method should be called after Close. func (e *Election) Close(ctx context.Context) error { if err := e.Resign(ctx); err != nil && err != concurrency.ErrElectionNotLeader { klog.Errorf("%s: Resign(): %v", e.resourceID, err) } // Session's Close revokes the underlying lease, which results in removing // the election-related keys. This achieves the effect of resignation even if // the above Resign call failed (e.g. due to ctx cancelation). return e.session.Close() } // Factory creates Election instances. type Factory struct { client *clientv3.Client instanceID string lockDir string } // NewFactory builds an election factory that uses the given parameters. The // passed in etcd client should remain valid for the lifetime of the object. func NewFactory(instanceID string, client *clientv3.Client, lockDir string) *Factory { return &Factory{ client: client, instanceID: instanceID, lockDir: lockDir, } } // NewElection creates a specific Election instance. func (f *Factory) NewElection(ctx context.Context, resourceID string) (election2.Election, error) { // TODO(pavelkalinnikov): Re-create the session if it expires. // TODO(pavelkalinnikov): Share the same session between Election instances. session, err := concurrency.NewSession(f.client) if err != nil { return nil, fmt.Errorf("failed to create etcd session: %v", err) } lockFile := fmt.Sprintf("%s/%s", strings.TrimRight(f.lockDir, "/"), resourceID) election := concurrency.NewElection(session, lockFile) el := Election{ resourceID: resourceID, instanceID: f.instanceID, lockFile: lockFile, client: f.client, session: session, election: election, } klog.Infof("Election created: %+v", el) return &el, nil } trillian-1.6.1/util/election2/etcd/election_test.go000066400000000000000000000036061466362047600223300ustar00rootroot00000000000000// Copyright 2018 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 etcd import ( "context" "fmt" "testing" "github.com/google/trillian/testonly/integration/etcd" "github.com/google/trillian/util/election2/testonly" ) func TestElectionThroughCommonClient(t *testing.T) { _, client, cleanup, err := etcd.StartEtcd() if err != nil { t.Fatalf("StartEtcd(): %v", err) } defer cleanup() ctx := context.Background() fact := NewFactory("serv", client, "res/") el1, err := fact.NewElection(ctx, "10") if err != nil { t.Fatalf("NewElection(10): %v", err) } el2, err := fact.NewElection(ctx, "20") if err != nil { t.Fatalf("NewElection(20): %v", err) } if err := el1.Await(ctx); err != nil { t.Fatalf("Await(10): %v", err) } if err := el2.Await(ctx); err != nil { t.Fatalf("Await(20): %v", err) } if err := el1.Close(ctx); err != nil { t.Fatalf("Close(10): %v", err) } if err := el2.Close(ctx); err != nil { t.Fatalf("Close(20): %v", err) } } func TestElection(t *testing.T) { _, client, cleanup, err := etcd.StartEtcd() if err != nil { t.Fatalf("StartEtcd(): %v", err) } defer cleanup() for _, nt := range testonly.Tests { // Create a new Factory for each test for better isolation. fact := NewFactory("testID", client, fmt.Sprintf("%s/resources/", nt.Name)) t.Run(nt.Name, func(t *testing.T) { nt.Run(t, fact) }) } } trillian-1.6.1/util/election2/noop.go000066400000000000000000000031121466362047600175130ustar00rootroot00000000000000// Copyright 2018 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 election2 import "context" // NoopElection is a stub Election that always believes to be the master. type NoopElection string // Await returns immediately, as the instance is always the master. func (ne NoopElection) Await(ctx context.Context) error { return nil } // WithMastership returns the passed in context as a mastership context. func (ne NoopElection) WithMastership(ctx context.Context) (context.Context, error) { return ctx, nil } // Resign does nothing because NoopElection is always the master. func (ne NoopElection) Resign(ctx context.Context) error { return nil } // Close does nothing because NoopElection is always the master. func (ne NoopElection) Close(ctx context.Context) error { return nil } // NoopFactory creates NoopElection instances. type NoopFactory struct{} // NewElection creates a specific NoopElection instance. func (nf NoopFactory) NewElection(ctx context.Context, resourceID string) (Election, error) { return NoopElection(resourceID), nil } trillian-1.6.1/util/election2/testonly/000077500000000000000000000000001466362047600200755ustar00rootroot00000000000000trillian-1.6.1/util/election2/testonly/decorator.go000066400000000000000000000052121466362047600224060ustar00rootroot00000000000000// Copyright 2018 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 ( "context" "sync" "github.com/google/trillian/util/election2" ) // Errs contains errors to be returned by each of Election methods. type Errs struct { Await error WithMastership error Resign error Close error } // Decorator is an election2.Election decorator injecting errors, for testing. type Decorator struct { e election2.Election errs Errs block bool mu sync.Mutex cond *sync.Cond } // NewDecorator returns a Decorator wrapping the passed in Election object. func NewDecorator(e election2.Election) *Decorator { d := &Decorator{e: e} d.cond = sync.NewCond(&d.mu) return d } // Update updates errors returned by interface methods. func (d *Decorator) Update(errs Errs) { d.mu.Lock() defer d.mu.Unlock() d.errs = errs } // BlockAwait enables or disables Await method blocking. func (d *Decorator) BlockAwait(block bool) { d.mu.Lock() defer d.mu.Unlock() d.block = block d.cond.Broadcast() } // Await blocks until the instance captures mastership. func (d *Decorator) Await(ctx context.Context) error { d.mu.Lock() defer d.mu.Unlock() if err := d.errs.Await; err != nil { return err } _, cancel := watchContext(ctx, &d.mu, d.cond) defer cancel() for d.block && ctx.Err() == nil { d.cond.Wait() } if err := ctx.Err(); err != nil { return err } return d.e.Await(ctx) } // WithMastership returns a mastership context. func (d *Decorator) WithMastership(ctx context.Context) (context.Context, error) { d.mu.Lock() defer d.mu.Unlock() if err := d.errs.WithMastership; err != nil { return nil, err } return d.e.WithMastership(ctx) } // Resign releases mastership for this instance. func (d *Decorator) Resign(ctx context.Context) error { d.mu.Lock() defer d.mu.Unlock() if err := d.errs.Resign; err != nil { return err } return d.e.Resign(ctx) } // Close permanently stops participating in election. func (d *Decorator) Close(ctx context.Context) error { d.mu.Lock() defer d.mu.Unlock() if err := d.errs.Close; err != nil { return err } return d.e.Close(ctx) } trillian-1.6.1/util/election2/testonly/election.go000066400000000000000000000062201466362047600222260ustar00rootroot00000000000000// Copyright 2018 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 an Election implementation for testing. package testonly import ( "context" "sync" "github.com/google/trillian/util/election2" ) // Factory allows creating Election instances for testing. var Factory factory // Election implements election2.Election interface for testing. type Election struct { isMaster bool revision int mu sync.Mutex cond *sync.Cond } // NewElection returns a new initialized Election for testing. func NewElection() *Election { e := &Election{} e.cond = sync.NewCond(&e.mu) return e } // update updates this instance's mastership status. Must be called under lock. func (e *Election) update(isMaster bool) { e.isMaster = isMaster e.revision++ e.cond.Broadcast() } // Await sets this instance to be the master. It always succeeds. To imitate // errors and/or blocking behavior use the Decorator type. func (e *Election) Await(ctx context.Context) error { e.mu.Lock() defer e.mu.Unlock() if !e.isMaster { e.update(true) } return nil } // WithMastership returns mastership context, which gets canceled if / when // this instance is not / stops being the master. func (e *Election) WithMastership(ctx context.Context) (context.Context, error) { e.mu.Lock() defer e.mu.Unlock() if !e.isMaster { cctx, cancel := context.WithCancel(ctx) cancel() return cctx, nil } cctx, cancel := watchContext(ctx, &e.mu, e.cond) // Notify e.cond on ctx cancelation. rev := e.revision go func() { // Watch mastership and the context in the background. defer cancel() e.mu.Lock() defer e.mu.Unlock() for e.isMaster && e.revision == rev && ctx.Err() == nil { e.cond.Wait() } }() return cctx, nil } // Resign resets mastership. func (e *Election) Resign(ctx context.Context) error { e.mu.Lock() defer e.mu.Unlock() e.update(false) return nil } // Close resets mastership permanently. func (e *Election) Close(ctx context.Context) error { return e.Resign(ctx) } func watchContext(ctx context.Context, l sync.Locker, cond *sync.Cond) (context.Context, context.CancelFunc) { cctx, cancel := context.WithCancel(ctx) go func() { defer cancel() <-cctx.Done() l.Lock() // Avoid racing with cond waiters on ctx status. defer l.Unlock() cond.Broadcast() }() return cctx, cancel } // factory allows creating Election instances. type factory struct{} // NewElection creates a new Election instance. // TODO(pavelkalinnikov): Use resourceID in tests with multiple resources. func (f factory) NewElection(ctx context.Context, resourceID string) (election2.Election, error) { return NewElection(), nil } trillian-1.6.1/util/election2/testonly/election_test.go000066400000000000000000000015431466362047600232700ustar00rootroot00000000000000// Copyright 2018 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_test import ( "testing" "github.com/google/trillian/util/election2/testonly" ) func TestElection(t *testing.T) { for _, test := range testonly.Tests { t.Run(test.Name, func(t *testing.T) { test.Run(t, testonly.Factory) }) } } trillian-1.6.1/util/election2/testonly/tests.go000066400000000000000000000172271466362047600215770ustar00rootroot00000000000000// Copyright 2018 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 ( "context" "errors" "testing" "time" "github.com/google/trillian/util/clock" "github.com/google/trillian/util/election2" ) // Tests is the full list of available Election tests. // TODO(pavelkalinnikov): Add tests for unexpected mastership loss. var Tests = []NamedTest{ {Name: "RunElectionAwait", Run: runElectionAwait}, {Name: "RunElectionWithMastership", Run: runElectionWithMastership}, {Name: "RunElectionResign", Run: runElectionResign}, {Name: "RunElectionClose", Run: runElectionClose}, {Name: "RunElectionLoop", Run: runElectionLoop}, } // NamedTest is a test function paired with its string name. type NamedTest struct { Name string Run func(t *testing.T, f election2.Factory) } // checkNotDone ensures that the context is not done for some time. func checkNotDone(ctx context.Context, t *testing.T) { t.Helper() if err := clock.SleepContext(ctx, 100*time.Millisecond); err != nil { t.Error("unexpected context cancelation") } } // checkDone ensures that the context is done within the specified duration. func checkDone(ctx context.Context, t *testing.T, wait time.Duration) { t.Helper() if wait == 0 { select { case <-ctx.Done(): // Ok. default: t.Error("expected context done") } } else if err := clock.SleepContext(ctx, wait); err == nil { t.Errorf("expected context done within %v", wait) } } // runElectionAwait tests the Await call with different pre-conditions. func runElectionAwait(t *testing.T, f election2.Factory) { awaitErr := errors.New("await error") for _, tc := range []struct { desc string block bool cancel bool err error wantErr error }{ {desc: "ok", wantErr: nil}, {desc: "cancel", block: true, cancel: true, wantErr: context.Canceled}, {desc: "deadline", block: true, cancel: false, wantErr: context.DeadlineExceeded}, {desc: "error", err: awaitErr, wantErr: awaitErr}, } { t.Run(tc.desc, func(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() e, err := f.NewElection(ctx, tc.desc) if err != nil { t.Fatalf("NewElection(): %v", err) } d := NewDecorator(e) d.Update(Errs{Await: tc.err}) d.BlockAwait(tc.block) if tc.cancel { cancel() } else if tc.block { ctx, cancel = context.WithTimeout(ctx, 100*time.Millisecond) defer cancel() } if got, want := d.Await(ctx), tc.wantErr; got != want { t.Errorf("Await(): %v, want %v", got, want) } }) } } // runElectionWithMastership tests the WithMastership call. func runElectionWithMastership(t *testing.T, f election2.Factory) { ctxErr := errors.New("WithMastership error") for _, tc := range []struct { desc string beMaster bool cancel bool err error wantErr error }{ {desc: "master", beMaster: true}, {desc: "master-cancel", beMaster: true, cancel: true}, {desc: "not-master", beMaster: false}, {desc: "not-master-cancel", cancel: true}, {desc: "error", err: ctxErr, wantErr: ctxErr}, } { t.Run(tc.desc, func(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() e, err := f.NewElection(ctx, tc.desc) if err != nil { t.Fatalf("NewElection(): %v", err) } d := NewDecorator(e) d.Update(Errs{WithMastership: tc.err}) if tc.beMaster { if err := d.Await(ctx); err != nil { t.Fatalf("Await(): %v", err) } } mctx, err := d.WithMastership(ctx) if want := tc.wantErr; err != want { t.Fatalf("WithMastership(): %v, want %v", err, want) } if err != nil { return } if tc.cancel { cancel() } if tc.beMaster && !tc.cancel { checkNotDone(mctx, t) } else { checkDone(mctx, t, 0) } }) } } // runElectionResign tests the Resign call. func runElectionResign(t *testing.T, f election2.Factory) { resignErr := errors.New("resign error") for _, tc := range []struct { desc string beMaster bool err error wantErr error }{ {desc: "master", beMaster: true, wantErr: nil}, {desc: "master-error", beMaster: true, err: resignErr, wantErr: resignErr}, {desc: "not-master", beMaster: false, wantErr: nil}, {desc: "not-master-error", err: resignErr, wantErr: resignErr}, } { t.Run(tc.desc, func(t *testing.T) { ctx := context.Background() e, err := f.NewElection(ctx, tc.desc) if err != nil { t.Fatalf("NewElection(): %v", err) } d := NewDecorator(e) d.Update(Errs{Resign: tc.err}) if tc.beMaster { if err := d.Await(ctx); err != nil { t.Fatalf("Await(): %v", err) } } mctx, err := d.WithMastership(ctx) if err != nil { t.Fatalf("WithMastership(): %v", err) } if got, want := d.Resign(ctx), tc.wantErr; got != want { t.Errorf("Resign(): %v, want %v", got, want) } if tc.beMaster && tc.wantErr == nil { checkDone(mctx, t, 1*time.Second) } else if tc.beMaster { checkNotDone(mctx, t) } else { checkDone(mctx, t, 0) } }) } } // runElectionClose tests the Close call. func runElectionClose(t *testing.T, f election2.Factory) { closeErr := errors.New("close error") for _, tc := range []struct { desc string beMaster bool cancel bool err error wantErr error }{ {desc: "master", beMaster: true, wantErr: nil}, {desc: "master-error", beMaster: true, err: closeErr, wantErr: closeErr}, {desc: "not-master", beMaster: false, wantErr: nil}, {desc: "not-master-error", err: closeErr, wantErr: closeErr}, // TODO(pavelkalinnikov): reinstate when not flaky. // {desc: "cancel", cancel: true, wantErr: nil}, } { t.Run(tc.desc, func(t *testing.T) { ctx := context.Background() e, err := f.NewElection(ctx, tc.desc) if err != nil { t.Fatalf("NewElection(): %v", err) } d := NewDecorator(e) d.Update(Errs{Close: tc.err}) if tc.beMaster { if err := d.Await(ctx); err != nil { t.Fatalf("Await(): %v", err) } } mctx, err := d.WithMastership(ctx) if err != nil { t.Fatalf("WithMastership(): %v", err) } cctx, cancel := context.WithCancel(ctx) defer cancel() if tc.cancel { cancel() } if got, want := d.Close(cctx), tc.wantErr; got != want { t.Errorf("Close(): %v, want %v", got, want) } if tc.beMaster && tc.wantErr == nil { checkDone(mctx, t, 1*time.Second) } else if tc.beMaster { checkNotDone(mctx, t) } else { checkDone(mctx, t, 0) } }) } } // runElectionLoop runs a typical mastership loop. func runElectionLoop(t *testing.T, f election2.Factory) { ctx := context.Background() e, err := f.NewElection(ctx, "testID") if err != nil { t.Fatalf("NewElection(): %v", err) } d := NewDecorator(e) defer func() { if err := d.Close(ctx); err != nil { t.Errorf("Close(): %v", err) } }() for i := 0; i < 10; i++ { t.Logf("Mastership iteration: %d", i) if err := d.Await(ctx); err != nil { t.Fatalf("Await(): %v", err) } mctx, err := d.WithMastership(ctx) if err != nil { t.Fatalf("WithMastership(): %v", err) } checkNotDone(mctx, t) // Do some work as master. if err := d.Resign(ctx); err != nil { t.Errorf("Resign(): %v", err) } checkDone(mctx, t, 1*time.Second) // The mastership context should close. } } trillian-1.6.1/util/process.go000066400000000000000000000025361466362047600163430ustar00rootroot00000000000000// 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 util holds utility functions. package util import ( "context" "os" "os/signal" "syscall" "k8s.io/klog/v2" ) // AwaitSignal waits for standard termination signals, then runs the given // function. Can early return if the passed in context is canceled, in which // case the function is not run. func AwaitSignal(ctx context.Context, doneFn func()) { // Subscribe for the standard set of signals used to terminate a server. sigs := make(chan os.Signal, 1) signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) defer signal.Stop(sigs) // Wait for a signal or context cancellation. select { case sig := <-sigs: klog.Warningf("Signal received: %v", sig) doneFn() case <-ctx.Done(): klog.Infof("AwaitSignal canceled: %v", ctx.Err()) } }