Skip to content
Permalink
main
Switch branches/tags
Go to file
 
 
Cannot retrieve contributors at this time
LIP: 0017
Title: Make multisignature accounts more flexible, prevent spamming, and prevent signature mutability
Author: Andreas Kendziorra <andreas.kendziorra@lightcurve.io>
Discussions-To: https://research.lisk.com/t/make-multisignature-accounts-more-flexible-prevent-spamming-and-make-signature-sets-immutable/186
Status: Active
Type: Standards Track
Created: 2019-08-09
Updated: 2021-09-07
Requires: 0015

Abstract

This LIP proposes some improvements for the multisignature system in Lisk. The first improvement is that only valid transactions from multisignature accounts, i.e. transactions with all required signatures, are accepted and allowed to be forwarded by nodes. This will prevent spamming of the network and the transaction pool. Next, the settings for the account rules become more flexible. This allows, for example, to create accounts that require signatures for m arbitrary public keys from a set of n public keys without making the signature for any public key mandatory. Moreover, the upper bound on the number of participating keys is increased to 64 and the set of signatures of a transaction included in a block becomes immutable. Lastly, we remove the concept of second signature and convert all accounts with second signatures to multisignature accounts. The rules for existing multisignature accounts do not change with this proposal.

Copyright

This LIP is licensed under the Creative Commons Zero 1.0 Universal.

Motivation

The current multisignature system in Lisk has several shortcomings:

  1. It is possible to spam the network and the transaction pool of the nodes by sending transactions from multisignature accounts without all required signatures. Both sending the transactions and sending the signatures is free as long as the transaction does not receive the required amount of signatures. This is possible in the current protocol because:

    • The transactions from multisignature accounts are broadcast by nodes even if they do not contain the required signatures.
    • The signatures for a transaction from a multisignature account are collected by the nodes after the transaction was send to the network.

    The queue for transactions from multisignature accounts in a transaction pool could also be easily flooded without any malicious acting, because pending transactions from multisignature accounts can be very large and they can pend for up to 72 hours. Whenever the limit of the queue is reached due to too many pending transactions, all newly arriving transactions from multisignature accounts get rejected.

  2. There is little flexibility for the account rules. More precisely:

    • A signature by the key pair that registered the multisignature account is always mandatory for a transaction.
    • A signature by a specific key pair from the keys group cannot be made mandatory (unless it is mandatory for every key in the keys group).

    This prevents, for example, creating an account shared among several users in which everyone has equal rights. Also, it is not possible for a single user to create an account that requires signatures for two out of three (or more) public keys. This would increase the security of an account but permits to lose one key without losing access to the account.

  3. Users that register a multisignature account need a second key pair/passphrase if they want to have a personal account as well. In other words, the account creators cannot use a single key pair for their personal account and for the multisignature accounts they create.

  4. The number of keys participating in the account is upper bounded by 16.

  5. The set of signatures for a transaction from a multisignature account is mutable. There is no way to prove that a specific set of signatures was used during the transaction verification and during the inclusion into a block. This is because the signatures are neither included into the transactionID computation nor in the payload hash computation of a block.

Rationale

Prevent Spamming

An easy solution to prevent spamming in the network and in the transaction pools is to reject every transaction that does not contain the required signatures. In other words, transactions from multisignature accounts are treated as every other transaction: if they are not valid, they are rejected and not forwarded.

Besides removing the risk of spamming, this approach has the following advantages:

  • A transaction pool can treat a transaction from a multisignature account like any other transaction. Thus, there is no extra queue required anymore.
  • Multisignature accounts do not require the lifetime property anymore. This simplifies the registration process of multisignature accounts and allows an unlimited time span between transaction creation and signing.

Facilitate the Collection of Signatures

When signatures have to be collected from several users before sending a transaction to the network, some supporting functionalities could improve the user experience significantly. For example, a centralized service could be created (e.g. run by the Lisk Foundation or a third party) at which one can upload an unsigned transaction and collect the signatures. The signers could get the transaction object from the server, sign it locally and send the signature to the service. An integration of this service into wallets (e.g., Lisk Hub and Lisk Mobile) is possible as well.

Increase Flexibility of Account Rules

