From 477173cc3bf69c7bff3bcd33bb208c5d36a6341d Mon Sep 17 00:00:00 2001 From: wei Date: Sat, 11 Jan 2025 11:33:38 +0800 Subject: [PATCH] move reference to 0glabs repo --- ERCS/erc-7857.md | 308 ++++------------------------------------------- 1 file changed, 22 insertions(+), 286 deletions(-) diff --git a/ERCS/erc-7857.md b/ERCS/erc-7857.md index a4f4385d69..98ead75436 100644 --- a/ERCS/erc-7857.md +++ b/ERCS/erc-7857.md @@ -1,7 +1,7 @@ --- eip: 7857 -title: NFT Interface for AI Agents -description: Interface for AI agent NFTs, enabling secure management of private metadata with verifiable data transfer +title: AI Agents NFT with Private Metadata +description: AI agent NFTs, enabling secure management of private metadata with verifiable data transfer author: Ming Wu (@sparkmiw), Jason Zeng (@zenghbo), Wei Wu (@Wilbert957), Michael Heinrich (@michaelomg) discussions-to: https://ethereum-magicians.org/t/erc-7857-an-nft-standard-for-ai-agents-with-private-metadata/22391 status: Draft @@ -24,7 +24,7 @@ AI agents possess inherent non-fungible properties that make them natural candid 2. Agents embody clear ownership rights, representing significant computational investment and intellectual property 3. Agents have private metadata (e.g., neural network models, memory, character definitions) that defines their capabilities -However, current NFT standards like [ERC-721](https://github.com/ethereum/ERCs/blob/master/ERCS/erc-721.md) are insufficient for representing AI agents effectively. The key challenge lies in the metadata transfer mechanism. Unlike traditional NFTs where metadata is typically static and publicly accessible, an agent's metadata: +However, current NFT standards like [ERC-721](./erc-721.md) are insufficient for representing AI agents effectively. The key challenge lies in the metadata transfer mechanism. Unlike traditional NFTs where metadata is typically static and publicly accessible, an agent's metadata: 1. Has intrinsic value and is often the primary purpose of the transfer 2. Requires encrypted storage to protect intellectual property @@ -140,32 +140,26 @@ function name() external view returns (string memory); /// @notice Get the symbol of the NFT collection function symbol() external view returns (string memory); -/// @notice Get the config metadata for the NFT collection -/// @return storageURL The base storage URL for the functional data -function config() external view returns ( - string memory storageURL -); - -/// @notice Set the config metadata (only owner) -function setConfig( - string memory storageURL -) external; - /// @notice Get the metadata URI for a specific token function tokenURI(uint256 tokenId) external view returns (string memory); /// @notice Update data /// @param _tokenId The token to update -/// @param _proof Proof of updated data ownership +/// @param _proofs Proof of updated data ownership function update( uint256 _tokenId, - bytes calldata _proof + bytes[] calldata _proofs ) external; /// @notice Get the data hash of a token /// @param _tokenId The token identifier /// @return The current data hash of the token -function dataHashesOf(uint256 _tokenId) external view returns (bytes32[] memory); +function dataHashesOf(uint256 _tokenId) public view returns (bytes32[] memory); + +/// @notice Get the data description of a token +/// @param _tokenId The token identifier +/// @return The current data description of the token +function dataDescriptionsOf(uint256 _tokenId) public view returns (string[] memory); ``` ### Main NFT Interface @@ -175,7 +169,8 @@ function dataHashesOf(uint256 _tokenId) external view returns (bytes32[] memory) event Minted( uint256 indexed _tokenId, address indexed _creator, - bytes32[] _dataHashes + bytes32[] _dataHashes, + string[] _dataDescriptions ); /// @dev This emits when a user is authorized to use the data @@ -211,9 +206,10 @@ event PublishedSealedKey( function verifier() external view returns (IDataVerifier); /// @notice Mint new NFT with data ownership proof -/// @param _proof Proof of data ownership +/// @param _proofs Proofs of data ownership +/// @param _dataDescriptions Descriptions of the data /// @return _tokenId The ID of the newly minted token -function mint(bytes calldata _proof) +function mint(bytes[] calldata _proofs, string[] calldata _dataDescriptions) external payable returns (uint256 _tokenId); @@ -221,22 +217,22 @@ function mint(bytes calldata _proof) /// @notice Transfer full data (full means data and ownership) /// @param _to Address to transfer data to /// @param _tokenId The token to transfer data for -/// @param _proof Proof of data available for _to +/// @param _proofs Proofs of data available for _to function transfer( address _to, uint256 _tokenId, - bytes calldata _proof + bytes[] calldata _proofs ) external; /// @notice Clone data (clone means just data, not ownership) /// @param _to Address to clone data to /// @param _tokenId The token to clone data for -/// @param _proof Proof of data available for _to +/// @param _proofs Proofs of data available for _to /// @return _newTokenId The ID of the newly cloned token function clone( address _to, uint256 _tokenId, - bytes calldata _proof + bytes[] calldata _proofs ) external payable returns (uint256 _newTokenId); /// @notice Transfer public data with ownership @@ -292,270 +288,10 @@ The design choices in this standard are motivated by several key requirements: ## Backwards Compatibility -This EIP does not inherit from existing NFT standards to maintain its focus on functional data management. However, implementations can choose to additionally implement [ERC-721](https://github.com/ethereum/ERCs/blob/master/ERCS/erc-721.md) if traditional NFT compatibility is desired. +This EIP does not inherit from existing NFT standards to maintain its focus on functional data management. However, implementations can choose to additionally implement [ERC-721](./erc-721.md) if traditional NFT compatibility is desired. ## Reference Implementation - -```solidity -abstract contract BaseVerifier is IDataVerifier { - // prevent replay attack - mapping(bytes32 => bool) internal usedProofs; - - // check and mark proof used - function _checkAndMarkProof(bytes32 proofHash) internal { - require(!usedProofs[proofHash], "Proof already used"); - usedProofs[proofHash] = true; - } -} - -contract TEEVerifier is BaseVerifier { - address public immutable attestationContract; - - constructor(address _attestationContract) { - attestationContract = _attestationContract; - } - - /// @notice Verify ownership of data, the _proof prove: - /// 1. The pre-image of dataHashes - /// @param proof Proof generated by TEE - function verifyOwnership( - bytes calldata proof - ) external view override returns (OwnershipProofOutput memory) { - // TODO: Implement actual verification logic - return OwnershipProofOutput(new bytes32[](0), true); - } - - /// @notice Verify data transfer validity, the _proof prove: - /// 1. The pre-image of oldDataHashes - /// 2. The oldKey can decrypt the pre-image and the new key re-encrypt the plaintexts to new ciphertexts - /// 3. The newKey is encrypted using the receiver's pubKey - /// 4. The hashes of new ciphertexts is newDataHashes (key to note: TEE could support a private key of the receiver) - /// 5. The newDataHashes identified ciphertexts are available in the storage: need the signature from the receiver signing oldDataHashes and newDataHashes - /// @param proof Proof generated by TEE - function verifyTransferValidity( - bytes calldata proof - ) external view override returns (TransferValidityProofOutput memory) { - // TODO: Implement actual verification logic - return TransferValidityProofOutput(new bytes32[](0), new bytes32[](0), bytes("null"), bytes("null"), true); - } -} - -contract AgentNFT { - struct TokenData { - address owner; - bytes32[] dataHashes; - address[] authorizedUsers; - } - - IDataVerifier private immutable _verifier; - mapping(uint256 => TokenData) private _tokens; - uint256 private _nextTokenId; - - string private _name; - string private _symbol; - string private _storageURL; - address private immutable _owner; - - constructor( - string memory name_, - string memory symbol_, - address verifierAddr, - string memory storageURL_ - ) { - require(verifierAddr != address(0), "Zero address"); - _verifier = IDataVerifier(verifierAddr); - _name = name_; - _symbol = symbol_; - _storageURL = storageURL_; - _owner = msg.sender; - } - - function name() external view returns (string memory) { - return _name; - } - - function symbol() external view returns (string memory) { - return _symbol; - } - - function config() external view returns (string memory) { - return _storageURL; - } - - function setConfig(string memory storageURL) external { - require(msg.sender == _owner, "Not owner"); - _storageURL = storageURL; - } - - function tokenURI(uint256 tokenId) external view returns (string memory) { - require(_exists(tokenId), "Token does not exist"); - return string(abi.encodePacked(_storageURL)); - } - - function update( - uint256 tokenId, - bytes calldata proof - ) external { - TokenData storage token = _tokens[tokenId]; - require(token.owner == msg.sender, "Not owner"); - - OwnershipProofOutput memory proofOupt = _verifier.verifyOwnership(proof); - bytes32[] memory newDataHashes = proofOupt.dataHashes; - require( - proofOupt.isValid, - "Invalid ownership proof" - ); - - bytes32[] memory oldDataHashes = token.dataHashes; - token.dataHashes = newDataHashes; - - emit Updated(tokenId, oldDataHashes, newDataHashes); - } - - function dataHashesOf(uint256 tokenId) external view returns (bytes32[] memory) { - TokenData storage token = _tokens[tokenId]; - require(token.owner != address(0), "Token not exist"); - return token.dataHashes; - } - - function verifier() external view returns (IDataVerifier) { - return _verifier; - } - - function mint(bytes calldata proof) - external - payable - returns (uint256 tokenId) - { - OwnershipProofOutput memory proofOupt = _verifier.verifyOwnership(proof); - bytes32[] memory dataHashes = proofOupt.dataHashes; - require( - proofOupt.isValid, - "Invalid ownership proof" - ); - - tokenId = _nextTokenId++; - _tokens[tokenId] = TokenData({ - owner: msg.sender, - dataHashes: dataHashes, - authorizedUsers: new address[](0) - }); - - emit Minted(tokenId, msg.sender, dataHashes); - } - - function transfer( - address to, - uint256 tokenId, - bytes calldata proof - ) external { - require(to != address(0), "Zero address"); - - TokenData storage token = _tokens[tokenId]; - require(token.owner == msg.sender, "Not owner"); - - TransferValidityProofOutput memory proofOupt = _verifier.verifyTransferValidity(proof); - bytes32[] memory oldDataHashes = proofOupt.oldDataHashes; - bytes32[] memory newDataHashes = proofOupt.newDataHashes; - bytes32[] memory tokenDataHashes = token.dataHashes; - bytes memory pubKey = proofOupt.pubKey; - bytes memory sealedKey = proofOupt.sealedKey; - - require( - proofOupt.isValid - && _isEqual(oldDataHashes, tokenDataHashes) - && _pubKeyToAddress(pubKey) == to, - "Invalid transfer validity proof" - ); - - token.owner = to; - token.dataHashes = newDataHashes; - - emit Transferred(tokenId, msg.sender, to); - emit PublishedSealedKey(to, tokenId, sealedKey); - } - - function transferPublic( - address to, - uint256 tokenId - ) external { - require(to != address(0), "Zero address"); - require(_tokens[tokenId].owner == msg.sender, "Not owner"); - _tokens[tokenId].owner = to; - emit Transferred(tokenId, msg.sender, to); - } - - function clone( - address to, - uint256 tokenId, - bytes calldata proof - ) external payable returns (uint256) { - require(to != address(0), "Zero address"); - - require(_tokens[tokenId].owner == msg.sender, "Not owner"); - - TransferValidityProofOutput memory proofOupt = _verifier.verifyTransferValidity(proof); - bytes32[] memory oldDataHashes = proofOupt.oldDataHashes; - bytes32[] memory newDataHashes = proofOupt.newDataHashes; - bytes32[] memory tokenDataHashes = _tokens[tokenId].dataHashes; - bytes memory pubKey = proofOupt.pubKey; - bytes memory sealedKey = proofOupt.sealedKey; - - require( - proofOupt.isValid - && _isEqual(oldDataHashes, tokenDataHashes) - && _pubKeyToAddress(pubKey) == to, - "Invalid transfer validity proof" - ); - - uint256 newTokenId = _nextTokenId++; - _tokens[newTokenId] = TokenData({ - owner: to, - dataHashes: newDataHashes, - authorizedUsers: new address[](0) - }); - - emit Cloned(tokenId, newTokenId, msg.sender, to); - emit PublishedSealedKey(to, newTokenId, sealedKey); - return newTokenId; - } - - function clonePublic( - address to, - uint256 tokenId - ) external payable returns (uint256) { - require(to != address(0), "Zero address"); - require(_tokens[tokenId].owner == msg.sender, "Not owner"); - - uint256 newTokenId = _nextTokenId++; - _tokens[newTokenId] = TokenData({ - owner: to, - dataHashes: _tokens[tokenId].dataHashes, - authorizedUsers: new address[](0) - }); - emit Cloned(tokenId, newTokenId, msg.sender, to); - return newTokenId; - } - - function authorizeUsage(uint256 tokenId, address user) external { - require(_tokens[tokenId].owner == msg.sender, "Not owner"); - _tokens[tokenId].authorizedUsers.push(user); - emit AuthorizedUsage(tokenId, user); - } - - function ownerOf(uint256 tokenId) external view returns (address) { - TokenData storage token = _tokens[tokenId]; - require(token.owner != address(0), "Token not exist"); - return token.owner; - } - - function authorizedUsersOf(uint256 tokenId) external view returns (address[] memory) { - TokenData storage token = _tokens[tokenId]; - require(token.owner != address(0), "Token not exist"); - return token.authorizedUsers; - } -} -``` +For the reference implementation, please refer to 0G Labs' [0G Agent NFT](https://github.com/0glabs/0g-agent-nft/tree/eip-7844-draft). ## Security Considerations