Skip to content

Commit bcc5901

Browse files
committed
feat(utils): Adds key registration support
Adds support for online, offline, and non-participation key registration transactions to the composer. This enhancement allows users to manage their account's participation in the consensus protocol. Includes relevant parameter structs and composer methods. Also adds integration tests to verify the behavior of the key registration transactions.
1 parent f1dd5c3 commit bcc5901

File tree

5 files changed

+700
-6
lines changed

5 files changed

+700
-6
lines changed

crates/algokit_utils/src/lib.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ pub use testing::{
1111
AlgorandFixture, AlgorandTestContext, algorand_fixture, algorand_fixture_with_config,
1212
};
1313
pub use transactions::{
14-
CommonParams, Composer, ComposerError, ComposerTxn, EmptySigner, PaymentParams, TxnSigner,
15-
TxnSignerGetter,
14+
CommonParams, Composer, ComposerError, ComposerTxn, EmptySigner,
15+
NonParticipationKeyRegistrationParams, OfflineKeyRegistrationParams,
16+
OnlineKeyRegistrationParams, PaymentParams, TxnSigner, TxnSignerGetter,
1617
};

crates/algokit_utils/src/transactions/composer.rs

Lines changed: 196 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ use algod_client::{
44
models::{PendingTransactionResponse, TransactionParams},
55
};
66
use algokit_transact::{
7-
Address, AlgorandMsgpack, FeeParams, PaymentTransactionFields, SignedTransaction, Transaction,
8-
TransactionHeader, Transactions,
7+
Address, AlgorandMsgpack, FeeParams, KeyRegistrationTransactionFields,
8+
PaymentTransactionFields, SignedTransaction, Transaction, TransactionHeader, Transactions,
99
};
1010
use async_trait::async_trait;
1111
use derive_more::Debug;
@@ -98,6 +98,31 @@ pub struct AssetClawbackParams {
9898
pub clawback_target: Address,
9999
}
100100

101+
#[derive(Debug, Clone)]
102+
pub struct OnlineKeyRegistrationParams {
103+
pub common_params: CommonParams,
104+
pub vote_key: [u8; 32],
105+
pub selection_key: [u8; 32],
106+
pub vote_first: u64,
107+
pub vote_last: u64,
108+
pub vote_key_dilution: u64,
109+
pub state_proof_key: Option<[u8; 64]>,
110+
}
111+
112+
#[derive(Debug, Clone)]
113+
pub struct OfflineKeyRegistrationParams {
114+
pub common_params: CommonParams,
115+
/// Whether to prevent the account from ever participating in consensus again.
116+
/// - `false`: Temporary offline (can be brought back online later)
117+
/// - `true`: Permanent offline (account can never participate again)
118+
pub prevent_account_from_ever_participating_again: bool,
119+
}
120+
121+
#[derive(Debug, Clone)]
122+
pub struct NonParticipationKeyRegistrationParams {
123+
pub common_params: CommonParams,
124+
}
125+
101126
#[derive(Debug, Clone)]
102127
pub enum ComposerTxn {
103128
Transaction(Transaction),
@@ -106,6 +131,9 @@ pub enum ComposerTxn {
106131
AssetOptIn(AssetOptInParams),
107132
AssetOptOut(AssetOptOutParams),
108133
AssetClawback(AssetClawbackParams),
134+
OnlineKeyRegistration(OnlineKeyRegistrationParams),
135+
OfflineKeyRegistration(OfflineKeyRegistrationParams),
136+
NonParticipationKeyRegistration(NonParticipationKeyRegistrationParams),
109137
}
110138

111139
impl ComposerTxn {
@@ -124,6 +152,15 @@ impl ComposerTxn {
124152
ComposerTxn::AssetClawback(asset_clawback_params) => {
125153
asset_clawback_params.common_params.clone()
126154
}
155+
ComposerTxn::OnlineKeyRegistration(online_key_reg_params) => {
156+
online_key_reg_params.common_params.clone()
157+
}
158+
ComposerTxn::OfflineKeyRegistration(offline_key_reg_params) => {
159+
offline_key_reg_params.common_params.clone()
160+
}
161+
ComposerTxn::NonParticipationKeyRegistration(non_participation_params) => {
162+
non_participation_params.common_params.clone()
163+
}
127164
_ => CommonParams::default(),
128165
}
129166
}
@@ -205,6 +242,10 @@ impl Composer {
205242
self.built_group.as_ref()
206243
}
207244

245+
pub fn signed_group(&self) -> Option<&Vec<SignedTransaction>> {
246+
self.signed_group.as_ref()
247+
}
248+
208249
#[cfg(feature = "default_http_client")]
209250
pub fn testnet() -> Self {
210251
Composer {
@@ -256,6 +297,29 @@ impl Composer {
256297
self.push(ComposerTxn::AssetClawback(asset_clawback_params))
257298
}
258299

300+
pub fn add_online_key_registration(
301+
&mut self,
302+
online_key_reg_params: OnlineKeyRegistrationParams,
303+
) -> Result<(), String> {
304+
self.push(ComposerTxn::OnlineKeyRegistration(online_key_reg_params))
305+
}
306+
307+
pub fn add_offline_key_registration(
308+
&mut self,
309+
offline_key_reg_params: OfflineKeyRegistrationParams,
310+
) -> Result<(), String> {
311+
self.push(ComposerTxn::OfflineKeyRegistration(offline_key_reg_params))
312+
}
313+
314+
pub fn add_non_participation_key_registration(
315+
&mut self,
316+
non_participation_params: NonParticipationKeyRegistrationParams,
317+
) -> Result<(), String> {
318+
self.push(ComposerTxn::NonParticipationKeyRegistration(
319+
non_participation_params,
320+
))
321+
}
322+
259323
pub fn add_transaction(&mut self, transaction: Transaction) -> Result<(), String> {
260324
self.push(ComposerTxn::Transaction(transaction))
261325
}
@@ -378,6 +442,48 @@ impl Composer {
378442
},
379443
)
380444
}
445+
ComposerTxn::OnlineKeyRegistration(online_key_reg_params) => {
446+
Transaction::KeyRegistration(KeyRegistrationTransactionFields {
447+
header,
448+
vote_key: Some(online_key_reg_params.vote_key),
449+
selection_key: Some(online_key_reg_params.selection_key),
450+
vote_first: Some(online_key_reg_params.vote_first),
451+
vote_last: Some(online_key_reg_params.vote_last),
452+
vote_key_dilution: Some(online_key_reg_params.vote_key_dilution),
453+
state_proof_key: online_key_reg_params.state_proof_key,
454+
non_participation: None,
455+
})
456+
}
457+
ComposerTxn::OfflineKeyRegistration(offline_key_reg_params) => {
458+
Transaction::KeyRegistration(KeyRegistrationTransactionFields {
459+
header,
460+
vote_key: None,
461+
selection_key: None,
462+
vote_first: None,
463+
vote_last: None,
464+
vote_key_dilution: None,
465+
state_proof_key: None,
466+
non_participation: if offline_key_reg_params
467+
.prevent_account_from_ever_participating_again
468+
{
469+
Some(true)
470+
} else {
471+
None
472+
},
473+
})
474+
}
475+
ComposerTxn::NonParticipationKeyRegistration(_) => {
476+
Transaction::KeyRegistration(KeyRegistrationTransactionFields {
477+
header,
478+
vote_key: None,
479+
selection_key: None,
480+
vote_first: None,
481+
vote_last: None,
482+
vote_key_dilution: None,
483+
state_proof_key: None,
484+
non_participation: Some(true),
485+
})
486+
}
381487
};
382488

