Recent 51% attacks on the Ethereum Classic chain made me think about this attack and while thinking I learned that 51% attacks are not a network failure but it also made me wonder if there is a way to make such attacks on the network participants more expensive.
When a node client finds out another chain exists it has to make a decision which chain to choose. The node client calculates a
chainScore for the chains and then chooses the chain with the highest score. In PoW blockchains the rule which chain to choose is usually called the longest chain rule where the
chainScore value is equal to the total amount of work done on the chain. What’s nice about this chain selection score is that it is completely objective. An example of a subjective score would be if a node client assigned a
chainScore to other chains based not only on how much work was done on the chain but also taking into account how many blocks would be deleted from the local chain if we switched to the other chain (penalizing the chains that would delete the blocks from your chain).
Another example of a subjective chain selection rule is to compute the
chainScore and only switch to a chain with a higher score if it doesn’t delete more than 20 blocks from your local chain (this rule is often referred to as max reorg cap). Not only does this rule have a ‘new node problem’ (seen in the picture above) but it can also lead to hard forks even when all the nodes are running the same client version. Having a subjective chain score is something we want to avoid because assigning subjective scores makes it hard to reason and can lead to hard forks even if everyone follows the same rules.
Chain selection rule
The chain selection rule that Ethereum Classic uses is to choose the chain with the most work done. Let’s take the perspective of an ETC client connecting to the network for the first time named Bob. When Bob has to decide between multiple chains, he can immediately tell which chain he should follow — all he has to do is calculate how much work was done on each chain and follow the one with the most work. Even if we have an arbitrary number of Bobs they should all calculate the exact same
chainScore for the same set of chains. Given
N nodes in the system where they form a complete graph (every node is connected to every other node) the chain selection rule should have the following property
Every node should select the same chain as the best one.
Chain selection rule should also give the same
chainScore for a chain
C up to block height
H at any point in time. This means the
chainScore should be derived only from the data on the chain that we are evaluating. These properties make sure that Bob and the long living nodes agree on which chain to follow and makes them converge to the same chain over time.
Reminder: As we mentioned above, the chain with the biggest
chainScore in the case of ETC is simply the one with the most work done.
Observing our blocks
It’s very unlikely that a solo miner mines a block unless he has a reasonably big amount of hashrate. The mining pools were invented to solve this issue and give miners a consistent return for their work even when they don’t mine a block.
Assumption: We have multiple mining pools that together form the majority of the network hashrate
Each block contains a value called
coinbase/etherbase — I’ll be referring to this as
miner from now on — which represents the address that should receive the mining reward. Majority of the blocks are mined by the mining pools which usually use a specific
miner address they send the coins to. Below are 16 consecutive blocks that were mined on the ETC chain from height
7283680 up to
7283695 including their
As expected, the vast majority of the blocks were mined by the mining pools operating on the ETC network. Ethermine mined 5 blocks, MiningPoolHub mined 4 blocks and NanoPool mined 3 blocks. The blocks colored gray send the miner reward to an address that I don’t know who the owner is, but it is very likely owned by a mining pool.
Can we use this to our advantage?
Assuming we have pools that are consistently mining new blocks, we should be seeing their blocks pretty frequently. When an attacker performs a 51% attack he usually goes offline to mine
N blocks and then reappears with a longer chain causing a larger chain reorganization. The blocks the attacker mined usually don’t use the
miner addresses of the mining pools observed in the past. If we penalize the inconsistency of pool mining we might defend ourselves from some of the attacks by making the attack more expensive while still having similar consensus rules.
Before we continue we should know how 51% attacks can happen:
- Use hash renting service e.g. NiceHash to attack (assuming NiceHash is not also pool)
- A few pools collude to perform the attack
- A combination of the two — Pool + hash renting service
Pools Consistency Index
The chain selection rule can take into account not only the work done, but also the consistency of the mining pools. We could take the mining pools into account by changing the definition of the
chainScore to something like:
chainScore = parentChain.score + (block.PoW_score * PCI)
PCI = value between 0 and 1
PCI represents the consistency of the mining pools. Lets take a look at one of the possible implementations that uses the information of the mining pools to evaluate how ‘healthy’ a chain is.
The implementation of the
chainScore function can be seen below.
When we have multiple coexisting realities (chains) the most probable reality we want to be in is the one where the pools are consistently mining. This might not be always true, but is true for most cases — in case pools with more than 51% hashrate turn evil we have a problem anyway. This method tries to predict what the future of the chain looks like and penalize the reality that seems unlikely to happen based on our pool information from the past. When we get a high pool inconsistency penalty there are two possible scenarios:
- A single chain exists and some pools simply stopped mining. Since the
PoWvalue is lowered for all miners equally this shouldn’t matter for the miners — a lower
chainScoredoesn’t play a role in the mining reward
- We have multiple chains coexisting in which case the chain that has a lower pool consistency (as is usually the case in 51% attacks) will get penalized
Let’s go over the possible 51% attack with the above method:
- Use hash renting service e.g. NiceHash
Attacker gets the pool inconsistency penalty over time due to the pools not mining his chain.
- 2 pools with a total of 60% network hashrate attack
In order to solve this we need to account not only the hashrate of the pools but also the number of pools mining the chain. This is probably easier to solve if we have N > 10 pools with roughly the same hashrates.
- A combination of the two — e.g. a Pool with 40% and rented hash 30%
Attacker gets the pool inconsistency penalty over time. Note that if the attacker sets the
mineraddress for the rented hash blocks to that of the pool, he would not make his result any better because we are looking at the past data of mined blocks.
This seems more like a long term 51% attack. Attacker gets the pool inconsistency penalty over time but if he can sustain it, he could become our ‘past pool’. In this case, the problem reduces to a mining pool turning evil.
While this does not solve the 51% attacks, it could make them much more expensive because the attacker would get penalized for a long period of time (a lot of blocks). If a few pools suddenly stopped mining, the monitoring systems could catch this and the community would have some time to react e.g. begin mining themselves (they would have to be careful though because of the penalties) or just wait for a lot more confirmations when this happens.
- The idea is to live with the mining pools and have them as a part of the network that hardens the security of the chain. The pools can attack if they join the forces and have more than 51% hashrate, but we have this problem already so it’s nothing new — unless I’m missing something.
- Once a pool
Amines a block, all the next 29 blocks will get a reward for pool
Aconsistency regardless of who the miner is — so the pool
Adoesn’t get an advantage compared to others.
- Profit switching miners are still welcome but they could get penalized if they begin mining a lot of block all of a sudden. If they are somewhat consistent, we don’t penalize them a lot and they can mine new blocks easily. One could argue this reduces security, but my hunch is that you gain more from having PCI than you lose from not having profit switching miners.
- As long as each new block is guaranteed to add some positive value to the chain, the mining can move on also when no pools are present
- It’s rare to see solo miners mine a block so the penalty for them would be basically zero. I’d argue that most of them also want to be a part of the pool.
- New bigger mining pools would have to enter the chain gradually otherwise they would make the pool mining inconsistent resulting in a penalty for their blocks.
- There are MANY ways to define the
PCIformula or the
- This whole idea works better with blockchains that have lower block times because you get more information about the consistency of the pools in a shorter span of time
The idea should be challenged in a game theoretical way to make sure it doesn’t break any of the existing dynamics.
NOTE: It might be worth considering NOT adjusting the PoW value and rather use the PCI as an additional value added to it to keep the PoW consistent.
Results of Sliding-Window-Miner-Consistency delta testing
I’m testing the idea of measuring pool consistency over a window of blocks on a simulation of a very naive blockchain version. The code is available in a public github repository. While the results hint that 51% attacks would be significantly more expensive when using the Sliding-Window-Miners-Consistency delta compared to the longest chain implementation (most work done), it should be noted that the results could be faulty due to a flaw in testing, a bug in the code or simply due to the simulation being too naive/simple/wrong and thus having too big difference with the actual blockchain implementation.
The results below are from creating ~5500 blocks (the process goes through 5500 * 14 seconds) where the attacker forks at block height 4850. By default the block time is ~14 seconds. The code was simplified a lot and it doesn’t map exactly to the logic used in the blockchain (e.g. block time calculations are not the same, no uncles,…). Despite the differences, I think it should be good enough to provide some insight of the methods. The
Ratio represents the ratio of the chainscore between the Mainnet and the AttackerChain from the fork block (4850) onward — a ratio below
1.0 means that the sum of the chainscore on the Mainnet is lower than the sum on the AttackerChain. We can also see the height that the two chains achieved.
From the result table we can see that the attacker chain ended up mining more blocks in all cases but never outperformed the
chainScore of the Sliding-window scoring method (this is not entirely true as he might have achieved a higher score in the beginning, but in this testing the attacker chain never had 30 blocks more than the Mainnet and also have a higher score). If the attacker persisted for long enough he could become a part of the ‘pool history’ and over time would overtake the Mainnet score.
The code wasn’t checked by anyone other than myself. If you notice an issue in the code please consider contributing to the repository or reporting it.
Known attack vectors
d0h! They might be much more expensive to pull off though.
Mining pool impersonation attack
An attacker could mine a longer chain and insert the
miner addresses of other pools to adjust the pool distribution of the blocks mined by the attacker to roughly the same distribution that was present before the attack. In order to prevent this we would need to have some sort of an identity proof for the miners. This can be solved by adding an identity verification called
minerSig in the block header. The
minerSig is the
parentHash + nonce signed with the
miner private key and in order for a block to be valid, the verification needs to check out.
Attacker joins the main network
An attacker could join the main network and mine some blocks to avoid getting a horrible score later. This whole idea is the same as a mining pool turning evil.
Improvement: Getting rid of the mining pool assumption
Having mining pools implemented on the protocol level would give us a guarantee that the pools do exist. But even if we don’t have that, the protocol might be able to optimize for that scenario.
As an example, we could encourage miners to converge to a certain number of mining pools by making the mining reward depend on the observed hashrate of the miner and use that to optimize for a total of
N pools. In case we wanted to have ~20 pools, the mining reward would be highest (4 ETC) if the pool had 5% of the hashrate. If it has more/less than this, then the pool gets a lower reward and the remaining reward gets distributed to other pools depending on their hashrates. Since we end up creating the same amount of coins for each block (in this case 4), it does not change the total coins in circulation defined in the monetary policy. The incentives need to be done correctly so that they actually converge to where we want them to.
Note: Even if one person owned 5 pools each having 5% of the hashrate, that’s probably fine because it strengthens the consistency of the pools and hence provides us the security.
If the mining pools are already present — which in the case of ETC are — then this could be implemented at a later stage if needed.
Other possible PCI definitions
The formula could do some of the following:
- A Linear Regression based on pools data and similar analysis
- Penalize hashrate increase (both from the individuals miners and the whole network). The network penalty idea is that if there is no malicious chain then the penalty is equal for everyone and if there is a malicious chain that wants to move faster it will get penalized more than others. Due to a possibility of block timestamps being fake, it is hard to give such an objective score just from the chain data
- Giving speeding tickets to individual miners/pools if they are increasing the ratio of mined blocks in a given window of blocks too quickly
- Doing statistical analysis over the miners
PCI examples with protocol level mining pools
- Assume we have a chain that has an incentive mechanism built in for mining pools to converge to a 1% hashrate forming a total of ~100 pools. In order for a pool to contribute to the PCI score it would need to be consistently mining in the last 3000 blocks at ~1% hashrate. The PCI score would be the ratio of pools that pass this criteria. For example if we had 50 different pools in the last 100 blocks, the PCI would be equal to 0.5 hence contributing 0.5 difficulty. In this scenario, an attacker would need to mine 50% of the last 3000 blocks with different mining addresses to even start getting the same PCI score as the main chain if he wanted to make an attack. On top of that we could also apply the speeding ticket penalties for the pools.
- Assuming we have 100 pools with equal hashrate we could also do some statistics. All the pools have an equal probability of being ‘chosen’ which means we could calculate the probability of ending up with the state of the last 100 blocks assuming they were randomly chosen and put a penalty if the outcome is an unlikely event.
Thinking out loud
I’m not saying people should switch their consensus algorithm. There should be a very good reason to change the consensus algorithm and the alternatives should be well researched before any changes are even considered. This is just me thinking out loud about possible consensus changes that could reduce the likelihood of a successful 51% attack on network participants. I’m not a researcher nor have I read any research on the topic so the ideas above may be flawed — if nothing else they probably make some trade-offs.
In case the idea of using the mining pools as an additional information for calculating the chain score works, the security of the network could benefit from both
PCI which seem to play well together — at least in my mind. This would make us rethink pools and make them a welcome addition to the network instead of thinking about them as a “centralized threat”. Pools will be a part of the network anyway so why not use them to add another layer of security.
We can still think about this as the
longest chain rule. The difference is that we changed how we measure the
length of a chain. I’d prefer if people used a more general word for it e.g.
biggest chain score rule . It seems like a better word to use when describing how the chain selection works. Take this as an idea/framework to play with.
Special thanks to Zachary Belford for discussing and helping shape the idea.