Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

96 update accusal flow #101

Merged
merged 11 commits into from
Nov 21, 2022
Merged

96 update accusal flow #101

merged 11 commits into from
Nov 21, 2022

Conversation

gh-23378
Copy link
Contributor

No description provided.

@gh-23378 gh-23378 linked an issue Nov 18, 2022 that may be closed by this pull request
archaeologistParams: ArchaeologistParameters
): Promise<SignerWithAddress> => {
// calculate archaeologist's minimum digging fee and free bond in sarquitos
const archMinDiggingFeeSarquitos = ethers.utils
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling quintillionths of a sarco token sarquitos until we have official naming from product. Easy find and replace once we've agreed on terminology.

@@ -1,568 +0,0 @@
import "@nomiclabs/hardhat-waffle";
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deleted the entire file because we didn't have any other third party facet functions being tested here

for (uint256 i = 0; i < archaeologistAddresses.length; i++) {
// if the archaeologist has never been accused, release their locked bond back to them
if (!s.sarcophagusArchaeologists[sarcoId][archaeologistAddresses[i]].accused) {
LibBonds.freeArchaeologist(sarcoId, archaeologistAddresses[i]);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the original version of this function, we don't refund the digging fees allocated to well-behaved archaeologists back to the embalmer. In this PR I've preserved that functionality but I think we ought to provide the full refund because the archaeologists aren't receiving those digging fees if the sarcophagus is being closed down and the embalmer isn't receiving the services they've paid for.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also I'm wondering, could we not remove the need for this extra loop by doing the freeArchaeologist in the loop on 172 above?
...
No, no we can't. We can only free if we know the sarco is compromised.

How about instead using an in-memory list of goodArchAddresses that is added to during the first loop, then this loop would only go through that list and free them all?

Accuse has always been a loop-loving hell-hole, I remember now.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first loop only runs through archaeologists that have leaked keyshares and not the full list of archaeologists on the sarcophagus, so unfortunately there's no way to track historical accusations at that stage. Good thought though, I optimized as much as possible but the looping in this function is pretty aggressive anyway.

@gh-23378 gh-23378 requested a review from knoll3 November 18, 2022 07:36
@gh-23378
Copy link
Contributor Author

Besides the accuse function itself, the bulk of this PR is in accuse.spec.ts which is collapsed by default and comes up at the very end of the other tests so don't miss it! There's some duplicate functionality between the utilities I've created and the existing fixtures but my goal with these utilities is to break things up into smaller pieces so we can more easily generate single components of our tests. They're a WIP and I'll be adding to them as I knock out some of the other contract tasks.

});
});

// todo: can an async context be used with mocha?
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that this is something that can actually be done with mocha, but if anybody has suggestions on better ways to organize a set of tests where there's some asynchronous configuration and then several tests that use that configuration I'd like to hear them. Currently, there's a lot of duplication across tests because I'm having to set up the sarcophagus and accusal call over and over again.

Below is an example of what I'd like to be able to write, but context is an alias for describe which cannot take an async callback. We have the option to run mocha with --delay to do some asynchronous configuration before running the suite but I don't think it gives us the organizational advantage that context/describe do.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

beforeEach, maybe?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

beforeEach would execute code but I'd like to have access to the things being created in the beforeEach within the tests themselves. I think the best I could do would be to pull the common code out into another function and then call that at the start of each it callback. I just really liked the test structure we could have achieved if context worked asynchronously.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is what Kelvin is suggesting

let sarcohpagus: Sarcophagus;
...

beforeEach(async () => {
  sarcohpagus = await getSarcophagus();
  ...
})

it("should do stuff", () => {
  // use sarcophagus var
})

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What you're describing has always been a pain point for me that has made me rip what little hair I have left out of my head. I still haven't come to a satisfactory answer for this, but extracting common code into fixtures is the way I usually go. This is what the current test fixtures are supposed to do, but I'm not sure if they've retained their original purpose over time.

@gh-23378 gh-23378 marked this pull request as ready for review November 18, 2022 07:45
Copy link
Contributor

@DarksightKellar DarksightKellar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoo. This is a beefy one, and I'm ashamed to say I didn't read through every line.
The accuse tests are nice and specific, as they should be.

I did start to get the feeling that a number of the new helper functions are already implemented in some form in the existing fixtures. Is the intention to phase them out eventually, or to use both (maybe the new functions solve a different problem)?
EDIT: Never mind I somehow missed your comment saying this exact same thing.

