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

Persist StateProof builders on disk #4553

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
ac2cbdc
[WIP] Migrate stateproof database to store builders and add Persisten…
Aharonee Aug 31, 2022
f55e94e
[WIP] Refactor builder and remove PersistentBuilder and dependency on…
Aharonee Sep 1, 2022
8ad83d1
[WIP] Add persistBuilders flag to stateproof.Worker, and delete old b…
Aharonee Sep 1, 2022
de11f79
[WIP] .
Aharonee Sep 1, 2022
a49b125
Finalize the builder struct, fix deleteOldBuilders and make msgpack
Aharonee Sep 5, 2022
1e3fed1
Finalize implementation and fix tests
Aharonee Sep 8, 2022
eddbfa1
Add and update unit tests to verify database is consistent with build…
Aharonee Sep 15, 2022
c576f6b
rename stateproof top voters alloc bound name
algonathan Oct 18, 2022
d590d6b
fx: removed comment
algonathan Oct 18, 2022
f286467
refactor: fetchBuilder to treat errors from decoding.
algonathan Oct 18, 2022
12cdf5f
using sql.ErrNoRows
algonathan Oct 18, 2022
554ea13
rm: unused fields in stateproof/Worker
algonathan Oct 18, 2022
d057a0d
fx: avoiding sql access for sigs in the future
algonathan Oct 18, 2022
8f89870
refactor: moving persisted fields into BuilderPersistingFields struct
algonathan Oct 18, 2022
432a0e8
fx: stating that the worker is not a persistentBuilder in numerous un…
algonathan Oct 18, 2022
ee29689
inspect sig is in db prior to signing in func signStateProof
algonathan Oct 18, 2022
e679a81
improved exists claus
algonathan Oct 18, 2022
0bc918d
reviewdog-fix
algonathan Oct 18, 2022
6ea7c27
refactor: getBuilder func to not fill a ptr
algonathan Oct 18, 2022
5553b83
fx: fetchBuilderForRound behaviour
algonathan Oct 18, 2022
12eebee
fix: shutdown to worker after db-init in test
algonathan Oct 18, 2022
d0943f5
fx: warning instead of error log
algonathan Oct 18, 2022
98e994c
rm: error var
algonathan Oct 19, 2022
5b1fa5a
fx: separate SP DB schema upgrade into two functions
algonathan Oct 19, 2022
66c9477
adding unit test for db schema upgrade
algonathan Oct 19, 2022
99ef429
rm: close db inside makeStateProofDB
algonathan Oct 19, 2022
518e843
improved unit-test
algonathan Oct 19, 2022
e2cb9bb
renamed b as var name
algonathan Oct 19, 2022
699c723
fx: comment
algonathan Oct 19, 2022
42ac711
fix: unneeded if statement in the sigExistsInDB
algonathan Oct 19, 2022
056997f
fix: small bug in sigExistsInDv verify
algonathan Oct 19, 2022
b4daa95
adding unit-test to ensure fetching from disk is performed
algonathan Oct 19, 2022
994cde3
adding unit-tests
algonathan Oct 19, 2022
6dedf3f
adding unit-test that catchs an issue
algonathan Oct 19, 2022
9ec48e8
fx: when fetching builder from DB - fill with sigs
algonathan Oct 19, 2022
74443d2
fx: fetchBuilderFromDB refactor
algonathan Oct 19, 2022
9a898e3
fx:race
algonathan Oct 19, 2022
19f3a31
reusing code
algonathan Oct 19, 2022
acf3d32
fx: race
algonathan Oct 19, 2022
e680324
fx: watch-dog issue
algonathan Oct 19, 2022
aadb33c
fx: comment
algonathan Oct 19, 2022
e86c8e0
rename insertBuilder func
algonathan Oct 19, 2022
172621a
improvement: reloading sigs-per-round
algonathan Oct 19, 2022
ee6404e
removing unneeded field from func fillBuilder
algonathan Oct 19, 2022
61b33b9
fix CR
Oct 20, 2022
f2df8ef
fixing edge cases with when no signatures are in the database
Oct 20, 2022
f0f1426
support old databases having only signatures
Oct 20, 2022
4f853d5
fix bug + work on tests
Oct 20, 2022
9d78989
adjusting tests
Oct 21, 2022
d0269e6
close the query
Oct 21, 2022
e5ba262
verify that builder can be loaded with no signatures
Oct 23, 2022
9504705
remove the const flag for now
Oct 23, 2022
dda383d
fmt
Oct 23, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 53 additions & 35 deletions crypto/stateproof/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,28 @@ var (
ErrCoinIndexError = errors.New("could not find corresponding index for a given coin")
)

// VotersAllocBound should be equal to config.Consensus[protocol.ConsensusCurrentVersion].VotersAllocBound
const VotersAllocBound = 1024