When increasing the flexibility for the account rules, it has to be ensured that the rules for the existing multisignature accounts do not change. Therefore, we propose that each multisignature account has a set of mandatory keys, a set of optional keys and a numberOfSignatures property which defines how many signatures a transaction from this account requires (the reason to not call it min property as in the current protocol is explained below). For a transaction to be valid, one signature for each mandatory key and k signatures for k distinct optional keys are required, where k equals the numberOfSignatures value of the account minus the number of mandatory keys. An account should be allowed to have only optional or only mandatory keys. For example, an m-out-of-n multisignature account has n optional keys, no mandatory keys and the numberOfSignatures value equals m. Existing multisignature accounts get converted to a multisignature account with exactly one mandatory key (the key that registered the account), some optional keys (all keys specified in keysgroup) and the numberOfSignatures value equals the min value. If an existing multisignature account is additionally registered as a second signature account, then it gets converted into a multisignature according to the rules below.

Adding Sender Address to Transaction Objects

With the flexibility described above, it is possible that the key pair that registered the multisignature account, and from which the address of the account is derived, does not sign a transaction. In order to deduce the sender account (or rather the sender address) from a transaction object originating from a multisignature account, we propose that also for transactions from multisignature accounts the senderPublicKey property has to be provided. The value of this property has to be the public key used for registering the multisignature account.

Key Weights

One could increase the flexibility for the account rules even more by using weights for the keys. That means, one associates a weight to each key of the keys group and defines a weight threshold. Then, a transaction requires a set of signatures such that the sum of the weights of the corresponding keys is larger than the weight threshold. Such a system is used, for instance, in BitShares. However, this approach lacks simplicity and the proposed system is considered to be a better tradeoff between flexibility and simplicity while preserving the rules for the existing accounts.

Allow the Usage of a Single Key Pair for Each User

We propose to not require that the public key that registered the multisignature account belongs to the group of keys of the multisignature account, neither to the group of mandatory nor to the group of optional keys. This allows the following: Let U={U1, ..., Un} be a set of users where each user possesses a key pair for their “personal” account. Moreover, the users in U want to create a shared multisignature account. However, each user in U wants to add its existing public key to the multisignature account because nobody wants to handle a second private key (or passphrase). Then, one of the users, say Uk, creates a temporary key pair, KPtemp, transfers some Lisk tokens to the corresponding address of KPtemp and registers this account as a multisignature account. During the registration, Uk adds the public keys for every user in U to the keys group, including its own public key (which is not the one from KPtemp). Once the registration is included in the blockchain, Uk can “forget” the private key of KPtemp (or the corresponding passphrase) since this private key cannot be used anymore to access the funds. Note that the participants still need to "remember" the public key of KPtemp to issue transactions (see above). But it does not need to be kept secret, and front end tools like Lisk Hub could do this job for the user.

Multisignature Accounts as Delegate Accounts

In the current protocol, it is possible that an account is both a multisignature account and a delegate account. Since such accounts exist, this flexibility has to be kept. We propose that if a multisignature account is registered to be a delegate account (or if a delegate account is registered to be a multisignature account), then the public key that performed the multisignature registration is the delegate key. Block signatures have to be done by this key pair, even if the delegate key does not belong to the keys group. Note that in the example above, the user Uk should not forget the key pair KPtemp if the account is supposed to be a delegate account because KPtemp is needed for signing blocks.

Increase Upper Bound on Keys Group

A small upper bound on the number keys for a multisignature account is useful in a static fee system to prevent users from including transactions of a huge size (due to a large number of signatures) into the blockchain without paying a fee higher than for a transaction with a single signature. However, a small upper bound becomes superfluous if the following requirements are fulfilled:

  1. The fee of a transaction is proportional to the number of signatures.
  2. The storage required to store a transaction on a node is proportional to the number of signatures.
  3. The number of computational steps to verify a transaction is proportional to the number of signatures.

In other words, if the fee for a transaction is proportional to the required computational and storage resources then there is no harm in allowing a high number of signatures. In practice, we cannot achieve exact proportionality, but linear dependencies which we consider to be sufficient.

Point 1 is achieved by a dynamic fee system in which the fee depends linearly on the size of a transaction, e.g., the one currently proposed for Lisk. Point 2 is already fulfilled in the current protocol.

