Skip to content

Commit 96230de

Browse files
authored
fix: improve performance of ApplyPowerTableDiffs in ImportSnapshotToDatastore (#1047)
* fix: improve performance of ApplyPowerTableDiffs in ImportSnapshotToDatastore * avoid sorting in CertStore.Put * validate power tables * Resolve AI comments
1 parent 9288fba commit 96230de

File tree

2 files changed

+84
-20
lines changed

2 files changed

+84
-20
lines changed

certs/certs.go

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -236,15 +236,38 @@ func MakePowerTableDiff(oldPowerTable, newPowerTable gpbft.PowerEntries) PowerTa
236236
return diff
237237
}
238238

239+
func PowerTableArrayToMap(pt gpbft.PowerEntries) map[gpbft.ActorID]gpbft.PowerEntry {
240+
ptm := make(map[gpbft.ActorID]gpbft.PowerEntry, len(pt))
241+
for _, pe := range pt {
242+
ptm[pe.ID] = pe
243+
}
244+
return ptm
245+
}
246+
247+
func PowerTableMapToArray(ptm map[gpbft.ActorID]gpbft.PowerEntry) gpbft.PowerEntries {
248+
pt := make(gpbft.PowerEntries, 0, len(ptm))
249+
for _, pe := range ptm {
250+
pt = append(pt, pe)
251+
}
252+
sort.Sort(pt)
253+
return pt
254+
}
255+
239256
// Apply a set of power table diffs to the passed power table.
240257
//
241258
// - The delta must be sorted by participant ID, ascending.
242259
// - The returned power table is sorted by power, descending.
243260
func ApplyPowerTableDiffs(prevPowerTable gpbft.PowerEntries, diffs ...PowerTableDiff) (gpbft.PowerEntries, error) {
244-
powerTableMap := make(map[gpbft.ActorID]gpbft.PowerEntry, len(prevPowerTable))
245-
for _, pe := range prevPowerTable {
246-
powerTableMap[pe.ID] = pe
261+
powerTableMap := PowerTableArrayToMap(prevPowerTable)
262+
powerTableMap, err := ApplyPowerTableDiffsToMap(powerTableMap, diffs...)
263+
if err != nil {
264+
return nil, err
247265
}
266+
newPowerTable := PowerTableMapToArray(powerTableMap)
267+
return newPowerTable, nil
268+
}
269+
270+
func ApplyPowerTableDiffsToMap(powerTableMap map[gpbft.ActorID]gpbft.PowerEntry, diffs ...PowerTableDiff) (map[gpbft.ActorID]gpbft.PowerEntry, error) {
248271
for j, diff := range diffs {
249272
var lastActorId gpbft.ActorID
250273
for i, d := range diff {
@@ -298,17 +321,10 @@ func ApplyPowerTableDiffs(prevPowerTable gpbft.PowerEntries, diffs ...PowerTable
298321
default: // if the power becomes negative, something went wrong
299322
return nil, fmt.Errorf("diff %d resulted in negative power for participant %d", j, pe.ID)
300323
}
301-
302324
}
303325
}
304326

305-
newPowerTable := make(gpbft.PowerEntries, 0, len(powerTableMap))
306-
for _, pe := range powerTableMap {
307-
newPowerTable = append(newPowerTable, pe)
308-
}
309-
310-
sort.Sort(newPowerTable)
311-
return newPowerTable, nil
327+
return powerTableMap, nil
312328
}
313329

314330
// MakePowerTableCID returns the DagCBOR-blake2b256 CID of the given power entries. This method does

certstore/snapshot.go

Lines changed: 57 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ import (
1919
"golang.org/x/crypto/blake2b"
2020
)
2121

22-
var ErrUnknownLatestCertificate = errors.New("latest certificate is not known")
22+
var (
23+
ErrUnknownLatestCertificate = errors.New("latest certificate is not known")
24+
ErrNoCertificateExtracted = errors.New("no certificate is found in the snapshot")
25+
)
2326

2427
// ExportLatestSnapshot exports an F3 snapshot that includes the finality certificate chain until the current `latestCertificate`.
2528
//
@@ -104,34 +107,79 @@ func importSnapshotToDatastoreWithTestingPowerTableFrequency(ctx context.Context
104107
dsb := autobatch.NewAutoBatching(ds, 1000)
105108
defer dsb.Flush(ctx)
106109
cs, err := OpenOrCreateStore(ctx, dsb, header.FirstInstance, header.InitialPowerTable)
107-
if testingPowerTableFrequency > 0 {
108-
cs.powerTableFrequency = testingPowerTableFrequency
109-
}
110110
if err != nil {
111111
return err
112112
}
113-
pt := header.InitialPowerTable
114-
for {
113+
if testingPowerTableFrequency > 0 {
114+
cs.powerTableFrequency = testingPowerTableFrequency
115+
}
116+
var latestCert *certs.FinalityCertificate
117+
ptm := certs.PowerTableArrayToMap(header.InitialPowerTable)
118+
for i := header.FirstInstance; ; i += 1 {
115119
certBytes, err := readSnapshotBlockBytes(snapshot)
116120
if err == io.EOF {
117121
break
118122
} else if err != nil {
119123
return fmt.Errorf("failed to decode finality certificate: %w", err)
120124
}
125+
121126
var cert certs.FinalityCertificate
122-
cert.UnmarshalCBOR(bytes.NewReader(certBytes))
123-
if err = cs.Put(ctx, &cert); err != nil {
127+
if err = cert.UnmarshalCBOR(bytes.NewReader(certBytes)); err != nil {
124128
return err
125129
}
126-
if pt, err = certs.ApplyPowerTableDiffs(pt, cert.PowerTableDelta); err != nil {
130+
latestCert = &cert
131+
132+
if i != cert.GPBFTInstance {
133+
return fmt.Errorf("the certificate of instance %d is missing", i)
134+
}
135+
136+
if i > header.LatestInstance {
137+
return fmt.Errorf("certificate of instance %d is found, expected latest instance %d", i, header.LatestInstance)
138+
}
139+
140+
if err := cs.ds.Put(ctx, cs.keyForCert(cert.GPBFTInstance), certBytes); err != nil {
127141
return err
128142
}
143+
144+
if ptm, err = certs.ApplyPowerTableDiffsToMap(ptm, cert.PowerTableDelta); err != nil {
145+
return err
146+
}
147+
129148
if (cert.GPBFTInstance+1)%cs.powerTableFrequency == 0 {
149+
pt := certs.PowerTableMapToArray(ptm)
150+
if err = checkPowerTable(pt, cert.SupplementalData.PowerTable); err != nil {
151+
return err
152+
}
130153
if err := cs.putPowerTable(ctx, cert.GPBFTInstance+1, pt); err != nil {
131154
return err
132155
}
133156
}
134157
}
158+
159+
if latestCert == nil {
160+
return ErrNoCertificateExtracted
161+
}
162+
163+
if latestCert.GPBFTInstance != header.LatestInstance {
164+
return fmt.Errorf("extracted latest instance %d, but %d is expected", latestCert.GPBFTInstance, header.LatestInstance)
165+
}
166+
167+
pt := certs.PowerTableMapToArray(ptm)
168+
if err = checkPowerTable(pt, latestCert.SupplementalData.PowerTable); err != nil {
169+
return err
170+
}
171+
172+
return cs.writeInstanceNumber(ctx, certStoreLatestKey, header.LatestInstance)
173+
}
174+
175+
func checkPowerTable(pt gpbft.PowerEntries, expectedCid cid.Cid) error {
176+
ptCid, err := certs.MakePowerTableCID(pt)
177+
if err != nil {
178+
return err
179+
}
180+
if ptCid != expectedCid {
181+
return fmt.Errorf("new power table differs from expected power table: %s != %s", ptCid, expectedCid)
182+
}
135183
return nil
136184
}
137185

0 commit comments

Comments
 (0)