Skip to content

Commit

Permalink
move reference to 0glabs repo
Browse files Browse the repository at this point in the history
  • Loading branch information
Wilbert957 committed Jan 11, 2025
1 parent 71ca8cc commit 477173c
Showing 1 changed file with 22 additions and 286 deletions.
308 changes: 22 additions & 286 deletions ERCS/erc-7857.md
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -211,32 +206,33 @@ 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);
/// @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
Expand Down Expand Up @@ -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).

Check failure on line 294 in ERCS/erc-7857.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

non-relative link or image

error[markdown-rel-links]: non-relative link or image --> ERCS/erc-7857.md | 294 | For the reference implementation, please refer to 0G Labs' [0G Agent NFT](https://github.com/0glabs/0g-agent-nft/tree/eip-7844-draft). | = help: see https://ethereum.github.io/eipw/markdown-rel-links/

Check failure on line 294 in ERCS/erc-7857.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

non-relative link or image

error[markdown-rel-links]: non-relative link or image --> ERCS/erc-7857.md | 294 | For the reference implementation, please refer to 0G Labs' [0G Agent NFT](https://github.com/0glabs/0g-agent-nft/tree/eip-7844-draft). | = help: see https://ethereum.github.io/eipw/markdown-rel-links/

## Security Considerations

Expand Down

0 comments on commit 477173c

Please sign in to comment.