You probably know about the ETC fork of ETH. I dont want to talk about the why or the politics of it, just the technical aspect of the replay attack.

If a spinoff of a coin is created, it inherits the genesis, the ledger state, basically everything. This is good as it means everyone with the original coin gets a new coin for "free". However there is a potential replay attack if nothing is done to prevent it.

The replay attack is just a matter of taking any transaction from one chain and broadcasting it to the other chain. So even if ports and low level network wire protocols are changed, unless the signatures are invalid, the transaction is valid. Now in a lot of cases the funds go to the same person for both replay and the original. however, these are two different coins so it is possible that the same address ends up crediting a different person's account. All that is required is a shared deposit address that uses some other means to determine who should be credited

As soon as this becomes possible, that means all sorts of mischief can happen. One simple example is a trading exchange that uses the same address for multiple customers and using some email confirmed field to credit accounts. You can see the problem now. If you happen to send funds to such an exchange, then literally anybody can replay that transaction on the other chain, at any time. Like right after they start a deposit to the same deposit address at that exchange. The funds go to the address, but they get the credit, they sell it on the exchange and there is little chance you will recover your replayed coins.

Notice that either direction is possible. So you could end up losing the lower value coin, by having the higher value coin's tx replayed, but the opposite is also possible. Quite a potential disaster, especially if we are talking about bitcoins.

Now one attempt to make the replay attack not possible is to use a different version id in the block. But that fails immediate as the transactions are still all the same. OK, so lets change the version number in the transaction. This is a bit better, but the problem is that it only works in one direction. The lower version number would be accepted by the new chain.

Of course an entirely new crypto signing can be used to create mutually exclusive signatures, but that involves a lot of code, maybe the size changes, and the ripple effect could be that blockexplorers and many other blockchain cognizant software just wont work. So in addition to wanting signatures to be mutually exclusive to prevent replay attack, we want to be as backward compatible as possible. Ideally fully backward compatible.

Now some devs say this is just impossible. I like to do the impossible :)

Here is the solution that doesnt change the blockchain at all, but yet creates mutually exclusive signatures. Signatures from one chain only work on that chain and since it is incompatible at the signature level, any transaction that has a signature will be invalid on the other chain, which prevents the replay attack.

In order to fully understand the solution, the following needs to be understood: http://bitcoinfactswiki.github.io/OP_CHECKSIG/

If you take a look at that page, be careful, it has been known to explode some people's brains. It is a summary and the actual signing code needs to deal with even more details and it is quite tricky and making any code change creates risk of making something that just doesnt work. That is why I like my solution which doesnt need to change the code!

The key is:

SIGHASH_ALL 0x00000001

SIGHASH_NONE 0x00000002

SIGHASH_SINGLE 0x00000003

SIGHASH_ANYONECANPAY 0x00000080

These are the four SIGHASH types that are made part of the data that is signed and the lowest byte ends up in the blockchain. However, the upper three bytes affect the data that is signed, but it doesnt end up in the blockchain. So my solution is to change the above constants to:

SIGHASH_ALL 0x77700001

SIGHASH_NONE 0x77700002

SIGHASH_SINGLE 0x77700003

SIGHASH_ANYONECANPAY 0x77700080

This fix is just as easy to do in JS: https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/src/transaction.js#L17

Transaction.SIGHASH_ALL = 0x01

Transaction.SIGHASH_NONE = 0x02

Transaction.SIGHASH_SINGLE = 0x03

Transaction.SIGHASH_ANYONECANPAY = 0x80

So by changing the upper bits it changes the SHA256 hash of the tx that is signed, but all the signing logic stays the same, which means no code changes, but yet the signatures are mutually exclusive. No replay attack

There is the very rare transaction that can be spent without any signatures for any of the inputs. If there is no signature, then a replay attack is possible just for these offbeat transactions. But I estimate that the percentage of transactions that dont require any signatures is very low.

Another edge case are transactions that are not in the original blockchain. It is probably obvious, but any transaction that is not confirmed in the original blockchain cant make it into the spinoff chain. This means anything in the mempool wont happen in the new chain and would need to be signed again if the transaction is desired to happen on both chains. Since unconfirmed transactions with existing signatures, like nLockTime, are not confirmed, they wont be valid in the new chain.

However, with an estimated 95%+ (might even be 99%+) of existing transactions retained, this is a very practical way to do a hardfork that arguably has the smallest footprint for a solution that creates mutually exclusive signatures

James

P.S. Still need to verify that SIGHASH_ANYONECANPAY is not used as a bitmask, if it is, then a slight modification is needed, but that is a very rare usage and might not even have signatures in the tx