Limit effects of negative citations
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 40s Details

This commit is contained in:
Ladd Hoffman 2024-04-12 16:54:48 -05:00
parent 1a4c5c338d
commit a7f3e5f2e4
3 changed files with 167 additions and 18 deletions

View File

@ -21,9 +21,12 @@ struct Post {
contract Forum is Reputation {
mapping(uint => Post) public posts;
uint public postCount;
mapping(uint => mapping(uint => int)) _edgeBalances;
event PostAdded(uint postIndex);
// TODO: Add forum parameters
function addPost(
address author,
string calldata contentId,
@ -59,25 +62,89 @@ contract Forum is Reputation {
}
function _onValidatePost(uint postIndex, uint amount) internal {
_propagateValue(postIndex, int(amount));
_propagateReputation(postIndex, int(amount), false, 0);
}
function _propagateValue(uint postIndex, int amount) internal {
Post storage post = posts[postIndex];
function _handleCitation(
uint postIndex,
Citation memory citation,
int amount,
bool initialNegative,
uint depth
) internal returns (int outboundAmount) {
outboundAmount = (amount * citation.weightPercent) / 100;
int balanceToOutbound = _edgeBalances[postIndex][
citation.targetPostIndex
];
if (initialNegative) {
if (outboundAmount < 0) {
outboundAmount = outboundAmount > -balanceToOutbound
? outboundAmount
: -balanceToOutbound;
} else {
outboundAmount = outboundAmount < -balanceToOutbound
? outboundAmount
: -balanceToOutbound;
}
}
int refund = _propagateReputation(
citation.targetPostIndex,
outboundAmount,
initialNegative || (depth == 0 && citation.weightPercent < 0),
depth + 1
);
outboundAmount -= refund;
_edgeBalances[postIndex][citation.targetPostIndex] += outboundAmount;
}
function _propagateReputation(
uint postIndex,
int amount,
bool initialNegative,
uint depth
) internal returns (int refundToInbound) {
Post storage post = posts[postIndex];
int totalOutboundAmount;
// Propagate negative citations first
for (uint i = 0; i < post.citations.length; i++) {
int share = (amount * post.citations[i].weightPercent) / 100;
totalOutboundAmount += share;
_propagateValue(post.citations[i].targetPostIndex, share);
if (post.citations[i].weightPercent < 0) {
int outboundAmount = _handleCitation(
postIndex,
post.citations[i],
amount,
initialNegative,
depth
);
totalOutboundAmount += outboundAmount;
}
}
// Now propagate positive citations
for (uint i = 0; i < post.citations.length; i++) {
if (post.citations[i].weightPercent > 0) {
int outboundAmount = _handleCitation(
postIndex,
post.citations[i],
amount,
initialNegative,
depth
);
totalOutboundAmount += outboundAmount;
}
}
int remaining = amount - totalOutboundAmount;
if (remaining > 0) {
_update(address(this), post.author, uint(remaining));
post.reputation += uint(remaining);
} else {
_update(post.author, address(this), uint(-remaining));
post.reputation -= uint(-remaining);
// Prevent reputation from being reduced below zero
if (int(post.reputation) + remaining >= 0) {
_update(post.author, address(this), uint(-remaining));
post.reputation -= uint(-remaining);
} else {
refundToInbound = int(post.reputation) + remaining;
_update(post.author, address(this), post.reputation);
post.reputation = 0;
}
}
}
}

View File

@ -56,8 +56,6 @@ contract ValidationPools is Reputation, Forum {
bool quorumMet
);
// TODO: Add forum parameters
/// Internal function to register a stake for/against a validation pool
function _stakeOnValidationPool(
ValidationPool storage pool,
@ -98,7 +96,6 @@ contract ValidationPools is Reputation, Forum {
}
/// Accept fee to initiate a validation pool
/// TODO: Handle multiple authors
function initiateValidationPool(
uint postIndex,
uint duration,
@ -140,10 +137,6 @@ contract ValidationPools is Reputation, Forum {
// TODO: Make minting ratio an adjustable parameter
_mint(address(this), msg.value);
pool.minted = msg.value;
// Here we assume a stakeForAuthor ratio of 0.5
// TODO: Make stakeForAuthor an adjustable parameter
// _stakeOnValidationPool(pool, post.author, msg.value / 2, true, true);
// _stakeOnValidationPool(pool, post.author, msg.value / 2, false, true);
emit ValidationPoolInitiated(poolIndex);
}
@ -176,7 +169,7 @@ contract ValidationPools is Reputation, Forum {
pool.params.quorum[1] * (stakedFor + stakedAgainst) <=
totalSupply() * pool.params.quorum[0]
) {
// Refund fee
// TODO: Refund fee
// TODO: this could be made available for the sender to withdraw
// payable(pool.sender).transfer(pool.fee);
pool.resolved = true;
@ -230,7 +223,9 @@ contract ValidationPools is Reputation, Forum {
}
if (votePasses) {
// If vote passes, reward the author as though they had staked the winnin portion of the VP initial stake
// If vote passes, reward the author as though they had staked the winning portion of the VP initial stake
// Here we assume a stakeForAuthor ratio of 0.5
// TODO: Make stakeForAuthor an adjustable parameter
totalRewards += pool.minted / 2;
uint reward = ((((totalRewards * pool.minted) / 2) /
amountFromWinners) * pool.params.bindingPercent) / 100;

View File

@ -30,7 +30,7 @@ describe('Forum', () => {
} = {}) => dao.initiateValidationPool(
postIndex ?? 0,
duration ?? POOL_DURATION,
quorum ?? [1, 3],
quorum ?? [1, 10],
winRatio ?? [1, 2],
bindingPercent ?? 100,
redistributeLosingStakes ?? true,
@ -114,5 +114,92 @@ describe('Forum', () => {
expect(await dao.balanceOf(account1)).to.equal(0);
expect(await dao.balanceOf(account2)).to.equal(100);
});
it('should be able to reverse a negative citation with a negative citation', async () => {
await dao.addPost(account1, 'content-id', []);
await initiateValidationPool({ postIndex: 0 });
await dao.evaluateOutcome(0);
expect(await dao.balanceOf(account1)).to.equal(100);
await dao.addPost(account2, 'second-content-id', [{ weightPercent: -100, targetPostIndex: 0 }]);
await initiateValidationPool({ postIndex: 1 });
await time.increase(POOL_DURATION + 1);
await dao.evaluateOutcome(1);
expect(await dao.balanceOf(account1)).to.equal(0);
expect(await dao.balanceOf(account2)).to.equal(200);
await dao.addPost(account3, 'third-content-id', [{ weightPercent: -100, targetPostIndex: 1 }]);
await initiateValidationPool({ postIndex: 2 });
await time.increase(POOL_DURATION + 1);
await dao.evaluateOutcome(2);
expect(await dao.balanceOf(account1)).to.equal(100);
expect(await dao.balanceOf(account2)).to.equal(0);
expect(await dao.balanceOf(account3)).to.equal(200);
});
it('forum reputation rewards are shared with validation pool policing rewards', async () => {
await dao.addPost(account1, 'content-id', []);
await initiateValidationPool({ postIndex: 0 });
await dao.evaluateOutcome(0);
expect(await dao.balanceOf(account1)).to.equal(100);
await dao.addPost(account2, 'second-content-id', []);
await initiateValidationPool({ postIndex: 1 });
await time.increase(POOL_DURATION + 1);
await dao.evaluateOutcome(1);
expect(await dao.balanceOf(account1)).to.equal(100);
expect(await dao.balanceOf(account2)).to.equal(100);
await dao.addPost(account3, 'third-content-id', [{ weightPercent: -100, targetPostIndex: 1 }]);
await initiateValidationPool({ postIndex: 2 });
await dao.stakeOnValidationPool(2, 100, true);
await time.increase(POOL_DURATION + 1);
await dao.evaluateOutcome(2);
expect(await dao.balanceOf(account1)).to.equal(133);
expect(await dao.balanceOf(account2)).to.equal(34);
expect(await dao.balanceOf(account3)).to.equal(133);
});
it('should limit effects of negative references on prior positive references', async () => {
console.log('First post');
await dao.addPost(account1, 'content-id', []);
await initiateValidationPool({ postIndex: 0 });
await dao.evaluateOutcome(0);
expect(await dao.balanceOf(account1)).to.equal(100);
console.log('Second post');
await dao.addPost(account2, 'second-content-id', [{ weightPercent: 50, targetPostIndex: 0 }]);
await initiateValidationPool({ postIndex: 1 });
await time.increase(POOL_DURATION + 1);
await dao.evaluateOutcome(1);
expect(await dao.balanceOf(account1)).to.equal(150);
expect(await dao.balanceOf(account2)).to.equal(50);
console.log('Third post');
await dao.addPost(account3, 'third-content-id', [{ weightPercent: -100, targetPostIndex: 1 }]);
await initiateValidationPool({ postIndex: 2, fee: 200 });
await time.increase(POOL_DURATION + 1);
await dao.evaluateOutcome(2);
expect(await dao.balanceOf(account1)).to.equal(100);
expect(await dao.balanceOf(account2)).to.equal(0);
expect(await dao.balanceOf(account3)).to.equal(300);
});
it('should limit effects of negative references', async () => {
console.log('First post');
await dao.addPost(account1, 'content-id', []);
await initiateValidationPool({ postIndex: 0 });
await dao.evaluateOutcome(0);
expect(await dao.balanceOf(account1)).to.equal(100);
console.log('Second post');
await dao.addPost(account2, 'second-content-id', [{ weightPercent: -100, targetPostIndex: 0 }]);
await initiateValidationPool({ postIndex: 1 });
await time.increase(POOL_DURATION + 1);
await dao.evaluateOutcome(1);
expect(await dao.balanceOf(account1)).to.equal(0);
expect(await dao.balanceOf(account2)).to.equal(200);
console.log('Third post');
await dao.addPost(account3, 'third-content-id', [{ weightPercent: -100, targetPostIndex: 1 }]);
await initiateValidationPool({ postIndex: 2, fee: 200 });
await time.increase(POOL_DURATION + 1);
await dao.evaluateOutcome(2);
expect(await dao.balanceOf(account1)).to.equal(100);
expect(await dao.balanceOf(account2)).to.equal(0);
expect(await dao.balanceOf(account3)).to.equal(300);
});
});
});