@@ -11,6 +11,8 @@ import (
1111 "github.com/Factom-Asset-Tokens/factom"
1212 "github.com/pegnet/pegnet/modules/conversions"
1313 "github.com/pegnet/pegnet/modules/grader"
14+ "github.com/pegnet/pegnet/modules/graderStake"
15+ "github.com/pegnet/pegnet/modules/opr"
1416 "github.com/pegnet/pegnet/modules/transactionid"
1517 "github.com/pegnet/pegnetd/config"
1618 "github.com/pegnet/pegnetd/fat/fat2"
@@ -247,43 +249,94 @@ func (d *Pegnetd) SyncBlock(ctx context.Context, tx *sql.Tx, height uint32) erro
247249 return err
248250 }
249251 }
252+ sprEBlock := dblock .EBlock (SPRChain )
253+ if sprEBlock != nil {
254+ if err := multiFetch (sprEBlock , d .FactomClient ); err != nil {
255+ return err
256+ }
257+ }
250258
251259 // Then, grade the new OPR Block. The results of this will be used
252260 // to execute conversions that are in holding.
253261 gradedBlock , err := d .Grade (ctx , oprEBlock )
254- if err != nil {
255- return err
256- } else if gradedBlock != nil {
257- err = d .Pegnet .InsertGradeBlock (tx , oprEBlock , gradedBlock )
262+ gradedSPRBlock , err_s := d .GradeS (ctx , sprEBlock )
263+ isRatesAvailable := false
264+ if height < V20HeightActivation {
258265 if err != nil {
259266 return err
260267 }
261- winners := gradedBlock .Winners ()
262- if 0 < len (winners ) {
263- // PEG has 3 current pricing phases
264- // 1: Price is 0
265- // 2: Price is determined by equation
266- // 3: Price is determine by miners
267- var phase pegnet.PEGPricingPhase
268- if height < PEGPricingActivation {
269- phase = pegnet .PEGPriceIsZero
270- }
271- if height >= PEGPricingActivation {
272- phase = pegnet .PEGPriceIsEquation
268+ isRatesAvailable = gradedBlock != nil && 0 < len (gradedBlock .Winners ())
269+ if gradedBlock != nil {
270+ err = d .Pegnet .InsertGradeBlock (tx , oprEBlock , gradedBlock )
271+ if err != nil {
272+ return err
273273 }
274- if height >= PEGFreeFloatingPriceActivation {
275- phase = pegnet .PEGPriceIsFloating
274+ winners := gradedBlock .Winners ()
275+ if 0 < len (winners ) {
276+ // PEG has 3 current pricing phases
277+ // 1: Price is 0
278+ // 2: Price is determined by equation
279+ // 3: Price is determine by miners
280+ var phase pegnet.PEGPricingPhase
281+ if height < PEGPricingActivation {
282+ phase = pegnet .PEGPriceIsZero
283+ }
284+ if height >= PEGPricingActivation {
285+ phase = pegnet .PEGPriceIsEquation
286+ }
287+ if height >= PEGFreeFloatingPriceActivation {
288+ phase = pegnet .PEGPriceIsFloating
289+ }
290+
291+ err = d .Pegnet .InsertRates (tx , height , winners [0 ].OPR .GetOrderedAssetsUint (), phase )
292+ if err != nil {
293+ return err
294+ }
295+ } else {
296+ fLog .WithFields (log.Fields {"section" : "grading" , "reason" : "no winners" }).Tracef ("block not graded" )
276297 }
298+ } else {
299+ fLog .WithFields (log.Fields {"section" : "grading" , "reason" : "no graded block" }).Tracef ("block not graded" )
300+ }
301+ } else {
302+ if err != nil {
303+ return err
304+ }
305+ if err_s != nil {
306+ return err_s
307+ }
308+ // 1. Determine the rates from 2 OPRs (OPR, SPR)
309+ // 2. Insert rates to DB
310+ var oprWinners []opr.AssetUint
311+ var sprWinners []opr.AssetUint
277312
278- err = d .Pegnet .InsertRates (tx , height , winners [0 ].OPR .GetOrderedAssetsUint (), phase )
313+ if gradedBlock != nil {
314+ winnersOpr := gradedBlock .Winners ()
315+ if 0 < len (winnersOpr ) {
316+ oprWinners = winnersOpr [0 ].OPR .GetOrderedAssetsUint ()
317+ }
318+ }
319+ if gradedSPRBlock != nil {
320+ winnersSpr := gradedSPRBlock .Winners ()
321+ if 0 < len (winnersSpr ) {
322+ sprWinners = winnersSpr [0 ].SPR .GetOrderedAssetsUint ()
323+ }
324+ }
325+ if 0 < len (oprWinners ) || 0 < len (sprWinners ) {
326+ filteredRates , errRate := d .GetAssetRates (oprWinners , sprWinners )
327+ if errRate != nil {
328+ return err
329+ }
330+ isRatesAvailable = true
331+ var phase pegnet.PEGPricingPhase
332+ phase = pegnet .PEGPriceIsFloating
333+ err = d .Pegnet .InsertRates (tx , height , filteredRates , phase )
279334 if err != nil {
280335 return err
281336 }
282337 } else {
283- fLog .WithFields (log.Fields {"section" : "grading" , "reason" : "no winners" }).Tracef ("block not graded" )
338+ fLog .WithFields (log.Fields {"section" : "grading" , "reason" : "no winners from OPR & SPR " }).Tracef ("block not graded" )
284339 }
285- } else {
286- fLog .WithFields (log.Fields {"section" : "grading" , "reason" : "no graded block" }).Tracef ("block not graded" )
287340 }
288341
289342 // Only apply transactions if we crossed the activation
@@ -324,7 +377,7 @@ func (d *Pegnetd) SyncBlock(ctx context.Context, tx *sql.Tx, height uint32) erro
324377 // At this point, we start making updates to the database in a specific order:
325378 // TODO: ensure we rollback the tx when needed
326379 // 1) Apply transaction batches that are in holding (conversions are always applied here)
327- if gradedBlock != nil && 0 < len ( gradedBlock . Winners ()) {
380+ if isRatesAvailable {
328381 // Before conversions can be run, we have to adjust and discover the bank's value.
329382 // We also only sync the bank if the block is a pegnet block
330383 if err := d .SyncBank (ctx , tx , height ); err != nil {
@@ -362,7 +415,17 @@ func (d *Pegnetd) SyncBlock(ctx context.Context, tx *sql.Tx, height uint32) erro
362415 }
363416 }
364417
365- // 5) Apply Developers Rewards
418+ if height >= V20HeightActivation {
419+ // 5) Apply effects of graded SPR Block (PEG rewards, if any)
420+ // These funds will be available for transactions and conversions executed in the next block
421+ if gradedSPRBlock != nil {
422+ if err := d .ApplyGradedSPRBlock (tx , gradedSPRBlock , dblock .Timestamp ); err != nil {
423+ return err
424+ }
425+ }
426+ }
427+
428+ // 6) Apply Developers Rewards
366429 if height >= V20HeightActivation && height % pegnet .SnapshotRate == 0 {
367430 err := d .DevelopersPayouts (tx , fLog , height , dblock .Timestamp , DeveloperRewardAddreses )
368431 if err != nil {
@@ -1088,6 +1151,34 @@ func (d *Pegnetd) ApplyGradedOPRBlock(tx *sql.Tx, gradedBlock grader.GradedBlock
10881151 return nil
10891152}
10901153
1154+ // ApplyGradedOPRBlock pays out PEG to the winners of the given GradedBlock.
1155+ // If an error is returned, the sql.Tx should be rolled back by the caller.
1156+ func (d * Pegnetd ) ApplyGradedSPRBlock (tx * sql.Tx , gradedSPRBlock graderStake.GradedBlock , timestamp time.Time ) error {
1157+ winners := gradedSPRBlock .Winners ()
1158+ for i := range winners {
1159+ addr , err := factom .NewFAAddress (winners [i ].SPR .GetAddress ())
1160+ if err != nil {
1161+ // TODO: This is kinda an odd case. I think we should just drop the rewards
1162+ // for an invalid address. We can always add back the rewards and they will have
1163+ // a higher balance after a change.
1164+ log .WithError (err ).WithFields (log.Fields {
1165+ "height" : winners [i ].SPR .GetHeight (),
1166+ "ehash" : fmt .Sprintf ("%x" , winners [i ].EntryHash ),
1167+ }).Warnf ("failed to reward" )
1168+ continue
1169+ }
1170+
1171+ if _ , err := d .Pegnet .AddToBalance (tx , & addr , fat2 .PTickerPEG , uint64 (winners [i ].Payout ())); err != nil {
1172+ return err
1173+ }
1174+
1175+ if err := d .Pegnet .InsertStaking100Coinbase (tx , winners [i ], addr [:], timestamp ); err != nil {
1176+ return err
1177+ }
1178+ }
1179+ return nil
1180+ }
1181+
10911182func isDone (ctx context.Context ) bool {
10921183 select {
10931184 case <- ctx .Done ():
@@ -1096,3 +1187,40 @@ func isDone(ctx context.Context) bool {
10961187 return false
10971188 }
10981189}
1190+
1191+ func (d * Pegnetd ) GetAssetRates (oprWinners []opr.AssetUint , sprWinners []opr.AssetUint ) ([]opr.AssetUint , error ) {
1192+ if 0 < len (oprWinners ) && 0 == len (sprWinners ) {
1193+ return oprWinners , nil
1194+ }
1195+ if 0 == len (oprWinners ) && 0 < len (sprWinners ) {
1196+ return sprWinners , nil
1197+ }
1198+ if 0 < len (oprWinners ) && 0 < len (sprWinners ) {
1199+ // 1) sprWinners determine tolerance range
1200+ // 2) oprWinners set the real rates
1201+ if len (oprWinners ) != len (sprWinners ) {
1202+ return nil , fmt .Errorf ("SPR & OPR use different assets version" )
1203+ }
1204+
1205+ var filteredRates []opr.AssetUint
1206+ for i := range oprWinners {
1207+ if oprWinners [i ].Name == sprWinners [i ].Name {
1208+ sprRate := sprWinners [i ].Value
1209+ oprRate := oprWinners [i ].Value
1210+ toleranceRate := 0.01 // 1% band
1211+ if sprRate >= 100000 {
1212+ toleranceRate = 0.001 // 0.1% band
1213+ }
1214+ toleranceBandHigh := float64 (sprRate ) * (1 + toleranceRate )
1215+ toleranceBandLow := float64 (sprRate ) * (1 - toleranceRate )
1216+ if (float64 (oprRate ) >= toleranceBandLow ) && (float64 (oprRate ) <= toleranceBandHigh ) {
1217+ filteredRates = append (filteredRates , oprWinners [i ])
1218+ } else {
1219+ return nil , fmt .Errorf ("opr is out side of tolerance band" )
1220+ }
1221+ }
1222+ }
1223+ return filteredRates , nil
1224+ }
1225+ return nil , fmt .Errorf ("no winners" )
1226+ }
0 commit comments