Skip to content

Commit fcc2da8

Browse files
committed
Make script estimation more accurate
1 parent c426f5f commit fcc2da8

File tree

3 files changed

+49
-130
lines changed

3 files changed

+49
-130
lines changed

integration-test/run_tests.sh

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,39 +7,14 @@ ROOT=$(pwd)
77

88
poetry install
99

10-
##########
11-
# Alonzo #
12-
##########
13-
14-
# Cleanup containers and volumes in case there is any running
15-
docker-compose down --volumes --remove-orphans
16-
17-
# Run alonzo integration tests
18-
./bootstrap.sh local-alonzo
19-
20-
# Launch containers
21-
docker-compose up -d
22-
23-
export PAYMENT_KEY="$ROOT"/configs/local-alonzo/shelley/utxo-keys/utxo1.skey
24-
export EXTENDED_PAYMENT_KEY="$ROOT"/keys/extended.skey
25-
export POOL_ID=$(cat "$ROOT"/keys/pool/pool.id)
26-
27-
# Wait for stake pool to start producing blocks
28-
sleep 20
29-
30-
poetry run pytest -s -vv -n 4 "$ROOT"/test -m "not (post_alonzo)"
31-
32-
# Cleanup
33-
docker-compose down --volumes --remove-orphans
34-
3510
#########
3611
# Vasil #
3712
#########
3813

3914
# Cleanup containers and volumes in case there is any running
4015
docker-compose down --volumes --remove-orphans
4116

42-
# Run alonzo integration tests
17+
# Run integration tests
4318
./bootstrap.sh local-vasil
4419

4520
# Launch containers

pycardano/txbuilder.py

Lines changed: 48 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from __future__ import annotations
22

3-
from copy import copy, deepcopy
3+
from copy import deepcopy
44
from dataclasses import dataclass, field, fields
55
from typing import Dict, List, Optional, Set, Tuple, Union
66