To achieve point 3 as well, we propose to take into account the order of the arrays of public keys used in the multisignature registration. The signatures property of transactions originating from multisignature accounts must be an array with elements being either signatures, or empty byte buffers. For position i in signatures, the signature at position i must be valid with respect to the public key at position i in the array of public keys, i.e. the array of mandatory keys concatenated with the optional keys. There must be a signature for every mandatory key and the total number of signatures must equal numberOfSignatures. This allows the verification of all signatures by iterating only once through the list of signatures.

Note that users should not be required to create the signatures array themselves. This should be done by the tools used to create the transactions (either the wallet or the tools used for collecting the signatures).

There are, however, still some limitations that disallow arbitrarily large numbers of keys. The block size limit proposed in LIP 0002 is 15 kB. Hence, no transaction can be larger than 15 kB if LIP 0002 gets implemented. To keep a generous buffer for potential protocol changes in the future that introduce transactions with a very large payload, we propose a rather conservative limit of at most 64 keys per account. 64 signatures require 4.1 kB of size which leaves more than 10 kB for the maximum payload size of a transaction. Note that this upper limit can easily be increased in the future by simply changing a constant.

Making the Signatures Set Immutable

The byte array of a transaction used for computing the transactionID and used in the payload hash of a block is not containing the value of the signatures property in the current protocol. This makes the set of signatures for a transaction from a multisignature account mutable. To avoid any mutable data contained in transactions, we propose to add the signatures to the byte array.

This has, however, the consequence that a transaction from a multisignature account can have several transactionsIDs. For example, if there is a transaction, tx, from a 2-out-of-3 multisignature account with 3 signatures, then 4 different transactionIDs are possible (3 transactionIDs for picking 2 out of 3 signatures and one for taking all signatures). To avoid that this can be exploited, we require that LIP 0015 is implemented. LIP 0015 adds a uniqueness requirement for nonces. If LIP 0015 is not implemented, then it will be possible to replay a transaction. For example, the transaction tx could be replayed 3 times with the given signature set (in fact, a cosigner can replay tx almost arbitrarily often since signatures are not unique for a given key pair and message).

Moreover, transactionIDs are used to identify unconfirmed transactions (transaction that are not included in the blockchain) within the peer-to-peer network. Therefore, it is desirable to use a single transactionID for the same transaction. To mitigate the risk that the same transaction is broadcast with different sets of signatures in the network, and therefore different transactionIDs, we propose to reject a transaction that has more signatures than required. Otherwise, it could happen that some nodes modify the transaction object by reducing the signature set to a minimum and distributing the same transaction with a new transactionID in the network. Especially active delegates could be tempted to do so, since they have an incentive to reduce the number of signatures once a dynamic fee system is enabled (currently planned and proposed in LIP 0013). For this reason, we use the name numberOfSignatures instead of min in the proposed protocol.

Accounts with Second Passphrase

An account that has registered a second passphrase has the same access rules as a multisignature account with two mandatory keys and numberOfSignatures=2, i.e. two signatures are required to validate transactions from this account. Keeping a distinction between second signature accounts and multisignature accounts with two signatures only brings confusion without bringing additional security. Future maintenance of the Lisk code base is also made easier by removing the concept of second signature. For those reasons, the second signature registration transaction is disabled and all second signature accounts are converted to multisignature accounts with the same two keys.

The Lisk UI interface currently makes it very easy to register a second passphrase for your account. This feature can be kept, but the application needs to send a multisignature registration instead of a second signature registration. This multisignature registration must contain two mandatory keys: the original public key of the account and the public key derived from the newly created passphrase.

In the current protocol, it is allowed to do a second public key registration and a multisignature registration for the same account, and there are some accounts for which both registrations were done. We propose to convert existing accounts for which both registrations were done into a multisignature account with two mandatory keys (the original public key of the account and the second registered public key) and all keys from the keygroup as optional keys. Moreover, the numberOfSignatures value equals min+1.

Note that those conversions do not change the access rules of accounts.

Specification

Multisignature Account Registration

