BIP9 allows the deployment of multiple backward-compatible soft forks, which are voted on during a target cycle through absenteeism, and can be successfully enabled if the activation threshold Nrulechangeactivationthreshold is reached. On the implementation side, by redefining the version field in the chunk header information, the version field is interpreted as a bit vector, each bit can be used to track a separate deployment, and after the activation condition is met, the deployment will take effect and the bit can be used by other deployments. Currently through BIP9 successfully carried out soft fork BIP68, 112, 113, in 2016-07-04, Height: 419328 successfully activated. BIP9 Deployment Settings
Each BIP9 that is deployed must set the bit bit, start time, and expiration time.
struct Bip9deployment {
int bit;
int64_t Nstarttime;
int64_t ntimeout;
};
Namespace:consensus
struct Params {
...
uint32_t Nrulechangeactivationthreshold;
uint32_t Nminerconfirmationwindow;
Bip9deployment Vdeployments[max_version_bits_deployments]; BIP9
uint256 powlimit;
BOOL Fpowallowmindifficultyblocks;
BOOL fpownoretargeting;
int64_t npowtargetspacing;
int64_t Npowtargettimespan;
...
};
The bit is converted to a uint32_t integer in 1 << bit mode, and the condition (...) is used when verifying that a BIP9 deployment is successfully activated. function to verify that a block is in favor of the deployment.
BOOL Condition (const cblockindex *pindex, const Consensus::P arams ¶ms) const {return ((
pi Ndex->nversion & Versionbits_top_mask ==versionbits_top_bits) &&
(Pindex->nversion & Mask (params))!= 0);
}
uint32_t Mask (const Consensus::P arams ¶ms) const {return
((uint32_t) 1) << params.vdeployments[id].b it;
}
The logical analysis first verifies that the version is a valid versioning setting (001) To verify that the specified bit-bit Mask () function is set in the revision number of the block to generate a version of the block represented by moving 1 left BIP9 the bit set in the deployment
The start time and expiration time are primarily intended to provide a basis and a critical value for state judgment when checking the BIP9 deployment state. For example, if the median time of a chunk exceeds the expiration time ntimetimeout, the BIP9 deployment has failed (after a detailed resolution).
if (Pindexprev->getmediantimepast () >= ntimetimeout) {
statenext = threshold_failed;
} else if (pindexpre V->getmediantimepast () >= ntimestart) {
statenext = threshold_started;
}
if (Pindexprev->getmediantimepast () >= ntimetimeout) {
statenext = threshold_failed;
break;
Deployment State Transitions
The initial state of all soft fork upgrades defined in the BIP9 deployment is threshold_defined and the founding block state is threshold_defined, and if Blockindex is nullptr in the program, Returns the threshold_defined state.
The specific conversion process is as follows: Threshold_defined is the initial state of the soft fork, if the past median time (MTP) is greater than nstarttime, then the state is converted to threshold_started, if MTP is greater than or equal to Ntimeout, The state is converted to threshold_failed, and if the number of blocks in favour of upgrading in one target cycle (2016 blocks) is over 95% (about 1915 blocks), the state is converted to threshold_locked_in or converted to Threshold_ FAILED the next target cycle after threshold_locked_in, the state is converted to threshold_active, and the deployment remains that state.
Enum Thresholdstate {
threshold_defined,
threshold_started,
threshold_locked_in,
threshold_act IVE,
threshold_failed,
};
Business Logic
The base class Abstractthresholdconditionchecker defines the status of a BIP9 deployment through a consensus rule. The following methods are used, where the last two methods are implemented in the base class, and the subclass inherits the implementation of the method: Condition (...) Detect whether a block is in favor of a soft fork upgrade: First verify that the block version is valid in version format, and then detect if the version has set the corresponding bit bit begintime (...) Returns the start polling time in the consensus rule (using MTP validation pindexprev->getmediantimepast () >= ntimestart) endtime (...) Returns the expiration time of a setting in a consensus rule Period (...) Returns a target period in a consensus rule (the target cycle of the current main chain is 2016 blocks) Threshold (...) Returns Nrulechangeactivationthreshold to meet the minimum requirements for soft fork upgrades getstatefor (...) The status of the current deployment is determined by providing consensus rules, starting the retrieved chunk index, and previously cached state data (the logic is later analyzed in detail) getstatesinceheightfor (...) The function is to find out which block height to start, and the status of the deployment is already consistent with the current
class Abstractthresholdconditionchecker {protected: virtual bool Condition (const CBLOCKINDEX *pindex
, const Consensus::P arams ¶ms) const = 0;
virtual int64_t begintime (const Consensus::P arams ¶ms) const = 0;
virtual int64_t endtime (const Consensus::P arams ¶ms) const = 0;
virtual int Period (const Consensus::P arams ¶ms) const = 0;
virtual int Threshold (const Consensus::P arams ¶ms) const = 0; Public: thresholdstate getstatefor (const cblockindex *pindexprev, const Consensus::P arams &am
P;params, Thresholdconditioncache &cache) const; int getstatesinceheightfor (const cblockindex *pindexprev, const Consensus::P arams ¶ms,
Thresholdconditioncache &cache) const; };
The
Class Versionbitsconditionchecker inherits Abstractthresholdconditionchecker. Implemented: BeginTime (const Consensus::P arams ¶ms) endtime (const Consensus::P arams ¶ms) Period (const Consensus::P arams ¶ms) Threshold (const Consensus::P arams ¶ms) Condition (const Cblockindex *pindex, Const Consensus::P arams ¶ms)
Class Versionbitsconditionchecker:public Abstractthresholdconditionchecker {private://Maybe:deployment_testdummy,
Deployment_csv,max_version_bits_deployments const Consensus::D eploymentpos ID; Protected: int64_t begintime (const Consensus::P arams ¶ms) const { return
Params.vdeployments[id].nstarttime; &NBSP} int64_t endtime (const Consensus::P arams ¶ms) const { retu
RN Params.vdeployments[id].ntimeout; &NBSP} int Period (const Consensus::P arams ¶ms) const { return PA
Rams.nminerconfirmationwindow; &NBSP} int Threshold (const Consensus::P arams ¶ms) const { return
Params.nrulechangeactivationthreshold; &NBSP} bool Condition (const cblockindex *pindex, const Consensus::P arams ¶ms) const {&NBS P retuRN ( (pindex->nversion & versionbits_top_mask) = = Versionb
its_top_bits) && (Pindex->nversion & Mask (params))!= 0); &NBSP}   ...}
Another important class Versionbitscache, including a method and an array. The array is used as a memory cache, and the member of the array is a map, and when checking the state of a BIP9 deployment, if the deployment state is judged during the inspection, the map is cached with the block index as the key value, with state information (int) as the value, The next inspection can be in the location of the block directly to obtain its status information, the program played a role in optimizing, to avoid duplication of retrieval.
struct Versionbitscache {
thresholdconditioncache caches[consensus::max_version_bits_deployments];
void Clear ();
typedef std::map<const CBLOCKINDEX *, thresholdstate> Thresholdconditioncache;
In addition, the Warningbitsconditionchecker class also inherits the Abstractthresholdconditionchecker class, which enables tracking and warning of unknown upgrades. Once the unexpected bits in the nversion are set to 1,mask, a Non-zero value will be generated. When an unknown upgrade is detected everywhere threshold_locked_in state, the software should warn the user about the impending unknown soft fork. In the next target cycle, in the threshold_active state, you should emphasize warning users.
What needs to be explained is that an unknown upgrade warns only if it is in locked_in or active condition
...
Warningbitsconditionchecker Checker (bit);
Thresholdstate state = Checker. Getstatefor (Pindex, Chainparams.getconsensus (), warningcache[bit]);
if (state = = Threshold_active | | | state = = threshold_locked_in) {
if (state = = threshold_active) {
std::s Tring strwarning =
strprintf (_ ("Warning:unknown new rules activated (Versionbit%i)"), bit);
Setmiscwarning (strwarning);
if (!fwarned) {
alertnotify (strwarning);
fwarned = true;
}
else {
warningmessages.push_back
strprintf ("Unknown new rules are about to Activa Te (Versionbit%i) ", bit)";
}
}
...
Code Dismantling GetAncestor (int height) function is very high in the entire module, its function is to return the specified height of the block index, the function is very simple but its code logic is not very good understanding. The whole block chain can be simply seen as a linked list structure, in order to obtain the specified height of the node information, generally by moving the pointer to the specified block in turn. In this module, the Pskip field in the Cblockindex class, with the getskipheight (int height) function, is able to quickly navigate to a specified height of the block, optimizing the efficiency of execution.
Cblockindex *cblockindex::getancestor (int height) { if (height > nheight | | Height < 0) { &nbs P
return nullptr;
  cblockindex *pindexwalk = this;
int heightwalk = nheight;
while (Heightwalk > height) { int heightskip = Getskipheight (Heightwalk);
int Heightskipprev = Getskipheight (heightWalk-1); if (pindexwalk->pskip!= nullptr && (Heigh Tskip = = Height | | (Heightskip > Height &&!) (Heightskipprev < heightSkip-2 && heightskipprev >= height)))
{ pindexwalk = pindexwalk->pskip;
heightwalk = Heightskip;
&NBSP} else { assert (Pindexwalk->pprev); &NBsp
pindexwalk = pindexwalk->pprev;
heightWalk--;
&NBSP} } return Pindexwalk;
static inline int getskipheight (int height) { if (height < 2) { return 0;   return (height & 1)?
Invertlowestone (Invertlowestone (height-1)) + 1:invertlowestone (height); }
In the whole module of time comparison to judge is to use the Getmediantimepast (), its role is to find the current block before the 10 blocks, sorted, return the 5th element ntime
enum {nmediantimespan = one};
int64_t getmediantimepast () const {
int64_t Pmedian[nmediantimespan];
int64_t *pbegin = &pmedian[nMedianTimeSpan];
int64_t *pend = &pmedian[nMedianTimeSpan];
Const Cblockindex *pindex = this;
for (int i = 0; i < nmediantimespan && pindex; i++, pindex = Pindex->pprev) {
* (--pbegin) = Pinde X->getblocktime ();
}
Std::sort (Pbegin, pend);
Return
pbegin[(Pend-pbegin)/2];
}
The
Logic is as follows: Create an array of 11 elements, including the block and the previous 10 blocks pbegin, pend two cursors (array cursors) point to the end of the array to traverse 11 blocks, and the Pindex cursor moves the array cursor forward continuously, and assigns the timestamp obtained by Pindex to the array arrays (the reason for the order is: block timestamp is an unreliable field with a size that may not match the order in which it was created. 11 blocks to the middle element, that is, the array subscript 5 element, because it is an odd number of elements, so do not need to judge the problem of invalid subscript Getstatefor (...) Functions are critical throughout the module to obtain status information for BIP9 deployments. The first description is that within a target cycle, the status of a BIP9 deployment is the same, meaning that the deployment status is only updated after the difficulty target has changed. Getstatefor (...) function gets the state of the last block of the previous target cycle, and if the state can determine the deployment state, the result is obtained and the result is saved in the Versionbitscache structure; If the state already exists in the cache, the result is returned directly; The status information of the (pindexprev.nheight-nperiod) height is searched in turn until the result can be obtained. If there is no nullptr, then return threshold_defined. It is more important that if a block indicates that the deployment state is in threshold_started, a more detailed judgment will be made to prove whether the state is as well as failed or can enter the locked_in phase.
thresholdstate abstractthresholdconditionchecker::getstatefor (...) {... if (Pindexprev!= nullptr) { pindexprev = Pindexprev->getancestor ( & nbsp
pindexprev->nheight-((pindexprev->nheight + 1)% nperiod));
std::vector<const cblockindex *> Vtocompute; while (Cache.count (pindexprev) = = 0) { if (Pindexprev