Forking tx sigs - technical discussion

freetrader

Moderator
Staff member
Dec 16, 2015
2,806
6,088
I'm starting to look at implementing a changeover of transaction signatures across a block-height hard fork.
The reason for doing so is that I want a fork chain to be independent (free) in terms of transactions - it should not be the case that issuing a transaction using the forked client creates a transaction that is seen as valid on other chains.

My first impression is that it is a non-trivial topic, and I feel I should solicit some input from the community here.
So here's what my plan is as of now. Please critique.

NOTE: I sort of mix block height (as in the fork height) and time (nLockTime). This abstracts that a timestamp will be assigned to the block that triggers the fork in the usual way.

When the fork triggers, new transactions are signed using a new transaction version (nVersion).
(to better understand the situation going forward: the block version produced by fork clients also changes at that point)

Going forward, transactions received via p2p will be rejected if their nLockTime >= the fork height and they do not carry the new tx version. If their nLockTime < the fork height and they have the old tx version, they will still be accepted (as long as the tx validates as usual).
[ NOTE: Based on a suggestion by J.Stolfi, I would amend the above to not allow old transactions indefinitely, even if their nLockTime < fork height. I would consider them valid only within a limited window after the fork, and I support Jorge's suggestions about warning the user about the upcoming fork changes to the transaction format.]

Fork clients shall mine blocks containing any transactions that are in the mempool.
This includes tx's prior to the fork (old tx version) and those after the fork which already have the new tx version. Gradually, a miners mempool will be emptied of old-versioned transactions. Miners/nodes will only be able to exchange old transactions that predate the fork within a limited time window after the fork has occurred [see the note above]. After that window has passed, old transactions would be discarded from the mempool.

Validation of transaction received in blocks must take into account that old tx versions are allowed for "old" transactions mined within a limited time window after the fork [see note above]. Transactions sporting new tx versions shall be rejected if their nLockTime predates the fork height.

It appears to me that this breaks nTimeLocked transactions which were meant to mature after the fork height, but I don't have a technical solution for that. The way I would deal with it is to allow sufficient time between release of the fork and trigger of the fork so that folks who have produced such transactions can have sufficient time to re-issue their transactions with the new tx version, so that they will be accepted as valid transactions after the fork.
This could be done at any time - even after the fork actually - as long as the signer hasn't thrown away the private keys they used to sign the original transaction. As for those who've thrown away their keys, I currently have no solution and could only say "I don't understand why you did that."


Please let me know if you see any obvious holes in this plan.
 
Last edited:
  • Like
Reactions: Bloomie

freetrader

Moderator
Staff member
Dec 16, 2015
2,806
6,088
I asked for and received comments from /u/jstolfi, which I am posting here (with my replies) to record all relevant remarks to the conversation here.


The first take-away from his post, for me at least, is that it is probably not such a good idea to allow old-style signed transactions indefinitely. So a time window around the fork would solve that problem quite easily. I even think as there is no known vulnerability, this window could be longer than he suggests - my feeling is probably a whole day / 144 blocks might be ok. I'd be interested to hear other views on that though.

NOTE: I am going to add Jorge's "safety window" in to the thread post proposal.
 
Last edited:

jl777

Active Member
Feb 26, 2016
279
345
I think it will have a smaller footprint to make new SIGHASH types. Just OR in an unused bit in the existing SIGHASH types:

#define SIGHASH_ALL 1
#define SIGHASH_NONE 2
#define SIGHASH_SINGLE 3
#define SIGHASH_ANYONECANPAY 0x80

So 0x40, 0x20, 0x10, 0x08, 0x04 are the possible bits that are open.

#define SIGHASH_CLASSICBIT 0x20
The advantage of making SIGHASH_ALL_CLASSIC (SIGHASH_ALL | SIGHASH_CLASSICBIT) is that I believe the existing timelocks wont need to be changed. Only very deep internal code even looks at the SIGHASH type bytes and it is much less likely that external software is looking at the signatures than it is looking at transaction type.

Assuming that unknown SIGHASH types are rejected as invalid in the existing code and as long as existing code isnt masking out the 3 active bits and ignoring the SIGHASH_CLASSICBIT, I think this is a safer way to go. Would be nice to get confirmation from gavin.
[doublepost=1470488836,1470487757][/doublepost]I also suggest reducing the 2016 blocks window for adjusting diff. Due to the expected rise in hashrate, the blocktimes will be very much faster than 10 minutes for 2016 blocks. Not sure what the right number is, but maybe 16 blocks at first and it can gradually increase back to 2016.

Also change the netmagic and good idea to change the default ports to allow for running both bitcoins on the same node.

Not sure how many other changes (other than user visible text) are needed
 

freetrader

Moderator
Staff member
Dec 16, 2015
2,806
6,088
I also suggest reducing the 2016 blocks window for adjusting diff.
Yes, this will be done on my fork definitely. It's getting a new diff algorithm.

The magic I'll need to look at, don't think I'm currently changing that.

Fork-time port changes will necessitate a little "dance" logic to close and re-open peer connections. Might be worth it - I'll let public testing decide if that's required.
 

jl777

Active Member
Feb 26, 2016
279
345
good point. the moment of hardfork, would need to disconnect and reconnect on the new port. that would be really cool! then it would seamlessly just change

also, I realized there is no need for any splitting script. just the standard client is needed. newBTC clients automatically create newBTC only outputs using whatever inputs they have. any tx it makes will get rejected (assuming that is verified) by oldBTC, so whatever utxo was spent in newBTC is still valid in oldBTC

As far as users go, if they install the newBTC wallet, they will find the pre-hardfork amount of coins in it that they can spend. Once the spend it, it is split. There does not seem to be a time limit by when they have to do this by

it seems quite a good solution, if I do say so myself