Skip to content

Commit e329959

Browse files
committed
TVM instructions: SECP256K1_XONLY_PUBKEY_TWEAK_ADD, SETCONTCTRMANY(X)
1 parent 52b010f commit e329959

File tree

7 files changed

+116
-5
lines changed

7 files changed

+116
-5
lines changed

crypto/ellcurve/secp256k1.cpp

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,22 @@
1717

1818
#include "secp256k1.h"
1919
#include "td/utils/check.h"
20+
#include "td/utils/logging.h"
21+
2022
#include <secp256k1_recovery.h>
23+
#include <secp256k1_extrakeys.h>
2124
#include <cstring>
2225

23-
namespace td {
26+
namespace td::secp256k1 {
2427

25-
bool ecrecover(const unsigned char* hash, const unsigned char* signature, unsigned char* public_key) {
28+
static const secp256k1_context* get_context() {
2629
static secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY);
30+
LOG_CHECK(ctx) << "Failed to create secp256k1_context";
31+
return ctx;
32+
}
33+
34+
bool ecrecover(const unsigned char* hash, const unsigned char* signature, unsigned char* public_key) {
35+
const secp256k1_context* ctx = get_context();
2736
secp256k1_ecdsa_recoverable_signature ecdsa_signature;
2837
if (signature[64] > 3 ||
2938
!secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &ecdsa_signature, signature, signature[64])) {
@@ -39,4 +48,22 @@ bool ecrecover(const unsigned char* hash, const unsigned char* signature, unsign
3948
return true;
4049
}
4150

51+
bool xonly_pubkey_tweak_add(const unsigned char* xonly_pubkey_bytes, const unsigned char* tweak,
52+
unsigned char* output_pubkey_bytes) {
53+
const secp256k1_context* ctx = get_context();
54+
55+
secp256k1_xonly_pubkey xonly_pubkey;
56+
secp256k1_pubkey output_pubkey;
57+
if (!secp256k1_xonly_pubkey_parse(ctx, &xonly_pubkey, xonly_pubkey_bytes)) {
58+
return false;
59+
}
60+
if (!secp256k1_xonly_pubkey_tweak_add(ctx, &output_pubkey, &xonly_pubkey, tweak)) {
61+
return false;
62+
}
63+
size_t len = 65;
64+
secp256k1_ec_pubkey_serialize(ctx, output_pubkey_bytes, &len, &output_pubkey, SECP256K1_EC_UNCOMPRESSED);
65+
CHECK(len == 65);
66+
return true;
4267
}
68+
69+
} // namespace td::secp256k1

crypto/ellcurve/secp256k1.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@
1616
*/
1717
#pragma once
1818

19-
namespace td {
19+
namespace td::secp256k1 {
2020

2121
bool ecrecover(const unsigned char* hash, const unsigned char* signature, unsigned char* public_key);
22+
bool xonly_pubkey_tweak_add(const unsigned char* xonly_pubkey_bytes, const unsigned char* tweak,
23+
unsigned char* output_pubkey_bytes);
2224

23-
}
25+
} // namespace td::secp256k1

crypto/fift/lib/Asm.fif

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,6 +1015,10 @@ x{EDC} dup @Defop(c) SAVEBOTH @Defop(c) SAVEBOTHCTR
10151015
x{EDE0} @Defop PUSHCTRX
10161016
x{EDE1} @Defop POPCTRX
10171017
x{EDE2} @Defop SETCONTCTRX
1018+
x{EDE3} @Defop(8u) SETCONTCTRMANY
1019+
x{EDE3} @Defop(8u) SETCONTMANY
1020+
x{EDE4} @Defop SETCONTCTRMANYX
1021+
x{EDE4} @Defop SETCONTMANYX
10181022
x{EDF0} dup @Defop BOOLAND @Defop COMPOS
10191023
x{EDF1} dup @Defop BOOLOR @Defop COMPOSALT
10201024
x{EDF2} @Defop COMPOSBOTH
@@ -1354,6 +1358,7 @@ x{F90704} @Defop HASHEXTAR_KECCAK512
13541358
x{F910} @Defop CHKSIGNU
13551359
x{F911} @Defop CHKSIGNS
13561360
x{F912} @Defop ECRECOVER
1361+
x{F913} @Defop SECP256K1_XONLY_PUBKEY_TWEAK_ADD
13571362
x{F914} @Defop P256_CHKSIGNU
13581363
x{F915} @Defop P256_CHKSIGNS
13591364

crypto/vm/contops.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -923,6 +923,41 @@ int exec_setcont_ctr_var(VmState* st) {
923923
return 0;
924924
}
925925

926+
int exec_setcont_ctr_many(VmState* st, unsigned args) {
927+
unsigned mask = args & 255;
928+
VM_LOG(st) << "execute SETCONTCTRMANY " << mask;
929+
if (mask & (1 << 6)) {
930+
throw VmError{Excno::range_chk, "no control register c6"};
931+
}
932+
Stack& stack = st->get_stack();
933+
auto cont = stack.pop_cont();
934+
for (int i = 0; i < 8; ++i) {
935+
if (mask & (1 << i)) {
936+
throw_typechk(force_cregs(cont)->define(i, st->get(i)));
937+
}
938+
}
939+
st->get_stack().push_cont(std::move(cont));
940+
return 0;
941+
}
942+
943+
int exec_setcont_ctr_many_var(VmState* st) {
944+
VM_LOG(st) << "execute SETCONTCTRMANYX";
945+
Stack& stack = st->get_stack();
946+
stack.check_underflow(2);
947+
int mask = stack.pop_smallint_range(255);
948+
if (mask & (1 << 6)) {
949+
throw VmError{Excno::range_chk, "no control register c6"};
950+
}
951+
auto cont = stack.pop_cont();
952+
for (int i = 0; i < 8; ++i) {
953+
if (mask & (1 << i)) {
954+
throw_typechk(force_cregs(cont)->define(i, st->get(i)));
955+
}
956+
}
957+
st->get_stack().push_cont(std::move(cont));
958+
return 0;
959+
}
960+
926961
int exec_compos(VmState* st, unsigned mask, const char* name) {
927962
Stack& stack = st->get_stack();
928963
VM_LOG(st) << "execute " << name;
@@ -1037,6 +1072,8 @@ void register_continuation_change_ops(OpcodeTable& cp0) {
10371072
cp0.insert(OpcodeInstr::mksimple(0xede0, 16, "PUSHCTRX", exec_push_ctr_var))
10381073
.insert(OpcodeInstr::mksimple(0xede1, 16, "POPCTRX", exec_pop_ctr_var))
10391074
.insert(OpcodeInstr::mksimple(0xede2, 16, "SETCONTCTRX", exec_setcont_ctr_var))
1075+
.insert(OpcodeInstr::mkfixed(0xede3, 16, 8, instr::dump_1c_l_add(1, "SETCONTCTRMANY "), exec_setcont_ctr_many)->require_version(9))
1076+
.insert(OpcodeInstr::mksimple(0xede4, 16, "SETCONTCTRMANYX", exec_setcont_ctr_many_var)->require_version(9))
10401077
.insert(OpcodeInstr::mksimple(0xedf0, 16, "BOOLAND", std::bind(exec_compos, _1, 1, "BOOLAND")))
10411078
.insert(OpcodeInstr::mksimple(0xedf1, 16, "BOOLOR", std::bind(exec_compos, _1, 2, "BOOLOR")))
10421079
.insert(OpcodeInstr::mksimple(0xedf2, 16, "COMPOSBOTH", std::bind(exec_compos, _1, 3, "COMPOSBOTH")))

crypto/vm/tonops.cpp

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -661,7 +661,38 @@ int exec_ecrecover(VmState* st) {
661661
}
662662
st->consume_gas(VmState::ecrecover_gas_price);
663663
unsigned char public_key[65];
664-
if (td::ecrecover(hash_bytes, signature, public_key)) {
664+
if (td::secp256k1::ecrecover(hash_bytes, signature, public_key)) {
665+
td::uint8 h = public_key[0];
666+
td::RefInt256 x1{true}, x2{true};
667+
CHECK(x1.write().import_bytes(public_key + 1, 32, false));
668+
CHECK(x2.write().import_bytes(public_key + 33, 32, false));
669+
stack.push_smallint(h);
670+
stack.push_int(std::move(x1));
671+
stack.push_int(std::move(x2));
672+
stack.push_bool(true);
673+
} else {
674+
stack.push_bool(false);
675+
}
676+
return 0;
677+
}
678+
679+
int exec_secp256k1_xonly_pubkey_tweak_add(VmState* st) {
680+
VM_LOG(st) << "execute SECP256K1_XONLY_PUBKEY_TWEAK_ADD";
681+
Stack& stack = st->get_stack();
682+
stack.check_underflow(2);
683+
auto tweak_int = stack.pop_int();
684+
auto key_int = stack.pop_int();
685+
686+
unsigned char key[32], tweak[32];
687+
if (!key_int->export_bytes(key, 32, false)) {
688+
throw VmError{Excno::range_chk, "key must fit in an unsigned 256-bit integer"};
689+
}
690+
if (!tweak_int->export_bytes(tweak, 32, false)) {
691+
throw VmError{Excno::range_chk, "tweak must fit in an unsigned 256-bit integer"};
692+
}
693+
st->consume_gas(VmState::secp256k1_xonly_pubkey_tweak_add_gas_price);
694+
unsigned char public_key[65];
695+
if (td::secp256k1::xonly_pubkey_tweak_add(key, tweak, public_key)) {
665696
td::uint8 h = public_key[0];
666697
td::RefInt256 x1{true}, x2{true};
667698
CHECK(x1.write().import_bytes(public_key + 1, 32, false));
@@ -1214,6 +1245,7 @@ void register_ton_crypto_ops(OpcodeTable& cp0) {
12141245
.insert(OpcodeInstr::mksimple(0xf910, 16, "CHKSIGNU", std::bind(exec_ed25519_check_signature, _1, false)))
12151246
.insert(OpcodeInstr::mksimple(0xf911, 16, "CHKSIGNS", std::bind(exec_ed25519_check_signature, _1, true)))
12161247
.insert(OpcodeInstr::mksimple(0xf912, 16, "ECRECOVER", exec_ecrecover)->require_version(4))
1248+
.insert(OpcodeInstr::mksimple(0xf913, 16, "SECP256K1_XONLY_PUBKEY_TWEAK_ADD", exec_secp256k1_xonly_pubkey_tweak_add)->require_version(9))
12171249
.insert(OpcodeInstr::mksimple(0xf914, 16, "P256_CHKSIGNU", std::bind(exec_p256_chksign, _1, false))->require_version(4))
12181250
.insert(OpcodeInstr::mksimple(0xf915, 16, "P256_CHKSIGNS", std::bind(exec_p256_chksign, _1, true))->require_version(4))
12191251

crypto/vm/vm.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ class VmState final : public VmStateInterface {
127127
rist255_validate_gas_price = 200,
128128

129129
ecrecover_gas_price = 1500,
130+
secp256k1_xonly_pubkey_tweak_add_gas_price = 1250,
130131
chksgn_free_count = 10,
131132
chksgn_gas_price = 4000,
132133
p256_chksgn_gas_price = 3500,

doc/GlobalVersions.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,13 @@ Operations for working with Merkle proofs, where cells can have non-zero level a
113113

114114
## Version 9
115115

116+
### New TVM instructions
117+
- `SECP256K1_XONLY_PUBKEY_TWEAK_ADD` (`key tweak - 0 or h x1 x2 -1`) - performs [`secp256k1_xonly_pubkey_tweak_add`](https://github.com/bitcoin-core/secp256k1/blob/master/include/secp256k1_extrakeys.h#L120).
118+
`key` and `tweak` are 256-bit unsigned integers. 65-byte public key is returned as `uint8 h`, `uint256 x1, x2` (as in `ECRECOVER`). Gas cost: `1276`.
119+
- `mask SETCONTCTRMANY` (`cont - cont'`) - takes continuation, performs the equivalent of `c[i] PUSHCTR SWAP c[i] SETCONTCNR` for each `i` that is set in `mask` (mask is in `0..255`).
120+
- `SETCONTCTRMANYX` (`cont mask - cont'`) - same as `SETCONTCTRMANY`, but takes `mask` from stack.
121+
122+
### Other changes
116123
- Fix `RAWRESERVE` action with flag `4` (use original balance of the account) by explicitly setting `original_balance` to `balance - msg_balance_remaining`.
117124
- Previously it did not work if storage fee was greater than the original balance.
118125
- Jumps to nested continuations of depth more than 8 consume 1 gas for eact subsequent continuation (this does not affect most of TVM code).

0 commit comments

Comments
 (0)