Skip to content

Commit 27ffc02

Browse files
committed
Fix semantic misuse of signature_script field (fixes #227)
- Changed PopulatedInput to include optional redeem_script as third element - Keep signature_script empty for unsigned transactions (as it should be) - Store redeem_script separately and only add to PSKT when needed - Update all usages to handle the new tuple structure This fixes the semantic confusion where redeem_script was incorrectly stored in the signature_script field of TransactionInput.
1 parent ed3d3d6 commit 27ffc02

File tree

3 files changed

+34
-28
lines changed

3 files changed

+34
-28
lines changed

dymension/libs/kaspa/lib/relayer/src/withdraw/hub_to_kaspa.rs

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -37,24 +37,25 @@ use tracing::info;
3737
pub async fn fetch_input_utxos(
3838
kaspa_rpc: &Arc<DynRpcApi>,
3939
address: &kaspa_addresses::Address,
40-
signature_script: Vec<u8>,
40+
redeem_script: Option<Vec<u8>>,
4141
sig_op_count: u8,
4242
network_id: NetworkId,
4343
) -> Result<Vec<PopulatedInput>> {
4444
let utxos = get_utxo_to_spend(&address, kaspa_rpc, network_id).await?;
4545

46-
// Create a vector of "populated" inputs: TransactionInput and UtxoEntry.
46+
// Create a vector of "populated" inputs: TransactionInput, UtxoEntry, and optional redeem_script.
4747
Ok(utxos
4848
.into_iter()
4949
.map(|utxo| {
5050
(
5151
TransactionInput::new(
5252
kaspa_consensus_core::tx::TransactionOutpoint::from(utxo.outpoint),
53-
signature_script.clone(),
54-
0, // sequence does not matter
53+
vec![], // signature_script is empty for unsigned transactions
54+
0, // sequence does not matter
5555
sig_op_count,
5656
),
5757
UtxoEntry::from(utxo.utxo_entry),
58+
redeem_script.clone(),
5859
)
5960
})
6061
.collect())
@@ -125,15 +126,18 @@ pub fn build_withdrawal_pskt(
125126
// in case of hyperinflation
126127

127128
let (escrow_balance, relayer_balance) =
128-
inputs.iter().fold((0, 0), |mut acc, (input, entry)| {
129-
if input.signature_script.is_empty() {
130-
// relayer has empty signature script
131-
acc.1 += entry.amount;
132-
} else {
133-
acc.0 += entry.amount;
134-
}
135-
acc
136-
});
129+
inputs
130+
.iter()
131+
.fold((0, 0), |mut acc, (_input, entry, redeem_script)| {
132+
if redeem_script.is_none() {
133+
// relayer has no redeem script
134+
acc.1 += entry.amount;
135+
} else {
136+
// escrow has redeem script
137+
acc.0 += entry.amount;
138+
}
139+
acc
140+
});
137141

138142
let withdrawal_balance: u64 = outputs.iter().map(|w| w.value).sum();
139143

@@ -241,17 +245,17 @@ fn create_withdrawal_pskt(
241245
let mut pskt = PSKT::<Creator>::default().constructor();
242246

243247
// Add inputs
244-
for (input, entry) in inputs.into_iter() {
248+
for (input, entry, redeem_script) in inputs.into_iter() {
245249
let mut b = InputBuilder::default();
246250

247251
b.utxo_entry(entry)
248252
.previous_outpoint(input.previous_outpoint)
249253
.sig_op_count(input.sig_op_count)
250254
.sighash_type(input_sighash_type());
251255

252-
if !input.signature_script.is_empty() {
256+
if let Some(script) = redeem_script {
253257
// escrow inputs need redeem_script
254-
b.redeem_script(input.signature_script);
258+
b.redeem_script(script);
255259
}
256260

257261
pskt = pskt.input(
@@ -348,7 +352,7 @@ pub(crate) fn extract_current_anchor(
348352
) -> Result<(PopulatedInput, Vec<PopulatedInput>)> {
349353
let anchor_index = escrow_inputs
350354
.iter()
351-
.position(|i| i.0.previous_outpoint == current_anchor)
355+
.position(|(input, _, _)| input.previous_outpoint == current_anchor)
352356
.ok_or(eyre::eyre!(
353357
"Current anchor not found in escrow UTXO set: {current_anchor:?}"
354358
))?; // Should always be found
@@ -368,10 +372,9 @@ fn estimate_mass(
368372
let (inputs, utxo_references): (Vec<_>, Vec<_>) = populated_inputs
369373
.into_iter()
370374
.map(|populated| {
371-
(
372-
populated.0.clone(),
373-
utxo_reference_from_populated_input(populated),
374-
)
375+
let input = populated.0.clone();
376+
let utxo_ref = utxo_reference_from_populated_input(populated);
377+
(input, utxo_ref)
375378
})
376379
.unzip();
377380

dymension/libs/kaspa/lib/relayer/src/withdraw/messages.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ use kaspa_consensus_core::tx::{TransactionInput, TransactionOutpoint, UtxoEntry}
1717
use kaspa_wallet_pskt::bundle::Bundle;
1818
use tracing::info;
1919

20-
pub(crate) type PopulatedInput = (TransactionInput, UtxoEntry);
20+
// (input, entry, optional_redeem_script)
21+
pub(crate) type PopulatedInput = (TransactionInput, UtxoEntry, Option<Vec<u8>>);
2122

2223
/// Processes given messages and returns WithdrawFXG and the very first outpoint
2324
/// (the one preceding all the given transfers; it should be used during process indication).
@@ -74,7 +75,7 @@ pub async fn build_withdrawal_fxg(
7475
let escrow_inputs = fetch_input_utxos(
7576
&relayer.api(),
7677
&escrow_public.addr,
77-
escrow_public.redeem_script.clone(),
78+
Some(escrow_public.redeem_script.clone()),
7879
escrow_public.n() as u8,
7980
relayer.net.network_id,
8081
)
@@ -85,7 +86,7 @@ pub async fn build_withdrawal_fxg(
8586
let relayer_inputs = fetch_input_utxos(
8687
&relayer.api(),
8788
&relayer_address,
88-
vec![],
89+
None,
8990
RELAYER_SIG_OP_COUNT,
9091
relayer.net.network_id,
9192
)

dymension/libs/kaspa/lib/relayer/src/withdraw/sweep.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ pub async fn create_sweeping_bundle(
3131
escrow_inputs: Vec<PopulatedInput>,
3232
relayer_inputs: Vec<PopulatedInput>,
3333
) -> Result<Bundle> {
34-
let sweep_balance = escrow_inputs.iter().map(|(_, e)| e.amount).sum::<u64>();
34+
let sweep_balance = escrow_inputs.iter().map(|(_, e, _)| e.amount).sum::<u64>();
3535

3636
let utxo_iterator = Box::new(
3737
escrow_inputs
@@ -156,7 +156,7 @@ pub fn create_inputs_from_sweeping_bundle(
156156
let relayer_input: PopulatedInput = (
157157
TransactionInput::new(
158158
TransactionOutpoint::new(tx_id, relayer_idx),
159-
vec![],
159+
vec![], // signature_script is empty for unsigned transactions
160160
u64::MAX,
161161
RELAYER_SIG_OP_COUNT,
162162
),
@@ -166,12 +166,13 @@ pub fn create_inputs_from_sweeping_bundle(
166166
UNACCEPTED_DAA_SCORE,
167167
false,
168168
),
169+
None, // relayer has no redeem script
169170
);
170171

171172
let escrow_input: PopulatedInput = (
172173
TransactionInput::new(
173174
TransactionOutpoint::new(tx_id, escrow_idx),
174-
escrow.redeem_script.clone(),
175+
vec![], // signature_script is empty for unsigned transactions
175176
u64::MAX,
176177
escrow.n() as u8,
177178
),
@@ -181,13 +182,14 @@ pub fn create_inputs_from_sweeping_bundle(
181182
UNACCEPTED_DAA_SCORE,
182183
false,
183184
),
185+
Some(escrow.redeem_script.clone()), // escrow has redeem script
184186
);
185187

186188
Ok(vec![relayer_input, escrow_input])
187189
}
188190

189191
pub(crate) fn utxo_reference_from_populated_input(
190-
(input, entry): PopulatedInput,
192+
(input, entry, _redeem_script): PopulatedInput,
191193
) -> UtxoEntryReference {
192194
UtxoEntryReference::from(ClientUtxoEntry {
193195
address: None,

0 commit comments

Comments
 (0)