github.com/igggame/nebulas-go@v2.1.0+incompatible/nip/dip/dip.go (about) 1 // Copyright (C) 2018 go-nebulas authors 2 // 3 // This file is part of the go-nebulas library. 4 // 5 // the go-nebulas library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // the go-nebulas library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU General Public License for more details. 14 // 15 // You should have received a copy of the GNU General Public License 16 // along with the go-nebulas library. If not, see <http://www.gnu.org/licenses/>. 17 // 18 19 package dip 20 21 import ( 22 "time" 23 24 "github.com/hashicorp/golang-lru" 25 "github.com/nebulasio/go-nebulas/core" 26 "github.com/nebulasio/go-nebulas/util" 27 "github.com/nebulasio/go-nebulas/util/byteutils" 28 "github.com/nebulasio/go-nebulas/util/logging" 29 "github.com/sirupsen/logrus" 30 ) 31 32 type Dip struct { 33 neb Neblet 34 35 cache *lru.Cache 36 37 // reward rewardAddress 38 rewardAddress *core.Address 39 rewardValue *util.Uint128 40 41 isLooping bool 42 quitCh chan int 43 } 44 45 // NewDIP create a dip 46 func NewDIP(neb Neblet) (*Dip, error) { 47 cache, err := lru.New(CacheSize) 48 if err != nil { 49 return nil, err 50 } 51 52 // dip reward address. 53 priv, err := byteutils.FromHex(DipRewardAddressPrivate) 54 if err != nil { 55 return nil, err 56 } 57 addr, err := neb.AccountManager().LoadPrivate(priv, []byte(DipRewardAddressPassphrase)) 58 if err != nil { 59 return nil, err 60 } 61 62 dip := &Dip{ 63 neb: neb, 64 cache: cache, 65 quitCh: make(chan int, 1), 66 isLooping: false, 67 rewardAddress: addr, 68 rewardValue: core.DIPReward, 69 } 70 return dip, nil 71 } 72 73 // RewardAddress return dip reward rewardAddress. 74 func (d *Dip) RewardAddress() *core.Address { 75 return d.rewardAddress 76 } 77 78 func (d *Dip) RewardValue() *util.Uint128 { 79 return d.rewardValue 80 } 81 82 // Start start dip. 83 func (d *Dip) Start() { 84 if !d.isLooping { 85 d.isLooping = true 86 87 logging.CLog().WithFields(logrus.Fields{}).Info("Starting Dip...") 88 89 go d.loop() 90 } 91 } 92 93 // Stop stop dip. 94 func (d *Dip) Stop() { 95 if d.isLooping { 96 d.isLooping = false 97 logging.CLog().WithFields(logrus.Fields{}).Info("Stopping Dip...") 98 99 d.quitCh <- 1 100 } 101 } 102 103 func (d *Dip) loop() { 104 logging.CLog().Info("Started Dip.") 105 106 timerChan := time.NewTicker(time.Second * 15).C 107 for { 108 select { 109 case <-d.quitCh: 110 logging.CLog().Info("Stopped Dip.") 111 return 112 case <-timerChan: 113 if d.isLooping { 114 d.submitReward() 115 } 116 } 117 } 118 } 119 120 func (d *Dip) DipDelayRewardHeight() uint64 { 121 chainID := d.neb.BlockChain().ChainID() 122 // for Mainnet and Testnet, delay 0.5 day to submit dip reward. 123 if chainID == core.MainNetID { 124 return 12 * 60 * 60 / 15 125 } else if chainID == core.TestNetID { 126 return 12 * 60 * 60 / 15 127 } else { 128 return 33 129 } 130 } 131 132 // submitReward generate dip transactions and push to tx pool 133 func (d *Dip) submitReward() { 134 height := d.neb.BlockChain().TailBlock().Height() - d.DipDelayRewardHeight() 135 if height < 1 { 136 return 137 } 138 logging.VLog().WithFields(logrus.Fields{ 139 "height": height, 140 }).Debug("loop to query dip for submit.") 141 data, err := d.GetDipList(height, 0) 142 if err != nil { 143 logging.VLog().WithFields(logrus.Fields{ 144 "err": err, 145 }).Warn("Failed to get dip reward list.") 146 return 147 } 148 149 dipData := data.(*DIPData) 150 endBlock := d.neb.BlockChain().GetBlockOnCanonicalChainByHeight(dipData.EndHeight) 151 endAccount, err := endBlock.GetAccount(d.RewardAddress().Bytes()) 152 if err != nil { 153 logging.VLog().WithFields(logrus.Fields{ 154 "err": err, 155 }).Error("Failed to load reward account in dip end account.") 156 return 157 } 158 159 tailAccount, err := d.neb.BlockChain().TailBlock().GetAccount(d.RewardAddress().Bytes()) 160 if err != nil { 161 logging.VLog().WithFields(logrus.Fields{ 162 "err": err, 163 }).Error("Failed to load reward account in tail block.") 164 return 165 } 166 167 for idx, v := range dipData.Dips { 168 nonce := endAccount.Nonce() + uint64(idx) + 1 169 // only not reward tx can be pushed to tx pool. 170 if nonce > tailAccount.Nonce() { 171 tx, err := d.generateRewardTx(dipData.StartHeight, dipData.EndHeight, dipData.Version, v, nonce, endBlock) 172 if err != nil { 173 logging.VLog().WithFields(logrus.Fields{ 174 "err": err, 175 }).Error("Failed to generate reward transaction.") 176 return 177 } 178 179 logging.VLog().WithFields(logrus.Fields{ 180 "height": d.neb.BlockChain().TailBlock().Height(), 181 "start": dipData.StartHeight, 182 "end": dipData.EndHeight, 183 "tx": tx, 184 }).Info("Success to push dip reward tx.") 185 186 d.neb.BlockChain().TransactionPool().Push(tx) 187 } 188 } 189 } 190 191 func (d *Dip) generateRewardTx(start uint64, end uint64, version uint64, item *DIPItem, nonce uint64, block *core.Block) (*core.Transaction, error) { 192 var ( 193 to *core.Address 194 value *util.Uint128 195 payload *core.DipPayload 196 payloadBytes []byte 197 gasLimit *util.Uint128 198 err error 199 ) 200 if to, err = core.AddressParse(item.Address); err != nil { 201 return nil, err 202 } 203 if value, err = util.NewUint128FromString(item.Reward); err != nil { 204 return nil, err 205 } 206 if payload, err = core.NewDipPayload(start, end, version, item.Contract); err != nil { 207 return nil, err 208 } 209 if payloadBytes, err = payload.ToBytes(); err != nil { 210 return nil, err 211 } 212 213 if gasLimit, err = core.MinGasCountPerTransaction.Mul(util.NewUint128FromUint(10)); err != nil { 214 return nil, err 215 } 216 217 tx, err := core.NewTransaction( 218 block.ChainID(), 219 d.RewardAddress(), 220 to, 221 value, 222 nonce, 223 core.TxPayloadDipType, 224 payloadBytes, 225 d.neb.BlockChain().TransactionPool().GetMinGasPrice(), 226 gasLimit) 227 if err != nil { 228 return nil, err 229 } 230 // update all reward timestamp to last calculated block timestamp, generate hash equal every loop. 231 tx.SetTimestamp(block.Timestamp()) 232 if err = d.neb.AccountManager().SignTransactionWithPassphrase(d.RewardAddress(), tx, []byte(DipRewardAddressPassphrase)); err != nil { 233 return nil, err 234 } 235 return tx, nil 236 } 237 238 // GetDipList returns dip info list 239 func (d *Dip) GetDipList(height, version uint64) (core.Data, error) { 240 data, err := d.checkCache(height) 241 if err != nil { 242 return nil, err 243 } 244 return data, nil 245 } 246 247 func (d *Dip) checkCache(height uint64) (*DIPData, error) { 248 if d.cache.Len() == 0 { 249 d.loadCache() 250 } 251 252 key := (height-core.NrStartHeight)/core.NrIntervalHeight - 1 253 if data, ok := d.cache.Get(key); ok { 254 dipData := data.(*DIPData) 255 logging.VLog().WithFields(logrus.Fields{ 256 "height": height, 257 "start": dipData.StartHeight, 258 "end": dipData.EndHeight, 259 "dataSize": len(dipData.Dips), 260 }).Debug("Success to find dip list in cache.") 261 return dipData, nil 262 } 263 return nil, ErrDipNotFound 264 } 265 266 func (d *Dip) CheckReward(tx *core.Transaction) error { 267 //if tx type is not dip, can't use the reward address send tx. 268 if tx.Type() != core.TxPayloadDipType && tx.From().Equals(d.rewardAddress) { 269 return ErrUnsupportedTransactionFromDipAddress 270 } 271 272 if tx.Type() == core.TxPayloadDipType { 273 if !tx.From().Equals(d.rewardAddress) { 274 return ErrInvalidDipAddress 275 } 276 277 if tx.To().Equals(core.DIPRewardAddress2) { 278 return nil 279 } 280 281 payload, err := tx.LoadPayload() 282 if err != nil { 283 return err 284 } 285 286 dipPayload := payload.(*core.DipPayload) 287 height := dipPayload.EndHeight + 1 288 data, err := d.GetDipList(height, dipPayload.Version) 289 if err != nil { 290 return err 291 } 292 dip := data.(*DIPData) 293 for _, v := range dip.Dips { 294 if tx.To().String() == v.Address && tx.Value().String() == v.Reward { 295 logging.VLog().WithFields(logrus.Fields{ 296 "height": height, 297 "start": dip.StartHeight, 298 "end": dip.EndHeight, 299 "tx": tx, 300 }).Debug("Success to check dip reward tx.") 301 return nil 302 } 303 } 304 return ErrDipNotFound 305 } 306 return nil 307 }