Skip to content

Commit aa23c1f

Browse files
committed
allow for renewal controllers to renew a name
1 parent eaa6a02 commit aa23c1f

File tree

2 files changed

+196
-21
lines changed

2 files changed

+196
-21
lines changed

contracts/l2/FuseController.sol

+55-21
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ contract FuseController is Ownable, IFuseController {
198198
bytes memory tokenData = registry.getData(uint256(node));
199199

200200
// if the tokenData is not of the correct length, return 0.
201-
if (tokenData.length < 96) {
201+
if (tokenData.length != 96) {
202202
return 0;
203203
}
204204

@@ -369,50 +369,84 @@ contract FuseController is Ownable, IFuseController {
369369
}
370370

371371
// Set the expiry of a subnode, with a node and a label.
372-
function setExpiry(bytes32 node, uint256 label, uint64 newExpiry) external {
372+
function setExpiry(
373+
bytes32 node,
374+
bytes32 labelhash,
375+
uint64 newExpiry
376+
) external {
377+
TokenData memory td;
378+
TokenData memory sub;
379+
373380
// get the subnode
374-
bytes32 subnode = keccak256(abi.encodePacked(node, label));
381+
bytes32 subnode = keccak256(abi.encodePacked(node, labelhash));
375382

376383
// get tokenData
377-
bytes memory tokenData = registry.getData(uint256(subnode));
384+
bytes memory tokenData = registry.getData(uint256(node));
378385

379386
// Make sure the parent node controller is this contract.
380387
require(
381388
address(_getController(tokenData)) == address(this),
382389
"Controller is not this contract"
383390
);
384391

392+
// Make sure the tokenData is 96 bytes long.
393+
require(tokenData.length == 96, "Invalid tokenData length");
394+
385395
(
386-
address owner,
387-
address resolver, // we don't need the old expiry
396+
td.owner, // resolver // expiry
388397
,
389-
/*uint64 expiry*/ uint64 fuses,
390-
address renewalController
398+
,
399+
td.fuses,
400+
td.renewalController
391401
) = _unpack(tokenData);
392402

393-
// Check to make sure partent cannot control is not burned.
394-
require((fuses & PARENT_CANNOT_CONTROL) != 0, "Parent cannot control");
395-
396403
// Make sure the caller is authroized in the parent node.
397404
bool isAuthorized = registry.getAuthorization(
398405
uint256(node),
399-
owner,
406+
td.owner,
400407
msg.sender
401408
);
402409

403-
if (owner != msg.sender && !isAuthorized) {
404-
revert Unauthorised(node, msg.sender);
410+
// get tokenDataSubnode
411+
bytes memory tokenDataSubnode = registry.getData(uint256(subnode));
412+
413+
// Get the data of the subnode, including the fuses and renewal controller, get the data
414+
(sub.owner, sub.resolver, , sub.fuses, sub.renewalController) = _unpack(
415+
tokenDataSubnode
416+
);
417+
418+
// Check to make sure partent cannot control is not burned.
419+
require(
420+
(sub.fuses & PARENT_CANNOT_CONTROL) == 0,
421+
"Parent cannot control"
422+
);
423+
424+
// Check to make sure the caller is authorized.
425+
if (
426+
// Allow the owner of the parent node to set the expiry.
427+
!(td.owner == msg.sender) &&
428+
// Allow an authorized user of the parent node to set the expiry.
429+
!(isAuthorized) &&
430+
// Allow the renewal controller of the parent node
431+
// as long as the there is no renewal controller set on the subnode
432+
// to set the expiry.
433+
!(td.renewalController == msg.sender &&
434+
sub.renewalController == address(0)) &&
435+
// Allow the renewal controller of the subnode to set the expiry.
436+
!(sub.renewalController == msg.sender)
437+
) {
438+
revert Unauthorised(subnode, msg.sender);
405439
}
406440

407441
registry.setNode(
408442
uint256(subnode),
409443
_pack(
410444
address(this),
411-
owner,
412-
resolver,
445+
sub.owner,
446+
sub.resolver,
413447
newExpiry,
414-
fuses,
415-
renewalController
448+
sub.fuses,
449+
sub.renewalController
416450
)
417451
);
418452
}
@@ -481,7 +515,7 @@ contract FuseController is Ownable, IFuseController {
481515

482516
function setSubnode(
483517
bytes32 node,
484-
uint256 label,
518+
bytes32 labelhash,
485519
address subnodeOwner,
486520
address subnodeResolver,
487521
uint64 subnodeExpiry,
@@ -507,7 +541,7 @@ contract FuseController is Ownable, IFuseController {
507541
);
508542

509543
// Make the node of the subnode.
510-
bytes32 subnode = keccak256(abi.encodePacked(node, label));
544+
bytes32 subnode = keccak256(abi.encodePacked(node, labelhash));
511545

512546
// Get the subnode fuses.
513547
uint64 subnodeFusesOld = fusesOf(subnode);
@@ -546,7 +580,7 @@ contract FuseController is Ownable, IFuseController {
546580

547581
registry.setSubnode(
548582
uint256(node),
549-
label,
583+
uint256(labelhash),
550584
_pack(
551585
address(this),
552586
subnodeOwner,

test/l2/TestL2Registry.js

+141
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ describe.only('L2Registry', () => {
8585
hackerAddress = await hacker.getAddress()
8686
dummyAccount = await signers[4]
8787
dummyAccountAddress = await dummyAccount.getAddress()
88+
renewalController = await signers[5]
89+
renewalControllerAddress = await renewalController.getAddress()
8890

8991
resolver = await DelegatableResolver.new()
9092
metaDataservice = await StaticMetadataService.new('https://ens.domains')
@@ -404,5 +406,144 @@ describe.only('L2Registry', () => {
404406
EMPTY_ADDRESS,
405407
)
406408
})
409+
410+
// Make sure that a test subnode can be renewed by the renewal controller address.
411+
it('should renew a subnode using the renewal controller', async () => {
412+
const blockTime = (await ethers.provider.getBlock('latest')).timestamp
413+
414+
await controller.setSubnode(
415+
TEST_NODE,
416+
labelhash('sub'),
417+
subnodeOwnerAddress,
418+
resolver.address,
419+
// blocktime + 60 DAYs
420+
blockTime + 60 * DAY,
421+
0, // no fuse
422+
renewalControllerAddress, // no controller
423+
{ from: ownerAddress },
424+
)
425+
426+
// Make sure the subnode is owned by the subnodeOwnerAddress
427+
assert.equal(await controller.ownerOf(TEST_SUBNODE), subnodeOwnerAddress)
428+
429+
// Make sure the renewal controller is set to the renewalControllerAddress
430+
assert.equal(
431+
await controller.renewalControllerOf(TEST_SUBNODE),
432+
renewalControllerAddress,
433+
)
434+
435+
// Extend the expiry of the subnode by 30 days by calling the setExpiry function from the renewal controller address.
436+
await controller.setExpiry(
437+
TEST_NODE,
438+
labelhash('sub'),
439+
blockTime + 90 * DAY,
440+
{
441+
from: renewalControllerAddress,
442+
},
443+
)
444+
445+
// Make sure the subnode is still owned by the subnodeOwnerAddress
446+
assert.equal(await controller.ownerOf(TEST_SUBNODE), subnodeOwnerAddress)
447+
448+
// Make sure the expiry of the subnode has been extended by 30 days
449+
assert.equal(
450+
await controller.expiryOf(TEST_SUBNODE),
451+
blockTime + 90 * DAY,
452+
)
453+
})
454+
455+
// Make sure that the test subnode can be renewed by the parent renewal controller address.
456+
it('should renew a subnode using the parent renewal controller', async () => {
457+
const blockTime = (await ethers.provider.getBlock('latest')).timestamp
458+
459+
await controller.setSubnode(
460+
TEST_NODE,
461+
labelhash('sub'),
462+
subnodeOwnerAddress,
463+
resolver.address,
464+
// blocktime + 60 DAYs
465+
blockTime + 60 * DAY,
466+
0, // no fuse
467+
renewalControllerAddress, // no controller
468+
{ from: ownerAddress },
469+
)
470+
471+
// Predict the node hash of the sub-subnode
472+
const subSubNode = namehash('sub-sub.sub.test')
473+
474+
// Make a sub-subnode without a renewal controller
475+
await controller.setSubnode(
476+
TEST_SUBNODE,
477+
labelhash('sub-sub'),
478+
dummyAccountAddress,
479+
resolver.address,
480+
// blocktime + 60 DAYs
481+
blockTime + 60 * DAY,
482+
0, // no fuse
483+
EMPTY_ADDRESS, // no controller
484+
{ from: subnodeOwnerAddress },
485+
)
486+
487+
// Make sure the sub-subnode is owned by the dummyAccountAddress
488+
assert.equal(await controller.ownerOf(subSubNode), dummyAccountAddress)
489+
490+
// Make sure we can renew the sub-subnode using the parent renewal controller
491+
await controller.setExpiry(
492+
TEST_SUBNODE,
493+
labelhash('sub-sub'),
494+
blockTime + 90 * DAY,
495+
{
496+
from: renewalControllerAddress,
497+
},
498+
)
499+
500+
// Make sure the sub-subnode is still owned by the dummyAccountAddress
501+
assert.equal(await controller.ownerOf(subSubNode), dummyAccountAddress)
502+
503+
// Make sure the expiry of the sub-subnode has been extended by 30 days
504+
assert.equal(await controller.expiryOf(subSubNode), blockTime + 90 * DAY)
505+
})
506+
507+
// Make sure that a hacker can't renew a subnode using the renewal controller address.
508+
it('should revert when a hacker tries to renew a subnode using the renewal controller', async () => {
509+
const blockTime = (await ethers.provider.getBlock('latest')).timestamp
510+
511+
await controller.setSubnode(
512+
TEST_NODE,
513+
labelhash('sub'),
514+
subnodeOwnerAddress,
515+
resolver.address,
516+
// blocktime + 60 DAYs
517+
blockTime + 60 * DAY,
518+
0, // no fuse
519+
renewalControllerAddress, // no controller
520+
{ from: ownerAddress },
521+
)
522+
523+
// Make sure the subnode is owned by the subnodeOwnerAddress
524+
assert.equal(await controller.ownerOf(TEST_SUBNODE), subnodeOwnerAddress)
525+
526+
// Make sure the renewal controller is set to the renewalControllerAddress
527+
assert.equal(
528+
await controller.renewalControllerOf(TEST_SUBNODE),
529+
renewalControllerAddress,
530+
)
531+
532+
// Extend the expiry of the subnode by 30 days by calling the setExpiry
533+
// function from the renewal controller address, expect it to revert
534+
// with custom error, Unauthorised(bytes32 node, address addr);
535+
await expect(
536+
controller.setExpiry(
537+
TEST_NODE,
538+
labelhash('sub'),
539+
blockTime + 90 * DAY,
540+
{
541+
from: hackerAddress,
542+
},
543+
),
544+
).to.be.revertedWith(
545+
`Unauthorised("${TEST_SUBNODE}", "${hackerAddress}")`,
546+
)
547+
})
407548
})
408549
})

0 commit comments

Comments
 (0)