github.com/deroproject/derosuite@v2.1.6-1.0.20200307070847-0f2e589c7a2b+incompatible/blockchain/hardfork_core.go (about)

     1  // Copyright 2017-2018 DERO Project. All rights reserved.
     2  // Use of this source code in any form is governed by RESEARCH license.
     3  // license can be found in the LICENSE file.
     4  // GPG: 0F39 E425 8C65 3947 702A  8234 08B2 0360 A03A 9DE8
     5  //
     6  //
     7  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
     8  // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     9  // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
    10  // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    11  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
    12  // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    13  // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
    14  // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
    15  // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    16  
    17  package blockchain
    18  
    19  import "github.com/deroproject/derosuite/block"
    20  import "github.com/deroproject/derosuite/config"
    21  import "github.com/deroproject/derosuite/storage"
    22  import "github.com/deroproject/derosuite/globals"
    23  
    24  // the voting for hard fork works as follows
    25  // block major version remains contant, while block minor version contains the next hard fork number,
    26  // at trigger height, the last window_size blocks are counted as folllows
    27  // if Major_Version == minor version, it is a negative vote
    28  // if minor_version > major_version, it is positive vote
    29  // if threshold votes are positive, the next hard fork triggers
    30  
    31  // this is work in progress
    32  // hard forking is integrated deep within the the blockchain as almost anything can be replaced in DERO without disruption
    33  const default_voting_window_size = 6000 // this many votes will counted
    34  const default_vote_percent = 62         // 62 percent votes means the hard fork is locked in
    35  
    36  type Hard_fork struct {
    37  	Version     int64 // which version will trigger
    38  	Height      int64 // at what height hard fork will come into effect, trigger block
    39  	Window_size int64 // how many votes to count (x number of votes)
    40  	Threshold   int64 //  between 0 and 99  // percent number of votes required to lock in  hardfork, 0 = mandatory
    41  	Votes       int64 // number of votes in favor
    42  	Voted       bool  // whether voting resulted in hardfork
    43  }
    44  
    45  // current mainnet_hard_forks
    46  var mainnet_hard_forks = []Hard_fork{
    47  	// {0, 0,0,0,0,true}, // dummy entry so as we can directly use the fork index into this entry
    48  	{1,      0, 0, 0, 0, true}, // version 1 hard fork where genesis block landed and chain migration occurs
    49  	// version 1 has difficulty hardcoded to 1
    50  	{2, 95551,  0, 0, 0, true}, // version 2 hard fork where Atlantis bootstraps , it's mandatory
    51          {3, 721000, 0, 0, 0, true}, // version 3 hard fork emission fix, it's mandatory
    52          {4, 4550555, 0, 0, 0, true}, // version 4 hard fork AstroBWT CPU Mining enabled. It's mandatory
    53  }
    54  
    55  // current testnet_hard_forks
    56  var testnet_hard_forks = []Hard_fork{
    57  	//{1, 0, 0, 0, 0, true},    // version 1 hard fork where genesis block landed
    58  	{3, 0, 0, 0, 0, true}, // version 3 hard fork where we started , it's mandatory
    59    	{4, 3, 0, 0, 0, true}, // version 4 hard fork where we change mining algorithm it's mandatory
    60  }
    61  
    62  // current simulation_hard_forks
    63  // these can be tampered with for testing and other purposes
    64  // this variable is exported so as simulation can play/test hard fork code
    65  var Simulation_hard_forks = []Hard_fork{
    66  	{1, 0, 0, 0, 0, true}, // version 1 hard fork where genesis block landed
    67  	{2, 1, 0, 0, 0, true}, // version 2 hard fork where we started , it's mandatory
    68  }
    69  
    70  // at init time, suitable versions are selected
    71  var current_hard_forks []Hard_fork
    72  
    73  // init suitable structure based on mainnet/testnet selection at runtime
    74  func init_hard_forks(params map[string]interface{}) {
    75  
    76  	// if simulation , load simulation features
    77  	if params["--simulator"] == true {
    78  		current_hard_forks = Simulation_hard_forks // enable simulator mode hard forks
    79  		logger.Debugf("simulator hardforks are online")
    80  	} else {
    81  		if globals.IsMainnet() {
    82  			current_hard_forks = mainnet_hard_forks
    83  			logger.Debugf("mainnet hardforks are online")
    84  		} else {
    85  			current_hard_forks = testnet_hard_forks
    86  			logger.Debugf("testnet hardforks are online")
    87  		}
    88  
    89  	}
    90  
    91  	// if voting in progress, load all votes from db, since we do not store votes in disk,
    92  	// we will load all necessary blocks, counting votes
    93  }
    94  
    95  // check block version validity at specific height according to current network
    96  func (chain *Blockchain) Check_Block_Version(dbtx storage.DBTX, bl *block.Block) (result bool) {
    97  
    98  	height := chain.Calculate_Height_At_Tips(dbtx, bl.Tips)
    99  
   100  	if height == 0 && bl.Major_Version == 1 { // handle genesis block as exception
   101  		return true
   102  	}
   103  	// all blocks except genesis block land here
   104  	if bl.Major_Version == uint64(chain.Get_Current_Version_at_Height(height)) {
   105  		return true
   106  	}
   107  
   108  	return
   109  }
   110  
   111  // this func will recount votes, set whether the version is voted in
   112  // only the main chain blocks are counted in
   113  // this func must be called with chain in lock state
   114  /*
   115  func (chain *Blockchain) Recount_Votes() {
   116  	height := chain.Load_Height_for_BL_ID(chain.Get_Top_ID())
   117  
   118  	for i := len(current_hard_forks) - 1; i > 0; i-- {
   119  		// count votes only if voting is in progress
   120  		if 0 != current_hard_forks[i].Window_size && // if window_size > 0
   121  			height <= current_hard_forks[i].Height &&
   122  			height >= (current_hard_forks[i].Height-current_hard_forks[i].Window_size) { // start voting when required
   123  
   124  			hard_fork_locked := false
   125  			current_hard_forks[i].Votes = 0 // make votes zero, before counting
   126  			for j := height; j >= (current_hard_forks[i].Height - current_hard_forks[i].Window_size); j-- {
   127  				// load each block, and count the votes
   128  
   129  				hash, err := chain.Load_BL_ID_at_Height(j)
   130  				if err == nil {
   131  					bl, err := chain.Load_BL_FROM_ID(hash)
   132  					if err == nil {
   133  						if bl.Minor_Version == uint64(current_hard_forks[i].Version) {
   134  							current_hard_forks[i].Votes++
   135  						}
   136  					} else {
   137  						logger.Warnf("err loading block (%s) at height %d,  chain height %d err %s", hash, j, height, err)
   138  					}
   139  
   140  				} else {
   141  					logger.Warnf("err loading block at height %d,  chain height %d err %s", j, height, err)
   142  				}
   143  
   144  			}
   145  
   146  			// if necessary votes have been accumulated , lock in the hard fork
   147  			if ((current_hard_forks[i].Votes * 100) / current_hard_forks[i].Window_size) >= current_hard_forks[i].Threshold {
   148  				hard_fork_locked = true
   149  			}
   150  			current_hard_forks[i].Voted = hard_fork_locked // keep it as per status
   151  		}
   152  
   153  	}
   154  
   155  }
   156  */
   157  // this function returns  number of information whether hf is going on scheduled, everything is okay etc
   158  func (chain *Blockchain) Get_HF_info() (state int, enabled bool, earliest_height, threshold, version, votes, window int64) {
   159  
   160  	state = 2 // default is everything is okay
   161  	enabled = true
   162  
   163  	topoheight := chain.Load_TOPO_HEIGHT(nil)
   164  	block_id, err := chain.Load_Block_Topological_order_at_index(nil, topoheight)
   165  	if err != nil {
   166  		return
   167  	}
   168  
   169  	bl, err := chain.Load_BL_FROM_ID(nil, block_id)
   170  	if err != nil {
   171  		logger.Warnf("err loading block (%s) at topo height %d err %s", block_id, topoheight, err)
   172  	}
   173  
   174  	height := chain.Load_Height_for_BL_ID(nil, block_id)
   175  
   176  	version = chain.Get_Current_Version_at_Height(height)
   177  
   178  	// check top block to see if the network is going through a hard fork
   179  	if bl.Major_Version != bl.Minor_Version { // network is going through voting
   180  		state = 0
   181  		enabled = false
   182  	}
   183  
   184  	if bl.Minor_Version != uint64(chain.Get_Ideal_Version_at_Height(height)) {
   185  		// we are NOT voting for the hard fork ( or we are already broken), issue warning to user, that we need an upgrade NOW
   186  		state = 1
   187  		enabled = false
   188  		version = int64(bl.Minor_Version)
   189  	}
   190  	if state == 0 { // we know our state is good, report back, good info
   191  		for i := range current_hard_forks {
   192  			if version == current_hard_forks[i].Version {
   193  				earliest_height = current_hard_forks[i].Height
   194  				threshold = current_hard_forks[i].Threshold
   195  				version = current_hard_forks[i].Version
   196  				votes = current_hard_forks[i].Votes
   197  				window = current_hard_forks[i].Window_size
   198  			}
   199  		}
   200  	}
   201  
   202  	return
   203  }
   204  
   205  // current hard fork version , block major version
   206  // we may be at genesis block height
   207  func (chain *Blockchain) Get_Current_Version() int64 { // it is last version voted or mandatory update
   208  	return chain.Get_Current_Version_at_Height(chain.Get_Height())
   209  }
   210  
   211  func (chain *Blockchain) Get_Current_BlockTime() uint64 { // it is last version voted or mandatory update
   212     block_time:= config.BLOCK_TIME
   213     if chain.Get_Current_Version() >= 4 {
   214          block_time= config.BLOCK_TIME_hf4
   215      }
   216  	return block_time
   217  }
   218  
   219  
   220  func (chain *Blockchain) Get_Current_Version_at_Height(height int64) int64 {
   221  	for i := len(current_hard_forks) - 1; i >= 0; i-- {
   222  		//logger.Infof("i %d height %d  hf height %d",i, height,current_hard_forks[i].Height )
   223  		if height >= current_hard_forks[i].Height {
   224  
   225  			// if it was a mandatory fork handle it directly
   226  			if current_hard_forks[i].Threshold == 0 {
   227  				return current_hard_forks[i].Version
   228  			}
   229  
   230  			if current_hard_forks[i].Voted { // if the version was voted in, select it, other wise try lower
   231  				return current_hard_forks[i].Version
   232  			}
   233  		}
   234  	}
   235  	return 0
   236  }
   237  
   238  // if we are voting, return the next expected version
   239  func (chain *Blockchain) Get_Ideal_Version() int64 {
   240  	return chain.Get_Ideal_Version_at_Height(chain.Get_Height())
   241  }
   242  
   243  // used to cast vote
   244  func (chain *Blockchain) Get_Ideal_Version_at_Height(height int64) int64 {
   245  	for i := len(current_hard_forks) - 1; i > 0; i-- {
   246  		// only voted during the period required
   247  		if height <= current_hard_forks[i].Height &&
   248  			height >= (current_hard_forks[i].Height-current_hard_forks[i].Window_size) { // start voting when required
   249  			return current_hard_forks[i].Version
   250  		}
   251  	}
   252  
   253  	// if we are not voting, return current version
   254  	return chain.Get_Current_Version_at_Height(height)
   255  }
   256  
   257  /*
   258  
   259  //  if the block major version is more than what we have in our index, display warning to user
   260  func (chain *Blockchain) Display_Warning_If_Blocks_are_New(bl *block.Block)  {
   261          // check the biggest fork
   262          if current_hard_forks[len(current_hard_forks )-1].version < bl.Major_Version {
   263              logger.Warnf("We have seen new blocks floating with version number bigger than ours, please update the software")
   264          }
   265   return
   266  }
   267  */