partial slashing reward, reward msgsender and reserver
This commit is contained in:
parent
a1fe1f02e7
commit
c6636fe6b9
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue