Skip to content

Commit

Permalink
refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
algoidan committed Nov 3, 2022
1 parent 4cb742b commit 3d1c510
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 20 deletions.
4 changes: 2 additions & 2 deletions ledger/apply/stateproof.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func StateProof(tx transactions.StateProofTxnFields, atRound basics.Round, sp St
if config.Consensus[atRoundHdr.CurrentProtocol].StateProofUseTrackerVerification {
verificationData, err = sp.StateProofVerificationData(lastRoundInInterval)
} else {
verificationData, err = gatherVerificationDataUsingHeaders(sp, lastRoundInInterval)
verificationData, err = gatherVerificationDataUsingBlockHeaders(sp, lastRoundInInterval)
}
if err != nil {
return err
Expand All @@ -73,7 +73,7 @@ func StateProof(tx transactions.StateProofTxnFields, atRound basics.Round, sp St
return nil
}

func gatherVerificationDataUsingHeaders(sp StateProofsApplier, lastRoundInInterval basics.Round) (*ledgercore.StateProofVerificationData, error) {
func gatherVerificationDataUsingBlockHeaders(sp StateProofsApplier, lastRoundInInterval basics.Round) (*ledgercore.StateProofVerificationData, error) {
lastRoundHdr, err := sp.BlockHdr(lastRoundInInterval)
if err != nil {
return nil, err
Expand Down
42 changes: 37 additions & 5 deletions ledger/internal/eval_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,8 @@ func TestCowStateProofV34(t *testing.T) {
var validate bool
msg := stateproofmsg.Message{}

const version = protocol.ConsensusV34

accts0 := ledgertesting.RandomAccounts(20, true)
blocks := make(map[basics.Round]bookkeeping.BlockHeader)
blockErr := make(map[basics.Round]error)
Expand Down Expand Up @@ -242,12 +244,20 @@ func TestCowStateProofV34(t *testing.T) {
err = apply.StateProof(stateProofTx, atRound, c0, validate)
require.ErrorIs(t, err, apply.ErrExpectedDifferentStateProofRound)

// no atRound block
noBlockErr := errors.New("no block")
blockErr[atRound] = noBlockErr
stateProofTx.Message.LastAttestedRound = 32
err = apply.StateProof(stateProofTx, atRound, c0, validate)
require.ErrorIs(t, err, noBlockErr)
delete(blockErr, atRound)

atRoundBlock := bookkeeping.BlockHeader{}
atRoundBlock.CurrentProtocol = protocol.ConsensusV34
atRoundBlock.CurrentProtocol = version
blocks[atRound] = atRoundBlock

// no spRnd block
noBlockErr := errors.New("no block")
noBlockErr = errors.New("no block")
blockErr[32] = noBlockErr
stateProofTx.Message.LastAttestedRound = 32
err = apply.StateProof(stateProofTx, atRound, c0, validate)
Expand All @@ -273,10 +283,32 @@ func TestCowStateProofV34(t *testing.T) {
require.Contains(t, err.Error(), "no block")
delete(blockErr, 13)

// fall through to no err
validate = false
// check the happy flow - we should fail only on crypto
atRound = 800
spHdr = bookkeeping.BlockHeader{}
spHdr.CurrentProtocol = version
blocks[basics.Round(2*config.Consensus[version].StateProofInterval)] = spHdr

votersHdr := bookkeeping.BlockHeader{}
votersHdr.CurrentProtocol = version
stateproofTracking := bookkeeping.StateProofTrackingData{
StateProofVotersCommitment: []byte{0x1}[:],
StateProofOnlineTotalWeight: basics.MicroAlgos{Raw: 5},
}
votersHdr.StateProofTracking = make(map[protocol.StateProofType]bookkeeping.StateProofTrackingData)
votersHdr.StateProofTracking[protocol.StateProofBasic] = stateproofTracking

blocks[basics.Round(config.Consensus[version].StateProofInterval)] = votersHdr
atRoundBlock = bookkeeping.BlockHeader{}
atRoundBlock.CurrentProtocol = version
blocks[atRound] = atRoundBlock

stateProofTx.Message.LastAttestedRound = 2 * config.Consensus[version].StateProofInterval
stateProofTx.StateProof.SignedWeight = 100
c0.SetStateProofNextRound(basics.Round(2 * config.Consensus[version].StateProofInterval))

err = apply.StateProof(stateProofTx, atRound, c0, validate)
require.NoError(t, err)
require.Contains(t, err.Error(), "crypto error")
}

func TestCowStateProof(t *testing.T) {
Expand Down
25 changes: 12 additions & 13 deletions stateproof/verify/stateproof.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,58 +50,57 @@ func AcceptableStateProofWeight(votersHdr *bookkeeping.BlockHeader, firstValid b
latestRoundInProof := votersHdr.Round + basics.Round(proto.StateProofInterval)
total := votersHdr.StateProofTracking[protocol.StateProofBasic].StateProofOnlineTotalWeight

return calculateAcceptableStateProofWeight(total, proto.StateProofInterval, proto.StateProofWeightThreshold, latestRoundInProof, firstValid, logger)
return calculateAcceptableStateProofWeight(total, &proto, latestRoundInProof, firstValid, logger)
}

func calculateAcceptableStateProofWeight(totalOnline basics.MicroAlgos, stateProofInterval uint64, provenWeightThreshold uint32, lastAttestedRound basics.Round, firstValid basics.Round, logger logging.Logger) uint64 {

halfPeriodForInterval := stateProofInterval / 2
func calculateAcceptableStateProofWeight(total basics.MicroAlgos, proto *config.ConsensusParams, lastAttestedRound basics.Round, firstValid basics.Round, logger logging.Logger) uint64 {
halfPeriodForInterval := proto.StateProofInterval / 2
// The acceptable weight depends on the elapsed time (in rounds)
// from the block we are trying to construct a proof for.
// Start by subtracting the latest round number in the state proof interval.
// If that round hasn't even passed yet, require 100% votes in proof.
offset := firstValid.SubSaturate(lastAttestedRound)
if offset == 0 {
return totalOnline.ToUint64()
return total.ToUint64()
}

// During the first proto.StateProofInterval/2 blocks, the
// signatures are still being broadcast, so, continue requiring
// 100% votes.
offset = offset.SubSaturate(basics.Round(halfPeriodForInterval))
if offset == 0 {
return totalOnline.ToUint64()
return total.ToUint64()
}

// In the next proto.StateProofInterval/2 blocks, linearly scale
// the acceptable weight from 100% to StateProofWeightThreshold.
// If we are outside of that window, accept any weight at or above
// StateProofWeightThreshold.
provenWeight, overflowed := basics.Muldiv(totalOnline.ToUint64(), uint64(provenWeightThreshold), 1<<32)
if overflowed || provenWeight > totalOnline.ToUint64() {
provenWeight, overflowed := basics.Muldiv(total.ToUint64(), uint64(proto.StateProofWeightThreshold), 1<<32)
if overflowed || provenWeight > total.ToUint64() {
// Shouldn't happen, but a safe fallback is to accept a larger proof.
logger.Warnf("calculateAcceptableStateProofWeight(%d, %d, %d, %d) overflow provenWeight",
totalOnline, stateProofInterval, lastAttestedRound, firstValid)
total, proto.StateProofInterval, lastAttestedRound, firstValid)
return 0
}

if offset >= basics.Round(halfPeriodForInterval) {
return provenWeight
}

scaledWeight, overflowed := basics.Muldiv(totalOnline.ToUint64()-provenWeight, halfPeriodForInterval-uint64(offset), halfPeriodForInterval)
scaledWeight, overflowed := basics.Muldiv(total.ToUint64()-provenWeight, halfPeriodForInterval-uint64(offset), halfPeriodForInterval)
if overflowed {
// Shouldn't happen, but a safe fallback is to accept a larger state proof.
logger.Warnf("calculateAcceptableStateProofWeight(%d, %d, %d, %d) overflow scaledWeight",
totalOnline, stateProofInterval, lastAttestedRound, firstValid)
total, proto.StateProofInterval, lastAttestedRound, firstValid)
return 0
}

w, overflowed := basics.OAdd(provenWeight, scaledWeight)
if overflowed {
// Shouldn't happen, but a safe fallback is to accept a larger state proof.
logger.Warnf("calculateAcceptableStateProofWeight(%d, %d, %d, %d) overflow provenWeight (%d) + scaledWeight (%d)",
totalOnline, stateProofInterval, lastAttestedRound, firstValid, provenWeight, scaledWeight)
total, proto.StateProofInterval, lastAttestedRound, firstValid, provenWeight, scaledWeight)
return 0
}

Expand Down Expand Up @@ -152,7 +151,7 @@ func ValidateStateProof(verificationData *ledgercore.StateProofVerificationData,
return fmt.Errorf("state proof at %d for non-multiple of %d: %w", verificationData.TargetStateProofRound, proto.StateProofInterval, errNotAtRightMultiple)
}

acceptableWeight := calculateAcceptableStateProofWeight(verificationData.OnlineTotalWeight, proto.StateProofInterval, proto.StateProofWeightThreshold, verificationData.TargetStateProofRound, atRound, logging.Base())
acceptableWeight := calculateAcceptableStateProofWeight(verificationData.OnlineTotalWeight, &proto, verificationData.TargetStateProofRound, atRound, logging.Base())
if stateProof.SignedWeight < acceptableWeight {
return fmt.Errorf("insufficient weight at round %d: %d < %d: %w",
atRound, stateProof.SignedWeight, acceptableWeight, errInsufficientWeight)
Expand Down

0 comments on commit 3d1c510

Please sign in to comment.