use crate::error::{Error, Result};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u32)]
#[non_exhaustive]
pub enum OperationId {
Vote = 0,
Comment = 1,
Transfer = 2,
TransferToVesting = 3,
WithdrawVesting = 4,
LimitOrderCreate = 5,
LimitOrderCancel = 6,
FeedPublish = 7,
Convert = 8,
AccountCreate = 9,
AccountUpdate = 10,
WitnessUpdate = 11,
AccountWitnessVote = 12,
AccountWitnessProxy = 13,
Pow = 14,
Custom = 15,
WitnessBlockApprove = 16,
DeleteComment = 17,
CustomJson = 18,
CommentOptions = 19,
SetWithdrawVestingRoute = 20,
LimitOrderCreate2 = 21,
ClaimAccount = 22,
CreateClaimedAccount = 23,
RequestAccountRecovery = 24,
RecoverAccount = 25,
ChangeRecoveryAccount = 26,
EscrowTransfer = 27,
EscrowDispute = 28,
EscrowRelease = 29,
Pow2 = 30,
EscrowApprove = 31,
TransferToSavings = 32,
TransferFromSavings = 33,
CancelTransferFromSavings = 34,
CustomBinary = 35,
DeclineVotingRights = 36,
ResetAccount = 37,
SetResetAccount = 38,
ClaimRewardBalance = 39,
DelegateVestingShares = 40,
AccountCreateWithDelegation = 41,
WitnessSetProperties = 42,
AccountUpdate2 = 43,
CreateProposal = 44,
UpdateProposalVotes = 45,
RemoveProposal = 46,
UpdateProposal = 47,
CollateralizedConvert = 48,
RecurrentTransfer = 49,
FillConvertRequest = 50,
AuthorReward = 51,
CurationReward = 52,
CommentReward = 53,
LiquidityReward = 54,
Interest = 55,
FillVestingWithdraw = 56,
FillOrder = 57,
ShutdownWitness = 58,
FillTransferFromSavings = 59,
Hardfork = 60,
CommentPayoutUpdate = 61,
ReturnVestingDelegation = 62,
CommentBenefactorReward = 63,
ProducerReward = 64,
ClearNullAccountBalance = 65,
ProposalPay = 66,
DhfFunding = 67,
HardforkHive = 68,
HardforkHiveRestore = 69,
DelayedVoting = 70,
ConsolidateTreasuryBalance = 71,
EffectiveCommentVote = 72,
IneffectiveDeleteComment = 73,
DhfConversion = 74,
ExpiredAccountNotification = 75,
ChangedRecoveryAccount = 76,
TransferToVestingCompleted = 77,
PowReward = 78,
VestingSharesSplit = 79,
AccountCreated = 80,
FillCollateralizedConvertRequest = 81,
SystemWarning = 82,
FillRecurrentTransfer = 83,
FailedRecurrentTransfer = 84,
LimitOrderCancelled = 85,
ProducerMissed = 86,
ProposalFee = 87,
CollateralizedConvertImmediateConversion = 88,
EscrowApproved = 89,
EscrowRejected = 90,
ProxyCleared = 91,
DeclinedVotingRights = 92,
}
pub const FIRST_VIRTUAL_OP: u32 = 50;
pub const LAST_OP: u32 = 92;
impl OperationId {
pub fn as_u32(&self) -> u32 {
*self as u32
}
pub fn is_virtual(&self) -> bool {
self.as_u32() >= FIRST_VIRTUAL_OP
}
pub fn name(&self) -> &'static str {
NAMES[self.as_u32() as usize].0
}
pub fn from_u32(id: u32) -> Result<Self> {
if id > LAST_OP {
return Err(Error::Unknown {
kind: "operation id",
name: id.to_string(),
});
}
Ok(NAMES[id as usize].1)
}
pub fn from_name(name: &str) -> Result<Self> {
let name = name.strip_suffix("_operation").unwrap_or(name);
if name == "recurring_transfer" {
return Ok(OperationId::RecurrentTransfer);
}
NAMES
.iter()
.find(|(n, _)| *n == name)
.map(|(_, id)| *id)
.ok_or_else(|| Error::Unknown {
kind: "operation",
name: name.to_string(),
})
}
}
pub fn all_names() -> &'static [&'static str] {
static ONLY_NAMES: std::sync::OnceLock<Vec<&'static str>> = std::sync::OnceLock::new();
ONLY_NAMES
.get_or_init(|| NAMES.iter().map(|(name, _)| *name).collect())
.as_slice()
}
static NAMES: [(&str, OperationId); 93] = [
("vote", OperationId::Vote),
("comment", OperationId::Comment),
("transfer", OperationId::Transfer),
("transfer_to_vesting", OperationId::TransferToVesting),
("withdraw_vesting", OperationId::WithdrawVesting),
("limit_order_create", OperationId::LimitOrderCreate),
("limit_order_cancel", OperationId::LimitOrderCancel),
("feed_publish", OperationId::FeedPublish),
("convert", OperationId::Convert),
("account_create", OperationId::AccountCreate),
("account_update", OperationId::AccountUpdate),
("witness_update", OperationId::WitnessUpdate),
("account_witness_vote", OperationId::AccountWitnessVote),
("account_witness_proxy", OperationId::AccountWitnessProxy),
("pow", OperationId::Pow),
("custom", OperationId::Custom),
("witness_block_approve", OperationId::WitnessBlockApprove),
("delete_comment", OperationId::DeleteComment),
("custom_json", OperationId::CustomJson),
("comment_options", OperationId::CommentOptions),
(
"set_withdraw_vesting_route",
OperationId::SetWithdrawVestingRoute,
),
("limit_order_create2", OperationId::LimitOrderCreate2),
("claim_account", OperationId::ClaimAccount),
("create_claimed_account", OperationId::CreateClaimedAccount),
(
"request_account_recovery",
OperationId::RequestAccountRecovery,
),
("recover_account", OperationId::RecoverAccount),
(
"change_recovery_account",
OperationId::ChangeRecoveryAccount,
),
("escrow_transfer", OperationId::EscrowTransfer),
("escrow_dispute", OperationId::EscrowDispute),
("escrow_release", OperationId::EscrowRelease),
("pow2", OperationId::Pow2),
("escrow_approve", OperationId::EscrowApprove),
("transfer_to_savings", OperationId::TransferToSavings),
("transfer_from_savings", OperationId::TransferFromSavings),
(
"cancel_transfer_from_savings",
OperationId::CancelTransferFromSavings,
),
("custom_binary", OperationId::CustomBinary),
("decline_voting_rights", OperationId::DeclineVotingRights),
("reset_account", OperationId::ResetAccount),
("set_reset_account", OperationId::SetResetAccount),
("claim_reward_balance", OperationId::ClaimRewardBalance),
(
"delegate_vesting_shares",
OperationId::DelegateVestingShares,
),
(
"account_create_with_delegation",
OperationId::AccountCreateWithDelegation,
),
("witness_set_properties", OperationId::WitnessSetProperties),
("account_update2", OperationId::AccountUpdate2),
("create_proposal", OperationId::CreateProposal),
("update_proposal_votes", OperationId::UpdateProposalVotes),
("remove_proposal", OperationId::RemoveProposal),
("update_proposal", OperationId::UpdateProposal),
("collateralized_convert", OperationId::CollateralizedConvert),
("recurrent_transfer", OperationId::RecurrentTransfer),
("fill_convert_request", OperationId::FillConvertRequest),
("author_reward", OperationId::AuthorReward),
("curation_reward", OperationId::CurationReward),
("comment_reward", OperationId::CommentReward),
("liquidity_reward", OperationId::LiquidityReward),
("interest", OperationId::Interest),
("fill_vesting_withdraw", OperationId::FillVestingWithdraw),
("fill_order", OperationId::FillOrder),
("shutdown_witness", OperationId::ShutdownWitness),
(
"fill_transfer_from_savings",
OperationId::FillTransferFromSavings,
),
("hardfork", OperationId::Hardfork),
("comment_payout_update", OperationId::CommentPayoutUpdate),
(
"return_vesting_delegation",
OperationId::ReturnVestingDelegation,
),
(
"comment_benefactor_reward",
OperationId::CommentBenefactorReward,
),
("producer_reward", OperationId::ProducerReward),
(
"clear_null_account_balance",
OperationId::ClearNullAccountBalance,
),
("proposal_pay", OperationId::ProposalPay),
("dhf_funding", OperationId::DhfFunding),
("hardfork_hive", OperationId::HardforkHive),
("hardfork_hive_restore", OperationId::HardforkHiveRestore),
("delayed_voting", OperationId::DelayedVoting),
(
"consolidate_treasury_balance",
OperationId::ConsolidateTreasuryBalance,
),
("effective_comment_vote", OperationId::EffectiveCommentVote),
(
"ineffective_delete_comment",
OperationId::IneffectiveDeleteComment,
),
("dhf_conversion", OperationId::DhfConversion),
(
"expired_account_notification",
OperationId::ExpiredAccountNotification,
),
(
"changed_recovery_account",
OperationId::ChangedRecoveryAccount,
),
(
"transfer_to_vesting_completed",
OperationId::TransferToVestingCompleted,
),
("pow_reward", OperationId::PowReward),
("vesting_shares_split", OperationId::VestingSharesSplit),
("account_created", OperationId::AccountCreated),
(
"fill_collateralized_convert_request",
OperationId::FillCollateralizedConvertRequest,
),
("system_warning", OperationId::SystemWarning),
(
"fill_recurrent_transfer",
OperationId::FillRecurrentTransfer,
),
(
"failed_recurrent_transfer",
OperationId::FailedRecurrentTransfer,
),
("limit_order_cancelled", OperationId::LimitOrderCancelled),
("producer_missed", OperationId::ProducerMissed),
("proposal_fee", OperationId::ProposalFee),
(
"collateralized_convert_immediate_conversion",
OperationId::CollateralizedConvertImmediateConversion,
),
("escrow_approved", OperationId::EscrowApproved),
("escrow_rejected", OperationId::EscrowRejected),
("proxy_cleared", OperationId::ProxyCleared),
("declined_voting_rights", OperationId::DeclinedVotingRights),
];
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_table_index_is_the_operation_id() {
for (index, (name, id)) in NAMES.iter().enumerate() {
assert_eq!(
id.as_u32(),
index as u32,
"{name} is at index {index} but has id {}",
id.as_u32()
);
}
assert_eq!(NAMES.len() as u32, LAST_OP + 1);
}
#[test]
fn names_are_unique() {
let mut seen: Vec<&str> = NAMES.iter().map(|(n, _)| *n).collect();
seen.sort_unstable();
let before = seen.len();
seen.dedup();
assert_eq!(seen.len(), before, "duplicate operation name in the table");
}
#[test]
fn the_post_hf25_operations_exist() {
assert_eq!(
OperationId::from_name("collateralized_convert")
.unwrap()
.as_u32(),
48
);
assert_eq!(
OperationId::from_name("recurrent_transfer")
.unwrap()
.as_u32(),
49
);
}
#[test]
fn beems_misspelling_is_accepted_as_an_alias() {
assert_eq!(
OperationId::from_name("recurring_transfer").unwrap(),
OperationId::RecurrentTransfer
);
assert_eq!(OperationId::RecurrentTransfer.name(), "recurrent_transfer");
}
#[test]
fn virtual_operations_start_at_fifty() {
assert_eq!(OperationId::FillConvertRequest.as_u32(), 50);
assert_eq!(OperationId::ProducerReward.as_u32(), 64);
assert!(OperationId::FillConvertRequest.is_virtual());
assert!(!OperationId::RecurrentTransfer.is_virtual());
assert!(!OperationId::CollateralizedConvert.is_virtual());
}
#[test]
fn round_trips_by_id_and_name() {
for id in 0..=LAST_OP {
let op = OperationId::from_u32(id).unwrap();
assert_eq!(op.as_u32(), id);
assert_eq!(OperationId::from_name(op.name()).unwrap(), op);
let suffixed = format!("{}_operation", op.name());
assert_eq!(OperationId::from_name(&suffixed).unwrap(), op);
}
}
#[test]
fn unknown_ids_and_names_are_errors() {
assert!(OperationId::from_u32(93).is_err());
assert!(OperationId::from_u32(u32::MAX).is_err());
assert!(OperationId::from_name("not_an_operation").is_err());
assert!(OperationId::from_name("collateralized_convertaccount_create").is_err());
}
}