// BuilderPersistingFields is the set of fields of a Builder that are persisted to disk.
type BuilderPersistingFields struct {
_struct struct{} `codec:",omitempty,omitemptyarray"`
Data MessageHash `codec:"data"`
Round uint64 `codec:"rnd"`
Participants []basics.Participant `codec:"parts,allocbound=VotersAllocBound"`
Parttree *merklearray.Tree `codec:"parttree"`
LnProvenWeight uint64 `codec:"lnprv"`
ProvenWeight uint64 `codec:"prv"`
StrengthTarget uint64 `codec:"str"`
}

// Builder keeps track of signatures on a message and eventually produces
// a state proof for that message.
type Builder struct {
data MessageHash
round uint64
sigs []sigslot // Indexed by pos in participants
signedWeight uint64 // Total weight of signatures so far
participants []basics.Participant
parttree *merklearray.Tree
lnProvenWeight uint64
provenWeight uint64
strengthTarget uint64
cachedProof *StateProof
BuilderPersistingFields
sigs []sigslot // Indexed by pos in Participants
signedWeight uint64 // Total weight of signatures so far
cachedProof *StateProof
}

// MakeBuilder constructs an empty builder. After adding enough signatures and signed weight, this builder is used to create a stateproof.
Expand All @@ -58,16 +67,20 @@ func MakeBuilder(data MessageHash, round uint64, provenWeight uint64, part []bas
}

b := &Builder{
data: data,
round: round,
sigs: make([]sigslot, npart),
signedWeight: 0,
participants: part,
parttree: parttree,
lnProvenWeight: lnProvenWt,
provenWeight: provenWeight,
strengthTarget: strengthTarget,
cachedProof: nil,
BuilderPersistingFields: BuilderPersistingFields{
Data: data,
Round: round,
Participants: part,
Parttree: parttree,
LnProvenWeight: lnProvenWt,
ProvenWeight: provenWeight,
StrengthTarget: strengthTarget,
},

sigs: make([]sigslot, npart),
signedWeight: 0,

cachedProof: nil,
}

