Skip to content

Commit fa2eeb0

Browse files
committed
add support for stake pool creation from trezor and tests
1 parent 00ad5bb commit fa2eeb0

File tree

9 files changed

+287
-128
lines changed

9 files changed

+287
-128
lines changed

Cargo.lock

Lines changed: 14 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

wallet/src/account/mod.rs

Lines changed: 112 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,10 @@ impl<K: AccountKeyChains> Account<K> {
433433
}
434434
}
435435

436-
let selected_inputs = selected_inputs.into_iter().flat_map(|x| x.1.into_output_pairs());
436+
let selected_inputs = selected_inputs
437+
.into_iter()
438+
.flat_map(|x| x.1.into_output_pairs())
439+
.map(|(inp, out)| (inp, out, None));
437440

438441
let pool_data_getter = |pool_id: &PoolId| self.output_cache.pool_data(*pool_id).ok();
439442
request.with_inputs(selected_inputs, &pool_data_getter)
@@ -728,7 +731,7 @@ impl<K: AccountKeyChains> Account<K> {
728731
.ok_or(WalletError::NoUtxos)?;
729732

730733
let mut req = SendRequest::new()
731-
.with_inputs([(tx_input, input_utxo.clone())], &|id| {
734+
.with_inputs([(tx_input, input_utxo.clone(), None)], &|id| {
732735
(*id == pool_id).then_some(pool_data)
733736
})?
734737
.with_outputs([output]);
@@ -1324,6 +1327,107 @@ impl<K: AccountKeyChains> Account<K> {
13241327
)
13251328
}
13261329

1330+
pub fn create_stake_pool_tx_with_vrf_key(
1331+
&mut self,
1332+
db_tx: &mut impl WalletStorageWriteUnlocked,
1333+
mut stake_pool_arguments: StakePoolCreationArguments,
1334+
median_time: BlockTimestamp,
1335+
fee_rate: CurrentFeeRate,
1336+
) -> WalletResult<SendRequest> {
1337+
let Some(vrf_public_key) = stake_pool_arguments.vrf_public_key.take() else {
1338+
return Err(WalletError::VrfKeyMustBeProvided);
1339+
};
1340+
1341+
self.create_stake_pool_tx_impl(
1342+
stake_pool_arguments,
1343+
db_tx,
1344+
vrf_public_key,
1345+
median_time,
1346+
fee_rate,
1347+
)
1348+
}
1349+
1350+
fn create_stake_pool_tx_impl(
1351+
&mut self,
1352+
stake_pool_arguments: StakePoolCreationArguments,
1353+
db_tx: &mut impl WalletStorageWriteUnlocked,
1354+
vrf_public_key: VRFPublicKey,
1355+
median_time: BlockTimestamp,
1356+
fee_rate: CurrentFeeRate,
1357+
) -> Result<SendRequest, WalletError> {
1358+
let staker = match stake_pool_arguments.staker_key {
1359+
Some(staker) => match staker {
1360+
Destination::PublicKey(_) => staker,
1361+
// Note: technically it's possible to create a pool with PublicKeyHash as the staker,
1362+
// the pool will seem to work and will actually try producing blocks. However,
1363+
// the produced blocks will be rejected by chainstate, see get_staking_kernel_destination
1364+
// in `consensus`.
1365+
Destination::AnyoneCanSpend
1366+
| Destination::PublicKeyHash(_)
1367+
| Destination::ScriptHash(_)
1368+
| Destination::ClassicMultisig(_) => {
1369+
return Err(WalletError::StakerDestinationMustBePublicKey)
1370+
}
1371+
},
1372+
None => Destination::PublicKey(
1373+
self.key_chain.issue_key(db_tx, KeyPurpose::ReceiveFunds)?.into_public_key(),
1374+
),
1375+
};
1376+
1377+
// the first UTXO is needed in advance to calculate pool_id, so just make a dummy one
1378+
// and then replace it with when we can calculate the pool_id
1379+
let dummy_pool_id = PoolId::new(Uint256::from_u64(0).into());
1380+
let dummy_stake_output = make_stake_output(
1381+
dummy_pool_id,
1382+
StakePoolCreationResolvedArguments {
1383+
amount: stake_pool_arguments.amount,
1384+
margin_ratio_per_thousand: stake_pool_arguments.margin_ratio_per_thousand,
1385+
cost_per_block: stake_pool_arguments.cost_per_block,
1386+
decommission_key: stake_pool_arguments.decommission_key,
1387+
staker_key: staker,
1388+
vrf_public_key,
1389+
},
1390+
);
1391+
let request = SendRequest::new().with_outputs([dummy_stake_output]);
1392+
let mut request = self.select_inputs_for_send_request(
1393+
request,
1394+
SelectedInputs::Utxos(vec![]),
1395+
None,
1396+
BTreeMap::new(),
1397+
db_tx,
1398+
median_time,
1399+
fee_rate,
1400+
None,
1401+
)?;
1402+
1403+
let input0_outpoint = crate::utils::get_first_utxo_outpoint(request.inputs())?;
1404+
let new_pool_id = pos_accounting::make_pool_id(input0_outpoint);
1405+
1406+
// update the dummy_pool_id with the new pool_id
1407+
let old_pool_id = request
1408+
.get_outputs_mut()
1409+
.iter_mut()
1410+
.find_map(|out| match out {
1411+
TxOutput::CreateStakePool(pool_id, _) if *pool_id == dummy_pool_id => Some(pool_id),
1412+
TxOutput::CreateStakePool(_, _)
1413+
| TxOutput::Burn(_)
1414+
| TxOutput::Transfer(_, _)
1415+
| TxOutput::DelegateStaking(_, _)
1416+
| TxOutput::LockThenTransfer(_, _, _)
1417+
| TxOutput::CreateDelegationId(_, _)
1418+
| TxOutput::ProduceBlockFromStake(_, _)
1419+
| TxOutput::IssueFungibleToken(_)
1420+
| TxOutput::IssueNft(_, _, _)
1421+
| TxOutput::DataDeposit(_)
1422+
| TxOutput::Htlc(_, _)
1423+
| TxOutput::CreateOrder(_) => None,
1424+
})
1425+
.expect("find output with dummy_pool_id");
1426+
*old_pool_id = new_pool_id;
1427+
1428+
Ok(request)
1429+
}
1430+
13271431
pub fn pool_exists(&self, pool_id: PoolId) -> bool {
13281432
self.output_cache.pool_data(pool_id).is_ok()
13291433
}
@@ -2200,86 +2304,21 @@ impl<K: AccountKeyChains + VRFAccountKeyChains> Account<K> {
22002304
pub fn create_stake_pool_tx(
22012305
&mut self,
22022306
db_tx: &mut impl WalletStorageWriteUnlocked,
2203-
stake_pool_arguments: StakePoolCreationArguments,
2307+
mut stake_pool_arguments: StakePoolCreationArguments,
22042308
median_time: BlockTimestamp,
22052309
fee_rate: CurrentFeeRate,
22062310
) -> WalletResult<SendRequest> {
2207-
// TODO: Use other accounts here
2208-
let staker = match stake_pool_arguments.staker_key {
2209-
Some(staker) => match staker {
2210-
Destination::PublicKey(_) => staker,
2211-
// Note: technically it's possible to create a pool with PublicKeyHash as the staker,
2212-
// the pool will seem to work and will actually try producing blocks. However,
2213-
// the produced blocks will be rejected by chainstate, see get_staking_kernel_destination
2214-
// in `consensus`.
2215-
Destination::AnyoneCanSpend
2216-
| Destination::PublicKeyHash(_)
2217-
| Destination::ScriptHash(_)
2218-
| Destination::ClassicMultisig(_) => {
2219-
return Err(WalletError::StakerDestinationMustBePublicKey)
2220-
}
2221-
},
2222-
None => Destination::PublicKey(
2223-
self.key_chain.issue_key(db_tx, KeyPurpose::ReceiveFunds)?.into_public_key(),
2224-
),
2225-
};
2226-
let vrf_public_key = match stake_pool_arguments.vrf_public_key {
2311+
let vrf_public_key = match stake_pool_arguments.vrf_public_key.take() {
22272312
Some(vrf_public_key) => vrf_public_key,
22282313
None => self.get_vrf_public_key(db_tx)?,
22292314
};
2230-
2231-
// the first UTXO is needed in advance to calculate pool_id, so just make a dummy one
2232-
// and then replace it with when we can calculate the pool_id
2233-
let dummy_pool_id = PoolId::new(Uint256::from_u64(0).into());
2234-
let dummy_stake_output = make_stake_output(
2235-
dummy_pool_id,
2236-
StakePoolCreationResolvedArguments {
2237-
amount: stake_pool_arguments.amount,
2238-
margin_ratio_per_thousand: stake_pool_arguments.margin_ratio_per_thousand,
2239-
cost_per_block: stake_pool_arguments.cost_per_block,
2240-
decommission_key: stake_pool_arguments.decommission_key,
2241-
staker_key: staker,
2242-
vrf_public_key,
2243-
},
2244-
);
2245-
let request = SendRequest::new().with_outputs([dummy_stake_output]);
2246-
let mut request = self.select_inputs_for_send_request(
2247-
request,
2248-
SelectedInputs::Utxos(vec![]),
2249-
None,
2250-
BTreeMap::new(),
2315+
self.create_stake_pool_tx_impl(
2316+
stake_pool_arguments,
22512317
db_tx,
2318+
vrf_public_key,
22522319
median_time,
22532320
fee_rate,
2254-
None,
2255-
)?;
2256-
2257-
let input0_outpoint = crate::utils::get_first_utxo_outpoint(request.inputs())?;
2258-
let new_pool_id = pos_accounting::make_pool_id(input0_outpoint);
2259-
2260-
// update the dummy_pool_id with the new pool_id
2261-
let old_pool_id = request
2262-
.get_outputs_mut()
2263-
.iter_mut()
2264-
.find_map(|out| match out {
2265-
TxOutput::CreateStakePool(pool_id, _) if *pool_id == dummy_pool_id => Some(pool_id),
2266-
TxOutput::CreateStakePool(_, _)
2267-
| TxOutput::Burn(_)
2268-
| TxOutput::Transfer(_, _)
2269-
| TxOutput::DelegateStaking(_, _)
2270-
| TxOutput::LockThenTransfer(_, _, _)
2271-
| TxOutput::CreateDelegationId(_, _)
2272-
| TxOutput::ProduceBlockFromStake(_, _)
2273-
| TxOutput::IssueFungibleToken(_)
2274-
| TxOutput::IssueNft(_, _, _)
2275-
| TxOutput::DataDeposit(_)
2276-
| TxOutput::Htlc(_, _)
2277-
| TxOutput::CreateOrder(_) => None,
2278-
})
2279-
.expect("find output with dummy_pool_id");
2280-
*old_pool_id = new_pool_id;
2281-
2282-
Ok(request)
2321+
)
22832322
}
22842323
}
22852324

wallet/src/send_request/mod.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use std::collections::BTreeMap;
1717
use std::mem::take;
1818

1919
use common::address::Address;
20+
use common::chain::htlc::HtlcSecret;
2021
use common::chain::output_value::OutputValue;
2122
use common::chain::stakelock::StakePoolData;
2223
use common::chain::timelock::OutputTimeLock::ForBlockCount;
@@ -51,6 +52,8 @@ pub struct SendRequest {
5152

5253
outputs: Vec<TxOutput>,
5354

55+
htlc_secrets: Vec<Option<HtlcSecret>>,
56+
5457
fees: BTreeMap<Currency, Amount>,
5558
}
5659

@@ -196,6 +199,7 @@ impl SendRequest {
196199
destinations: Vec::new(),
197200
inputs: Vec::new(),
198201
outputs: Vec::new(),
202+
htlc_secrets: Vec::new(),
199203
fees: BTreeMap::new(),
200204
}
201205
}
@@ -230,6 +234,7 @@ impl SendRequest {
230234
destinations,
231235
inputs: transaction.inputs().to_vec(),
232236
outputs: transaction.outputs().to_vec(),
237+
htlc_secrets: vec![None; transaction.inputs().len()],
233238
fees: BTreeMap::new(),
234239
})
235240
}
@@ -258,28 +263,35 @@ impl SendRequest {
258263
self.inputs.push(outpoint);
259264
self.destinations.push(destination);
260265
self.utxos.push(None);
266+
self.htlc_secrets.push(None);
261267
}
262268

