diff --git a/CHANGELOG.md b/CHANGELOG.md index 399b205d..2a09cae6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,8 @@ the release policy in `docs/release-policy.md`. - Added metadata golden-fixture safety checks for JSON/data-URI decoding, current URI scheme policy, and generated animation HTML script-boundary validation in local and CI gates. +- Added renderer URI policy helpers and production token image URI validation + for required metadata image inputs. ### Release Impact @@ -44,6 +46,8 @@ the release policy in `docs/release-policy.md`. metadata size-limit custom errors and public limit constants. - Gate D now runs metadata fixture safety checks in `make check`, CI, and the platform check wrappers. +- Gate D/G release artifacts now include the ABI and bytecode deltas from the + metadata URI policy helper functions and `UnsafeMetadataURI()` custom error. - Detached checksum signatures, signed release tags, production address books, and verified live deployment addresses remain future release-ceremony work. diff --git a/deployments/address-books/anvil-6529stream-v0.1.0-001-broadcast.json b/deployments/address-books/anvil-6529stream-v0.1.0-001-broadcast.json index d01b550a..5403cf86 100644 --- a/deployments/address-books/anvil-6529stream-v0.1.0-001-broadcast.json +++ b/deployments/address-books/anvil-6529stream-v0.1.0-001-broadcast.json @@ -3,7 +3,7 @@ "generated_by": "scripts/generate_address_books.py:1", "source": { "deployment_manifest": "deployments/examples/anvil-6529stream-v0.1.0-001-broadcast.json", - "deployment_manifest_sha256": "sha256:79a0530da77ce6653198a15ccccf2a2deb023765f7a6d14b5a3f28300e19785e", + "deployment_manifest_sha256": "sha256:38bdd644fe53b71a2b3b9afb6cc4de258f28e43b48428cda4646075ea5e34ed1", "release_artifacts": "release-artifacts/latest", "abi_checksums": "release-artifacts/latest/abi-checksums.json", "event_topic_catalog": "release-artifacts/latest/event-topic-catalog.json" @@ -65,8 +65,8 @@ "address": "0x1000000000000000000000000000000000000003", "source": "smart-contracts/StreamCore.sol", "artifact_path": "out/StreamCore.sol/StreamCore.json", - "abi_hash": "sha256:820f6962c8e27c7e73ac51e18fdfddd81c517f58101a3b28335fcdbba5f7d4b3", - "runtime_bytecode_hash": "sha256:db8cc579c656c43f09acc0fb6ee3d74ad2bc1b0877fc71f0c0737116556e7977", + "abi_hash": "sha256:3738c2bf120feb5bb300a05cb19553829c65b76b2526a70e880e181027d49627", + "runtime_bytecode_hash": "sha256:5a16fdc2550ebfd91c705e6fe790836c86d1b5600f07d46f21db5603fccaa156", "verification_status": "not_applicable" }, "StreamCuratorsPool": { diff --git a/deployments/address-books/anvil-6529stream-v0.1.0-001.json b/deployments/address-books/anvil-6529stream-v0.1.0-001.json index 68d6024d..a699b63d 100644 --- a/deployments/address-books/anvil-6529stream-v0.1.0-001.json +++ b/deployments/address-books/anvil-6529stream-v0.1.0-001.json @@ -3,7 +3,7 @@ "generated_by": "scripts/generate_address_books.py:1", "source": { "deployment_manifest": "deployments/examples/anvil-6529stream-v0.1.0-001.json", - "deployment_manifest_sha256": "sha256:926ef2be2b0c7b68f860cb678d545bb35fb3e4507867085d5aff56ec8f70f721", + "deployment_manifest_sha256": "sha256:c1b7b64b7fc2f528260788e206dd3270341be9810230c9914ae350084667fa82", "release_artifacts": "release-artifacts/latest", "abi_checksums": "release-artifacts/latest/abi-checksums.json", "event_topic_catalog": "release-artifacts/latest/event-topic-catalog.json" @@ -65,8 +65,8 @@ "address": "0x0000000000000000000000000000000000000003", "source": "smart-contracts/StreamCore.sol", "artifact_path": "out/StreamCore.sol/StreamCore.json", - "abi_hash": "sha256:820f6962c8e27c7e73ac51e18fdfddd81c517f58101a3b28335fcdbba5f7d4b3", - "runtime_bytecode_hash": "sha256:db8cc579c656c43f09acc0fb6ee3d74ad2bc1b0877fc71f0c0737116556e7977", + "abi_hash": "sha256:3738c2bf120feb5bb300a05cb19553829c65b76b2526a70e880e181027d49627", + "runtime_bytecode_hash": "sha256:5a16fdc2550ebfd91c705e6fe790836c86d1b5600f07d46f21db5603fccaa156", "verification_status": "not_applicable" }, "StreamCuratorsPool": { diff --git a/deployments/examples/anvil-6529stream-v0.1.0-001-broadcast.json b/deployments/examples/anvil-6529stream-v0.1.0-001-broadcast.json index c2723413..7ce316a0 100644 --- a/deployments/examples/anvil-6529stream-v0.1.0-001-broadcast.json +++ b/deployments/examples/anvil-6529stream-v0.1.0-001-broadcast.json @@ -49,8 +49,8 @@ "0x0000000000000000000000000000000000000001", "0x0000000000000000000000000000000000000002" ], - "abi_hash": "sha256:820f6962c8e27c7e73ac51e18fdfddd81c517f58101a3b28335fcdbba5f7d4b3", - "bytecode_hash": "sha256:db8cc579c656c43f09acc0fb6ee3d74ad2bc1b0877fc71f0c0737116556e7977", + "abi_hash": "sha256:3738c2bf120feb5bb300a05cb19553829c65b76b2526a70e880e181027d49627", + "bytecode_hash": "sha256:5a16fdc2550ebfd91c705e6fe790836c86d1b5600f07d46f21db5603fccaa156", "verification_status": "not_applicable" }, "StreamCuratorsPool": { @@ -166,11 +166,11 @@ ] }, "release_artifacts": { - "manifest_sha256": "sha256:79a0530da77ce6653198a15ccccf2a2deb023765f7a6d14b5a3f28300e19785e", + "manifest_sha256": "sha256:38bdd644fe53b71a2b3b9afb6cc4de258f28e43b48428cda4646075ea5e34ed1", "abi_hashes": { "StreamAdmins": "sha256:0dec302776ef6fe8038a96a716c7300d697c52c9b8d79559ab86c2b5c6599dd5", "DependencyRegistry": "sha256:8600876198d591fdae934b20c5d370490939504890944dbd8249d85ef17a6795", - "StreamCore": "sha256:820f6962c8e27c7e73ac51e18fdfddd81c517f58101a3b28335fcdbba5f7d4b3", + "StreamCore": "sha256:3738c2bf120feb5bb300a05cb19553829c65b76b2526a70e880e181027d49627", "StreamCuratorsPool": "sha256:f5d65fb2b17cf0caae7c8c75c6736c5828c869502f55634279556ba8483e6daf", "StreamMinter": "sha256:5464ee3c1b5c40867aba4565d11fe6b708ea2b2e93aba4f7e3a52ebbe4296c3e", "StreamDrops": "sha256:2314f89858673138e8b9c2e421dd7a727c7e7746d5eb069e58a61699f2f4de6a", diff --git a/deployments/examples/anvil-6529stream-v0.1.0-001.json b/deployments/examples/anvil-6529stream-v0.1.0-001.json index e5c6f1b3..99524a11 100644 --- a/deployments/examples/anvil-6529stream-v0.1.0-001.json +++ b/deployments/examples/anvil-6529stream-v0.1.0-001.json @@ -49,8 +49,8 @@ "0x0000000000000000000000000000000000000001", "0x0000000000000000000000000000000000000002" ], - "abi_hash": "sha256:820f6962c8e27c7e73ac51e18fdfddd81c517f58101a3b28335fcdbba5f7d4b3", - "bytecode_hash": "sha256:db8cc579c656c43f09acc0fb6ee3d74ad2bc1b0877fc71f0c0737116556e7977", + "abi_hash": "sha256:3738c2bf120feb5bb300a05cb19553829c65b76b2526a70e880e181027d49627", + "bytecode_hash": "sha256:5a16fdc2550ebfd91c705e6fe790836c86d1b5600f07d46f21db5603fccaa156", "verification_status": "not_applicable" }, "StreamCuratorsPool": { @@ -166,11 +166,11 @@ ] }, "release_artifacts": { - "manifest_sha256": "sha256:926ef2be2b0c7b68f860cb678d545bb35fb3e4507867085d5aff56ec8f70f721", + "manifest_sha256": "sha256:c1b7b64b7fc2f528260788e206dd3270341be9810230c9914ae350084667fa82", "abi_hashes": { "StreamAdmins": "sha256:0dec302776ef6fe8038a96a716c7300d697c52c9b8d79559ab86c2b5c6599dd5", "DependencyRegistry": "sha256:8600876198d591fdae934b20c5d370490939504890944dbd8249d85ef17a6795", - "StreamCore": "sha256:820f6962c8e27c7e73ac51e18fdfddd81c517f58101a3b28335fcdbba5f7d4b3", + "StreamCore": "sha256:3738c2bf120feb5bb300a05cb19553829c65b76b2526a70e880e181027d49627", "StreamCuratorsPool": "sha256:f5d65fb2b17cf0caae7c8c75c6736c5828c869502f55634279556ba8483e6daf", "StreamMinter": "sha256:5464ee3c1b5c40867aba4565d11fe6b708ea2b2e93aba4f7e3a52ebbe4296c3e", "StreamDrops": "sha256:2314f89858673138e8b9c2e421dd7a727c7e7746d5eb069e58a61699f2f4de6a", diff --git a/docs/adr/0006-metadata-freeze.md b/docs/adr/0006-metadata-freeze.md index fdeae119..aa5fd6b4 100644 --- a/docs/adr/0006-metadata-freeze.md +++ b/docs/adr/0006-metadata-freeze.md @@ -231,10 +231,14 @@ strings. The second slice hardens generated animation wrapper boundaries by escaping the external library attribute, embedding `tokenData` and dependency scripts through escaped JavaScript strings, and neutralizing closing-script sequences. The third slice defines numeric byte limits for stored metadata -inputs and generated `tokenURI` output. The remaining public-beta work is still -to define semantic attribute schema validation or structured attributes, -browser render-sandbox proofing for generated animation code, URI policy checks, -and invalid UTF-8 policy. +inputs and generated `tokenURI` output. The fourth slice validates committed +metadata fixtures outside Foundry for JSON/data-URI/HTML boundaries and the +current URI scheme policy. The fifth slice exposes renderer URI policy helpers +and enforces the required content URI policy for token image writes. The +remaining public-beta work is still to define semantic attribute schema +validation or structured attributes, browser render-sandbox proofing for +generated animation code, collection base URI and external library URL +production URI checks, and invalid UTF-8 policy. The implementation must define maximum sizes for: diff --git a/docs/known-blockers.md b/docs/known-blockers.md index fc266f52..22cd111e 100644 --- a/docs/known-blockers.md +++ b/docs/known-blockers.md @@ -81,18 +81,21 @@ contributors who start from the README. `tokenData` from an escaped string, and neutralizes closing-script sequences in generated HTML. Numeric byte limits now cover collection display fields, collection scripts, token data, token images, token attributes, generated - `tokenURI` output, dependency scripts, and dependency provenance. Remaining - metadata blockers include dependency artifact packaging and deployment + `tokenURI` output, dependency scripts, and dependency provenance. Token image + writes now reject unsafe content URI inputs, and renderer helpers define the + current content/script URI scheme policy for tests and fixture checks. + Remaining metadata blockers include dependency artifact packaging and deployment migration runbooks beyond registry provenance strings, full browser execution - sandbox automation, semantic attribute schema validation, production URI - enforcement, and invalid UTF-8 policy. Committed metadata fixtures now have + sandbox automation, semantic attribute schema validation, collection base URI + and external library URL production enforcement, and invalid UTF-8 policy. + Committed metadata fixtures now have Python checks for JSON/data-URI decoding, current URI scheme policy, and final animation HTML wrapper/script boundaries. - `StreamCore` now uses a linked metadata renderer library, removes optional ERC-721 Enumerable support, preserves a live `totalSupply()` view, and has a production-only size gate: `forge build --sizes --via-ir --skip test --skip script --force`. That gate currently shows - `StreamCore` under EIP-170 with deployment headroom, but deployment scripts, + `StreamCore` under EIP-170 with narrow deployment headroom, but deployment scripts, manifests, and rehearsals still need to use this production profile. - Dead public/allowlist mint-count mappings and retrieval APIs were removed from `StreamCore`; the retained airdrop counter now has explicit regression diff --git a/docs/metadata.md b/docs/metadata.md index 06618050..96b6a02f 100644 --- a/docs/metadata.md +++ b/docs/metadata.md @@ -50,9 +50,10 @@ base64-encoded HTML animation URL. fields emitted by on-chain metadata and rejects raw attribute fragments that contain literal control characters, unterminated strings, unbalanced object/array delimiters, or unquoted `]`/`}` breakout attempts. It does not yet -solve semantic attribute schema validation, URI policy, invalid UTF-8 policy, -browser render-sandbox checks, dependency artifact packaging beyond registry -provenance strings, stale randomness display, or deployment release manifests. +solve semantic attribute schema validation, collection base URI or external +library URL production enforcement, invalid UTF-8 policy, browser +render-sandbox checks, dependency artifact packaging beyond registry provenance +strings, stale randomness display, or deployment release manifests. ## Escaping And Attribute Fragments @@ -83,9 +84,30 @@ script. This protects wrapper structure, but it does not sandbox artist `collectionScript` code or certify dependency code as safe. Release tooling now validates the committed golden fixtures for JSON/data-URI structure, current URI scheme policy, and generated HTML wrapper/script boundaries. -Full browser execution sandboxing, production URI enforcement, semantic -attribute validation, and invalid UTF-8 policy remain required before public -beta. +Full browser execution sandboxing, collection base URI and external library URL +production enforcement, semantic attribute validation, and invalid UTF-8 policy +remain required before public beta. + +## URI Policy + +`StreamMetadataRenderer` exposes the current URI policy used by metadata tests +and fixture checks: + +- Content URIs allow `https://`, `ipfs://`, and `ar://` values. +- Required content URIs must be nonempty. +- Optional content URIs may be empty when the caller explicitly allows it. +- Script URIs allow only nonempty `https://` values. +- `https://` URIs must include a host byte after the scheme. +- URI values containing ASCII whitespace, other ASCII control characters, or + DEL are rejected. + +`StreamCore.updateImagesAndAttributes` now enforces the required content URI +policy for token image inputs before storing them, reverting with +`UnsafeMetadataURI()` on failure. Collection base URI and external library URL +production enforcement remain open because `StreamCore` is close to the +EIP-170 bytecode limit; those surfaces are still covered only by size limits, +fixture checks, docs, and release review until a later implementation slice +adds deployable enforcement. ## Size Limits diff --git a/docs/status.md b/docs/status.md index 3c627400..8bc32e76 100644 --- a/docs/status.md +++ b/docs/status.md @@ -7,8 +7,8 @@ The current Gate A smoke baseline proves: - Foundry is configured to compile `smart-contracts`. - `forge build` runs against Solidity `0.8.19`. - `forge build --sizes --via-ir --skip test --skip script --force` runs as the production - size gate. Current `StreamCore` production runtime size is 24,461 bytes, - leaving 115 bytes of EIP-170 headroom under the IR-optimized deployment + size gate. Current `StreamCore` production runtime size is 24,508 bytes, + leaving 68 bytes of EIP-170 headroom under the IR-optimized deployment profile. - `forge test -vvv` executes real tests for admin guards, target-scoped function-admin permission regressions, domain-scoped pause controls, @@ -72,6 +72,10 @@ The current Gate A smoke baseline proves: for collection display fields, collection script chunks and counts, token data, token images, token raw attributes, generated `tokenURI` output, dependency script chunks and counts, and dependency provenance strings. + `StreamMetadataUriPolicy.t.sol` proves the renderer content/script URI + policy helpers and the production token image URI guard for allowed + `https://`, `ipfs://`, and `ar://` content URIs plus rejected empty, + JavaScript, hostless HTTPS, and whitespace-bearing token image inputs. `scripts/test_metadata_fixtures.py` and `scripts/check_metadata_fixtures.py` validate the committed metadata golden fixtures outside Foundry by strictly decoding JSON and HTML data URIs, @@ -144,8 +148,9 @@ release tags, production address books, verified live deployment hashes and explorer submissions, remaining generated HTML/JavaScript render-sandbox hardening, dependency artifact packaging and migration runbooks beyond registry provenance strings, -semantic attribute schema validation, production URI enforcement, invalid UTF-8 -policy, full browser execution sandbox automation, deployment discipline, and +semantic attribute schema validation, collection base URI and external library +URL production enforcement, invalid UTF-8 policy, full browser execution +sandbox automation, deployment discipline, and the broader P0/P1 test suite. Contributor and security intake files exist so future work can be packaged and diff --git a/ops/AUTONOMOUS_RUN.md b/ops/AUTONOMOUS_RUN.md index 80b1f2c6..b5c9dd54 100644 --- a/ops/AUTONOMOUS_RUN.md +++ b/ops/AUTONOMOUS_RUN.md @@ -32,11 +32,11 @@ tests, security hardening, deployment discipline, and release/audit readiness. | Field | Value | | --- | --- | | Remote | `https://github.com/6529-Collections/6529Stream.git` | -| Active PR branch | `codex/metadata-render-sandbox-checks` | -| Last merged PR | `https://github.com/6529-Collections/6529Stream/pull/111` | +| Active PR branch | `codex/metadata-uri-policy` | +| Last merged PR | `https://github.com/6529-Collections/6529Stream/pull/112` | | Roadmap file | `ops/ROADMAP.md` | | State file | `ops/AUTONOMOUS_RUN.md` | -| Last updated | `2026-06-11 15:42 UTC` | +| Last updated | `2026-06-11 16:32 UTC` | ## Packaging Notes @@ -111,14 +111,82 @@ The queue will evolve as PRs merge and bot feedback arrives. | 55 | Generate source verification inputs | Gate G support | Implement P1-RELEASE-007 by generating deterministic source-verification inputs from Foundry artifacts, source files, compiler settings, and contract config, with local/CI drift checks | Merged in PR #108 | | 56 | Add Foundry broadcast manifest ingestion | Gate E/Gate G support | Implement P1-DEPLOY-004 by parsing sanitized Foundry broadcast output into deterministic deployment-manifest evidence with local/CI drift checks | Merged in PR #110 | | 57 | Add metadata size limits | Gate D/Gate G support | Continue P1-META-006 by enforcing numeric byte caps for metadata storage inputs, generated `tokenURI` output, and dependency registry metadata, with focused tests, docs, release artifact refresh, and roadmap traceability | Merged in PR #111 | -| 58 | Add metadata render-sandbox fixture checks | Gate D | Continue P1-META-006 by validating committed metadata golden fixtures for JSON/data-URI/HTML script-boundary shape and URI scheme policy in local/CI gates, without changing production bytecode | Active | +| 58 | Add metadata render-sandbox fixture checks | Gate D | Continue P1-META-006 by validating committed metadata golden fixtures for JSON/data-URI/HTML script-boundary shape and URI scheme policy in local/CI gates, without changing production bytecode | Merged in PR #112 | +| 59 | Add metadata token image URI policy | Gate D/Gate G support | Continue P1-META-006 by defining renderer content/script URI policy helpers and rejecting unsafe token image URI writes in production while preserving collection base URI, external library URL, structured-attributes, invalid-UTF-8, and browser-sandbox work as follow-up slices | Active | ## Current PR Worklog +### PR candidate: Metadata token image URI policy (Queue Item 59) + +Status: PR open; awaiting CI and CodeRabbit. +Branch: `codex/metadata-uri-policy`. +Pull request: `https://github.com/6529-Collections/6529Stream/pull/113`. + +Goal: + +- Define reusable renderer helpers for the current safe content/script URI + policy rather than keeping the policy only in Python fixture checks. +- Reject unsafe required token image URI inputs in `StreamCore` before storage. +- Record that collection base URI and external animation library URL production + enforcement exceeded the current `StreamCore` EIP-170 budget in the first + local implementation attempt and must remain a follow-up slice unless more + production bytecode is freed. +- Keep this slice tightly scoped to URI policy so invalid UTF-8 handling, + semantic/structured attributes, and full browser execution sandboxing remain + separate P1-META-006 follow-up work. +- Verify the change does not break the tight `StreamCore` EIP-170 size budget. +- Update docs, roadmap, changelog, and run-state traceability. + +Candidate files: + +- `smart-contracts/StreamCore.sol` +- `smart-contracts/StreamMetadataRenderer.sol` +- `test/StreamMetadataUriPolicy.t.sol` +- `docs/metadata.md` +- `docs/known-blockers.md` +- `docs/status.md` +- `docs/adr/0006-metadata-freeze.md` +- `test/README.md` +- `ops/ROADMAP.md` +- `ops/AUTONOMOUS_RUN.md` +- `CHANGELOG.md` + +Validation: + +- `forge test --match-path test/StreamMetadataUriPolicy.t.sol -vvv` passed. +- `forge test --match-path test/StreamMetadataEscaping.t.sol -vvv` passed. +- `forge test --match-path test/StreamMetadataSizeLimits.t.sol -vvv` passed + after the accepted boundary image fixture was updated to a valid exact-length + `ipfs://` URI. +- `forge build --sizes --via-ir --skip test --skip script --force` passed with + `StreamCore` runtime size 24,508 bytes and 68 bytes of EIP-170 headroom. +- `make release-checksums` passed and regenerated release/deployment artifacts. +- `make check` passed. +- `powershell -ExecutionPolicy Bypass -File scripts\check.ps1` passed. +- Targeted `forge fmt --check` passed for this PR's touched Solidity files. +- `git diff --check` passed, with only the existing Windows line-ending warning + for `release-artifacts/latest/SHA256SUMS`. + +Notes: + +- A first local implementation also enforced collection base URI and external + animation library URL writes, but pushed `StreamCore` over EIP-170 by 468 + bytes. The deployable scope was narrowed to token image production enforcement + plus renderer helpers; the remaining collection/library surfaces stay queued + for a later bytecode-saving slice. +- Broad `forge fmt --check smart-contracts` still reports pre-existing + formatting drift in vendored/support contracts outside this PR's touched + files; the full project check gate does not currently include that broad + formatter target. + +Outcome: + +- Awaiting CI and CodeRabbit. + ### PR #112: Metadata render-sandbox fixture checks (Queue Item 58) -Status: PR open; local validation passed and CodeRabbit review was requested in -issue comment `4682324523`. +Status: Merged in PR #112 as +`419fb1db67cd329afeea7f9c17ccd67c3b4b477c`. Branch: `codex/metadata-render-sandbox-checks`. Pull request: `https://github.com/6529-Collections/6529Stream/pull/112`. @@ -168,7 +236,8 @@ Validation: Outcome: -- TBD. +- CI run `27359975388` passed, CodeRabbit status was success, and the only + actionable review thread was resolved after the empty-image regression fix. ### PR #4: Reproducible baseline tooling (Queue Item 2) diff --git a/ops/ROADMAP.md b/ops/ROADMAP.md index cb5c6d52..bc015e9e 100644 --- a/ops/ROADMAP.md +++ b/ops/ROADMAP.md @@ -1617,11 +1617,15 @@ Acceptance criteria: fourth slice validates committed metadata golden fixtures outside Foundry by decoding JSON and HTML data URIs, parsing metadata JSON, checking current URI scheme policy, and asserting final animation HTML wrapper/script boundaries. + The fifth slice defines renderer content/script URI policy helpers and + rejects unsafe token image URI writes in `StreamCore` while keeping the + production contract under EIP-170. - Add metadata schema and golden-file tests for `name`, `description`, `image`, `attributes`, and `animation_url`. - Complete the remaining render-safety work for full browser execution - sandboxing, production URI enforcement, invalid UTF-8 policy, and structured - attributes or semantic attribute schema validation. + sandboxing, collection base URI and external library URL production + enforcement, invalid UTF-8 policy, and structured attributes or semantic + attribute schema validation. - On-chain metadata now uses base64 JSON data URIs for schema-v1 output. - Numeric byte limits are now set for collection scripts, dependency scripts, `tokenData`, image data, attributes, dependency provenance, and generated diff --git a/release-artifacts/latest/SHA256SUMS b/release-artifacts/latest/SHA256SUMS index 5594f22a..92d6d0d0 100644 --- a/release-artifacts/latest/SHA256SUMS +++ b/release-artifacts/latest/SHA256SUMS @@ -1,17 +1,17 @@ -07947d84e990814edc6b483452c047ed469817de4b7c3c2c8c62dd0c5c9a3010 deployments/address-books/anvil-6529stream-v0.1.0-001-broadcast.json -698a21551331fd7f51769e9ee56cf0296df4fb34d2ca4fc3c95bc5ed6ed85e20 deployments/address-books/anvil-6529stream-v0.1.0-001.json +18c7e1ee1ec81ef45a15da3df1a22eb6bf4ecf5ff5f6e77cb2e4aa07bf193400 deployments/address-books/anvil-6529stream-v0.1.0-001-broadcast.json +35cc9c13380d4c18066d7e11cb15174b8984891542dd0f698e847e433f09c310 deployments/address-books/anvil-6529stream-v0.1.0-001.json 5697fb0387369b1127666f8c38b2a952dc2b085f7ab1188c1d18ac32da6141d3 deployments/broadcasts/anvil-6529stream-v0.1.0-001-run-latest.json b4ebc06aaa7c06ccf1a15ef8a79a6fdc866785f5d4996fc34af42bf3deed07f5 deployments/config/anvil-6529stream-v0.1.0-001-broadcast.json 03ff60467a5271ff5c8c638525dc355c3d00d78781cbcaa23c2d380995bb658b deployments/config/anvil-6529stream-v0.1.0-001.json -ff705c482c9f2566cd380388298fdc0b0a4017b1e965a3fae249503706ce3b30 deployments/examples/anvil-6529stream-v0.1.0-001-broadcast.json -f60b24f22126a90f6fbb6f7dc6c3e1a764f23c1aa94f9179987d6ec407f1ba3a deployments/examples/anvil-6529stream-v0.1.0-001.json +3f447863e05e7e4291c36b545058e5445c706b1e71855b3265a24c8f53633ad0 deployments/examples/anvil-6529stream-v0.1.0-001-broadcast.json +6d99f1b06ff41d908443cb4006bcc9d6e3c694c5a598c241fc35bc204903f98e deployments/examples/anvil-6529stream-v0.1.0-001.json e36a4a98e66c0bdcd6f5cd6c8125e547c53bd4537a7ee2c25a69f041ec2c3d13 deployments/schema/address-book.schema.json 3de078b625d0bf9fd49e352137efe7320d8ef701c04b0d5dc3dbfb5e560b7cfd deployments/schema/deployment-manifest.schema.json 44e97807c70ed606edb985cca85eace493a893a62c345212c18e7834ba6c08be release-artifacts/baselines/v0.1.0/abi-surface.json da895fbb51bc43b91b9c704bdef2186f718f171158487a52332023b858489011 release-artifacts/contracts.json -7bde6ce76e38f152d4640db721cd860defb971e2c3f4ed5da5a122c90cbc2cc2 release-artifacts/latest/abi-checksums.json +aa10a4981c8ba9390b41dd1648591bf1225df4cc5c07f48160b1ce25b93ee6c9 release-artifacts/latest/abi-checksums.json 6dd5190f816fd3ab72adcc6f1ae761aadf900dd24f0bd0f7bebc1697fed423af release-artifacts/latest/event-topic-catalog.json 8a8a41d89757d3d16f0718638d1175290dcd00cfea86204665cd525f608d7c1a release-artifacts/latest/interface-ids.json -5ba3be1dd7abb580e330de16f3193d6f9e2a879b807f78a7946dbf71e01f180c release-artifacts/latest/release-artifact-manifest.json -f79e08dc5672a7eba191f6982c1e3c2d49187cb90df4f3b806873fd95c40b43d release-artifacts/latest/release-manifest.json -1a643d87b29d1e821c72514c62c2a3880de96fc36890ad534a7068ea08c41527 release-artifacts/latest/source-verification-inputs.json +cd765fb8bbaf55c4004b1ca3efffb352cd1e72612bd14a1aeb3d9637b4a82849 release-artifacts/latest/release-artifact-manifest.json +0cc33ce7bb5728e2242e2930e87198ef8693e969f62426dd8c1e27684c6edb10 release-artifacts/latest/release-manifest.json +4cd6d577e0bed6cd63225e43addb60aef01b604efeaf6fb250f069ac72b5175c release-artifacts/latest/source-verification-inputs.json diff --git a/release-artifacts/latest/abi-checksums.json b/release-artifacts/latest/abi-checksums.json index b26045ae..7beb6ea8 100644 --- a/release-artifacts/latest/abi-checksums.json +++ b/release-artifacts/latest/abi-checksums.json @@ -11,7 +11,7 @@ "NextGenRandomizerVRF": "sha256:d913a668add41a0e37da1913191d4e35b3428fb62c5e55be96f1e9ba239b58c6", "StreamAdmins": "sha256:0dec302776ef6fe8038a96a716c7300d697c52c9b8d79559ab86c2b5c6599dd5", "StreamAuctions": "sha256:8fca4fb9860c9dd5fcbe7287dcd3cc86c69ae5d28488302602e27f0dfc3685d6", - "StreamCore": "sha256:820f6962c8e27c7e73ac51e18fdfddd81c517f58101a3b28335fcdbba5f7d4b3", + "StreamCore": "sha256:3738c2bf120feb5bb300a05cb19553829c65b76b2526a70e880e181027d49627", "StreamCuratorsPool": "sha256:f5d65fb2b17cf0caae7c8c75c6736c5828c869502f55634279556ba8483e6daf", "StreamDrops": "sha256:2314f89858673138e8b9c2e421dd7a727c7e7746d5eb069e58a61699f2f4de6a", "StreamMinter": "sha256:5464ee3c1b5c40867aba4565d11fe6b708ea2b2e93aba4f7e3a52ebbe4296c3e" @@ -79,12 +79,12 @@ }, "StreamCore": { "creation": { - "sha256": "sha256:58bedaea826f8c8e965a8d627e36ed5114a18c5f481be00205b4cf949dc9c28f", + "sha256": "sha256:2c88742e6981ac1d5ca90ccffadca1e3951dca1b324169936f2b88c426e835e3", "linked": false, "hash_mode": "unlinked_artifact_object" }, "runtime": { - "sha256": "sha256:db8cc579c656c43f09acc0fb6ee3d74ad2bc1b0877fc71f0c0737116556e7977", + "sha256": "sha256:5a16fdc2550ebfd91c705e6fe790836c86d1b5600f07d46f21db5603fccaa156", "linked": false, "hash_mode": "unlinked_artifact_object" } @@ -205,14 +205,14 @@ "StreamCore": { "source": "smart-contracts/StreamCore.sol", "artifact_path": "out/StreamCore.sol/StreamCore.json", - "abi_sha256": "sha256:820f6962c8e27c7e73ac51e18fdfddd81c517f58101a3b28335fcdbba5f7d4b3", - "bytecode_sha256": "sha256:58bedaea826f8c8e965a8d627e36ed5114a18c5f481be00205b4cf949dc9c28f", + "abi_sha256": "sha256:3738c2bf120feb5bb300a05cb19553829c65b76b2526a70e880e181027d49627", + "bytecode_sha256": "sha256:2c88742e6981ac1d5ca90ccffadca1e3951dca1b324169936f2b88c426e835e3", "bytecode_linked": false, "bytecode_hash_mode": "unlinked_artifact_object", - "deployed_bytecode_sha256": "sha256:db8cc579c656c43f09acc0fb6ee3d74ad2bc1b0877fc71f0c0737116556e7977", + "deployed_bytecode_sha256": "sha256:5a16fdc2550ebfd91c705e6fe790836c86d1b5600f07d46f21db5603fccaa156", "deployed_bytecode_linked": false, "deployed_bytecode_hash_mode": "unlinked_artifact_object", - "abi_entries": 103, + "abi_entries": 104, "function_count": 74, "event_count": 11, "constructor_count": 1 diff --git a/release-artifacts/latest/release-artifact-manifest.json b/release-artifacts/latest/release-artifact-manifest.json index 38f1905c..157d0953 100644 --- a/release-artifacts/latest/release-artifact-manifest.json +++ b/release-artifacts/latest/release-artifact-manifest.json @@ -8,7 +8,7 @@ "artifacts": { "abi-checksums.json": { "path": "abi-checksums.json", - "sha256": "sha256:7bde6ce76e38f152d4640db721cd860defb971e2c3f4ed5da5a122c90cbc2cc2" + "sha256": "sha256:aa10a4981c8ba9390b41dd1648591bf1225df4cc5c07f48160b1ce25b93ee6c9" }, "event-topic-catalog.json": { "path": "event-topic-catalog.json", diff --git a/release-artifacts/latest/release-checksums.json b/release-artifacts/latest/release-checksums.json index 26f3ca23..20a8df83 100644 --- a/release-artifacts/latest/release-checksums.json +++ b/release-artifacts/latest/release-checksums.json @@ -18,7 +18,7 @@ "text_checksum_file": { "path": "release-artifacts/latest/SHA256SUMS", "format": "sha256sum", - "sha256": "sha256:991f48130b156295074f29e10f0d12ca2f9d4d00a99d0790bbc9d5bc2a35ba00" + "sha256": "sha256:b7b3996aa93614549b5b45228b40f4502111415c88bd1d213d506ef83fd0991b" }, "manifest_file": { "path": "release-artifacts/latest/release-checksums.json", @@ -27,12 +27,12 @@ "files": [ { "path": "deployments/address-books/anvil-6529stream-v0.1.0-001-broadcast.json", - "sha256": "sha256:07947d84e990814edc6b483452c047ed469817de4b7c3c2c8c62dd0c5c9a3010", + "sha256": "sha256:18c7e1ee1ec81ef45a15da3df1a22eb6bf4ecf5ff5f6e77cb2e4aa07bf193400", "size_bytes": 5082 }, { "path": "deployments/address-books/anvil-6529stream-v0.1.0-001.json", - "sha256": "sha256:698a21551331fd7f51769e9ee56cf0296df4fb34d2ca4fc3c95bc5ed6ed85e20", + "sha256": "sha256:35cc9c13380d4c18066d7e11cb15174b8984891542dd0f698e847e433f09c310", "size_bytes": 5062 }, { @@ -52,12 +52,12 @@ }, { "path": "deployments/examples/anvil-6529stream-v0.1.0-001-broadcast.json", - "sha256": "sha256:ff705c482c9f2566cd380388298fdc0b0a4017b1e965a3fae249503706ce3b30", + "sha256": "sha256:3f447863e05e7e4291c36b545058e5445c706b1e71855b3265a24c8f53633ad0", "size_bytes": 8299 }, { "path": "deployments/examples/anvil-6529stream-v0.1.0-001.json", - "sha256": "sha256:f60b24f22126a90f6fbb6f7dc6c3e1a764f23c1aa94f9179987d6ec407f1ba3a", + "sha256": "sha256:6d99f1b06ff41d908443cb4006bcc9d6e3c694c5a598c241fc35bc204903f98e", "size_bytes": 8262 }, { @@ -82,7 +82,7 @@ }, { "path": "release-artifacts/latest/abi-checksums.json", - "sha256": "sha256:7bde6ce76e38f152d4640db721cd860defb971e2c3f4ed5da5a122c90cbc2cc2", + "sha256": "sha256:aa10a4981c8ba9390b41dd1648591bf1225df4cc5c07f48160b1ce25b93ee6c9", "size_bytes": 11136 }, { @@ -97,17 +97,17 @@ }, { "path": "release-artifacts/latest/release-artifact-manifest.json", - "sha256": "sha256:5ba3be1dd7abb580e330de16f3193d6f9e2a879b807f78a7946dbf71e01f180c", + "sha256": "sha256:cd765fb8bbaf55c4004b1ca3efffb352cd1e72612bd14a1aeb3d9637b4a82849", "size_bytes": 737 }, { "path": "release-artifacts/latest/release-manifest.json", - "sha256": "sha256:f79e08dc5672a7eba191f6982c1e3c2d49187cb90df4f3b806873fd95c40b43d", + "sha256": "sha256:0cc33ce7bb5728e2242e2930e87198ef8693e969f62426dd8c1e27684c6edb10", "size_bytes": 10704 }, { "path": "release-artifacts/latest/source-verification-inputs.json", - "sha256": "sha256:1a643d87b29d1e821c72514c62c2a3880de96fc36890ad534a7068ea08c41527", + "sha256": "sha256:4cd6d577e0bed6cd63225e43addb60aef01b604efeaf6fb250f069ac72b5175c", "size_bytes": 45397 } ] diff --git a/release-artifacts/latest/release-manifest.json b/release-artifacts/latest/release-manifest.json index 35611893..2285bbfe 100644 --- a/release-artifacts/latest/release-manifest.json +++ b/release-artifacts/latest/release-manifest.json @@ -30,7 +30,7 @@ }, "abi_checksums": { "path": "release-artifacts/latest/abi-checksums.json", - "sha256": "sha256:7bde6ce76e38f152d4640db721cd860defb971e2c3f4ed5da5a122c90cbc2cc2", + "sha256": "sha256:aa10a4981c8ba9390b41dd1648591bf1225df4cc5c07f48160b1ce25b93ee6c9", "size_bytes": 11136, "schema_version": "6529stream.abi-checksums.v1" }, @@ -48,13 +48,13 @@ }, "artifact_manifest": { "path": "release-artifacts/latest/release-artifact-manifest.json", - "sha256": "sha256:5ba3be1dd7abb580e330de16f3193d6f9e2a879b807f78a7946dbf71e01f180c", + "sha256": "sha256:cd765fb8bbaf55c4004b1ca3efffb352cd1e72612bd14a1aeb3d9637b4a82849", "size_bytes": 737, "schema_version": "6529stream.release-artifact-manifest.v1", "artifacts": { "abi-checksums.json": { "path": "abi-checksums.json", - "sha256": "sha256:7bde6ce76e38f152d4640db721cd860defb971e2c3f4ed5da5a122c90cbc2cc2" + "sha256": "sha256:aa10a4981c8ba9390b41dd1648591bf1225df4cc5c07f48160b1ce25b93ee6c9" }, "event-topic-catalog.json": { "path": "event-topic-catalog.json", @@ -68,7 +68,7 @@ }, "source_verification_inputs": { "path": "release-artifacts/latest/source-verification-inputs.json", - "sha256": "sha256:1a643d87b29d1e821c72514c62c2a3880de96fc36890ad534a7068ea08c41527", + "sha256": "sha256:4cd6d577e0bed6cd63225e43addb60aef01b604efeaf6fb250f069ac72b5175c", "size_bytes": 45397, "schema_version": "6529stream.source-verification-inputs.v1" }, @@ -104,7 +104,7 @@ "manifests": [ { "path": "deployments/examples/anvil-6529stream-v0.1.0-001-broadcast.json", - "sha256": "sha256:ff705c482c9f2566cd380388298fdc0b0a4017b1e965a3fae249503706ce3b30", + "sha256": "sha256:3f447863e05e7e4291c36b545058e5445c706b1e71855b3265a24c8f53633ad0", "size_bytes": 8299, "schema_version": "6529stream.deployment-manifest.v1", "protocol_version": "0.1.0", @@ -114,7 +114,7 @@ "name": "anvil", "chain_id": 31337 }, - "manifest_sha256": "sha256:79a0530da77ce6653198a15ccccf2a2deb023765f7a6d14b5a3f28300e19785e", + "manifest_sha256": "sha256:38bdd644fe53b71a2b3b9afb6cc4de258f28e43b48428cda4646075ea5e34ed1", "contracts": [ "DependencyRegistry", "NextGenRandomizerRNG", @@ -129,7 +129,7 @@ }, { "path": "deployments/examples/anvil-6529stream-v0.1.0-001.json", - "sha256": "sha256:f60b24f22126a90f6fbb6f7dc6c3e1a764f23c1aa94f9179987d6ec407f1ba3a", + "sha256": "sha256:6d99f1b06ff41d908443cb4006bcc9d6e3c694c5a598c241fc35bc204903f98e", "size_bytes": 8262, "schema_version": "6529stream.deployment-manifest.v1", "protocol_version": "0.1.0", @@ -139,7 +139,7 @@ "name": "anvil", "chain_id": 31337 }, - "manifest_sha256": "sha256:926ef2be2b0c7b68f860cb678d545bb35fb3e4507867085d5aff56ec8f70f721", + "manifest_sha256": "sha256:c1b7b64b7fc2f528260788e206dd3270341be9810230c9914ae350084667fa82", "contracts": [ "DependencyRegistry", "NextGenRandomizerRNG", @@ -156,7 +156,7 @@ "address_books": [ { "path": "deployments/address-books/anvil-6529stream-v0.1.0-001-broadcast.json", - "sha256": "sha256:07947d84e990814edc6b483452c047ed469817de4b7c3c2c8c62dd0c5c9a3010", + "sha256": "sha256:18c7e1ee1ec81ef45a15da3df1a22eb6bf4ecf5ff5f6e77cb2e4aa07bf193400", "size_bytes": 5082, "schema_version": "6529stream.address-book.v1", "protocol_version": "0.1.0", @@ -167,7 +167,7 @@ "chain_id": 31337 }, "deployment_manifest": "deployments/examples/anvil-6529stream-v0.1.0-001-broadcast.json", - "deployment_manifest_sha256": "sha256:79a0530da77ce6653198a15ccccf2a2deb023765f7a6d14b5a3f28300e19785e", + "deployment_manifest_sha256": "sha256:38bdd644fe53b71a2b3b9afb6cc4de258f28e43b48428cda4646075ea5e34ed1", "contracts": [ "DependencyRegistry", "NextGenRandomizerRNG", @@ -182,7 +182,7 @@ }, { "path": "deployments/address-books/anvil-6529stream-v0.1.0-001.json", - "sha256": "sha256:698a21551331fd7f51769e9ee56cf0296df4fb34d2ca4fc3c95bc5ed6ed85e20", + "sha256": "sha256:35cc9c13380d4c18066d7e11cb15174b8984891542dd0f698e847e433f09c310", "size_bytes": 5062, "schema_version": "6529stream.address-book.v1", "protocol_version": "0.1.0", @@ -193,7 +193,7 @@ "chain_id": 31337 }, "deployment_manifest": "deployments/examples/anvil-6529stream-v0.1.0-001.json", - "deployment_manifest_sha256": "sha256:926ef2be2b0c7b68f860cb678d545bb35fb3e4507867085d5aff56ec8f70f721", + "deployment_manifest_sha256": "sha256:c1b7b64b7fc2f528260788e206dd3270341be9810230c9914ae350084667fa82", "contracts": [ "DependencyRegistry", "NextGenRandomizerRNG", @@ -225,8 +225,8 @@ "release_notes_and_policy": { "changelog": { "path": "CHANGELOG.md", - "sha256": "sha256:037abad57da6410942fca77a69ecb11a66d465f25c05ec79a1b79301fb905e04", - "size_bytes": 2912 + "sha256": "sha256:91677b95ff714c58e279efc600a49c0bcd3af92c645e8d6b46812b80e7bc9f54", + "size_bytes": 3185 }, "governance_docs": [ { @@ -246,8 +246,8 @@ }, { "path": "docs/status.md", - "sha256": "sha256:f3b27cda96f45539a6ef1d5f4b7a6cf061416b6fe8ce74a542928696e32b5e53", - "size_bytes": 10064 + "sha256": "sha256:74400edcdb1f81927d59d81b8694dd99b5ff251f7b76f071c625b3947fa50660", + "size_bytes": 10391 } ] }, diff --git a/release-artifacts/latest/source-verification-inputs.json b/release-artifacts/latest/source-verification-inputs.json index e49800bd..803efbae 100644 --- a/release-artifacts/latest/source-verification-inputs.json +++ b/release-artifacts/latest/source-verification-inputs.json @@ -375,8 +375,8 @@ }, "smart-contracts/StreamCore.sol": { "path": "smart-contracts/StreamCore.sol", - "sha256": "sha256:fad2ff5c482260edacc992b846b61c0e4a656bff9f5f5818525ee4ab72766067", - "solc_keccak256": "0xa6ffcdcadbf1ca924f368ee36fa89a53d4ee8269af9225d1938228ece697d0b0", + "sha256": "sha256:7dcd35d66776ecd1c933ba3a5200e4e9302709a3727fdfba7e1552a7854113f0", + "solc_keccak256": "0xc4a969d1dd6d901db7b234fd6e3204cafd965e0b9d99d5271ca3e1ac254fba98", "license": "MIT", "used_by": [ "StreamCore" @@ -402,8 +402,8 @@ }, "smart-contracts/StreamMetadataRenderer.sol": { "path": "smart-contracts/StreamMetadataRenderer.sol", - "sha256": "sha256:0e28d97ef9fce7f94ca46f9b53e45bd7153f07a572f41ea73a274c812b250422", - "solc_keccak256": "0xf526e848a2b8df10d02d0fefa00398dd6cc180063ce3fd5cfdf8110bd9cace6e", + "sha256": "sha256:935d0645d5c1447ae2aea5f6a2444e4d21cde3930fcdb33cd228dcb4b9338a5c", + "solc_keccak256": "0x28e0361fe72891c1df6e921996d620542f9a5feabfcf35ffee79102bbace8c7f", "license": "MIT", "used_by": [ "StreamCore" @@ -612,7 +612,7 @@ "source_sha256": "sha256:e126bc7c0b08a3318dbb435ce24831a59b3f113c19df49752c3b71f85d748a4c", "source_solc_keccak256": "0xd5d9f10afe33ff40fa74816b9b5be05856d7a8f8a89a5b5dd22121c467291cb9", "artifact_path": "out/RandomizerVRF.sol/NextGenRandomizerVRF.json", - "artifact_sha256": "sha256:84fe84e67bda3bb81b4b37ae689740818a93ad959f630c17f3a5d2205cc88897", + "artifact_sha256": "sha256:0cddc6def8e04a9b61a03973b9cdf427388787e06bb0c5fedca95f5d6d6b6109", "compilation_target": "smart-contracts/RandomizerVRF.sol:NextGenRandomizerVRF", "compiler_version": "0.8.19+commit.7dd6d404", "language": "Solidity", @@ -840,10 +840,10 @@ }, "StreamCore": { "source": "smart-contracts/StreamCore.sol", - "source_sha256": "sha256:fad2ff5c482260edacc992b846b61c0e4a656bff9f5f5818525ee4ab72766067", - "source_solc_keccak256": "0xa6ffcdcadbf1ca924f368ee36fa89a53d4ee8269af9225d1938228ece697d0b0", + "source_sha256": "sha256:7dcd35d66776ecd1c933ba3a5200e4e9302709a3727fdfba7e1552a7854113f0", + "source_solc_keccak256": "0xc4a969d1dd6d901db7b234fd6e3204cafd965e0b9d99d5271ca3e1ac254fba98", "artifact_path": "out/StreamCore.sol/StreamCore.json", - "artifact_sha256": "sha256:567a83eae89b827b5b5f148f64b612401ff15dfb310f5def08dc1cc12f2e0f87", + "artifact_sha256": "sha256:942a9d6a54435de1009983c1f79310d6a0609786c25b058d3aaa0d4b8ed3de96", "compilation_target": "smart-contracts/StreamCore.sol:StreamCore", "compiler_version": "0.8.19+commit.7dd6d404", "language": "Solidity", @@ -857,19 +857,19 @@ "metadata_bytecode_hash": "ipfs", "libraries": {} }, - "abi_sha256": "sha256:820f6962c8e27c7e73ac51e18fdfddd81c517f58101a3b28335fcdbba5f7d4b3", + "abi_sha256": "sha256:3738c2bf120feb5bb300a05cb19553829c65b76b2526a70e880e181027d49627", "bytecode_hashes": { "creation": { - "sha256": "sha256:58bedaea826f8c8e965a8d627e36ed5114a18c5f481be00205b4cf949dc9c28f", + "sha256": "sha256:2c88742e6981ac1d5ca90ccffadca1e3951dca1b324169936f2b88c426e835e3", "linked": false, "hash_mode": "unlinked_artifact_object", - "release_artifact_sha256": "sha256:58bedaea826f8c8e965a8d627e36ed5114a18c5f481be00205b4cf949dc9c28f" + "release_artifact_sha256": "sha256:2c88742e6981ac1d5ca90ccffadca1e3951dca1b324169936f2b88c426e835e3" }, "runtime": { - "sha256": "sha256:db8cc579c656c43f09acc0fb6ee3d74ad2bc1b0877fc71f0c0737116556e7977", + "sha256": "sha256:5a16fdc2550ebfd91c705e6fe790836c86d1b5600f07d46f21db5603fccaa156", "linked": false, "hash_mode": "unlinked_artifact_object", - "release_artifact_sha256": "sha256:db8cc579c656c43f09acc0fb6ee3d74ad2bc1b0877fc71f0c0737116556e7977" + "release_artifact_sha256": "sha256:5a16fdc2550ebfd91c705e6fe790836c86d1b5600f07d46f21db5603fccaa156" } }, "constructor": { @@ -915,15 +915,15 @@ "library": "StreamMetadataRenderer", "positions": [ { - "start": 8182, + "start": 8181, "length": 20 }, { - "start": 21539, + "start": 21586, "length": 20 }, { - "start": 23322, + "start": 23369, "length": 20 } ] @@ -935,15 +935,15 @@ "library": "StreamMetadataRenderer", "positions": [ { - "start": 6958, + "start": 6957, "length": 20 }, { - "start": 20315, + "start": 20362, "length": 20 }, { - "start": 22098, + "start": 22145, "length": 20 } ] diff --git a/smart-contracts/StreamCore.sol b/smart-contracts/StreamCore.sol index d80ebe20..06d1203c 100644 --- a/smart-contracts/StreamCore.sol +++ b/smart-contracts/StreamCore.sol @@ -93,6 +93,7 @@ contract StreamCore is ERC721, ERC2981, Ownable, IERC4906 { error FrozenCollectionDependencyRegistry(); error MetadataFieldTooLarge(bytes32 field, uint256 actual, uint256 maximum); error MetadataFrozen(uint256 collectionId); + error UnsafeMetadataURI(); error UnsafeRawAttributes(uint256 tokenId); error UnknownDependency(bytes32 dependencyNameAndVersion); @@ -534,7 +535,7 @@ contract StreamCore is ERC721, ERC2981, Ownable, IERC4906 { { _requireMetadataMutationNotPaused(); uint256 collectionId = tokenIdsToCollectionIds[_tokenId]; - require(collectionFreeze[collectionId] == false, "Data frozen"); + _requireCollectionNotFrozen(collectionId); _requireMinted(_tokenId); _requireMaxBytes(_FIELD_TOKEN_DATA, newData, MAX_TOKEN_DATA_BYTES); tokenData[_tokenId] = newData; @@ -554,10 +555,13 @@ contract StreamCore is ERC721, ERC2981, Ownable, IERC4906 { ); for (uint256 x; x < _tokenId.length; x++) { uint256 collectionId = tokenIdsToCollectionIds[_tokenId[x]]; - require(collectionFreeze[collectionId] == false, "Data frozen"); + _requireCollectionNotFrozen(collectionId); _requireMinted(_tokenId[x]); _requireMaxBytes(_FIELD_TOKEN_IMAGE, _images[x], MAX_TOKEN_IMAGE_BYTES); _requireMaxBytes(_FIELD_TOKEN_ATTRIBUTES, _attributes[x], MAX_TOKEN_ATTRIBUTES_BYTES); + if (!StreamMetadataRenderer.isSafeContentUri(_images[x], false)) { + revert UnsafeMetadataURI(); + } _requireSafeRawAttributes(_tokenId[x], _attributes[x]); tokenImageAndAttributes[_tokenId[x]][0] = _images[x]; tokenImageAndAttributes[_tokenId[x]][1] = _attributes[x]; diff --git a/smart-contracts/StreamMetadataRenderer.sol b/smart-contracts/StreamMetadataRenderer.sol index 54dc2f92..ee889840 100644 --- a/smart-contracts/StreamMetadataRenderer.sol +++ b/smart-contracts/StreamMetadataRenderer.sol @@ -335,6 +335,25 @@ library StreamMetadataRenderer { || (state.sawTopLevelValue && state.expectingTopLevelValue)); } + function isSafeContentUri(string memory uri, bool allowEmpty) public pure returns (bool) { + bytes memory input = bytes(uri); + if (input.length == 0) { + return allowEmpty; + } + if (!_hasNoUriWhitespaceOrControls(input)) { + return false; + } + return (_startsWith(input, "https://") && _hasHttpsHost(input)) + || (_startsWith(input, "ipfs://") && input.length > 7) + || (_startsWith(input, "ar://") && input.length > 5); + } + + function isSafeScriptUri(string memory uri) public pure returns (bool) { + bytes memory input = bytes(uri); + return input.length > 0 && _hasNoUriWhitespaceOrControls(input) + && _startsWith(input, "https://") && _hasHttpsHost(input); + } + function _advanceRawAttributeStringState( RawAttributeValidationState memory state, bytes1 character @@ -437,6 +456,37 @@ library StreamMetadataRenderer { && _lowerAscii(input[index + 6]) == 0x70 && _lowerAscii(input[index + 7]) == 0x74; } + function _hasNoUriWhitespaceOrControls(bytes memory input) private pure returns (bool) { + for (uint256 i = 0; i < input.length; i++) { + uint8 value = uint8(input[i]); + if (value <= 0x20 || value == 0x7f) { + return false; + } + } + return true; + } + + function _hasHttpsHost(bytes memory input) private pure returns (bool) { + if (input.length <= 8) { + return false; + } + bytes1 firstHostByte = input[8]; + return firstHostByte != 0x2f && firstHostByte != 0x3f && firstHostByte != 0x23; + } + + function _startsWith(bytes memory input, string memory rawPrefix) private pure returns (bool) { + bytes memory prefix = bytes(rawPrefix); + if (input.length < prefix.length) { + return false; + } + for (uint256 i = 0; i < prefix.length; i++) { + if (input[i] != prefix[i]) { + return false; + } + } + return true; + } + function _lowerAscii(bytes1 character) private pure returns (bytes1) { if (character >= 0x41 && character <= 0x5a) { return bytes1(uint8(character) + 32); diff --git a/test/README.md b/test/README.md index 02345605..71fa00b4 100644 --- a/test/README.md +++ b/test/README.md @@ -186,9 +186,13 @@ inputs with structured custom errors. `scripts/test_metadata_fixtures.py` and metadata golden fixtures: the scripts strictly decode JSON/HTML data URIs, parse metadata JSON, validate current URI scheme policy, and assert the final animation wrapper has exactly one external script and one inline generative -script with no raw script-boundary breakout. Full browser execution sandboxing, -production URI enforcement, semantic attribute schema validation, and invalid -UTF-8 policy remain future P1-META-006 work. +script with no raw script-boundary breakout. `StreamMetadataUriPolicy.t.sol` +covers the renderer's current content/script URI policy helpers and production +token image URI enforcement for allowed `https://`, `ipfs://`, and `ar://` +content URIs plus rejected empty, JavaScript, hostless HTTPS, and +whitespace-bearing image inputs. Full browser execution sandboxing, collection +base URI and external library URL production enforcement, semantic attribute +schema validation, and invalid UTF-8 policy remain future P1-META-006 work. ERC-4906 metadata signaling now has P1-META-004 target-state coverage in `StreamMetadataEvents.t.sol`: `supportsInterface(0x49064906)` succeeds, diff --git a/test/StreamMetadataEscaping.t.sol b/test/StreamMetadataEscaping.t.sol index 6da2bc12..487f6cb4 100644 --- a/test/StreamMetadataEscaping.t.sol +++ b/test/StreamMetadataEscaping.t.sol @@ -28,7 +28,9 @@ contract StreamMetadataEscapingTest is CharacterizationTestBase, StreamFixture { string(abi.encodePacked("pending", bytes1(0x22), bytes1(0x5c), "state")), "Name", "Description", - "ipfs://image.png", + string( + abi.encodePacked("ipfs://image/quote\"", bytes1(0x5c), "line", bytes1(0x0a), ".png") + ), "", "", "", @@ -41,7 +43,7 @@ contract StreamMetadataEscapingTest is CharacterizationTestBase, StreamFixture { "{\"metadata_schema_version\":\"schema\\\"\\\\v\",", "\"metadata_state\":\"pending\\\"\\\\state\",", "\"name\":\"Name\",\"description\":\"Description\",", - "\"image\":\"ipfs://image.png\",\"attributes\":[]}" + "\"image\":\"ipfs://image/quote\\\"\\\\line\\n.png\",\"attributes\":[]}" ), "schema and state fields were not escaped" ); @@ -55,9 +57,7 @@ contract StreamMetadataEscapingTest is CharacterizationTestBase, StreamFixture { _mintToken(deployed); _setImageAndAttributes( deployed.core, - string( - abi.encodePacked("ipfs://image/quote\"", bytes1(0x5c), "line", bytes1(0x0a), ".png") - ), + "ipfs://image/escaped-safe.png", "{\"trait_type\":\"Mood\",\"value\":\"Calm\"}" ); deployed.core.changeMetadataView(COLLECTION_ID, true); @@ -70,7 +70,7 @@ contract StreamMetadataEscapingTest is CharacterizationTestBase, StreamFixture { "{\"metadata_schema_version\":\"6529stream-v1\",\"metadata_state\":\"pending\",", "\"name\":\"Genesis \\\"Alpha\\\"\\\\Beta #0\",", "\"description\":\"Line 1\\nTabbed\\tUnit\\u0001\\\"\\\\\",", - "\"image\":\"ipfs://image/quote\\\"\\\\line\\n.png\",", + "\"image\":\"ipfs://image/escaped-safe.png\",", "\"attributes\":[{\"trait_type\":\"Mood\",\"value\":\"Calm\"}]}" ), "escaped metadata JSON changed" @@ -250,6 +250,18 @@ contract StreamMetadataEscapingTest is CharacterizationTestBase, StreamFixture { "https://cdn.example/lib.js\" async=\"bad\"&", string(abi.encodePacked(bytes1(0), bytes1(0x0a))) ); + string memory hostileLibraryHtml = + _decodeHtmlDataUri(StreamMetadataRenderer.onchainAnimationURI(hostileLibrary, "")); + bytes memory hostileLibraryBytes = bytes(hostileLibraryHtml); + _contains( + hostileLibraryBytes, + bytes("src=\"https://cdn.example/lib.js" async="bad"") + ).assertTrue("library attribute quote was not escaped"); + _contains(hostileLibraryBytes, bytes("</script><img src=x>&")) + .assertTrue("library attribute markup was not escaped"); + _contains(hostileLibraryBytes, bytes("� ")) + .assertTrue("library attribute controls were not escaped"); + deployed.core .updateCollectionInfo( COLLECTION_ID, @@ -259,7 +271,7 @@ contract StreamMetadataEscapingTest is CharacterizationTestBase, StreamFixture { "https://6529.io", "CC0", "ipfs://base/", - hostileLibrary, + "https://cdn.example/lib.js", dependencyKey, FULL_COLLECTION_UPDATE_INDEX, scripts @@ -286,12 +298,6 @@ contract StreamMetadataEscapingTest is CharacterizationTestBase, StreamFixture { _countOccurrences(htmlBytes, bytes("")) .assertEq(2, "unexpected raw script close count"); - _contains(htmlBytes, bytes("src=\"https://cdn.example/lib.js" async="bad"")) - .assertTrue("library attribute quote was not escaped"); - _contains(htmlBytes, bytes("</script><img src=x>&")) - .assertTrue("library attribute markup was not escaped"); - _contains(htmlBytes, bytes("� ")) - .assertTrue("library attribute controls were not escaped"); _contains( htmlBytes, bytes("let tokenDataRaw='1];window.injected=true;//\\x3c/script\\x3e';") ).assertTrue("tokenData raw string was not escaped"); diff --git a/test/StreamMetadataFreeze.t.sol b/test/StreamMetadataFreeze.t.sol index 9277125b..3ec34561 100644 --- a/test/StreamMetadataFreeze.t.sol +++ b/test/StreamMetadataFreeze.t.sol @@ -190,7 +190,7 @@ contract StreamMetadataFreezeTest is CharacterizationTestBase, StreamFixture { scripts ); - vm.expectRevert("Data frozen"); + vm.expectRevert(abi.encodeWithSelector(StreamCore.MetadataFrozen.selector, COLLECTION_ID)); deployed.core.changeTokenData(TOKEN_ID, "4,5,6"); uint256[] memory tokenIds = new uint256[](1); @@ -200,7 +200,7 @@ contract StreamMetadataFreezeTest is CharacterizationTestBase, StreamFixture { images[0] = "ipfs://image/updated.png"; attributes[0] = "{\"trait_type\":\"Mood\",\"value\":\"Locked\"}"; - vm.expectRevert("Data frozen"); + vm.expectRevert(abi.encodeWithSelector(StreamCore.MetadataFrozen.selector, COLLECTION_ID)); deployed.core.updateImagesAndAttributes(tokenIds, images, attributes); } diff --git a/test/StreamMetadataSizeLimits.t.sol b/test/StreamMetadataSizeLimits.t.sol index d67afe2e..14cbf9c2 100644 --- a/test/StreamMetadataSizeLimits.t.sol +++ b/test/StreamMetadataSizeLimits.t.sol @@ -47,7 +47,8 @@ contract StreamMetadataSizeLimitsTest is CharacterizationTestBase, StreamFixture string[] memory images = new string[](1); string[] memory attributes = new string[](1); tokenIds[0] = TOKEN_ID; - images[0] = _repeat("i", maxImage); + images[0] = _ipfsUriWithSize(maxImage); + bytes(images[0]).length.assertEq(maxImage, "image fixture length"); attributes[0] = _attributeWithValueSize(maxAttributes); bytes(attributes[0]).length.assertEq(maxAttributes, "attribute fixture length"); @@ -307,6 +308,12 @@ contract StreamMetadataSizeLimitsTest is CharacterizationTestBase, StreamFixture ); } + function _ipfsUriWithSize(uint256 size) private pure returns (string memory) { + string memory prefix = "ipfs://"; + require(size > bytes(prefix).length, "size too small"); + return string.concat(prefix, _repeat("i", size - bytes(prefix).length)); + } + function _chunkArray(uint256 count, string memory value) private pure diff --git a/test/StreamMetadataUriPolicy.t.sol b/test/StreamMetadataUriPolicy.t.sol new file mode 100644 index 00000000..12955b98 --- /dev/null +++ b/test/StreamMetadataUriPolicy.t.sol @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import "../smart-contracts/StreamCore.sol"; +import "../smart-contracts/StreamMetadataRenderer.sol"; +import "./helpers/Assertions.sol"; +import "./helpers/CharacterizationTestBase.sol"; +import "./helpers/StreamFixture.sol"; + +contract StreamMetadataUriPolicyTest is CharacterizationTestBase, StreamFixture { + using Assertions for bool; + + uint256 private constant COLLECTION_ID = 1; + uint256 private constant TOKEN_ID = 10_000_000_000; + address private constant RECIPIENT = address(0xA11CE); + + function testRendererUriPolicyHelpers() public pure { + StreamMetadataRenderer.isSafeContentUri("https://metadata.example/base/", false) + .assertTrue("https content uri rejected"); + StreamMetadataRenderer.isSafeContentUri("ipfs://image/10000000000.png", false) + .assertTrue("ipfs content uri rejected"); + StreamMetadataRenderer.isSafeContentUri("ar://transaction-id", false) + .assertTrue("ar content uri rejected"); + StreamMetadataRenderer.isSafeContentUri("", true).assertTrue("empty optional uri rejected"); + StreamMetadataRenderer.isSafeContentUri("", false) + .assertFalse("empty required uri accepted"); + StreamMetadataRenderer.isSafeContentUri("javascript:alert(1)", false) + .assertFalse("javascript uri accepted"); + StreamMetadataRenderer.isSafeContentUri("https:///missing-host", false) + .assertFalse("hostless https uri accepted"); + StreamMetadataRenderer.isSafeContentUri("https://metadata.example/bad path", false) + .assertFalse("whitespace uri accepted"); + StreamMetadataRenderer.isSafeScriptUri("https://cdn.example/script.js") + .assertTrue("https script uri rejected"); + StreamMetadataRenderer.isSafeScriptUri("ipfs://dependency/script.js") + .assertFalse("ipfs script uri accepted"); + } + + function testProductionTokenImagePolicyAcceptsAllowedUris() public { + DeployedStream memory deployed = deployStream(address(0xBEEF), address(0xCAFE)); + _mintToken(deployed); + + _updateTokenImage(deployed.core, "https://image.example/token.png"); + _updateTokenImage(deployed.core, "ipfs://image/10000000000.png"); + _updateTokenImage(deployed.core, "ar://image-transaction-id"); + } + + function testTokenImageRejectsUnsafeProductionUris() public { + DeployedStream memory deployed = deployStream(address(0xBEEF), address(0xCAFE)); + _mintToken(deployed); + + vm.expectRevert(abi.encodeWithSelector(StreamCore.UnsafeMetadataURI.selector)); + _updateTokenImage(deployed.core, ""); + + vm.expectRevert(abi.encodeWithSelector(StreamCore.UnsafeMetadataURI.selector)); + _updateTokenImage(deployed.core, "javascript:alert(1)"); + + vm.expectRevert(abi.encodeWithSelector(StreamCore.UnsafeMetadataURI.selector)); + _updateTokenImage(deployed.core, "https://image.example/bad path.png"); + } + + function _mintToken(DeployedStream memory deployed) private { + vm.prank(address(deployed.minter)); + deployed.core.mint(TOKEN_ID, RECIPIENT, "1,2,3", 7, COLLECTION_ID); + } + + function _updateTokenImage(StreamCore core, string memory image) private { + uint256[] memory tokenIds = new uint256[](1); + string[] memory images = new string[](1); + string[] memory attributes = new string[](1); + tokenIds[0] = TOKEN_ID; + images[0] = image; + attributes[0] = "{\"trait_type\":\"Mood\",\"value\":\"Calm\"}"; + core.updateImagesAndAttributes(tokenIds, images, attributes); + } +}