@@ -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"
@@ -257,11 +259,14 @@ func (d *Pegnetd) SyncBlock(ctx context.Context, tx *sql.Tx, height uint32) erro
257259 // Then, grade the new OPR Block. The results of this will be used
258260 // to execute conversions that are in holding.
259261 gradedBlock , err := d .Grade (ctx , oprEBlock )
260- gradedSPRBlock , err_s := d .Grade (ctx , sprEBlock )
261- if err != nil {
262- return err
263- } else if gradedBlock != nil {
264- if height < V20HeightActivation {
262+ gradedSPRBlock , err_s := d .GradeS (ctx , sprEBlock )
263+ isRatesAvailable := false
264+ if height < V20HeightActivation {
265+ if err != nil {
266+ return err
267+ }
268+ isRatesAvailable = gradedBlock != nil && 0 < len (gradedBlock .Winners ())
269+ if gradedBlock != nil {
265270 err = d .Pegnet .InsertGradeBlock (tx , oprEBlock , gradedBlock )
266271 if err != nil {
267272 return err
@@ -291,15 +296,47 @@ func (d *Pegnetd) SyncBlock(ctx context.Context, tx *sql.Tx, height uint32) erro
291296 fLog .WithFields (log.Fields {"section" : "grading" , "reason" : "no winners" }).Tracef ("block not graded" )
292297 }
293298 } else {
294- if err_s != nil {
295- return err_s
296- }
297- // Todo:
298- // 1. Determine the rate from 3 OPRs (OPR, SPR, DPR)
299- // 2. Insert rate to DB
299+ fLog .WithFields (log.Fields {"section" : "grading" , "reason" : "no graded block" }).Tracef ("block not graded" )
300300 }
301301 } else {
302- fLog .WithFields (log.Fields {"section" : "grading" , "reason" : "no graded block" }).Tracef ("block not graded" )
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
312+
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 )
334+ if err != nil {
335+ return err
336+ }
337+ } else {
338+ fLog .WithFields (log.Fields {"section" : "grading" , "reason" : "no winners from OPR & SPR" }).Tracef ("block not graded" )
339+ }
303340 }
304341
305342 // Only apply transactions if we crossed the activation
@@ -340,7 +377,7 @@ func (d *Pegnetd) SyncBlock(ctx context.Context, tx *sql.Tx, height uint32) erro
340377 // At this point, we start making updates to the database in a specific order:
341378 // TODO: ensure we rollback the tx when needed
342379 // 1) Apply transaction batches that are in holding (conversions are always applied here)
343- if gradedBlock != nil && 0 < len ( gradedBlock . Winners ()) {
380+ if isRatesAvailable {
344381 // Before conversions can be run, we have to adjust and discover the bank's value.
345382 // We also only sync the bank if the block is a pegnet block
346383 if err := d .SyncBank (ctx , tx , height ); err != nil {
@@ -382,7 +419,7 @@ func (d *Pegnetd) SyncBlock(ctx context.Context, tx *sql.Tx, height uint32) erro
382419 // 5) Apply effects of graded SPR Block (PEG rewards, if any)
383420 // These funds will be available for transactions and conversions executed in the next block
384421 if gradedSPRBlock != nil {
385- if err := d .ApplyGradedOPRBlock (tx , gradedSPRBlock , dblock .Timestamp ); err != nil {
422+ if err := d .ApplyGradedSPRBlock (tx , gradedSPRBlock , dblock .Timestamp ); err != nil {
386423 return err
387424 }
388425 }
@@ -1114,6 +1151,34 @@ func (d *Pegnetd) ApplyGradedOPRBlock(tx *sql.Tx, gradedBlock grader.GradedBlock
11141151 return nil
11151152}
11161153
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+
11171182func isDone (ctx context.Context ) bool {
11181183 select {
11191184 case <- ctx .Done ():
@@ -1122,3 +1187,40 @@ func isDone(ctx context.Context) bool {
11221187 return false
11231188 }
11241189}
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+ filteredRates = append (filteredRates , sprWinners [i ])
1220+ }
1221+ }
1222+ }
1223+ return filteredRates , nil
1224+ }
1225+ return nil , fmt .Errorf ("no winners" )
1226+ }
0 commit comments