DIP: 0010 Title: LLMQ InstantSend Author(s): Alexander Block Special-Thanks: Status: Proposed Type: Standard Created: 2019-05-16 License: MIT License
Table of Contents
- Prior Work
- Making all transactions instant
- Used LLMQ type
- Eligible transactions for InstantSend
- Locking Transaction Inputs
- Finalization and creation of ISLOCK messages
- Detecting and handling double-spend attempts
- Conflicts between ChainLocks and InstantSend
- Retroactive signing of transactions in blocks
- Dangling Partial Locks
- Persistence of InstantSend locks
This DIP defines a new implementation for InstantSend based on DIP0006 LLMQs and DIP0007 LLMQ Signing Requests/Sessions.
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.
- DIP 0006: Long Living Masternode Quorums
- DIP 0007: LLMQ Signing Requests / Sessions
- DIP 0008: LLMQ based ChainLocks
- Transaction Locking and masternode Consensus: A Mechanism for Mitigating Double Spending Attacks
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
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 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
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 <
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
message. It has the following structure:
|inputCount||compactSize uint||1 - 9||Number of inputs in the transaction|
||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
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
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
Detecting and handling double-spend attempts
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
ISLOCK message is present (which might be the case due to the
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
but its txid/hash does not match the txid from the
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
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
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
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
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 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
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
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
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 (c) 2019 Dash Core Group, Inc. Licensed under the MIT License