Skip to content

Commit 085f660

Browse files
Merge branch 'main' into docs/introduce-mkdocs
2 parents bebb057 + 520c80f commit 085f660

21 files changed

Lines changed: 1755 additions & 212 deletions

File tree

token/core/zkatdlog/nogh/v1/crypto/rp/bulletproof/ipa.go

Lines changed: 50 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -184,25 +184,63 @@ func (p *ipaProver) Prove() (*IPA, error) {
184184
// of the left vector and right is a function of right vector.
185185
// Both vectors are committed in com which is passed as a parameter to reduce
186186
func (p *ipaProver) reduce(X, com *mathlib.G1) (*mathlib.Zr, *mathlib.Zr, []*mathlib.G1, []*mathlib.G1, error) {
187-
leftGen, rightGen := CloneGenerators(p.LeftGenerators, p.RightGenerators)
188-
189187
left := p.leftVector
190188
right := p.rightVector
191189

192190
LArray := make([]*mathlib.G1, p.NumberOfRounds)
193191
RArray := make([]*mathlib.G1, p.NumberOfRounds)
192+
xList := make([]*mathlib.Zr, 0, p.NumberOfRounds)
193+
194194
for i := range p.NumberOfRounds {
195195
// in each round the size of the vector is reduced by 2
196-
n := len(leftGen) / 2
197-
leftIP := math.InnerProduct(left[:n], right[n:], p.Curve)
198-
rightIP := math.InnerProduct(left[n:], right[:n], p.Curve)
199-
// LArray[i] is a commitment to left[:n], right[n:] and their inner product
200-
LArray[i] = CommitVectorPlusOne(left[:n], right[n:], leftGen[n:], rightGen[:n], leftIP, X, p.Curve)
201-
// LArray[i].Add(X.Mul(leftIP))
196+
n_current := len(left) / 2
197+
leftIP := math.InnerProduct(left[:n_current], right[n_current:], p.Curve)
198+
rightIP := math.InnerProduct(left[n_current:], right[:n_current], p.Curve)
199+
200+
var s, sInv []*mathlib.Zr
201+
if i == 0 {
202+
s = []*mathlib.Zr{math.One(p.Curve)}
203+
sInv = []*mathlib.Zr{math.One(p.Curve)}
204+
} else {
205+
s, sInv = ComputeSVector(1<<i, xList, p.Curve)
206+
}
207+
208+
pointsL := make([]*mathlib.G1, 0, len(p.LeftGenerators)+1)
209+
scalarsL := make([]*mathlib.Zr, 0, len(p.LeftGenerators)+1)
210+
211+
pointsR := make([]*mathlib.G1, 0, len(p.LeftGenerators)+1)
212+
scalarsR := make([]*mathlib.Zr, 0, len(p.LeftGenerators)+1)
213+
214+
for m := range 1 << i {
215+
for j := range n_current {
216+
idxG_R := j + (2*m+1)*n_current
217+
idxH_L := j + 2*m*n_current
218+
219+
pointsL = append(pointsL, p.LeftGenerators[idxG_R], p.RightGenerators[idxH_L])
220+
scalarsL = append(scalarsL,
221+
p.Curve.ModMul(left[j], s[m], p.Curve.GroupOrder),
222+
p.Curve.ModMul(right[n_current+j], sInv[m], p.Curve.GroupOrder),
223+
)
224+
225+
idxG_L := j + 2*m*n_current
226+
idxH_R := j + (2*m+1)*n_current
227+
228+
pointsR = append(pointsR, p.LeftGenerators[idxG_L], p.RightGenerators[idxH_R])
229+
scalarsR = append(scalarsR,
230+
p.Curve.ModMul(left[n_current+j], s[m], p.Curve.GroupOrder),
231+
p.Curve.ModMul(right[j], sInv[m], p.Curve.GroupOrder),
232+
)
233+
}
234+
}
235+
236+
pointsL = append(pointsL, X)
237+
scalarsL = append(scalarsL, leftIP)
238+
239+
pointsR = append(pointsR, X)
240+
scalarsR = append(scalarsR, rightIP)
202241

203-
// RArray[i] is a commitment to left[n:], right[:n] and their inner product
204-
RArray[i] = CommitVectorPlusOne(left[n:], right[:n], leftGen[:n], rightGen[n:], rightIP, X, p.Curve)
205-
// RArray[i].Add(X.Mul(rightIP))
242+
LArray[i] = p.Curve.MultiScalarMul(pointsL, scalarsL)
243+
RArray[i] = p.Curve.MultiScalarMul(pointsR, scalarsR)
206244

207245
// compute this round's challenge x
208246
array := common.GetG1Array([]*mathlib.G1{LArray[i], RArray[i]})
@@ -211,14 +249,12 @@ func (p *ipaProver) reduce(X, com *mathlib.G1) (*mathlib.Zr, *mathlib.Zr, []*mat
211249
return nil, nil, nil, nil, err
212250
}
213251
x := p.Curve.HashToZr(bytesToHash)
252+
xList = append(xList, x)
214253

215254
// compute 1/x
216255
xInv := x.Copy()
217256
xInv.InvModOrder()
218257

219-
// reduce the generators by 1/2, as a function of the old generators and x and 1/x
220-
leftGen, rightGen = reduceGenerators(leftGen, rightGen, x, xInv, p.Provider)
221-
222258
// reduce the vectors by 1/2, a function of the old vectors and x and 1/x
223259
left, right = reduceVectors(left, right, x, xInv, p.Curve)
224260

@@ -404,27 +440,6 @@ func reduceVectors(left, right []*mathlib.Zr, x, xInv *mathlib.Zr, c *mathlib.Cu
404440
return leftPrime, rightPrime
405441
}
406442

407-
// reduceGenerators reduces the number of generators passed in the parameters by 1/2,
408-
// as a function of the old generators, x and 1/x
409-
func reduceGenerators(leftGen, rightGen []*mathlib.G1, x, xInv *mathlib.Zr, provider executor.ExecutorProvider) ([]*mathlib.G1, []*mathlib.G1) {
410-
l := len(leftGen) / 2
411-
// Use the Executor abstraction so that the execution strategy can be
412-
// swapped without changing this function. SerialExecutor runs each task
413-
// immediately with no locks or goroutine overhead.
414-
exec := provider.New()
415-
for i := range l {
416-
exec.Submit(func() {
417-
// G_i = G_i^{x_inv} * G_{i+l}^x
418-
leftGen[i].Mul2InPlace(xInv, leftGen[i+l], x)
419-
// H_i = H_i^x * H_{i+l}^{x_inv}
420-
rightGen[i].Mul2InPlace(x, rightGen[i+l], xInv)
421-
})
422-
}
423-
exec.Wait()
424-
425-
return leftGen[:l], rightGen[:l]
426-
}
427-
428443
func CommitVector(
429444
left []*mathlib.Zr,
430445
right []*mathlib.Zr,

token/services/auditor/mock/audit_transaction_store.go

Lines changed: 10 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

token/services/network/fabric/network.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,6 @@ type transactionDB interface {
618618
GetTokenRequest(ctx context.Context, txID string) ([]byte, error)
619619
SetStatus(ctx context.Context, txID string, status storage.TxStatus, message string) error
620620
AcquireRecoveryLeadership(ctx context.Context, lockID int64) (recovery.Leadership, bool, error)
621-
ClaimPendingTransactions(ctx context.Context, olderThan time.Duration, leaseDuration time.Duration, limit int, owner string) ([]*ttxdb.TransactionRecord, error)
621+
ClaimPendingTransactions(ctx context.Context, olderThan time.Duration, leaseDuration time.Duration, limit int, owner string) ([]*ttxdb.RecoveryClaim, error)
622622
ReleaseRecoveryClaim(ctx context.Context, txID string, owner string, message string) error
623623
}

token/services/selector/sherdlock/fetcher.go

Lines changed: 71 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@ package sherdlock
88

99
import (
1010
"context"
11+
"io"
1112
"sync"
1213
"sync/atomic"
1314
"time"
1415

16+
"github.com/hyperledger-labs/fabric-smart-client/pkg/utils/errors"
1517
"github.com/hyperledger-labs/fabric-smart-client/platform/common/utils/collections"
1618
"github.com/hyperledger-labs/fabric-smart-client/platform/common/utils/collections/iterators"
1719
"github.com/hyperledger-labs/fabric-token-sdk/token"
@@ -170,7 +172,11 @@ type cachedFetcher struct {
170172
queriesResponded uint32
171173
// prevKeys tracks cache keys from the previous update cycle to identify stale entries that need removal.
172174
prevKeys map[string]struct{}
173-
mu sync.RWMutex
175+
// isUpdating indicates if a cache refresh is currently in progress.
176+
isUpdating bool
177+
// updateCond allows goroutines to wait for an in-progress update to complete.
178+
updateCond *sync.Cond
179+
mu sync.RWMutex
174180
}
175181

176182
// NewCachedFetcher creates a fetcher that maintains a periodically refreshed cache of all tokens.
@@ -198,48 +204,107 @@ func NewCachedFetcher(tokenDB TokenDB, cacheSize int64, freshnessInterval time.D
198204
panic("failed to create ristretto cache: " + err.Error())
199205
}
200206

201-
return &cachedFetcher{
207+
f := &cachedFetcher{
202208
tokenDB: tokenDB,
203209
cache: ristrettoCache,
204210
freshnessInterval: freshnessInterval,
205211
maxQueriesBeforeRefresh: uint32(maxQueriesBeforeRefresh),
206212
prevKeys: make(map[string]struct{}),
207213
}
214+
f.updateCond = sync.NewCond(&f.mu)
215+
216+
return f
217+
}
218+
219+
// finishUpdate releases the update lock and signals all waiting goroutines.
220+
// finishUpdate cleans up after an update operation: marks updating as complete,
221+
// broadcasts to waiting goroutines, and releases the lock.
222+
// Must be called while holding f.mu.
223+
func (f *cachedFetcher) finishUpdate() {
224+
f.isUpdating = false
225+
f.updateCond.Broadcast()
226+
f.mu.Unlock()
208227
}
209228

229+
// update refreshes the token cache from the database. It releases the lock during the
230+
// potentially slow DB operation to avoid blocking other goroutines, then re-acquires
231+
// the lock to atomically update the cache. A re-check of staleness is performed
232+
// after the DB call completes to avoid overwriting a cache that was refreshed by
233+
// another goroutine while waiting for the database.
210234
func (f *cachedFetcher) update(ctx context.Context) {
211235
f.mu.Lock()
212-
defer f.mu.Unlock()
236+
if f.isUpdating {
237+
// Wait for the in-progress update to finish
238+
for f.isUpdating {
239+
f.updateCond.Wait()
240+
}
241+
f.mu.Unlock()
242+
243+
return
244+
}
245+
213246
if !f.isCacheStale() && !f.isCacheOverused() {
214247
logger.DebugfContext(ctx, "Cache renewed in the meantime by another process")
248+
f.mu.Unlock()
215249

216250
return
217251
}
218252
logger.DebugfContext(ctx, "Renew token cache")
253+
f.isUpdating = true
254+
255+
// Release lock during slow DB operation to not block other token operations
256+
f.mu.Unlock()
257+
219258
it, err := f.tokenDB.SpendableTokensIteratorBy(ctx, "", "")
220259
if err != nil {
221260
logger.Warnf("Failed to get token iterator: %v", err)
261+
f.mu.Lock()
262+
f.finishUpdate()
222263

223264
return
224265
}
225266
defer it.Close()
226267

227-
m := f.groupTokensByKey(ctx, it)
268+
m, err := f.groupTokensByKey(ctx, it)
269+
if err != nil {
270+
logger.Warnf("Failed to group tokens from iterator: %v", err)
271+
f.mu.Lock()
272+
f.finishUpdate()
273+
274+
return
275+
}
276+
277+
f.mu.Lock()
278+
// Re-check: another goroutine may have refreshed while we waited for DB
279+
if !f.isCacheStale() && !f.isCacheOverused() {
280+
logger.DebugfContext(ctx, "Cache renewed in the meantime by another process, skipping")
281+
f.finishUpdate()
282+
283+
return
284+
}
285+
228286
f.updateCache(ctx, m)
229287
atomic.StoreInt64(&f.lastFetched, time.Now().UnixNano())
230288
atomic.StoreUint32(&f.queriesResponded, 0)
289+
f.finishUpdate()
231290
}
232291

233292
// groupTokensByKey reads tokens from the iterator and groups them by wallet/currency key.
234-
func (f *cachedFetcher) groupTokensByKey(ctx context.Context, it driver.SpendableTokensIterator) map[string][]*token2.UnspentTokenInWallet {
293+
// It returns an error if the iterator fails mid-way to prevent partial updates.
294+
func (f *cachedFetcher) groupTokensByKey(ctx context.Context, it driver.SpendableTokensIterator) (map[string][]*token2.UnspentTokenInWallet, error) {
235295
m := map[string][]*token2.UnspentTokenInWallet{}
236296
for t, err := it.Next(); err == nil && t != nil; t, err = it.Next() {
237297
key := tokenKey(t.WalletID, t.Type)
238298
logger.DebugfContext(ctx, "Adding token with key [%s]", key)
239299
m[key] = append(m[key], t)
240300
}
301+
// Re-check for error after loop termination
302+
_, err := it.Next()
303+
if err != nil && !errors.Is(err, io.EOF) {
304+
return nil, err
305+
}
241306

242-
return m
307+
return m, nil
243308
}
244309

245310
// updateCache updates the cache by adding new entries before removing stale ones.

0 commit comments

Comments
 (0)