diff --git a/grpc/tests/utils.rs b/grpc/tests/utils.rs index 0dc0488f..d5c467ca 100644 --- a/grpc/tests/utils.rs +++ b/grpc/tests/utils.rs @@ -65,7 +65,7 @@ pub async fn new_test_client() -> Result> { } pub fn load_account(path: &str) -> TestAccount { - let account_file = format!("{path}.addr"); // TODO: consolidate + let account_file = format!("{path}.addr"); let key_file = format!("{path}.plaintext-key"); let account = fs::read_to_string(account_file).expect("file with account name to exists"); diff --git a/rpc/src/state.rs b/rpc/src/state.rs index 2e349fb9..2ab58350 100644 --- a/rpc/src/state.rs +++ b/rpc/src/state.rs @@ -1,6 +1,6 @@ use celestia_types::state::{ AccAddress, Address, Balance, QueryDelegationResponse, QueryRedelegationsResponse, - QueryUnbondingDelegationResponse, TxResponse, Uint, ValAddress, + QueryUnbondingDelegationResponse, RawTxResponse, Uint, ValAddress, }; use celestia_types::{blob::RawBlob, TxConfig}; use jsonrpsee::proc_macros::rpc; @@ -31,7 +31,7 @@ pub trait State { dest: &ValAddress, amount: Uint, config: TxConfig, - ) -> Result; + ) -> Result; /// CancelUnbondingDelegation cancels a user's pending undelegation from a validator. #[method(name = "state.CancelUnbondingDelegation")] @@ -41,7 +41,7 @@ pub trait State { amount: Uint, height: Uint, config: TxConfig, - ) -> Result; + ) -> Result; /// Delegate sends a user's liquid tokens to a validator for delegation. #[method(name = "state.Delegate")] @@ -50,7 +50,7 @@ pub trait State { addr: &ValAddress, amount: Uint, config: TxConfig, - ) -> Result; + ) -> Result; /// IsStopped checks if the Module's context has been stopped. #[method(name = "state.IsStopped")] @@ -84,7 +84,7 @@ pub trait State { &self, blobs: &[RawBlob], config: TxConfig, - ) -> Result; + ) -> Result; /// Transfer sends the given amount of coins from default wallet of the node to the given account address. #[method(name = "state.Transfer")] @@ -93,7 +93,7 @@ pub trait State { to: &AccAddress, amount: Uint, config: TxConfig, - ) -> Result; + ) -> Result; /// Undelegate undelegates a user's delegated tokens, unbonding them from the current validator. #[method(name = "Undelegate")] @@ -102,5 +102,5 @@ pub trait State { addr: &ValAddress, amount: Uint, config: TxConfig, - ) -> Result; + ) -> Result; } diff --git a/rpc/tests/state.rs b/rpc/tests/state.rs index 36f63aae..9e535914 100644 --- a/rpc/tests/state.rs +++ b/rpc/tests/state.rs @@ -45,7 +45,7 @@ async fn submit_pay_for_blob() { .unwrap(); let received_blob = client - .blob_get(tx_response.height.into(), namespace, blob.commitment) + .blob_get(tx_response.height as u64, namespace, blob.commitment) .await .unwrap(); diff --git a/types/src/bit_array.rs b/types/src/bit_array.rs index 4083d0d6..51021be1 100644 --- a/types/src/bit_array.rs +++ b/types/src/bit_array.rs @@ -11,7 +11,17 @@ impl TryFrom for BitVector { type Error = Error; fn try_from(value: CompactBitArray) -> Result { - let num_bits = (value.elems.len() - 1) * 8 + value.extra_bits_stored as usize; + let num_bytes = value.elems.len(); + if num_bytes == 0 && value.extra_bits_stored != 0 { + return Err(Error::MalformedCompactBitArray); + } + + let last_byte_bits = match value.extra_bits_stored { + 0 => 8, + n @ 1..=7 => n, + _ => return Err(Error::MalformedCompactBitArray), + } as usize; + let num_bits = num_bytes.saturating_sub(1) * 8 + last_byte_bits; let mut bit_vec = BitVec::<_, Msb0>::try_from_vec(value.elems).map_err(|_| Error::BitarrayTooLarge)?; @@ -39,6 +49,7 @@ impl From for CompactBitArray { #[cfg(test)] mod tests { use super::*; + use bitvec::prelude::*; #[test] fn test_empty() { @@ -51,4 +62,55 @@ mod tests { let result = CompactBitArray::from(vec); assert_eq!(source, result); } + + #[test] + fn byte_aligned() { + let source = CompactBitArray { + extra_bits_stored: 0, + elems: vec![0b00000001, 0b00000010], + }; + let expected_bits = bits![0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0]; + + let vec = BitVector::try_from(source.clone()).unwrap(); + assert_eq!(vec.0.len(), 16); + assert_eq!(vec.0, expected_bits); + + let result = CompactBitArray::from(vec); + assert_eq!(source, result); + } + + // test relies on the fact that we're setting uninitialised part of bitfield to 0, + // which we do anyway for safety + #[test] + fn with_extra_bytes() { + let source = CompactBitArray { + extra_bits_stored: 1, + elems: vec![0b00000000, 0b10000000], + }; + let expected_bits = bits![0, 0, 0, 0, 0, 0, 0, 0, 1]; + + let vec = BitVector::try_from(source.clone()).unwrap(); + assert_eq!(vec.0.len(), 9); + assert_eq!(vec.0, expected_bits); + + let result = CompactBitArray::from(vec); + assert_eq!(source, result); + } + + #[test] + fn malformed() { + let source = CompactBitArray { + extra_bits_stored: 8, + elems: vec![0b00000000], + }; + let error = BitVector::try_from(source.clone()).unwrap_err(); + assert!(matches!(error, Error::MalformedCompactBitArray)); + + let source = CompactBitArray { + extra_bits_stored: 1, + elems: vec![], + }; + let error = BitVector::try_from(source.clone()).unwrap_err(); + assert!(matches!(error, Error::MalformedCompactBitArray)); + } } diff --git a/types/src/error.rs b/types/src/error.rs index 68b37f3b..c8475241 100644 --- a/types/src/error.rs +++ b/types/src/error.rs @@ -86,6 +86,10 @@ pub enum Error { #[error("Bit array to large")] BitarrayTooLarge, + /// Malformed CompactBitArray + #[error("CompactBitArray malformed")] + MalformedCompactBitArray, + /// Wrong proof type. #[error("Wrong proof type")] WrongProofType, diff --git a/types/src/state/tx.rs b/types/src/state/tx.rs index d407cbde..0a32b1d1 100644 --- a/types/src/state/tx.rs +++ b/types/src/state/tx.rs @@ -25,7 +25,7 @@ pub use celestia_proto::cosmos::tx::v1beta1::TxBody as RawTxBody; pub type Signature = Vec; -// [`BOND_DENOM`] defines the native staking denomination +/// [`BOND_DENOM`] defines the native staking denomination pub const BOND_DENOM: &str = "utia"; /// [`Tx`] is the standard type used for broadcasting transactions. @@ -218,6 +218,9 @@ pub struct Coin { } impl Fee { + /// Create [`Fee`] struct with provided number of utia and gas limit, + /// without setting custom [`Fee::payer`] or [`Fee::granter`] fields, + /// which means first tx signer is responsible for paying. pub fn new(utia_fee: u64, gas_limit: u64) -> Self { Fee { amount: vec![Coin {