parent
0cdc5e13ce
commit
4fe2157e36
@ -0,0 +1,91 @@ |
|||||||
|
pragma solidity ^0.4.11; |
||||||
|
|
||||||
|
|
||||||
|
import './Ownable.sol'; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* @title Inheritable |
||||||
|
* @dev The Inheritable contract provides ownership transfer capabilities, in the |
||||||
|
* case that the current owner stops "heartbeating". Only the heir can pronounce the |
||||||
|
* owner's death. |
||||||
|
*/ |
||||||
|
contract Inheritable2 is Ownable { |
||||||
|
address public heir; |
||||||
|
|
||||||
|
// Time window the owner has to notify she is alive. |
||||||
|
uint public heartbeatTimeout; |
||||||
|
|
||||||
|
// Timestamp of the owner's death, as pronounced by the heir. |
||||||
|
uint public timeOfDeath; |
||||||
|
|
||||||
|
|
||||||
|
event OwnerPronouncedDead(address indexed owner, address indexed heir, uint indexed timeOfDeath); |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* @dev Throw an exception if called by any account other than the heir's. |
||||||
|
*/ |
||||||
|
modifier onlyHeir() { |
||||||
|
require(msg.sender == heir); |
||||||
|
_; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* @notice Create a new Inheritable Contract with heir address 0x0. |
||||||
|
* @param _heartbeatTimeout time available for the owner to notify she's alive, |
||||||
|
* before the heir can take ownership. |
||||||
|
*/ |
||||||
|
function Inheritable(uint _heartbeatTimeout) public { |
||||||
|
heartbeatTimeout = _heartbeatTimeout; |
||||||
|
} |
||||||
|
|
||||||
|
function setHeir(address newHeir) public onlyOwner { |
||||||
|
heir = newHeir; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @dev set heir = 0x0 |
||||||
|
*/ |
||||||
|
function removeHeir() public onlyOwner { |
||||||
|
delete(heir); |
||||||
|
} |
||||||
|
|
||||||
|
function setHeartbeatTimeout(uint newHeartbeatTimeout) public onlyOwner { |
||||||
|
require(ownerLives()); |
||||||
|
heartbeatTimeout = newHeartbeatTimeout; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @dev Heir can pronounce the owners death. To inherit the ownership, he will |
||||||
|
* have to wait for `heartbeatTimeout` seconds. |
||||||
|
*/ |
||||||
|
function pronounceDeath() public onlyHeir { |
||||||
|
require(ownerLives()); |
||||||
|
timeOfDeath = now; |
||||||
|
OwnerPronouncedDead(owner, heir, timeOfDeath); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @dev Owner can send a heartbeat if she was mistakenly pronounced dead. |
||||||
|
*/ |
||||||
|
function heartbeat() public onlyOwner { |
||||||
|
delete(timeOfDeath); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @dev Allows heir to transfer ownership only if heartbeat has timed out. |
||||||
|
*/ |
||||||
|
function inherit() public onlyHeir { |
||||||
|
require(!ownerLives()); |
||||||
|
require(now >= timeOfDeath + heartbeatTimeout); |
||||||
|
OwnershipTransferred(owner, heir); |
||||||
|
owner = heir; |
||||||
|
delete(timeOfDeath); |
||||||
|
} |
||||||
|
|
||||||
|
function ownerLives() internal returns (bool) { |
||||||
|
return timeOfDeath == 0; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,139 @@ |
|||||||
|
'use strict' |
||||||
|
import { advanceBlock } from './helpers/advanceToBlock' |
||||||
|
import increaseTime from './helpers/increaseTime' |
||||||
|
import { increaseTimeTo, duration } from './helpers/increaseTime' |
||||||
|
import assertJump from './helpers/assertJump' |
||||||
|
|
||||||
|
|
||||||
|
const NULL_ADDRESS = '0x0000000000000000000000000000000000000000' |
||||||
|
|
||||||
|
const Inheritable = artifacts.require('../contracts/ownership/Inheritable2.sol') |
||||||
|
|
||||||
|
contract('Inheritable', function(accounts) { |
||||||
|
let inheritable |
||||||
|
let owner |
||||||
|
|
||||||
|
beforeEach(async function() { |
||||||
|
inheritable = await Inheritable.new() |
||||||
|
owner = await inheritable.owner() |
||||||
|
}) |
||||||
|
|
||||||
|
it('should start off with an owner, but without heir', async function() { |
||||||
|
const heir = await inheritable.heir() |
||||||
|
|
||||||
|
assert.equal(typeof(owner), 'string') |
||||||
|
assert.equal(typeof(heir), 'string') |
||||||
|
assert.notStrictEqual( |
||||||
|
owner, NULL_ADDRESS, |
||||||
|
"Owner shouldn't be the null address" |
||||||
|
) |
||||||
|
assert.isTrue( |
||||||
|
heir === NULL_ADDRESS, |
||||||
|
"Heir should be the null address" |
||||||
|
) |
||||||
|
}) |
||||||
|
|
||||||
|
it('only owner should set heir', async function() { |
||||||
|
const newHeir = accounts[1] |
||||||
|
const someRandomAddress = accounts[2] |
||||||
|
assert.isTrue(owner !== someRandomAddress) |
||||||
|
|
||||||
|
await inheritable.setHeir(newHeir, {from: owner}) |
||||||
|
try { |
||||||
|
await inheritable.setHeir(newHeir, {from: someRandomAddress}) |
||||||
|
assert.fail('should have thrown before') |
||||||
|
} catch(error) { |
||||||
|
assertJump(error) |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
it('owner can remove heir', async function() { |
||||||
|
const newHeir = accounts[1] |
||||||
|
await inheritable.setHeir(newHeir, {from: owner}) |
||||||
|
let heir = await inheritable.heir() |
||||||
|
|
||||||
|
assert.notStrictEqual(heir, NULL_ADDRESS) |
||||||
|
await inheritable.removeHeir() |
||||||
|
heir = await inheritable.heir() |
||||||
|
assert.isTrue(heir === NULL_ADDRESS) |
||||||
|
}) |
||||||
|
|
||||||
|
it('owner can set heartbeatTimeout only if she\'s alive', async function() { |
||||||
|
const newTimeout = 41414141 |
||||||
|
await inheritable.setHeartbeatTimeout(newTimeout, {from: owner}) |
||||||
|
|
||||||
|
assert.isTrue((await inheritable.heartbeatTimeout()).equals(new web3.BigNumber(newTimeout))) |
||||||
|
|
||||||
|
const heir = accounts[1] |
||||||
|
await inheritable.setHeir(heir, {from: owner}) |
||||||
|
await inheritable.pronounceDeath({from: heir}) |
||||||
|
|
||||||
|
try { |
||||||
|
await inheritable.setHeartbeatTimeout(newTimeout, {from: owner}) |
||||||
|
assert.fail('should have thrown before') |
||||||
|
} catch(error) { |
||||||
|
assertJump(error) |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
it('heir can inherit only if owner is dead and timeout was reached', async function() { |
||||||
|
const heir = accounts[1] |
||||||
|
await inheritable.setHeir(heir, {from: owner}) |
||||||
|
await inheritable.setHeartbeatTimeout(4141, {from: owner}) |
||||||
|
|
||||||
|
try { |
||||||
|
await inheritable.inherit({from: heir}) |
||||||
|
assert.fail('should have thrown before') |
||||||
|
} catch(error) { |
||||||
|
assertJump(error) |
||||||
|
} |
||||||
|
|
||||||
|
await inheritable.pronounceDeath({from: heir}) |
||||||
|
await increaseTime(1) |
||||||
|
try { |
||||||
|
await inheritable.inherit({from: heir}) |
||||||
|
assert.fail('should have thrown before') |
||||||
|
} catch(error) { |
||||||
|
assertJump(error) |
||||||
|
} |
||||||
|
|
||||||
|
await increaseTime(4141) |
||||||
|
await inheritable.inherit({from: heir}) |
||||||
|
|
||||||
|
}) |
||||||
|
|
||||||
|
it('heir can\'t inherit if owner heartbeats', async function() { |
||||||
|
const heir = accounts[1] |
||||||
|
await inheritable.setHeir(heir, {from: owner}) |
||||||
|
await inheritable.setHeartbeatTimeout(4141, {from: owner}) |
||||||
|
|
||||||
|
await inheritable.pronounceDeath({from: heir}) |
||||||
|
await inheritable.heartbeat({from: owner}) |
||||||
|
try { |
||||||
|
await inheritable.inherit({from: heir}) |
||||||
|
assert.fail('should have thrown before') |
||||||
|
} catch(error) { |
||||||
|
assertJump(error) |
||||||
|
} |
||||||
|
|
||||||
|
await inheritable.pronounceDeath({from: heir}) |
||||||
|
await increaseTime(4141) |
||||||
|
await inheritable.heartbeat({from: owner}) |
||||||
|
try { |
||||||
|
await inheritable.inherit({from: heir}) |
||||||
|
assert.fail('should have thrown before') |
||||||
|
} catch(error) { |
||||||
|
assertJump(error) |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
it('should log owner dead and ownership transfer', async function() { |
||||||
|
const heir = accounts[1] |
||||||
|
await inheritable.setHeir(heir, {from: owner}) |
||||||
|
const { logs } = await inheritable.pronounceDeath({from: heir}) |
||||||
|
const event = logs.find(e => e.event === 'OwnerPronouncedDead') |
||||||
|
|
||||||
|
assert.isTrue(event.args.owner === owner) |
||||||
|
assert.isTrue(event.args.heir === heir) |
||||||
|
}) |
||||||
|
}) |
Loading…
Reference in new issue