-
Notifications
You must be signed in to change notification settings - Fork 4
Komodo Consensus Specification Draft
The purpose of this doc is to list all Komodo chain consensus rules to refer to them in code and docs, f.e. to help to find out missed or unimplemented rules in new rust-based Komodo Zebra (Kebra) codebase.
Look for links to rules in the Komodod C++ or Komodo Zebra code by using search by rule identifiers (like 'kmd-0001', in lowercase).
The rules in this section are for KMD Main network chain (excluding rules specific for dPoW, PoS and asset chains).
The rule categories:
KMD Mainnet Block Rules
KMD Mainnet Block-level contextual rules
KMD Mainnet Block Connect Rules
KMD Mainnet Legacy Z-transaction rules
KMD Mainnet DPoW Block-Level Rules
KMD Mainnet Checkpoint Level rules
KMD Mainnet Mempool-level transaction rules
KMD Mainnet Non-consensus checks in P2P Message Processing
KMD Mainnet Non-consensus run chain rules
Zebra-only rules
NOTE:
- MultiSig is not supported
- Cryptoconditions framework (disabled in the Komodo Mainnet) currently is not supported in Zebra.
Non-contextual block rules for the KMD Mainnet
If the difficulty target is negative or zero or is over the PoW limit the block is invalid. The PoW limit is set to either the chain parameters' powLimit (if height <= 1 or ASSETCHAINS_ALGO is equal to ASSETCHAINS_EQUIHASH) or to the powLimitAlternate value. Note that using ASSETCHAINS_ALGO var probably was not intended for KMD mainnet (it is asset chain param actually) but nonetheless this parameter should not be changed in the komodo.conf or komodod start command line.
if block hash is over the Target the block is invalid. The Target is calculated according to zcash rules. This rule is activated after block 792000
The Equihash Solution in the block header must be valid
This rule duplicates the rule KMD-0003 but this check can be skipped (see the rule KMD-0007 description about skipping rules). This rule may be eliminated.
Fail already known block with BLOCK_FAILED_MASK set.
TODO: check if this is probably not a part of the consensus but validation optimisation.
TODO: check if this is a consensus part. Looks like this is not a consensus rule but block processing optimisation
If block time is equal or greater than the current time + 300 sec the block is invalid
If block time is equal or greater than the current time + 60 sec and less than current time + 300 then skip rules KMD-0008 and KMD-0003-2
Note for Komodo Zebra: this rule is not implemented yet and KMD-0076 rule is implemented instead. Probably just change of the max future time to 300 sec would suffice to fix it in Zebra.
For height > 0 if block version less than 4 the block is invalid
Note that this check may be skipped - see rule KMD-0007
Duplicates KMD-0080 and may be eliminated.
Not for Komodo Zebra: not implemented, see KMD-0080.
Calculate the Merkle Root and check it is equal to the value in the block header
While calculating the Merkle Root get the mutated flag. If it is true the block is invalid (CVE-2012-2459)
Check that the block transaction count is not zero or over MAX_BLOCK_SIZE. Check that the block serialised size is not over MAX_BLOCK_SIZE. MAX_BLOCK_SIZE is set for block height. Note that this rule must be checked before transaction validations.
Note for Komodo Zebra: this rule is not correctly implemented yet. See the latest zcash zebra code for the MAX_BLOCK_BYTES var use.
Check that transaction does not spend any transaction from the banned set. Take into account that first 15 txns from the banned set have only vout1 banned and the rest 2 txns have all vouts banned.
For transaction with fOverwintered false its version is not less than SPROUT_MIN_TX_VERSION.
Note for Komodo zebra: this rule is not implemented at the consensus level (transactions before Sapling are not supported and should be covered with checkpoints). However version check is done at serialization: see ZcashDeserialize for Transaction
For transaction with fOverwintered true its version is not less than OVERWINTER_MIN_TX_VERSION.
Note for Komodo zebra: this rule is not implemented at the consensus level (transactions before Sapling are not supported and should be covered with checkpoints). However version check is done at serialization: see ZcashDeserialize for Transaction
Overwinter transaction version Group Id is OVERWINTER_VERSION_GROUP_ID or SAPLING_VERSION_GROUP_ID.
Note that this rule is overridden by KMD-0042 and KMD-0046
Transaction expiry height is not equal or more than TX_EXPIRY_HEIGHT_THRESHOLD
If transaction has empty vins it cannot have empty both vjoinsplit and vShieldedSpend.
If transaction has empty vouts it cannot have empty vjoinsplit and vShieldedOutput.
Transaction serialised size after Sapling upgrade must not be over MAX_TX_SIZE_AFTER_SAPLING.
Note for Komodo Zebra: this rule is not implemented yet
Note for Komodo Zebra: implemented at serialization: see ZcashDeserialize for Transaction
Note for Komodo Zebra: implemented at serialization: see ZcashDeserialize for Transaction
Every transaction output script pubkey size (including opreturn and non-opreturn) cannot exceed IGUANA_MAXSCRIPTSIZE Note for Komodo Zebra: apparently not implemented yet
For each transaction transparent output, the total value of this output and outputs with lower indexes is calculated and checked that it is within the 'Money Range' (non-negative and not greater than MAX_MONEY)
Note for Komodo Zebra: I did not find any dedicated code for this rule but I believe it should be checked in the general sapling shield data validation (see the link in the zebra code)
Transaction's vShieldedSpend and vShieldedOutput list should be both empty for public chains (var acpublic is not zero). For KMD Mainnet var acpublic is set to 1 since blocktime is KOMODO_SAPLING_DEADLINE. (This rule disables Sapling shielded transactions. Note that we cannot also spend Sapling commitments created before this deadline).
Notes for Komodo zebra: TODO: to be implemented
Transaction's shielded valueBalance must be equal or over -MAX_MONEY and below or equal to MAX_MONEY
If transaction's shielded valueBalance is less or equal than zero (takes money from the tx transparent value pool) then transaction's total value plus -valueBalance must be within the 'Money Range' (non negative and not greater than MAX_MONEY).
Note for Komodo Zebra: it looks like there is no such dedicated rule in zebra code. It is similar to the conversion to ValueBalance but not exactly. TODO: to be implemented
Transaction vjoinsplit list should be empty for public chains (var acpublic is not zero). For KMD Mainnet var acpublic is set to 1 since blocktime reaches KOMODO_SAPLING_DEADLINE. (This rule disables Sprout transfers in transactions. Note that we also cannot spend Sprout commitments created before this deadline).
Note for Komodo zebra: TODO: to be implemented
Note for Komodo Zebra: I did not find this rule implementation. There is the check_for_duplicates helper function that may be used for that but it's not.
Minted transaction is either coinbase or coin import transaction. For such transactions input 0 ScriptSig size must be greater or equal to 2 and less or equal to 100 bytes
In this section there are rules for the block in context with the whole chain
The var cmptime, limiting the lock time, is increased by 777 secs. Activated since S6 Hardfork.
Note for Komodo Zebra: cmp_time is calculated only by after S6 rules
For each transaction in the block transaction lock time must be not too old to prevent cheating with interest value.
Transaction is final if its lock time is set to 0, or its lock time is less than the current block height or tip block time, or its inputs marked as final, or if some inputs are not marked as final then the transaction lock time must not exceed the current block height or tip block time.
Coinbase input 0 must contain serialised this block height. Activated since height 1.
Rules for each transaction in the block in context with other transactions in the chain
For the Sprout upgrade it is too early for transactions with overwinter flag.
Note for Komodo Zebra: not implemented (there is no rules before the Canopy upgrade)
For transaction version set for Sapling upgrade the Overwintered flag should be set.
Note for Komodo Zebra: not implemented (there is no rules before the Canopy upgrade)
For Overwintered flag set the transaction version GroupId must be set to SAPLING_VERSION_GROUP_ID if Sapling upgrade is active.
Overrides KMD-0017.
Note for Komodo Zebra: not implemented (there is no rules before the Canopy upgrade)
Note for Komodo Zebra: not implemented (there is no rules before the Canopy upgrade). The transaction version is checked in zcash_deserialize but current network upgrade is not checked
Note for Komodo Zebra: not implemented (there is no rules before the Canopy upgrade). The transaction version is checked in zcash_deserialize but current network upgrade is not checked
Note for Komodo Zebra: not implemented (there is no rules before the Canopy upgrade). The transaction version is checked in zcash_deserialize but current network upgrade is not checked
KMD-0046 When transaction Overwintered flag is on transaction version GroupId must be valid for Overwinter
Overrides KMD-0017.
Note for Komodo zebra: this rule is implemented at serialization: see impl ZcashDeserialize for Transaction
Note for Komodo Zebra: not implemented (there is no rules before the Canopy upgrade). The transaction version is checked in zcash_deserialize but current network upgrade is not checked
Note for Komodo Zebra: not implemented (there is no rules before the Canopy upgrade). The transaction version is checked in zcash_deserialize but current network upgrade is not checked
Note for Komodo Zebra: not implemented (there is no rules before the Canopy upgrade). The transaction version is checked in zcash_deserialize but current network upgrade is not checked
If the Sapling upgrade is not active the transaction size must not be over MAX_TX_SIZE_BEFORE_SAPLING.
Note for Komodo Zebra: not implemented (there is no rules before the Canopy upgrade). The transaction version is checked in zcash_deserialize but current network upgrade is not checked
If transaction is not minted and JoinSplit or ShieldedSpend or ShieldedOutput are not empty the signature hash must be valid.
Note for Komodo Zebra: the implementation is different with the Komodo C++: in the Komodo C++ this rule is checked for non-minted txns.
Additional consensus rules applied when a block is connected
Note for Komodo Zebra: not implemented yet
TODO: not sure if this is a consensus rule or a sanity check
If a new block's transaction already exists in the chain and was not pruned such block is invalid
Note for Komodo Zebra: I did not find code for this rule. Apparently it is covered by the double-spend rule. It looks like it is possible to have another tx with the same txid if the first tx was pruned (in such a case the double-spend rule would not cover this rule), but I did not find possibility of tx pruning in zebra.
This rule seems to duplicate the KMD-0034 rule.
Transactions, to which a block non-coinbase transaction refers to, must exist in the chain and be unspent
Check komodo rules for max output value (not greater than 10000000001*COIN).
Note for Komodo Zebra: not implemented yet
If a transaction is not a coinbase then calculate the komodo interest value and add it to the transaction input value. There are several paths to calculate the komodo interest depending on the spent utxo's height and value.
If this option enabled for a network, disable spending coinbase outputs to transparent outputs. This rule is disabled for the KMD Mainnet.
Transaction each input value and current total input value, including komodo interest, is within Money Range
Transaction total input value, with added komodo interest and shielded value, is within Money Range.
Note for Komodo Zebra: I could not find exactly such a rule in the zebra code (which calculates the tx total input value and checks it against the Money Range, i.e. 0..MAX_MONEY) but value_balance() gets the tx total input value and places it into Amount which validates against -MAX_MONEY..MAX_MONEY what should suffice
Transaction total input value, with added komodo interest and shielded value, is not less than total output value
Transaction fee, equal to total input minus total output value, is not negative. Seems duplicate the previous rule.
If transaction is not a coinbase run the standard script (with the passed option flags) for each transaction input. This rule is evaluated if expensive checks are enabled (see rule KMD-0082).
Note for Komodo Zebra: the script interpreter is implemented in legacy zcash C++ code (see zcash_script lib). TODO: check difference with the Komodo C++ interpreter
Activated in the Sapling upgrade
Remove komodo interest from block reward, apparently added by mistake. Activated since KOMODO_NOTARIES_HEIGHT2
The block is invalid if the coinbase total value is greater than calculated block reward plus extra satoshi and any of the following is true:
- the block height is greater or equal to KOMODO_NOTARIES_HEIGHT1,
- the coinbase output 0 value is greater that the calculated block reward.
It looks like before KOMODO_NOTARIES_HEIGHT1 it was allowed to generate extra unchecked reward in coinbase outputs with indexes over 0.
If the chain reorganises for the depth more than MAX_REORG_LENGTH (99 blocks) stop the chain
Block header nBits field must be equal to the calculated next work required
Block header blocktime field must be greater than the median time past
Yet another rule for the block time that should not be greater than current time plus nMaxFutureBlockTime (7 * 60).
This rule actually does not work as the KMD-0007 rule is more restrictive.
Note for Komodo Zebra: currently only this rule is implemented although the acting rule is KMD-0007
If for the block height a checkpoint is found the block hash must be equal to the checkpoint hash.
If checkpoints are enabled check that new block's hash at height 1 is not changed.
Note for Komodo Zebra: there is no such a rule found, it looks like it is not needed and covered by checkpoints.
This rule duplicates the rule KMD-0008.
Note for Komodo Zebra: implemented at block deserialisation
If KOMODO_REWIND is set, when the previous best chain is deactivated disconnect blocks until KOMODO_REWIND. It is not completely clear how this option is used though.
Note for Komodo Zebra: not implemented
Disable expensive checks if the block is an ancestor of the last checkpoint block. By default expensive checks are enabled.
Apparently this is an optimisation feature and not a consensus rule.
Note for Komodo Zebra: checkpoint-based block validation is executed by a different code path, with only minimum checks
Note for Komodo Zebra: the current upgrade set to Sapling, but in Zebra most rules for private txns are fully implemented only for Canopy+ upgrades (lower upgrades are covered by checkpoints). In Komodo we disabled private txns since Dec 2019, so only minimum private txns rules are actually needed (like to ensure tx integrity for empty private transfers).
In Komodo C++ the Sapling upgrade is activated for the Mainnet by KOMODO_SAPLING_ACTIVATION timestamp (15 Dec 2018).<br<
Note for Komodo Zebra: the Sapling upgrade is activated by Zebra's standard NetworkUpgrade object, by setting the Sapling block height at which it was activated in the KMD Mainnet
A transaction is coinbase if it has 1 input and the input's previous tx hash is null
This rule is deactivated in the Komodo C++ codebase after KOMODO_SAPLING_DEADLINE
Transaction Sprout old pub value (taken from the tx transparent value pool) is within the Money Range
Transaction Sapling old pub value (taken from the tx transparent value pool) is within the Money Range
Note for Komodo Zebra: this check is not implemented but scripts will be checked when executed
Note for Komodo Zebra: this check is not implemented but scripts will be checked when executed
Note for Komodo Zebra: this check is not implemented but scripts will be checked when executed
Note for Komodo Zebra: this check is not implemented but scripts will be checked when executed
Note for Komodo Zebra: this check is not implemented but scripts will be checked when executed
Note for Komodo Zebra: this check is not implemented but scripts will be checked when executed
Note for Komodo Zebra: not implemented (but for Komodo Mainnet the rule KMD-0030 would suffice)
Note for Komodo Zebra: this check is not implemented
Note for Komodo Zebra: this check is not implemented. This rule should be covered for Mainnet if we disable z-transactions and use checkpoints for earlier blocks
Note for Komodo Zebra: I could not find exactly the same rule which checks for the Money Range but value_balance places the total tx input value into Amount which checks for -MAX_MONEY..MOX_MONEY what should suffice
This rule is similar or identical to KMD-0029.
Note for Komodo Zebra: I could not find exactly the same rule but I think the value_balance constraint Amount should suffice
Rules for zcash private transactions. Those rules are currently disabled in the KMD Mainnet.
WARNING: we probably should never use those rules in Komodo Zebra as we are going to cover the chain part, where those rules were used, with checkpoints. But if we want to use them (f.e. for the PIRATE chain or else), be aware of that: for the original Zebra code it is claimed that the private transaction rules are fully implemented only since the Canopy upgrade, so we cannot be sure that the Zebra code for the Sapling upgrade or before (periods in Komodo mainnet when private txns were used) is valid and fully implements the same rules in Komodo C++.
If Sapling Spend are non empty Sapling Spend descriptions must be valid
If Sapling Outputs are non empty Sapling Output descriptions must be valid
If transaction is not minted and JoinSplit is not empty the signature must be valid Note for Komodo Zebra: the implementation is different: in komodo cpp this rule is checked for non-minted txns
If Sapling Spend or Outputs are non empty Sapling binding signature must be valid
Several rules are evaluated both for Sprout and Sapling: nullifiers do not exist to prevent double spends, anchors exists. This rule is deactivated after KOMODO_SAPLING_DEADLINE.
Block-level rules specific for DPoW protocol
Check that a special block (easy mined by a notary) must have the correct OpReturn output in the last transaction: it should contain the Merkle Root for the previous block hash and all transactions in the current block (this is to prevent special block last transaction reuse). This rule is activated since Season5.
The block's second and next coinbase output values should not be negative or exceed MAX_MONEY. Total of second and next coinbase output values should not exceed COIN/10. Activated since height 235300.
KMD-DPOW-0003 Do not allow total of second and next coinbase output values over zero for min diff blocks
If the block's nBits is KOMODO_MINDIFF_NBITS then the total of 2nd+ coinbase output values must be zero. Activated since height 814000. Also, this is activated only if the total is not overflowed (see rule KMD-DPOW-0002).
If the block coinbase pays to the same scriptPubKey as the block's last transaction vin 0 then this block possibly has a notary proof (a special block). For such coinbase we do not allow outputs with an OpReturn and small amount less 5000 sat, for output indexes > 1. This is activated since height 1000000.
If the block coinbase pays to the same scriptPubKey as the block last transaction vin 0 then this block possibly has a notary proof (a special block). If it does not, then coinbase scriptPubKey must not pay to a valid notary and if height is over 1000000. If this condition is not true the special block is invalid. This rule is activated since height 814000.
Looks for a notarisation transaction by making sure all of the following checks are true for a transaction:
- the tx output must be an opreturn
- if the opreturn data in position 68 contains "KMD\0" then this is a back notarisation. The output index must be 1 and the opreturn data size must be sufficient to have the source txid and the destination txid (if this is a back notarisation) and last notarised height
- the number of signed notaries is calculated as a mask (var 'signedmask'). If the current height < 91400 'signedmask is initialised to 1 otherwise 0. The 'signedmask' must be equal or greater than KOMODO_MINRATIFY. KOMODO_MINRATIFY is 7 if the current height < 90000 or 11
- the opreturn data length must be sufficient to include the MoM (merkle of merkle roots) and MoM depth If a notarisation tx is found and valid then collect the notarisation data from it: flag if this is a back notarisation, source txid, destination chain, destination txid (if a back notarisation), last notarised height, MoM and MoM depth, and update it in the memory as the current last notarisation.
Komodo dPoW protocol requires that in case of chain reorganisation forks are not allowed below the last notarised height stored in the current last notarisation, signed by current season notaries (at least of MIN_RATIFY quantity)
This rule appears to be always valid because the KMD-DPOW-0007 rule would not allow forks below the last notarised height
Blocks could not be connected to heights below the last notarised height. To check this:
- ensure that the last notarised height equals to the height of the block index element found by the last notarised block hash (I am not sure how this could not be true). If that is not true stop the validation with the result of true
- if the block height is below the last notarised height the block is invalid
- if the block height equals to the last notarised height check that the block hash equals to the last notarised hash. If that is not true the block is invalid.
This rule complements the KMD-DPOW-0007 rule:
the KMD-DPOW-0007 rule prevents activation of a longest chain fork if it does not have the last notarisation (if a chain with the last notarisation was received after the longest chain was created)
this rule prevents connecting new blocks at heights below the last notarised height.
If block is created by a valid notary, has the valid last special block transaction (see rules KMD-DPOW-0001, KMD-DPOW-0004, KMD-DPOW-0005, KMD-DPOW-0012) and there was no any blocks already created by this notary within recent 65 blocks, then the block target is allowed to be minimal (easy mining). Otherwise the block target must be for the normal PoW block
If there is a big time gap after the last mined normal pow block then notary is allowed to create a second special block. Notaries allowed to create a second special block are ordered in a priority list according to their first created special block order. The rule is activated since S6 season.
Special block hash must be over KOMODO_MINDIFF_NBITS.
Note that KOMODO_MINDIFF_NBITS is the Mainnet target limit.
Note that special block's target (block header field nBits) must also conform to the calculated PoW difficulty for this block, although the special block hash does not need to conform to this rule.
Rules that are evaluated when blocks are within the static checkpoint range.
TODO...
Rules to evaluate when a transaction is placed to the mempool.
If the limit is set the transaction inputs quantity should not exceed it
This is not a consensus rule as any node may not set this limit
The transaction locktime value must be not too old to prevent cheating with interest value. Similar to the KMD-0037 rule but adjusted for mempool
Evaluate rules implemented in CheckTransaction function when a tx is placed in mempool. The same code for several rules is called when tx in a block is validated and when a transaction is placed in mempool. There is no special links in the code for these rules for mempool so here is a list of those rules here with a mark whether it is called in Komodo C++ and Zebra for mempool validation:
If any rule is not implemented see the rule description.
Evaluate rules implemented in ContextualCheckTransaction function. Those rules are used both for tx in mempool and block validation. There is no special links in the code for these rules for mempool so here is a list whether those rules are called in Komodo C++ and Zebra for tx in mempool validation:
If any rule is not implemented see the rule description.
Note for Komodo Zebra: there is no similar function like IsStandardTx in Komodo C++. TODO: check if deserialize function does this right
Defines the next block time as the Median Time Past and the next height as the tip height plus one and performs the validation as in the KMD-0038 Transaction is final rule.
Note for Komodo Zebra: not implemented
For all transaction inputs check if any previous output is not spent already in mempool
Check if a transaction does not exists in both the mempool and coin cache.
Partially overlaps with KMD-MEM-0008.
Note for Komodo Zebra: apparently there is no check if a transaction hash exists in the chain in Zebra but the rule that prevents double-spends (KMD-0057) should cover it.
Note for Komodo Zebra: there is no similar function like AreInputsStandard() in Komodo C++. TODO: check maybe the script interpreter covers this rule
Note for Komodo Zebra: not implemented
The transaction total output value does not exceed 777,777 multiplied by COIN satoshi For difference of the transaction total output value and 777,777 multiplied by COIN the KOMODO_VALUETOOBIG condition is not true Note for Komodo Zebra: not implemented
If limiting transactions with low fee is on and the transaction does not fit the block priority size or has zero user priority the transaction fee must not be less that the minimum relay tx fee.
Note for Komodo Zebra: not implemented
If the relay priority option is on and the transaction fee is less than the minimum relay tx fee the transaction calculated priority should be greater than the free tx priority threshold. The calculated priority is obtained from transaction inputs and transaction output values increased by the tx depth. For private transactions it is always set to the max priority value.
Note for Komodo Zebra: not implemented
If limiting transactions with low fee is on use a rate limiter to constrain throughput of low fee transactions data.
If rejecting absurd tx fee is on then reject transactions which have too big tx fee, when the tx fee is greater than the minimum relay tx fee multiplied by 1000 and total tx outputs value divided by 19
Evaluate the following rules for contextual transaction inputs when a tx is placed in mempool
The same code for several rules is called when tx in a block is validated and when a transaction is placed in mempool. There is no special links in the code for these rules for mempool so here is a list of those rules here with a mark whether it is called in Komodo C++ and Zebra for mempool validation:
If any rule is not implemented see the rule description.
Evaluate transaction scripts with MANDATORY_SCRIPT_VERIFY_FLAGS.
This is probably not a consensus-important call but error determination, because there is another script executing call with all needed flags.
Note for Komodo Zebra: there is no a separate call to script interpreter with MANDATORY_SCRIPT_VERIFY_FLAGS only. It looks like we need two dedicated calls to script verification: with standard and mandatory only flags: with CC modules enabled, if any consensus error in a cc module the error was generated by the call to the script interpreter with the mandatory flags (the call with standard flags did not return any error for that case). TODO: check and implement if necessary.
Max input scriptSig size is no more than 1560 bytes.
Note for Komodo Zebra: not implemented
Multiple OpReturns are not permitted.
Note for Komodo Zebra: not implemented
Note for Komodo Zebra: not implemented
Remove from mempool transactions that were just added to the new connected block
Remove from mempool transactions whose nExpiryHeight value became less than the new connected block height. Also remove transactions whose locktime value violates the too-long-in-mempool rule (KMD-MEM-0002)
Checks are made when a message with a block or header or transaction is received. In progress..
to be continued...
If KOMODO_STOPAT is set and height is over then the block is invalid.
For non-upgraded nodes they will deprecate and stop after a certain height
These rules are found in the zebra code but they look like not implemented in Komodod C++. TODO: study their implementation and disable them in Zebra, if needed.
I did not find if this is checked in the Komodo C++ codebase.
When a tx input spends another tx output and both txns are in the same block and the spent tx index in the block is greater than the first tx index, such spends are invalid.
I did not find such a rule in the Komodo C++ code.
Zebra maintains the transparent chain value pool which must be within the Money Range. Apparently we do not have such a rule in Komodo and this pool should be disabled. This transparent value pool in zcash may potentially go negative as value from it may be taken to private value pool. In Komodo we disabled private transactions and this is not an issue anymore.
Block time should be go for more than 2 h in future
TODO: there is a certain number of more zebra rules regarding Canopy+ upgrade. Currently the Canopy height is set to the max height in our Komodo Zebra code but better to remove these rules at all.
to be continued...
to be continued...