An account is converted into a multisignature account via a Multisignature Registration Transaction. This transaction has to be sent from the account that is supposed to be converted. An account can only be converted into a multisignature account once and Multisignature Registration Transactions from multisignature accounts are invalid.

Besides all mandatory properties of a transaction (type, senderPublicKey, etc.), the registration transaction needs the following:

  1. The asset property needs to contain the property mandatoryKeys. The value of this property is an array of pairwise distinct public keys. This array must be ordered lexicographically. Once the account is registered as a multisignature account, every outgoing transaction requires for every public key in mandatoryKeys a signature. The array may be empty.
  2. The asset property needs to contain the property optionalKeys. The value of this property is an array of pairwise distinct public keys. This array must be ordered lexicographically. Once the account is registered as a multisignature account, every outgoing transaction requires some signatures for some public keys in optionalKeys (the number of needed signatures depends on the numberOfSignatures property and may also be zero). The array may be empty.
  3. The sets of mandatory and optional keys, specified by mandatoryKeys and optionalKeys, must be disjoint; their union must have at least one element and at most 64 elements.
  4. The asset property needs to contain the numberOfSignatures property.
    • The value must be an integer between 1 and the total number of public keys of the account (sum of optional and mandatory keys).
    • The value must not be smaller than the length of the value of mandatoryKeys.
    • The value specifies how many signatures are needed for every outgoing transaction from the registered multisignature account, where signatures for mandatory and optional keys are counted.
  5. The signatures property of the transaction object is appended with signatures corresponding to the public keys contained in mandatoryKeys and optionalKeys. All public keys must have a corresponding signature. The signatures corresponding to mandatoryKeys are appended first, followed by those corresponding to optionalKeys, where the order of signatures is the same as the order of public keys in the respective array.

The properties lifetime and keysgroup are not required anymore.

The address of the multisignature account is the same as the one before the multisignature registration. That means, this address has to be used as the recipientID for balance transfers to this multisignature account.

Adding Sender Public Key to Transaction Objects

The senderPublicKey property has to be provided in all transactions. The value of this property is always the original public key of the account, i.e. the public key used for registering the multisignature account. This is the case even if this key does neither belong to the set of mandatory nor to the set of optional keys.

signatures Replaces signature

Transactions currently have multiple properties which can hold signatures: signature, signSignature and signatures. We remove the signature and signSignature properties and only keep the signatures property. This property is used to hold the signatures necessary to validate transactions. The items of this array are byte buffers of length 64, or empty byte buffers.

Transaction Creation and Verification

Transaction from Non-Multisignature Accounts

Transactions outgoing from non-multisignature accounts are validly signed if: The first element of signatures is a valid signature with respect to the serialized transaction (without signatures) and the public key value of senderPublicKey.

As described above, for Multisignature Registration Transaction, signatures also contains signatures corresponding to the public keys participating in the multisignature account. Specifically, signatures is an array composed of a signature corresponding to the senderPublicKey followed by signatures for the public keys in mandatoryKeys and optionalKeys, where the order of signatures is the same as the order of public keys in the respective array.

For other transactions, signatures contains exactly one element.

Transaction from Multisignature Accounts

Transactions outgoing from multisignature accounts are validly signed if the following logic returns true,

validity_from_multisignature_accounts(transaction):
    let signatures be transaction.signatures
    let message be the serialized transaction without signatures
    let mandatoryKeys and optionalKeys be the arrays of public keys defined when registering the multisignature account
    let numMandatoryKeys = length(mandatoryKeys)
    let numOptionalKeys = length(optionalKeys)

    for k from 0 to numMandatoryKeys-1
        if signatures[k] is NOT a valid signature with respect to message and mandatoryKeys[k]
            return false

    validOptionalSig = 0
    for k from 0 to numOptionalKeys-1
        if signatures[numMandatoryKeys + k] is a valid signature with respect to message and optionalKeys[k]
            validOptionalSig += 1
        else if signatures[numMandatoryKeys + k].byteLength != 0
            return false

    if numMandatorKeys + validOptionalSig == numberOfSignatures
        return true
    else
        return false

Therefore, for transactions outgoing from multisignature accounts, signatures must be of length length(mandatoryKeys) + length(optionalKeys).

Transaction Broadcasting

