Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
2 contributors

Users who have contributed to this file

@thephez @nmarley
253 lines (198 sloc) 12.1 KB
  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

  1. Abstract
  2. Motivation
  3. Prior Work
  4. Making all transactions instant
  5. Used LLMQ type
  6. Eligible transactions for InstantSend
  7. Locking Transaction Inputs
  8. Finalization and creation of ISLOCK messages
  9. Detecting and handling double-spend attempts
  10. Conflicts between ChainLocks and InstantSend
  11. Retroactive signing of transactions in blocks
  12. Dangling Partial Locks
  13. Persistence of InstantSend locks
  14. 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

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 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 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 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

You can’t perform that action at this time.