Programming Hotmoka
A tutorial on Hotmoka and smart contracts in Takamaka
8.8 Signatures and quantum-resistance
Hotmoka is agnostic wrt. the algorithm used for signing requests. This means that it is possible to deploy Hotmoka nodes that sign requests with distinct signature algorithms. Of course, if nodes must re-execute the same transactions, such as in the case of a blockchain, then all nodes of the blockchain must use the same algorithm for the transactions signed by each given account, or otherwise they will not be able to reach consensus. Yet, any fixed algorithm can be chosen for each account. In principle, it is even possible to use an algorithm that does not sign the transactions, if the identity of the callers of the transactions needn’t be verified. However, this might be sensible in private networks only.
The default signature algorithm used by a node is specified at construction time, as a configuration parameter. For instance, the code
var config = TendermintNodeConfigBuilders.defaults().build();
var consensus = TendermintConsensusConfigBuilders.defaults()
.setPublicKeyOfGamete(keys.getPublic())
.setInitialSupply(SUPPLY)
...
.setSignatureForRequests(SignatureAlgorithms.ed25519()) // this is the default
.build();
try (var node = TendermintNodes.init(config);
var initialized = TendermintInitializedNodes.of(node, consensus, takamakaCodePath)) {
...
}
starts a Tendermint-based blockchain node that uses the ed25519 signature algorithm as default signature algorithm for the requests. Requests sent to that node can be signed as follows:
// recover the algorithm used by the node SignatureAlgorithm signature = node.getConfig().getSignatureForRequests(); // create a key pair for that algorithm KeyPair keys = signature.getKeyPair(); // create a signer object with the private key of the key pair Signer<SignedTransactionRequest<?>> signer = signature.getSigner (keys.getPrivate(), SignedTransactionRequest<?>::toByteArrayWithoutSignature); // create an account having public key keys.getPublic() var account = .... // create a transaction request on behalf of the account ConstructorCallTransactionRequest request = TransactionRequests.constructorCall(signer, account, ...); // send the request to the node node.addConstructorCallTransaction(request);
In the example above, we have explicitly specified to use ed25519 as default signature algorithm. That is what is chosen if nothing is specified at configuration-time. Consequently, there is no need to specify that algorithm in the configuration object and that is why we never did it in the previous chapters. But it is possible to configure nodes with other default signature algorithms. For instance:
var consensus = TendermintConsensusConfigBuilders.defaults() .setPublicKeyOfGamete(keys.getPublic()) .setInitialSupply(SUPPLY) ... .setSignatureForRequests(SignatureAlgorithms.sha256dsa()) // this replaces the default .build();
configures a node that uses sha256dsa as default signature algorithm, while
var consensus = TendermintConsensusConfigBuilders.defaults() .setPublicKeyOfGamete(keys.getPublic()) .setInitialSupply(SUPPLY) ... .setSignatureForRequests(SignatureAlgorithms.empty()) .build();
configures a node that uses the empty signature as default signature algorithm; it is an algorithm that accepts all signatures, in practice disabling signature checking.
It is possible to specify a quantum-resistant signature algorithm as default, that is, one that belongs to a family of algorithms that are expected to be immune from attacks performed through a quantistic computer. For instance,
var consensus = TendermintConsensusConfigBuilders.defaults() .setPublicKeyOfGamete(keys.getPublic()) .setInitialSupply(SUPPLY) ... .setSignatureForRequests(SignatureAlgorithms.qtesla1()) .build();
configures a node that uses the quantum-resistant qtesla-p-I algorithm as default signature algorithm, while
var consensus = TendermintConsensusConfigBuilders.defaults() .setPublicKeyOfGamete(keys.getPublic()) .setInitialSupply(SUPPLY) ... .setSignatureForRequests(SignatureAlgorithms.qtesla3()) .build();
configures a node that uses the quantum-resistant qtesla-p-III algorithm as default signature algorithm, that is expected to be more resistant than qtesla-p-I but has larger signatures than qtesla-p-I.
Quantum-resistance is an important aspect of future-generation blockchains. However, at the time of this writing, a quantum attack is mainly a theoretical possibility, while the large size of quantum-resistant keys and signatures is already a reality and a node using a qtesla signature algorithm as default might exhaust the disk space of your computer very quickly. In practice, it is better to use a quantum-resistant signature algorithm only for a subset of the transactions, whose quantum-resistance is deemed important. Instead, one should use a lighter algorithm (such as the default ed25519) for all other transactions. This is possible because Hotmoka nodes allow one to mix transactions signed with distinct algorithms. Namely, one can use ed25519 as default algorithm, for all transactions signed by instances of ExternallyOwnedAccounts, with the exception of those transactions that are signed by instances of the interface AccountQTESLA1, such as the class ExternallyOwnedAccountQTESLA1, or of the interface AccountQTESLA3, such as the class ExternallyOwnedAccountQTESLA3, or of the interface AccountSHA256DSA, such as the class ExternallyOwnedAccountSHA256DSA (see Fig. 4.1). Namely, if the caller of a transaction is an AccountQTESLA1, then the request of the transaction must be signed with the qtesla-p-I algorithm. If the caller of a transaction is an AccountQTESLA3, then the request of the transaction must be signed with the qtesla-p-III algorithm. If the caller of a transaction is an AccountSHA256DSA, then the request of the transaction must be signed with the sha256dsa algorithm. If the caller of a transaction is an AccountED25519, then the request of the transaction must be signed with the ed25519 algorithm. In practice, this allows specific transactions to override the default signature algorithm for the node.
Let us for instance create an account that uses the default signature algorithm for the node. We charge its creation to the faucet of the node:
moka keys create --name=account7.pem --password
Enter value for --password (the password that will be needed later to use the key pair): orange The new key pair has been written into "account7.pem": * public key: 6tZrMNTwPpLC6ri5HbnVcmxvLYAfvuoyTGwhS7nuGg1M (ed25519, base58) * public key: V4AzVGGPWiV9MNIbQbKkdSMJETYHNBFwDLdpzlqnGUg= (ed25519, base64) * Tendermint-like address: C983D02A713E0BB14333BB80C712F0CCA0EE89C3
moka accounts create faucet 1000000000000 account7.pem --password --yes --uri ws://panarea.hotmoka.io:8001
Enter value for --password (the password of the key pair): orange A new account f5c7da4ff221f158f7e8050ac4f909dd04e07e28a0f27d0e533a8353b90ee4f8#0 has been created. Its key pair has been saved into the file "f5c7da4ff221f158f7e8050ac4f909dd04e07e28a0f27d0e533a8353b90ee4f8#0.pem". Gas consumption: * total: 60076 * for CPU: 15846 * for RAM: 7270 * for storage: 36960 * for penalty: 0 * price per unit: 1 pana * total price: 60076 panas
You can check the class of the new account with the moka objects show command:
moka objects show f5c7da4ff221f158f7e8050ac4f909dd04e07e28a0f27d0e533a8353b90ee4f8#0 --uri ws://panarea.hotmoka.io:8001
class io.takamaka.code.lang.ExternallyOwnedAccountED25519 (from jar installed at 68e90e2868751a3f22fd5fad3ceef377b91b3e52fdd30705f57505e22902ce40) io.takamaka.code.lang.Contract.balance:java.math.BigInteger = 1000000000000 io.takamaka.code.lang.ExternallyOwnedAccount.nonce:java.math.BigInteger = 0 io.takamaka.code.lang.ExternallyOwnedAccount.publicKey:java.lang.String = "V4AzVGGPWiV9MNIbQbKkdSMJETYHNBFwDLdpzlqnGUg="
As you can see, an account has been created, that uses the default ed25519 signature algorithm of the node. Assume that we want to create an account now, that always uses the sha256dsa signature algorithm instead, regardless of the default signature algorithm of the node. We can specify that when invoking the moka accounts create command:
moka keys create --name=account8.pem --password --signature=sha256dsa
Enter value for --password (the password that will be needed later to use the key pair): play The new key pair has been written into "account8.pem": * public key: 7sNSqvEbG3PWHnn8JYcB4wBJyGKHTbE9tRyTy84yfSYQnXJZoceNy8YFHUhjmsNS9wEV3FHhAncMk45eS4rcFSJgJ3AxKyf5xewfFxygnpSh61rPNbcVZDTfzBxmy5o4vyU9oXEBJgUKAhyLMhhiWspG4Tbxw5Ya4DFigJ4czDKGJ4JhHzXwV8a9u8SgMeNwj5wW57RP... (sha256dsa, base58) * public key: MIIDRjCCAjkGByqGSM44BAEwggIsAoIBAQCVR1z12T5ZbD/NHZAq3QL0J/XzxyEDE7tF+01bsuX+HL1njNS73YTJg2vh8xwHd3Ja62wvw4uF9IB2+na82BRsyJpvsvcG3XGYmMIIPcjYlvhAYuLJyU0TewVKjYCWrbjVGVI5juyoUqCvEt+D5HWqZdTsDDipVg1WYRhv... (sha256dsa, base64) * Tendermint-like address: 0BBD03BC39E77DC1A259BFE7059C1A3E532285AF
moka accounts create faucet 1000000000000 account8.pem --password --signature=sha256dsa --yes --uri ws://panarea.hotmoka.io:8001
Enter value for --password (the password of the key pair): play A new account 7e0508e1907f510ab83003fee1cde09168d81a727aa0f13408f1348c182821d5#0 has been created. Its key pair has been saved into the file "7e0508e1907f510ab83003fee1cde09168d81a727aa0f13408f1348c182821d5#0.pem". Gas consumption: * total: 232796 * for CPU: 15846 * for RAM: 7270 * for storage: 209680 * for penalty: 0 * price per unit: 1 pana * total price: 232796 panas
This creation has been more expensive than for the previous account, because the public key of the sha256dsa algorithm is much longer than that for the ed25519 algorithm. You can verify this with the moka objects show command:
moka objects show 7e0508e1907f510ab83003fee1cde09168d81a727aa0f13408f1348c182821d5#0 --uri ws://panarea.hotmoka.io:8001
class io.takamaka.code.lang.ExternallyOwnedAccountSHA256DSA (from jar installed at 68e90e2868751a3f22fd5fad3ceef377b91b3e52fdd30705f57505e22902ce40) io.takamaka.code.lang.Contract.balance:java.math.BigInteger = 1000000000000 io.takamaka.code.lang.ExternallyOwnedAccount.nonce:java.math.BigInteger = 0 io.takamaka.code.lang.ExternallyOwnedAccount.publicKey:java.lang.String = "MIIDRjCCAjkGByqGSM44BAEwggIsAoIBAQCVR1z12T5ZbD/NHZAq3QL0J/XzxyEDE7tF+01bsuX+HL1njNS73YTJg2vh8xwHd3Ja62wvw4uF9IB2+ na82BRsyJpvsvcG3XGYmMIIPcjYlvhAYuLJyU0TewVKjYCWrbjVGVI5juyoUqCvEt+D5HWqZdTsDDipVg1WYRhv+Yufyetg7uiwMDdrI2vHO+Os29dP1hwdJHX6MHe48IBGeIH/fhylb+4GbXlQat5R7btUQ6Vjkn28S6UgCGdGF1yIhZJevGTGFHkGdzSWmQy3FOxmcwTiYfruM7PL3wCODD+pBlDZfTkJySdb9KyG/8 s9A+bfyK2lk0JC3W07zKKkBssLAiEA+Bg2aLpfxbsGtZgebYt5XTC4l41Dyg7FcuN+CZOal3MCggEAQt67naWz2IzJVuCHh+w/Ogm7pfSLiJp0qvUxdKoPvn48W4/NelO+9WOw6YVgMolgqVF/QBTTMl/ Hlivx4Ek3DXbRMUp2E355Lz8NuFnQleSluTICTweezy7wnHl0UrB3DhNQeC7Vfd95SXnc7yPLlvGDBhllxOvJPJxxxWuSWVWnX5TMzxRJrEPVhtC+7kMlGwsihzSdaN4NFEQD8T6AL0FG2ILgV68ZtvYnXGZ2yPoOPKJxOjJX/Rsn0GOfaV40fY0c+ayBmibKmwTLDrm3sDWYjRW7rGUhKlUjnPx+WPrjjXJQq5mR/7 yXE0Al/ozgTEOZrZZWm+kaVG9JeGk8egOCAQUAAoIBAGkHc/7kmHQpGYZSGznUUGLdG6lr7rq5MZ98f+LhDFLoKOu8l79OKitKjGVi+qBRbAO49lJaFOHQ+ibzCZeWmBGMeHNTxtgMmdQPu71HSu/ep+N4mcdn5PP7SjulTlvrNwh7SXzVWTb7M5BC18AFvKXeQUj/Dw3/OihfzpC787ap+7GBGL4874B+6vrz0llZH+VA +dS4CRYoE8rn5U55iR2zcgsT9z+jM2yYLNJgdzFR8qDCfNN9avTRPWZKIHKyfxEemh+/KG9fzB6Lmc3qyLAaT6YkMyygdtSmC/cDWw5vcXsVwMynGV8gQCdxmG7aPV5zN8w4WzgqSBb7SVcywqg="
Note that the class of the account is ExternallyOwnedAccountSHA256DSA this time.
Let us create an account that uses the qtesla-p-I signature algorithm now:
moka keys create --name=account9.pem --password --signature=quantum1
Enter value for --password (the password that will be needed later to use the key pair): quantum1 The new key pair has been written into "account9.pem": * public key: 5Ku6q1BJpPtWJW9b2nnez9cxNWGTygAoS87jFvDSNAoig23guqbGfYPoQ8gXV2tNJjp1kRmG7U67LPTGCskoEAJD3ssi3veVXeeNcYvYh5Zr9zEmjMZkx12ppRZtFNFjLChyrYpuKG5qZDAKwSwcohP6udkzPNhyPYgYS4s3Yia5gKuauMGmqeePpMNWk1Jrcc5WF4hP... (qtesla1, base58) * public key: MII6NDANBgsrBgEEAYGwGgIECwOCOiEArh13IwC/ENlrOsW96RzSMviLFPVMgiTvwoD9Wo6LkywNltJ3CYWRFXHUH8dzNQLitK3iXTTRwarLVbSjoO1deCE0LgCLvYBDZxaYEXiKP5nHbtf4AugOotoaDCDRirlSnaF8rSr4vMq7ZoAlGpsJMUEUfZJhQQGUUqYvGrD7... (qtesla1, base64) * Tendermint-like address: F370131711D6C687A16723C7A8E6EE6B100EA02D
moka accounts create faucet 1000000000000 account9.pem --password --signature=qtesla1 --yes --uri ws://panarea.hotmoka.io:8001
Enter value for --password (the password of the key pair): quantum1 A new account 20185799a10c8c876078dd67ed9a337378531c05b40e14f4f235d51f44a3b303#0 has been created. Its key pair has been saved into the file "20185799a10c8c876078dd67ed9a337378531c05b40e14f4f235d51f44a3b303#0.pem". Gas consumption: * total: 3233036 * for CPU: 15846 * for RAM: 7270 * for storage: 3209920 * for penalty: 0 * price per unit: 1 pana * total price: 3233036 panas
The creation of this account has been still more expensive, since this kind of quantum-resistant keys are very large. Again, you can use the moka object show command to verify that it has class ExternallyOwnedAccountQTESLA1.
Finally, let us use the previous qtesla-p-I account to create a qtesla-p-III account:
moka keys create --name=account10.pem --password --signature=quantum3
Enter value for --password (the password that will be needed later to use the key pair): quantum3 The new key pair has been written into "account10.pem": * public key: 52z96jjVXCf3D9XkWHwT2kenXqU77HJXSKk5HuUQYqBJjrWpPQfxhFQLwtgYVP6atSAeqH9winukeMf9H7EdjxQJgRK3CJAoaTn14ZFZc4oDDFVrcgzteUNibf4FL7ySCTq4f7bBaMyWhhYwxtmViaaLCwVC5sqaYZD5s6JCW9ogRjuPoH3rwLDCY3XT7z7nwqQ4MTJN... (qtesla3, base58) * public key: MIKWNDANBgsrBgEEAYGwGgIEDAOCliEALLMi5THMi3DCfXQpopulMGps4x8hN6Jtx+V8V9EA1fJPQsa80AfdxS83B+QWIWWr1XZtkpHUs4eKtUC0tWz9V2YW9pV+K2ca7YdV5NN2APM14yhybB4dKmxISw0BD2n8HuLgJv89QAeDMQ9eqMtbNBuZupm4VlRXCBnMo9C6... (qtesla3, base64) * Tendermint-like address: CC8D8844917E5C370A0C2418568CEE9B78B52A82
moka accounts create 20185799a10c8c876078dd67ed9a337378531c05b40e14f4f235d51f44a3b303#0 100000 account10.pem --password --password-of-payer --signature=qtesla3 --yes --uri ws://panarea.hotmoka.io:8001
Enter value for --password (the password of the key pair): quantum3 Enter value for --password-of-payer (the password of the payer): quantum1 A new account 9945efb6933e4ecc291fc66de0f839c8e9170b670cf07e514220a154112ea755#0 has been created. Its key pair has been saved into the file "9945efb6933e4ecc291fc66de0f839c8e9170b670cf07e514220a154112ea755#0.pem". Gas consumption: * total: 8458589 * for CPU: 15403 * for RAM: 6546 * for storage: 8436640 * for penalty: 0 * price per unit: 1 pana * total price: 8458589 panas
Note, again, the extremely high gas cost of this creation.
Regardless of the kind of account, their use is always the same. The only difference is to use the right signature algorithm when signing a transaction, since it must match that of the caller account. This is automatic, if we use the moka tool. For instance, let us use our qtesla-p-I account to install the io-hotmoka-tutorial-examples-family-1.12.1.jar file from Sec. 3.4 in the node:
moka jars install 20185799a10c8c876078dd67ed9a337378531c05b40e14f4f235d51f44a3b303#0 io-hotmoka-tutorial-examples-family/target/io-hotmoka-tutorial-examples-family-1.12.1.jar --yes --password-of-payer --uri=ws://panarea.hotmoka.io:8001
Enter value for --password-of-payer (the password of the key pair of the payer account): quantum1 The jar has been installed at 40b7740025c00d1245e392ee4a24cdee2d9cef8868b915880e52b99b89593f9c. Gas consumption: * total: 699432 * for CPU: 15369 * for RAM: 5183 * for storage: 678880 * for penalty: 0 * price per unit: 5 panas * total price: 3497160 panas
The moka tool has understood that the payer is an account that signs with the qtesla-p-I algorithm and has signed the request accordingly.