263269
self
264270
}
265271

266272
pub fn with_inputs<'a, PoolDataGetter>(
267273
mut self,
268-
utxos: impl IntoIterator<Item = (TxInput, TxOutput)>,
274+
utxos: impl IntoIterator<Item = (TxInput, TxOutput, Option<HtlcSecret>)>,
269275
pool_data_getter: &PoolDataGetter,
270276
) -> WalletResult<Self>
271277
where
272278
PoolDataGetter: Fn(&PoolId) -> Option<&'a PoolData>,
273279
{
274-
for (outpoint, txo) in utxos {
280+
for (outpoint, txo, secret) in utxos {
275281
self.inputs.push(outpoint);
282+
let htlc_spending_condition = match &secret {
283+
Some(_) => HtlcSpendingCondition::WithSecret,
284+
None => HtlcSpendingCondition::WithMultisig,
285+
};
286+
276287
self.destinations.push(
277-
get_tx_output_destination(&txo, &pool_data_getter, HtlcSpendingCondition::Skip)
288+
get_tx_output_destination(&txo, &pool_data_getter, htlc_spending_condition)
278289
.ok_or_else(|| {
279290
WalletError::UnsupportedTransactionOutput(Box::new(txo.clone()))
280291
})?,
281292
);
282293
self.utxos.push(Some(txo));
294+
self.htlc_secrets.push(secret);
283295
}
284296

285297
Ok(self)
@@ -312,7 +324,7 @@ impl SendRequest {
312324
vec![None; num_inputs],
313325
utxos,
314326
destinations,
315-
None,
327+
Some(self.htlc_secrets),
316328
additional_info,
317329
)?;
318330
Ok(ptx)

0 commit comments

Comments
 (0)