Skip to content

Commit

Permalink
Fix fee too low
Browse files Browse the repository at this point in the history
User can potentially provide signing keys that are not required by the finalized transaction.
For example, if a transaction include a minting script and signing key for the script, but the minted value is 0, the signature of the minting script isn't required.
This commit will auto detect such cases and skip the signing of this key.
  • Loading branch information
cffls committed Sep 7, 2024
1 parent 623bdfe commit 853a4dc
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 3 deletions.
4 changes: 2 additions & 2 deletions pycardano/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,13 @@
from pycardano.serialization import (
ArrayCBORSerializable,
CBORSerializable,
DictBase,
DictCBORSerializable,
MapCBORSerializable,
Primitive,
default_encoder,
list_hook,
DictBase,
limit_primitive_type,
list_hook,
)
from pycardano.types import typechecked
from pycardano.witness import TransactionWitnessSet
Expand Down
19 changes: 18 additions & 1 deletion pycardano/txbuilder.py
Original file line number Diff line number Diff line change
Expand Up @@ -928,12 +928,16 @@ def _build_tx_body(self) -> TransactionBody:
)
return tx_body

def _build_fake_vkey_witnesses(self) -> List[VerificationKeyWitness]:
def _build_required_vkeys(self) -> Set[VerificationKeyHash]:
vkey_hashes = self._input_vkey_hashes()
vkey_hashes.update(self._required_signer_vkey_hashes())
vkey_hashes.update(self._native_scripts_vkey_hashes())
vkey_hashes.update(self._certificate_vkey_hashes())
vkey_hashes.update(self._withdrawal_vkey_hashes())
return vkey_hashes

def _build_fake_vkey_witnesses(self) -> List[VerificationKeyWitness]:
vkey_hashes = self._build_required_vkeys()

witness_count = self.witness_override or len(vkey_hashes)

Expand Down Expand Up @@ -1441,6 +1445,7 @@ def build_and_sign(
auto_validity_start_offset: Optional[int] = None,
auto_ttl_offset: Optional[int] = None,
auto_required_signers: Optional[bool] = None,
force_skeys: Optional[bool] = False,
) -> Transaction:
"""Build a transaction body from all constraints set through the builder and sign the transaction with
provided signing keys.
Expand All @@ -1462,6 +1467,10 @@ def build_and_sign(
auto_required_signers (Optional[bool]): Automatically add all pubkeyhashes of transaction inputs
and the given signers to required signatories (default only for Smart Contract transactions).
Manually set required signers will always take precedence.
force_skeys (Optional[bool]): Whether to force the use of signing keys for signing the transaction.
Default is False, which means that provided signing keys will only be used to sign the transaction if
they are actually required by the transaction. This is useful to reduce tx fees by not including
unnecessary signatures. If set to True, all provided signing keys will be used to sign the transaction.
Returns:
Transaction: A signed transaction.
Expand All @@ -1483,7 +1492,15 @@ def build_and_sign(
witness_set = self.build_witness_set(True)
witness_set.vkey_witnesses = []

required_vkeys = self._build_required_vkeys()

for signing_key in set(signing_keys):
vkey_hash = signing_key.to_verification_key().hash()
if not force_skeys and vkey_hash not in required_vkeys:
logger.warn(
f"Verification key hash {vkey_hash} is not required for this tx."
)
continue
signature = signing_key.sign(tx_body.hash())
witness_set.vkey_witnesses.append(
VerificationKeyWitness(signing_key.to_verification_key(), signature)
Expand Down

0 comments on commit 853a4dc

Please sign in to comment.