🛡️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 4myPubId- choose some initial numeric ID for yourselfpubKeysArr- 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:
verificationVectoris a verification vector that we have to send to all other 5 friendssecretSharesare 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 shareidis 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.
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 1hexSomeSignerSecretKeyContribution- 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):
A SIMILAR PROCEDURE IS CARRIED OUT BY EACH OF THE FRIENDS ON EACH SHARE!
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 1sharedPayload- 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:
We hope that such a step-by-step example where we describe generation and exchange of the necessary data will help developers in creating their products in the KLY ecosystem
Now, let's build the transactions
TBLS => Ed25519 transaction
Output:
TBLS => BLS(multisig address) transaction
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