When a node receives a transaction, it first has to verify the transaction before broadcasting. This includes the steps from the section above for transactions from multisignature accounts. In particular, a node is not allowed to broadcast a transaction that does not have the appropriate signatures.

Transaction Serialization

Asset Property Serialization for Multisignature Registration Transactions

The asset property of a Multisignature Registration Transaction has to be serialized as by the function serialize_asset_for_multisignature_registration in the pseudocode below. This serialization is used to compute the byte array of a transaction for the signature computation, for the ID computation and to include the transaction into the payload hash of a block.

serialize_asset_for_multisignature_registration(asset):
    let byteBuffer be an empty byte buffer
    let numMandatoryKeys be the length of the array asset.mandatoryKeys
    append numMandatoryKeys as an 8-bit unsigned integer to byteBuffer
    for each key in asset.mandatoryKeys:
        append the big endian encoding of key to byteBuffer
    let numOptionalKeys be the length of the array asset.optionalKeys
    append numOptionalKeys as an 8-bit unsigned integer to byteBuffer
    for each key in asset.optionalKeys:
        append the big endian encoding of key to byteBuffer
    append asset.numberOfSignatures as an 8-bit unsigned integer to byteBuffer
    return byteBuffer

Note that both for each loops in serialize_asset_for_multisignature_registration have to process the array elements in the order given in the JSON arrays.

Signatures Serialization

The signatures property of a transaction has to be serialized as by the function serialize_signatures in the pseudocode below. This serialization is used to compute the byte array of a signed transaction for the ID computation and to include the transaction into the payload hash of a block. This serialization is always inserted at the end of the transaction byte buffer, i.e. the result of the pseudocode below is appended to the buffer to obtain the serialized transaction.

serialize_signatures(signatures):
    let byteBuffer be an empty byte buffer
    for each element in signatures
        if element is an empty byte buffer
            append 0x00 to byteBuffer
        if element is a signature
            append 0x01 to byteBuffer
            append element to byteBuffer
    return byteBuffer

Converting Existing Accounts

Existing multisignature accounts are converted in order to meet the specifications of multisignature accounts proposed above:

  • The lifetime property is removed and ignored.
  • The set of optional keys equals the set of keys previously listed in the keysgroup property ordered lexicographically.
  • If the account is NOT registered as a second signature account:
    • The set of mandatory keys contains exactly one public key, namely the one that registered the multisignature account.
    • The value of the numberOfSignatures property equals the value of the min property. The min property is removed and ignored.
  • If the account is registered as a second signature account:
    • The set of mandatory keys contains two public keys: the one that registered the multisignature account and the second registered public key. This set is ordered lexicographically.
    • The value of the numberOfSignatures property equals the value of the min property plus 1. The min property is removed and ignored.

Second signature accounts that have not been converted by the above procedure are converted into multisignature accounts as follow:

  • The set of mandatory keys contains two public keys: the one that registered the second signature account and the second registered public key. This set is ordered lexicographically.
  • The set of optional keys is empty.
  • The value of the numberOfSignatures property is set to 2.

Delegate Accounts

It is allowed that accounts are registered as delegate accounts and as multisignature accounts (this includes current second signature accounts). Blocks forged by such accounts are signed by the original public key of the account, i.e. the public key that was used for the multisignature account registration (or the second signature registration). This is the case even if this key does neither belong to the set of mandatory nor to the set of optional keys.

Second Signature Registrations

The Second Signature Registration Transaction is disabled. No account is considered as a second signature account anymore.

Backwards Compatibility

This proposal results in a hard fork because:

  • Every outgoing transaction from a multisignature account according to the proposed change will be rejected by nodes following the current protocol, and vice versa.
  • Multisignature Registration Transactions according to the proposed change will be rejected by nodes following the current protocol, and vice versa.
  • The second signature registration transaction is disabled and will be rejected by nodes following the proposed protocol.
  • Accounts that are second signature accounts in the current protocol will be converted to multisignature accounts in the proposed protocol. This introduces another incompatibility for transactions from such accounts.
  • The signature and signSignature properties of transactions are removed. All transactions now have their signatures in the signatures property.

Reference Implementation

Network Economics - Improve multi-signature solution