@@ -40,10 +40,22 @@ const (
4040// sigTypeStore is a shorthand for a map of sigType to map of core.PubKey to slice of core.ParSignedData.
4141type sigTypeStore map [sigType ]map [core.PubKey ][]core.ParSignedData
4242
43- // dataByPubkey maps a sigType to its map of public key to slice of core.ParSignedData..
43+ // dataByPubkey holds the partial signatures collected per sigType and the pending exchange queries
44+ // awaiting them. Both are guarded by lock.
4445type dataByPubkey struct {
45- store sigTypeStore
46- lock sync.Mutex
46+ store sigTypeStore
47+ queries []exchangeQuery
48+ lock sync.Mutex
49+ }
50+
51+ // exchangeQuery is a pending exchange call awaiting all its expected partial signatures for a sigType.
52+ type exchangeQuery struct {
53+ sigType sigType
54+ expected int
55+ // response is buffered (size 1) so resolveQueriesUnsafe never blocks, even when it runs on the
56+ // exchange goroutine itself (pushPsigs may resolve queries synchronously via StoreInternal).
57+ response chan <- map [core.PubKey ][]core.ParSignedData
58+ cancel <- chan struct {}
4759}
4860
4961// exchanger is responsible for exchanging partial signatures between peers on libp2p.
@@ -53,7 +65,6 @@ type exchanger struct {
5365 sigTypes map [sigType ]bool
5466 sigData dataByPubkey
5567 dutyGaterFunc func (duty core.Duty ) bool
56- sigDatasChan chan map [core.PubKey ][]core.ParSignedData
5768}
5869
5970func newExchanger (p2pNode host.Host , peerIdx int , peers []peer.ID , sigTypes []sigType , timeout time.Duration ) * exchanger {
@@ -90,7 +101,6 @@ func newExchanger(p2pNode host.Host, peerIdx int, peers []peer.ID, sigTypes []si
90101 lock : sync.Mutex {},
91102 },
92103 dutyGaterFunc : dutyGaterFunc ,
93- sigDatasChan : make (chan map [core.PubKey ][]core.ParSignedData , 1 ),
94104 }
95105
96106 // Wiring core workflow components
@@ -112,60 +122,87 @@ func (e *exchanger) exchange(ctx context.Context, sigType sigType, set core.ParS
112122 return nil , err
113123 }
114124
115- expectedSigs := len (set )
116-
117- for {
118- select {
119- case sigDatas , ok := <- e .sigDatasChan :
120- if ! ok {
121- return nil , errors .New ("sigdata channel has been closed" )
122- }
123-
124- if len (sigDatas ) == expectedSigs {
125- // We are done when we have ParSignedData of all the DVs from all each peer
126- return sigDatas , nil
127- }
128- case <- ctx .Done ():
129- return nil , ctx .Err ()
125+ cancel := make (chan struct {})
126+ defer close (cancel )
127+
128+ response := make (chan map [core.PubKey ][]core.ParSignedData , 1 )
129+
130+ // Register the query, then resolve immediately in case all expected signatures are already
131+ // collected (e.g. peers delivered theirs before the StoreInternal above completed the threshold).
132+ e .sigData .lock .Lock ()
133+ e .sigData .queries = append (e .sigData .queries , exchangeQuery {
134+ sigType : sigType ,
135+ expected : len (set ),
136+ response : response ,
137+ cancel : cancel ,
138+ })
139+ e .resolveQueriesUnsafe ()
140+ e .sigData .lock .Unlock ()
141+
142+ select {
143+ case <- ctx .Done ():
144+ return nil , ctx .Err ()
145+ case data := <- response :
146+ return data , nil
147+ }
148+ }
149+
150+ // resolveQueriesUnsafe delivers the collected signatures to every pending query whose sigType has
151+ // reached its expected count, dropping cancelled queries. It must be called with sigData.lock held.
152+ func (e * exchanger ) resolveQueriesUnsafe () {
153+ var remaining []exchangeQuery
154+
155+ for _ , q := range e .sigData .queries {
156+ if cancelled (q .cancel ) {
157+ continue
158+ }
159+
160+ data := e .sigData .store [q .sigType ]
161+ if len (data ) != q .expected {
162+ remaining = append (remaining , q )
163+ continue
130164 }
165+
166+ // We are done when we have ParSignedData of all the DVs from each peer.
167+ ret := make (map [core.PubKey ][]core.ParSignedData , len (data ))
168+ maps .Copy (ret , data )
169+
170+ q .response <- ret // Never blocks: response is buffered and each query is resolved at most once.
171+ }
172+
173+ e .sigData .queries = remaining
174+ }
175+
176+ // cancelled returns true if the channel is closed.
177+ func cancelled (ch <- chan struct {}) bool {
178+ select {
179+ case <- ch :
180+ return true
181+ default :
182+ return false
131183 }
132184}
133185
134- // pushPsigs is responsible for writing partial signature data to sigChan obtained from other peers.
135- func (e * exchanger ) pushPsigs (ctx context.Context , duty core.Duty , set map [core.PubKey ][]core.ParSignedData ) error {
186+ // pushPsigs stores partial signature data obtained from peers and resolves any pending query in
187+ // exchange whose expected signatures are now all collected.
188+ func (e * exchanger ) pushPsigs (_ context.Context , duty core.Duty , set map [core.PubKey ][]core.ParSignedData ) error {
136189 sigType := sigType (duty .Slot )
137190
138191 if ! e .dutyGaterFunc (duty ) {
139192 return errors .New ("unrecognized sigType" , z .Int ("sigType" , int (sigType )))
140193 }
141194
142195 e .sigData .lock .Lock ()
196+ defer e .sigData .lock .Unlock ()
143197
144- for pk , psigs := range set {
145- _ , ok := e .sigData .store [sigType ]
146- if ! ok {
147- e .sigData .store [sigType ] = map [core.PubKey ][]core.ParSignedData {}
148- }
149-
150- e .sigData .store [sigType ][pk ] = psigs
151- }
152-
153- data , ok := e .sigData .store [sigType ]
198+ _ , ok := e .sigData .store [sigType ]
154199 if ! ok {
155- e .sigData .lock .Unlock ()
156- return nil
200+ e .sigData .store [sigType ] = map [core.PubKey ][]core.ParSignedData {}
157201 }
158202
159- ret := make (map [core.PubKey ][]core.ParSignedData )
160- maps .Copy (ret , data )
161-
162- e .sigData .lock .Unlock ()
203+ maps .Copy (e .sigData .store [sigType ], set )
163204
164- select {
165- case e .sigDatasChan <- ret :
166- case <- ctx .Done ():
167- return errors .Wrap (ctx .Err (), "feed collected sig data" )
168- }
205+ e .resolveQueriesUnsafe ()
169206
170207 return nil
171208}
0 commit comments