Added friends to message tributes to skip fees and added test unit

This commit is contained in:
Richard Ramos 2018-03-19 15:01:26 -04:00
parent 5272171a17
commit 0db5b6af5d
2 changed files with 366 additions and 13 deletions

View File

@ -25,6 +25,9 @@ contract MessageTribute is Controlled {
mapping(address => mapping(address => Fee)) feeCatalog;
mapping(address => uint256) balances;
mapping(bytes32 => uint256) friendIndex;
address[] friends;
struct Audience {
uint256 blockNum;
@ -33,12 +36,39 @@ contract MessageTribute is Controlled {
mapping(address => mapping(address => Audience)) audienceRequested;
function MessageTribute(address _SNT) public {
SNT = MiniMeToken(_SNT);
}
function addFriends(address[] _friends) public {
uint256 len = _friends.length;
for (uint256 i = 0; i < len; i++) {
bytes32 frHash = keccak256(_friends[i], msg.sender);
if (friendIndex[frHash] == 0)
friendIndex[frHash] = friends.push(_friends[i]);
}
}
function removeFriends(address[] _friends) public {
uint256 len = _friends.length;
for (uint256 i = 0; i < len; i++) {
bytes32 frHash = keccak256(_friends[i], msg.sender);
require(friendIndex[frHash] > 0);
uint index = friendIndex[frHash] - 1;
delete friendIndex[frHash];
address replacer = friends[friends.length - 1];
friends[index] = replacer;
friendIndex[keccak256(replacer, msg.sender)] = index;
friends.length--;
}
}
function isFriend(address _friend) public view returns(bool) {
return friendIndex[keccak256(_friend, msg.sender)] > 0;
}
function setRequiredTribute(address _to, uint _amount, bool _isTribute, bool _isPermanent) public {
require(friendIndex[keccak256(msg.sender, _to)] == 0);
feeCatalog[msg.sender][_to] = Fee(_amount, _isTribute, _isPermanent);
}
@ -49,20 +79,11 @@ contract MessageTribute is Controlled {
fee = f.amount;
tribute = f.tribute;
}
function getFee(address _from) internal
returns (Fee)
{
Fee memory generalFee = feeCatalog[_from][address(0)];
Fee memory specificFee = feeCatalog[_from][msg.sender];
return specificFee.amount > 0 ? specificFee : generalFee;
}
function deposit(uint256 _value) public {
require(_value > 0);
balances[msg.sender] += _value;
require(SNT.transferFrom(msg.sender, this, _value));
require(SNT.transferFrom(msg.sender, address(this), _value));
}
function balance() public view returns (uint256) {
@ -72,7 +93,8 @@ contract MessageTribute is Controlled {
function withdraw(uint256 _value) public {
require(balances[msg.sender] > 0);
require(_value <= balances[msg.sender]);
require(SNT.transferFrom(msg.sender, this, _value));
balances[msg.sender] -= _value;
require(SNT.transfer(msg.sender, _value));
}
event AudienceRequested(address from, address to);
@ -91,6 +113,11 @@ contract MessageTribute is Controlled {
balances[msg.sender] -= f.amount;
}
function hasPendingAudience(address _from)
public view returns (bool) {
return audienceRequested[_from][msg.sender].blockNum > 0;
}
function cancelAudienceRequest(address _from) public {
if (audienceRequested[_from][msg.sender].blockNum > 0) {
AudienceCancelled(_from, msg.sender);
@ -99,7 +126,7 @@ contract MessageTribute is Controlled {
}
}
function grantAudience(address _to, bool _approve) {
function grantAudience(address _to, bool _approve) public {
Audience memory aud = audienceRequested[msg.sender][_to];
@ -132,6 +159,18 @@ contract MessageTribute is Controlled {
return getFee(_to).amount <= balances[msg.sender];
}
function getFee(address _from) internal view
returns (Fee)
{
Fee memory generalFee = feeCatalog[_from][address(0)];
Fee memory specificFee = feeCatalog[_from][msg.sender];
if (friendIndex[keccak256(msg.sender, _from)] > 0)
return Fee(0, false, false);
return specificFee.amount > 0 ? specificFee : generalFee;
}
function clearFee(address _from, address _to) private {
if (!feeCatalog[_from][_to].permanent) {
feeCatalog[_from][_to].amount = 0;
@ -139,5 +178,7 @@ contract MessageTribute is Controlled {
feeCatalog[_from][_to].permanent = false;
}
}
}

312
test/messageTribute.js Normal file
View File

@ -0,0 +1,312 @@
const TestUtils = require("../utils/testUtils.js");
describe('MessageTribute', function() {
let accounts;
let SNT;
this.timeout(0);
before( function(done) {
this.timeout(0);
EmbarkSpec.deployAll({
"MiniMeTokenFactory": {},
"MiniMeToken": {
"args": [
"$MiniMeTokenFactory",
"0x0",
"0x0",
"Status Test Token",
18,
"STT",
true
]
},
"MessageTribute": {
"args": ["$MiniMeToken"]
}
}, (_accounts) => {
accounts = _accounts;
SNT = MiniMeToken;
Promise.all([
SNT.methods.generateTokens(accounts[0], 5000).send(),
SNT.methods.generateTokens(accounts[1], 5000).send(),
SNT.methods.generateTokens(accounts[2], 5000).send(),
SNT.methods.generateTokens(accounts[3], 5000).send(),
SNT.methods.generateTokens(accounts[4], 5000).send(),
SNT.methods.generateTokens(accounts[5], 5000).send(),
SNT.methods.generateTokens(accounts[6], 5000).send(),
SNT.methods.generateTokens(accounts[7], 5000).send(),
SNT.methods.generateTokens(accounts[8], 5000).send(),
SNT.methods.generateTokens(accounts[9], 5000).send()
])
.then(() => {
console.log(" - Added balances");
done();
});
});
});
it("Adding friends", async () => {
await MessageTribute.methods.addFriends([accounts[1], accounts[2]]).send({from: accounts[0]});
await MessageTribute.methods.addFriends([accounts[3]]).send({from: accounts[1]});
await MessageTribute.methods.addFriends([accounts[4]]).send({from: accounts[1]});
await MessageTribute.methods.addFriends([accounts[5]]).send({from: accounts[1]});
assert.equal(
await MessageTribute.methods.isFriend(accounts[1]).call({from: accounts[0]}),
true,
accounts[1] + " must be a friend of " + accounts[0]);
assert.equal(
await MessageTribute.methods.isFriend(accounts[3]).call({from: accounts[1]}),
true,
accounts[3] + " must be a friend of " + accounts[1]);
assert.equal(
await MessageTribute.methods.isFriend(accounts[4]).call({from: accounts[0]}),
false,
accounts[4] + " must not be a friend of " + accounts[0]);
assert.equal(
await MessageTribute.methods.isFriend(accounts[2]).call({from: accounts[1]}),
false,
accounts[2] + " must not be a friend of " + accounts[1]);
});
it("Removing friends", async () => {
await MessageTribute.methods.removeFriends([accounts[3]]).send({from: accounts[1]});
await MessageTribute.methods.removeFriends([accounts[4], accounts[5]]).send({from: accounts[1]});
assert.equal(
await MessageTribute.methods.isFriend(accounts[3]).call({from: accounts[0]}),
false,
accounts[3] + " must not be a friend of " + accounts[0]);
assert.equal(
await MessageTribute.methods.isFriend(accounts[4]).call({from: accounts[0]}),
false,
accounts[4] + " must not be a friend of " + accounts[0]);
try {
let tx = await MessageTribute.methods.removeFriends([accounts[5]]).send({from: accounts[1]});
assert.fail('should have reverted before');
} catch(error) {
TestUtils.assertJump(error);
}
});
it("Should be able to deposit", async() => {
let amount = 2000;
await SNT.methods.approve(MessageTribute.address, amount).send({from: accounts[1]});
let initialBalance = await SNT.methods.balanceOf(accounts[1]).call();
await MessageTribute.methods.deposit(amount).send({from: accounts[1]});
assert.equal(
await MessageTribute.methods.balance().call({from: accounts[1]}),
amount,
"Deposited balance must be " + amount);
assert.equal(
await SNT.methods.balanceOf(accounts[1]).call(),
web3.utils.toBN(initialBalance).sub(web3.utils.toBN(amount)).toString(),
accounts[1] + " SNT balance is incorrect");
assert.equal(
await SNT.methods.balanceOf(MessageTribute.address).call(),
amount,
"Contract SNT balance is incorrect");
});
it("Should be able to withdraw", async() => {
let amount = 2000;
let initialBalance = await SNT.methods.balanceOf(accounts[1]).call();
await MessageTribute.methods.withdraw(amount).send({from: accounts[1]});
assert.equal(
await SNT.methods.balanceOf(accounts[1]).call(),
web3.utils.toBN(initialBalance).add(web3.utils.toBN(amount)).toString(),
"SNT Balance must be " + initialBalance + amount);
assert.equal(
await SNT.methods.balanceOf(MessageTribute.address).call(),
0,
"Contract SNT balance is incorrect");
assert.equal(
await MessageTribute.methods.balance().call({from: accounts[1]}),
0,
"Deposited balance must be 0");
});
it("Requesting audience without requiring deposit", async () => {
assert.equal(
await MessageTribute.methods.balance().call({from: accounts[9]}),
0,
"Deposited balance must be 0");
let tx = await MessageTribute.methods.requestAudience(accounts[0]).send({from: accounts[9]});
assert.notEqual(tx.events.AudienceRequested, undefined, "AudienceRequested wasn't triggered");
});
it("Requesting audience requiring deposit and no funds deposited", async () => {
await MessageTribute.methods.setRequiredTribute(accounts[9], 100, false, true).send({from: accounts[0]});
assert.equal(
await MessageTribute.methods.balance().call({from: accounts[9]}),
0,
"Deposited balance must be 0");
assert.equal(
await MessageTribute.methods.hasEnoughFundsToTalk(accounts[0]).call({from: accounts[9]}),
false,
"Must return false");
try {
let tx = await MessageTribute.methods.requestAudience(accounts[0]).send({from: accounts[9]});
assert.fail('should have reverted before');
} catch(error) {
TestUtils.assertJump(error);
}
});
it("Requesting an audience as a friend", async() => {
assert.equal(
await MessageTribute.methods.isFriend(accounts[2]).call({from: accounts[0]}),
true,
"Should be friends");
assert.equal(
await MessageTribute.methods.balance().call({from: accounts[2]}),
0,
"Deposited balance must be 0");
assert.equal(
await MessageTribute.methods.hasEnoughFundsToTalk(accounts[0]).call({from: accounts[2]}),
true,
"Must return true");
let tx = await MessageTribute.methods.requestAudience(accounts[0]).send({from: accounts[2]});
assert.notEqual(tx.events.AudienceRequested, undefined, "AudienceRequested wasn't triggered");
});
it("Request audience requiring deposit, not having funds at the beginning, deposit funds and request audience", async () => {
await MessageTribute.methods.setRequiredTribute(accounts[8], 200, false, true).send({from: accounts[0]});
await SNT.methods.approve(MessageTribute.address, 100).send({from: accounts[8]});
await MessageTribute.methods.deposit(100).send({from: accounts[8]});
assert.equal(
await MessageTribute.methods.balance().call({from: accounts[8]}),
100,
"Deposited balance must be 100");
assert.equal(
await MessageTribute.methods.hasEnoughFundsToTalk(accounts[0]).call({from: accounts[8]}),
false,
"Must return false");
try {
let tx = await MessageTribute.methods.requestAudience(accounts[0]).send({from: accounts[8]});
assert.fail('should have reverted before');
} catch(error) {
TestUtils.assertJump(error);
}
await SNT.methods.approve(MessageTribute.address, 100).send({from: accounts[8]});
await MessageTribute.methods.deposit(100).send({from: accounts[8]});
assert.equal(
await MessageTribute.methods.balance().call({from: accounts[8]}),
200,
"Deposited balance must be 200");
assert.equal(
await MessageTribute.methods.hasEnoughFundsToTalk(accounts[0]).call({from: accounts[8]}),
true,
"Must return true");
let tx = await MessageTribute.methods.requestAudience(accounts[0]).send({from: accounts[8]});
assert.notEqual(tx.events.AudienceRequested, undefined, "AudienceRequested wasn't triggered");
});
it("Requesting tribute from specific account", async() => {
await MessageTribute.methods.setRequiredTribute(accounts[7], 100, true, true).send({from: accounts[0]});
await SNT.methods.approve(MessageTribute.address, 200).send({from: accounts[7]});
await MessageTribute.methods.deposit(200).send({from: accounts[7]});
assert.equal(
await MessageTribute.methods.balance().call({from: accounts[7]}),
200,
"Deposited balance must be 200");
let tx = await MessageTribute.methods.requestAudience(accounts[0]).send({from: accounts[7]});
assert.notEqual(tx.events.AudienceRequested, undefined, "AudienceRequested wasn't triggered");
assert.equal(
await MessageTribute.methods.balance().call({from: accounts[7]}),
100,
"Deposited balance must be 100");
});
it("Missing tests", async () => {
// Cancelling Audience - account8
// Granting Audience - account7
// Deniying Audience
/*
// Requiring 200 tribute from account6 non permanent
await MessageTribute.methods.setRequiredTribute(accounts[4], 200, true, false).send({from: accounts[0]});
// Requiring 100 deposit from everyone
await MessageTribute.methods.setRequiredTribute("0x0", 100, false, true).send({from: accounts[0]});
// Requiring 100 tribute from everyone
await MessageTribute.methods.setRequiredTribute("0x0", 100, true, true).send({from: accounts[1]});
*/
});
it("Requesting a non permanent tribute from specific account", async() => {
await MessageTribute.methods.setRequiredTribute(accounts[6], 100, true, false).send({from: accounts[0]});
await SNT.methods.approve(MessageTribute.address, 200).send({from: accounts[6]});
await MessageTribute.methods.deposit(200).send({from: accounts[6]});
let tx = await MessageTribute.methods.requestAudience(accounts[0]).send({from: accounts[6]});
assert.notEqual(tx.events.AudienceRequested, undefined, "AudienceRequested wasn't triggered");
assert.equal(
await MessageTribute.methods.balance().call({from: accounts[6]}),
100,
"Deposited balance must be 100");
// TODO grant audience
// TODO request another audience
});
});