githubEdit

🛡️TBLS thresholdsig transactions

Get to know how to use TBLS accounts in your DApps on KLY

Intro

Threshold signatures may seem more complex than the multi-signatures described earlier. And yet, with a large number of parties involved, it is recommended to use TBLS, since the size of such a transaction will be much smaller.

For example, if we have 100 parties that together manage a certain account and it was decided that the decision threshold is 60% (60 parties), then you will need to send 40 public keys in the afkVoters array.

At the same time, in TBLS, the identifier of the entire group of signers is a 48-byte root key and in order for the network to understand that the initially set threshold has been reached (it doesn’t matter whether 60 participants, 63, 87, or all 100 agree with the decision, the main thing is more than initialy defined threshold) you need to provide only 1 signature. By verifying it using the master key, the network will allow the group to complete the transaction.

Use case

There are 6 friends who want to share some cryptocurrency when they go to the festival. At the same time, there was an agreement that the costs agree with the majority. That is, 4 out of 6 friends must agree to a certain purchase or other type of expenditure. For this, the friends agreed that each of them would replenish the joint wallet with 500 units of KLY. Well, let's help them generate such a wallet and everything else for honest money management.

Step 1 - generation

So, consider the signature of the first function generateTBLS() It has the following parameters:

  • threshold - threshold. In our case, it will be equal to 4

  • myPubId - choose some initial numeric ID for yourself

  • pubKeysArr - an array of other identifiers. Our ID must also be included in this array

Let's generate

Output:

Cool, now let's figure out what we have:

  • verificationVector is a verification vector that we have to send to all other 5 friends

  • secretShares are something that we have to send to friends(1 share for 1 friend). That is, the first share(secretShares[0]) goes to the friend with ID=1 (this is you), the second share (secretShares[1]) goes to the second friend - the one with ID=2, and so on. There are 6 of them here, so it is clear that everyone has a share

  • id is our ID that we will now use for further generations

Step 2 - friends do the same

A similar procedure is performed locally by yourself and 5 of your friends. Each of them will receive its ID, each will receive an array of 6 shares and a verification vector.

circle-info

An important point is to agree a set of identifiers with your friends

I mean that the other 6 friends must also use the same ID array. And your ID will be the same for all friends. That is:

  • For a friend with ID=1 - takes share 1 for himself

  • For a friend with ID=2 - takes layer 2 for himself

And so on

Step 3 - get the root public key

At this stage, friends will receive 48-bytes hex encoded address in the KLY network to which they can send funds for shared use. For this purpose, they exchange verification vectors. When you receive 5 verification vectors from 5 other friends, then add your sixth one and call the deriveGroupPubTBLS() function. But before we break it down, let's create some imaginary dataset with the data of the other 5 friends. So, let it look like this:

We call the public key generation function of our group. In this case, we equate the concept of public key/network address. In any case, the address can be obtained from the public key. So, we have:

Step 4 - verify the shares

Can we now deposit funds into the wallet

8aae5ae3b51a6f4bba62f64ab44b2135339831f662f8ef9e004bffb1458faa045f2c9a640acb466c5c35e2c9af757ac7fad74e3865b8527452619236822f9 797

and be sure that everything is OK? No, because you need to check the validity of the shares first to omit situation when maulicious actors(your "friends") can control this wallet without you. This is a VSS(Verifiable Secret Sharing) element - we need to make sure that each friend sent us a valid share. For this, there is a verifyShareTBLS() function in the module. Let's consider its parameters

  • hexMyId - it's our ID from step 1

  • hexSomeSignerSecretKeyContribution - it's share that we're going to verify(share from friend X).

  • hexSomeSignerVerificationVector - it's verification vector by friend X

Ok, let's verify. From the friend 1 point orf view:

Let's say we send the share to friend 4. So friend 4 will have the following:

Ok, let's verify

Now imagine that we change a single byte of share(last a becomes b):

triangle-exclamation

Only if there are no violations - friends can safely deposit currency into the wallet

8aae5ae3b51a6f4bba62f64ab44b2135339831f662f8ef9e004bffb1458faa045f2c9a640acb466c5c35e2c9af757ac7fad74e3865b85274526 19236822f9797

And note, there will only be this public key in the blockchain - without any additional settings or conditions. The key size will be similar even if there are 20 or 100 signatory participants

Step 5 - generate partial signatures

Ok, let's move on to the moment when the friends finally decided to make a purchase. The worst possible scenario is when 4 out of 6 friends agree to the purchase and 2 of them do not. Nevertheless, 4 is a sufficient threshold and each of the 4 friends must generate a partial signature, in order to then combine them and send them to the blockchain as cryptographic proof that the threshold has been reached and the network can allow spending on this wallet. To do this, each of the 4 friends must generate a partial signature and share it with the other 3 friends.

It is worth noting that the threshold T indicates the minimum value of the signatories. In our example, we consider just such a case. However, funds can be spent even when 5 or all 6 agree with the decision

So, to generate a partial signature, we will use the signTBLS() function. Let's consider its parameters:

  • hexMyId - it's our ID from step 1

  • sharedPayload - it's array like this:

  • message - it's message that we need to sign(transaction hash)

Let's generate partial signatures for 4 friends

Step 6 - aggregation of partial signatures

Now, having 4 partial signatures, we can get a master signature. Later, with the message and master public key will be able to verify this signature and make sure it's valid and wished threshold was reached. It's time for the buildSignature() function. Traditionally, let's consider the parameters:

  • signaturesArray - array like

Execute the function

Received master signature with the value

46c1f011e7d7039bb77a638e60e7e1c3acbfb3124ec2b06b918f1fd5d4a0b39c

Step 7 - verification of group signature using the group public key

So, at the previous stage, the signature of the group was obtained. Even earlier, we received the public key of the group - this is our address in the KLY network:

8aae5ae3b51a6f4bba62f64ab44b2135339831f662f8ef9e004bffb1458faa045f2c9a640acb466c5c35e2c9af757ac7fad74e3865b85274526192 36822f9797

And only one what the network needs to do to accept a transaction from friends is simply to make such a check:

circle-check

Now, let's build the transactions

TBLS => Ed25519 transaction

Output:

TBLS => BLS(multisig address) transaction

circle-info

The same for TBLs but remember about rev_t for new account

Tx structure should be like this:

TBLS => TBLS(thresholdsig address) transaction

Here you just need to set the master pubkey of recipient. The snippet as for TBLS => Ed25519 transaction

TBLS => PostQuantum(Dilithium/BLISS) transaction

See Ed25519 => PostQuantum(Dilithium/BLISS) transaction and BLS => PostQuantum(Dilithium/BLISS) transaction

Last updated