Skip to content

Commit

Permalink
Merge pull request #117 from Consensys/feat/CZK-731-renew-poh
Browse files Browse the repository at this point in the history
feat: add renewPoH method
  • Loading branch information
Julink-eth authored May 2, 2024
2 parents ab293aa + f93dbdd commit 0b098d2
Show file tree
Hide file tree
Showing 2 changed files with 197 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ error MaxCommitmentAgeTooHigh();
error PohVerificationFailed(address owner);
error OwnerAlreadyRegistered(address owner);
error SenderNotOwner(address owner, address sender);
error NotInGracePeriod(uint256 current, uint256 expiry);
error WrongPohRegistrationDuration(uint256 duration);

/**
Expand All @@ -51,6 +52,7 @@ contract ETHRegistrarController is
/// @dev Registration through POH is fixed to a 3 years duration
uint256 public constant POH_REGISTRATION_DURATION = 1 days * 365 * 3;
uint64 private constant MAX_EXPIRY = type(uint64).max;
uint256 public constant GRACE_PERIOD = 90 days;
BaseRegistrarImplementation immutable base;
IPriceOracle public immutable prices;
uint256 public immutable minCommitmentAge;
Expand Down Expand Up @@ -97,6 +99,8 @@ contract ETHRegistrarController is
uint256 expires
);

event NameRenewedPoh(string name, bytes32 indexed label, uint256 expires);

/**
* @notice Create registrar for the base domain passed in parameter.
* @param _base Base registrar address.
Expand Down Expand Up @@ -402,6 +406,41 @@ contract ETHRegistrarController is
emit NameRenewed(name, labelhash, msg.value, expires);
}

/**
* @notice Same as renew method except that it uses the user's POH to renew for free
* @dev Can only renew after the GRACE_PERIOD has started
* @param name to renew
* @param signature POH of the owner to renew
*/
function renewPoh(string calldata name, bytes memory signature) external {
bytes32 labelhash = keccak256(bytes(name));
bytes32 nodehash = keccak256(abi.encodePacked(baseNode, labelhash));

(address currentOwner, , ) = nameWrapper.getData(uint256(nodehash));

// The sender of the transaction needs to be the current owner of the name
if (msg.sender != currentOwner) {
revert SenderNotOwner(currentOwner, msg.sender);
}

// Check that the signature sent is valid, this is the reference for an address to have a valid PoH
if (!pohVerifier.verify(signature, currentOwner)) {
revert PohVerificationFailed(currentOwner);
}

uint256 tokenId = uint256(labelhash);
uint256 currentExpiry = base.nameExpires(tokenId);

// Renewal using POH can only occurs within the GRACE_PERIOD
if (block.timestamp < (currentExpiry - GRACE_PERIOD)) {
revert NotInGracePeriod(block.timestamp, currentExpiry);
}

uint256 expires = nameWrapper.renew(tokenId, POH_REGISTRATION_DURATION);

emit NameRenewedPoh(name, labelhash, expires);
}

function withdraw() public {
payable(owner()).transfer(address(this).balance);
}
Expand Down
158 changes: 158 additions & 0 deletions packages/l2-contracts/test/ethregistrar/TestEthRegistrarController.js
Original file line number Diff line number Diff line change
Expand Up @@ -804,6 +804,164 @@ contract('ETHRegistrarController', function () {
).to.equal(86400)
})

