-
Notifications
You must be signed in to change notification settings - Fork 0
/
paracommitmsg.go
674 lines (594 loc) · 20.8 KB
/
paracommitmsg.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
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package para
import (
"bytes"
"context"
"time"
"github.com/33cn/chain33/common"
"github.com/33cn/chain33/common/crypto"
"github.com/33cn/chain33/types"
paracross "github.com/GM-Publicchain/gm/plugin/dapp/paracross/types"
pt "github.com/GM-Publicchain/gm/plugin/dapp/paracross/types"
"github.com/pkg/errors"
)
var (
consensusInterval = 10 //about 1 new block interval
minerInterval = 10 //5s的主块间隔后分叉概率增加,10s可以消除一些分叉回退
)
type commitMsgClient struct {
paraClient *client
waitMainBlocks int32
commitMsgNotify chan int64
delMsgNotify chan int64
mainBlockAdd chan *types.BlockDetail
minerSwitch chan bool
currentTx *types.Transaction
checkTxCommitTimes int32
privateKey crypto.PrivKey
quit chan struct{}
}
func (client *commitMsgClient) handler() {
var isSync bool
var isRollback bool
var notification []int64 //记录每次系统重启后 min and current height
var finishHeight int64
var consensHeight int64
var sendingHeight int64 //当前发送的最大高度
var sendingMsgs []*pt.ParacrossNodeStatus
var readTick <-chan time.Time
var ticker *time.Ticker
client.paraClient.wg.Add(1)
consensusCh := make(chan *pt.ParacrossStatus, 1)
go client.getConsensusHeight(consensusCh)
client.paraClient.wg.Add(1)
sendMsgCh := make(chan *types.Transaction, 1)
go client.sendCommitMsg(sendMsgCh)
out:
for {
select {
case height := <-client.commitMsgNotify:
if notification == nil {
notification = append(notification, height)
notification = append(notification, height)
finishHeight = height - 1
} else {
//[0] need update to min value if any, [1] always get current height, as for fork case, the height may lower than before
if height < notification[0] {
notification[0] = height
finishHeight = height - 1
}
notification[1] = height
if finishHeight >= notification[1] {
//分叉场景,finish设置为最小值,等待主链共识高度重新设定finishHeight
finishHeight = notification[0] - 1
}
}
isRollback = false
case height := <-client.delMsgNotify:
if notification == nil {
continue
}
if height <= notification[1] {
notification[1] = height - 1
}
if height <= sendingHeight && client.currentTx != nil {
sendingMsgs = nil
client.currentTx = nil
}
//在分叉的主链上,回滚会连续回滚,回滚结束前不会add block,停止发送同时也忽略共识消息,回滚结束后根据共识高度重新设定finishHeight
//如果分叉高度大于当前已完成高度,说明新的主链也收到了finish的tx,不需要重发,也就不需要重新设定
if height <= finishHeight {
finishHeight = notification[0] - 1
}
isSync = false
isRollback = true
plog.Debug("para del block", "delHeight", height)
case block := <-client.mainBlockAdd:
client.paraClient.mtx.Lock()
isCaughtUp := client.paraClient.isCaughtUp
client.paraClient.mtx.Unlock()
if client.currentTx != nil && isCaughtUp {
exist := checkTxInMainBlock(client.currentTx, block)
if exist {
finishHeight = sendingHeight
sendingMsgs = nil
client.currentTx = nil
} else {
client.checkTxCommitTimes++
if client.checkTxCommitTimes > client.waitMainBlocks {
//超过等待最大次数,reset,重新组织发送,防止一直发送同一笔消息
sendingMsgs = nil
client.currentTx = nil
client.checkTxCommitTimes = 0
}
}
}
case <-readTick:
plog.Debug("para readTick", "notify", notification, "sending", len(sendingMsgs),
"finishHeight", finishHeight, "txIsNil", client.currentTx == nil, "sync", isSync)
if notification != nil && finishHeight < notification[1] && client.currentTx == nil && isSync {
count := notification[1] - finishHeight
if count > types.TxGroupMaxCount {
count = types.TxGroupMaxCount
}
status, err := client.getNodeStatus(finishHeight+1, finishHeight+count)
if err != nil {
plog.Error("para commit msg read tick", "err", err.Error())
continue
}
if len(status) == 0 {
continue
}
signTx, count, err := client.calcCommitMsgTxs(status)
if err != nil || signTx == nil {
continue
}
sendingHeight = finishHeight + count
sendingMsgs = status[:count]
client.currentTx = signTx
client.checkTxCommitTimes = 0
sendMsgCh <- client.currentTx
plog.Info("paracommitmsg sending", "txhash", common.ToHex(signTx.Hash()), "exec", string(signTx.Execer))
for i, msg := range sendingMsgs {
plog.Info("paracommitmsg sending", "idx", i, "height", msg.Height, "mainheight", msg.MainBlockHeight,
"blockhash", common.HashHex(msg.BlockHash), "mainHash", common.HashHex(msg.MainBlockHash),
"from", client.paraClient.authAccount)
}
}
//获取正在共识的高度,同步有两层意思,一个是主链跟其他节点完成了同步,另一个是当前平行链节点的高度追赶上了共识高度
//一般来说高度增长从小到大: notifiy[0] -- selfConsensusHeight(mainHeight) -- finishHeight -- sendingHeight -- notify[1]
case rsp := <-consensusCh:
consensHeight = rsp.Height
plog.Info("para consensus rcv", "notify", notification, "sending", len(sendingMsgs),
"consensHeigt", rsp.Height, "finished", finishHeight, "sync", isSync, "miner", readTick != nil, "consensBlockHash", common.ToHex(rsp.BlockHash))
if notification == nil || isRollback {
continue
}
//所有节点还没有共识场景或新节点或重启节点catchingUp场景,要等到收到区块高度大于共识高度时候发送,在catchingup时候本身共识高度和块高度一起增长
if notification[1] > consensHeight {
isSync = true
}
// 共识高度追赶上完成高度之后再发,不然分叉节点继续发浪费手续费
if finishHeight > consensHeight {
isSync = false
}
//未共识过的小于当前共识高度的区块,可以不参与共识, 如果是新节点,一直等到同步的区块达到了共识高度,才设置同步参与共识
//在某些特殊场景下,比如平行链连接的主链节点分叉后又恢复,主链的共识高度低于分叉高度时候,主链上形成共识空洞,需要从共识高度重新发送而不是分叉高度
//共识高度和分叉高度不一致其中一个原因是共识交易组里面某个高度分叉了,分叉的主链节点执行成功,而其他主链节点执行失败,共识高度停留在交易组最小高度-1
//而分叉高度是交易组里面的某个高度
if finishHeight < consensHeight {
finishHeight = consensHeight
sendingMsgs = nil
client.currentTx = nil
}
//系统每次重启都有检查一次共识,如果共识高度落后于系统起来后完成的第一个高度或最小高度,说明可能有共识空洞,需要把从当前共识高度到完成的
//最大高度重发一遍,直到确认收到,发过的最小到最大高度也要重发是因为之前空洞原因共识不连续,即便满足2/3节点也不会增长,需要重发来触发commit
//此处也整合了当前consensus height=-1 场景
// 需要是<而不是<=, 因为notification[0]被认为是系统起来后已经发送过的
nextConsensHeight := consensHeight + 1
if nextConsensHeight < notification[0] {
notification[0] = nextConsensHeight
finishHeight = consensHeight
sendingMsgs = nil
client.currentTx = nil
}
case miner := <-client.minerSwitch:
plog.Info("para consensus mining", "miner", miner)
//停止挖矿
if !miner {
readTick = nil
if ticker != nil {
ticker.Stop()
}
plog.Info("para consensus stop mining")
continue
}
//开启挖矿
if readTick == nil {
ticker = time.NewTicker(time.Second * time.Duration(minerInterval))
readTick = ticker.C
plog.Info("para consensus start mining")
//钱包开启后,从共识高度重新开始发送,在需要重发共识时候,不需要重启设备
finishHeight = consensHeight
}
case <-client.quit:
break out
}
}
client.paraClient.wg.Done()
}
func (client *commitMsgClient) calcCommitMsgTxs(notifications []*pt.ParacrossNodeStatus) (*types.Transaction, int64, error) {
txs, count, err := client.batchCalcTxGroup(notifications)
if err != nil {
txs, err = client.singleCalcTx((notifications)[0])
if err != nil {
plog.Error("single calc tx", "height", notifications[0].Height)
return nil, 0, err
}
return txs, 1, nil
}
return txs, int64(count), nil
}
func (client *commitMsgClient) getTxsGroup(txsArr *types.Transactions) (*types.Transaction, error) {
if len(txsArr.Txs) < 2 {
tx := txsArr.Txs[0]
tx.Sign(types.SECP256K1, client.privateKey)
return tx, nil
}
group, err := types.CreateTxGroup(txsArr.Txs)
if err != nil {
plog.Error("para CreateTxGroup", "err", err.Error())
return nil, err
}
err = group.Check(0, types.GInt("MinFee"), types.GInt("MaxFee"))
if err != nil {
plog.Error("para CheckTxGroup", "err", err.Error())
return nil, err
}
for i := range group.Txs {
group.SignN(i, int32(types.SECP256K1), client.privateKey)
}
newtx := group.Tx()
return newtx, nil
}
func (client *commitMsgClient) batchCalcTxGroup(notifications []*pt.ParacrossNodeStatus) (*types.Transaction, int, error) {
var rawTxs types.Transactions
for _, status := range notifications {
execName := pt.ParaX
if isParaSelfConsensusForked(status.MainBlockHeight) {
execName = paracross.GetExecName()
}
tx, err := paracross.CreateRawCommitTx4MainChain(status, execName, 0)
if err != nil {
plog.Error("para get commit tx", "block height", status.Height)
return nil, 0, err
}
rawTxs.Txs = append(rawTxs.Txs, tx)
}
txs, err := client.getTxsGroup(&rawTxs)
if err != nil {
return nil, 0, err
}
return txs, len(notifications), nil
}
func (client *commitMsgClient) singleCalcTx(status *pt.ParacrossNodeStatus) (*types.Transaction, error) {
execName := pt.ParaX
if isParaSelfConsensusForked(status.MainBlockHeight) {
execName = paracross.GetExecName()
}
tx, err := paracross.CreateRawCommitTx4MainChain(status, execName, 0)
if err != nil {
plog.Error("para get commit tx", "block height", status.Height)
return nil, err
}
tx.Sign(types.SECP256K1, client.privateKey)
return tx, nil
}
func (client *commitMsgClient) sendCommitMsg(ch chan *types.Transaction) {
var err error
var tx *types.Transaction
var resendTimer <-chan time.Time
out:
for {
select {
case tx = <-ch:
err = client.sendCommitMsgTx(tx)
if err != nil && (err != types.ErrBalanceLessThanTenTimesFee && err != types.ErrNoBalance) {
resendTimer = time.After(time.Second * 2)
}
case <-resendTimer:
if err != nil && tx != nil {
client.sendCommitMsgTx(tx)
}
case <-client.quit:
break out
}
}
client.paraClient.wg.Done()
}
func (client *commitMsgClient) sendCommitMsgTx(tx *types.Transaction) error {
if tx == nil {
return nil
}
resp, err := client.paraClient.grpcClient.SendTransaction(context.Background(), tx)
if err != nil {
plog.Error("sendCommitMsgTx send tx", "tx", common.ToHex(tx.Hash()), "err", err.Error())
return err
}
if !resp.GetIsOk() {
plog.Error("sendCommitMsgTx send tx Nok", "tx", common.ToHex(tx.Hash()), "err", string(resp.GetMsg()))
return errors.New(string(resp.GetMsg()))
}
return nil
}
func checkTxInMainBlock(targetTx *types.Transaction, detail *types.BlockDetail) bool {
txMap := make(map[string]bool)
for i, tx := range detail.Block.Txs {
if bytes.HasSuffix(tx.Execer, []byte(pt.ParaX)) && detail.Receipts[i].Ty == types.ExecOk {
txMap[string(tx.Hash())] = true
}
}
return txMap[string(targetTx.Hash())]
}
func isParaSelfConsensusForked(height int64) bool {
return height > mainParaSelfConsensusForkHeight
}
//当前未考虑获取key非常多失败的场景, 如果获取height非常多,block模块会比较大,但是使用完了就释放了
//如果有必要也可以考虑每次最多取20个一个txgroup,发送共识部分循环获取发送也没问题
func (client *commitMsgClient) getNodeStatus(start, end int64) ([]*pt.ParacrossNodeStatus, error) {
var ret []*pt.ParacrossNodeStatus
if start == 0 {
geneStatus, err := client.getGenesisNodeStatus()
if err != nil {
return nil, err
}
ret = append(ret, geneStatus)
start++
}
if end < start {
return ret, nil
}
req := &types.ReqBlocks{Start: start, End: end}
count := req.End - req.Start + 1
nodeList := make(map[int64]*pt.ParacrossNodeStatus, count+1)
keys := &types.LocalDBGet{}
for i := 0; i < int(count); i++ {
key := paracross.CalcMinerHeightKey(types.GetTitle(), req.Start+int64(i))
keys.Keys = append(keys.Keys, key)
}
r, err := client.paraClient.GetAPI().LocalGet(keys)
if err != nil {
return nil, err
}
if count != int64(len(r.Values)) {
plog.Error("paracommitmsg get node status key", "expect count", count, "actual count", len(r.Values))
return nil, err
}
for _, val := range r.Values {
status := &pt.ParacrossNodeStatus{}
err = types.Decode(val, status)
if err != nil {
return nil, err
}
if !(status.Height >= req.Start && status.Height <= req.End) {
plog.Error("paracommitmsg decode node status", "height", status.Height, "expect start", req.Start,
"end", req.End, "status", status)
return nil, errors.New("paracommitmsg wrong key result")
}
nodeList[status.Height] = status
}
for i := 0; i < int(count); i++ {
if nodeList[req.Start+int64(i)] == nil {
plog.Error("paracommitmsg get node status key nil", "height", req.Start+int64(i))
return nil, errors.New("paracommitmsg wrong key status result")
}
}
v, err := client.paraClient.GetAPI().GetBlocks(req)
if err != nil {
return nil, err
}
if count != int64(len(v.Items)) {
plog.Error("paracommitmsg get node status block", "expect count", count, "actual count", len(v.Items))
return nil, err
}
for _, block := range v.Items {
if !(block.Block.Height >= req.Start && block.Block.Height <= req.End) {
plog.Error("paracommitmsg get node status block", "height", block.Block.Height, "expect start", req.Start, "end", req.End)
return nil, errors.New("paracommitmsg wrong block result")
}
nodeList[block.Block.Height].BlockHash = block.Block.Hash()
nodeList[block.Block.Height].StateHash = block.Block.StateHash
}
var needSentTxs uint32
for i := 0; i < int(count); i++ {
ret = append(ret, nodeList[req.Start+int64(i)])
needSentTxs += nodeList[req.Start+int64(i)].NonCommitTxCounts
}
//1.如果是只有commit tx的空块,推迟发送,直到等到一个完全没有commit tx的空块或者其他tx的块
//2,如果20个块都是 commit tx的空块,20个块打包一次发送,尽量减少commit tx造成的空块
//3,如果形如xxoxx的块排列,x代表commit空块,o代表实际的块,即只要不全部是commit块,也要全部打包一起发出去
//如果=0 意味着全部是paracross commit tx,延迟发送
if needSentTxs == 0 && len(ret) < types.TxGroupMaxCount {
plog.Debug("para commitmsg getNodeStatus all self consensus commit tx,send delay", "start", start, "end", end)
return nil, nil
}
return ret, nil
}
func (client *commitMsgClient) getGenesisNodeStatus() (*pt.ParacrossNodeStatus, error) {
var status pt.ParacrossNodeStatus
req := &types.ReqBlocks{Start: 0, End: 0}
v, err := client.paraClient.GetAPI().GetBlocks(req)
if err != nil {
return nil, err
}
block := v.Items[0].Block
if block.Height != 0 {
return nil, errors.New("block chain not return 0 height block")
}
status.Title = types.GetTitle()
status.Height = block.Height
status.PreBlockHash = zeroHash[:]
status.BlockHash = block.Hash()
status.PreStateHash = zeroHash[:]
status.StateHash = block.StateHash
return &status, nil
}
func (client *commitMsgClient) onBlockAdded(height int64) error {
select {
case client.commitMsgNotify <- height:
case <-client.quit:
}
return nil
}
func (client *commitMsgClient) onBlockDeleted(height int64) {
select {
case client.delMsgNotify <- height:
case <-client.quit:
}
}
func (client *commitMsgClient) onMainBlockAdded(block *types.BlockDetail) {
select {
case client.mainBlockAdd <- block:
case <-client.quit:
}
}
//only sync once, as main usually sync, here just need the first sync status after start up
func (client *commitMsgClient) mainSync() error {
req := &types.ReqNil{}
reply, err := client.paraClient.grpcClient.IsSync(context.Background(), req)
if err != nil {
plog.Error("Paracross main is syncing", "err", err.Error())
return err
}
if !reply.IsOk {
plog.Error("Paracross main reply not ok")
return err
}
plog.Info("Paracross main sync succ")
return nil
}
func (client *commitMsgClient) getConsensusHeight(consensusRst chan *pt.ParacrossStatus) {
ticker := time.NewTicker(time.Second * time.Duration(consensusInterval))
isSync := false
defer ticker.Stop()
out:
for {
select {
case <-client.quit:
break out
case <-ticker.C:
if !isSync {
err := client.mainSync()
if err != nil {
continue
}
isSync = true
}
client.paraClient.mtx.Lock()
isCaughtUp := client.paraClient.isCaughtUp
client.paraClient.mtx.Unlock()
if !isCaughtUp {
plog.Debug("getConsensusHeight para is CatchingUp")
continue
}
_, block, err := client.paraClient.getLastBlockInfo()
if err != nil {
continue
}
status, err := client.getConsensusStatus(block)
if err != nil {
continue
}
consensusRst <- status
}
}
client.paraClient.wg.Done()
}
func (client *commitMsgClient) getConsensusStatus(block *types.Block) (*pt.ParacrossStatus, error) {
if isParaSelfConsensusForked(block.MainHeight) {
//从本地查询共识高度
ret, err := client.paraClient.GetAPI().QueryChain(&types.ChainExecutor{
Driver: "paracross",
FuncName: "GetTitle",
Param: types.Encode(&types.ReqString{Data: types.GetTitle()}),
})
if err != nil {
plog.Error("getConsensusHeight ", "err", err.Error())
return nil, err
}
resp, ok := ret.(*pt.ParacrossStatus)
if !ok {
plog.Error("getConsensusHeight ParacrossStatus nok")
return nil, err
}
//开启自共识后也要等到自共识真正切换之后再使用,如果本地区块已经过了自共识高度,但自共识的高度还没达成,就会导致共识机制出错
if resp.Height > -1 {
return resp, nil
}
}
//去主链获取共识高度
reply, err := client.paraClient.grpcClient.QueryChain(context.Background(), &types.ChainExecutor{
Driver: "paracross",
FuncName: "GetTitleByHash",
Param: types.Encode(&pt.ReqParacrossTitleHash{Title: types.GetTitle(), BlockHash: block.MainHash}),
})
if err != nil {
plog.Error("getMainConsensusHeight", "err", err.Error())
return nil, err
}
if !reply.GetIsOk() {
plog.Info("getMainConsensusHeight nok", "error", reply.GetMsg())
return nil, err
}
var result pt.ParacrossStatus
err = types.Decode(reply.Msg, &result)
if err != nil {
plog.Error("getMainConsensusHeight decode", "err", err.Error())
return nil, err
}
return &result, nil
}
func (client *commitMsgClient) onWalletStatus(status *types.WalletStatus) {
if status == nil || client.paraClient.authAccount == "" {
return
}
if !status.IsWalletLock && client.privateKey == nil {
client.fetchPriKey()
plog.Info("para commit fetchPriKey")
}
if client.privateKey == nil {
plog.Info("para commit wallet status prikey null", "status", status.IsWalletLock)
return
}
select {
case client.minerSwitch <- !status.IsWalletLock:
case <-client.quit:
}
}
func (client *commitMsgClient) onWalletAccount(acc *types.Account) {
if acc == nil || client.paraClient.authAccount == "" || client.paraClient.authAccount != acc.Addr || client.privateKey != nil {
return
}
err := client.fetchPriKey()
if err != nil {
plog.Error("para commit fetchPriKey", "err", err.Error())
return
}
select {
case client.minerSwitch <- true:
case <-client.quit:
}
}
func (client *commitMsgClient) fetchPriKey() error {
req := &types.ReqString{Data: client.paraClient.authAccount}
msg := client.paraClient.GetQueueClient().NewMessage("wallet", types.EventDumpPrivkey, req)
err := client.paraClient.GetQueueClient().Send(msg, true)
if err != nil {
plog.Error("para commit send msg", "err", err.Error())
return err
}
resp, err := client.paraClient.GetQueueClient().Wait(msg)
if err != nil {
plog.Error("para commit msg sign to wallet", "err", err.Error())
return err
}
str := resp.GetData().(*types.ReplyString).Data
pk, err := common.FromHex(str)
if err != nil && pk == nil {
return err
}
secp, err := crypto.New(types.GetSignName("", types.SECP256K1))
if err != nil {
return err
}
priKey, err := secp.PrivKeyFromBytes(pk)
if err != nil {
plog.Error("para commit msg get priKey", "err", err.Error())
return err
}
client.privateKey = priKey
plog.Info("para commit fetchPriKey success")
return nil
}