Adds overflow test and better protection given limitations

This commit is contained in:
Andy Tudhope 2019-04-11 17:15:40 +02:00
parent 2889281dbb
commit afc8adb22c
No known key found for this signature in database
GPG Key ID: 02A3DFA93BF26AD2
2 changed files with 61 additions and 10 deletions

View File

@ -44,7 +44,7 @@ contract DAppStore is ApproveAndCallFallBack, BancorFormula {
mapping(bytes32 => uint) public id2index;
mapping(bytes32 => bool) existingIDs;
event DAppCreated(bytes32 indexed id, uint votesMint, uint amount);
event DAppCreated(bytes32 indexed id, uint newEffectiveBalance);
event Upvote(bytes32 indexed id, uint newEffectiveBalance);
event Downvote(bytes32 indexed id, uint newEffectiveBalance);
event Withdraw(bytes32 indexed id, uint newEffectiveBalance);
@ -61,7 +61,7 @@ contract DAppStore is ApproveAndCallFallBack, BancorFormula {
max = (total * ceiling) / decimals;
safeMax = 98 * max / 100;
safeMax = 77 * max / 100; // Limited by accuracy of BancorFormula
}
/**
@ -197,7 +197,7 @@ contract DAppStore is ApproveAndCallFallBack, BancorFormula {
uint dappIdx = id2index[_id];
Data memory d = dapps[dappIdx];
require(d.id == _id, "Error fetching correct data");
require(d.balance + _amount < safeMax, "You cannot upvote by this much, try with a lower amount");
require(d.balance + _amount <= safeMax, "You cannot upvote by this much, try with a lower amount");
// Special case - no downvotes yet cast
if (d.votesCast == 0) {
@ -250,7 +250,7 @@ contract DAppStore is ApproveAndCallFallBack, BancorFormula {
require(!existingIDs[_id], "You must submit a unique ID");
require(_amount > 0, "You must spend some SNT to submit a ranking in order to avoid spam");
require (_amount < safeMax, "You cannot stake more SNT than the ceiling dictates");
require (_amount <= safeMax, "You cannot stake more SNT than the ceiling dictates");
uint dappIdx = dapps.length;
@ -283,7 +283,7 @@ contract DAppStore is ApproveAndCallFallBack, BancorFormula {
require(SNT.allowance(_from, address(this)) >= _amount, "Not enough SNT allowance");
require(SNT.transferFrom(_from, address(this), _amount), "Transfer failed");
emit DAppCreated(_id, d.votesMinted, d.effectiveBalance);
emit DAppCreated(_id, d.effectiveBalance);
}
function _upvote(address _from, bytes32 _id, uint _amount) internal {
@ -293,7 +293,7 @@ contract DAppStore is ApproveAndCallFallBack, BancorFormula {
Data storage d = dapps[dappIdx];
require(d.id == _id, "Error fetching correct data");
require(d.balance + _amount < safeMax, "You cannot upvote by this much, try with a lower amount");
require(d.balance + _amount <= safeMax, "You cannot upvote by this much, try with a lower amount");
uint precision;
uint result;

View File

@ -45,10 +45,10 @@ contract("DAppStore", function () {
it("should set max and safeMax values correctly", async function () {
let resultMax = await DAppStore.methods.max().call();
let resultSafeMax = await DAppStore.methods.safeMax().call();
let expectedMax = Math.round(3470483788 * 588 / 1000000);
let expectedSafeMax = Math.round(expectedMax * 0.98);
assert.strictEqual(parseInt(resultMax, 10), expectedMax);
assert.strictEqual(parseInt(resultSafeMax, 10), expectedSafeMax);
let expectedMax = 3470483788 * 588 / 1000000;
let expectedSafeMax = expectedMax * 77 / 100 - 1;
assert.strictEqual(parseInt(resultMax, 10), Math.round(expectedMax));
assert.strictEqual(parseInt(resultSafeMax, 10), Math.round(expectedSafeMax));
});
it("should create a new DApp and initialise it correctly", async function () {
@ -590,4 +590,55 @@ contract("DAppStore", function () {
let e_balance_2 = parseInt(up_effect, 10) + parseInt(initial.effectiveBalance, 10);
assert.strictEqual(e_balance_2, parseInt(receipt.effectiveBalance, 10));
})
it("should create a DApp without overflowing", async function () {
let id = "0x0000000000000000000000000000000000000000000000000000000000000001";
let temp = await DAppStore.methods.safeMax().call();
let amount = parseInt(temp, 10);
await SNT.methods.generateTokens(accounts[0], amount).send();
const encodedCall = DAppStore.methods.createDApp(id,amount).encodeABI();
await SNT.methods.approveAndCall(DAppStore.options.address, amount, encodedCall).send({from: accounts[0]});
let receipt = await DAppStore.methods.dapps(1).call();
let developer = accounts[0];
assert.strictEqual(developer, receipt.developer);
assert.strictEqual(id, receipt.id);
// Having received the SNT, check that it updates the particular DApp's storage values
assert.strictEqual(amount, parseInt(receipt.balance, 10));
let max = await DAppStore.methods.max().call();
let decimals = await DAppStore.methods.decimals().call();
let rate = Math.ceil(decimals - (amount * decimals/max));
assert.strictEqual(rate, parseInt(receipt.rate, 10));
let available = amount * rate;
assert.strictEqual(available, parseInt(receipt.available, 10));
// It's checking that votesMinted doesn't overflow which we're really interested in here
let votes_minted = Math.round((available/decimals) ** (decimals/rate));
let returned = parseInt(receipt.votesMinted, 10);
// Is going to be less than due to rounding inaccuracies at higher exponents
assert.ok(returned <= votes_minted);
})
it("should prove we have the highest safeMax allowed for by Bancor's power approximation", async function () {
let id = "0x0000000000000000000000000000000000000000000000000000000000000002";
let max = await DAppStore.methods.max().call();
// Choose a safeMax 1% higher than is currently set
let safe = await DAppStore.methods.safeMax().call();
let amount = parseInt(max, 10) * (parseInt(safe, 10) + 1);
await SNT.methods.generateTokens(accounts[0], amount).send();
const encodedCall = DAppStore.methods.createDApp(id,amount).encodeABI();
try {
await SNT.methods.approveAndCall(DAppStore.options.address, amount, encodedCall).send({from: accounts[0]});
} catch (error) {
TestUtils.assertJump(error);
}
})
});