return b, nil
Expand All @@ -86,11 +99,11 @@ func (b *Builder) Present(pos uint64) (bool, error) {
// IsValid verifies that the participant along with the signature can be inserted to the builder.
// verifySig can be set to false when the signature is already verified (e.g. loaded from the DB)
func (b *Builder) IsValid(pos uint64, sig *merklesignature.Signature, verifySig bool) error {
if pos >= uint64(len(b.participants)) {
return fmt.Errorf("%w pos %d >= len(participants) %d", ErrPositionOutOfBound, pos, len(b.participants))
if pos >= uint64(len(b.Participants)) {
return fmt.Errorf("%w pos %d >= len(participants) %d", ErrPositionOutOfBound, pos, len(b.Participants))
}

p := b.participants[pos]
p := b.Participants[pos]

if p.Weight == 0 {
return fmt.Errorf("builder.IsValid: %w: position = %d", ErrPositionWithZeroWeight, pos)
Expand All @@ -101,7 +114,7 @@ func (b *Builder) IsValid(pos uint64, sig *merklesignature.Signature, verifySig
if err := sig.ValidateSaltVersion(merklesignature.SchemeSaltVersion); err != nil {
return err
}
if err := p.PK.VerifyBytes(b.round, b.data[:], sig); err != nil {
if err := p.PK.VerifyBytes(b.Round, b.Data[:], sig); err != nil {
return err
}
}
Expand All @@ -118,7 +131,7 @@ func (b *Builder) Add(pos uint64, sig merklesignature.Signature) error {
return ErrPositionAlreadyPresent
}

p := b.participants[pos]
p := b.Participants[pos]

// Remember the signature
b.sigs[pos].Weight = p.Weight
Expand All @@ -130,7 +143,7 @@ func (b *Builder) Add(pos uint64, sig merklesignature.Signature) error {

// Ready returns whether the state proof is ready to be built.
func (b *Builder) Ready() bool {
return b.cachedProof != nil || b.signedWeight > b.provenWeight
return b.cachedProof != nil || b.signedWeight > b.ProvenWeight
}

// SignedWeight returns the total weight of signatures added so far.
Expand Down Expand Up @@ -175,7 +188,7 @@ func (b *Builder) Build() (*StateProof, error) {
}

if !b.Ready() {
return nil, fmt.Errorf("%w: %d <= %d", ErrSignedWeightLessThanProvenWeight, b.signedWeight, b.provenWeight)
return nil, fmt.Errorf("%w: %d <= %d", ErrSignedWeightLessThanProvenWeight, b.signedWeight, b.ProvenWeight)
}

// Commit to the sigs array
Expand All @@ -197,17 +210,17 @@ func (b *Builder) Build() (*StateProof, error) {
MerkleSignatureSaltVersion: merklesignature.SchemeSaltVersion,
}

nr, err := numReveals(b.signedWeight, b.lnProvenWeight, b.strengthTarget)
nr, err := numReveals(b.signedWeight, b.LnProvenWeight, b.StrengthTarget)
if err != nil {
return nil, err
}

choice := coinChoiceSeed{
partCommitment: b.parttree.Root(),
lnProvenWeight: b.lnProvenWeight,
partCommitment: b.Parttree.Root(),
lnProvenWeight: b.LnProvenWeight,
sigCommitment: s.SigCommit,
signedWeight: s.SignedWeight,
data: b.data,
data: b.Data,
}

coinHash := makeCoinGenerator(&choice)
Expand All @@ -221,8 +234,8 @@ func (b *Builder) Build() (*StateProof, error) {
return nil, err
}

if pos >= uint64(len(b.participants)) {
return nil, fmt.Errorf("%w pos %d >= len(participants) %d", ErrPositionOutOfBound, pos, len(b.participants))
if pos >= uint64(len(b.Participants)) {
return nil, fmt.Errorf("%w pos %d >= len(participants) %d", ErrPositionOutOfBound, pos, len(b.Participants))
}

revealsSequence[j] = pos
Expand All @@ -236,7 +249,7 @@ func (b *Builder) Build() (*StateProof, error) {
// Generate the reveal for pos
s.Reveals[pos] = Reveal{
SigSlot: b.sigs[pos].sigslotCommit,
Part: b.participants[pos],
Part: b.Participants[pos],
}

proofPositions = append(proofPositions, pos)
Expand All @@ -247,7 +260,7 @@ func (b *Builder) Build() (*StateProof, error) {
return nil, err
}

partProofs, err := b.parttree.Prove(proofPositions)
partProofs, err := b.Parttree.Prove(proofPositions)
if err != nil {
return nil, err
}
Expand All @@ -258,3 +271,8 @@ func (b *Builder) Build() (*StateProof, error) {
b.cachedProof = s
return s, nil
}

// AllocSigs should only be used after decoding msgpacked Builder, as the sigs field is not exported and encoded
func (b *Builder) AllocSigs() {
b.sigs = make([]sigslot, len(b.Participants))
}
18 changes: 13 additions & 5 deletions crypto/stateproof/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (

"github.com/algorand/falcon"

"github.com/algorand/go-algorand/config"
"github.com/algorand/go-algorand/crypto"
"github.com/algorand/go-algorand/crypto/merklearray"
"github.com/algorand/go-algorand/crypto/merklesignature"
Expand Down Expand Up @@ -496,12 +497,12 @@ func TestBuildAndReady(t *testing.T) {
_, err = builder.Build()
a.ErrorIs(err, ErrSignedWeightLessThanProvenWeight)

builder.signedWeight = builder.provenWeight
builder.signedWeight = builder.ProvenWeight
a.False(builder.Ready())
_, err = builder.Build()
a.ErrorIs(err, ErrSignedWeightLessThanProvenWeight)

builder.signedWeight = builder.provenWeight + 1
builder.signedWeight = builder.ProvenWeight + 1
a.True(builder.Ready())
_, err = builder.Build()
a.NotErrorIs(err, ErrSignedWeightLessThanProvenWeight)
Expand All @@ -516,19 +517,19 @@ func TestErrorCases(t *testing.T) {
_, err := builder.Present(1)
a.ErrorIs(err, ErrPositionOutOfBound)

builder.participants = make([]basics.Participant, 1, 1)
builder.Participants = make([]basics.Participant, 1, 1)
builder.sigs = make([]sigslot, 1, 1)
err = builder.IsValid(1, &merklesignature.Signature{}, false)
a.ErrorIs(err, ErrPositionOutOfBound)

err = builder.IsValid(0, &merklesignature.Signature{}, false)
require.ErrorIs(t, err, ErrPositionWithZeroWeight)

builder.participants[0].Weight = 1
builder.Participants[0].Weight = 1
err = builder.IsValid(0, &merklesignature.Signature{}, true)
a.ErrorIs(err, merklesignature.ErrKeyLifetimeIsZero)

builder.participants[0].PK.KeyLifetime = 20
builder.Participants[0].PK.KeyLifetime = 20
err = builder.IsValid(0, &merklesignature.Signature{}, true)
a.ErrorIs(err, merklesignature.ErrSignatureSchemeVerificationFailed)

Expand Down Expand Up @@ -631,6 +632,13 @@ func TestBuilder_BuildStateProofCache(t *testing.T) {
return
}

// Verifies that the VotersAllocBound constant is equal to the current consensus parameters.
// It is used for msgpack allocbound (needs to be static)
func TestBuilder_StateProofTopVoters(t *testing.T) {
partitiontest.PartitionTest(t)
require.Equal(t, config.Consensus[protocol.ConsensusCurrentVersion].StateProofTopVoters, uint64(VotersAllocBound))
}

func BenchmarkBuildVerify(b *testing.B) {
totalWeight := 1000000
npart := 1000
Expand Down
Loading