7 - LLMQ Signing Requests / Sessions#

  DIP: 0007
  Title: LLMQ Signing Requests / Sessions
  Author(s):  Alexander Block
  Special-Thanks: Cofresi, Darren Tapp, Thephez, Samuel Westrich
  Comments-Summary: No comments yet.
  Status: Final
  Type: Standard
  Created: 2018-09-07
  License: MIT License

Table of Contents#

  1. Abstract

  2. Motivation

  3. Prior Work

  4. Signing request

  5. Signing session

  6. Choosing the active LLMQ to perform signing

  7. Validating recovered threshold signatures

  8. Propagating signature shares

    1. Recovered threshold signatures

  9. Internal high-level API

  10. Copyright

Abstract#

This DIP defines how signing requests and sessions are performed by activated DIP006 Long Living Masternode Quorums (LLMQs). A LLMQ is able to perform BLS threshold signing on arbitrary messages. If enough members have signed the same message, a valid recovered threshold signature can be created and propagated to the rest of the network. A successfully recovered threshold signature is strongly secure, as it is either created or not created with no in-between state that might result in different nodes seeing different/conflicting signing results.

Motivation#

We want to have a generalized method of signing arbitrary messages, which can be applied to all kinds of use cases. One use case is InstantSend, other use cases will come from new features in Evolution. We will only slightly handle InstantSend as an example in this DIP, more details can be found in the appropriate specialized DIPs.

All these use cases require unambiguous results from a signing request: It should either succeed or fail network-wide. It should not be possible to have two nodes in the network see conflicting messages with different valid signatures for the same request.

Prior work#

Signing request#

A signing request is identified by a deterministically calculable, unique id which we will refer to as requestId in the remainder of the document. It can, for example, be the hash of an InstantSend transaction’s input.

Initiation of a signing request happens independently per LLMQ member. When and how to initiate the request depends on the use case. For InstantSend, this would be directly after receiving and validating a transaction.

The message hash must be supplied along with the requestId. For InstantSend, the message hash would be the hash of the full transaction. The combination of quorumHash, requestId and message hash forms the signing session.

A signing request can only be initiated once and should never be performed multiple times by the same masternode. This in turn results in “one vote per member” on each individual signing request and makes it impossible to have conflicting recovered signatures for the same signing request.

As a result of a (non-conflicting) signing request, the member must create a threshold signature share. It does this by calculating SHA256(quorumHash, requestId, messageHash) and then signing the resulting hash with the members individual threshold secret key share. After signing, the signature share must be propagated to all LLMQ members (explained later).

Signing session#

A signing session is identified by the LLMQ’s quorumHash, signing request id and the message hash. If any member of the LLMQ receives enough threshold signature shares for a signing session, it can create the final recovered threshold signature from these shares. This recovered signature is propagated to the whole network (including non-members) and can then be verified by all nodes based on the LLMQs quorum public key.

If different members signed the same request with differing message hashes, only one of the signing sessions will create enough signature shares to reach the threshold and thus only one valid recovered signature can be created. If not enough signature shares for the same signing session can be collected, no valid recovered threshold signature will be created. In the InstantSend use case, this would indicate a failed attempt to double spend transaction inputs.

Choosing the active LLMQ to perform signing#

As multiple LLMQs are active at the same time and masternodes might be members of multiple LLMQs as well, it’s important that all masternodes agree on which LLMQ should service the signing request. Otherwise it would be impossible to collect enough signature shares for the same signing session and thus recovery of the threshold signature would never succeed.

To calculate which LLMQ is responsible for a signing request, each masternode should perform the following:

  1. Take the active LLMQ set from 8 blocks before the current chain tip. Active LLMQ sets are described in DIP6 - Long-Living Masternode Quorums section “Active LLMQ sets”.

  2. For each LLMQ of the active set, calculate SHA256(quorumType, quorumHash, requestId)

  3. Sort the list of LLMQs based on the result of step 2 in ascending order.

  4. Use the first entry of the sorted list as the LLMQ to perform the signing request

After the responsible LLMQ is determined, the masternode should check if it is part of the chosen LLMQ. If it is not part of that LLMQ, it must completely skip the signing request.

Validating recovered threshold signatures#

Each node (including regular nodes) must verify the recovered signatures for each signing session before accepting and relaying them. This can be done by validating the recovered signature and SHA256(quorumHash, requestId, messageHash) against the LLMQ’s quorum public key.

If this succeeds, the signing request is considered successful. In the InstantSend example, this would mean that a single input of the transaction has been locked.

Propagating signature shares#

Propagation of signature shares is performed through the Intra-Quorum Communication mechanism described in DIP006. This means, that only the members of a LLMQ see and propagate signature shares belonging to this LLMQ. Watchers that connect and issue the qwatch message will also receive signature shares.

