Skip to content

Commit

Permalink
apply options for hash signing
Browse files Browse the repository at this point in the history
  • Loading branch information
popenta committed Mar 28, 2024
1 parent c3ec6a4 commit d2c6333
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 5 deletions.
2 changes: 2 additions & 0 deletions multiversx_sdk/core/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,5 @@

DEFAULT_MESSAGE_VERSION = 1
BECH32_ADDRESS_LENGTH = 62

MIN_TRANSACTION_VERSION_THAT_SUPPORTS_OPTIONS = 2
21 changes: 17 additions & 4 deletions multiversx_sdk/core/transaction_computer.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@

from Cryptodome.Hash import keccak

from multiversx_sdk.core.constants import (BECH32_ADDRESS_LENGTH, DIGEST_SIZE,
TRANSACTION_OPTIONS_TX_GUARDED,
TRANSACTION_OPTIONS_TX_HASH_SIGN)
from multiversx_sdk.core.constants import (
BECH32_ADDRESS_LENGTH, DIGEST_SIZE,
MIN_TRANSACTION_VERSION_THAT_SUPPORTS_OPTIONS,
TRANSACTION_OPTIONS_TX_GUARDED, TRANSACTION_OPTIONS_TX_HASH_SIGN)
from multiversx_sdk.core.errors import BadUsageError, NotEnoughGasError
from multiversx_sdk.core.interfaces import INetworkConfig, ITransaction
from multiversx_sdk.core.proto.transaction_serializer import ProtoSerializer
Expand Down Expand Up @@ -61,10 +62,18 @@ def has_options_set_for_hash_signing(self, transaction: ITransaction) -> bool:
return (transaction.options & TRANSACTION_OPTIONS_TX_HASH_SIGN) == TRANSACTION_OPTIONS_TX_HASH_SIGN

def apply_guardian(self, transaction: ITransaction, guardian: str) -> None:
transaction.version = 2
if transaction.version < MIN_TRANSACTION_VERSION_THAT_SUPPORTS_OPTIONS:
transaction.version = MIN_TRANSACTION_VERSION_THAT_SUPPORTS_OPTIONS

transaction.options = transaction.options | TRANSACTION_OPTIONS_TX_GUARDED
transaction.guardian = guardian

def apply_options_for_hash_signing(self, transaction: ITransaction) -> None:
if transaction.version < MIN_TRANSACTION_VERSION_THAT_SUPPORTS_OPTIONS:
transaction.version = MIN_TRANSACTION_VERSION_THAT_SUPPORTS_OPTIONS

transaction.options = transaction.options | TRANSACTION_OPTIONS_TX_HASH_SIGN

def _ensure_fields(self, transaction: ITransaction) -> None:
if len(transaction.sender) != BECH32_ADDRESS_LENGTH:
raise BadUsageError("Invalid `sender` field. Should be the bech32 address of the sender.")
Expand All @@ -75,6 +84,10 @@ def _ensure_fields(self, transaction: ITransaction) -> None:
if not len(transaction.chain_id):
raise BadUsageError("The `chainID` field is not set")

if transaction.version < MIN_TRANSACTION_VERSION_THAT_SUPPORTS_OPTIONS:
if self.has_options_set_for_guarded_transaction(transaction) or self.has_options_set_for_hash_signing(transaction):
raise BadUsageError(f"Non-empty transaction options requires transaction version >= {MIN_TRANSACTION_VERSION_THAT_SUPPORTS_OPTIONS}")

def _to_dictionary(self, transaction: ITransaction) -> Dict[str, Any]:
dictionary: Dict[str, Any] = OrderedDict()
dictionary["nonce"] = transaction.nonce
Expand Down
49 changes: 48 additions & 1 deletion multiversx_sdk/core/transaction_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import pytest

from multiversx_sdk.core.errors import NotEnoughGasError
from multiversx_sdk.core.constants import \
MIN_TRANSACTION_VERSION_THAT_SUPPORTS_OPTIONS
from multiversx_sdk.core.errors import BadUsageError, NotEnoughGasError
from multiversx_sdk.core.proto.transaction_serializer import ProtoSerializer
from multiversx_sdk.core.transaction import Transaction
from multiversx_sdk.core.transaction_computer import TransactionComputer
Expand All @@ -22,6 +24,7 @@ def __init__(self, min_gas_limit: int = 50000) -> None:
class TestTransaction:
wallets = load_wallets()
alice = wallets["alice"]
bob = wallets["bob"]
carol = wallets["carol"]
transaction_computer = TransactionComputer()

Expand Down Expand Up @@ -245,3 +248,47 @@ def test_sign_transaction_by_hash(self):
tx.signature = pem.secret_key.sign(serialized)

assert tx.signature.hex() == "f0c81f2393b1ec5972c813f817bae8daa00ade91c6f75ea604ab6a4d2797aca4378d783023ff98f1a02717fe4f24240cdfba0b674ee9abb18042203d713bc70a"

def test_apply_guardian_with_hash_signing(self):
tx = Transaction(
sender="erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th",
receiver="erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx",
value=0,
gas_limit=50000,
version=1,
chain_id="localnet",
nonce=89
)

self.transaction_computer.apply_options_for_hash_signing(tx)
assert tx.version == 2
assert tx.options == 1

self.transaction_computer.apply_guardian(transaction=tx, guardian=self.carol.label)
assert tx.version == 2
assert tx.options == 3

def test_ensure_transaction_is_valid(self):
tx = Transaction(
sender="invalid_sender",
receiver=self.bob.label,
gas_limit=50000,
chain_id=""
)

with pytest.raises(BadUsageError, match="Invalid `sender` field. Should be the bech32 address of the sender."):
self.transaction_computer.compute_bytes_for_signing(tx)

tx.sender = self.alice.label
with pytest.raises(BadUsageError, match="The `chainID` field is not set"):
self.transaction_computer.compute_bytes_for_signing(tx)

tx.chain_id = "localnet"
tx.version = 1
tx.options = 2
with pytest.raises(BadUsageError, match=f"Non-empty transaction options requires transaction version >= {MIN_TRANSACTION_VERSION_THAT_SUPPORTS_OPTIONS}"):
self.transaction_computer.compute_bytes_for_signing(tx)

self.transaction_computer.apply_options_for_hash_signing(tx)
assert tx.version == 2
assert tx.options == 3

0 comments on commit d2c6333

Please sign in to comment.