Registering peg-out and migration transactions by hash

Abstract

A peg-out or a migration transaction is registered in the Bridge so that the funds it pays back to a federation (the change of a peg-out and the funds moved by a migration) can be spent again. Today that is done by submitting the whole serialized transaction to registerBtcTransaction, which is redundant: the Bridge built that transaction, so it already knows its outputs.

This RSKIP makes the Bridge records, under each release transaction’s hash, the outputs of the transaction that pays back to a federation. It then adds a method that registers such a transaction given its hash and a proof that it has enough confirmations.

Finally, this RSKIP also adds two events reporting the outputs credited to a federation, and the height of the Bitcoin block that included each of those outputs, which until now was always stored as zero.

Motivation

Registering a release transaction today means sending the Bridge back the whole BTC transaction, data the Bridge already has: it picked the inputs and built the outputs itself. The one thing it cannot know is whether the transaction reached Bitcoin and whether it is buried deep enough, which is what the proof is for.

Sending the whole raw transaction makes the registering RSK transaction grow with the Bitcoin transaction it carries, so the size and cost of a registration are only known once the release transaction exists. Replacing it with a 32-byte BTC transaction hash makes the size and cost more predictable.

Specification

The new method

function registerPegoutTransaction(bytes32 btcTxHash, int height, bytes calldata pmt) external;

The method exists only once RSKIP643 is active, and anyone may call it. height and pmt mean exactly as in registerBtcTransaction [1]. A caller that can build a proof for one can build it for the other.

btcTxHash is the BTC transaction hash without the witness. The call does not revert and changes no state when any of the following holds:

  1. The hash has already been registered.
  2. The height does not have enough confirmations, or the partial merkle tree does not prove the hash at that height.
  3. The hash is not in the index described below.

The third covers anything that is not a release transaction the Bridge created, including a peg-in or any other Bitcoin transaction.

Otherwise the Bridge credits the outputs recorded for that hash to the federation each one pays to, removes the entry from the index, and marks the hash as registered.

The call has a fixed cost, as registerBtcTransaction does.

The index

When the Bridge creates a release transaction it records, under that transaction’s hash, the outputs of that transaction that pay to the active or the retiring federation. For each one it stores what registering it needs: the amount, the position of the output in the transaction, and the script it pays to, which is what says which federation to credit. Outputs paying anywhere else are not recorded, since the federation never receives them.

The entry is keyed by the transaction hash, under the storage key federationsPendingBtcUTXOs+btcTxId, one entry per transaction, so an entry is read and removed without touching any other.

An entry is never empty. A release transaction that pays nothing back to a federation is not recorded, so its hash is not in the index and trying to register it does nothing, like any other hash the index does not hold.

This index is how the Bridge now recognizes its own release transactions, in place of the peg-out transaction index of RSKIP379 [2]. That one is still read during the transition described below.

The height of credited outputs

Every output credited to a federation is stored with the height of the Bitcoin block that confirmed it, so the Bridge can tell how old each of its outputs is. Until now that height was always recorded as zero.

The height is the height argument of the registration call, for both methods, which the proof ties to the block containing the transaction.

Events

Two events are added. Both are emitted whenever outputs are credited to a federation, whichever method did it, and only once RSKIP643 is active.

event utxos_registered(bytes32 indexed btcTxHash, bytes values, bytes outputIndexes, string federationBtcAddress);
event flyover_utxos_registered(bytes32 indexed btcTxHash, bytes values, bytes outputIndexes, string federationBtcAddress, bytes32 flyoverDerivationHash);

values holds the amounts of the credited outputs in satoshis and outputIndexes their positions in the transaction. Each field is the concatenation of those numbers encoded as Bitcoin compact size integers, each in its shortest form. Both fields hold the same count of numbers in the same order, so the nth amount belongs to the nth position. federationBtcAddress says which federation was credited.

The flyover variant is emitted when the outputs are credited to a flyover federation, which only a flyover peg-in does, and carries the derivation hash that identifies it.

Coexistence with registerBtcTransaction

registerBtcTransaction keeps working and keeps accepting every transaction type, including peg-outs and migrations.

From the activation on, every release transaction the Bridge creates goes into the new index, and the RSKIP379 index is no longer written. A release transaction created before the activation went into the RSKIP379 index instead, and it may still be waiting to be confirmed.

So up to a cutoff, registerBtcTransaction looks for the transaction in the new index first and in the RSKIP379 index second, and from the cutoff on it looks only in the new index. registerPegoutTransaction only ever looks in the new index.

A caller therefore does not have to tell the two apart while the window is open. registerBtcTransaction covers release transactions created on either side of the activation, so a caller that does not know which side a given transaction falls on can keep using it.

The cutoff is a Bitcoin block height, fixed per network, and applies to transactions confirmed at that height or above.

The cutoff MUST be high enough for every release transaction created before the activation to be confirmed and registered before it is reached. After the cutoff neither method recognizes one of those transactions, and the funds it pays back to a federation stay out of the Bridge’s accounting.

Rationale

Why the transaction hash is safe here, when RSKIP379 chose the input sighash instead. RSKIP379 avoided the transaction hash because signing changed it. That holds only while the signatures the federation adds are covered by the hash. Federations spend P2SH-P2WSH inputs, whose signatures live in the witness, and the hash used here excludes the witness, so it is the same before and after signing.

Why the outputs are recorded rather than recomputed. The Bridge could keep the whole transaction and read its outputs back at registration time. Recording only the outputs that pay back to a federation stores less, and stores exactly what registering needs.

Why outputs to other destinations are not recorded. They are the peg-out payments. The federation never receives them and can never spend them.

Why anyone may call it. As with registerBtcTransaction, the caller proves the transaction was confirmed and the Bridge decides what follows. A caller who submits a hash the Bridge is not expecting changes nothing.

Backwards compatibility

This change is a hard fork and therefore all full nodes must be updated.

registerBtcTransaction keeps its signature and its behavior for every transaction type. A caller that keeps using it for peg-outs and migrations continues to work.

The new method does not exist before the activation, so blocks from before the fork replay to the same state.

References

[1] RSKIP40: The two-way peg Bridge, where registerBtcTransaction and its parameters are defined

[2] RSKIP379: The peg-out and migration transaction index this one replaces, and the reasoning about transaction malleability that made it use an input sighash

[3] RSKIP305: The change that made federations spend P2SH-P2WSH inputs, which is what allows a transaction hash to be used here

[4] RSKIP378: The release transaction size limit, which bounds the transaction this method registers

[5] BIP141: Segregated witness, which moves the signatures out of the data the transaction hash covers

[6] BIP37: Partial merkle trees, the proof format this method takes

Copyright and related rights waived via CC0.