Skip to content
This repository has been archived by the owner on Nov 7, 2023. It is now read-only.

Commit

Permalink
Merge pull request #33 from rnsdomains/docs
Browse files Browse the repository at this point in the history
Docs
  • Loading branch information
ilanolkies authored Nov 21, 2019
2 parents 30dccdd + 1e5f87c commit 848fa92
Show file tree
Hide file tree
Showing 3 changed files with 280 additions and 99 deletions.
76 changes: 69 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,81 @@
# rns-fifs
First-in first-served registrar for rsk top level domain.
# RSK Registrar

RNS Registrar for _rsk_ top level domain.<sup>1</sup>

## Run tests
## Contracts

The registrar is separated into several components for simplicity, modularity, and privilege minimization.

### RSK Owner

Owner of _rsk_ top level domain. It can `setSubdomainOwner` in RNS.

- It represents domain ownership implementing ERC-721<sup>1</sup> non-fungible token standard. This standard provides basic functionality to track and transfer NFTs<sup>2</sup>.
- Stores domains' expiration time. The expiration time determines wether a domain is owned or not.
- Determines if a domain is available to be purchased.
- Accepts domain ownership clamming from previous _rsk_ registrar.
- Grants access to other contracts for registering new domains (registrar role)<sup>2</sup>.
- Grants access to other contracts for renewing domains (renewer role)<sup>2</sup>.
- Allows to reclaim ownership in RNS of owned domains.
- It has an owner that can<sup>2</sup>
- Change _rsk_ tld resolver and ttl.
- Add/remove registrar contracts.
- Add/remove renewer contracts.

### FIFS Registrar

Has registration role in `RSK Owner`.

- Defines a commit-reveal process to avoid front-running.
- Accepts payments via
- ERC-20 `approve()` + `register()`.<sup>3</sup>
- ERC-721 `transferAndCall()`.<sup>4</sup>
- Calculates price using `NamePrice` contract.
- It has an owner that can<sup>2</sup>
- Set minimum commitment age.
- Set minimum registration name length available.
- Change name price contract.

The registration must be performed as follows:

0. Calculate `makeCommitment` hash of the domain to be registered (off-chain).
1. Commit the calculated hash using `commit`.
2. Wait `minCommitmentAge` seconds.
3. Execute registration via ERC-20 (with approval) or ERC-677.

> Find `transferAndCall()` encoder in `utils/index.js`
### Name Price

Determines the price of a domain.

| Years | Price |
| - | - |
| 1 | 2 RIF |
| 2 | 4 RIF |
| 2+k | 4+k RIF |

> For example, 5 years cost 7 RIF.
## Setup

Unit tests:
```
truffle test
npm install
```

> Get truffle: https://www.trufflesuite.com/
## Run tests

Static analyzes:
```
truffle test
slither .
```

> Get truffle: https://www.trufflesuite.com/
> Get slither:https://github.com/crytic/slither
## References

1. Strongly based on https://github.com/ensdomains/ethregistrar.
2. https://github.com/OpenZeppelin/openzeppelin-contracts implementation.
3. https://eips.ethereum.org/EIPS/eip-20
4. https://github.com/ethereum/EIPs/issues/677
161 changes: 91 additions & 70 deletions contracts/FIFSRegistrar.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,9 @@ import "@ensdomains/ethregistrar/contracts/StringUtils.sol";
import "./testing/ERC677Receiver.sol";
import "./BytesUtils.sol";

