Furtheon v1.1 upgrade requirements
The Furtheon v1.1 release includes crucial adjustments through two hard forks: quorumcalcalignment
and txHashWithType
.
This guide provides details on these changes and the requirements for implementing this upgrade.
While these are important changes at the technical level, they do not affect the user experience and user data remains unaltered. It's also important to note that the upgrade process will involve some downtime as the nodes need to be stopped, updated, and then restarted.
Node Upgrade Process
Follow these steps to upgrade your nodes and implement the hard forks:
-
Stop the node(s). The ideal scenario would be to halt all nodes simultaneously, but if that isn't feasible, ensure at least the majority are stopped around the same time.
-
Update the binaries. Replace the old Furtheon binary with the new one which incorporates the necessary changes for both the
quorumcalcalignment
andtxHashWithType
forks.
For different methods on downloading the latest release, check out the installation guide available here.
- Update the
genesis.json
file. Include both the new forks in thegenesis.json
file and specify a block from which each becomes active. The block number for each fork should be greater than the current maximum block number across all nodes.
Here's an example:
```json
"params": {
"forks": {
"txHashWithType": {
"block": 100
},
"quorumcalcalignment": {
"block": 120
},
...
}
}
```
- Restart the nodes. Once the binary and
genesis.json
are updated, the node can be restarted.
Understanding the Forks
quorumcalcalignment
This fork corrects the calculation of the quorum, aligning it with the IBFT paper. This prevents issues that arise when there are a number of validators divisible by 3.
txHashWithType
This fork introduces dynamic fee transaction hash calculations that include the transaction type and the correct chainID. The new binary includes a new transaction hash calculation algorithm but will also support the old one until the fork block. Consequently, the hash calculation algorithm will now depend on the block number.
Note: It's crucial to ensure no dynamic fee transactions from the block occur when the node is restarted until the
txHashWithType
fork block. The new binary will reject all dynamic fee transactions received from json-rpc or gossip until the fork block is reached.
Please follow these guidelines carefully to ensure a successful transition while upgrading to Furtheon v1.1.
Additional Details on Quorum Calculation Discrepancy
Before the Furtheon v1.1 release, the quorum was calculated by taking the ceiling of (2*totalVotingPower)/3
, deviating from the specifications of the IBFT paper. With total voting power of 6, where each validator has one token staked, the calculation resulted in:
CEILING(2*6/3) = 4 -> 2 faulty -> INCORRECT
The correct formula, per the IBFT paper, should be:
FLOOR(2*6/3) + 1 = 5 -> 1 faulty -> CORRECT
This discrepancy arose between the core contracts and the client side of quorum calculations.
Core Contract Side: The calculation is: aggVotingPower > ((2 * totalVotingPower) / 3)
Reference CheckpointManager.sol
Client Side: The calculation is: aggVotingPower >= math.Ceil(2 * totalVotingPower) / 3)
Reference validator_set.go
The client side equation adheres to the IBFT paper's specifications: arxiv.org/pdf/1909.10194.pdf (page 7, lines 2 and 4)
Core Issue
Problems arise when the number of validators can be divided by 3 (such as 6, 9, 12, etc.). For instance, given 6 validators each with voting powers equal to 1, and 4 out of 6 validators have voted (aggVotingPower = 4
):
- The Core Contract will check:
4 > (2 * 6)/3 → 4 > 4 → false
- The Client Side will check:
4 >= math.Ceil((2 * 6)/3) → 4 >= 4 → true
In this case, the client side reaches the quorum, but the core contract does not. This inconsistency prevents the checkpoint from passing, causing the bridging from the edge to rootchain to halt. The quorumcalcalignment
hard fork resolves this issue.
Additional Details on Transaction Hash Fork - txHashWithType
The txHashWithType
fork is implemented to enhance dynamic fee transaction hash calculations, including the transaction type and the correct chainID.
After implementing this fork, the new binary includes a revised transaction hash calculation algorithm, while still supporting the old one until the fork block. As a result, the hash calculation algorithm depends on the block number, i.e., the block number determines which hash algorithm—old or new—should be used.
For instance, if we call tx.ComputeHash(blockNumber)
for a dynamic fee tx
, we get:
- For block number < 100, the old hash without the type
- For block number >= 100, the new hash with the type and the correct chain ID
A transaction hash is computed as soon as a transaction enters a node's transaction pool. At that point, the latest block number from the blockchain is used for hash calculation. Later, if the transaction is needed, it's read from storage, deserialized, and the hash is recalculated using the block number of the block containing the transaction.
Given this, it's crucial to ensure that no dynamic fee transactions from the block occur when the node is restarted until the txHashWithType
fork block. The new binary rejects all dynamic fee transactions received from json-rpc or gossip until the fork block is reached.
Note: Select a sufficiently large block number for the
txHashWithType
fork to prevent dynamic fee transactions with old hashes from being included in blocks after the fork block. After replacing the new binary, updatinggenesis.json
, and restarting the node, there will be a period during which the node won't be able to accept new dynamic fee transactions either from json-rpc or gossip. Ensure enough "room" between the latest block synced/validated and the fork block on the node.