Skip to content

Commit 810ee26

Browse files
authored
GNS: cleanup and v1 (#199)
* GNS: cleanup and v1
1 parent 53f6cf6 commit 810ee26

File tree

7 files changed

+203
-306
lines changed

7 files changed

+203
-306
lines changed

contracts/GNS.sol

Lines changed: 73 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -4,162 +4,105 @@ pragma experimental ABIEncoderV2;
44
import "./Governed.sol";
55

66

7+
/**
8+
* @title GNS
9+
* @dev The Graph Name System contract provides a decentralized namings system for subgraphs
10+
* used in the scope of the Graph Network. It translate subgraph names into subgraphID regarded as
11+
* versions.
12+
*/
713
contract GNS is Governed {
8-
/* Events */
9-
event DomainAdded(bytes32 indexed topLevelDomainHash, address indexed owner, string domainName);
10-
event DomainTransferred(bytes32 indexed domainHash, address indexed newOwner);
11-
event SubgraphCreated(
12-
bytes32 indexed topLevelDomainHash,
13-
bytes32 indexed registeredHash,
14-
string subdomainName,
15-
address indexed owner
16-
);
17-
event SubgraphIDUpdated(bytes32 indexed domainHash, bytes32 indexed subgraphID);
18-
event DomainDeleted(bytes32 indexed domainHash);
19-
event AccountMetadataChanged(address indexed account, bytes32 indexed ipfsHash);
20-
event SubgraphMetadataChanged(bytes32 indexed domainHash, bytes32 indexed ipfsHash);
21-
22-
/* TYPES */
23-
struct Domain {
14+
// -- Types --
15+
16+
enum RecordType { GNS }
17+
18+
struct Record {
2419
address owner;
2520
bytes32 subgraphID;
21+
RecordType nameSystem;
2622
}
2723

28-
/* STATE VARIABLES */
29-
// Storage of a hashed top level domain to owners.
30-
mapping(bytes32 => Domain) public domains;
24+
// -- State --
3125

32-
/* Contract Constructor */
33-
/* @param _governor <address> - Address of the multisig contract as Governor */
34-
constructor(address _governor) public Governed(_governor) {}
26+
mapping(bytes32 => Record) public records;
3527

36-
/* Graph Protocol Functions */
28+
// -- Events --
3729

38-
modifier onlyDomainOwner(bytes32 _domainHash) {
39-
require(msg.sender == domains[_domainHash].owner, "Only domain owner can call.");
40-
_;
41-
}
30+
/**
31+
* @dev Emitted when `owner` publish a `subgraphID` version under subgraph `name`.
32+
* The event also attach `metadataHash` with extra information.
33+
*/
34+
event SubgraphPublished(string name, address owner, bytes32 subgraphID, bytes32 metadataHash);
4235

43-
/*
44-
* @notice Register a domain to an owner.
45-
* @param _domainName <string> - Domain name, which is treated as a username.
36+
/**
37+
* @dev Emitted when subgraph `nameHash` is unpublished by its owner.
4638
*/
47-
function registerDomain(string calldata _domainName) external {
48-
bytes32 hashedName = keccak256(abi.encodePacked(_domainName));
49-
// Require that this domain is not yet owned by anyone.
50-
require(domains[hashedName].owner == address(0), "Domain is already owned.");
51-
domains[hashedName].owner = msg.sender;
52-
emit DomainAdded(hashedName, msg.sender, _domainName);
53-
}
39+
event SubgraphUnpublished(bytes32 nameHash);
5440

55-
/*
56-
* @notice Create a subgraph by registering a subdomain, or registering the top level
57-
* domain as a subgraph.
58-
* @notice To register to the top level domain, pass _subdomainName as a blank string.
59-
* @dev Only the domain owner may do this.
60-
*
61-
* @param _topLevelDomainHash <bytes32> - Hash of the top level domain name.
62-
* @param _subdomainName <string> - Name of the Subdomain. If you were
63-
* registering 'david.thegraph', _subdomainName would be just 'david'.
64-
* @param _ipfsHash <bytes32> - Hash of the subgraph metadata, such as description.
41+
/**
42+
* @dev Emitted when subgraph `nameHash` is transferred to new owner.
6543
*/
66-
function createSubgraph(
67-
bytes32 _topLevelDomainHash,
68-
string calldata _subdomainName,
69-
bytes32 _ipfsHash
70-
) external onlyDomainOwner(_topLevelDomainHash) {
71-
bytes32 domainHash;
72-
bytes32 subdomainHash = keccak256(abi.encodePacked(_subdomainName));
73-
74-
// Subdomain is blank, therefore we are setting the subgraphID of the top level domain
75-
if (subdomainHash == keccak256("")) {
76-
// The domain hash ends up being the top level domain hash.
77-
domainHash = _topLevelDomainHash;
78-
} else {
79-
// The domain hash becomes the subdomain concatenated with the top level domain hash.
80-
domainHash = keccak256(abi.encodePacked(subdomainHash, _topLevelDomainHash));
81-
require(
82-
domains[domainHash].owner == address(0),
83-
"Someone already owns this subdomain."
84-
);
85-
domains[domainHash].owner = msg.sender;
86-
}
87-
88-
// Note - subdomain name and IPFS hash are only emitted through the events.
89-
// Note - if the subdomain is blank, the domain hash ends up being the top level
90-
// domain hash, not the hash of a blank string.
91-
emit SubgraphCreated(_topLevelDomainHash, domainHash, _subdomainName, msg.sender);
92-
emit SubgraphMetadataChanged(domainHash, _ipfsHash);
44+
event SubgraphTransferred(bytes32 nameHash, address from, address to);
45+
46+
modifier onlyRecordOwner(bytes32 _nameHash) {
47+
require(msg.sender == records[_nameHash].owner, "GNS: Only record owner can call");
48+
_;
9349
}
9450

95-
/*
96-
* @notice Update an existing subdomain with a subgraph ID.
97-
* @dev Only the domain owner may do this.
98-
*
99-
* @param _domainHash <bytes32> - Hash of the domain name.
100-
* @param _subgraphID <bytes32> - IPLD subgraph ID of the domain.
51+
/**
52+
* @dev Contract Constructor
53+
* @param _governor Owner address of this contract
10154
*/
102-
function updateDomainSubgraphID(bytes32 _domainHash, bytes32 _subgraphID)
103-
external
104-
onlyDomainOwner(_domainHash)
105-
{
55+
constructor(address _governor) public Governed(_governor) {}
56+
57+
/**
58+
* @dev Publish a version using `subgraphID` under a subgraph name.
59+
* @param _name Name of the subgraph
60+
* @param _subgraphID Subgraph ID to link to the subgraph name
61+
* @param _metadataHash IPFS hash linked to the metadata
62+
*/
63+
function publish(
64+
string calldata _name,
65+
bytes32 _subgraphID,
66+
bytes32 _metadataHash
67+
) external {
68+
address owner = msg.sender;
69+
bytes32 nameHash = keccak256(bytes(_name));
10670
require(
107-
_subgraphID != bytes32(0),
108-
"If you want to reset the subgraphID, call deleteSubdomain."
71+
!isReserved(nameHash) || records[nameHash].owner == owner,
72+
"GNS: Record reserved, only record owner can publish"
10973
);
110-
domains[_domainHash].subgraphID = _subgraphID;
111-
emit SubgraphIDUpdated(_domainHash, _subgraphID);
112-
}
11374

114-
/*
115-
* @notice Remove an existing domain owner and subgraphID
116-
* @dev Only the domain owner may do this.
117-
*
118-
* @param _domainHash <bytes32> - Hash of the domain name.
119-
*/
120-
function deleteSubdomain(bytes32 _domainHash) external onlyDomainOwner(_domainHash) {
121-
delete domains[_domainHash];
122-
emit DomainDeleted(_domainHash);
75+
records[nameHash] = Record(owner, _subgraphID, RecordType.GNS);
76+
emit SubgraphPublished(_name, owner, _subgraphID, _metadataHash);
12377
}
12478

125-
/*
126-
* @notice Transfer ownership of domain by existing domain owner.
127-
* @dev Only the domain owner may do this.
128-
*
129-
* @param _domainHash <bytes32> - Hash of the domain name.
130-
* @param _newOwner <address> - New owner of the domain.
79+
/**
80+
* @dev Unpublish a subgraph name. Can only be done by the owner.
81+
* @param _nameHash Keccak256 hash of the subgraph name
13182
*/
132-
function transferDomainOwnership(bytes32 _domainHash, address _newOwner)
133-
external
134-
onlyDomainOwner(_domainHash)
135-
{
136-
require(_newOwner != address(0), "If you want to reset the owner, call deleteSubdomain.");
137-
domains[_domainHash].owner = _newOwner;
138-
emit DomainTransferred(_domainHash, _newOwner);
83+
function unpublish(bytes32 _nameHash) external onlyRecordOwner(_nameHash) {
84+
delete records[_nameHash];
85+
emit SubgraphUnpublished(_nameHash);
13986
}
14087

141-
/*
142-
* @notice Change or initalize the Account Metadata, which is stored in a schema on IPFS.
143-
* @dev Only the msg.sender can do this.
144-
*
145-
* @param _ipfsHash <bytes32> - Hash of the IPFS file that stores the account metadata.
146-
* @param _account <address> - msg.sender.
88+
/**
89+
* @dev Tranfer the subgraph name to a new owner.
90+
* @param _nameHash Keccak256 hash of the subgraph name
91+
* @param _to Address of the new owner
14792
*/
148-
function changeAccountMetadata(bytes32 _ipfsHash) external {
149-
emit AccountMetadataChanged(msg.sender, _ipfsHash);
93+
function transfer(bytes32 _nameHash, address _to) external onlyRecordOwner(_nameHash) {
94+
require(_to != address(0), "GNS: Cannot transfer to empty address");
95+
require(records[_nameHash].owner != _to, "GNS: Cannot transfer to itself");
96+
records[_nameHash].owner = _to;
97+
emit SubgraphTransferred(_nameHash, msg.sender, _to);
15098
}
15199

152-
/*
153-
* @notice Change or initalize the Account Metadata, which is stored in a schema on IPFS.
154-
* @dev Only the msg.sender can do this.
155-
*
156-
* @param _ipfsHash <bytes32> - Hash of the IPFS file that stores the subgraph metadata.
157-
* @param _domainHash <bytes32> - Hash of the domain name.
100+
/**
101+
* @dev Return whether a subgraph name is registed or not.
102+
* @param _nameHash Keccak256 hash of the subgraph name
103+
* @return Return true if subgraph name exists
158104
*/
159-
function changeSubgraphMetadata(bytes32 _domainHash, bytes32 _ipfsHash)
160-
public
161-
onlyDomainOwner(_domainHash)
162-
{
163-
emit SubgraphMetadataChanged(_domainHash, _ipfsHash);
105+
function isReserved(bytes32 _nameHash) public view returns (bool) {
106+
return records[_nameHash].owner != address(0);
164107
}
165108
}

test/curation.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ contract('Curation', ([me, other, governor, curator, staking]) => {
3939
await this.grt.approve(this.curation.address, this.curatorTokens, { from: staking })
4040

4141
// Randomize a subgraphId
42-
this.subgraphId = helpers.randomSubgraphIdHex0x()
42+
this.subgraphId = helpers.randomSubgraphId()
4343
})
4444

4545
describe('configuration', function() {
@@ -146,7 +146,7 @@ contract('Curation', ([me, other, governor, curator, staking]) => {
146146

147147
context('> bonding curve', function() {
148148
beforeEach(function() {
149-
this.subgraphId = helpers.randomSubgraphIdHex0x()
149+
this.subgraphId = helpers.randomSubgraphId()
150150
})
151151

152152
it('convert shares to tokens', async function() {

test/disputes.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ contract('Disputes', ([me, other, governor, arbitrator, indexer, fisherman, othe
205205
const receipt = {
206206
requestCID: web3.utils.randomHex(32),
207207
responseCID: web3.utils.randomHex(32),
208-
subgraphID: helpers.randomSubgraphIdHex0x(),
208+
subgraphID: helpers.randomSubgraphId(),
209209
}
210210
this.dispute = await attestation.createDispute(
211211
receipt,

0 commit comments

Comments
 (0)