Skip to content

Commit

Permalink
TVM instructions: SECP256K1_XONLY_PUBKEY_TWEAK_ADD, SETCONTCTRMANY(X)
Browse files Browse the repository at this point in the history
  • Loading branch information
SpyCheese committed Nov 21, 2024
1 parent 52b010f commit e329959
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 5 deletions.
31 changes: 29 additions & 2 deletions crypto/ellcurve/secp256k1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,22 @@

#include "secp256k1.h"
#include "td/utils/check.h"
#include "td/utils/logging.h"

#include <secp256k1_recovery.h>
#include <secp256k1_extrakeys.h>
#include <cstring>

namespace td {
namespace td::secp256k1 {

bool ecrecover(const unsigned char* hash, const unsigned char* signature, unsigned char* public_key) {
static const secp256k1_context* get_context() {
static secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY);
LOG_CHECK(ctx) << "Failed to create secp256k1_context";
return ctx;
}

bool ecrecover(const unsigned char* hash, const unsigned char* signature, unsigned char* public_key) {
const secp256k1_context* ctx = get_context();
secp256k1_ecdsa_recoverable_signature ecdsa_signature;
if (signature[64] > 3 ||
!secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &ecdsa_signature, signature, signature[64])) {
Expand All @@ -39,4 +48,22 @@ bool ecrecover(const unsigned char* hash, const unsigned char* signature, unsign
return true;
}

bool xonly_pubkey_tweak_add(const unsigned char* xonly_pubkey_bytes, const unsigned char* tweak,
unsigned char* output_pubkey_bytes) {
const secp256k1_context* ctx = get_context();

secp256k1_xonly_pubkey xonly_pubkey;
secp256k1_pubkey output_pubkey;
if (!secp256k1_xonly_pubkey_parse(ctx, &xonly_pubkey, xonly_pubkey_bytes)) {
return false;
}
if (!secp256k1_xonly_pubkey_tweak_add(ctx, &output_pubkey, &xonly_pubkey, tweak)) {
return false;
}
size_t len = 65;
secp256k1_ec_pubkey_serialize(ctx, output_pubkey_bytes, &len, &output_pubkey, SECP256K1_EC_UNCOMPRESSED);
CHECK(len == 65);
return true;
}

} // namespace td::secp256k1
6 changes: 4 additions & 2 deletions crypto/ellcurve/secp256k1.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
*/
#pragma once

namespace td {
namespace td::secp256k1 {

bool ecrecover(const unsigned char* hash, const unsigned char* signature, unsigned char* public_key);
bool xonly_pubkey_tweak_add(const unsigned char* xonly_pubkey_bytes, const unsigned char* tweak,
unsigned char* output_pubkey_bytes);

}
} // namespace td::secp256k1
5 changes: 5 additions & 0 deletions crypto/fift/lib/Asm.fif
Original file line number Diff line number Diff line change
Expand Up @@ -1015,6 +1015,10 @@ x{EDC} dup @Defop(c) SAVEBOTH @Defop(c) SAVEBOTHCTR
x{EDE0} @Defop PUSHCTRX
x{EDE1} @Defop POPCTRX
x{EDE2} @Defop SETCONTCTRX
x{EDE3} @Defop(8u) SETCONTCTRMANY
x{EDE3} @Defop(8u) SETCONTMANY
x{EDE4} @Defop SETCONTCTRMANYX
x{EDE4} @Defop SETCONTMANYX
x{EDF0} dup @Defop BOOLAND @Defop COMPOS
x{EDF1} dup @Defop BOOLOR @Defop COMPOSALT
x{EDF2} @Defop COMPOSBOTH
Expand Down Expand Up @@ -1354,6 +1358,7 @@ x{F90704} @Defop HASHEXTAR_KECCAK512
x{F910} @Defop CHKSIGNU
x{F911} @Defop CHKSIGNS
x{F912} @Defop ECRECOVER
x{F913} @Defop SECP256K1_XONLY_PUBKEY_TWEAK_ADD
x{F914} @Defop P256_CHKSIGNU
x{F915} @Defop P256_CHKSIGNS

Expand Down
37 changes: 37 additions & 0 deletions crypto/vm/contops.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -923,6 +923,41 @@ int exec_setcont_ctr_var(VmState* st) {
return 0;
}

int exec_setcont_ctr_many(VmState* st, unsigned args) {
unsigned mask = args & 255;
VM_LOG(st) << "execute SETCONTCTRMANY " << mask;
if (mask & (1 << 6)) {
throw VmError{Excno::range_chk, "no control register c6"};
}
Stack& stack = st->get_stack();
auto cont = stack.pop_cont();
for (int i = 0; i < 8; ++i) {
if (mask & (1 << i)) {
throw_typechk(force_cregs(cont)->define(i, st->get(i)));
}
}
st->get_stack().push_cont(std::move(cont));
return 0;
}

int exec_setcont_ctr_many_var(VmState* st) {
VM_LOG(st) << "execute SETCONTCTRMANYX";
Stack& stack = st->get_stack();
stack.check_underflow(2);
int mask = stack.pop_smallint_range(255);
if (mask & (1 << 6)) {
throw VmError{Excno::range_chk, "no control register c6"};
}
auto cont = stack.pop_cont();
for (int i = 0; i < 8; ++i) {
if (mask & (1 << i)) {
throw_typechk(force_cregs(cont)->define(i, st->get(i)));
}
}
st->get_stack().push_cont(std::move(cont));
return 0;
}

int exec_compos(VmState* st, unsigned mask, const char* name) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute " << name;
Expand Down Expand Up @@ -1037,6 +1072,8 @@ void register_continuation_change_ops(OpcodeTable& cp0) {
cp0.insert(OpcodeInstr::mksimple(0xede0, 16, "PUSHCTRX", exec_push_ctr_var))
.insert(OpcodeInstr::mksimple(0xede1, 16, "POPCTRX", exec_pop_ctr_var))
.insert(OpcodeInstr::mksimple(0xede2, 16, "SETCONTCTRX", exec_setcont_ctr_var))
.insert(OpcodeInstr::mkfixed(0xede3, 16, 8, instr::dump_1c_l_add(1, "SETCONTCTRMANY "), exec_setcont_ctr_many)->require_version(9))
.insert(OpcodeInstr::mksimple(0xede4, 16, "SETCONTCTRMANYX", exec_setcont_ctr_many_var)->require_version(9))
.insert(OpcodeInstr::mksimple(0xedf0, 16, "BOOLAND", std::bind(exec_compos, _1, 1, "BOOLAND")))
.insert(OpcodeInstr::mksimple(0xedf1, 16, "BOOLOR", std::bind(exec_compos, _1, 2, "BOOLOR")))
.insert(OpcodeInstr::mksimple(0xedf2, 16, "COMPOSBOTH", std::bind(exec_compos, _1, 3, "COMPOSBOTH")))
Expand Down
34 changes: 33 additions & 1 deletion crypto/vm/tonops.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -661,7 +661,38 @@ int exec_ecrecover(VmState* st) {
}
st->consume_gas(VmState::ecrecover_gas_price);
unsigned char public_key[65];
if (td::ecrecover(hash_bytes, signature, public_key)) {
if (td::secp256k1::ecrecover(hash_bytes, signature, public_key)) {
td::uint8 h = public_key[0];
td::RefInt256 x1{true}, x2{true};
CHECK(x1.write().import_bytes(public_key + 1, 32, false));
CHECK(x2.write().import_bytes(public_key + 33, 32, false));
stack.push_smallint(h);
stack.push_int(std::move(x1));
stack.push_int(std::move(x2));
stack.push_bool(true);
} else {
stack.push_bool(false);
}
return 0;
}

int exec_secp256k1_xonly_pubkey_tweak_add(VmState* st) {
VM_LOG(st) << "execute SECP256K1_XONLY_PUBKEY_TWEAK_ADD";
Stack& stack = st->get_stack();
stack.check_underflow(2);
auto tweak_int = stack.pop_int();
auto key_int = stack.pop_int();

unsigned char key[32], tweak[32];
if (!key_int->export_bytes(key, 32, false)) {
throw VmError{Excno::range_chk, "key must fit in an unsigned 256-bit integer"};
}
if (!tweak_int->export_bytes(tweak, 32, false)) {
throw VmError{Excno::range_chk, "tweak must fit in an unsigned 256-bit integer"};
}
st->consume_gas(VmState::secp256k1_xonly_pubkey_tweak_add_gas_price);
unsigned char public_key[65];
if (td::secp256k1::xonly_pubkey_tweak_add(key, tweak, public_key)) {
td::uint8 h = public_key[0];
td::RefInt256 x1{true}, x2{true};
CHECK(x1.write().import_bytes(public_key + 1, 32, false));
Expand Down Expand Up @@ -1214,6 +1245,7 @@ void register_ton_crypto_ops(OpcodeTable& cp0) {
.insert(OpcodeInstr::mksimple(0xf910, 16, "CHKSIGNU", std::bind(exec_ed25519_check_signature, _1, false)))
.insert(OpcodeInstr::mksimple(0xf911, 16, "CHKSIGNS", std::bind(exec_ed25519_check_signature, _1, true)))
.insert(OpcodeInstr::mksimple(0xf912, 16, "ECRECOVER", exec_ecrecover)->require_version(4))
.insert(OpcodeInstr::mksimple(0xf913, 16, "SECP256K1_XONLY_PUBKEY_TWEAK_ADD", exec_secp256k1_xonly_pubkey_tweak_add)->require_version(9))
.insert(OpcodeInstr::mksimple(0xf914, 16, "P256_CHKSIGNU", std::bind(exec_p256_chksign, _1, false))->require_version(4))
.insert(OpcodeInstr::mksimple(0xf915, 16, "P256_CHKSIGNS", std::bind(exec_p256_chksign, _1, true))->require_version(4))

Expand Down
1 change: 1 addition & 0 deletions crypto/vm/vm.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ class VmState final : public VmStateInterface {
rist255_validate_gas_price = 200,

ecrecover_gas_price = 1500,
secp256k1_xonly_pubkey_tweak_add_gas_price = 1250,
chksgn_free_count = 10,
chksgn_gas_price = 4000,
p256_chksgn_gas_price = 3500,
Expand Down
7 changes: 7 additions & 0 deletions doc/GlobalVersions.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,13 @@ Operations for working with Merkle proofs, where cells can have non-zero level a

## Version 9

### New TVM instructions
- `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).
`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`.
- `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`).
- `SETCONTCTRMANYX` (`cont mask - cont'`) - same as `SETCONTCTRMANY`, but takes `mask` from stack.

### Other changes
- Fix `RAWRESERVE` action with flag `4` (use original balance of the account) by explicitly setting `original_balance` to `balance - msg_balance_remaining`.
- Previously it did not work if storage fee was greater than the original balance.
- 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 comments on commit e329959

Please sign in to comment.