partial slashing reward, reward msgsender and reserver

This commit is contained in:
Ricardo Guilherme Schmidt 2018-09-30 20:14:29 -03:00
parent a1fe1f02e7
commit c6636fe6b9
No known key found for this signature in database
GPG Key ID: 3F95A3AD0B607030
2 changed files with 74 additions and 17 deletions

View File

@ -35,7 +35,7 @@ contract UsernameRegistrar is Controlled, ApproveAndCallFallBack {
bytes32 public ensNode; bytes32 public ensNode;
uint256 public price; uint256 public price;
RegistrarState public state; RegistrarState public state;
uint256 private reserveAmount; uint256 public reserveAmount;
struct Account { struct Account {
uint256 balance; uint256 balance;
@ -181,9 +181,10 @@ contract UsernameRegistrar is Controlled, ApproveAndCallFallBack {
/** /**
* @notice secretly reserve the slashing reward to `msg.sender` * @notice secretly reserve the slashing reward to `msg.sender`
* @param _secret keccak256(abi.encodePacked(namehash, owner, creationTime)) * @param _secret keccak256(abi.encodePacked(namehash, creationTime))
*/ */
function reserveSlash(bytes32 _secret) external { function reserveSlash(bytes32 _secret) external {
require(reservedSlashers[_secret] == address(0), "Already Reserved");
reservedSlashers[_secret] = msg.sender; reservedSlashers[_secret] = msg.sender;
} }
@ -367,7 +368,7 @@ contract UsernameRegistrar is Controlled, ApproveAndCallFallBack {
} }
/** /**
* @notice Withdraw tokens wrongly sent to the contract. * @notice Withdraw not reserved tokens
* @param _token Address of ERC20 withdrawing excess, or address(0) if want ETH. * @param _token Address of ERC20 withdrawing excess, or address(0) if want ETH.
* @param _beneficiary Address to send the funds. * @param _beneficiary Address to send the funds.
**/ **/
@ -388,7 +389,7 @@ contract UsernameRegistrar is Controlled, ApproveAndCallFallBack {
require(amount > reserveAmount, "Is not excess"); require(amount > reserveAmount, "Is not excess");
amount -= reserveAmount; amount -= reserveAmount;
} else { } else {
require(amount > 0, "Is not excess"); require(amount > 0, "No balance");
} }
excessToken.transfer(_beneficiary, amount); excessToken.transfer(_beneficiary, amount);
} }
@ -483,7 +484,7 @@ contract UsernameRegistrar is Controlled, ApproveAndCallFallBack {
/** /**
* @notice returns address that reserved slashing of an account * @notice returns address that reserved slashing of an account
* @param _label Username hash. * @param _label Username hash.
* @return Exact time when username can be released. * @return Reserved Slasher
**/ **/
function getReservedSlasher(bytes32 _label) function getReservedSlasher(bytes32 _label)
external external
@ -492,11 +493,26 @@ contract UsernameRegistrar is Controlled, ApproveAndCallFallBack {
{ {
bytes32 namehash = keccak256(abi.encodePacked(ensNode, _label)); bytes32 namehash = keccak256(abi.encodePacked(ensNode, _label));
uint256 creationTime = accounts[_label].creationTime; uint256 creationTime = accounts[_label].creationTime;
address owner = ensRegistry.owner(namehash); bytes32 secret = keccak256(abi.encodePacked(namehash, creationTime));
bytes32 secret = keccak256(abi.encodePacked(namehash, owner, creationTime));
reservedSlasher = reservedSlashers[secret]; reservedSlasher = reservedSlashers[secret];
} }
/**
* @notice calculate reward part an account could payout on slash
* @param _label Username hash.
* @return Part of reward
**/
function getSlashRewardPart(bytes32 _label)
external
view
returns(uint256 partReward)
{
uint256 balance = accounts[_label].balance;
if (balance > 0) {
partReward = balance / 3;
}
}
/** /**
* @notice Support for "approveAndCall". Callable only by `token()`. * @notice Support for "approveAndCall". Callable only by `token()`.
* @param _from Who approved. * @param _from Who approved.
@ -661,14 +677,20 @@ contract UsernameRegistrar is Controlled, ApproveAndCallFallBack {
if (amountToTransfer > 0) { if (amountToTransfer > 0) {
reserveAmount -= amountToTransfer; reserveAmount -= amountToTransfer;
address receiver = msg.sender; uint256 partialDeposit = amountToTransfer / 3;
bytes32 secret = keccak256(abi.encodePacked(namehash, owner, creationTime)); amountToTransfer = partialDeposit * 2; // reserve 1/3 to network (controller)
address slasher = msg.sender;
bytes32 secret = keccak256(abi.encodePacked(namehash, creationTime));
address reservedSlasher = reservedSlashers[secret]; address reservedSlasher = reservedSlashers[secret];
if (reservedSlasher != address(0)) { if (reservedSlasher != address(0)) {
delete reservedSlashers[secret]; delete reservedSlashers[secret];
receiver = reservedSlasher; if (reservedSlasher != msg.sender) {
amountToTransfer -= partialDeposit; // 1/3 goes to msg.sender
require(token.transfer(msg.sender, partialDeposit), "Error in transfer.");
}
slasher = reservedSlasher; // reservedSlasher will receive the amountToTransfer instead of msg.sender
} }
require(token.transfer(receiver, amountToTransfer), "Error in transfer."); require(token.transfer(slasher, amountToTransfer), "Error in transfer.");
} }
emit UsernameOwner(namehash, address(0)); emit UsernameOwner(namehash, address(0));
} }

View File

@ -813,20 +813,22 @@ contract('UsernameRegistrar', function () {
const usernameHash = namehash.hash(username + '.' + registry.registry); const usernameHash = namehash.hash(username + '.' + registry.registry);
const registrant = accountsArr[1]; const registrant = accountsArr[1];
const slasher = accountsArr[2]; const slasher = accountsArr[2];
const label = web3Utils.sha3(username);
await TestToken.methods.mint(registry.price).send({from: registrant}); await TestToken.methods.mint(registry.price).send({from: registrant});
await TestToken.methods.approve(UsernameRegistrar.address, registry.price).send({from: registrant}); await TestToken.methods.approve(UsernameRegistrar.address, registry.price).send({from: registrant});
await UsernameRegistrar.methods.register( await UsernameRegistrar.methods.register(
web3Utils.sha3(username), label,
utils.zeroAddress, utils.zeroAddress,
utils.zeroBytes32, utils.zeroBytes32,
utils.zeroBytes32 utils.zeroBytes32
).send({from: registrant}); ).send({from: registrant});
await utils.increaseTime(20000) await utils.increaseTime(20000)
const partReward = await UsernameRegistrar.methods.getSlashRewardPart(label).call();
assert.equal(await ens.methods.owner(usernameHash).call(), registrant); assert.equal(await ens.methods.owner(usernameHash).call(), registrant);
const initialSlasherBalance = await TestToken.methods.balanceOf(slasher).call(); const initialSlasherBalance = await TestToken.methods.balanceOf(slasher).call();
await UsernameRegistrar.methods.slashSmallUsername(username).send({from: slasher}) await UsernameRegistrar.methods.slashSmallUsername(username).send({from: slasher})
//TODO: check events //TODO: check events
assert.equal(await TestToken.methods.balanceOf(slasher).call(), (+initialSlasherBalance)+(+registry.price)); assert.equal(await TestToken.methods.balanceOf(slasher).call(), (+initialSlasherBalance)+((+partReward)*2));
assert.equal(await ens.methods.owner(usernameHash).call(), utils.zeroAddress); assert.equal(await ens.methods.owner(usernameHash).call(), utils.zeroAddress);
}); });
@ -872,7 +874,7 @@ contract('UsernameRegistrar', function () {
}); });
describe('reserveSlash()', function() { describe('reserveSlash()', function() {
it('should send funds to reserver', async() =>{ it('should send 1/3 funds to reserver and 1/3 of funds to caller', async() =>{
const username = 'c'; const username = 'c';
const label = web3Utils.sha3(username); const label = web3Utils.sha3(username);
const usernameHash = namehash.hash(username + '.' + registry.registry); const usernameHash = namehash.hash(username + '.' + registry.registry);
@ -889,15 +891,48 @@ contract('UsernameRegistrar', function () {
).send({from: registrant}); ).send({from: registrant});
await utils.increaseTime(20000) await utils.increaseTime(20000)
assert.equal(await ens.methods.owner(usernameHash).call(), registrant); assert.equal(await ens.methods.owner(usernameHash).call(), registrant);
const initialSlasherBalance = await TestToken.methods.balanceOf(slashReserver).call(); const partReward = await UsernameRegistrar.methods.getSlashRewardPart(label).call();
const initialSlashReserverBalance = await TestToken.methods.balanceOf(slashReserver).call();
const initialSlashCallerBalance = await TestToken.methods.balanceOf(slashCaller).call();
const creationTime = await UsernameRegistrar.methods.getCreationTime(label).call(); const creationTime = await UsernameRegistrar.methods.getCreationTime(label).call();
const secret = web3Utils.soliditySha3(usernameHash, registrant, creationTime); const secret = web3Utils.soliditySha3(usernameHash, creationTime);
assert.equal(await UsernameRegistrar.methods.getReservedSlasher(label).call(), utils.zeroAddress); assert.equal(await UsernameRegistrar.methods.getReservedSlasher(label).call(), utils.zeroAddress);
await UsernameRegistrar.methods.reserveSlash(secret).send({from: slashReserver}); await UsernameRegistrar.methods.reserveSlash(secret).send({from: slashReserver});
assert.equal(await UsernameRegistrar.methods.getReservedSlasher(label).call(), slashReserver); assert.equal(await UsernameRegistrar.methods.getReservedSlasher(label).call(), slashReserver);
await UsernameRegistrar.methods.slashSmallUsername(username).send({from: slashCaller}) await UsernameRegistrar.methods.slashSmallUsername(username).send({from: slashCaller})
//TODO: check events //TODO: check events
assert.equal(await TestToken.methods.balanceOf(slashReserver).call(), (+initialSlasherBalance)+(+registry.price)); assert.equal(await TestToken.methods.balanceOf(slashReserver).call(), (+initialSlashReserverBalance)+(+partReward));
assert.equal(await TestToken.methods.balanceOf(slashCaller).call(), (+initialSlashCallerBalance)+(+partReward));
assert.equal(await ens.methods.owner(usernameHash).call(), utils.zeroAddress);
});
it('should send 2/3 funds to reserver as is also the caller', async() =>{
const username = 'c';
const label = web3Utils.sha3(username);
const usernameHash = namehash.hash(username + '.' + registry.registry);
const registrant = accountsArr[1];
const slashReserverCaller = accountsArr[2];
await TestToken.methods.mint(registry.price).send({from: registrant});
await TestToken.methods.approve(UsernameRegistrar.address, registry.price).send({from: registrant});
await UsernameRegistrar.methods.register(
web3Utils.sha3(username),
utils.zeroAddress,
utils.zeroBytes32,
utils.zeroBytes32
).send({from: registrant});
await utils.increaseTime(20000)
assert.equal(await ens.methods.owner(usernameHash).call(), registrant);
const partReward = await UsernameRegistrar.methods.getSlashRewardPart(label).call();
const initialSlashReserverBalance = await TestToken.methods.balanceOf(slashReserverCaller).call();
const creationTime = await UsernameRegistrar.methods.getCreationTime(label).call();
const secret = web3Utils.soliditySha3(usernameHash, creationTime);
assert.equal(await UsernameRegistrar.methods.getReservedSlasher(label).call(), utils.zeroAddress);
await UsernameRegistrar.methods.reserveSlash(secret).send({from: slashReserverCaller});
assert.equal(await UsernameRegistrar.methods.getReservedSlasher(label).call(), slashReserverCaller);
await UsernameRegistrar.methods.slashSmallUsername(username).send({from: slashReserverCaller})
//TODO: check events
assert.equal(await TestToken.methods.balanceOf(slashReserverCaller).call(), (+initialSlashReserverBalance)+(+partReward*2));
assert.equal(await ens.methods.owner(usernameHash).call(), utils.zeroAddress); assert.equal(await ens.methods.owner(usernameHash).call(), utils.zeroAddress);
}); });
}); });