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  }