diff --git a/node/devs.go b/node/devs.go new file mode 100644 index 0000000..0fc674e --- /dev/null +++ b/node/devs.go @@ -0,0 +1,35 @@ +package node + +// Special structure for the Developer rewards +type DevReward struct { + DevGroup string // group or information about reward + DevAddress string // FCT address where to send developer reward + DevRewardPct float64 // % from total development rewards +} + +// Hardcode developers who work active on PegNet to avoid manipulations from config files +// +// contains the percentage distribution of the total 2000 +// defined in `pegnet` repo `modules/conversions/conversionlimit.go` as PerBlockDevelopers +// +// TODO: in v2.5 move into special Developer Rewards Chain (DRC) +var ( + DeveloperRewardAddreses = []DevReward{ + {"Listing Tech Support", "FA2i9WZqJnaKbJxDY2AZdVgewE28uCcSwoFt8LJCMtGCC7tpCa2n", 10.00}, + {"Architecture Dev for PegNet 2.5", "FA37cGXKWMtf2MmHy3n1rMCYeLVuR5MpDaP4VXVeFavjJCJLYYez", 9.0}, + {"Trading Bots Work", "FA2wDRieaBrWeZHVuXXWUHY6t9nKCVCCKAMS5xknLUExuVAq3ziS", 9.0}, + {"Mining Pool Support / Dev/ Infra Hosting/ Gateway Operation", "FA3LDEA5fcskV6ZoFpKE84qPcjd7GYjEnswGHMZXL1V9d14wmgh3", 9.0}, + {"Media Tech Support", "FA381EygeEXjZzB6hNvxbE4oSUzHZMfvGByMZoW5UrG1gHEKJcNK", 8.0}, + {"Social Coverage User Support", "FA2DxkaTx1k2oGfbTqvwVMScSHHac7JFRiBjRngjRnqQpeBxsLhA", 8.0}, + {"pTrader + PIPs", "FA2Ersb227gn7eWJ2HPsHZ5QqxfMBZhSjwixQ44dAS17CtRXSDRU", 8.0}, + {"Desktop Wallet + PIPs", " FA2eFEVUzTQZxNp3LYYgjPaaHUfGmuvShhtBdGB2BBWMeByPCmJy", 8.0}, + {"DeFi Integrations + PIPs Work", "FA2z6Nnaj8a5nXmwZD8tYhDENAx8ciVE3xeNeixKiFj22vZEZEdT", 8.0}, + {"Explorer + Mobile", " FA2T72oxBxXvnujNdsVUshqFM2qV1W4nJy33nkrpxbYQV8rFbUPP", 5.0}, + {"Prosper / Staking GUI Upgrades", "FA2cEaq1GdGfFjhymiTEzW24DocZFZHNBqe9qkT18YPaL5ZzsgRi", 5.0}, + {"Payment Intergrations Work", "FA2YhZBZbc4V858ao7dJuAqRC4iwA3MrbZs7BHUPK7Mq19yYdMwZ", 3.0}, + {"General Development Tasks", "FA3PYuvrsDvkhnekokVNrgLn7JiL5pChSBTtR9gZB1mVGFVB7JRD", 3.0}, + {"Gateway Ethereum Upgrade", "FA2Wy7AzeoBuaXYnGu67xa5zdNkmqTbPryUgpy7qVPvj46GRZkep", 2.0}, + {"Statistics & Visualizations of PegNet", "FA3dsCiKGzwrTALfX4T2CKv8wCmNMxwJx3jS4jz1ST9fwge9Wrnm", 2.0}, + {"Trading Tech Support", "FA2a2nXgkBg7pL5wrgm99rLZDGFs2T8jfTgMuia6ep8ZMkVtPe8E", 3.00}, + } +) diff --git a/node/pegnet/txhistory.go b/node/pegnet/txhistory.go index a739ab9..5d6182a 100644 --- a/node/pegnet/txhistory.go +++ b/node/pegnet/txhistory.go @@ -451,6 +451,65 @@ func (p *Pegnet) InsertStakingCoinbase(tx *sql.Tx, txid string, height uint32, h return nil } +func (p *Pegnet) InsertDeveloperRewardCoinbase(tx *sql.Tx, txid string, addTxid string, height uint32, heightTimestamp time.Time, payout uint64, faAdd factom.FAAddress) error { + txidBytes, err := hex.DecodeString(txid) + if err != nil { + return err + } + + // First we need to record the batch. The batch is the entire set of transactions, where + // each tx is a deverloper reward. + stmt, err := tx.Prepare(`INSERT INTO "pn_history_txbatch" + (entry_hash, height, blockorder, timestamp, executed) VALUES + (?, ?, ?, ?, ?)`) + if err != nil { + return err + } + + // The Entryhash is the custom txid, it is not an actual entry on chain + // The executed height is the same height as the recorded. + _, err = stmt.Exec(txidBytes, height, 0, heightTimestamp.Unix(), height) + if err != nil { + return err + } + + // Now we record each developer reward. + + // All addresses are stored as bytes in the sqlitedb + add := faAdd[:] + // index for the address + index, _, err := SplitTxID(addTxid) + if err != nil { + return err + } + + // Insert each payout as a coinbase. + // Insert the TX + coinbaseStatement, err := tx.Prepare(`INSERT INTO "pn_history_transaction" + (entry_hash, tx_index, action_type, from_address, from_asset, from_amount, to_asset, to_amount, outputs) VALUES + (?, ?, ?, ?, ?, ?, ?, ?, ?)`) + if err != nil { + return err + } + + _, err = coinbaseStatement.Exec(txidBytes, index, Coinbase, add, "", 0, "PEG", payout, "") + if err != nil { + return err + } + + // Insert into lookup table + lookup, err := tx.Prepare(insertLookupQuery) + if err != nil { + return err + } + + if _, err = lookup.Exec(txidBytes, index, add); err != nil { + return err + } + + return nil +} + // Special construction to nullify burn address func (p *Pegnet) InsertZeroingCoinbase(tx *sql.Tx, txid string, addTxid string, height uint32, heightTimestamp time.Time, payout uint64, asset string, faAdd factom.FAAddress) error { txidBytes, err := hex.DecodeString(txid) diff --git a/node/sync.go b/node/sync.go index f72f614..a6aef03 100644 --- a/node/sync.go +++ b/node/sync.go @@ -362,6 +362,15 @@ func (d *Pegnetd) SyncBlock(ctx context.Context, tx *sql.Tx, height uint32) erro } } + // 5) Apply Developers Rewards + if height >= V20HeightActivation && height%pegnet.SnapshotRate == 0 { + err := d.DevelopersPayouts(tx, fLog, height, dblock.Timestamp, DeveloperRewardAddreses) + if err != nil { + // something wrong happend during payout execution + return err + } + } + return nil } @@ -484,6 +493,67 @@ func (d *Pegnetd) SnapshotPayouts(tx *sql.Tx, fLog *log.Entry, rates map[fat2.PT return nil } +// Developers Reward Payouts +// implementation of PIP16 - distributed rewards collected for developers every 24h +func (d *Pegnetd) DevelopersPayouts(tx *sql.Tx, fLog *log.Entry, height uint32, heightTimestamp time.Time, developers []DevReward) error { + + totalPayout := uint64(conversions.PerBlockDevelopers) * pegnet.SnapshotRate // once a day + payoutStart := time.Now() + + // We need to mock a TXID to record dev rewards + txid := fmt.Sprintf("%064d", height) + + // we use hardcoded list of dev payouts + i := 0 + for _, dev := range developers { + + // we calculate developers reward from % pre-defined + rewardPayout := uint64((conversions.PerBlockDevelopers / 100) * dev.DevRewardPct) + addr, err := factom.NewFAAddress(dev.DevAddress) + + _, err = d.Pegnet.AddToBalance(tx, &addr, fat2.PTickerPEG, rewardPayout) + if err != nil { + return err + } + + // Mock entry hash value + addTxid := fmt.Sprintf("%d-%s", i, txid) + i++ + + // Get dev address as FAAdress + FADevAddress, err := factom.NewFAAddress(dev.DevAddress) + if err != nil { + log.WithFields(log.Fields{ + "error": err, + "addr": dev.DevAddress, + }).Info("error getting developer address") + return err + } + + // ---- Database Payouts ---- + // Inserts tx into the db + err = d.Pegnet.InsertDeveloperRewardCoinbase(tx, txid, addTxid, height, heightTimestamp, rewardPayout, FADevAddress) + if err != nil { + return err + } + + fLog.WithFields(log.Fields{ + "total": float64(totalPayout) / 1e8, + "developer": len(dev.DevAddress), + "PEG": float64(rewardPayout) / 1e8, // Float is good enough here + }).Info("developer reward | paid out to") + + } + + fLog.WithFields(log.Fields{ + "total": float64(totalPayout) / 1e8, + "elapsed": time.Since(payoutStart), + "txid": txid, + }).Info("developer rewards | paid out") + + return nil +} + func multiFetch(eblock *factom.EBlock, c *factom.Client) error { err := eblock.Get(nil, c) if err != nil {