@@ -865,7 +865,9 @@ def build(
865865
Returns:
866866
TransactionBody: A transaction body.
867867
"""
868-
self._update_execution_units()
868+
self._update_execution_units(
869+
change_address, merge_change, collateral_change_address
870+
)
869871
self._ensure_no_input_exclusion_conflict()
870872
selected_utxos = []
871873
selected_amount = Value()
@@ -1020,7 +1022,7 @@ def _set_collateral_return(self, collateral_return_address: Address):
10201022
):
10211023
return
10221024

1023-
if not collateral_return_address or self._should_estimate_execution_units:
1025+
if not collateral_return_address:
10241026
return
10251027

10261028
collateral_amount = (
@@ -1045,15 +1047,15 @@ def _add_collateral_input(cur_total, candidate_inputs):
10451047
cur_total += candidate.output.amount
10461048

10471049
sorted_inputs = sorted(
1048-
self.inputs.copy(), key=lambda i: i.output.amount, reverse=True
1050+
self.inputs.copy(),
1051+
key=lambda i: (len(i.output.to_cbor()), -i.output.amount.coin),
10491052
)
10501053
_add_collateral_input(tmp_val, sorted_inputs)
10511054

10521055
if tmp_val.coin < collateral_amount:
10531056
sorted_inputs = sorted(
10541057
self.context.utxos(str(collateral_return_address)),
1055-
key=lambda i: i.output.amount,
1056-
reverse=True,
1058+
key=lambda i: (len(i.output.to_cbor()), -i.output.amount.coin),
10571059
)
10581060
_add_collateral_input(tmp_val, sorted_inputs)
10591061

@@ -1062,33 +1064,38 @@ def _add_collateral_input(cur_total, candidate_inputs):
10621064
for utxo in self.collaterals:
10631065
total_input += utxo.output.amount
10641066

1065-
# TODO: Remove this check when we are in Vasil era
1066-
if total_input.multi_asset:
1067-
if collateral_amount > total_input.coin:
1067+
if collateral_amount > total_input.coin:
1068+
raise ValueError(
1069+
f"Minimum collateral amount {collateral_amount} is greater than total "
1070+
f"provided collateral inputs {total_input}"
1071+
)
1072+
else:
1073+
return_amount = total_input - collateral_amount
1074+
min_lovelace_val = min_lovelace_post_alonzo(
1075+
TransactionOutput(collateral_return_address, return_amount),
1076+
self.context,
1077+
)
1078+
if min_lovelace_val > return_amount.coin:
10681079
raise ValueError(
1069-
f"Minimum collateral amount {collateral_amount} is greater than total "
1070-
f"provided collateral inputs {total_input}"
1080+
f"Minimum lovelace amount for collateral return {min_lovelace_val} is "
1081+
f"greater than collateral change {return_amount.coin}. Please provide more collateral inputs."
10711082
)
10721083
else:
1073-
return_amount = total_input - collateral_amount
1074-
min_lovelace_val = min_lovelace_post_alonzo(
1075-
TransactionOutput(collateral_return_address, return_amount),
1076-
self.context,
1084+
self._collateral_return = TransactionOutput(
1085+
collateral_return_address, total_input - collateral_amount
10771086
)
1078-
if min_lovelace_val > return_amount.coin:
1079-
raise ValueError(
1080-
f"Minimum lovelace amount for collateral return {min_lovelace_val} is "
1081-
f"greater than collateral change {return_amount.coin}. Please provide more collateral inputs."
1082-
)
1083-
else:
1084-
self._collateral_return = TransactionOutput(
1085-
collateral_return_address, total_input - collateral_amount
1086-
)
1087-
self._total_collateral = collateral_amount
1087+
self._total_collateral = collateral_amount
10881088

1089-
def _update_execution_units(self):
1089+
def _update_execution_units(
1090+
self,
1091+
change_address: Optional[Address] = None,
1092+
merge_change: bool = False,
1093+
collateral_change_address: Optional[Address] = None,
1094+
):
10901095
if self._should_estimate_execution_units:
1091-
estimated_execution_units = self._estimate_execution_units()
1096+
estimated_execution_units = self._estimate_execution_units(
1097+
change_address, merge_change, collateral_change_address
1098+
)
10921099
for r in self.redeemers:
10931100
key = f"{r.tag.name.lower()}:{r.index}"
10941101
if key not in estimated_execution_units:
@@ -1107,15 +1114,19 @@ def _update_execution_units(self):
11071114
def _estimate_execution_units(
11081115
self,
11091116
change_address: Optional[Address] = None,
1117+
merge_change: bool = False,
1118+
collateral_change_address: Optional[Address] = None,
11101119
):
1111-
# Create a shallow copy of current builder, so we won't mess up current builder's internal states
1120+
# Create a deep copy of current builder, so we won't mess up current builder's internal states
11121121
tmp_builder = TransactionBuilder(self.context)
11131122
for f in fields(self):
11141123
if f.name not in ("context",):
1115-
setattr(tmp_builder, f.name, copy(getattr(self, f.name)))
1124+
setattr(tmp_builder, f.name, deepcopy(getattr(self, f.name)))
11161125
tmp_builder._should_estimate_execution_units = False
11171126
self._should_estimate_execution_units = False
1118-
tx_body = tmp_builder.build(change_address)
1127+
tx_body = tmp_builder.build(
1128+
change_address, merge_change, collateral_change_address
1129+
)
11191130
witness_set = tmp_builder._build_fake_witness_set()
11201131
tx = Transaction(
11211132
tx_body, witness_set, auxiliary_data=tmp_builder.auxiliary_data
@@ -1128,6 +1139,7 @@ def build_and_sign(
11281139
signing_keys: List[Union[SigningKey, ExtendedSigningKey]],
11291140
change_address: Optional[Address] = None,
11301141
merge_change: Optional[bool] = False,
1142+
collateral_change_address: Optional[Address] = None,
11311143
) -> Transaction:
11321144
"""Build a transaction body from all constraints set through the builder and sign the transaction with
11331145
provided signing keys.
@@ -1139,12 +1151,17 @@ def build_and_sign(
11391151
transaction body will likely be unbalanced (sum of inputs is greater than the sum of outputs).
11401152
merge_change (Optional[bool]): If the change address match one of the transaction output, the change amount
11411153
will be directly added to that transaction output, instead of being added as a separate output.
1154+
collateral_change_address (Optional[Address]): Address to which collateral changes will be returned.
11421155
11431156
Returns:
11441157
Transaction: A signed transaction.
11451158
"""
11461159

1147-
tx_body = self.build(change_address=change_address, merge_change=merge_change)
1160+
tx_body = self.build(
1161+
change_address=change_address,
1162+
merge_change=merge_change,
1163+
collateral_change_address=collateral_change_address,
1164+
)
11481165
witness_set = self.build_witness_set()
11491166
witness_set.vkey_witnesses = []
11501167

test/pycardano/test_txbuilder.py

Lines changed: 0 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -473,17 +473,6 @@ def test_add_script_input(chain_context):
473473
assert [datum] == witness.plutus_data
474474
assert [redeemer1, redeemer2] == witness.redeemer
475475
assert [plutus_script] == witness.plutus_v1_script
476-
assert (
477-
"a6008282582018cbe6cadecd3f89b60e08e68e5e6c7d72d730aaa1ad21431590f7e6643438"
478-
"ef0082582018cbe6cadecd3f89b60e08e68e5e6c7d72d730aaa1ad21431590f7e6643438ef"
479-
"01018282581d60f6532850e1bccee9c72a9113ad98bcc5dbb30d2ac960262444f6e5f41a00"
480-
"4c4b4082581d60f6532850e1bccee9c72a9113ad98bcc5dbb30d2ac960262444f6e5f4821a"
481-
"00e06beba1581c876f19078b059c928258d848c8cd871864d281eb6776ed7f80b68536a149"
482-
"54657374546f6b656e02021a000475d509a1581c876f19078b059c928258d848c8cd871864"
483-
"d281eb6776ed7f80b68536a14954657374546f6b656e010b5820c0978261d9818d92926eb0"
484-
"31d38d141f513a05478d697555f32edf6443ebeb080d818258203131313131313131313131"
485-
"31313131313131313131313131313131313131313100" == tx_body.to_cbor()
486-
)
487476

488477

489478
def test_add_script_input_no_script(chain_context):
@@ -514,16 +503,6 @@ def test_add_script_input_no_script(chain_context):
514503
assert [datum] == witness.plutus_data
515504
assert [redeemer] == witness.redeemer
516505
assert witness.plutus_v1_script is None
517-
assert (
518-
"a6008182582018cbe6cadecd3f89b60e08e68e5e6c7d72d730aaa1ad21431590f"
519-
"7e6643438ef00018282581d60f6532850e1bccee9c72a9113ad98bcc5dbb30d2a"
520-
"c960262444f6e5f41a004c4b4082581d60f6532850e1bccee9c72a9113ad98bcc"
521-
"5dbb30d2ac960262444f6e5f41a0048cc3b021a00037f050b5820032d812ee073"
522-
"1af78fe4ec67e4d30d16313c09e6fb675af28f825797e8b5621d0d81825820313"
523-
"13131313131313131313131313131313131313131313131313131313131310012"
524-
"8182582018cbe6cadecd3f89b60e08e68e5e6c7d72d730aaa1ad21431590f7e66"
525-
"43438ef00" == tx_body.to_cbor()
526-
)
527506

528507

529508
def test_add_script_input_find_script(chain_context):
@@ -569,16 +548,6 @@ def test_add_script_input_find_script(chain_context):
569548
assert [redeemer] == witness.redeemer
570549
assert witness.plutus_v1_script is None
571550
assert [existing_script_utxo.input] == tx_body.reference_inputs
572-
assert (
573-
"a6008182582018cbe6cadecd3f89b60e08e68e5e6c7d72d730aaa1ad2143159"
574-
"0f7e6643438ef00018282581d60f6532850e1bccee9c72a9113ad98bcc5dbb3"
575-
"0d2ac960262444f6e5f41a004c4b4082581d60f6532850e1bccee9c72a9113a"
576-
"d98bcc5dbb30d2ac960262444f6e5f41a0048cc3b021a00037f050b5820032d"
577-
"812ee0731af78fe4ec67e4d30d16313c09e6fb675af28f825797e8b5621d0d8"
578-
"182582031313131313131313131313131313131313131313131313131313131"
579-
"3131313100128182582041cb004bec7051621b19b46aea28f0657a586a05ce2"
580-
"013152ea9b9f1a5614cc701" == tx_body.to_cbor()
581-
)
582551

583552

584553
def test_add_script_input_with_script_from_specified_utxo(chain_context):
@@ -620,15 +589,6 @@ def test_add_script_input_with_script_from_specified_utxo(chain_context):
620589
assert [redeemer] == witness.redeemer
621590
assert witness.plutus_v2_script is None
622591
assert [existing_script_utxo.input] == tx_body.reference_inputs
623-
assert (
624-
"a6008182582018cbe6cadecd3f89b60e08e68e5e6c7d72d730aaa1ad21431590f7e6643438"
625-
"ef00018282581d60f6532850e1bccee9c72a9113ad98bcc5dbb30d2ac960262444f6e5f41a"
626-
"004c4b4082581d60f6532850e1bccee9c72a9113ad98bcc5dbb30d2ac960262444f6e5f41a"
627-
"0048cc3b021a00037f050b58203dde0555ddcbc6ae54f5b3c72c0d1f84dfcd22fa758f64bd"
628-
"2122885c4c5f8ed20d81825820313131313131313131313131313131313131313131313131"
629-
"313131313131313100128182582041cb004bec7051621b19b46aea28f0657a586a05ce2013"
630-
"152ea9b9f1a5614cc701" == tx_body.to_cbor()
631-
)
632592

633593

634594
def test_add_minting_script_from_specified_utxo(chain_context):
@@ -667,19 +627,6 @@ def test_add_minting_script_from_specified_utxo(chain_context):
667627
assert [redeemer] == witness.redeemer
668628
assert witness.plutus_v2_script is None
669629
assert [existing_script_utxo.input] == tx_body.reference_inputs
670-
assert (
671-
"a700828258203131313131313131313131313131313131313131313131313131313131313131"
672-
"0082582032323232323232323232323232323232323232323232323232323232323232320101"
673-
"8282581d60f6532850e1bccee9c72a9113ad98bcc5dbb30d2ac960262444f6e5f41a004c4b40"
674-
"82581d60f6532850e1bccee9c72a9113ad98bcc5dbb30d2ac960262444f6e5f4821a0057f1f3"
675-
"a2581c31313131313131313131313131313131313131313131313131313131a246546f6b656e"
676-
"310146546f6b656e3202581c669ce610ef17d3ac9f5663f0748381120376e72b031e002db95a"
677-
"3981a14954657374546f6b656e01021a00039b8d09a1581c669ce610ef17d3ac9f5663f07483"
678-
"81120376e72b031e002db95a3981a14954657374546f6b656e010b5820cb8b108226416e0c75"
679-
"46b09bdee0726dfcd07827a0a6c9c98dac7f02d7d3382f0d8182582031313131313131313131"
680-
"3131313131313131313131313131313131313131313100128182582041cb004bec7051621b19"
681-
"b46aea28f0657a586a05ce2013152ea9b9f1a5614cc701" == tx_body.to_cbor()
682-
)
683630

684631

685632
def test_collateral_return(chain_context):
@@ -814,16 +761,6 @@ def test_add_minting_script(chain_context):
814761
tx_body = tx_builder.build(change_address=receiver)
815762
witness = tx_builder.build_witness_set()
816763
assert [plutus_script] == witness.plutus_v1_script
817-
assert (
818-
"a6008182582018cbe6cadecd3f89b60e08e68e5e6c7d72d730aaa1ad21431590f7e6643438ef"
819-
"00018282581d60f6532850e1bccee9c72a9113ad98bcc5dbb30d2ac960262444f6e5f4821a00"
820-
"4c4b40a1581c876f19078b059c928258d848c8cd871864d281eb6776ed7f80b68536a1495465"
821-
"7374546f6b656e0182581d60f6532850e1bccee9c72a9113ad98bcc5dbb30d2ac960262444f6"
822-
"e5f41a0048c10f021a00038a3109a1581c876f19078b059c928258d848c8cd871864d281eb67"
823-
"76ed7f80b68536a14954657374546f6b656e010b58205fcf68adc7eb6e507d15fb07d1c4e39d"
824-
"908bc9dfe642368afcddd881c5d465170d818258203131313131313131313131313131313131"
825-
"31313131313131313131313131313100" == tx_body.to_cbor()
826-
)
827764

828765

829766
def test_add_minting_script_wrong_redeemer_type(chain_context):
@@ -929,16 +866,6 @@ def test_estimate_execution_unit(chain_context):
929866
assert [redeemer1] == witness.redeemer
930867
assert redeemer1.ex_units is not None
931868
assert [plutus_script] == witness.plutus_v1_script
932-
assert (
933-
"a6008182582018cbe6cadecd3f89b60e08e68e5e6c7d72d730aaa1ad21431590f7e6643438ef"
934-
"00018282581d60f6532850e1bccee9c72a9113ad98bcc5dbb30d2ac960262444f6e5f41a004c"
935-
"4b4082581d60f6532850e1bccee9c72a9113ad98bcc5dbb30d2ac960262444f6e5f4821a0048"
936-
"fa42a1581c876f19078b059c928258d848c8cd871864d281eb6776ed7f80b68536a149546573"
937-
"74546f6b656e01021a000350fe09a1581c876f19078b059c928258d848c8cd871864d281eb67"
938-
"76ed7f80b68536a14954657374546f6b656e010b58206b5664c6f79646f2a4c17bdc1ecb6f6b"
939-
"f540db5c82dfa0a9d806c435398756fa0d818258203131313131313131313131313131313131"
940-
"31313131313131313131313131313100" == tx_body.to_cbor()
941-
)
942869

943870

944871
def test_tx_builder_exact_fee_no_change(chain_context):

0 commit comments

Comments
 (0)