Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,4 +119,5 @@ pnpm demo:private-witness

## License

代码使用 Apache-2.0。ERC 草案文本按 EIP 流程使用 CC0。
产品代码使用 Apache-2.0。ERC 草案文本与 `erc/assets/erc-8350/` 下的官方提交资产使用
[CC0](erc/LICENSE.md);该例外不改变 `contracts/src/` 和 TypeScript 包的现有许可证。
2 changes: 2 additions & 0 deletions contracts/foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ src = "src"
test = "test"
out = "out"
libs = ["lib"]
remappings = ["erc8350-assets/=../erc/assets/erc-8350/"]
allow_paths = ["../erc/assets/erc-8350"]
solc = "0.8.24"
optimizer = true
optimizer_runs = 200
Expand Down
4 changes: 4 additions & 0 deletions contracts/src/interfaces/IAgentMemoryState.sol
Original file line number Diff line number Diff line change
Expand Up @@ -72,21 +72,25 @@ interface IAgentMemoryState {
view
returns (address controller, address authorizer, uint64 configNonce);

/// @dev Optional, non-normative convenience; not required for ERC-8350 conformance.
function transition(bytes32 transitionId)
external
view
returns (bytes32 spaceId, bytes32 nextStateRoot, uint64 sequence, uint64 committedAt);

/// @dev Optional, non-normative convenience; not required for ERC-8350 conformance.
function hashExperienceDelta(ExperienceDelta calldata delta)
external
pure
returns (bytes32 transitionId);

/// @dev Optional, non-normative convenience; not required for ERC-8350 conformance.
function computeNextStateRoot(bytes32 prevStateRoot, bytes32 transitionId)
external
pure
returns (bytes32 nextStateRoot);

/// @dev Optional, non-normative convenience; not required for ERC-8350 conformance.
function signingDigest(bytes32 structHash) external view returns (bytes32 digest);

function deriveSpaceId(address initialController, bytes32 salt)
Expand Down
35 changes: 32 additions & 3 deletions contracts/test/unit/GoldenVector.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ import {Test} from "forge-std/Test.sol";
import {AgentMemoryStateRegistry} from "../../src/reference/AgentMemoryStateRegistry.sol";
import {PrivateCommitment} from "../../src/reference/PrivateCommitment.sol";
import {IAgentMemoryState} from "../../src/interfaces/IAgentMemoryState.sol";
import {
AgentMemoryStateRegistry as OfficialAssetRegistry
} from "erc8350-assets/AgentMemoryStateRegistry.sol";

contract GoldenVectorTest is Test {
AgentMemoryStateRegistry internal registry;
string internal vector;

function setUp() public {
function setUp() public virtual {
registry = new AgentMemoryStateRegistry();
vector = vm.readFile(string.concat(vm.projectRoot(), "/../test-vectors/v1.json"));
}
Expand All @@ -22,6 +25,7 @@ contract GoldenVectorTest is Test {
bytes32 transitionId = registry.hashExperienceDelta(delta);

_assertPrivateCommitments(delta);
_assertTypeStrings();
_assertTransitionHashes(delta, transitionId);
_assertSpaceAuthorization(delta);
_assertEip712(transitionId);
Expand All @@ -39,6 +43,23 @@ contract GoldenVectorTest is Test {
});
}

function _assertTypeStrings() private view {
assertEq(
keccak256(bytes(_string(".types.experienceDelta"))),
registry.EXPERIENCE_DELTA_TYPEHASH()
);
assertEq(keccak256(bytes(_string(".types.memoryState"))), registry.MEMORY_STATE_TYPEHASH());
assertEq(keccak256(bytes(_string(".types.memorySpace"))), registry.MEMORY_SPACE_TYPEHASH());
assertEq(
keccak256(bytes(_string(".types.spaceRegistration"))),
registry.SPACE_REGISTRATION_TYPEHASH()
);
assertEq(
keccak256(bytes(_string(".types.spaceAuthorization"))),
registry.SPACE_AUTHORIZATION_TYPEHASH()
);
}

function _assertPrivateCommitments(IAgentMemoryState.ExperienceDelta memory delta)
private
view
Expand Down Expand Up @@ -98,8 +119,8 @@ contract GoldenVectorTest is Test {
assertEq(
registry.hashSpaceAuthorization(
delta.spaceId,
controller,
authorizer,
vm.parseJsonAddress(vector, ".spaceAuthorization.newController"),
vm.parseJsonAddress(vector, ".spaceAuthorization.newAuthorizer"),
uint64(vm.parseUint(_string(".spaceAuthorization.updateNonce")))
),
_bytes32(".spaceAuthorization.authorizationId")
Expand Down Expand Up @@ -127,3 +148,11 @@ contract GoldenVectorTest is Test {
return vm.parseJsonString(vector, path);
}
}

contract OfficialAssetGoldenVectorTest is GoldenVectorTest {
function setUp() public override {
// Exercise the submitted asset through the shared reference ABI.
registry = AgentMemoryStateRegistry(address(new OfficialAssetRegistry()));
vector = vm.readFile(string.concat(vm.projectRoot(), "/../test-vectors/v1.json"));
}
}
7 changes: 6 additions & 1 deletion docs/conformance.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ A run passes only when:
3. `packages/core` reads `test-vectors/v1.json` and matches every expected byte;
4. `implementations/minimal-ts` independently reads the same file and matches it;
5. `contracts/test/unit/GoldenVector.t.sol` reads the same file through Foundry and
matches it; and
matches it against both the product registry and the submitted Solidity asset; and
6. the gate writes a JSON record, a Markdown summary, and the command logs.

The three vector suites cover:
Expand All @@ -29,6 +29,11 @@ The three vector suites cover:
- Transition ID and next state root; and
- EIP-712 domain separator and signing digest.

All suites also check the five explicit type strings. Authorization-update hashes
use `newController` and `newAuthorizer` from the JSON rather than assuming that
the replacement accounts equal the registration accounts. These additional v1
inputs do not change the published expected hashes.

“Byte-for-byte” means each computed `bytes32` value is compared directly with the
corresponding lowercase `0x` value decoded from the canonical JSON file. The
Solidity suite does not contain a second hard-coded copy of those expected values.
Expand Down
60 changes: 60 additions & 0 deletions docs/reviews/office-hour-113.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Office Hour 113 revision

## Scope

This revision addresses Jochem Brouwer's September 15, 2026 reviews on
[ethereum/ERCs#1910](https://github.com/ethereum/ERCs/pull/1910), based on upstream
commit `708e8130a32a525ed001225925aeed04905cb98f`. The product baseline is
`8bef6e77db895ec47220715fc73acf0e6a18954e`.

The editor confirmed that Sam's earlier six comments were addressed. The
licensing issue is an explicit merge blocker. The supplementary review is
LLM-assisted feedback, not an independent security audit or reproduction report.

## Changes

| Review item | Resolution |
| --- | --- |
| [Asset licensing](https://github.com/ethereum/ERCs/pull/1910#discussion_r4018175652) | The three submitted Solidity assets declare CC0-1.0. Product sources retain Apache-2.0. |
| [Reference implementation](https://github.com/ethereum/ERCs/pull/1910#discussion_r4018243131) | Describe only the Solidity registry and interface included in the official assets; omit product names and implementations not included there. |
| [RFC links](https://github.com/ethereum/ERCs/pull/1910#discussion_r4018243138) | Link RFC 2119 and RFC 8174 to rfc-editor.org. |
| [Complete vector inputs](https://github.com/ethereum/ERCs/pull/1910#discussion_r4018243147) | Add both authorization type strings and explicit replacement accounts. All three suites read these inputs. |
| [Optional helpers](https://github.com/ethereum/ERCs/pull/1910#discussion_r4018243155) | Mark each of the four extra helpers as optional and non-normative; keep deriveSpaceId normative and preserve the ABI. |

The earlier Sam revision is also synchronized into the product's ERC mirror,
including removal of the duplicated IERC1271 asset. No published deployment is
replaced or relabeled by this source revision.

## Licensing boundary

The CC0 change is limited to the three submitted Solidity assets and their copies
in `erc/assets/erc-8350/`, following the project representative's instruction for
this revision. The product LICENSE and package licenses are unchanged. Existing
source-attribution comments are retained. Git history identifies the original
ECDSA source submission as Everest's commit `a57bed8`; that history alone is not
an independent legal provenance assessment.

## Verification boundary

- ExperienceDelta field order, signing-domain values and hash algorithms are unchanged.
- No existing v1 input or expected output is changed; four explicit inputs are added.
- Core TS, minimal TS and Foundry validate the five type strings and replacement accounts.
- Foundry runs the same golden assertions against the actual submitted Solidity registry.
- The official vector and product canonical vector remain byte-for-byte copies.
- Source hashes and compiler metadata can change with comments or license headers.
Do not claim that deployment bytecode is unchanged on the basis of these tests.
- Frozen G4 source/evidence tags, manifests and existing reproduction records are
historical evidence and are not rewritten. They do not certify this revision.

## Reproduce

From a committed clean checkout with the documented toolchain:

```bash
pnpm conformance:clean
```

The command produces the source commit/tree, vector SHA-256, tool versions,
per-suite logs and pass/fail results under `artifacts/conformance/<commit>/`.
Run the official repository's document checks before upstream submission as well.
Editor approval, GitHub CI on the updated PR and merge are separate steps.
18 changes: 16 additions & 2 deletions erc/SUBMISSION.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,25 @@ pnpm check:erc-assets
```

`erc/assets/erc-8350/` mirrors upstream `assets/erc-8350/` (golden vector + the
dependency-free Solidity reference: registry, interface, ERC-1271 interface, ECDSA).
dependency-free Solidity reference: registry, interface, ECDSA). The duplicate
ERC-1271 interface was removed during the first editor review.

## Office Hour 113 revision

Jochem confirmed that Sam's six comments were addressed and requested CC0
licensing for the submitted Solidity assets. This revision also clarifies the
reference-implementation scope, links the RFCs, completes the v1 vector inputs,
and labels optional interface helpers. See [the revision record](../docs/reviews/office-hour-113.md).

The ERC text and submitted files under `erc/assets/erc-8350/` use
[CC0](./LICENSE.md). The product implementation under `contracts/src/` and the
TypeScript packages retain their existing licenses. This is not a repository-wide
license change.

## Remaining to Draft-merge and beyond

- [ ] ERC editor review on PR #1910 (number assignment applied 2026-07-29; awaiting one more editor review for Draft merge)
- [ ] Editor acceptance on PR #1910 after the Office Hour 113 revision (a local
revision or passing test run is not approval or merge)
- [x] Magicians topic retitled with the assigned number (done by the editor, 2026-07-29)
- [x] Sepolia deployment — registry `0xDdf21937ba80b5fF973610877A0955b320C91241`, see `docs/deployment.md` (2026-07-26)
- [x] Independent reproduction evidence — two external, commit-pinned executable suites pass; see `docs/interop/external-reproduction.md` (2026-07-31)
Expand Down
14 changes: 7 additions & 7 deletions erc/assets/erc-8350/AgentMemoryStateRegistry.sol
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.24;

import {ECDSA} from "./ECDSA.sol";
import {IAgentMemoryState} from "./IAgentMemoryState.sol";
import {IERC1271} from "./IERC1271.sol";

/// @title AgentMemoryStateRegistry
/// @notice Reference implementation of ExperienceDelta v1 and its linear state machine.
Expand Down Expand Up @@ -39,7 +38,7 @@ contract AgentMemoryStateRegistry is IAgentMemoryState {
"SpaceAuthorization(bytes32 spaceId,address newController,address newAuthorizer,uint64 nonce)"
);

bytes4 private constant _ERC1271_MAGIC_VALUE = IERC1271.isValidSignature.selector;
bytes4 private constant _ERC1271_MAGIC_VALUE = 0x1626ba7e;

struct SpaceRecord {
address controller;
Expand Down Expand Up @@ -325,10 +324,11 @@ contract AgentMemoryStateRegistry is IAgentMemoryState {
view
returns (bool)
{
(bool success, bytes memory result) =
signer.staticcall(abi.encodeCall(IERC1271.isValidSignature, (digest, signature)));
return
success && result.length >= 32 && abi.decode(result, (bytes4)) == _ERC1271_MAGIC_VALUE;
(bool success, bytes memory result) = signer.staticcall(
abi.encodeWithSelector(_ERC1271_MAGIC_VALUE, digest, signature)
);
return success && result.length >= 32
&& abi.decode(result, (bytes4)) == _ERC1271_MAGIC_VALUE;
}

function _digest(bytes32 structHash) private view returns (bytes32) {
Expand Down
2 changes: 1 addition & 1 deletion erc/assets/erc-8350/ECDSA.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.24;

/// @notice Minimal, dependency-free ECDSA recovery with malleability guard.
Expand Down
6 changes: 5 additions & 1 deletion erc/assets/erc-8350/IAgentMemoryState.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.24;

/// @title Agent Memory State
Expand Down Expand Up @@ -72,21 +72,25 @@ interface IAgentMemoryState {
view
returns (address controller, address authorizer, uint64 configNonce);

/// @dev Optional, non-normative convenience; not required for ERC-8350 conformance.
function transition(bytes32 transitionId)
external
view
returns (bytes32 spaceId, bytes32 nextStateRoot, uint64 sequence, uint64 committedAt);

/// @dev Optional, non-normative convenience; not required for ERC-8350 conformance.
function hashExperienceDelta(ExperienceDelta calldata delta)
external
pure
returns (bytes32 transitionId);

/// @dev Optional, non-normative convenience; not required for ERC-8350 conformance.
function computeNextStateRoot(bytes32 prevStateRoot, bytes32 transitionId)
external
pure
returns (bytes32 nextStateRoot);

/// @dev Optional, non-normative convenience; not required for ERC-8350 conformance.
function signingDigest(bytes32 structHash) external view returns (bytes32 digest);

function deriveSpaceId(address initialController, bytes32 salt)
Expand Down
10 changes: 0 additions & 10 deletions erc/assets/erc-8350/IERC1271.sol

This file was deleted.

6 changes: 5 additions & 1 deletion erc/assets/erc-8350/test-vectors-v1.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
"types": {
"experienceDelta": "ExperienceDelta(bytes32 spaceId,uint64 sequence,bytes32 prevStateRoot,bytes32 deltaCommitment,bytes32 provenanceCommitment,bytes32 profileId,bytes32 locatorCommitment)",
"memoryState": "MemoryState(bytes32 prevStateRoot,bytes32 transitionId)",
"memorySpace": "MemorySpace(address initialController,bytes32 salt)"
"memorySpace": "MemorySpace(address initialController,bytes32 salt)",
"spaceRegistration": "SpaceRegistration(bytes32 spaceId,address controller,address authorizer)",
"spaceAuthorization": "SpaceAuthorization(bytes32 spaceId,address newController,address newAuthorizer,uint64 nonce)"
},
"commitment": {
"payload": "{\"op\":\"upsert\",\"resourceId\":\"memory-42\",\"value\":\"prefers dark mode\"}",
Expand Down Expand Up @@ -47,6 +49,8 @@
"authorizer": "0x3333333333333333333333333333333333333333",
"spaceSalt": "0xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
"registrationId": "0x7bea30aa6018c9c8a3e66bd9573377af482dc791994af8c50698e3fc9d4e5d64",
"newController": "0x2222222222222222222222222222222222222222",
"newAuthorizer": "0x3333333333333333333333333333333333333333",
"updateNonce": "1",
"authorizationId": "0xa1fef7a8e52a533f7839f51962fa6e3ef9f8a8081efbac1eb13651801f4d1a92"
}
Expand Down
Loading
Loading