it('should allow token owners to renew a name for free using POH', async () => {
const name = 'newname'
const duration = 3 * 365 * 24 * 60 * 60 // 3 years
const secret = ethers.utils.formatBytes32String('secret')
const human = signers[0].address
const signature = ethers.utils.hexlify(ethers.utils.randomBytes(65)) // Mock signature

// Generate a commitment for the registration
const commitment = await controllerPoh.makeCommitment(
name,
human,
duration,
secret,
ethers.constants.AddressZero, // resolver address, using zero address for simplicity
[],
false,
0,
)

// Commit the registration
await controllerPoh.commit(commitment)

// Advance time to satisfy the minCommitmentAge requirement
await ethers.provider.send('evm_increaseTime', [600]) // Increase time by 600 seconds
await ethers.provider.send('evm_mine') // Mine the next block

// Perform the registration using registerPoh
const tx = await controllerPoh.registerPoh(
name,
human,
duration,
secret,
ethers.constants.AddressZero, // resolver address, using zero address for simplicity
[],
false,
0,
signature,
)

// Wait for the transaction to be mined
await tx.wait()

await ethers.provider.send('evm_increaseTime', [duration])
await ethers.provider.send('evm_mine')

var nodehash = namehash(`newname.${BASE_DOMAIN_STR}`)
const [, fuses, fuseExpiry] = await nameWrapper.getData(nodehash)

var expires = await baseRegistrar.nameExpires(sha3('newname'))

await controllerPoh.renewPoh('newname', signature)
var newExpires = await baseRegistrar.nameExpires(sha3('newname'))
const [, newFuses, newFuseExpiry] = await nameWrapper.getData(nodehash)
expect(newExpires.toNumber() - expires.toNumber()).to.equal(duration)
expect(newFuseExpiry.toNumber() - fuseExpiry.toNumber()).to.equal(duration)
expect(newFuses).to.equal(fuses)
})

it('should not allow renewing a domain that does not belong to the user using POH', async () => {
const name = 'newname'
const duration = 3 * 365 * 24 * 60 * 60 // 3 years
const secret = ethers.utils.formatBytes32String('secret')
const signer1 = signers[0]
const signer2 = signers[1]
const signature = ethers.utils.hexlify(ethers.utils.randomBytes(65)) // Mock signature

// Generate a commitment for the registration
const commitment = await controllerPoh.connect(signer2).makeCommitment(
name,
signer2.address,
duration,
secret,
ethers.constants.AddressZero, // resolver address, using zero address for simplicity
[],
false,
0,
)

// Commit the registration
await controllerPoh.connect(signer2).commit(commitment)

// Advance time to satisfy the minCommitmentAge requirement
await ethers.provider.send('evm_increaseTime', [600]) // Increase time by 600 seconds
await ethers.provider.send('evm_mine') // Mine the next block

// Perform the registration using registerPoh
const tx = await controllerPoh.connect(signer2).registerPoh(
name,
signer2.address,
duration,
secret,
ethers.constants.AddressZero, // resolver address, using zero address for simplicity
[],
false,
0,
signature,
)

// Wait for the transaction to be mined
await tx.wait()

await ethers.provider.send('evm_increaseTime', [duration])
await ethers.provider.send('evm_mine')

await expect(
controllerPoh.renewPoh('newname', signature),
).to.be.revertedWith(
`'SenderNotOwner("${signer2.address}", "${signer1.address}")'`,
)
})

it('should not allow renewing a domain that does not belong to the user using POH', async () => {
const name = 'newname'
const duration = 3 * 365 * 24 * 60 * 60 // 3 years
const secret = ethers.utils.formatBytes32String('secret')
const signer1 = signers[0]
const signature = ethers.utils.hexlify(ethers.utils.randomBytes(65)) // Mock signature

// Generate a commitment for the registration
const commitment = await controllerPoh.makeCommitment(
name,
signer1.address,
duration,
secret,
ethers.constants.AddressZero, // resolver address, using zero address for simplicity
[],
false,
0,
)

// Commit the registration
await controllerPoh.commit(commitment)

// Advance time to satisfy the minCommitmentAge requirement
await ethers.provider.send('evm_increaseTime', [600]) // Increase time by 600 seconds
await ethers.provider.send('evm_mine') // Mine the next block

// Perform the registration using registerPoh
const tx = await controllerPoh.registerPoh(
name,
signer1.address,
duration,
secret,
ethers.constants.AddressZero, // resolver address, using zero address for simplicity
[],
false,
0,
signature,
)

// Wait for the transaction to be mined
await tx.wait()

await expect(
controllerPoh.renewPoh('newname', signature),
).to.be.revertedWith(`NotInGracePeriod`)
})

it('non wrapped names can renew', async () => {
const label = 'newname'
const tokenId = sha3(label)
Expand Down

0 comments on commit 0b098d2

Please sign in to comment.