for (uint256 i = 0; i < archaeologistAddresses.length; i++) {
// if the archaeologist has never been accused, release their locked bond back to them
if (!s.sarcophagusArchaeologists[sarcoId][archaeologistAddresses[i]].accused) {
LibBonds.freeArchaeologist(sarcoId, archaeologistAddresses[i]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also I'm wondering, could we not remove the need for this extra loop by doing the freeArchaeologist in the loop on 172 above?
...
No, no we can't. We can only free if we know the sarco is compromised.

How about instead using an in-memory list of goodArchAddresses that is added to during the first loop, then this loop would only go through that list and free them all?

Accuse has always been a loop-loving hell-hole, I remember now.

@@ -65,6 +65,7 @@ library LibTypes {
// of the archaeologist uploading to arweave, which will be stored directly
// on the sarcophagus.
struct ArchaeologistStorage {
bool accused;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isAccused? 😬

});

describe("Supports accusal of fewer than k archaeologists", function () {
it("On a successful accusal of an archaeologist, should transfer the correct amount of funds to embalmer and accuser, slash the archaeologist's bond, mark the arch as accused, and emit an AccuseArchaeologist event", async function () {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd very much rather split up these (and other similarly combined) assertions into their own it-test blocks -- even if it means duplicate code.

Specifically, either a describe or context with "On successful accusal of an archaeologist", within which we have a distinct it "should do xyz" for each expectation.

I'm sure beforeEach would be enough to do the prerequisite setup being done below.

It's much easier to digest that way, and easier to debug as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

100%, I would love to be able to set up this test set with a context and then have an individual it() for most of these expect calls. I thought about breaking these up into more its and duplicating the setup, but even with utility methods it ends up being a pretty significant amount of boilerplate getting copied between cases.

"Not enough test accounts. Increase value in hardhat config."
);
return await ethers.getSigner(unnamedAccounts[index++]);
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, nice

totalCursedBond,
diggingFeesToBeDistributed
totalDiggingFees,
totalDiggingFees
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we not now remove one of these params?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

haha definitely


// if the current call hasn't resulted in at least k archaeologists being accused
// check if total number of historical accusals on sarcophagus is greater than k
uint historicalAccusals = 0;
Copy link
Contributor

@knoll3 knoll3 Nov 18, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to store historicalAccusals in sarcoState to avoid the loop below?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking that it's better to have the accuser incur the cost of the loop than it is to add to the data model because accuse isn't going to be called often (and the caller is getting a payout) but an increase in the size of the Sarcophagus model impacts all users.

* @param sarcoId The identifier of the sarcophagus having leaked shards
* @param unencryptedShardHashes At least 'm' unencrypted shard hashes as proof of bad behaviour
* @param unencryptedShardHashes hashes of the leaked keyshares
* @param paymentAddress the address to which rewards should be sent if successful
*/
function accuse(
bytes32 sarcoId,
bytes32[] memory unencryptedShardHashes,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the purpose for being able to accuse multiple archs in one function? Is it just to save on transaction costs if an accuser has multiple accusals to make?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

savings on transaction cost and also if we required accusals to be one at a time, we'd be limiting rewards to the bonds for k archaeologists. With the array, an accuser could get a payout for all n if they'd all leaked their keyshare but with individual calls, the remaining n-k would be freed as soon as the sarcophagus was deemed compromised.

Comment on lines 170 to 181
uint historicalAccusals = 0;
if (!isSarcophagusCompromised) {
for (uint256 i = 0; i < archaeologistAddresses.length; i++) {
if (s.sarcophagusArchaeologists[sarcoId][archaeologistAddresses[i]].accused) {
historicalAccusals++;
}
}
// the sarcophagus is compromised if k or more archaeologists have been accused over the lifetime of the sarcophagus
if (historicalAccusals >= sarco.minShards) {
isSarcophagusCompromised = true;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function looks good. My only thought is whether we could get significant gas savings for the user if they were only submitting a single hash --- this would require an alternate accuseSingle function, where there's only a single hash passed in. In that scenario, we would probably need a lot less code (could just iterate over all the archaeologists on the sarcophagus once, then do the distributions).

I'm not suggesting we necessarily do this, but could discuss.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we'd actually be able to remove any of the logic other than the part that says to loop over the set of supplied keyshare hashes. We'd still need to take the same actions on the single supplied share, so the logic inside of the loop would stay, and then we'd also need to evaluate whether or not the single archaeologist being accused brought the sarcophagus over the k threshold and if yes free the other archaeologists.

@sethhrbek sethhrbek merged commit fa31111 into main Nov 21, 2022
@sethhrbek sethhrbek deleted the 96-update-accusal-flow branch November 21, 2022 21:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Update accusal flow
4 participants