Skip to content

Commit e58394c

Browse files
committed
Enable creating a series of spends by passing in the spends that are expected to happen prior, in order
1 parent 4cb2b6b commit e58394c

File tree

1 file changed

+64
-2
lines changed

1 file changed

+64
-2
lines changed

cic/cli/main.py

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -968,6 +968,12 @@ async def do_command():
968968
default=1000000000000,
969969
show_default=True,
970970
)
971+
@click.option(
972+
"-ps",
973+
"--previous-spends",
974+
help="A comma separated list of previously generated spend bundle files to simulate spending first (in order)",
975+
default=None,
976+
)
971977
def payments_cmd(
972978
db_path: str,
973979
pubkeys: str,
@@ -977,6 +983,7 @@ def payments_cmd(
977983
maximum_extra_cost: Optional[int],
978984
amount_threshold: int,
979985
filename: Optional[str],
986+
previous_spends: Optional[str],
980987
):
981988
# Check to make sure we've been given a correct set of parameters
982989
if amount % 2 == 1:
@@ -994,6 +1001,61 @@ async def do_command():
9941001
clawforward_ph: bytes32 = decode_puzzle_hash(recipient_address)
9951002
fee_conditions: List[Program] = [Program.to([60, b""])]
9961003

1004+
# Handle previous spends if provided
1005+
current_coin = current_singleton.coin
1006+
current_lineage_proof = current_singleton.lineage_proof
1007+
1008+
if previous_spends:
1009+
previous_spend_files = previous_spends.split(",")
1010+
for spend_file in previous_spend_files:
1011+
try:
1012+
# Read the unsigned spend
1013+
unsigned_spend = read_unsigned_spend(spend_file.strip())
1014+
1015+
# Extract the coin spend for the singleton (first one should be the singleton)
1016+
if len(unsigned_spend.coin_spends) == 0:
1017+
raise ValueError(f"No coin spends found in {spend_file}")
1018+
1019+
# Get the singleton coin spend
1020+
singleton_spend = unsigned_spend.coin_spends[0]
1021+
1022+
# Extract the payment amount from the spend bundle
1023+
# We need to parse the solution to get the out_amount
1024+
try:
1025+
# The HSM coin spend has the solution as a Program, not bytes
1026+
# We can work with it directly
1027+
solution = singleton_spend.solution
1028+
out_amount, in_amount, p2_ph = get_spend_params_for_ach_creation(solution)
1029+
1030+
# Calculate the new amount after this payment
1031+
# out_amount is what we're paying out
1032+
# in_amount is what we're absorbing from p2_singletons
1033+
net_change = in_amount - out_amount
1034+
new_amount = current_coin.amount + net_change
1035+
1036+
except Exception as parse_error:
1037+
raise ValueError(f"Cannot parse previous spend bundle {spend_file}. The tool needs to be able to extract the payment amount from the spend bundle to calculate the correct coin state.")
1038+
1039+
# Safety check: ensure new_amount is not negative
1040+
if new_amount < 0:
1041+
raise ValueError(f"Calculated new amount {new_amount} is negative. This indicates an error in the calculation.")
1042+
1043+
# Update for next iteration
1044+
previous_coin: Coin = current_coin
1045+
current_coin = Coin(
1046+
previous_coin.name(),
1047+
current_coin.puzzle_hash,
1048+
new_amount
1049+
)
1050+
current_lineage_proof = LineageProof(
1051+
previous_coin.parent_coin_info,
1052+
construct_singleton_inner_puzzle(derivation.prefarm_info).get_tree_hash(),
1053+
previous_coin.amount,
1054+
)
1055+
1056+
except Exception as e:
1057+
raise ValueError(f"Error processing previous spend file {spend_file}: {e}")
1058+
9971059
# Get any p2_singletons to spend
9981060
if absorb_available_payments:
9991061
max_num: Optional[uint32] = (
@@ -1010,10 +1072,10 @@ async def do_command():
10101072

10111073
# Get the spend bundle
10121074
singleton_bundle, data_to_sign = get_withdrawal_spend_info(
1013-
current_singleton.coin,
1075+
current_coin,
10141076
pubkey_list,
10151077
derivation,
1016-
current_singleton.lineage_proof,
1078+
current_lineage_proof,
10171079
amount,
10181080
clawforward_ph,
10191081
p2_singletons_to_claim=p2_singletons,

0 commit comments

Comments
 (0)