383489
if calculate_fee {
@@ -731,6 +837,94 @@ mod tests {
731837
assert!(composer.built_group().is_some());
732838
}
733839

840+
#[tokio::test]
841+
async fn test_build_online_key_registration() {
842+
let mut composer = Composer::testnet();
843+
let online_key_reg_params = OnlineKeyRegistrationParams {
844+
common_params: CommonParams {
845+
sender: AddressMother::address(),
846+
signer: None,
847+
rekey_to: None,
848+
note: None,
849+
lease: None,
850+
static_fee: None,
851+
extra_fee: None,
852+
max_fee: None,
853+
validity_window: None,
854+
first_valid_round: None,
855+
last_valid_round: None,
856+
},
857+
vote_key: [1u8; 32],
858+
selection_key: [2u8; 32],
859+
vote_first: 100000,
860+
vote_last: 200000,
861+
vote_key_dilution: 10000,
862+
state_proof_key: Some([3u8; 64]),
863+
};
864+
assert!(
865+
composer
866+
.add_online_key_registration(online_key_reg_params)
867+
.is_ok()
868+
);
869+
assert!(composer.build().await.is_ok());
870+
assert!(composer.built_group().is_some());
871+
}
872+
873+
#[tokio::test]
874+
async fn test_build_offline_key_registration() {
875+
let mut composer = Composer::testnet();
876+
let offline_key_reg_params = OfflineKeyRegistrationParams {
877+
common_params: CommonParams {
878+
sender: AddressMother::address(),
879+
signer: None,
880+
rekey_to: None,
881+
note: None,
882+
lease: None,
883+
static_fee: None,
884+
extra_fee: None,
885+
max_fee: None,
886+
validity_window: None,
887+
first_valid_round: None,
888+
last_valid_round: None,
889+
},
890+
prevent_account_from_ever_participating_again: false,
891+
};
892+
assert!(
893+
composer
894+
.add_offline_key_registration(offline_key_reg_params)
895+
.is_ok()
896+
);
897+
assert!(composer.build().await.is_ok());
898+
assert!(composer.built_group().is_some());
899+
}
900+
901+
#[tokio::test]
902+
async fn test_build_non_participation_key_registration() {
903+
let mut composer = Composer::testnet();
904+
let non_participation_params = NonParticipationKeyRegistrationParams {
905+
common_params: CommonParams {
906+
sender: AddressMother::address(),
907+
signer: None,
908+
rekey_to: None,
909+
note: None,
910+
lease: None,
911+
static_fee: None,
912+
extra_fee: None,
913+
max_fee: None,
914+
validity_window: None,
915+
first_valid_round: None,
916+
last_valid_round: None,
917+
},
918+
};
919+
assert!(
920+
composer
921+
.add_non_participation_key_registration(non_participation_params)
922+
.is_ok()
923+
);
924+
assert!(composer.build().await.is_ok());
925+
assert!(composer.built_group().is_some());
926+
}
927+
734928
#[tokio::test]
735929
async fn test_gather_signatures() {
736930
let mut composer = Composer::new(AlgodClient::testnet(), Some(Arc::new(EmptySigner {})));

crates/algokit_utils/src/transactions/mod.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ pub mod composer;
22

33
// Re-export commonly used transaction types
44
pub use composer::{
5-
CommonParams, Composer, ComposerError, ComposerTxn, EmptySigner, PaymentParams, TxnSigner,
6-
TxnSignerGetter,
5+
CommonParams, Composer, ComposerError, ComposerTxn, EmptySigner,
6+
NonParticipationKeyRegistrationParams, OfflineKeyRegistrationParams,
7+
OnlineKeyRegistrationParams, PaymentParams, TxnSigner, TxnSignerGetter,
78
};

0 commit comments

Comments
 (0)