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 #42 from rnsdomains/fifs-with-addr
Browse files Browse the repository at this point in the history
Fifs with addr
  • Loading branch information
ilanolkies authored Jan 31, 2020
2 parents ef55791 + 13bce66 commit 501ca63
Show file tree
Hide file tree
Showing 10 changed files with 1,264 additions and 80 deletions.
1 change: 1 addition & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ jobs:
command: |
truffle test ./test/fifs/fifs_registrar_rskowner.test.js
truffle test ./test/fifs/fifs_registrar.test.js
truffle test ./test/fifs/fifs_addr_registrar.test.js
truffle test ./test/nodeowner/nodeowner_erc721.test.js
truffle test ./test/nodeowner/nodeowner_reclaim.test.js
truffle test ./test/nodeowner/nodeowner_registrar_role.test.js
Expand Down
1 change: 1 addition & 0 deletions contracts/Dummy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pragma solidity ^0.5.3;
import "@rsksmart/rns-registry/contracts/RNS.sol";
import "@rsksmart/rns-auction-registrar/contracts/TokenRegistrar.sol";
import "@rsksmart/erc677/contracts/ERC677.sol";
import "@rsksmart/rns-resolver/contracts/PublicResolver.sol";

contract Dummy {
}
146 changes: 146 additions & 0 deletions contracts/FIFSAddrRegistrar.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
pragma solidity ^0.5.3;

import "@rsksmart/rns-registry/contracts/AbstractRNS.sol";
import "@rsksmart/rns-resolver/contracts/AbstractAddrResolver.sol";
import "./FIFSRegistrarBase.sol";
import "./PricedContract.sol";

/// @title First-in first-served registrar with automatic addr setup.
/// @notice You can use this contract to register names in RNS with addr
/// resolution set automatcially.
/// @dev This contract has permission to register in Node Owner.
contract FIFSAddrRegistrar is FIFSRegistrarBase, PricedContract {
address pool;
AbstractRNS rns;
bytes32 rootNode;

// sha3('register(string,address,bytes32,uint,address)')
bytes4 constant REGISTER_SIGNATURE = 0x5f7b99d5;

constructor (
ERC677 _rif,
NodeOwner _nodeOwner,
address _pool,
AbstractNamePrice _namePrice,
AbstractRNS _rns,
bytes32 _rootNode
) public FIFSRegistrarBase(_rif, _nodeOwner) PricedContract(_namePrice) {
pool = _pool;
rns = _rns;
rootNode = _rootNode;
}

/*
3. Execute registration via:
- ERC-20 with approve() + register()
- ERC-677 with transferAndCall()
The price of a domain is given by name price contract.
*/

// - 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.
/// @param addr Address to set as addr resolution.
function register(
string calldata name,
address nameOwner,
bytes32 secret,
uint duration,
address addr
) external {
uint cost = executeRegistration(name, nameOwner, secret, duration, addr);
require(rif.transferFrom(msg.sender, pool, cost), "Token transfer failed");
}

// - Via ERC-677
/* Encoding:
| signature | 4 bytes - offset 0
| owner | 20 bytes - offset 4
| secret | 32 bytes - offest 24
| duration | 32 bytes - offset 56
| duration | 20 bytes - offset 88
| name | variable size - offset 108
*/

/// @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 > 108, "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);
address addr = data.toAddress(88);
string memory name = data.toString(108, data.length.sub(108));

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

return true;
}

function registerWithToken(
string memory name,
address nameOwner,
bytes32 secret,
uint duration,
address from,
uint amount,
address addr
) private {
uint cost = executeRegistration(name, nameOwner, secret, duration, addr);
require(amount >= cost, "Not enough tokens");
require(rif.transfer(pool, cost), "Token transfer failed");
if (amount.sub(cost) > 0)
require(rif.transfer(from, amount.sub(cost)), "Token transfer failed");
}

/// @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.
/// @param addr Address to set as addr resolution.
/// @return price Price of the name to register.
function executeRegistration (
string memory name,
address nameOwner,
bytes32 secret,
uint duration,
address addr
) private returns (uint) {
bytes32 label = keccak256(abi.encodePacked(name));
uint256 tokenId = uint256(label);

require(name.strlen() >= minLength, "Short names not available");

bytes32 commitment = makeCommitment(label, nameOwner, secret);
require(canReveal(commitment), "No commitment found");
commitmentRevealTime[commitment] = 0;

nodeOwner.register(label, address(this), duration.mul(365 days));

AbstractAddrResolver(rns.resolver(rootNode))
.setAddr(
keccak256(abi.encodePacked(rootNode, label)),
addr
);

nodeOwner.reclaim(tokenId, nameOwner);
nodeOwner.transferFrom(address(this), nameOwner, tokenId);

return price(name, nodeOwner.expirationTime(uint(label)), duration);
}
}
84 changes: 4 additions & 80 deletions contracts/FIFSRegistrar.sol
Original file line number Diff line number Diff line change
@@ -1,29 +1,12 @@
pragma solidity ^0.5.3;

