forked from meshplus/bitxhub-core
/
keygen.go
233 lines (209 loc) · 8.03 KB
/
keygen.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
package tss
import (
"crypto/ecdsa"
"errors"
"fmt"
"sync"
"time"
bkg "github.com/binance-chain/tss-lib/ecdsa/keygen"
btss "github.com/binance-chain/tss-lib/tss"
"github.com/meshplus/bitxhub-core/tss/blame"
"github.com/meshplus/bitxhub-core/tss/conversion"
"github.com/meshplus/bitxhub-core/tss/keygen"
"github.com/meshplus/bitxhub-core/tss/message"
"github.com/meshplus/bitxhub-core/tss/storage"
"github.com/sirupsen/logrus"
)
// Keygen generates the key share of the participants of the threshold signature
func (t *TssInstance) Keygen(req keygen.Request) (*keygen.Response, error) {
t.logger.WithFields(logrus.Fields{}).Info("Received keygen request")
// 2 Get parties info
partiesID, localPartyID, err := conversion.GetParties(req.Pubkeys, t.localPubK, t.p2pComm.Peers())
if err != nil {
return nil, fmt.Errorf("fail to get keygen parties: %w", err)
}
// 3 Construct persistent data information whitch KeyGen required
partyPksDataMap, err := conversion.GetPubKeyDatasMapFromPartyIDs(partiesID)
if err != nil {
return nil, fmt.Errorf("fail to get pids from party ids: %w", err)
}
localPkData, err := conversion.GetPubKeyDataFromPartyID(localPartyID)
if err != nil {
return nil, fmt.Errorf("fail to get pid from party id: %w", err)
}
t.keygenLocalState = &storage.KeygenLocalState{
ParticipantPksMap: partyPksDataMap,
LocalPartyPk: localPkData,
}
// 4 Construct keygen params
// - ctx
ctx := btss.NewPeerContext(partiesID)
// - localPartyID
// - parties num
// - threshold
params := btss.NewParameters(ctx, localPartyID, len(partiesID), t.threshold)
// - msg to be sent channel
outCh := make(chan btss.Message, len(partiesID))
// - final channel for storing information
endCh := make(chan bkg.LocalPartySaveData, len(partiesID))
// - err channel
errChan := make(chan struct{})
// - preparams
if t.keygenPreParams == nil {
t.logger.Error("keygen: empty pre-parameters")
return nil, fmt.Errorf("empty keygen pre-parameters")
}
// 5 Construct local party
keyGenParty := bkg.NewLocalParty(t.logger, params, outCh, endCh, *t.keygenPreParams)
t.localPartyID = keyGenParty.PartyID().Id
// 6 Set parties info
// 6.1 Message-to-party mapping, the Keygen only needs one
keyGenPartyMap := new(sync.Map)
keyGenPartyMap.Store("", keyGenParty)
// 6.2 partyID.id-to-partyID mapping
partyIDMap, err := conversion.GetPatyIDInfoMap(partiesID)
if err != nil {
t.logger.Errorf("faile to get partyID info map: %v", err)
return nil, err
}
partyInfo := &conversion.PartyInfo{
PartyMap: keyGenPartyMap,
PartyIDMap: partyIDMap,
}
t.setPartyInfo(partyInfo)
t.blameMgr.SetPartyInfo(partyInfo)
// 7 Start Keygen
var keyGenWg sync.WaitGroup
keyGenWg.Add(2)
// 7.1 First thread: Call the library method to start keyGen
go func() {
defer keyGenWg.Done()
defer t.logger.Infof(">>>>>>>>>>>>>. keyGenParty started: n-%d t-%d, localParty: %s", len(partiesID), t.threshold, t.localPartyID)
if err := keyGenParty.Start(); nil != err {
t.logger.Errorf("keygen: fail to start keygen party: %v", err)
close(errChan)
}
}()
// 7.2 Second thread: process received p2p messages - in this case, received messages are those sent by 7.3
go t.ProcessInboundMessages(&keyGenWg)
// 7.3 Current main process: advance the execution of keyGen process - send out the pending p2p messages given in the library method as required
newPubKey, newPubAddr, err := t.processKeyGen(errChan, outCh, endCh)
if err != nil {
close(t.inMsgHandleStopChan)
return nil, fmt.Errorf("fail to process keygen: %w", err)
}
keyGenTicker := time.NewTicker(t.conf.KeyGenTimeout)
defer keyGenTicker.Stop()
select {
case <-keyGenTicker.C:
t.logger.WithFields(logrus.Fields{"timeout": t.conf.KeyGenTimeout}).Infof("close inMsgHandleStopChan because of timeout")
close(t.inMsgHandleStopChan)
case <-t.taskDoneChan:
close(t.inMsgHandleStopChan)
}
keyGenWg.Wait()
pid, _ := conversion.GetPIDFromPartyID(keyGenParty.PartyID())
t.logger.WithFields(logrus.Fields{
"partyID": t.localPartyID,
"partyPID": pid,
"newPubkey": newPubKey,
"newPubAddr": newPubAddr,
}).Info(">>>>>>>>>>>>>. keygen success!!!")
return keygen.NewResponse(
newPubKey,
newPubAddr,
t.keygenLocalState,
t.blameMgr.Blame,
), nil
}
// Handles messages returned by KeyGen library methods that need to be sent
// - outCh: the messages that need to be sent for each turn
// - endCh:the message that ultimately needs to be stored
func (t *TssInstance) processKeyGen(errChan chan struct{},
outCh <-chan btss.Message,
endCh <-chan bkg.LocalPartySaveData) (*ecdsa.PublicKey, string, error) {
defer t.logger.Debug("finished keygen process")
t.logger.Debug("start to read messages from local party")
kenGenTicker := time.NewTicker(t.conf.KeySignTimeout)
defer kenGenTicker.Stop()
for {
select {
case <-t.stopChan: // when TSS processor receive signal to quit
return nil, "", errors.New("received exit signal")
case <-errChan: // when keyGenParty return
t.logger.Error("key gen failed")
return nil, "", fmt.Errorf("error channel closed fail to start local party")
case <-kenGenTicker.C: // key gen timeout
// we bail out after KeyGenTimeoutSeconds
t.logger.Errorf("fail to generate key in time %s", t.conf.KeyGenTimeout.String())
// 1. get fail reason
failReason := t.blameMgr.Blame.FailReason
if failReason == "" {
failReason = blame.TssTimeout
}
// 2. get last msg
lastMsg := t.blameMgr.GetLastMsg()
if lastMsg == nil {
t.logger.Errorf("fail to start the keygen, the last produced message of this node is none")
return nil, "", fmt.Errorf("timeout before shared message is generated")
}
// 3. blame problem node in unicast
// only KEYGEN2aUnicast is unicast during keygen
blameNodesUnicast, err := t.blameMgr.GetUnicastBlame(message.KEYGEN2aUnicast)
if err != nil {
t.logger.Errorf("error in get unicast blame")
}
if len(blameNodesUnicast) > 0 && len(blameNodesUnicast) <= t.threshold {
t.blameMgr.Blame.SetBlame(failReason, blameNodesUnicast, true)
}
// 4. blame problem node in broadcast
blameNodesBroadcast, err := t.blameMgr.GetBroadcastBlame(lastMsg.Type())
if err != nil {
t.logger.Errorf("error in get broadcast blame")
}
t.blameMgr.Blame.AddBlameNodes(blameNodesBroadcast...)
// 5. blame the node fail to send the shares to the node with batch signing
if len(t.blameMgr.Blame.BlameNodes) == 0 {
blameNodesMisingShare, isUnicast, err := t.blameMgr.TssMissingShareBlame(message.TSSKEYGENROUNDS)
if err != nil {
t.logger.Errorf("fail to get the node of missing share ")
}
if len(blameNodesMisingShare) > 0 && len(blameNodesMisingShare) <= t.threshold {
t.blameMgr.Blame.AddBlameNodes(blameNodesMisingShare...)
t.blameMgr.Blame.IsUnicast = isUnicast
}
}
return nil, "", blame.ErrTssTimeOut
case tssMsg := <-outCh: // get msg to send
t.logger.Debugf(">>>>>>>>>> key gen msg: %s", tssMsg.String())
t.blameMgr.SetLastMsg(tssMsg)
err := t.ProcessOutCh(tssMsg, message.TSSKeyGenMsg)
if err != nil {
t.logger.Errorf("fail to process the message")
return nil, "", err
}
case tssSaveData := <-endCh: // end
t.logger.Debugf("keygen finished successfully: %s", tssSaveData.ECDSAPub.Y().String())
// 1. notify task done
if err := t.NotifyTaskDone(); err != nil {
t.logger.Errorf("fail to broadcast the keygen done")
}
// 2. save local state to file
ecdsaPk, err := conversion.GetTssPubKey(tssSaveData.ECDSAPub)
if err != nil {
t.logger.Errorf("fail to get threshold pubkey: %v", err)
return nil, "", fmt.Errorf("fail to get threshold pubkey: %w", err)
}
pubAddr, pubData, err := conversion.GetPubKeyInfoFromECDSAPubkey(ecdsaPk)
if err != nil {
t.logger.Errorf("fail to convert ecdsa pubkey to pubkey addr and byte: %v", err)
return nil, "", fmt.Errorf("fail to convert ecdsa pubkey to pubkey addr and byte: %w", err)
}
t.keygenLocalState.LocalData = tssSaveData
t.keygenLocalState.PubKeyData = pubData
t.keygenLocalState.PubKeyAddr = pubAddr
t.logger.Infof("processKeyGen end")
return ecdsaPk, pubAddr, nil
}
}
}