/// @title First-in first-served registrar
/// @title First-in first-served registrar.
/// @notice You can use this contract to register .rsk names in RNS.
/// First make a commitment of the name to be registered, wait 1
/// minute, and proceed to register the name.
/// @dev This contract has permission to register in RSK Owner
/// @dev This contract has permission to register in RSK Owner.
contract FIFSRegistrar is PricedContract, ERC677Receiver {
using SafeMath for uint256;
using StringUtils for string;
Expand Down Expand Up @@ -42,73 +40,111 @@ contract FIFSRegistrar is PricedContract, ERC677Receiver {
pool = _pool;
}

/// @notice Create a commitment for register action
/// @dev Don't use this method on-chain when commiting
/// @param label keccak256 of the name to be registered
/// @param nameOwner Owner of the name to be registered
/// @param secret Secret to protect the name to be registered
/// @return The commitment hash
///////////////////
// COMMIT-REVEAL //
///////////////////

/*
0. Caclulate makeCommitment hash of the domain to be registered (off-chain)
1. Commit the calculated hash
2. Wait minCommitmentAge
3. Execute registration via:
- ERC-20 with approve() + register()
- ERC-677 with transferAndCall()
The price of a domain is given by name price contract.
*/

// 0.
/// @notice Create a commitment for register action.
/// @dev Don't use this method on-chain when commiting.
/// @param label keccak256 of the name to be registered.
/// @param nameOwner Owner of the name to be registered.
/// @param secret Secret to protect the name to be registered.
/// @return The commitment hash.
function makeCommitment (bytes32 label, address nameOwner, bytes32 secret) public pure returns (bytes32) {
return keccak256(abi.encodePacked(label, nameOwner, secret));
}

/// @notice Commit before registring a name
/// @dev A valid commitment can be calculated using makeCommitment off-chain
/// @param commitment A valid commitment hash
// 1.
/// @notice Commit before registring a name.
/// @dev A valid commitment can be calculated using makeCommitment off-chain.
/// @param commitment A valid commitment hash.
function commit(bytes32 commitment) external {
require(commitmentRevealTime[commitment] < 1, "Existent commitment");
commitmentRevealTime[commitment] = now.add(minCommitmentAge);
}

/// @notice Ensure the commitment is ready to be revealed
/// @param commitment Commitment to be queried
/// @return Wether the commitment can be revealed or not
// 2.
/// @notice Ensure the commitment is ready to be revealed.
/// @dev This method can be polled to ensure registration.
/// @param commitment Commitment to be queried.
/// @return Wether the commitment can be revealed or not.
function canReveal(bytes32 commitment) public view returns (bool) {
uint revealTime = commitmentRevealTime[commitment];
return 0 < revealTime && revealTime <= now;
}

/// @notice Registers a .rsk name in RNS
/// @dev This method must be called after commiting
/// @param name The name to register
/// @param nameOwner The owner of the name to regiter
/// @param secret The secret used to make the commitment
/// @param duration Time to register in years
// 3.

// - Via ERC-20
/// @notice Registers a .rsk name in RNS.
/// @dev This method must be called after commiting.
/// @param name The name to register.
/// @param nameOwner The owner of the name to regiter.
/// @param secret The secret used to make the commitment.
/// @param duration Time to register in years.
function register(string calldata name, address nameOwner, bytes32 secret, uint duration) external {
uint cost = executeRegistration(name, nameOwner, secret, duration);
require(rif.transferFrom(msg.sender, pool, cost), "Token transfer failed");
}

/// @notice Change required commitment maturity
/// @dev Only owner
/// @param newMinCommitmentAge The new maturity required
function setMinCommitmentAge (uint newMinCommitmentAge) external onlyOwner {
minCommitmentAge = newMinCommitmentAge;
}
// - Via ERC-677
/* Encoding:
| signature | 4 bytes - offset 0
| owner | 20 bytes - offset 4
| secret | 32 bytes - offest 24
| duration | 32 bytes - offset 56
| name | variable size - offset 88
*/

/// @notice Change disbaled names
/// @dev Only owner
/// @param newMinLength The new minimum length enabled
function setMinLength (uint newMinLength) external onlyOwner {
minLength = newMinLength;
/// @notice ERC-677 token fallback function.
/// @dev Follow 'Register encoding' to execute a one-transaction regitration.
/// @param from token sender.
/// @param value amount of tokens sent.
/// @param data data associated with transaction.
/// @return true if successfull.
function tokenFallback(address from, uint value, bytes calldata data) external returns (bool) {
require(msg.sender == address(rif), "Only RIF token");
require(data.length > 88, "Invalid data");

bytes4 signature = data.toBytes4(0);

require(signature == REGISTER_SIGNATURE, "Invalid signature");

address nameOwner = data.toAddress(4);
bytes32 secret = data.toBytes32(24);
uint duration = data.toUint(56);
string memory name = data.toString(88, data.length.sub(88));

registerWithToken(name, nameOwner, secret, duration, from, value);

return true;
}

function registerWithToken(string memory name, address nameOwner, bytes32 secret, uint duration, address from, uint amount) private {
uint cost = executeRegistration(name, nameOwner, secret, duration);
require(amount >= cost, "Not enough tokens");
require(rif.transfer(pool, cost), "Token transfer failed");
// Calculated twise because the common case is the exact amount is sent. No variables.
if (amount.sub(cost) > 0)
require(rif.transfer(from, amount.sub(cost)), "Token transfer failed");
}

/// @notice Executes registration without any payments.
/// @dev This method is used to abstract from payment method.
/// @param name The name to register
/// @param nameOwner The owner of the name to regiter
/// @param secret The secret used to make the commitment
/// @param duration Time to register in years
/// @return price Price of the name to register
/// @notice Executes registration abstracted from payment method.
/// @param name The name to register.
/// @param nameOwner The owner of the name to regiter.
/// @param secret The secret used to make the commitment.
/// @param duration Time to register in years.
/// @return price Price of the name to register.
function executeRegistration (string memory name, address nameOwner, bytes32 secret, uint duration) private returns (uint) {
bytes32 label = keccak256(abi.encodePacked(name));

Expand All @@ -123,36 +159,21 @@ contract FIFSRegistrar is PricedContract, ERC677Receiver {
return price(name, rskOwner.expirationTime(uint(label)), duration);
}

/**
Register encoding:
| signature | 4 bytes - offset 0
| owner | 20 bytes - offset 4
| secret | 32 bytes - offest 24
| duration | 32 bytes - offset 56
| name | variable size - offset 88
*/

/// @notice ERC-677 token fallback function
/// @dev Follow 'Register encoding' to execute a one-transaction regitration.
/// @param from token sender
/// @param value amount of tokens sent
/// @param data data associated with transaction
/// @return true if successfull
function tokenFallback(address from, uint value, bytes calldata data) external returns (bool) {
require(msg.sender == address(rif), "Only RIF token");
require(data.length > 88, "Invalid data");

bytes4 signature = data.toBytes4(0);

require(signature == REGISTER_SIGNATURE, "Invalid signature");

address nameOwner = data.toAddress(4);
bytes32 secret = data.toBytes32(24);
uint duration = data.toUint(56);
string memory name = data.toString(88, data.length.sub(88));
/////////////////////
// REGISTRAR ADMIN //
/////////////////////

registerWithToken(name, nameOwner, secret, duration, from, value);
/// @notice Change required commitment maturity.
/// @dev Only owner.
/// @param newMinCommitmentAge The new maturity required.
function setMinCommitmentAge (uint newMinCommitmentAge) external onlyOwner {
minCommitmentAge = newMinCommitmentAge;
}

return true;
/// @notice Change disbaled names.
/// @dev Only owner.
/// @param newMinLength The new minimum length enabled.
function setMinLength (uint newMinLength) external onlyOwner {
minLength = newMinLength;
}
}
Loading

0 comments on commit 848fa92

Please sign in to comment.