mirror of
https://github.com/seigler/dips
synced 2025-07-27 09:46:12 +00:00
parent
49435ff650
commit
a984d416e3
2 changed files with 250 additions and 0 deletions
|
@ -26,6 +26,7 @@ Number | Layer | Title | Owner | Type | Status
|
||||||
[6](dip-0006.md) | Consensus | Long-Living Masternode Quorums | Alexander Block | Standard | Active
|
[6](dip-0006.md) | Consensus | Long-Living Masternode Quorums | Alexander Block | Standard | Active
|
||||||
[7](dip-0007.md) | Consensus | LLMQ Signing Requests / Sessions | Alexander Block | Standard | Active
|
[7](dip-0007.md) | Consensus | LLMQ Signing Requests / Sessions | Alexander Block | Standard | Active
|
||||||
[8](dip-0008.md) | Consensus | ChainLocks | Alexander Block | Standard | Active
|
[8](dip-0008.md) | Consensus | ChainLocks | Alexander Block | Standard | Active
|
||||||
|
[10](dip-0010.md) | Consensus | LLMQ InstantSend | Alexander Block | Standard | Proposed
|
||||||
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
249
dip-0010.md
Normal file
249
dip-0010.md
Normal file
|
@ -0,0 +1,249 @@
|
||||||
|
<pre>
|
||||||
|
DIP: 0010
|
||||||
|
Title: LLMQ InstantSend
|
||||||
|
Author(s): Alexander Block
|
||||||
|
Special-Thanks:
|
||||||
|
Status: Proposed
|
||||||
|
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 transaction’s inputs. The request
|
||||||
|
id is:
|
||||||
|
|
||||||
|
hash("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:
|
||||||
|
|
||||||
|
hash("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 LLMQ’s 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)
|
Loading…
Add table
Add a link
Reference in a new issue