Propagation of signature shares however does NOT use the inventory system of Dash. Instead, signature shares are sent to other quorum members in a batched message. This is due to the overhead and latency in the inventory system that, while acceptable for other message types, is unsuitable for the low-latency requirement of the signature share system. Fortunately, signature shares can be batched in a way that keeps the overhead to a minimum compared to the inventory system (and might even reduce it). The most important advantage is the removal of the latency produced by the multiple round-trip (INV->GETDATA->MESSAGE) sequences, which would add up to unacceptable latencies when propagating hundreds of messages.

When a member has signed his own signature share or validated incoming signature shares, it must add these signature shares to a pending list of batched signature shares per other member in the deterministic connections (see DIP06, Intra-Quorum Connections) set. It should then periodically (e.g. every 100ms) send out all pending batches.

On receipt of a batch, each included share must be validated individually.

Validation includes:

  1. The quorumHash must belong to an activated LLMQ

  2. shareCount must not be larger than the quorumSize

  3. Each entry in shareMembers must be in the bounds of quorumSize

  4. No duplicate entries in shareMembers

  5. No duplicate entries in shareSigs

  6. The individual shares must validate to the corresponding public key shares of the corresponding members

Valid shares from the batched message must be relayed even if other shares are invalid. If an invalid share is encountered, the sending node must be banned even if other shares were valid.

If an individual share was already received from another member, verification and relaying can be skipped (as both were already done before). It is safe to check duplicate shares by hash as BLS signatures cannot differ for the same message.

The internal Dash message name is qbsigshares and the format of the message is:

Field

Type

Size

Description

quorumHash

uint256

32

The quorum identifier

id

uint256

32

The signing request id

messageHash

uint256

32

The SHA256 hash of the message

shareCount

compactSize uint

1-9

The number of signature shares in this batch

shareMembers

uint32[]

4 * shareCount

Member indexes of the shares in this batch

shareSigs

BLSSig[]

96 * shareCount

The signature shares

Recovered threshold signatures#

After a member of a LLMQ has received enough (>= quorumThreshold) shares for a signing session, it can create the final recovered threshold signature from the signature shares. If this succeeds, the recovered threshold signature must be relayed to the whole network (including non-members).

If a valid recovered threshold signature was already received before enough shares have been received, recovering can be skipped. The reason is that the custom-created recovered signature would always be identical to the received one, thanks to the uniqueness property of BLS signatures.

On receipt of a recovered signature, all nodes should perform the following verification:

  1. The quorumHash should belong to an active LLMQ

  2. The thresholdSig should validate against the LLMQ’s quorum public key and SHA256(quorumHash, id, messageHash).

Propagation of recovered signature shares utilizes the inventory system.

The internal Dash message name is qsigrec and the format of the message is:

Field

Type

Size

Description

quorumHash

uint256

32

The quorum identifier

id

uint256

32

The signing request id

messageHash

uint256

32

The SHA256 hash of the message

thresholdSig

BLSSig

96

The final recovered threshold signature

Internal high-level API#

Inside the Dash Core Node, we will implement a high-level API that can be leveraged by the individual use cases. The API will support the following operations:

void SignIfMember(uint256 id, uint256 msgHash, int activeLLMQs)#

Selects the correct LLMQ based on the last active LLMQs and performs the signing request if the caller is a member and only if it did not already sign another signing session for the same signing request.

void Sign(uint256 quorumHash, uint256 id, uint256 msgHash)#

Sign the given session if the caller is a member of the given quorum.

bool HasRecoveredSig(uint256 id, uint256 msgHash, int activeLLMQs)#

Returns true if a valid recovered threshold signature was received (or locally created) for the signing session and from the last active LLMQs. Please note that the activeLLMQs parameter must not necessarily be the same value as was previously given to SignIfMember. For some use cases it might be desirable to accept signatures from old/inactive LLMQs as well.

bool IsConflicting(uint256 id, uint256 msgHash)#

Returns true if any LLMQ created a valid recovered threshold signature for any signing session with a different message hash than the given one.

bool IsMajorityPossible(uint256 id, uint256 msgHash, int activeLLMQs)#

Returns true if a recovered signature has already been received (or locally created) or if it’s still possible for this to happen. This means, that it returns false if >= quorumThreshold members have already signed a different signing session for the same request.

uint256 GetMostSignedSession(uint256 id, int activeLLMQs)#

Returns the msgHash which was signed by most LLMQ members so far. Please note that the result is not final until HasRecoveredSig returns true.

These operations will also be made available through RPC APIs so that external applications can also use the LLMQ signing capabilities. This will especially be useful for Evolution, as non-core services (L2) can then simply call these RPCs and won’t have to deal with the internals.