derohe-miniblock-mod/blockchain/hardfork_core.go
2021-11-08 16:39:17 +00:00

265 lines
10 KiB
Go

// Copyright 2017-2021 DERO Project. All rights reserved.
// Use of this source code in any form is governed by RESEARCH license.
// license can be found in the LICENSE file.
// GPG: 0F39 E425 8C65 3947 702A 8234 08B2 0360 A03A 9DE8
//
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package blockchain
import "github.com/deroproject/derohe/block"
import "github.com/deroproject/derohe/config"
import "github.com/deroproject/derohe/globals"
// the voting for hard fork works as follows
// block major version remains contant, while block minor version contains the next hard fork number,
// at trigger height, the last window_size blocks are counted as folllows
// if Major_Version == minor version, it is a negative vote
// if minor_version > major_version, it is positive vote
// if threshold votes are positive, the next hard fork triggers
// this is work in progress
// hard forking is integrated deep within the the blockchain as almost anything can be replaced in DERO without disruption
const default_voting_window_size = 6000 // this many votes will counted
const default_vote_percent = 62 // 62 percent votes means the hard fork is locked in
type Hard_fork struct {
Version int64 // which version will trigger
Height int64 // at what height hard fork will come into effect, trigger block
Window_size int64 // how many votes to count (x number of votes)
Threshold int64 // between 0 and 99 // percent number of votes required to lock in hardfork, 0 = mandatory
Votes int64 // number of votes in favor
Voted bool // whether voting resulted in hardfork
}
// current mainnet_hard_forks
var mainnet_hard_forks = []Hard_fork{
// {1, 0,0,0,0,true}, // dummy entry so as we can directly use the fork index into this entry
{1, 0, 0, 0, 0, true}, // version 1 hard fork where genesis block landed and chain migration occurs
// version 1 has difficulty hardcoded to 1
//{2, 95551, 0, 0, 0, true}, // version 2 hard fork where Atlantis bootstraps , it's mandatory
// {3, 721000, 0, 0, 0, true}, // version 3 hard fork emission fix, it's mandatory
}
// current testnet_hard_forks
var testnet_hard_forks = []Hard_fork{
{1, 0, 0, 0, 0, true}, // version 1 hard fork where genesis block landed
//{3, 0, 0, 0, 0, true}, // version 3 hard fork where we started , it's mandatory
//{4, 3, 0, 0, 0, true}, // version 4 hard fork where we change mining algorithm it's mandatory
}
// current simulation_hard_forks
// these can be tampered with for testing and other purposes
// this variable is exported so as simulation can play/test hard fork code
var Simulation_hard_forks = []Hard_fork{
{1, 0, 0, 0, 0, true}, // version 1 hard fork where genesis block landed
{2, 1, 0, 0, 0, true}, // version 2 hard fork where we started , it's mandatory
}
// at init time, suitable versions are selected
var current_hard_forks []Hard_fork
// init suitable structure based on mainnet/testnet selection at runtime
func init_hard_forks(params map[string]interface{}) {
// if simulation , load simulation features
if params["--simulator"] == true {
current_hard_forks = Simulation_hard_forks // enable simulator mode hard forks
logger.Info("simulator hardforks are online")
} else {
if globals.IsMainnet() {
current_hard_forks = mainnet_hard_forks
logger.V(1).Info("mainnet hardforks are online")
} else {
current_hard_forks = testnet_hard_forks
logger.V(1).Info("testnet hardforks are online")
}
}
// if voting in progress, load all votes from db, since we do not store votes in disk,
// we will load all necessary blocks, counting votes
}
// check block version validity at specific height according to current network
func (chain *Blockchain) Check_Block_Version(bl *block.Block) (result bool) {
height := chain.Calculate_Height_At_Tips(bl.Tips)
if height == 0 && bl.Major_Version == 1 { // handle genesis block as exception
return true
}
// all blocks except genesis block land here
if bl.Major_Version == uint64(chain.Get_Current_Version_at_Height(height)) {
return true
}
return
}
// this func will recount votes, set whether the version is voted in
// only the main chain blocks are counted in
// this func must be called with chain in lock state
/*
func (chain *Blockchain) Recount_Votes() {
height := chain.Load_Height_for_BL_ID(chain.Get_Top_ID())
for i := len(current_hard_forks) - 1; i > 0; i-- {
// count votes only if voting is in progress
if 0 != current_hard_forks[i].Window_size && // if window_size > 0
height <= current_hard_forks[i].Height &&
height >= (current_hard_forks[i].Height-current_hard_forks[i].Window_size) { // start voting when required
hard_fork_locked := false
current_hard_forks[i].Votes = 0 // make votes zero, before counting
for j := height; j >= (current_hard_forks[i].Height - current_hard_forks[i].Window_size); j-- {
// load each block, and count the votes
hash, err := chain.Load_BL_ID_at_Height(j)
if err == nil {
bl, err := chain.Load_BL_FROM_ID(hash)
if err == nil {
if bl.Minor_Version == uint64(current_hard_forks[i].Version) {
current_hard_forks[i].Votes++
}
} else {
logger.Warnf("err loading block (%s) at height %d, chain height %d err %s", hash, j, height, err)
}
} else {
logger.Warnf("err loading block at height %d, chain height %d err %s", j, height, err)
}
}
// if necessary votes have been accumulated , lock in the hard fork
if ((current_hard_forks[i].Votes * 100) / current_hard_forks[i].Window_size) >= current_hard_forks[i].Threshold {
hard_fork_locked = true
}
current_hard_forks[i].Voted = hard_fork_locked // keep it as per status
}
}
}
*/
// this function returns number of information whether hf is going on scheduled, everything is okay etc
func (chain *Blockchain) Get_HF_info() (state int, enabled bool, earliest_height, threshold, version, votes, window int64) {
state = 2 // default is everything is okay
enabled = true
topoheight := chain.Load_TOPO_HEIGHT()
block_id, err := chain.Load_Block_Topological_order_at_index(topoheight)
if err != nil {
return
}
bl, err := chain.Load_BL_FROM_ID(block_id)
if err != nil {
logger.Error(err, "loading block", "blid", block_id, "topoheight", topoheight)
}
height := chain.Load_Height_for_BL_ID(block_id)
version = chain.Get_Current_Version_at_Height(height)
// check top block to see if the network is going through a hard fork
if bl.Major_Version != bl.Minor_Version { // network is going through voting
state = 0
enabled = false
}
if bl.Minor_Version != uint64(chain.Get_Ideal_Version_at_Height(height)) {
// we are NOT voting for the hard fork ( or we are already broken), issue warning to user, that we need an upgrade NOW
state = 1
enabled = false
version = int64(bl.Minor_Version)
}
if state == 0 { // we know our state is good, report back, good info
for i := range current_hard_forks {
if version == current_hard_forks[i].Version {
earliest_height = current_hard_forks[i].Height
threshold = current_hard_forks[i].Threshold
version = current_hard_forks[i].Version
votes = current_hard_forks[i].Votes
window = current_hard_forks[i].Window_size
}
}
}
return
}
// current hard fork version , block major version
// we may be at genesis block height
func (chain *Blockchain) Get_Current_Version() int64 { // it is last version voted or mandatory update
return chain.Get_Current_Version_at_Height(chain.Get_Height())
}
func (chain *Blockchain) Get_Current_BlockTime() uint64 { // it is last version voted or mandatory update
block_time := config.BLOCK_TIME
//if chain.Get_Current_Version() >= 4 {
// block_time= config.BLOCK_TIME_hf4
// }
return block_time
}
func (chain *Blockchain) Get_Current_Version_at_Height(height int64) int64 {
for i := len(current_hard_forks) - 1; i >= 0; i-- {
//logger.Infof("i %d height %d hf height %d",i, height,current_hard_forks[i].Height )
if height >= current_hard_forks[i].Height {
// if it was a mandatory fork handle it directly
if current_hard_forks[i].Threshold == 0 {
return current_hard_forks[i].Version
}
if current_hard_forks[i].Voted { // if the version was voted in, select it, other wise try lower
return current_hard_forks[i].Version
}
}
}
return 0
}
// if we are voting, return the next expected version
func (chain *Blockchain) Get_Ideal_Version() int64 {
return chain.Get_Ideal_Version_at_Height(chain.Get_Height())
}
// used to cast vote
func (chain *Blockchain) Get_Ideal_Version_at_Height(height int64) int64 {
for i := len(current_hard_forks) - 1; i > 0; i-- {
// only voted during the period required
if height <= current_hard_forks[i].Height &&
height >= (current_hard_forks[i].Height-current_hard_forks[i].Window_size) { // start voting when required
return current_hard_forks[i].Version
}
}
// if we are not voting, return current version
return chain.Get_Current_Version_at_Height(height)
}
/*
// if the block major version is more than what we have in our index, display warning to user
func (chain *Blockchain) Display_Warning_If_Blocks_are_New(bl *block.Block) {
// check the biggest fork
if current_hard_forks[len(current_hard_forks )-1].version < bl.Major_Version {
logger.Warnf("We have seen new blocks floating with version number bigger than ours, please update the software")
}
return
}
*/