@@ -538,3 +538,165 @@ func (s *simulator) calcNextStakeDiffProposal7() int64 {
538
538
}
539
539
return nextDiff
540
540
}
541
+
542
+ // Algo 8 by raedah
543
+ // The goal of this algorithm is to remove oscillations from ticket price.
544
+ func (s * simulator ) calcNextStakeDiffProposal8 () int64 {
545
+ // Stake difficulty before any tickets could possibly be purchased is
546
+ // the minimum value.
547
+ nextHeight := int32 (0 )
548
+ if s .tip != nil {
549
+ nextHeight = s .tip .height + 1
550
+ }
551
+ stakeDiffStartHeight := int32 (s .params .CoinbaseMaturity ) + 1
552
+ if nextHeight < stakeDiffStartHeight {
553
+ return s .params .MinimumStakeDiff
554
+ }
555
+
556
+ // Return the previous block's difficulty requirements if the next block
557
+ // is not at a difficulty retarget interval.
558
+ intervalSize := s .params .StakeDiffWindowSize
559
+ curDiff := s .tip .ticketPrice
560
+ if int64 (nextHeight )% intervalSize != 0 {
561
+ return curDiff
562
+ }
563
+
564
+ // Attempt to get the pool size from the previous retarget interval.
565
+ var prevPoolSize int64
566
+ prevRetargetHeight := nextHeight - int32 (intervalSize )
567
+ node := s .ancestorNode (s .tip , prevRetargetHeight , nil )
568
+ if node != nil {
569
+ prevPoolSize = int64 (node .poolSize )
570
+ }
571
+
572
+ // Get the immature ticket count from the previous interval.
573
+ var prevImmatureTickets int64
574
+ ticketMaturity := int64 (s .params .TicketMaturity )
575
+ s .ancestorNode (node , node .height - int32 (ticketMaturity ), func (n * blockNode ) {
576
+ prevImmatureTickets += int64 (len (n .ticketsAdded ))
577
+ })
578
+
579
+ // Return the existing ticket price for the first interval.
580
+ if prevPoolSize + prevImmatureTickets == 0 {
581
+ return curDiff
582
+ }
583
+
584
+ immatureTickets := int64 (len (s .immatureTickets ))
585
+ curPoolSize := int64 (s .tip .poolSize )
586
+ curPoolSizeAll := curPoolSize + immatureTickets
587
+ prevPoolSizeAll := prevPoolSize + prevImmatureTickets
588
+ poolSizeChangeRatio := float64 (curPoolSizeAll ) / float64 (prevPoolSizeAll )
589
+ ticketsPerBlock := int64 (s .params .TicketsPerBlock )
590
+ ticketPoolSize := int64 (s .params .TicketPoolSize )
591
+ targetPoolSizeAll := ticketsPerBlock * (ticketPoolSize + ticketMaturity ) // responsive, less expires
592
+ //targetPoolSizeAll := (ticketsPerBlock * ticketPoolSize) + immatureTickets // smooth
593
+ targetRatio := float64 (curPoolSizeAll ) / float64 (targetPoolSizeAll )
594
+
595
+ targetBalancer := 1.0
596
+ relativeBalancer := 1.0
597
+ if curPoolSizeAll < prevPoolSizeAll { // trending down
598
+ if targetRatio > 1.0 { // above target, right direction, continue
599
+ relativeBalancer = poolSizeChangeRatio // lower price
600
+ }
601
+ if targetRatio < 1.0 { // below target, wrong direction, reverse
602
+ targetBalancer = targetRatio // lower price
603
+ }
604
+ }
605
+ if curPoolSizeAll > prevPoolSizeAll { // trending up
606
+ if targetRatio < 1.0 { // below target, right direction, continue
607
+ relativeBalancer = poolSizeChangeRatio // raise price
608
+ }
609
+ if targetRatio > 1.0 { // above target, wrong direction, reverse
610
+ targetBalancer = targetRatio // raise price
611
+ }
612
+ }
613
+
614
+ // Voila!
615
+ nextDiff := float64 (curDiff ) * relativeBalancer * targetBalancer
616
+
617
+ // Upper bound on price. Insures the pool gets fully populated during price rises.
618
+ maximumStakeDiff := (float64 (s .tip .totalSupply ) / float64 (targetPoolSizeAll )) * targetRatio
619
+ if nextDiff > maximumStakeDiff {
620
+ nextDiff = maximumStakeDiff
621
+ }
622
+
623
+ // Lower bound on price. Prevents price bounce.
624
+ weightedIdealDiff := (float64 (s .tip .stakedCoins ) / float64 (targetPoolSizeAll )) * targetRatio
625
+ if nextDiff < weightedIdealDiff {
626
+ nextDiff = weightedIdealDiff
627
+ }
628
+
629
+ // Hard coded minimum value.
630
+ if int64 (nextDiff ) < s .params .MinimumStakeDiff {
631
+ return s .params .MinimumStakeDiff
632
+ }
633
+
634
+ return int64 (nextDiff )
635
+ }
636
+
637
+ // Algo 9 by raedah
638
+ // This algorithm has the interesting property that it does not base its
639
+ // calculations on the previous diff. Rather, it calculates anew each run.
640
+ // This creates the smoothest ticket price adjustments,
641
+ // with a trade off of a less stable ticket pool size. Expiries increase 1%.
642
+ // Perhaps the accuracy can still be solved.
643
+ func (s * simulator ) calcNextStakeDiffProposal9 () int64 {
644
+ // Stake difficulty before any tickets could possibly be purchased is
645
+ // the minimum value.
646
+ nextHeight := int32 (0 )
647
+ if s .tip != nil {
648
+ nextHeight = s .tip .height + 1
649
+ }
650
+ stakeDiffStartHeight := int32 (s .params .CoinbaseMaturity ) + 1
651
+ if nextHeight < stakeDiffStartHeight {
652
+ return s .params .MinimumStakeDiff
653
+ }
654
+
655
+ // Return the previous block's difficulty requirements if the next block
656
+ // is not at a difficulty retarget interval.
657
+ intervalSize := s .params .StakeDiffWindowSize
658
+ curDiff := s .tip .ticketPrice
659
+ if int64 (nextHeight )% intervalSize != 0 {
660
+ return curDiff
661
+ }
662
+
663
+ // Attempt to get the pool size from the previous retarget interval.
664
+ var prevPoolSize int64
665
+ prevRetargetHeight := nextHeight - int32 (intervalSize )
666
+ node := s .ancestorNode (s .tip , prevRetargetHeight , nil )
667
+ if node != nil {
668
+ prevPoolSize = int64 (node .poolSize )
669
+ }
670
+
671
+ // Get the immature ticket count from the previous interval.
672
+ var prevImmatureTickets int64
673
+ ticketMaturity := int64 (s .params .TicketMaturity )
674
+ s .ancestorNode (node , node .height - int32 (ticketMaturity ), func (n * blockNode ) {
675
+ prevImmatureTickets += int64 (len (n .ticketsAdded ))
676
+ })
677
+
678
+ // Return the existing ticket price for the first interval.
679
+ if prevPoolSize + prevImmatureTickets == 0 {
680
+ return curDiff
681
+ }
682
+
683
+ immatureTickets := int64 (len (s .immatureTickets ))
684
+ //curPoolSize := int64(s.tip.poolSize)
685
+ //curPoolSizeAll := curPoolSize + immatureTickets
686
+ //targetRatio := float64(curPoolSizeAll) / float64(targetPoolSizeAll)
687
+ ticketsPerBlock := int64 (s .params .TicketsPerBlock )
688
+ ticketPoolSize := int64 (s .params .TicketPoolSize )
689
+ targetPoolSizeAll := (ticketsPerBlock * ticketPoolSize ) + immatureTickets
690
+
691
+ // Voila!
692
+ idealPrice := float64 (s .tip .stakedCoins ) / float64 (targetPoolSizeAll )
693
+ nextDiff := idealPrice
694
+ //nextDiff := idealPrice * targetRatio //more responsive, less smooth
695
+
696
+ // Hard coded minimum value.
697
+ if int64 (nextDiff ) < s .params .MinimumStakeDiff {
698
+ return s .params .MinimumStakeDiff
699
+ }
700
+
701
+ return int64 (nextDiff )
702
+ }
0 commit comments