/
template.go
1056 lines (939 loc) · 36.3 KB
/
template.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
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
package blockchain
import (
"container/heap"
"container/list"
"crypto/sha256"
"encoding/hex"
"fmt"
"math/big"
"math/rand"
"time"
"github.com/massnetorg/mass-core/blockchain/state"
"github.com/massnetorg/mass-core/config"
"github.com/massnetorg/mass-core/consensus"
"github.com/massnetorg/mass-core/consensus/forks"
"github.com/massnetorg/mass-core/database"
"github.com/massnetorg/mass-core/logging"
"github.com/massnetorg/mass-core/massutil"
"github.com/massnetorg/mass-core/massutil/safetype"
"github.com/massnetorg/mass-core/poc"
"github.com/massnetorg/mass-core/txscript"
"github.com/massnetorg/mass-core/wire"
)
const (
// blockHeaderOverhead is the min number of bytes it takes to serialize
// a block header.
blockHeaderOverhead = wire.MinBlockHeaderPayload
// maximum number of binding evidence(recommended)
MaxBindingNum = 10
// 36 prev outpoint, 8 sequence
bindingPayload = 44 * MaxBindingNum
// minHighPriority is the minimum priority value that allows a
// transaction to be considered high priority.
minHighPriority = consensus.MinHighPriority
PriorityProposalSize = wire.MaxBlockPayload / 20
)
var (
anyoneRedeemableScript []byte
)
func init() {
var err error
anyoneRedeemableScript, err = txscript.NewScriptBuilder().AddOp(txscript.OP_TRUE).Script()
if err != nil {
panic("init anyoneRedeemableScript: " + err.Error())
}
}
// txPrioItem houses a transaction along with extra information that allows the
// transaction to be prioritized and track dependencies on other transactions
// which have not been mined into a block yet.
type txPrioItem struct {
tx *massutil.Tx
fee int64
priority float64
feePerKB float64
// dependsOn holds a map of transaction hashes which this one depends
// on. It will only be set when the transaction references other
// transactions in the memory pool and hence must come after them in
// a block.
dependsOn map[wire.Hash]struct{}
}
// txPriorityQueueLessFunc describes a function that can be used as a compare
// function for a transaction priority queue (txPriorityQueue).
type txPriorityQueueLessFunc func(*txPriorityQueue, int, int) bool
// txPriorityQueue implements a priority queue of txPrioItem elements that
// supports an arbitrary compare function as defined by txPriorityQueueLessFunc.
type txPriorityQueue struct {
lessFunc txPriorityQueueLessFunc
items []*txPrioItem
}
// Len returns the number of items in the priority queue. It is part of the
// heap.Interface implementation.
func (pq *txPriorityQueue) Len() int {
return len(pq.items)
}
// Less returns whether the item in the priority queue with index i should sort
// before the item with index j by deferring to the assigned less function. It
// is part of the heap.Interface implementation.
func (pq *txPriorityQueue) Less(i, j int) bool {
return pq.lessFunc(pq, i, j)
}
// Swap swaps the items at the passed indices in the priority queue. It is
// part of the heap.Interface implementation.
func (pq *txPriorityQueue) Swap(i, j int) {
pq.items[i], pq.items[j] = pq.items[j], pq.items[i]
}
// Push pushes the passed item onto the priority queue. It is part of the
// heap.Interface implementation.
func (pq *txPriorityQueue) Push(x interface{}) {
pq.items = append(pq.items, x.(*txPrioItem))
}
// Pop removes the highest priority item (according to Less) from the priority
// queue and returns it. It is part of the heap.Interface implementation.
func (pq *txPriorityQueue) Pop() interface{} {
n := len(pq.items)
item := pq.items[n-1]
pq.items[n-1] = nil
pq.items = pq.items[0 : n-1]
return item
}
// SetLessFunc sets the compare function for the priority queue to the provided
// function. It also invokes heap.Init on the priority queue using the new
// function so it can immediately be used with heap.Push/Pop.
func (pq *txPriorityQueue) SetLessFunc(lessFunc txPriorityQueueLessFunc) {
pq.lessFunc = lessFunc
heap.Init(pq)
}
// txPQByPriority sorts a txPriorityQueue by transaction priority and then fees
// per kilobyte.
func txPQByPriority(pq *txPriorityQueue, i, j int) bool {
// Using > here so that pop gives the highest priority item as opposed
// to the lowest. Sort by priority first, then fee.
if pq.items[i].priority == pq.items[j].priority {
return pq.items[i].feePerKB > pq.items[j].feePerKB
}
return pq.items[i].priority > pq.items[j].priority
}
// txPQByFee sorts a txPriorityQueue by fees per kilobyte and then transaction
// priority.
func txPQByFee(pq *txPriorityQueue, i, j int) bool {
// Using > here so that pop gives the highest fee item as opposed
// to the lowest. Sort by fee first, then priority.
if pq.items[i].feePerKB == pq.items[j].feePerKB {
return pq.items[i].priority > pq.items[j].priority
}
return pq.items[i].feePerKB > pq.items[j].feePerKB
}
// newTxPriorityQueue returns a new transaction priority queue that reserves the
// passed amount of space for the elements. The new priority queue uses either
// the txPQByPriority or the txPQByFee compare function depending on the
// sortByFee parameter and is already initialized for use with heap.Push/Pop.
// The priority queue can grow larger than the reserved space, but extra copies
// of the underlying array can be avoided by reserving a sane value.
func newTxPriorityQueue(reserve int, sortByFee bool) *txPriorityQueue {
pq := &txPriorityQueue{
items: make([]*txPrioItem, 0, reserve),
}
if sortByFee {
pq.SetLessFunc(txPQByFee)
} else {
pq.SetLessFunc(txPQByPriority)
}
return pq
}
type Proof interface {
// Returns PublicKey.SerializeCompressed()
PlotPublicKey() []byte
ProofType() poc.ProofType
ProofBitLength() int
// Returns PublicKey.SerializeCompressed()
ChiaPoolPublicKey() []byte
ChiaPlotID() [32]byte
}
type PoCTemplate struct {
Height uint64
Timestamp time.Time
Previous wire.Hash
Challenge wire.Hash
GetTarget func(time.Time) *big.Int
RewardAddress []database.Rank
GetCoinbase func(Proof, massutil.Amount) (*massutil.Tx, error)
PassBinding func(Proof) bool
Err error
}
type BlockTemplate struct {
Block *wire.MsgBlock
TotalFee massutil.Amount
SigOpCounts []int64
Height uint64
ValidPayAddress bool
MerkleCache []*wire.Hash
WitnessMerkleCache []*wire.Hash
Err error
}
func reCreateCoinbaseTx(coinbase *wire.MsgTx, bindingTxListReply []*database.BindingTxReply, nextBlockHeight uint64,
rewardAddresses []database.Rank, totalFee, requiredBinding massutil.Amount, chainParams *config.Params) (err error) {
minerPkScript := coinbase.TxOut[len(coinbase.TxOut)-1].PkScript
coinbase.RemoveAllTxOut()
totalBinding := massutil.ZeroAmount()
// means still have reward in coinbase
// originMiner cannot be smaller than diff
// has guranty tx
txIns := make([]*wire.TxIn, 0)
hasValidBinding := false
if len(bindingTxListReply) > 0 {
var witness [][]byte
bindingNum := 0
for _, bindingTx := range bindingTxListReply {
txHash := bindingTx.TxSha
index := bindingTx.Index
blocksSincePrev := nextBlockHeight - bindingTx.Height
if bindingTx.IsCoinbase {
if blocksSincePrev < consensus.CoinbaseMaturity {
logging.CPrint(logging.WARN, "the txIn is not mature", logging.LogFormat{"txid": txHash.String(), "index": index})
continue
}
} else {
if blocksSincePrev < consensus.TransactionMaturity {
logging.CPrint(logging.WARN, "the txIn is not mature", logging.LogFormat{"txid": txHash.String(), "index": index})
continue
}
}
totalBinding, err = totalBinding.AddInt(bindingTx.Value)
if err != nil {
return err
}
prevOut := wire.NewOutPoint(txHash, index)
txIn := wire.NewTxIn(prevOut, witness)
txIns = append(txIns, txIn)
if totalBinding.Cmp(requiredBinding) >= 0 {
hasValidBinding = true
break
}
bindingNum++
if bindingNum >= MaxBindingNum {
break
}
}
} else {
if !forks.EnforceMASSIP0002(nextBlockHeight) {
logging.CPrint(logging.INFO, "re-create coinbase transaction without mass binding", logging.LogFormat{"height": nextBlockHeight})
} else {
// If miner has no binding, execution cannot reach here.
hasValidBinding = true
}
}
miner, superNode, err := CalcBlockSubsidy(nextBlockHeight, chainParams, hasValidBinding, len(rewardAddresses) > 0)
if err != nil {
logging.CPrint(logging.ERROR, "fail on CalcBlockSubsidy", logging.LogFormat{
"err": err,
"height": nextBlockHeight,
"total_binding": totalBinding,
"required_binding": requiredBinding,
"reward_addresses_count": len(rewardAddresses),
})
return err
}
// mint
diff := massutil.ZeroAmount()
if !miner.IsZero() {
if hasValidBinding {
for _, txIn := range txIns {
coinbase.AddTxIn(txIn)
}
}
stakingNodes := make([]forks.StakingNode, 0, len(rewardAddresses))
for _, snode := range rewardAddresses {
stakingNodes = append(stakingNodes, snode)
}
totalWeight, err := forks.CalcTotalStakingWeight(nextBlockHeight, stakingNodes...)
if err != nil {
return err
}
logging.CPrint(logging.INFO, "show the count of stakingTx", logging.LogFormat{"count": len(rewardAddresses)})
// calc reward
totalSNValue := massutil.ZeroAmount()
for i := 0; i < len(rewardAddresses); i++ {
key := make([]byte, sha256.Size)
copy(key, rewardAddresses[i].ScriptHash[:])
pkScriptSuperNode, err := txscript.PayToWitnessScriptHashScript(key)
if err != nil {
return err
}
nodeWeight, err := forks.CalcStakingNodeWeight(nextBlockHeight, rewardAddresses[i])
if err != nil {
return err
}
nodeReward, err := calcNodeReward(superNode, totalWeight, nodeWeight)
if err != nil {
return err
}
if nodeReward.IsZero() {
// break loop as rewordAddress is in descending order by value
break
}
totalSNValue, err = totalSNValue.Add(nodeReward)
if err != nil {
return err
}
coinbase.AddTxOut(&wire.TxOut{
Value: nodeReward.IntValue(),
PkScript: pkScriptSuperNode,
})
}
coinbase.SetPayload(standardCoinbasePayload(nextBlockHeight, uint32(len(coinbase.TxOut))))
diff, err = superNode.Sub(totalSNValue)
if err != nil {
return err
}
// add diff from staking tx
miner, err = miner.Add(diff)
if err != nil {
return err
}
}
miner, err = miner.Add(totalFee)
if err != nil {
return err
}
coinbase.AddTxOut(&wire.TxOut{
PkScript: minerPkScript,
Value: miner.IntValue(),
})
return
}
// createCoinbaseTx returns a coinbase transaction paying an appropriate subsidy
// based on the passed block height to the provided wallet. When the wallet
// is nil, the coinbase transaction will instead be redeemable by anyone.
//
// See the comment for NewBlockTemplate for more information about why the nil
// address handling is useful.
func createCoinbaseTx(nextBlockHeight uint64, addr massutil.Address, rewardAddresses []database.Rank, chainParams *config.Params) (*massutil.Tx, error) {
tx := wire.NewMsgTx()
tx.AddTxIn(&wire.TxIn{
// Coinbase transactions have no inputs, so previous outpoint is
// zero hash and max index.
PreviousOutPoint: *wire.NewOutPoint(&wire.Hash{},
wire.MaxPrevOutIndex),
Sequence: wire.MaxTxInSequenceNum,
})
tx.SetPayload(standardCoinbasePayload(nextBlockHeight, 0))
// Create a script for paying to the miner if one was specified.
// Otherwise create a script that allows the coinbase to be
// redeemable by anyone.
pkScriptMiner := anyoneRedeemableScript
var err error
if addr != nil {
pkScriptMiner, err = txscript.PayToAddrScript(addr)
if err != nil {
return nil, err
}
}
miner, _, err := CalcBlockSubsidy(nextBlockHeight, chainParams, true, len(rewardAddresses) > 0)
if err != nil {
return nil, err
}
// no longer mint
if miner.IsZero() {
tx.AddTxOut(&wire.TxOut{
Value: miner.IntValue(),
PkScript: pkScriptMiner,
})
return massutil.NewTx(tx), nil
}
// mint
//diff := safetype.NewUint64()
//totalStakingValue := safetype.NewUint64()
//for _, v := range rewardAddresses {
// totalStakingValue, err = totalStakingValue.AddInt(v.Value)
// if err != nil {
// return nil, err
// }
//}
// calc reward
//totalSNValue := safetype.NewUint64()
for i := 0; i < len(rewardAddresses); i++ {
key := make([]byte, sha256.Size)
copy(key, rewardAddresses[i].ScriptHash[:])
pkScriptSuperNode, err := txscript.PayToWitnessScriptHashScript(key)
if err != nil {
return nil, err
}
//superNodeValue, err := calcSuperNodeReward(superNode, totalStakingValue, rewardAddresses[i].Value)
//if err != nil {
// return nil, err
//}
//if superNodeValue.IsZero() {
// // break loop as rewardAddresses is in descending order by value
// break
//}
//totalSNValue, err = totalSNValue.Add(superNodeValue)
//if err != nil {
// return nil, err
//}
tx.AddTxOut(&wire.TxOut{
Value: 0,
PkScript: pkScriptSuperNode,
})
}
//miner, err = miner.Add(diff)
//if err != nil {
// return nil, err
//}
tx.AddTxOut(&wire.TxOut{
Value: miner.IntValue(),
PkScript: pkScriptMiner,
})
return massutil.NewTx(tx), nil
}
func calcNodeReward(totalReward massutil.Amount, totalWeight, nodeWeight *safetype.Uint128) (massutil.Amount, error) {
u, err := totalReward.Value().Mul(nodeWeight)
if err != nil {
return massutil.ZeroAmount(), err
}
u, err = u.Div(totalWeight)
if err != nil {
return massutil.ZeroAmount(), err
}
return massutil.NewAmount(u)
}
// logSkippedDeps logs any dependencies which are also skipped as a result of
// skipping a transaction while generating a block template at the trace level.
func logSkippedDeps(tx *massutil.Tx, deps *list.List) {
if deps == nil {
return
}
for e := deps.Front(); e != nil; e = e.Next() {
item := e.Value.(*txPrioItem)
logging.CPrint(logging.TRACE, "skipping tx since it depends on tx which is already skipped", logging.LogFormat{"txid": item.tx.Hash().String(), "depend": tx.Hash().String()})
}
}
// NewBlockTemplate returns a new block template that is ready to be solved
// using the transactions from the passed transaction memory pool and a coinbase
// that either pays to the passed address if it is not nil, or a coinbase that
// is redeemable by anyone if the passed wallet is nil. The nil wallet
// functionality is useful since there are cases such as the getblocktemplate
// RPC where external mining software is responsible for creating their own
// coinbase which will replace the one generated for the block template. Thus
// the need to have configured address can be avoided.
//
// The transactions selected and included are prioritized according to several
// factors. First, each transaction has a priority calculated based on its
// value, age of inputs, and size. Transactions which consist of larger
// amounts, older inputs, and small sizes have the highest priority. Second, a
// fee per kilobyte is calculated for each transaction. Transactions with a
// higher fee per kilobyte are preferred. Finally, the block generation related
// configuration options are all taken into account.
//
// Transactions which only spend outputs from other transactions already in the
// block chain are immediately added to a priority queue which either
// prioritizes based on the priority (then fee per kilobyte) or the fee per
// kilobyte (then priority) depending on whether or not the BlockPrioritySize
// configuration option allots space for high-priority transactions.
// Transactions which spend outputs from other transactions in the memory pool
// are added to a dependency map so they can be added to the priority queue once
// the transactions they depend on have been included.
//
// Once the high-priority area (if configured) has been filled with transactions,
// or the priority falls below what is considered high-priority, the priority
// queue is updated to prioritize by fees per kilobyte (then priority).
//
// When the fees per kilobyte drop below the TxMinFreeFee configuration option,
// the transaction will be skipped unless there is a BlockMinSize set, in which
// case the block will be filled with the low-fee/free transactions until the
// block size reaches that minimum size.
//
// Any transactions which would cause the block to exceed the BlockMaxSize
// configuration option, exceed the maximum allowed signature operations per
// block, or otherwise cause the Block to be invalid are skipped.
//
// Given the above, a block generated by this function is of the following form:
//
// ----------------------------------- -- --
// | Coinbase Transaction | | |
// |-----------------------------------| | |
// | | | | ----- cfg.BlockPrioritySize
// | High-priority Transactions | | |
// | | | |
// |-----------------------------------| | --
// | | |
// | | |
// | | |--- cfg.BlockMaxSize
// | Transactions prioritized by fee | |
// | until <= cfg.TxMinFreeFee | |
// | | |
// | | |
// | | |
// |-----------------------------------| |
// | Low-fee/Non high-priority (free) | |
// | transactions (while block size | |
// | <= cfg.BlockMinSize) | |
// ----------------------------------- --
func (chain *Blockchain) NewBlockTemplate(payoutAddresses []massutil.Address, templateCh chan interface{}) error {
chain.l.Lock()
defer chain.l.Unlock()
// Get snapshot of chain/txPool
bestNode := chain.blockTree.bestBlockNode()
txs := chain.txPool.TxDescs()
punishments := chain.proposalPool.PunishmentProposals()
rewardAddress, err := chain.db.FetchUnexpiredStakingRank(bestNode.Height+1, true)
if err != nil {
return err
}
bindingState, err := bestNode.BindingState(chain.stateBindingDb)
if err != nil {
return err
}
// run newBlockTemplate as goroutine
go newBlockTemplate(chain, payoutAddresses, templateCh, bestNode, txs, punishments, rewardAddress, bindingState)
return nil
}
func newBlockTemplate(chain *Blockchain, payoutAddresses []massutil.Address, templateCh chan interface{}, bestNode *BlockNode,
mempoolTxns []*TxDesc, proposals []*PunishmentProposal, rewardAddresses []database.Rank, bindingState state.Trie) {
rand.Seed(time.Now().Unix())
payoutAddress := payoutAddresses[rand.Intn(len(payoutAddresses))]
addrSet := make(map[string]int)
for i, addr := range payoutAddresses {
if !massutil.IsWitnessV0Address(addr) {
// should never happen
logging.CPrint(logging.WARN, "skip non-witnessV0 address", logging.LogFormat{"addr": addr.EncodeAddress()})
continue
}
addrSet[string(addr.ScriptAddress())] = i
}
nextBlockHeight := bestNode.Height + 1
challenge, err := calcNextChallenge(bestNode)
if err != nil {
templateCh <- &PoCTemplate{
Err: err,
}
return
}
coinbaseTx, err := createCoinbaseTx(nextBlockHeight, payoutAddress, rewardAddresses, chain.chainParams)
if err != nil {
templateCh <- &PoCTemplate{
Err: err,
}
return
}
// getCoinbaseTx := func(pubKey interfaces.PublicKey, totalFee massutil.Amount, proofType poc.ProofType, bitLength int) (*massutil.Tx, error) {
getCoinbaseTx := func(proof Proof, totalFee massutil.Amount) (*massutil.Tx, error) {
var bindingTxListReply []*database.BindingTxReply
requiredBinding := massutil.ZeroAmount()
if !forks.EnforceMASSIP0002WarmUp(nextBlockHeight) {
pkScriptHash, err := pkToScriptHash(proof.PlotPublicKey(), chain.chainParams)
if err != nil {
return nil, err
}
bindingTxListReply, err = chain.db.FetchOldBinding(pkScriptHash)
if err != nil {
return nil, err
}
requiredBinding, err = forks.GetRequiredBinding(nextBlockHeight, 0, proof.ProofBitLength(), massutil.ZeroAmount())
if err != nil {
return nil, err
}
}
actual := payoutAddress.EncodeAddress()
if proof.ProofType() == poc.ProofTypeChia {
poolPk := proof.ChiaPoolPublicKey()
if len(poolPk) == 0 {
return nil, fmt.Errorf("unexpect ni pool pk in getCoinbaseTx")
}
replaceCoinbase, _, err := GetPoolPkCoinbase(bindingState, poolPk)
if err != nil {
return nil, fmt.Errorf("unexpected GetPoolPkCoinbase error in getCoinbaseTx: %v", err)
}
if len(replaceCoinbase) != 0 {
if _, ok := addrSet[string(replaceCoinbase)]; !ok {
return nil, fmt.Errorf("unexpected coinbase missing in getCoinbaseTx %s", hex.EncodeToString(replaceCoinbase))
}
replaceScript, err := txscript.PayToWitnessScriptHashScript(replaceCoinbase)
if err != nil {
return nil, fmt.Errorf("failed to create replace coinbase script: %v", err)
}
replacedAddress, err := massutil.NewAddressWitnessScriptHash(replaceCoinbase, chain.chainParams)
if err != nil {
return nil, fmt.Errorf("failed to new address from wit script hash: %v", err)
}
actual = replacedAddress.EncodeAddress()
coinbaseTx.MsgTx().TxOut[len(coinbaseTx.MsgTx().TxOut)-1].PkScript = replaceScript
}
}
err = reCreateCoinbaseTx(coinbaseTx.MsgTx(), bindingTxListReply, nextBlockHeight, rewardAddresses, totalFee, requiredBinding, chain.chainParams)
if err != nil {
return nil, err
}
logging.CPrint(logging.INFO, "finalize coinbase transaction", logging.LogFormat{
"default_coinbase": payoutAddress.EncodeAddress(),
"actual_coinbase": actual,
})
return coinbaseTx, nil
}
getTarget := func(timestamp time.Time) *big.Int {
target, _ := calcNextTarget(bestNode, timestamp, chain.chainParams)
return target
}
// passBinding := func(pubKey interfaces.PublicKey, proofType poc.ProofType, bitLength int, plotID [32]byte) bool {
passBinding := func(proof Proof) bool {
if !forks.EnforceMASSIP0002(nextBlockHeight) {
// Only MASS allowed
return proof.ProofType() == poc.ProofTypeDefault
}
if !poc.IsValidProofType(proof.ProofType()) || !proof.ProofType().EnsureBitLength(proof.ProofBitLength()) {
logging.CPrint(logging.INFO, "invalid proof type or bitlength", logging.LogFormat{"type": proof.ProofType(), "bl": proof.ProofBitLength()})
return false
}
bindingTarget, err := pkToScriptHash(proof.PlotPublicKey(), chain.chainParams)
if err != nil {
logging.CPrint(logging.INFO, "get binding target error", logging.LogFormat{"err": err})
return false
}
if proof.ProofType() == poc.ProofTypeChia {
plotID := proof.ChiaPlotID()
if plotID == [32]byte{} {
logging.CPrint(logging.ERROR, "get zero plot ID")
return false
}
if bindingTarget, err = pkToScriptHash(plotID[:], chain.chainParams); err != nil {
logging.CPrint(logging.ERROR, "failed to hash chia plot ID", logging.LogFormat{"err": err})
return false
}
poolPk := proof.ChiaPoolPublicKey()
if len(poolPk) == 0 {
logging.CPrint(logging.ERROR, "get nil chia pool pk")
return false
}
coinbase, _, err := GetPoolPkCoinbase(bindingState, poolPk)
if err != nil {
logging.CPrint(logging.ERROR, "failed to get coinbase associated with pool pk", logging.LogFormat{
"poolPk": hex.EncodeToString(poolPk),
"err": err,
})
return false
}
if len(coinbase) != 0 {
if _, ok := addrSet[string(coinbase)]; !ok {
logging.CPrint(logging.WARN, "coinbase associated with pool pk not in list", logging.LogFormat{
"poolPk": hex.EncodeToString(poolPk),
"coinbase": hex.EncodeToString(coinbase),
})
return false
}
}
}
bindingTarget = append(bindingTarget, byte(proof.ProofType()), byte(proof.ProofBitLength()))
buf, _ := bindingState.TryGet(bindingTarget)
// err does no matter
return buf != nil
}
templateCh <- &PoCTemplate{
Height: nextBlockHeight,
Timestamp: bestNode.Timestamp.Add(1 * poc.PoCSlot * time.Second),
Previous: *bestNode.Hash,
Challenge: *challenge,
GetTarget: getTarget,
RewardAddress: rewardAddresses,
GetCoinbase: getCoinbaseTx,
PassBinding: passBinding,
}
numCoinbaseSigOps := int64(CountSigOps(coinbaseTx))
// Get the current memory pool transactions and create a priority queue
// to hold the transactions which are ready for inclusion into a block
// along with some priority related and fee metadata. Reserve the same
// number of items that are in the memory pool for the priority queue.
// Also, choose the initial sort order for the priority queue based on
// whether or not there is an area allocated for high-priority
// transactions.
depMap := make(map[wire.Hash]struct{})
for _, txDesc := range mempoolTxns {
txhash := txDesc.Tx.Hash()
if _, exist := depMap[*txhash]; !exist {
depMap[*txhash] = struct{}{}
}
}
sortedByFee := config.BlockPrioritySize == 0
priorityQueue := newTxPriorityQueue(len(mempoolTxns), sortedByFee)
// Create a slice to hold the transactions to be included in the
// generated block with reserved space. Also create a transaction
// store to house all of the input transactions so multiple lookups
// can be avoided.
blockTxns := make([]*wire.MsgTx, 0, len(mempoolTxns))
blockTxns = append(blockTxns, coinbaseTx.MsgTx())
//blockTxStore := make(TxStore)
punishProposals := make([]*wire.FaultPubKey, 0, len(proposals))
otherProposals := make([]*wire.NormalProposal, 0)
var totalProposalsSize int
totalProposalsSize += 4
// dependers is used to track transactions which depend on another
// transaction in the memory pool. This, in conjunction with the
// dependsOn map kept with each dependent transaction helps quickly
// determine which dependent transactions are now eligible for inclusion
// in the block once each transaction has been included.
dependers := make(map[wire.Hash]*list.List)
// Create slices to hold the fees and number of signature operations
// for each of the selected transactions and add an entry for the
// coinbase. This allows the code below to simply append details about
// a transaction as it is selected for inclusion in the final block.
// However, since the total fees aren't known yet, use a dummy value for
// the coinbase fee which will be updated later.
txSigOpCounts := make([]int64, 0, len(mempoolTxns))
txSigOpCounts = append(txSigOpCounts, numCoinbaseSigOps)
logging.CPrint(logging.DEBUG, "Considering transactions in mempool for inclusion to new block", logging.LogFormat{"tx count": len(mempoolTxns)})
if len(proposals) != 0 {
for _, proposal := range proposals {
if totalProposalsSize+proposal.Size <= PriorityProposalSize {
totalProposalsSize += proposal.Size
punishProposals = append(punishProposals, proposal.FaultPubKey)
}
}
} else {
totalProposalsSize += wire.HeaderSizePerPlaceHolder * 2
}
//mempoolLoop:
for _, txDesc := range mempoolTxns {
// A block can't have more than one coinbase or contain
// non-finalized transactions.
tx := txDesc.Tx
// Setup dependencies for any transactions which reference
// other transactions in the mempool so they can be properly
// ordered below.
prioItem := &txPrioItem{tx: txDesc.Tx}
for _, txIn := range tx.MsgTx().TxIn {
originHash := &txIn.PreviousOutPoint.Hash
// because mempoolTxns is snapshot of mempool, its tx can not orphan
if _, exist := depMap[*originHash]; exist {
// The transaction is referencing another
// transaction in the memory pool, so setup an
// ordering dependency.
depList, exists := dependers[*originHash]
if !exists {
depList = list.New()
dependers[*originHash] = depList
}
depList.PushBack(prioItem)
if prioItem.dependsOn == nil {
prioItem.dependsOn = make(
map[wire.Hash]struct{})
}
prioItem.dependsOn[*originHash] = struct{}{}
}
}
// Calculate the final transaction priority using the input
// value age sum as well as the adjusted transaction size. The
// formula is: sum(inputValue * inputAge) / adjustedTxSize
prioItem.priority = float64(txDesc.totalInputValue.IntValue())*(float64(nextBlockHeight)-float64(txDesc.Height)) + txDesc.startingPriority
// Calculate the fee in Maxwell/KB.
// NOTE: This is a more precise value than the one calculated
// during calcMinRelayFee which rounds up to the nearest full
// kilobyte boundary. This is beneficial since it provides an
// incentive to create smaller transactions.
txSize := tx.MsgTx().PlainSize()
prioItem.feePerKB = float64(txDesc.Fee.IntValue()) / (float64(txSize) / 1000)
prioItem.fee = txDesc.Fee.IntValue()
// Add the transaction to the priority queue to mark it ready
// for inclusion in the block unless it has dependencies.
if prioItem.dependsOn == nil {
heap.Push(priorityQueue, prioItem)
}
}
logging.CPrint(logging.TRACE, "Check the length of priority queue and dependent",
logging.LogFormat{"priority": priorityQueue.Len(), "dependent": len(dependers)})
// The starting block size is the size of the Block header plus the max
// possible transaction count size, plus the size of the coinbase
// transaction.
//modify: 360 is coinbase`s binding txIn
blockSize := uint32(blockHeaderOverhead+int64(len(punishProposals)*33)+int64(coinbaseTx.PlainSize())+int64(totalProposalsSize)) + bindingPayload
blockSigOps := numCoinbaseSigOps
totalFee := massutil.ZeroAmount()
// Choose which transactions make it into the block.
for priorityQueue.Len() > 0 {
// Grab the highest priority (or highest fee per kilobyte
// depending on the sort order) transaction.
prioItem := heap.Pop(priorityQueue).(*txPrioItem)
tx := prioItem.tx
// Grab the list of transactions which depend on this one (if
// any) and remove the entry for this transaction as it will
// either be included or skipped, but in either case the deps
// are no longer needed.
deps := dependers[*tx.Hash()]
delete(dependers, *tx.Hash())
// Enforce maximum block size. Also check for overflow.
txSize := uint32(tx.PlainSize())
blockPlusTxSize := blockSize + txSize
// Enforce maximum block size. Also check for overflow.
if blockPlusTxSize < blockSize ||
blockPlusTxSize >= config.BlockMaxSize {
logging.CPrint(logging.TRACE, "Skipping tx because it would exceed the max block weight",
logging.LogFormat{"txid": tx.Hash().String()})
logSkippedDeps(tx, deps)
continue
}
// Enforce maximum signature operations per block. Also check
// for overflow.
numSigOps := int64(CountSigOps(tx))
if blockSigOps+numSigOps < blockSigOps || blockSigOps+numSigOps > MaxSigOpsPerBlock {
logging.CPrint(logging.TRACE, "Skipping tx because it would exceed the maximum sigops per block",
logging.LogFormat{"txid": tx.Hash().String()})
logSkippedDeps(tx, deps)
continue
}
// Skip free transactions once the block is larger than the
// minimum block size.
if sortedByFee &&
prioItem.feePerKB < float64(consensus.MinRelayTxFee) &&
blockPlusTxSize >= config.BlockMinSize {
logging.CPrint(logging.TRACE, "Skipping tx with feePerKB < TxMinFreeFee and block weight >= minBlockSize",
logging.LogFormat{"txid": tx.Hash().String(), "feePerKB": prioItem.feePerKB, "TxMinFreeFee": consensus.MinRelayTxFee, "block weight": blockPlusTxSize, "minBlockSize": config.BlockMinSize})
logSkippedDeps(tx, deps)
continue
}
// Prioritize by fee per kilobyte once the block is larger than
// the priority size or there are no more high-priority
// transactions.
if !sortedByFee && (blockPlusTxSize >= config.BlockPrioritySize ||
prioItem.priority <= minHighPriority) {
logging.CPrint(logging.TRACE, "Switching to sort by fees per kilobyte since blockSize >= BlockPrioritySize || priority <= minHighPriority",
logging.LogFormat{
"block size": blockPlusTxSize,
"BlockPrioritySize": config.BlockPrioritySize,
"priority": fmt.Sprintf("%.2f", prioItem.priority),
"minHighPriority": fmt.Sprintf("%.2f", minHighPriority)})
sortedByFee = true
priorityQueue.SetLessFunc(txPQByFee)
// Put the transaction back into the priority queue and
// skip it so it is re-priortized by fees if it won't
// fit into the high-priority section or the priority
// is too low. Otherwise this transaction will be the
// final one in the high-priority section, so just fall
// though to the code below so it is added now.
if blockPlusTxSize > config.BlockPrioritySize ||
prioItem.priority < minHighPriority {
heap.Push(priorityQueue, prioItem)
continue
}
}
temp, err := totalFee.AddInt(prioItem.fee)
if err != nil {
logging.CPrint(logging.ERROR, "calc total fee error",
logging.LogFormat{
"err": err,
"txid": tx.Hash().String(),
})
continue
}
// Add the transaction to the block, increment counters, and
// save the fees and signature operation counts to the block
// template.
blockTxns = append(blockTxns, tx.MsgTx())
blockSize += txSize
blockSigOps += numSigOps
totalFee = temp
txSigOpCounts = append(txSigOpCounts, numSigOps)
logging.CPrint(logging.TRACE, "Adding tx",
logging.LogFormat{"txid": tx.Hash().String(),
"priority": fmt.Sprintf("%.2f", prioItem.priority),
"feePerKB": fmt.Sprintf("%.2f", prioItem.feePerKB)})
// Add transactions which depend on this one (and also do not
// have any other unsatisified dependencies) to the priority
// queue.
if deps != nil {
for e := deps.Front(); e != nil; e = e.Next() {
// Add the transaction to the priority queue if
// there are no more dependencies after this
// one.
item := e.Value.(*txPrioItem)
delete(item.dependsOn, *tx.Hash())
if len(item.dependsOn) == 0 {
heap.Push(priorityQueue, item)
}
}
}
}
// Next, obtain the merkle root of a tree which consists of the
// wtxid of all transactions in the block. The coinbase
// transaction will have a special wtxid of all zeroes.
witnessMerkleTree := wire.BuildMerkleTreeStoreTransactions(blockTxns, true)
witnessMerkleRoot := *witnessMerkleTree[len(witnessMerkleTree)-1]
// Create a new block ready to be solved.
// we will get chainID, Version, Height, Previous, TransactionRoot, CommitRoot,
// ProposalRoot, BanList, tx and proposal
merkles := wire.BuildMerkleTreeStoreTransactions(blockTxns, false)
var msgBlock = wire.NewEmptyMsgBlock()
msgBlock.Header.ChainID = bestNode.ChainID
msgBlock.Header.Version = forks.GetBlockVersion(nextBlockHeight)
msgBlock.Header.Height = nextBlockHeight
msgBlock.Header.Previous = *bestNode.Hash
msgBlock.Header.TransactionRoot = *merkles[len(merkles)-1]
msgBlock.Header.WitnessRoot = witnessMerkleRoot
merklesCache := make([]*wire.Hash, 0)
merklesCache = append(merklesCache, merkles[0])
if len(merkles) > 1 {
base := (len(merkles) + 1) / 2
for i := 1; i < len(merkles) && base > 0; {
merklesCache = append(merklesCache, merkles[i])
i += base
base = base / 2
}
}
witnessMerklesCache := make([]*wire.Hash, 0)
witnessMerklesCache = append(witnessMerklesCache, witnessMerkleTree[0])
if len(witnessMerkleTree) > 1 {
base := (len(witnessMerkleTree) + 1) / 2
for i := 1; i < len(witnessMerkleTree) && base > 0; {
witnessMerklesCache = append(witnessMerklesCache, witnessMerkleTree[i])
i += base
base = base / 2
}
}
proposalArea, err := wire.NewProposalArea(punishProposals, otherProposals)
if err != nil {