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

StateProofs: Use stateproof tracker #4733

Merged
4 changes: 4 additions & 0 deletions cmd/tealdbg/localLedger.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,10 @@ func (l *localLedger) BlockHdrCached(basics.Round) (bookkeeping.BlockHeader, err
return bookkeeping.BlockHeader{}, nil
}

func (l *localLedger) StateProofVerificationData(_ basics.Round) (*ledgercore.StateProofVerificationData, error) {
return nil, fmt.Errorf("localLedger: StateProofVerificationData is not implemented")
}

func (l *localLedger) CheckDup(config.ConsensusParams, basics.Round, basics.Round, basics.Round, transactions.Txid, ledgercore.Txlease) error {
return nil
}
Expand Down
5 changes: 5 additions & 0 deletions config/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,9 @@ type ConsensusParams struct {
// the rewardsLevel, but the rewardsLevel has no meaning because the account
// has fewer than RewardUnit algos.
UnfundedSenders bool

// StateProofUseTrackerVerification specifies whether the node will use the state proof verification tracker.
almog-t marked this conversation as resolved.
Show resolved Hide resolved
StateProofUseTrackerVerification bool
}

// PaysetCommitType enumerates possible ways for the block header to commit to
Expand Down Expand Up @@ -1250,6 +1253,8 @@ func initConsensusProtocols() {

vFuture.LogicSigVersion = 9 // When moving this to a release, put a new higher LogicSigVersion here

vFuture.StateProofUseTrackerVerification = true

Consensus[protocol.ConsensusFuture] = vFuture

// vAlphaX versions are an separate series of consensus parameters and versions for alphanet
Expand Down
6 changes: 5 additions & 1 deletion daemon/algod/api/server/v2/dryrun.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,10 @@ func (dl *dryrunLedger) CheckDup(config.ConsensusParams, basics.Round, basics.Ro
return nil
}

func (dl *dryrunLedger) StateProofVerificationData(_ basics.Round) (*ledgercore.StateProofVerificationData, error) {
return nil, fmt.Errorf("dryrunLedger: StateProofVerificationData is not implemented")
}

func (dl *dryrunLedger) lookup(rnd basics.Round, addr basics.Address) (basics.AccountData, basics.Round, error) {
// check accounts from debug records uploaded
out := basics.AccountData{}
Expand Down Expand Up @@ -474,7 +478,7 @@ func doDryrunRequest(dr *DryrunRequest, response *generated.DryrunResponse) {
}
}
if !found {
(*acct.AppsLocalState) = append(*acct.AppsLocalState, ls)
*acct.AppsLocalState = append(*acct.AppsLocalState, ls)
}
}
dl.dr.Accounts[idx] = acct
Expand Down
5 changes: 5 additions & 0 deletions ledger/acctupdates.go
Original file line number Diff line number Diff line change
Expand Up @@ -831,6 +831,11 @@ func (aul *accountUpdatesLedgerEvaluator) VotersForStateProof(rnd basics.Round)
return aul.ao.voters.getVoters(rnd)
}

func (aul *accountUpdatesLedgerEvaluator) StateProofVerificationData(_ basics.Round) (*ledgercore.StateProofVerificationData, error) {
// this function should not be used by accountUpdatesLedgerEvaluator
return nil, fmt.Errorf("accountUpdatesLedgerEvaluator: tried to get stateProofVerification data during accountUpdates initialization ")
}

// BlockHdr returns the header of the given round. When the evaluator is running, it's only referring to the previous header, which is what we
// are providing here. Any attempt to access a different header would get denied.
func (aul *accountUpdatesLedgerEvaluator) BlockHdr(r basics.Round) (bookkeeping.BlockHeader, error) {
Expand Down
1 change: 1 addition & 0 deletions ledger/apply/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ type StateProofsApplier interface {
BlockHdr(r basics.Round) (bookkeeping.BlockHeader, error)
GetStateProofNextRound() basics.Round
SetStateProofNextRound(rnd basics.Round)
StateProofVerificationData(stateProofLastAttestedRound basics.Round) (*ledgercore.StateProofVerificationData, error)
}

// Balances allow to move MicroAlgos from one address to another and to update balance records, or to access and modify individual balance records
Expand Down
54 changes: 39 additions & 15 deletions ledger/apply/stateproof.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/algorand/go-algorand/config"
"github.com/algorand/go-algorand/data/basics"
"github.com/algorand/go-algorand/data/transactions"
"github.com/algorand/go-algorand/ledger/ledgercore"
"github.com/algorand/go-algorand/protocol"
"github.com/algorand/go-algorand/stateproof/verify"
)
Expand All @@ -41,31 +42,54 @@ func StateProof(tx transactions.StateProofTxnFields, atRound basics.Round, sp St
}