import "@openzeppelin/contracts/math/SafeMath.sol";
import "@ensdomains/ethregistrar/contracts/StringUtils.sol";
import "@rsksmart/erc677/contracts/ERC677.sol";
import "@rsksmart/erc677/contracts/ERC677TransferReceiver.sol";
import "./NodeOwner.sol";
import "./FIFSRegistrarBase.sol";
import "./PricedContract.sol";
import "./AbstractNamePrice.sol";
import "./BytesUtils.sol";

/// @title First-in first-served registrar.
/// @notice You can use this contract to register .rsk names in RNS.
/// @dev This contract has permission to register in RSK Owner.
contract FIFSRegistrar is PricedContract, ERC677TransferReceiver {
using SafeMath for uint256;
using StringUtils for string;
using BytesUtils for bytes;

mapping (bytes32 => uint) private commitmentRevealTime;
uint public minCommitmentAge = 1 minutes;

uint public minLength = 5;

ERC677 rif;
NodeOwner nodeOwner;
contract FIFSRegistrar is FIFSRegistrarBase, PricedContract {
address pool;

// sha3('register(string,address,bytes32,uint)')
Expand All @@ -34,58 +17,17 @@ contract FIFSRegistrar is PricedContract, ERC677TransferReceiver {
NodeOwner _nodeOwner,
address _pool,
AbstractNamePrice _namePrice
) public PricedContract(_namePrice) {
rif = _rif;
nodeOwner = _nodeOwner;
) public FIFSRegistrarBase(_rif, _nodeOwner) PricedContract(_namePrice) {
pool = _pool;
}

///////////////////
// 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.
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));
}

// 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);
}

// 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;
}

// 3.

// - Via ERC-20
/// @notice Registers a .rsk name in RNS.
/// @dev This method must be called after commiting.
Expand Down Expand Up @@ -158,22 +100,4 @@ contract FIFSRegistrar is PricedContract, ERC677TransferReceiver {

return price(name, nodeOwner.expirationTime(uint(label)), duration);
}

/////////////////////
// REGISTRAR ADMIN //
/////////////////////

/// @notice Change required commitment maturity.
/// @dev Only owner.
/// @param newMinCommitmentAge The new maturity required.
function setMinCommitmentAge (uint newMinCommitmentAge) external onlyOwner {
minCommitmentAge = newMinCommitmentAge;
}

/// @notice Change disbaled names.
/// @dev Only owner.
/// @param newMinLength The new minimum length enabled.
function setMinLength (uint newMinLength) external onlyOwner {
minLength = newMinLength;
}
}
95 changes: 95 additions & 0 deletions contracts/FIFSRegistrarBase.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
pragma solidity ^0.5.3;

import "@openzeppelin/contracts/math/SafeMath.sol";
import "@ensdomains/ethregistrar/contracts/StringUtils.sol";
import "@rsksmart/erc677/contracts/ERC677.sol";
import "@rsksmart/erc677/contracts/ERC677TransferReceiver.sol";
import "./NodeOwner.sol";
import "./AbstractNamePrice.sol";
import "./BytesUtils.sol";


/// @title First-in first-served registrar base.
/// @notice This is an abstract contract. A Registrar can inherit from
/// this contract to implement basic commit-reveal and admin functionality.
/// @dev Inherited contract should have registrar permission in Node Owner.
contract FIFSRegistrarBase is ERC677TransferReceiver, Ownable {
using SafeMath for uint256;
using StringUtils for string;
using BytesUtils for bytes;

mapping (bytes32 => uint) internal commitmentRevealTime;
uint public minCommitmentAge = 1 minutes;

uint public minLength = 5;

ERC677 rif;
NodeOwner nodeOwner;

constructor (
ERC677 _rif,
NodeOwner _nodeOwner
) public {
rif = _rif;
nodeOwner = _nodeOwner;
}

///////////////////
// 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 inheriting 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));
}

// 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);
}

// 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;
}

/////////////////////
// REGISTRAR ADMIN //
/////////////////////

/// @notice Change required commitment maturity.
/// @dev Only owner.
/// @param newMinCommitmentAge The new maturity required.
function setMinCommitmentAge (uint newMinCommitmentAge) external onlyOwner {
minCommitmentAge = newMinCommitmentAge;
}

/// @notice Change disbaled names.
/// @dev Only owner.
/// @param newMinLength The new minimum length enabled.
function setMinLength (uint newMinLength) external onlyOwner {
minLength = newMinLength;
}
}
9 changes: 9 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"@rsksmart/erc677": "^1.0.1",
"@rsksmart/rns-auction-registrar": "^1.0.1",
"@rsksmart/rns-registry": "^1.0.0",
"@rsksmart/rns-resolver": "^1.0.1",
"@truffle/hdwallet-provider": "^1.0.25"
},
"devDependencies": {
Expand Down
Loading

0 comments on commit 501ca63

Please sign in to comment.