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

commit to Merkle tree of online participants in block header #1447

Merged
merged 30 commits into from
Sep 14, 2020

Conversation

zeldovich
Copy link
Contributor

@zeldovich zeldovich commented Aug 25, 2020

This PR commits to a Merkle tree of online participants in the block header, for use in validating compact certificates.

The main tricky aspect of this PR has to do with the fact that it takes a while to compute this Merkle tree, so the computation needs to happen over the course of several blocks, before the ledger can expect the Merkle root to be available.

Instead, add the normalized balance as a separate column to the existing
accountbase table, and construct an index on that table.
Resolve conflict in ledger/accountdb_test.go
the cert verification logic only checks the last reveal, since the
reveals get placed in a map, so duplicate reveals cause some reveals
to not be checked at all.
ledger/eval.go Outdated
@@ -762,6 +763,18 @@ func (eval *BlockEvaluator) endOfBlock() error {
} else {
eval.block.TxnCounter = 0
}

if eval.proto.CompactCertRounds > 0 && eval.block.Round()%basics.Round(eval.proto.CompactCertRounds) == 0 {
Copy link
Contributor

Choose a reason for hiding this comment

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

consider changing the CompactCertVoters(loopback) so that it have a similar signature to something like:

err := CompactCertVoters(basics.Round, *CompactCertVoters, *CompactCertVotersTotal)

so that you could collapse the logic you’ve added to finalValidation and endOfBlock into a single location.

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 reason the Ledger.CompactCertVoters() function returns the entire VotersForRound is that I need other parts from that VotersForRound in the rest of the code. Here's the complete compactcert implementation, if it's helpful, and the other place that I need to use Ledger.CompactCertVoters(): https://github.com/zeldovich/go-algorand/blob/compactcert/compactcert/builder.go#L48

But I agree it'd be good to factor out this common logic. Pushed that change.

// construct this Merkle tree, so as to avoid placing the
// construction of this Merkle tree (and obtaining the requisite
// accounts and balances) in the critical path.
CompactCertVotersLookback uint64
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you have any suggestion for good numbers for CompactCertVotersLookback and CompactCertRounds ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

CompactCertVotersLookback is relatively easy: something like 16 should be fine.

CompactCertRounds depends a lot on where we imagine using these compact certs, because it translates into how often you have to check compact certs (per unit time) to keep up with the state of the chain. I've been thinking of this being somewhere around 128-1024. Probably not smaller. Perhaps a bit longer depending on the use case (e.g., I could imagine this being 8K or 16K, so a compact cert once a day..)

var totalWeight basics.MicroAlgos

for i, acct := range top {
var ot basics.OverflowTracker
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: move the basics.OverflowTracker outside the for loop.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right that we could move it out (since, on each iteration of the loop, either ot remains non-overflowed, or the loop returns an error). But I think that would make the code harder to read, because ot is used independently for each iteration of the loop, and it's not used after the loop.

Copy link
Contributor

@tsachiherman tsachiherman left a comment

Choose a reason for hiding this comment

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

I think that overall, it looks great. One thing to add :
the votersTracker would need a "close()" function the waits until all the internal goroutines are done, and reset it's state so that when loadFromDisk is called, it would not retain any previous state.

You can do that by replacing the votesTracker in the acctupdates.go, or implement a cleanup in the votesTracker itself.

@zeldovich
Copy link
Contributor Author

Ah, good point about resetting the votersTracker on accountUpdates.loadFromDisk(). I pushed a fix for that, just re-allocating a fresh votersTracker as you suggested.

I think this is more-or-less good to go, then. I'll rebase against master and remove the draft flag when #1359 and #1361 get approved and merged.


// Wait for the Merkle tree to be constructed.
tr.mu.Lock()
for tr.Tree == nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

this could lead to a hang in case we had error during the VotersForRound::loadTree. maybe store the error generated during the VotersForRound::loadTree inside the VotersForRound, and make sure to broadcast when changing the error 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.

That seems like a good plan. I think originally I had no failure paths in the background loadTree() function, but indeed now there's possible errors that can cause this to hang forever. Pushed a revised version.

@zeldovich zeldovich changed the base branch from nickolai/compactcert-top-crypto to master September 9, 2020 02:12
@zeldovich zeldovich marked this pull request as ready for review September 9, 2020 02:13
Copy link
Contributor

@algorandskiy algorandskiy left a comment

Choose a reason for hiding this comment

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

Looks good to me

Comment on lines +386 to +390
func PendingRewards(ot *OverflowTracker, proto config.ConsensusParams, microAlgos MicroAlgos, rewardsBase uint64, rewardsLevel uint64) MicroAlgos {
rewardsUnits := microAlgos.RewardUnits(proto)
rewardsDelta := ot.Sub(rewardsLevel, rewardsBase)
return MicroAlgos{Raw: ot.Mul(rewardsUnits, rewardsDelta)}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

seems like this function needs to called in WithUpdatedRewards right 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.

Indeed it's basically the same code. The reason I didn't merge them is that I liked the detailed panic message that describes the internal computation steps that overflowed (in the off chance that we do trigger overflow), and the function below does one more overflow-checked addition that could overflow and cause that message to be printed (so it's not just a matter of moving the panic-on-error into this new PendingRewards())..

This code is rather deeply embedded in our protocol now, so it seemed not so bad to have a copy of this code, since it's not so likely to change.

Copy link
Contributor

@algonautshant algonautshant left a comment

Choose a reason for hiding this comment

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

Looks good.
I have couple of questions to better understand the code.

// The votersTracker maintains the Merkle tree for the most recent
// commitments to online accounts for compact certificates.
//
// We maintain multiple Merkle trees: we might commit to a new Merkle tree in
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you please give an example on how there are multiple Merkle trees?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What the comment refers to is that there might be multiple elements in the round map, and each one has a Merkle tree.

// are formed for blocks that are a multiple of CompactCertRounds, using
// the Merkle commitment to online accounts from the previous such block.
// Thus, we maintain X in the round map until we form a compact certificate
// for round X+CompactCertVotersLookback+CompactCertRounds.
Copy link
Contributor

Choose a reason for hiding this comment

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

if CompactCertVotersLookback is much smaller than CompactCertRounds, can this be X+CompactCertRounds?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is indeed the case that CompactCertVotersLookback is on the order of 16, and CompactCertRounds is probably something like 128 or 1024.

However, I believe the comment is correct: the two intervals have to be added together. We need the compact cert to certify the next block that is a multiple of CompactCertRounds, because that block will contain the commitment to the next group of voters (which will enable verifying the next compact cert, etc). And the CompactCertVotersLookback addition is needed to give the ledger time to compute the Merkle tree, in case there are many voters.

@tsachiherman tsachiherman merged commit cd76f72 into algorand:master Sep 14, 2020
@zeldovich zeldovich deleted the compactcert-voters branch September 14, 2020 16:45
tsachiherman pushed a commit to tsachiherman/go-algorand that referenced this pull request Jul 7, 2021
…d#1447)

commit to Merkle tree of online participants in block header
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.

4 participants