lastRoundInInterval := basics.Round(tx.Message.LastAttestedRound)
lastRoundHdr, err := sp.BlockHdr(lastRoundInInterval)
if err != nil {
return err
}

nextStateProofRnd := sp.GetStateProofNextRound()
if nextStateProofRnd == 0 || nextStateProofRnd != lastRoundInInterval {
return fmt.Errorf("applyStateProof: %w - expecting state proof for %d, but new state proof is for %d",
ErrExpectedDifferentStateProofRound, nextStateProofRnd, lastRoundInInterval)
}

proto := config.Consensus[lastRoundHdr.CurrentProtocol]
if validate {
votersRnd := lastRoundInInterval.SubSaturate(basics.Round(proto.StateProofInterval))
votersHdr, err := sp.BlockHdr(votersRnd)
if err != nil {
return err
}
atRoundHdr, err := sp.BlockHdr(atRound)
if err != nil {
return err
}

var verificationData *ledgercore.StateProofVerificationData
if config.Consensus[atRoundHdr.CurrentProtocol].StateProofUseTrackerVerification {
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 think we should cover the verification method switch with a test?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There are two tests -
1 TestCowStateProof check the old code - verification using blockheader
2 TestCowStateProofV34 - using the tracker.

verificationData, err = sp.StateProofVerificationData(lastRoundInInterval)
} else {
verificationData, err = gatherVerificationDataUsingHeaders(sp, lastRoundInInterval)
}
if err != nil {
return err
}

err = verify.ValidateStateProof(&lastRoundHdr, &tx.StateProof, &votersHdr, atRound, &tx.Message)
if err != nil {
if validate {
if err = verify.ValidateStateProof(verificationData, &tx.StateProof, atRound, &tx.Message); err != nil {
return err
}
}

sp.SetStateProofNextRound(lastRoundInInterval + basics.Round(proto.StateProofInterval))
sp.SetStateProofNextRound(lastRoundInInterval + basics.Round(config.Consensus[verificationData.Version].StateProofInterval))
return nil
}

func gatherVerificationDataUsingHeaders(sp StateProofsApplier, lastRoundInInterval basics.Round) (*ledgercore.StateProofVerificationData, error) {
lastRoundHdr, err := sp.BlockHdr(lastRoundInInterval)
if err != nil {
return nil, err
}
proto := config.Consensus[lastRoundHdr.CurrentProtocol]
votersRnd := lastRoundInInterval.SubSaturate(basics.Round(proto.StateProofInterval))
votersHdr, err := sp.BlockHdr(votersRnd)
if err != nil {
return nil, err
}

verificationData := ledgercore.StateProofVerificationData{
TargetStateProofRound: lastRoundInInterval,
VotersCommitment: votersHdr.StateProofTracking[protocol.StateProofBasic].StateProofVotersCommitment,
OnlineTotalWeight: votersHdr.StateProofTracking[protocol.StateProofBasic].StateProofOnlineTotalWeight,
Version: votersHdr.CurrentProtocol,
}
return &verificationData, nil
}
6 changes: 6 additions & 0 deletions ledger/evalindexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,12 @@ func (l indexerLedgerConnector) VotersForStateProof(_ basics.Round) (*ledgercore
return nil, errors.New("VotersForStateProof() not implemented")
}

// StateProofVerificationData is part of LedgerForEvaluator interface.
func (l indexerLedgerConnector) StateProofVerificationData(_ basics.Round) (*ledgercore.StateProofVerificationData, error) {
// This function is not used by evaluator.
return nil, errors.New("StateProofVerificationData() not implemented")
}

func makeIndexerLedgerConnector(il indexerLedgerForEval, genesisHash crypto.Digest, genesisProto config.ConsensusParams, latestRound basics.Round, roundResources EvalForIndexerResources) indexerLedgerConnector {
return indexerLedgerConnector{
il: il,
Expand Down
4 changes: 4 additions & 0 deletions ledger/internal/appcow_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ func (ml *emptyLedger) GetStateProofNextRound() basics.Round {
return basics.Round(0)
}

func (ml *emptyLedger) StateProofVerificationData(_ basics.Round) (*ledgercore.StateProofVerificationData, error) {
return nil, fmt.Errorf("emptyLedger does not implement StateProofVerificationData")
}

type modsData struct {
addr basics.Address
cidx basics.CreatableIndex
Expand Down
6 changes: 5 additions & 1 deletion ledger/internal/cow.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ type roundCowParent interface {
getStorageLimits(addr basics.Address, aidx basics.AppIndex, global bool) (basics.StateSchema, error)
allocated(addr basics.Address, aidx basics.AppIndex, global bool) (bool, error)
getKey(addr basics.Address, aidx basics.AppIndex, global bool, key string, accountIdx uint64) (basics.TealValue, bool, error)

kvGet(key string) ([]byte, bool, error)
StateProofVerificationData(stateProofLastAttestedRound basics.Round) (*ledgercore.StateProofVerificationData, error)
}

type roundCowState struct {
Expand Down Expand Up @@ -241,6 +241,10 @@ func (cb *roundCowState) BlockHdr(r basics.Round) (bookkeeping.BlockHeader, erro
return cb.lookupParent.BlockHdr(r)
}

func (cb *roundCowState) StateProofVerificationData(stateProofLastAttestedRound basics.Round) (*ledgercore.StateProofVerificationData, error) {
return cb.lookupParent.StateProofVerificationData(stateProofLastAttestedRound)
}

func (cb *roundCowState) blockHdrCached(r basics.Round) (bookkeeping.BlockHeader, error) {
return cb.lookupParent.blockHdrCached(r)
}
Expand Down
16 changes: 13 additions & 3 deletions ledger/internal/cow_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package internal

import (
"fmt"
"testing"

"github.com/stretchr/testify/require"
Expand All @@ -32,9 +33,10 @@ import (
)

type mockLedger struct {
balanceMap map[basics.Address]basics.AccountData
blocks map[basics.Round]bookkeeping.BlockHeader
blockErr map[basics.Round]error
balanceMap map[basics.Address]basics.AccountData
blocks map[basics.Round]bookkeeping.BlockHeader
blockErr map[basics.Round]error
stateProofVerification map[basics.Round]*ledgercore.StateProofVerificationData
}

func (ml *mockLedger) lookup(addr basics.Address) (ledgercore.AccountData, error) {
Expand Down Expand Up @@ -110,6 +112,14 @@ func (ml *mockLedger) blockHdrCached(rnd basics.Round) (bookkeeping.BlockHeader,
return ml.blockHdrCached(rnd)
}

func (ml *mockLedger) StateProofVerificationData(rnd basics.Round) (*ledgercore.StateProofVerificationData, error) {
element, exists := ml.stateProofVerification[rnd]
if !exists {
return nil, fmt.Errorf("requested state proof verification data not found")
}
return element, nil
}

func checkCowByUpdate(t *testing.T, cow *roundCowState, delta ledgercore.AccountDeltas) {
for i := 0; i < delta.Len(); i++ {
addr, data := delta.GetByIdx(i)
Expand Down
5 changes: 5 additions & 0 deletions ledger/internal/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ type LedgerForCowBase interface {
LookupApplication(basics.Round, basics.Address, basics.AppIndex) (ledgercore.AppResource, error)
LookupKv(basics.Round, string) ([]byte, error)
GetCreatorForRound(basics.Round, basics.CreatableIndex, basics.CreatableType) (basics.Address, bool, error)
StateProofVerificationData(stateProofLastAttestedRound basics.Round) (*ledgercore.StateProofVerificationData, error)
}

// ErrRoundZero is self-explanatory
Expand Down Expand Up @@ -337,6 +338,10 @@ func (x *roundCowBase) BlockHdr(r basics.Round) (bookkeeping.BlockHeader, error)
return x.l.BlockHdr(r)
}

func (x *roundCowBase) StateProofVerificationData(stateProofLastAttestedRound basics.Round) (*ledgercore.StateProofVerificationData, error) {
return x.l.StateProofVerificationData(stateProofLastAttestedRound)
}

func (x *roundCowBase) blockHdrCached(r basics.Round) (bookkeeping.BlockHeader, error) {
return x.l.BlockHdrCached(r)
}
Expand Down
Loading