dips/dip-0010.md
UdjinM6 41fc2c9413 A few (mostly trivial) updates (#54)
* Drop quorumMinMemberAge from LLMQ init phase

* Clarify that "hash" is "SHA256" in multiple cases

* Switch few quotes to apostrophes
2019-11-11 07:59:14 -05:00

252 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<pre>
DIP: 0010
Title: LLMQ InstantSend
Author(s): Alexander Block
Special-Thanks:
Status: Final
Type: Standard
Created: 2019-05-16
License: MIT License
</pre>
## Table of Contents
1. [Abstract](#abstract)
1. [Motivation](#motivation)
1. [Prior Work](#prior-work)
1. [Making all transactions instant](#making-all-transactions-instant)
1. [Used LLMQ type](#used-llmq-type)
1. [Eligible transactions for InstantSend](#eligible-transactions-for-instantsend)
1. [Locking Transaction Inputs](#locking-transaction-inputs)
1. [Finalization and creation of ISLOCK messages](#finalization-and-creation-of-islock-messages)
1. [Detecting and handling double-spend attempts](#detecting-and-handling-double-spend-attempts)
1. [Conflicts between ChainLocks and InstantSend](#conflicts-between-chainlocks-and-instantsend)
1. [Retroactive signing of transactions in blocks](#retroactive-signing-of-transactions-in-blocks)
1. [Dangling Partial Locks](#dangling-partial-locks)
1. [Persistence of InstantSend locks](#persistence-of-instantsend-locks)
1. [Copyright](#copyright)
## Abstract
This DIP defines a new implementation for InstantSend based on DIP0006 LLMQs
and DIP0007 LLMQ Signing Requests/Sessions.
## Motivation
InstantSend is a feature to allow instant confirmations of payments. It works
by locking transaction inputs through masternode quorums. It has been present
in Dash for a few years and been proven to work. Nevertheless, there are some
limitations which could theoretically be fixed in the old system. However,
fixing these limits in the old system (i.e. before LLMQs) would have created
risks in terms of scalability and security.
With LLMQs, these limitations can be lifted to enable more scaling and a better
user experience.
## Prior work
- [DIP 0006: Long Living Masternode Quorums](https://github.com/dashpay/dips/blob/master/dip-0006.md)
- [DIP 0007: LLMQ Signing Requests / Sessions](https://github.com/dashpay/dips/blob/master/dip-0007.md)
- [DIP 0008: LLMQ based ChainLocks](https://github.com/dashpay/dips/blob/master/dip-0008.md)
- [Transaction Locking and masternode Consensus: A Mechanism for Mitigating Double Spending Attacks](https://github.com/dashpay/docs/blob/master/binary/Dash%20Whitepaper%20-%20Transaction%20Locking%20and%20Masternode%20Consensus.pdf)
## Making all transactions instant
LLMQ-based InstantSend allows all transactions to be treated as InstantSend
transactions. The old system differentiated transactions as InstantSend
transactions by using the P2P message `ix` instead of `tx`. In the new system,
such distinction is not required anymore as LLMQs will try to lock every valid
transaction by default. If an `ix` P2P message is received from an older
client, nodes should convert these to simple `tx` messages and treat them as
such (including processing and propagating as `tx`).
## Used LLMQ type
All signing sessions/requests involved in InstantSend must use the LLMQ_50_60
LLMQ type.
## Eligible transactions for InstantSend
When a transaction is received and successfully added to the mempool (which
means it also passes the usual validation checks), each masternode should check
if the transaction is eligible for InstantSend.
A transaction is eligible for InstantSend when each of its inputs is considered
confirmed. This is the case when the previous transaction referred to by the
input is confirmed with 6 blocks, confirmed through an older InstantSend lock
or the block containing it is ChainLocked. When checking the previous
transaction for an InstantSend lock, it is important to also do this on mempool
(non-mined) transactions. This allows chained InstantSend locking.
## Locking Transaction Inputs
When a transaction is eligible for InstantSend, each masternode should try to
initiate a signing request for each of the transactions inputs. The request
id is:
```
SHA256("inlock", prevTxHash, prevTxOut)
```
`"inlock"` is a static string, prepended with the length (6, as a compact int,
which is a single byte) of the string. The message hash of the signing request
is the txid/hash of the transaction to be locked.
The [DIP0007 LLMQ Signing Request/Sessions](https://github.com/dashpay/dips/blob/master/dip-0007.md)
subsystem should handle the signing and recovery of signatures afterwards. It
will also automatically determine if the local masternode has to sign a share
or if it should skip it. In case of conflicts between transactions which are
not fully locked yet, the DIP0007 subsystem will handle this as well by
skipping signing of conflicting inputs.
Each masternode should then wait for the recovered signatures for each input to
appear. When all recovered signatures have appeared, each masternode should
initiate the finalization phase.
## Finalization and creation of ISLOCK messages
When a masternode has observed all recovered signatures for each input of a
transaction, it should initiate the finalization and creation of an `ISLOCK`
message.
Finalization is simply another signing request, performed on a (potentially)
different LLMQ than used before. The request id of the new signing request is:
```
SHA256("islock", inputCount, prevTxHash1, prevTxOut1, prevTxHash2, prevTxOut2, ...)
```
`"islock"` is a static string, prepended with the length (6, as a compact int,
which is a single byte) of the string. `inputCount` is a compact int,
comparable to what is typically used in serialized arrays/vectors to serialize
the size. After `inputCount`, multiple pairs of <`prevTxHash`, `prevTxOut`>
follow, which are corresponding to the individual inputs of the transaction to
be locked. The message hash of the signing request is the txid/hash of the
transaction to be locked.
The LLMQ Signing Request/Sessions subsystem will handle this the same way as in
the previous section.
When a masternode receives the recovered signature for this signing request, it
should use the signature to create a new p2p message, which is the `ISLOCK`
message. It has the following structure:
| Field | Type | Size | Description |
|--|--|--|--|
| inputCount | compactSize uint | 1 - 9 | Number of inputs in the transaction |
| inputs | COutpoint[] | `inputCount` * 36 | Inputs of the transaction. COutpoint is a uint256 (hash of previous transaction) and a uint32 (output index) |
| txid | uint256 | 32 | txid/hash of the transaction |
| sig | BLSSig | 96 | Recovered signature from the signing request/session |
This p2p message should be propagated to all full nodes, including
non-masternodes. This is done using the `INV`/`GETDATA` subsystem. It should
also be propagated to SPV nodes if the transaction referred to by the txid
matches the bloom filter of the SPV node. Receiving nodes should verify the
message by checking the signature against the responsible LLMQs public key.
The responsible LLMQ can be determined by re-calculating the request id from
the data found in the `ISLOCK` message and then choosing the responsible LLMQ
with the help of the LLMQ Signing Requests/Sessions subsystem. If the `ISLOCK`
message is determined to be valid, it should be propagated further.
All full nodes and SPV nodes should only consider a transaction to be locked
when a valid `ISLOCK` for the transaction is available. The individual inputs
previously locked and their corresponding recovered signatures should not be
used when it comes to `ISLOCK` validation.
## Detecting and handling double-spend attempts
When an `ISLOCK` message is present, it can be used to easily detect
conflicting transactions which try to double spend the locked transaction
inputs. This is even possible when the original transaction is not present and
only the `ISLOCK` message is present (which might be the case due to the
first-seen rule).
Each time an `ISLOCK` message is received, nodes should check the mempool and
recent non-ChainLocked blocks for conflicting transactions. A transaction is
considered conflicting when it spends an outpoint found in the `ISLOCK` message
but its txid/hash does not match the txid from the `ISLOCK` message.
The same checks should be performed when a transaction is received before the
corresponding (or conflicting) `ISLOCKs` are received. This includes
transactions received through normal transaction propagation or through mined
blocks.
If a conflict is found in the mempool, the conflicting transaction
should be removed from the mempool. The originally locked transaction will
naturally not be present in this situation, as the first-seen rule would have
already filtered it. Thus the transaction referenced in the `ISLOCK` message
should be re-requested from one of the nodes that had previously announced it.
If a conflict is found in a recently mined block, conflict resolution depends
on the ChainLock state of the block. If the block is not ChainLocked, the whole
block must be invalidated. This might result in a reorganization of the chain
and re-acceptance of non-conflicting transactions in the local mempool. If the
block (or one of its descendants) has a ChainLock, the `ISLOCK` (and all
chained descendants) must be ignored and pruned from memory (including
persistent memory).
## Conflicts between ChainLocks and InstantSend
The latter case with a conflicting block receiving a ChainLock is very unlikely
to happen. The ChainLocks system usually does not try to lock blocks which
contain non-InstantSend locked transactions. This means that even if a miner
manages to include a conflicting TX into a block, masternodes following normal
consensus rules will not create a ChainLock for this block. Consequently the
block would be invalidated on all nodes when the `ISLOCK` appears.
Only in the case of an attack, where an attacker managed to get control over
large parts of the masternode network, would this situation becomes a
possibility. In this situation, ChainLocks have a higher priority when it comes
to consensus. Please see [DIP0008 - ChainLocks](https://github.com/dashpay/dips/blob/master/dip-0008.md)
for more details.
## Retroactive signing of transactions in blocks
When a block is received that contains transactions which were previously
unknown to the receiving masternode, the masternode should try to lock these
transactions the same way as those received through normal transaction
propagation.
In most cases, this will be a no-op as the `ISLOCK` will already be present
locally and thus the locking attempt can be skipped. Retroactive signing
however becomes important when miners include transactions in new blocks which
are not known by the remainder of the network. As it is desirable to lock all
transactions, retroactive signing ensures that even these transactions get
locked.
This also allows the ChainLocks system to retroactively lock blocks which
include unsafe transactions, as these transactions will become safe after a
short delay.
## Dangling Partial Locks
In rare cases, a transaction input may not be locked. This could result in a
transaction where only some of the inputs are successfully locked.
This would happen if a LLMQ becomes inactive in the middle of a signing session
due to a fresh LLMQ pushing an old LLMQ out of the active list. In this case,
some members of the old LLMQ may not receive the transaction in a timely manner
and thus do not perform the threshold signing. It might also happen if a LLMQ
becomes dysfunctional, e.g. due to too many members being offline or
unresponsive.
In the initial implementation, we will ignore this unlikely scenario and simply
consider the transaction as not locked, which also means that the `ISLOCK`
message is never created. Such a transaction reverts to being a normal
transaction and will be mined with a delay of a few minutes. See
["Safe Transactions” in DIP0008 - ChainLocks](https://github.com/dashpay/dips/blob/master/dip-0008.md#safe-transactions)
for more details.
## Persistence of InstantSend locks
An InstantSend lock should be persistent until the corresponding transaction is
mined on-chain and either confirmed by 24 blocks or a ChainLock. InstantSend
locks for non-mined transaction should never expire or time out.
Persistency is meant to be persistent on-disk and able to survive restarts of
the node. Storing InstantSend locks in RAM only is not enough.
## Copyright
Copyright (c) 2019 Dash Core Group, Inc. [Licensed under the MIT
License](https://opensource.org/licenses/MIT)