change the capitalization of the table of contents
What changed, and why it matters
This commit only renames some Rust source directories from capital letters (C_chain, P_chain) to lowercase (c_chain, p_chain). The actual code inside the files is unchanged. It is a cosmetic cleanup with no security relevance.
No security action needed. Treat as routine refactoring.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The diff shows pure file moves/deletes-and-recreates caused by renaming directories to lowercase. Every line of code content is identical between the deleted and added files. No logic, parsing, cryptographic, or transaction-handling behavior was modified. The change is a case-normalization of directory names only.
Changed components
rust/apps/avalanche/src/transactions/c_chain/evm_export.rsrust/apps/avalanche/src/transactions/c_chain/evm_import.rsrust/apps/avalanche/src/transactions/p_chain/add_permissionless_delegator.rsrust/apps/avalanche/src/transactions/p_chain/add_permissionless_validator.rsrust/apps/avalanche/src/transactions/p_chain/node_id.rsrust/apps/avalanche/src/transactions/p_chain/output_owner.rsrust/apps/avalanche/src/transactions/p_chain/signer.rsrust/apps/avalanche/src/transactions/p_chain/validator.rsInspect captured patch +717 / −717
diff --git a/rust/apps/avalanche/src/transactions/C_chain/evm_export.rs b/rust/apps/avalanche/src/transactions/C_chain/evm_export.rs
deleted file mode 100644
index f09d735..0000000
--- a/rust/apps/avalanche/src/transactions/C_chain/evm_export.rs
+++ /dev/null
@@ -1,160 +0,0 @@
-use crate::address::Address;
-use crate::constants::*;
-use crate::errors::{AvaxError, Result};
-use crate::transactions::structs::{
- AvaxFromToInfo, AvaxMethodInfo, AvaxTxInfo, LengthPrefixedVec, ParsedSizeAble,
-};
-use crate::transactions::transferable::TransferableOutput;
-use crate::transactions::tx_header::Header;
-use crate::transactions::{asset_id::AssetId, type_id::TypeId};
-
-use alloc::{
- format,
- string::{String, ToString},
- vec::Vec,
-};
-use bytes::{Buf, Bytes};
-use core::convert::TryFrom;
-
-#[derive(Debug, Clone)]
-pub struct EvmInput {
- address: Address,
- amount: u64,
- asset_id: AssetId,
- nonce: u64,
-}
-
-impl TryFrom<Bytes> for EvmInput {
- type Error = AvaxError;
-
- fn try_from(mut bytes: Bytes) -> Result<Self> {
- let address = Address::try_from(bytes.split_to(C_CHAIN_ADDRESS_LEN))?;
- let amount = bytes.get_u64();
- let asset_id = AssetId::try_from(bytes.split_to(ASSET_ID_LEN))?;
- let nonce = bytes.get_u64();
- Ok(EvmInput {
- address,
- amount,
- asset_id,
- nonce,
- })
- }
-}
-
-impl ParsedSizeAble for EvmInput {
- fn parsed_size(&self) -> usize {
- C_CHAIN_ADDRESS_LEN + 8 + ASSET_ID_LEN + 8
- }
-}
-
-impl EvmInput {
- pub fn get_amount(&self) -> u64 {
- self.amount
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct ExportTx {
- codec_id: u16,
- type_id: TypeId,
- tx_header: Header,
- dest_chain: [u8; BLOCKCHAIN_ID_LEN],
- inputs: LengthPrefixedVec<EvmInput>,
- outputs: LengthPrefixedVec<TransferableOutput>,
-}
-
-impl AvaxTxInfo for ExportTx {
- fn get_total_input_amount(&self) -> u64 {
- self.inputs
- .iter()
- .fold(0, |acc, item| acc + item.get_amount())
- }
-
- fn get_total_output_amount(&self) -> u64 {
- self.outputs
- .iter()
- .fold(0, |acc, item| acc + item.get_amount())
- }
-
- fn get_outputs_addresses(&self) -> Vec<AvaxFromToInfo> {
- self.outputs
- .iter()
- .map(|output| {
- AvaxFromToInfo::from(
- output.get_amount(),
- output.get_addresses(),
- C_CHAIN_PREFIX.to_string(),
- )
- })
- .collect()
- }
-
- fn get_method_info(&self) -> Option<AvaxMethodInfo> {
- let method = match (self.type_id, self.dest_chain) {
- (TypeId::CchainExportTx, chain) => {
- let dest = match chain {
- X_BLOCKCHAIN_ID | X_TEST_BLOCKCHAIN_ID => "X",
- P_BLOCKCHAIN_ID => "P",
- C_BLOCKCHAIN_ID | C_TEST_BLOCKCHAIN_ID => "C",
- _ => "Unknown",
- };
- format!("C to {dest} Export")
- }
- _ => "Unknown".to_string(),
- };
- Some(AvaxMethodInfo::from_string(method).with_method_key("Export Tx".to_string()))
- }
-
- fn get_network_key(&self) -> String {
- "Export Tx".to_string()
- }
-}
-
-impl TryFrom<Bytes> for ExportTx {
- type Error = AvaxError;
-
- fn try_from(mut bytes: Bytes) -> Result<Self> {
- let codec_id = bytes.get_u16();
- let type_id = TypeId::try_from(bytes.get_u32())?;
- let tx_header = Header::try_from(bytes.clone())?;
- bytes.advance(tx_header.parsed_size());
-
- let mut dest_chain = [0u8; 32];
- bytes.copy_to_slice(&mut dest_chain);
-
- let inputs = LengthPrefixedVec::<EvmInput>::try_from(bytes.clone())?;
- bytes.advance(inputs.parsed_size());
-
- let outputs = LengthPrefixedVec::<TransferableOutput>::try_from(bytes.clone())?;
- bytes.advance(outputs.parsed_size());
-
- Ok(ExportTx {
- codec_id,
- type_id,
- tx_header,
- dest_chain,
- inputs,
- outputs,
- })
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn test_avax_c_export() {
- // f22ATVQT11pSdMwvWLwXgeHNshauTDZxzErFKT5nd64esWXjU
- let input_bytes = "000000000001000000057fc93d85c6d62c5b2ac0b519c87010ea5294012d1e407030d6acd0021cac10d5000000000000000000000000000000000000000000000000000000000000000000000001a9b548da818607e83cbcf6802370691948cbd4160000000005f60cde3d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa0000000000000000000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa000000070000000005f5e1000000000000000000000000010000000169bc9b5b6cbbbd490abbd79a37ad6cd643be87ab000000010000000900000001683728d1c682680aa80e702dcc8e349f79b5da8cac4b7fb59b28750f1c4c977014a70f69d5ef97dec616a21d69e3560cf2db78257710af3c03f959a487bcab04000218c13e";
- let binary_data = hex::decode(input_bytes).expect("Failed to decode hex string");
- let bytes = Bytes::from(binary_data);
- let result = ExportTx::try_from(bytes.clone()).unwrap();
- assert_eq!(result.codec_id, 0);
- assert_eq!(result.type_id, TypeId::CchainExportTx);
- assert_eq!(result.dest_chain, P_BLOCKCHAIN_ID);
- assert_eq!(result.inputs.get_len(), 1);
- assert_eq!(result.outputs.get_len(), 1);
- assert_eq!(result.inputs.get(0).unwrap().amount, 100011230);
- }
-}
diff --git a/rust/apps/avalanche/src/transactions/C_chain/evm_import.rs b/rust/apps/avalanche/src/transactions/C_chain/evm_import.rs
deleted file mode 100644
index e3513f9..0000000
--- a/rust/apps/avalanche/src/transactions/C_chain/evm_import.rs
+++ /dev/null
@@ -1,151 +0,0 @@
-use crate::address::Address;
-use crate::constants::*;
-use crate::errors::{AvaxError, Result};
-use crate::transactions::structs::{
- AvaxFromToInfo, AvaxMethodInfo, AvaxTxInfo, LengthPrefixedVec, ParsedSizeAble,
-};
-use crate::transactions::transferable::TransferableInput;
-use crate::transactions::tx_header::Header;
-use crate::transactions::{asset_id::AssetId, type_id::TypeId};
-
-use alloc::{
- string::{String, ToString},
- vec::Vec,
-};
-use bytes::{Buf, Bytes};
-use core::convert::TryFrom;
-
-#[derive(Debug, Clone)]
-pub struct EvmOutput {
- address: Address,
- amount: u64,
- asset_id: AssetId,
-}
-
-impl TryFrom<Bytes> for EvmOutput {
- type Error = AvaxError;
-
- fn try_from(mut bytes: Bytes) -> Result<Self> {
- let address = Address::try_from(bytes.split_to(C_CHAIN_ADDRESS_LEN))?;
- let amount = bytes.get_u64();
- let asset_id = AssetId::try_from(bytes.split_to(ASSET_ID_LEN))?;
- Ok(EvmOutput {
- address,
- amount,
- asset_id,
- })
- }
-}
-
-impl ParsedSizeAble for EvmOutput {
- fn parsed_size(&self) -> usize {
- C_CHAIN_ADDRESS_LEN + 8 + ASSET_ID_LEN
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct ImportTx {
- codec_id: u16,
- type_id: TypeId,
- tx_header: Header,
- source_chain: [u8; BLOCKCHAIN_ID_LEN],
- inputs: LengthPrefixedVec<TransferableInput>,
- outputs: LengthPrefixedVec<EvmOutput>,
-}
-
-impl AvaxTxInfo for ImportTx {
- fn get_total_input_amount(&self) -> u64 {
- self.inputs
- .iter()
- .fold(0, |acc, item| acc + item.get_amount())
- }
-
- fn get_total_output_amount(&self) -> u64 {
- self.outputs.iter().fold(0, |acc, item| acc + item.amount)
- }
-
- fn get_outputs_addresses(&self) -> Vec<AvaxFromToInfo> {
- self.outputs
- .iter()
- .map(|output| {
- AvaxFromToInfo::from(
- output.amount,
- vec![output.address.to_evm_address()],
- C_CHAIN_PREFIX.to_string(),
- )
- })
- .collect()
- }
-
- fn get_network_key(&self) -> String {
- "Import Tx".to_string()
- }
-
- fn get_method_info(&self) -> Option<AvaxMethodInfo> {
- let method = match self.source_chain {
- X_BLOCKCHAIN_ID | X_TEST_BLOCKCHAIN_ID => "Sending from X-Chain",
- P_BLOCKCHAIN_ID => "Sending from P-Chain",
- C_BLOCKCHAIN_ID | C_TEST_BLOCKCHAIN_ID => "Sending from C-Chain",
- _ => "Unknown",
- };
-
- Some(
- AvaxMethodInfo::from_string(method.to_string())
- .with_method_key("Import Tx".to_string()),
- )
- }
-
- fn get_import_tx_fee(&self) -> u64 {
- 0
- }
-}
-
-impl TryFrom<Bytes> for ImportTx {
- type Error = AvaxError;
-
- fn try_from(mut bytes: Bytes) -> Result<Self> {
- let codec_id = bytes.get_u16();
- let type_id = TypeId::try_from(bytes.get_u32())?;
- let tx_header = Header::try_from(bytes.clone())?;
- bytes.advance(tx_header.parsed_size());
-
- let mut source_chain = [0u8; 32];
- bytes.copy_to_slice(&mut source_chain);
-
- let inputs = LengthPrefixedVec::<TransferableInput>::try_from(bytes.clone())?;
- bytes.advance(inputs.parsed_size());
-
- let outputs = LengthPrefixedVec::<EvmOutput>::try_from(bytes.clone())?;
- bytes.advance(outputs.parsed_size());
-
- Ok(ImportTx {
- codec_id,
- type_id,
- tx_header,
- source_chain,
- inputs,
- outputs,
- })
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn test_avax_c_import() {
- let input_bytes = "000000000000000000057fc93d85c6d62c5b2ac0b519c87010ea5294012d1e407030d6acd0021cac10d5ab68eb1ee142a05cfe768c36e11f0b596db5a3c6c77aabe665dad9e638ca94f7000000013f407c793a9a31b5f281623e479bcaee760be3da54969b85f44ffc51a67c1c64000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa000000050000000005f5e1000000000100000000000000013fe51338992d913b6a999693d7c345646ca3bb4c0000000005f5b5223d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa0000000100000009000000017e79e5f2fba29e48d9d329f23e8e27bf20d79714555bb04a175e57136b95b282528654583b1fd568e7f8322962fb31e4174896a50114511d124bf74a92559c1f0048c457c5";
- let binary_data = hex::decode(input_bytes).expect("Failed to decode hex string");
- let bytes = Bytes::from(binary_data);
- let result = ImportTx::try_from(bytes.clone()).unwrap();
- assert_eq!(result.type_id, TypeId::BaseTx);
- assert_eq!(result.codec_id, 0);
- assert_eq!(result.inputs.get_len(), 1);
- assert_eq!(result.outputs.get_len(), 1);
-
- assert_eq!(result.source_chain, X_TEST_BLOCKCHAIN_ID);
- assert_eq!(result.get_total_output_amount(), 99988770);
- assert_eq!(result.get_total_input_amount(), 100000000);
- }
-}
diff --git a/rust/apps/avalanche/src/transactions/P_chain/add_permissionless_delegator.rs b/rust/apps/avalanche/src/transactions/P_chain/add_permissionless_delegator.rs
deleted file mode 100644
index 68b6278..0000000
--- a/rust/apps/avalanche/src/transactions/P_chain/add_permissionless_delegator.rs
+++ /dev/null
@@ -1,120 +0,0 @@
-use super::{output_owner::OutputOwner, validator::Validator};
-use crate::constants::*;
-use crate::errors::{AvaxError, Result};
-use crate::transactions::base_tx::BaseTx;
-use crate::transactions::structs::{
- AvaxFromToInfo, AvaxMethodInfo, AvaxTxInfo, LengthPrefixedVec, ParsedSizeAble,
-};
-use crate::transactions::subnet_id::SubnetId;
-use crate::transactions::transferable::TransferableOutput;
-use alloc::{
- string::{String, ToString},
- vec::Vec,
-};
-use bytes::{Buf, Bytes};
-use core::convert::TryFrom;
-
-#[derive(Debug)]
-pub struct AddPermissionlessDelegatorTx {
- base_tx: BaseTx,
- validator: Validator,
- subnet_id: SubnetId,
- stake_out: LengthPrefixedVec<TransferableOutput>,
- delegator_owner: OutputOwner,
-}
-
-impl AvaxTxInfo for AddPermissionlessDelegatorTx {
- fn get_total_input_amount(&self) -> u64 {
- self.base_tx.get_total_input_amount()
- }
-
- fn get_total_output_amount(&self) -> u64 {
- self.base_tx.get_total_output_amount()
- + self
- .stake_out
- .iter()
- .fold(0, |acc, item| acc + item.get_amount())
- }
-
- fn get_outputs_addresses(&self) -> Vec<AvaxFromToInfo> {
- self.base_tx
- .get_outputs_addresses()
- .into_iter()
- .chain(self.stake_out.iter().map(|output| {
- AvaxFromToInfo::from(
- output.get_amount(),
- output.get_addresses(),
- X_P_CHAIN_PREFIX.to_string(),
- )
- }))
- .collect()
- }
-
- fn get_method_info(&self) -> Option<AvaxMethodInfo> {
- Some(AvaxMethodInfo::from(
- "AddPermlessDelegator".to_string(),
- self.validator.start_time,
- self.validator.end_time,
- ))
- }
-
- fn get_network(&self) -> Option<String> {
- Some("Avalanche P-Chain".to_string())
- }
-
- fn get_subnet_id(&self) -> Option<String> {
- Some("Primary Subnet".to_string())
- }
-
- fn get_reward_address(&self) -> Option<String> {
- Some(
- self.delegator_owner
- .addresses
- .get(0)
- .map(|addr| addr.encode())
- .unwrap_or_default(),
- )
- }
-}
-
-impl TryFrom<Bytes> for AddPermissionlessDelegatorTx {
- type Error = AvaxError;
-
- fn try_from(mut bytes: Bytes) -> Result<Self> {
- let base_tx = BaseTx::try_from(bytes.clone())?;
- bytes.advance(base_tx.parsed_size());
-
- let validator = Validator::try_from(bytes.clone())?;
- bytes.advance(validator.parsed_size());
-
- let subnet_id = SubnetId::try_from(bytes.clone())?;
- bytes.advance(SUBNET_ID_LEN);
-
- let stake_out = LengthPrefixedVec::<TransferableOutput>::try_from(bytes.clone())?;
- bytes.advance(stake_out.parsed_size());
-
- let delegator_owner = OutputOwner::try_from(bytes.clone())?;
- bytes.advance(delegator_owner.parsed_size());
-
- Ok(AddPermissionlessDelegatorTx {
- base_tx,
- validator,
- subnet_id,
- stake_out,
- delegator_owner,
- })
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn test_add_permissionless_delegator() {
- let input_bytes = "00000000001a000000050000000000000000000000000000000000000000000000000000000000000000000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000007000000003b9a9e0400000000000000000000000100000001e0beb088f94b8224eb5d6f1115561d7173cd6e7f00000002295a7b15e26c6cafda8883afd0f724e0e0b1dad4517148711434cb96fb3c8a61000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000005000000003b9aca0000000001000000006109bc613691602ca0811312357676416252412a87ded6c56c240baba1afe042000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000005000000003b9aca000000000100000000000000007072a3df0cd056d9b9ef00c09630bad3027dc312000000006760c3b100000000676215a9000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000007000000003b9aca0000000000000000000000000100000001e0beb088f94b8224eb5d6f1115561d7173cd6e7f0000000b00000000000000000000000100000001a0f4d4d9a0ea219da5ed5499ad083e1942a0846a000000020000000900000001438c3a393f49bb27791ca830effec456c2642a487ee4ce89300dd2e591fc22ab6b2aa8e08515ca229f2a2f14168700e05a1f96bd61d1fc3ab31e9e71ef9f16bb000000000900000001438c3a393f49bb27791ca830effec456c2642a487ee4ce89300dd2e591fc22ab6b2aa8e08515ca229f2a2f14168700e05a1f96bd61d1fc3ab31e9e71ef9f16bb005c3d047c";
- let bytes = Bytes::from(hex::decode(input_bytes).expect("Failed to decode hex string"));
- let result = AddPermissionlessDelegatorTx::try_from(bytes.clone()).unwrap();
- assert_eq!(result.base_tx.get_blockchain_id(), P_BLOCKCHAIN_ID);
- }
-}
diff --git a/rust/apps/avalanche/src/transactions/P_chain/add_permissionless_validator.rs b/rust/apps/avalanche/src/transactions/P_chain/add_permissionless_validator.rs
deleted file mode 100644
index 8fb2d62..0000000
--- a/rust/apps/avalanche/src/transactions/P_chain/add_permissionless_validator.rs
+++ /dev/null
@@ -1,140 +0,0 @@
-use super::output_owner::OutputOwner;
-use super::signer::Signer;
-use super::validator::Validator;
-use crate::constants::*;
-use crate::errors::{AvaxError, Result};
-use crate::transactions::base_tx::BaseTx;
-use crate::transactions::structs::{
- AvaxFromToInfo, AvaxMethodInfo, AvaxTxInfo, LengthPrefixedVec, ParsedSizeAble,
-};
-use crate::transactions::subnet_id::SubnetId;
-use crate::transactions::transferable::TransferableOutput;
-use alloc::{
- string::{String, ToString},
- vec::Vec,
-};
-use bytes::{Buf, Bytes};
-use core::convert::TryFrom;
-
-#[derive(Debug)]
-pub struct AddPermissionlessValidatorTx {
- base_tx: BaseTx,
- validator: Validator,
- subnet_id: SubnetId,
- signer: Signer,
- stake_out: LengthPrefixedVec<TransferableOutput>,
- validator_owner: OutputOwner,
- delegator_owner: OutputOwner,
- delegator_share: u32,
-}
-
-impl AvaxTxInfo for AddPermissionlessValidatorTx {
- fn get_total_input_amount(&self) -> u64 {
- self.base_tx.get_total_input_amount()
- }
-
- fn get_total_output_amount(&self) -> u64 {
- self.base_tx.get_total_output_amount()
- + self
- .stake_out
- .iter()
- .fold(0, |acc, item| acc + item.get_amount())
- }
-
- fn get_outputs_addresses(&self) -> Vec<AvaxFromToInfo> {
- self.base_tx
- .get_outputs_addresses()
- .into_iter()
- .chain(self.stake_out.iter().map(|output| {
- AvaxFromToInfo::from(
- output.get_amount(),
- output.get_addresses(),
- X_P_CHAIN_PREFIX.to_string(),
- )
- }))
- .collect()
- }
-
- fn get_method_info(&self) -> Option<AvaxMethodInfo> {
- Some(AvaxMethodInfo::from(
- "AddPermlessValidator".to_string(),
- self.validator.start_time,
- self.validator.end_time,
- ))
- }
-
- fn get_subnet_id(&self) -> Option<String> {
- Some("Primary Subnet".to_string())
- }
-
- fn get_network(&self) -> Option<String> {
- Some("Avalanche P-Chain".to_string())
- }
-
- fn get_reward_address(&self) -> Option<String> {
- Some(
- self.validator_owner
- .addresses
- .get(0)
- .map(|addr| addr.encode())
- .unwrap_or_default(),
- )
- }
-}
-
-impl TryFrom<Bytes> for AddPermissionlessValidatorTx {
- type Error = AvaxError;
-
- fn try_from(mut bytes: Bytes) -> Result<Self> {
- let base_tx = BaseTx::try_from(bytes.clone())?;
- bytes.advance(base_tx.parsed_size());
-
- let validator = Validator::try_from(bytes.clone())?;
- bytes.advance(validator.parsed_size());
-
- let subnet_id = SubnetId::try_from(bytes.clone())?;
- bytes.advance(SUBNET_ID_LEN);
-
- let signer = Signer::try_from(bytes.clone())?;
- bytes.advance(PROOF_OF_POSESSION_PUBKEY_LEN + PROOF_OF_POSESSION_SIGNATURE_LEN);
-
- let stake_out = LengthPrefixedVec::<TransferableOutput>::try_from(bytes.clone())?;
- bytes.advance(stake_out.parsed_size());
-
- let validator_owner = OutputOwner::try_from(bytes.clone())?;
- bytes.advance(validator_owner.parsed_size());
-
- let delegator_owner = OutputOwner::try_from(bytes.clone())?;
- bytes.advance(delegator_owner.parsed_size());
-
- let delegator_share = bytes.get_u32();
-
- Ok(AddPermissionlessValidatorTx {
- base_tx,
- validator,
- subnet_id,
- signer,
- stake_out,
- validator_owner,
- delegator_owner,
- delegator_share,
- })
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn test_add_permissionless_validator() {
- // 23HbZUQ7ijjrDHfqnjKpd4MTMRY18Gc2JxCz79ZBwZAsCLfntb
- let input_bytes = "000000000019000000050000000000000000000000000000000000000000000000000000000000000000000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa000000070000000005e57a370000000000000000000000010000000161cd7d26c72edc631d4114d6eef2c4e069ec9206000000020033b7653ffbf19a2352591e8b6aea8e7c75f38d8e8f5f781cf15aad8425010a000000003d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000005000000003b9ab9d9000000010000000029ec95b1c9df6cd2598852d78fea7766c1aece1a7b5d24f6cf58adc98107f927000000003d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000005000000003b8ab46a0000000100000000000000009e843011540909cc62c64dc85a8a1507813777410000000067614160000000006851245000000000713fb30000000000000000000000000000000000000000000000000000000000000000000000001c87c87cef2e92bface778c711c752168a6e858d58ba62463e8bab336f9b05c98c695acf3c7da02b05c667ce5627e63a60ad53ad7da84734084394dedf6b3c4bb6c85922c2b08b09c55508d49d348ad0dcd9678be58197fef69bad862b1d170f4b0c24f189d9d4b6b5103d28b5e8146d305e28d3dcfb3279f089c7152535a24800c7a1a212868a5c76e3559ea9d4a64d9d000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa0000000700000000713fb3000000000000000000000000010000000161cd7d26c72edc631d4114d6eef2c4e069ec92060000000b0000000000000000000000010000000161cd7d26c72edc631d4114d6eef2c4e069ec92060000000b0000000000000000000000010000000161cd7d26c72edc631d4114d6eef2c4e069ec92060000c350";
- let bytes = Bytes::from(hex::decode(input_bytes).expect("Failed to decode hex string"));
- let result = AddPermissionlessValidatorTx::try_from(bytes.clone()).unwrap();
- assert_eq!(
- "avax1v8xh6fk89mwxx82pzntwaukyup57eysx7xzuwa".to_string(),
- result.get_reward_address().unwrap()
- );
- }
-}
diff --git a/rust/apps/avalanche/src/transactions/P_chain/node_id.rs b/rust/apps/avalanche/src/transactions/P_chain/node_id.rs
deleted file mode 100644
index 08865aa..0000000
--- a/rust/apps/avalanche/src/transactions/P_chain/node_id.rs
+++ /dev/null
@@ -1,30 +0,0 @@
-use crate::constants::*;
-use crate::encode::cb58::Cb58Encodable;
-use crate::errors::{AvaxError, Result};
-use bytes::{Buf, Bytes};
-use core::convert::TryFrom;
-
-#[derive(Debug, Clone)]
-pub struct NodeId {
- node_id: [u8; NODE_ID_LEN],
-}
-
-impl Cb58Encodable for NodeId {
- fn get_prefix(&self) -> &'static str {
- "Node"
- }
-
- fn get_data(&self) -> &[u8] {
- &self.node_id
- }
-}
-
-impl TryFrom<Bytes> for NodeId {
- type Error = AvaxError;
-
- fn try_from(mut bytes: Bytes) -> Result<Self> {
- let mut node_id = [0u8; NODE_ID_LEN];
- bytes.copy_to_slice(&mut node_id);
- Ok(NodeId { node_id })
- }
-}
diff --git a/rust/apps/avalanche/src/transactions/P_chain/output_owner.rs b/rust/apps/avalanche/src/transactions/P_chain/output_owner.rs
deleted file mode 100644
index 4fe76d3..0000000
--- a/rust/apps/avalanche/src/transactions/P_chain/output_owner.rs
+++ /dev/null
@@ -1,40 +0,0 @@
-use crate::address::Address;
-use crate::errors::{AvaxError, Result};
-use crate::transactions::{
- structs::{LengthPrefixedVec, ParsedSizeAble},
- type_id::TypeId,
-};
-use bytes::{Buf, Bytes};
-use core::convert::TryFrom;
-
-#[derive(Debug, Clone)]
-pub struct OutputOwner {
- type_id: TypeId,
- locktime: u64,
- threshold: u32,
- pub addresses: LengthPrefixedVec<Address>,
-}
-
-impl ParsedSizeAble for OutputOwner {
- fn parsed_size(&self) -> usize {
- 4 + 8 + 4 + self.addresses.parsed_size()
- }
-}
-
-impl TryFrom<Bytes> for OutputOwner {
- type Error = AvaxError;
-
- fn try_from(mut bytes: Bytes) -> Result<Self> {
- let type_id = TypeId::try_from(bytes.get_u32())?;
- let locktime = bytes.get_u64();
- let threshold = bytes.get_u32();
- let addresses = LengthPrefixedVec::<Address>::try_from(bytes.clone())?;
- bytes.advance(addresses.parsed_size());
- Ok(OutputOwner {
- type_id,
- locktime,
- threshold,
- addresses,
- })
- }
-}
diff --git a/rust/apps/avalanche/src/transactions/P_chain/signer.rs b/rust/apps/avalanche/src/transactions/P_chain/signer.rs
deleted file mode 100644
index 10eba67..0000000
--- a/rust/apps/avalanche/src/transactions/P_chain/signer.rs
+++ /dev/null
@@ -1,23 +0,0 @@
-use crate::constants::*;
-use crate::errors::{AvaxError, Result};
-use bytes::{Buf, Bytes};
-use core::convert::TryFrom;
-
-#[derive(Debug, Clone)]
-pub struct Signer {
- pubkey: [u8; PROOF_OF_POSESSION_PUBKEY_LEN],
- signature: [u8; PROOF_OF_POSESSION_SIGNATURE_LEN],
-}
-
-impl TryFrom<Bytes> for Signer {
- type Error = AvaxError;
-
- fn try_from(mut bytes: Bytes) -> Result<Self> {
- let mut pubkey = [0u8; PROOF_OF_POSESSION_PUBKEY_LEN];
- bytes.copy_to_slice(&mut pubkey);
-
- let mut signature = [0u8; PROOF_OF_POSESSION_SIGNATURE_LEN];
- bytes.copy_to_slice(&mut signature);
- Ok(Signer { pubkey, signature })
- }
-}
diff --git a/rust/apps/avalanche/src/transactions/P_chain/validator.rs b/rust/apps/avalanche/src/transactions/P_chain/validator.rs
deleted file mode 100644
index c284ca9..0000000
--- a/rust/apps/avalanche/src/transactions/P_chain/validator.rs
+++ /dev/null
@@ -1,53 +0,0 @@
-use super::node_id::NodeId;
-use crate::constants::*;
-use crate::errors::{AvaxError, Result};
-use bytes::{Buf, Bytes};
-use core::convert::TryFrom;
-
-#[derive(Debug, Clone)]
-pub struct Validator {
- pub node_id: NodeId,
- pub start_time: i64,
- pub end_time: i64,
- pub weight: u64,
-}
-
-impl Validator {
- pub fn parsed_size(&self) -> usize {
- 20 + 8 + 8 + 8
- }
-}
-
-impl TryFrom<Bytes> for Validator {
- type Error = AvaxError;
-
- fn try_from(mut bytes: Bytes) -> Result<Self> {
- let node_id = NodeId::try_from(bytes.clone())?;
- bytes.advance(NODE_ID_LEN);
- Ok(Validator {
- node_id,
- start_time: bytes.get_i64(),
- end_time: bytes.get_i64(),
- weight: bytes.get_u64(),
- })
- }
-}
-
-#[cfg(test)]
-mod tests {
- use crate::encode::cb58::Cb58Encodable;
-
- use super::*;
-
- #[test]
- fn test_validator_parse() {
- let input_bytes = "000000023d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa000000070000000005f5e100000000000000000000000001000000018771921301d5bffff592dae86695a615bdb4a4413d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa000000070000000017c771d2000000000000000000000001000000010969ea62e2bb30e66d82e82fe267edf6871ea5f7";
- let binary_data = hex::decode(input_bytes).expect("Failed to decode hex string");
- let bytes = Bytes::from(binary_data);
- let result = Validator::try_from(bytes.clone()).unwrap();
- assert_eq!(
- "Node-1118zVrK8tN1ic5wUb5dcsECsf2BtfQ",
- result.node_id.to_cb58()
- );
- }
-}
diff --git a/rust/apps/avalanche/src/transactions/c_chain/evm_export.rs b/rust/apps/avalanche/src/transactions/c_chain/evm_export.rs
new file mode 100644
index 0000000..f09d735
--- /dev/null
+++ b/rust/apps/avalanche/src/transactions/c_chain/evm_export.rs
@@ -0,0 +1,160 @@
+use crate::address::Address;
+use crate::constants::*;
+use crate::errors::{AvaxError, Result};
+use crate::transactions::structs::{
+ AvaxFromToInfo, AvaxMethodInfo, AvaxTxInfo, LengthPrefixedVec, ParsedSizeAble,
+};
+use crate::transactions::transferable::TransferableOutput;
+use crate::transactions::tx_header::Header;
+use crate::transactions::{asset_id::AssetId, type_id::TypeId};
+
+use alloc::{
+ format,
+ string::{String, ToString},
+ vec::Vec,
+};
+use bytes::{Buf, Bytes};
+use core::convert::TryFrom;
+
+#[derive(Debug, Clone)]
+pub struct EvmInput {
+ address: Address,
+ amount: u64,
+ asset_id: AssetId,
+ nonce: u64,
+}
+
+impl TryFrom<Bytes> for EvmInput {
+ type Error = AvaxError;
+
+ fn try_from(mut bytes: Bytes) -> Result<Self> {
+ let address = Address::try_from(bytes.split_to(C_CHAIN_ADDRESS_LEN))?;
+ let amount = bytes.get_u64();
+ let asset_id = AssetId::try_from(bytes.split_to(ASSET_ID_LEN))?;
+ let nonce = bytes.get_u64();
+ Ok(EvmInput {
+ address,
+ amount,
+ asset_id,
+ nonce,
+ })
+ }
+}
+
+impl ParsedSizeAble for EvmInput {
+ fn parsed_size(&self) -> usize {
+ C_CHAIN_ADDRESS_LEN + 8 + ASSET_ID_LEN + 8
+ }
+}
+
+impl EvmInput {
+ pub fn get_amount(&self) -> u64 {
+ self.amount
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct ExportTx {
+ codec_id: u16,
+ type_id: TypeId,
+ tx_header: Header,
+ dest_chain: [u8; BLOCKCHAIN_ID_LEN],
+ inputs: LengthPrefixedVec<EvmInput>,
+ outputs: LengthPrefixedVec<TransferableOutput>,
+}
+
+impl AvaxTxInfo for ExportTx {
+ fn get_total_input_amount(&self) -> u64 {
+ self.inputs
+ .iter()
+ .fold(0, |acc, item| acc + item.get_amount())
+ }
+
+ fn get_total_output_amount(&self) -> u64 {
+ self.outputs
+ .iter()
+ .fold(0, |acc, item| acc + item.get_amount())
+ }
+
+ fn get_outputs_addresses(&self) -> Vec<AvaxFromToInfo> {
+ self.outputs
+ .iter()
+ .map(|output| {
+ AvaxFromToInfo::from(
+ output.get_amount(),
+ output.get_addresses(),
+ C_CHAIN_PREFIX.to_string(),
+ )
+ })
+ .collect()
+ }
+
+ fn get_method_info(&self) -> Option<AvaxMethodInfo> {
+ let method = match (self.type_id, self.dest_chain) {
+ (TypeId::CchainExportTx, chain) => {
+ let dest = match chain {
+ X_BLOCKCHAIN_ID | X_TEST_BLOCKCHAIN_ID => "X",
+ P_BLOCKCHAIN_ID => "P",
+ C_BLOCKCHAIN_ID | C_TEST_BLOCKCHAIN_ID => "C",
+ _ => "Unknown",
+ };
+ format!("C to {dest} Export")
+ }
+ _ => "Unknown".to_string(),
+ };
+ Some(AvaxMethodInfo::from_string(method).with_method_key("Export Tx".to_string()))
+ }
+
+ fn get_network_key(&self) -> String {
+ "Export Tx".to_string()
+ }
+}
+
+impl TryFrom<Bytes> for ExportTx {
+ type Error = AvaxError;
+
+ fn try_from(mut bytes: Bytes) -> Result<Self> {
+ let codec_id = bytes.get_u16();
+ let type_id = TypeId::try_from(bytes.get_u32())?;
+ let tx_header = Header::try_from(bytes.clone())?;
+ bytes.advance(tx_header.parsed_size());
+
+ let mut dest_chain = [0u8; 32];
+ bytes.copy_to_slice(&mut dest_chain);
+
+ let inputs = LengthPrefixedVec::<EvmInput>::try_from(bytes.clone())?;
+ bytes.advance(inputs.parsed_size());
+
+ let outputs = LengthPrefixedVec::<TransferableOutput>::try_from(bytes.clone())?;
+ bytes.advance(outputs.parsed_size());
+
+ Ok(ExportTx {
+ codec_id,
+ type_id,
+ tx_header,
+ dest_chain,
+ inputs,
+ outputs,
+ })
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_avax_c_export() {
+ // f22ATVQT11pSdMwvWLwXgeHNshauTDZxzErFKT5nd64esWXjU
+ let input_bytes = "000000000001000000057fc93d85c6d62c5b2ac0b519c87010ea5294012d1e407030d6acd0021cac10d5000000000000000000000000000000000000000000000000000000000000000000000001a9b548da818607e83cbcf6802370691948cbd4160000000005f60cde3d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa0000000000000000000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa000000070000000005f5e1000000000000000000000000010000000169bc9b5b6cbbbd490abbd79a37ad6cd643be87ab000000010000000900000001683728d1c682680aa80e702dcc8e349f79b5da8cac4b7fb59b28750f1c4c977014a70f69d5ef97dec616a21d69e3560cf2db78257710af3c03f959a487bcab04000218c13e";
+ let binary_data = hex::decode(input_bytes).expect("Failed to decode hex string");
+ let bytes = Bytes::from(binary_data);
+ let result = ExportTx::try_from(bytes.clone()).unwrap();
+ assert_eq!(result.codec_id, 0);
+ assert_eq!(result.type_id, TypeId::CchainExportTx);
+ assert_eq!(result.dest_chain, P_BLOCKCHAIN_ID);
+ assert_eq!(result.inputs.get_len(), 1);
+ assert_eq!(result.outputs.get_len(), 1);
+ assert_eq!(result.inputs.get(0).unwrap().amount, 100011230);
+ }
+}
diff --git a/rust/apps/avalanche/src/transactions/c_chain/evm_import.rs b/rust/apps/avalanche/src/transactions/c_chain/evm_import.rs
new file mode 100644
index 0000000..e3513f9
--- /dev/null
+++ b/rust/apps/avalanche/src/transactions/c_chain/evm_import.rs
@@ -0,0 +1,151 @@
+use crate::address::Address;
+use crate::constants::*;
+use crate::errors::{AvaxError, Result};
+use crate::transactions::structs::{
+ AvaxFromToInfo, AvaxMethodInfo, AvaxTxInfo, LengthPrefixedVec, ParsedSizeAble,
+};
+use crate::transactions::transferable::TransferableInput;
+use crate::transactions::tx_header::Header;
+use crate::transactions::{asset_id::AssetId, type_id::TypeId};
+
+use alloc::{
+ string::{String, ToString},
+ vec::Vec,
+};
+use bytes::{Buf, Bytes};
+use core::convert::TryFrom;
+
+#[derive(Debug, Clone)]
+pub struct EvmOutput {
+ address: Address,
+ amount: u64,
+ asset_id: AssetId,
+}
+
+impl TryFrom<Bytes> for EvmOutput {
+ type Error = AvaxError;
+
+ fn try_from(mut bytes: Bytes) -> Result<Self> {
+ let address = Address::try_from(bytes.split_to(C_CHAIN_ADDRESS_LEN))?;
+ let amount = bytes.get_u64();
+ let asset_id = AssetId::try_from(bytes.split_to(ASSET_ID_LEN))?;
+ Ok(EvmOutput {
+ address,
+ amount,
+ asset_id,
+ })
+ }
+}
+
+impl ParsedSizeAble for EvmOutput {
+ fn parsed_size(&self) -> usize {
+ C_CHAIN_ADDRESS_LEN + 8 + ASSET_ID_LEN
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct ImportTx {
+ codec_id: u16,
+ type_id: TypeId,
+ tx_header: Header,
+ source_chain: [u8; BLOCKCHAIN_ID_LEN],
+ inputs: LengthPrefixedVec<TransferableInput>,
+ outputs: LengthPrefixedVec<EvmOutput>,
+}
+
+impl AvaxTxInfo for ImportTx {
+ fn get_total_input_amount(&self) -> u64 {
+ self.inputs
+ .iter()
+ .fold(0, |acc, item| acc + item.get_amount())
+ }
+
+ fn get_total_output_amount(&self) -> u64 {
+ self.outputs.iter().fold(0, |acc, item| acc + item.amount)
+ }
+
+ fn get_outputs_addresses(&self) -> Vec<AvaxFromToInfo> {
+ self.outputs
+ .iter()
+ .map(|output| {
+ AvaxFromToInfo::from(
+ output.amount,
+ vec![output.address.to_evm_address()],
+ C_CHAIN_PREFIX.to_string(),
+ )
+ })
+ .collect()
+ }
+
+ fn get_network_key(&self) -> String {
+ "Import Tx".to_string()
+ }
+
+ fn get_method_info(&self) -> Option<AvaxMethodInfo> {
+ let method = match self.source_chain {
+ X_BLOCKCHAIN_ID | X_TEST_BLOCKCHAIN_ID => "Sending from X-Chain",
+ P_BLOCKCHAIN_ID => "Sending from P-Chain",
+ C_BLOCKCHAIN_ID | C_TEST_BLOCKCHAIN_ID => "Sending from C-Chain",
+ _ => "Unknown",
+ };
+
+ Some(
+ AvaxMethodInfo::from_string(method.to_string())
+ .with_method_key("Import Tx".to_string()),
+ )
+ }
+
+ fn get_import_tx_fee(&self) -> u64 {
+ 0
+ }
+}
+
+impl TryFrom<Bytes> for ImportTx {
+ type Error = AvaxError;
+
+ fn try_from(mut bytes: Bytes) -> Result<Self> {
+ let codec_id = bytes.get_u16();
+ let type_id = TypeId::try_from(bytes.get_u32())?;
+ let tx_header = Header::try_from(bytes.clone())?;
+ bytes.advance(tx_header.parsed_size());
+
+ let mut source_chain = [0u8; 32];
+ bytes.copy_to_slice(&mut source_chain);
+
+ let inputs = LengthPrefixedVec::<TransferableInput>::try_from(bytes.clone())?;
+ bytes.advance(inputs.parsed_size());
+
+ let outputs = LengthPrefixedVec::<EvmOutput>::try_from(bytes.clone())?;
+ bytes.advance(outputs.parsed_size());
+
+ Ok(ImportTx {
+ codec_id,
+ type_id,
+ tx_header,
+ source_chain,
+ inputs,
+ outputs,
+ })
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_avax_c_import() {
+ let input_bytes = "000000000000000000057fc93d85c6d62c5b2ac0b519c87010ea5294012d1e407030d6acd0021cac10d5ab68eb1ee142a05cfe768c36e11f0b596db5a3c6c77aabe665dad9e638ca94f7000000013f407c793a9a31b5f281623e479bcaee760be3da54969b85f44ffc51a67c1c64000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa000000050000000005f5e1000000000100000000000000013fe51338992d913b6a999693d7c345646ca3bb4c0000000005f5b5223d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa0000000100000009000000017e79e5f2fba29e48d9d329f23e8e27bf20d79714555bb04a175e57136b95b282528654583b1fd568e7f8322962fb31e4174896a50114511d124bf74a92559c1f0048c457c5";
+ let binary_data = hex::decode(input_bytes).expect("Failed to decode hex string");
+ let bytes = Bytes::from(binary_data);
+ let result = ImportTx::try_from(bytes.clone()).unwrap();
+ assert_eq!(result.type_id, TypeId::BaseTx);
+ assert_eq!(result.codec_id, 0);
+ assert_eq!(result.inputs.get_len(), 1);
+ assert_eq!(result.outputs.get_len(), 1);
+
+ assert_eq!(result.source_chain, X_TEST_BLOCKCHAIN_ID);
+ assert_eq!(result.get_total_output_amount(), 99988770);
+ assert_eq!(result.get_total_input_amount(), 100000000);
+ }
+}
diff --git a/rust/apps/avalanche/src/transactions/p_chain/add_permissionless_delegator.rs b/rust/apps/avalanche/src/transactions/p_chain/add_permissionless_delegator.rs
new file mode 100644
index 0000000..68b6278
--- /dev/null
+++ b/rust/apps/avalanche/src/transactions/p_chain/add_permissionless_delegator.rs
@@ -0,0 +1,120 @@
+use super::{output_owner::OutputOwner, validator::Validator};
+use crate::constants::*;
+use crate::errors::{AvaxError, Result};
+use crate::transactions::base_tx::BaseTx;
+use crate::transactions::structs::{
+ AvaxFromToInfo, AvaxMethodInfo, AvaxTxInfo, LengthPrefixedVec, ParsedSizeAble,
+};
+use crate::transactions::subnet_id::SubnetId;
+use crate::transactions::transferable::TransferableOutput;
+use alloc::{
+ string::{String, ToString},
+ vec::Vec,
+};
+use bytes::{Buf, Bytes};
+use core::convert::TryFrom;
+
+#[derive(Debug)]
+pub struct AddPermissionlessDelegatorTx {
+ base_tx: BaseTx,
+ validator: Validator,
+ subnet_id: SubnetId,
+ stake_out: LengthPrefixedVec<TransferableOutput>,
+ delegator_owner: OutputOwner,
+}
+
+impl AvaxTxInfo for AddPermissionlessDelegatorTx {
+ fn get_total_input_amount(&self) -> u64 {
+ self.base_tx.get_total_input_amount()
+ }
+
+ fn get_total_output_amount(&self) -> u64 {
+ self.base_tx.get_total_output_amount()
+ + self
+ .stake_out
+ .iter()
+ .fold(0, |acc, item| acc + item.get_amount())
+ }
+
+ fn get_outputs_addresses(&self) -> Vec<AvaxFromToInfo> {
+ self.base_tx
+ .get_outputs_addresses()
+ .into_iter()
+ .chain(self.stake_out.iter().map(|output| {
+ AvaxFromToInfo::from(
+ output.get_amount(),
+ output.get_addresses(),
+ X_P_CHAIN_PREFIX.to_string(),
+ )
+ }))
+ .collect()
+ }
+
+ fn get_method_info(&self) -> Option<AvaxMethodInfo> {
+ Some(AvaxMethodInfo::from(
+ "AddPermlessDelegator".to_string(),
+ self.validator.start_time,
+ self.validator.end_time,
+ ))
+ }
+
+ fn get_network(&self) -> Option<String> {
+ Some("Avalanche P-Chain".to_string())
+ }
+
+ fn get_subnet_id(&self) -> Option<String> {
+ Some("Primary Subnet".to_string())
+ }
+
+ fn get_reward_address(&self) -> Option<String> {
+ Some(
+ self.delegator_owner
+ .addresses
+ .get(0)
+ .map(|addr| addr.encode())
+ .unwrap_or_default(),
+ )
+ }
+}
+
+impl TryFrom<Bytes> for AddPermissionlessDelegatorTx {
+ type Error = AvaxError;
+
+ fn try_from(mut bytes: Bytes) -> Result<Self> {
+ let base_tx = BaseTx::try_from(bytes.clone())?;
+ bytes.advance(base_tx.parsed_size());
+
+ let validator = Validator::try_from(bytes.clone())?;
+ bytes.advance(validator.parsed_size());
+
+ let subnet_id = SubnetId::try_from(bytes.clone())?;
+ bytes.advance(SUBNET_ID_LEN);
+
+ let stake_out = LengthPrefixedVec::<TransferableOutput>::try_from(bytes.clone())?;
+ bytes.advance(stake_out.parsed_size());
+
+ let delegator_owner = OutputOwner::try_from(bytes.clone())?;
+ bytes.advance(delegator_owner.parsed_size());
+
+ Ok(AddPermissionlessDelegatorTx {
+ base_tx,
+ validator,
+ subnet_id,
+ stake_out,
+ delegator_owner,
+ })
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_add_permissionless_delegator() {
+ let input_bytes = "00000000001a000000050000000000000000000000000000000000000000000000000000000000000000000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000007000000003b9a9e0400000000000000000000000100000001e0beb088f94b8224eb5d6f1115561d7173cd6e7f00000002295a7b15e26c6cafda8883afd0f724e0e0b1dad4517148711434cb96fb3c8a61000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000005000000003b9aca0000000001000000006109bc613691602ca0811312357676416252412a87ded6c56c240baba1afe042000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000005000000003b9aca000000000100000000000000007072a3df0cd056d9b9ef00c09630bad3027dc312000000006760c3b100000000676215a9000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000007000000003b9aca0000000000000000000000000100000001e0beb088f94b8224eb5d6f1115561d7173cd6e7f0000000b00000000000000000000000100000001a0f4d4d9a0ea219da5ed5499ad083e1942a0846a000000020000000900000001438c3a393f49bb27791ca830effec456c2642a487ee4ce89300dd2e591fc22ab6b2aa8e08515ca229f2a2f14168700e05a1f96bd61d1fc3ab31e9e71ef9f16bb000000000900000001438c3a393f49bb27791ca830effec456c2642a487ee4ce89300dd2e591fc22ab6b2aa8e08515ca229f2a2f14168700e05a1f96bd61d1fc3ab31e9e71ef9f16bb005c3d047c";
+ let bytes = Bytes::from(hex::decode(input_bytes).expect("Failed to decode hex string"));
+ let result = AddPermissionlessDelegatorTx::try_from(bytes.clone()).unwrap();
+ assert_eq!(result.base_tx.get_blockchain_id(), P_BLOCKCHAIN_ID);
+ }
+}
diff --git a/rust/apps/avalanche/src/transactions/p_chain/add_permissionless_validator.rs b/rust/apps/avalanche/src/transactions/p_chain/add_permissionless_validator.rs
new file mode 100644
index 0000000..8fb2d62
--- /dev/null
+++ b/rust/apps/avalanche/src/transactions/p_chain/add_permissionless_validator.rs
@@ -0,0 +1,140 @@
+use super::output_owner::OutputOwner;
+use super::signer::Signer;
+use super::validator::Validator;
+use crate::constants::*;
+use crate::errors::{AvaxError, Result};
+use crate::transactions::base_tx::BaseTx;
+use crate::transactions::structs::{
+ AvaxFromToInfo, AvaxMethodInfo, AvaxTxInfo, LengthPrefixedVec, ParsedSizeAble,
+};
+use crate::transactions::subnet_id::SubnetId;
+use crate::transactions::transferable::TransferableOutput;
+use alloc::{
+ string::{String, ToString},
+ vec::Vec,
+};
+use bytes::{Buf, Bytes};
+use core::convert::TryFrom;
+
+#[derive(Debug)]
+pub struct AddPermissionlessValidatorTx {
+ base_tx: BaseTx,
+ validator: Validator,
+ subnet_id: SubnetId,
+ signer: Signer,
+ stake_out: LengthPrefixedVec<TransferableOutput>,
+ validator_owner: OutputOwner,
+ delegator_owner: OutputOwner,
+ delegator_share: u32,
+}
+
+impl AvaxTxInfo for AddPermissionlessValidatorTx {
+ fn get_total_input_amount(&self) -> u64 {
+ self.base_tx.get_total_input_amount()
+ }
+
+ fn get_total_output_amount(&self) -> u64 {
+ self.base_tx.get_total_output_amount()
+ + self
+ .stake_out
+ .iter()
+ .fold(0, |acc, item| acc + item.get_amount())
+ }
+
+ fn get_outputs_addresses(&self) -> Vec<AvaxFromToInfo> {
+ self.base_tx
+ .get_outputs_addresses()
+ .into_iter()
+ .chain(self.stake_out.iter().map(|output| {
+ AvaxFromToInfo::from(
+ output.get_amount(),
+ output.get_addresses(),
+ X_P_CHAIN_PREFIX.to_string(),
+ )
+ }))
+ .collect()
+ }
+
+ fn get_method_info(&self) -> Option<AvaxMethodInfo> {
+ Some(AvaxMethodInfo::from(
+ "AddPermlessValidator".to_string(),
+ self.validator.start_time,
+ self.validator.end_time,
+ ))
+ }
+
+ fn get_subnet_id(&self) -> Option<String> {
+ Some("Primary Subnet".to_string())
+ }
+
+ fn get_network(&self) -> Option<String> {
+ Some("Avalanche P-Chain".to_string())
+ }
+
+ fn get_reward_address(&self) -> Option<String> {
+ Some(
+ self.validator_owner
+ .addresses
+ .get(0)
+ .map(|addr| addr.encode())
+ .unwrap_or_default(),
+ )
+ }
+}
+
+impl TryFrom<Bytes> for AddPermissionlessValidatorTx {
+ type Error = AvaxError;
+
+ fn try_from(mut bytes: Bytes) -> Result<Self> {
+ let base_tx = BaseTx::try_from(bytes.clone())?;
+ bytes.advance(base_tx.parsed_size());
+
+ let validator = Validator::try_from(bytes.clone())?;
+ bytes.advance(validator.parsed_size());
+
+ let subnet_id = SubnetId::try_from(bytes.clone())?;
+ bytes.advance(SUBNET_ID_LEN);
+
+ let signer = Signer::try_from(bytes.clone())?;
+ bytes.advance(PROOF_OF_POSESSION_PUBKEY_LEN + PROOF_OF_POSESSION_SIGNATURE_LEN);
+
+ let stake_out = LengthPrefixedVec::<TransferableOutput>::try_from(bytes.clone())?;
+ bytes.advance(stake_out.parsed_size());
+
+ let validator_owner = OutputOwner::try_from(bytes.clone())?;
+ bytes.advance(validator_owner.parsed_size());
+
+ let delegator_owner = OutputOwner::try_from(bytes.clone())?;
+ bytes.advance(delegator_owner.parsed_size());
+
+ let delegator_share = bytes.get_u32();
+
+ Ok(AddPermissionlessValidatorTx {
+ base_tx,
+ validator,
+ subnet_id,
+ signer,
+ stake_out,
+ validator_owner,
+ delegator_owner,
+ delegator_share,
+ })
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_add_permissionless_validator() {
+ // 23HbZUQ7ijjrDHfqnjKpd4MTMRY18Gc2JxCz79ZBwZAsCLfntb
+ let input_bytes = "000000000019000000050000000000000000000000000000000000000000000000000000000000000000000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa000000070000000005e57a370000000000000000000000010000000161cd7d26c72edc631d4114d6eef2c4e069ec9206000000020033b7653ffbf19a2352591e8b6aea8e7c75f38d8e8f5f781cf15aad8425010a000000003d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000005000000003b9ab9d9000000010000000029ec95b1c9df6cd2598852d78fea7766c1aece1a7b5d24f6cf58adc98107f927000000003d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000005000000003b8ab46a0000000100000000000000009e843011540909cc62c64dc85a8a1507813777410000000067614160000000006851245000000000713fb30000000000000000000000000000000000000000000000000000000000000000000000001c87c87cef2e92bface778c711c752168a6e858d58ba62463e8bab336f9b05c98c695acf3c7da02b05c667ce5627e63a60ad53ad7da84734084394dedf6b3c4bb6c85922c2b08b09c55508d49d348ad0dcd9678be58197fef69bad862b1d170f4b0c24f189d9d4b6b5103d28b5e8146d305e28d3dcfb3279f089c7152535a24800c7a1a212868a5c76e3559ea9d4a64d9d000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa0000000700000000713fb3000000000000000000000000010000000161cd7d26c72edc631d4114d6eef2c4e069ec92060000000b0000000000000000000000010000000161cd7d26c72edc631d4114d6eef2c4e069ec92060000000b0000000000000000000000010000000161cd7d26c72edc631d4114d6eef2c4e069ec92060000c350";
+ let bytes = Bytes::from(hex::decode(input_bytes).expect("Failed to decode hex string"));
+ let result = AddPermissionlessValidatorTx::try_from(bytes.clone()).unwrap();
+ assert_eq!(
+ "avax1v8xh6fk89mwxx82pzntwaukyup57eysx7xzuwa".to_string(),
+ result.get_reward_address().unwrap()
+ );
+ }
+}
diff --git a/rust/apps/avalanche/src/transactions/p_chain/node_id.rs b/rust/apps/avalanche/src/transactions/p_chain/node_id.rs
new file mode 100644
index 0000000..08865aa
--- /dev/null
+++ b/rust/apps/avalanche/src/transactions/p_chain/node_id.rs
@@ -0,0 +1,30 @@
+use crate::constants::*;
+use crate::encode::cb58::Cb58Encodable;
+use crate::errors::{AvaxError, Result};
+use bytes::{Buf, Bytes};
+use core::convert::TryFrom;
+
+#[derive(Debug, Clone)]
+pub struct NodeId {
+ node_id: [u8; NODE_ID_LEN],
+}
+
+impl Cb58Encodable for NodeId {
+ fn get_prefix(&self) -> &'static str {
+ "Node"
+ }
+
+ fn get_data(&self) -> &[u8] {
+ &self.node_id
+ }
+}
+
+impl TryFrom<Bytes> for NodeId {
+ type Error = AvaxError;
+
+ fn try_from(mut bytes: Bytes) -> Result<Self> {
+ let mut node_id = [0u8; NODE_ID_LEN];
+ bytes.copy_to_slice(&mut node_id);
+ Ok(NodeId { node_id })
+ }
+}
diff --git a/rust/apps/avalanche/src/transactions/p_chain/output_owner.rs b/rust/apps/avalanche/src/transactions/p_chain/output_owner.rs
new file mode 100644
index 0000000..4fe76d3
--- /dev/null
+++ b/rust/apps/avalanche/src/transactions/p_chain/output_owner.rs
@@ -0,0 +1,40 @@
+use crate::address::Address;
+use crate::errors::{AvaxError, Result};
+use crate::transactions::{
+ structs::{LengthPrefixedVec, ParsedSizeAble},
+ type_id::TypeId,
+};
+use bytes::{Buf, Bytes};
+use core::convert::TryFrom;
+
+#[derive(Debug, Clone)]
+pub struct OutputOwner {
+ type_id: TypeId,
+ locktime: u64,
+ threshold: u32,
+ pub addresses: LengthPrefixedVec<Address>,
+}
+
+impl ParsedSizeAble for OutputOwner {
+ fn parsed_size(&self) -> usize {
+ 4 + 8 + 4 + self.addresses.parsed_size()
+ }
+}
+
+impl TryFrom<Bytes> for OutputOwner {
+ type Error = AvaxError;
+
+ fn try_from(mut bytes: Bytes) -> Result<Self> {
+ let type_id = TypeId::try_from(bytes.get_u32())?;
+ let locktime = bytes.get_u64();
+ let threshold = bytes.get_u32();
+ let addresses = LengthPrefixedVec::<Address>::try_from(bytes.clone())?;
+ bytes.advance(addresses.parsed_size());
+ Ok(OutputOwner {
+ type_id,
+ locktime,
+ threshold,
+ addresses,
+ })
+ }
+}
diff --git a/rust/apps/avalanche/src/transactions/p_chain/signer.rs b/rust/apps/avalanche/src/transactions/p_chain/signer.rs
new file mode 100644
index 0000000..10eba67
--- /dev/null
+++ b/rust/apps/avalanche/src/transactions/p_chain/signer.rs
@@ -0,0 +1,23 @@
+use crate::constants::*;
+use crate::errors::{AvaxError, Result};
+use bytes::{Buf, Bytes};
+use core::convert::TryFrom;
+
+#[derive(Debug, Clone)]
+pub struct Signer {
+ pubkey: [u8; PROOF_OF_POSESSION_PUBKEY_LEN],
+ signature: [u8; PROOF_OF_POSESSION_SIGNATURE_LEN],
+}
+
+impl TryFrom<Bytes> for Signer {
+ type Error = AvaxError;
+
+ fn try_from(mut bytes: Bytes) -> Result<Self> {
+ let mut pubkey = [0u8; PROOF_OF_POSESSION_PUBKEY_LEN];
+ bytes.copy_to_slice(&mut pubkey);
+
+ let mut signature = [0u8; PROOF_OF_POSESSION_SIGNATURE_LEN];
+ bytes.copy_to_slice(&mut signature);
+ Ok(Signer { pubkey, signature })
+ }
+}
diff --git a/rust/apps/avalanche/src/transactions/p_chain/validator.rs b/rust/apps/avalanche/src/transactions/p_chain/validator.rs
new file mode 100644
index 0000000..c284ca9
--- /dev/null
+++ b/rust/apps/avalanche/src/transactions/p_chain/validator.rs
@@ -0,0 +1,53 @@
+use super::node_id::NodeId;
+use crate::constants::*;
+use crate::errors::{AvaxError, Result};
+use bytes::{Buf, Bytes};
+use core::convert::TryFrom;
+
+#[derive(Debug, Clone)]
+pub struct Validator {
+ pub node_id: NodeId,
+ pub start_time: i64,
+ pub end_time: i64,
+ pub weight: u64,
+}
+
+impl Validator {
+ pub fn parsed_size(&self) -> usize {
+ 20 + 8 + 8 + 8
+ }
+}
+
+impl TryFrom<Bytes> for Validator {
+ type Error = AvaxError;
+
+ fn try_from(mut bytes: Bytes) -> Result<Self> {
+ let node_id = NodeId::try_from(bytes.clone())?;
+ bytes.advance(NODE_ID_LEN);
+ Ok(Validator {
+ node_id,
+ start_time: bytes.get_i64(),
+ end_time: bytes.get_i64(),
+ weight: bytes.get_u64(),
+ })
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::encode::cb58::Cb58Encodable;
+
+ use super::*;
+
+ #[test]
+ fn test_validator_parse() {
+ let input_bytes = "000000023d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa000000070000000005f5e100000000000000000000000001000000018771921301d5bffff592dae86695a615bdb4a4413d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa000000070000000017c771d2000000000000000000000001000000010969ea62e2bb30e66d82e82fe267edf6871ea5f7";
+ let binary_data = hex::decode(input_bytes).expect("Failed to decode hex string");
+ let bytes = Bytes::from(binary_data);
+ let result = Validator::try_from(bytes.clone()).unwrap();
+ assert_eq!(
+ "Node-1118zVrK8tN1ic5wUb5dcsECsf2BtfQ",
+ result.node_id.to_cb58()
+ );
+ }
+}
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.