Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge development into main #986

Merged
merged 22 commits into from
Sep 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 155 additions & 0 deletions docs/developers/best-practices/managed-decimal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
---
id: managed-decimal
title: Managed Decimal
---

[comment]: # (mx-context-auto)

## Managed Decimal Overview

`ManagedDecimal` is a generic type representing a fixed-point decimal number managed by the API.
It is designed to handle decimal values with a specific number of decimals, providing operations such as addition, subtraction, multiplication, division, scaling, and conversion between different decimal precision and also more complex operations such as logarithm and nth root with a customizable degree of precision.

Essentially, `ManagedDecimal` is simply a struct containing a wrapper over a `BigIntHandle` (`BigUint`) and the number of decimals associated with that value (precision). Having such a light design helps `ManagedDecimal` become the optimal solution for dealing with fixed-point decimal numbers in terms of efficiency and readability.

```rust title=managed_decimal.rs
#[derive(Debug, Clone)]
pub struct ManagedDecimal<M: ManagedTypeApi, D: Decimals> {
data: BigUint<M>, // value * scaling_factor
decimals: D, // number_of_decimals (precision)
}
```

The `scaling factor` of the decimal is `10^num_of_decimals` and is a cached value across multiple `ManagedDecimal` instances for increased efficiency.

```rust title=example.rs
pub fn main() {
let decimal = ManagedDecimal::<StaticApi, ConstDecimals<2>>::from(BigUint::from(1u64));
let cached_decimal = ManagedDecimal::<StaticApi, ConstDecimals<2>>::from(BigUint::from(5u64));
}
```

[comment]: # (mx-context-auto)

## Number of decimals

`Decimals` is a trait representing the number of decimals associated with a decimal value and is only
implemented for `NumDecimals` and `ConstDecimals`.
`NumDecimals` is a type alias for `usize` and is used to specify the number of decimals dynamically, while `ConstDecimals` is a type that represents a constant number of decimals and is used to statically specify the number of decimals for ManagedDecimal instances.

```rust title=example.rs
pub fn main() {
let decimal: ManagedDecimal<StaticApi, NumDecimals> = ManagedDecimal::from_raw_units(BigUint::from(100u64), 2usize);
let const_decimal = ManagedDecimal::<StaticApi, ConstDecimals<3>>::const_decimals_from_raw(BigUint::from(500u64))
}
```

[comment]: # (mx-context-auto)

## Operations

`ManagedDecimal` supports various types of simple and complex operations, as well as conversions and scaling. More code examples can be found at `mx-sdk-rs/framework/scenario/tests/managed_decimal_test.rs`

### Available methods:
- Simple Operations:
- `add`, `mul`, `div`, `sub` are all implemented for `ConstDecimals` of the same scale.
```rust title=example.rs
pub fn main() {
let fixed = ManagedDecimal::<StaticApi, ConstDecimals<2>>::from(BigUint::from(1u64));
let fixed_2 = ManagedDecimal::<StaticApi, ConstDecimals<2>>::from(BigUint::from(5u64));

let addition = fixed + fixed_2;
assert_eq!(
addition,
ManagedDecimal::<StaticApi, ConstDecimals<2>>::from(BigUint::from(6u64))
);
}
```
- `trunc(&self) -> BigUint<M>` returns the `data` field without the `scaling_factor` applied, by dividing the value to the scaling factor and truncating.
```rust title=example.rs
pub fn main() {
...
assert_eq!(addition.trunc(), BigUint::from(6u64));
}
```
- Complex Operations:
- `log<T: Decimals>(self, target_base: BigUint<M>, precision: T) -> ManagedDecimal<M, T>` returns the value of log in any base with customizable precision level.
```rust title=example.rs
pub fn logarithm() {
let fixed = ManagedDecimal::<StaticApi, NumDecimals>::from_raw_units(BigUint::from(10u64), 1usize);
let fixed_const = ManagedDecimal::<StaticApi, ConstDecimals<1>>::const_decimals_from_raw(BigUint::from(10u64));

let log2_fixed = fixed.log(BigUint::from(2u64), 10_000usize);
assert_eq!(
log2_fixed,
ManagedDecimal::<StaticApi, NumDecimals>::from_raw_units(BigUint::from(33219u64), 10_000usize)
);
}
```
- Scaling:
- `scale(&self) -> usize` returns the number of decimals (the scale).
- `scaling_factor<M: ManagedTypeApi>(&self) -> BigUint<M>` returns the scaling factor value (`10^num_decimals`).
- `rescale<T: Decimals>(self, scale_to: T) -> ManagedDecimal<M, T>` returns the correspondent of the decimal in the newly specified scale. It can also convert between `NumDecimal` and `ConstDecimals` instances.

```rust title=example.rs
pub fn main() {
...
let fixed_8: ManagedDecimal<StaticApi, NumDecimals> = ManagedDecimal::from_raw_units(BigUint::from(5u64), 5usize);
let fixed_9 = fixed_8.rescale(ConstDecimals::<3>);
assert_eq!(
fixed_9,
ManagedDecimal::<StaticApi, ConstDecimals<3>>::const_decimals_from_raw(BigUint::from(500u64))
);
}
```
- Conversions:
- `into_raw_units(&self) -> &BigUint<M>` returns the `data` field value.

```rust title=example.rs
pub fn main() {
...
assert_eq!(addition.into_raw_units(), &BigUint::from(600u64));
}
```
- `from_raw_units(data: BigUint<M>, decimals: D) -> Self` returns a `ManagedDecimal` from a data field value without applying scaling factor.

```rust title=example.rs
pub fn main() {
...
let fixed_4: ManagedDecimal<StaticApi, NumDecimals> = ManagedDecimal::from_raw_units(BigUint::from(100u64), 2usize);
let fixed_5 = fixed_4.rescale(2usize);
assert_eq!(
fixed_5,
ManagedDecimal::from_raw_units(BigUint::from(100000000u64), 8usize)
);
}
```
- `const_decimals_from_raw(data: BigUint<M>) -> Self` returns a `ConstDecimals` type of `ManagedDecimal` from a data field value without applying scaling factor.
```rust title=example.rs
pub fn main() {
...
let fixed_const: ManagedDecimal<StaticApi, ConstDecimals<1>> = ManagedDecimal::const_decimals_from_raw(BigUint::from(1u64));
}
```
- `num_decimals(&self) -> NumDecimals` returns the number of decimals.
- `to_big_float(&self) -> BigFloat<M>` returns the decimal as `BigFloat<M>`.
- `to_big_int(self) -> BigInt<M>` returns the decimal as `BigInt`.
- `from_big_int<T: Decimals>(big_int: BigInt<M>, num_decimals: T) -> ManagedDecimal<M, T>` constructs a `ManagedDecimal` from a `BigInt` with customizable `num_decimals`.
- `from_big_float<T: Decimals>(big_float: BigFloat<M>, num_decimals: T) -> ManagedDecimal<M, T>` constructs a `ManagedDecimal` from a `BigFloat` with customizable `num_decimals`.
```rust title=example.rs
pub fn main() {
...
let float_1 = BigFloat::<StaticApi>::from_frac(3i64, 2i64);
let fixed_float_1 = ManagedDecimal::<StaticApi, ConstDecimals<1>>::from_big_float(float_1.clone(),ConstDecimals::<1>);
let fixed_float_2 = ManagedDecimal::<StaticApi, NumDecimals>::from_big_float(float_1, 1usize);

assert_eq!(
fixed_float_1,
ManagedDecimal::<StaticApi, ConstDecimals<1>>::const_decimals_from_raw(BigUint::from(15u64))
);
assert_eq!(
fixed_float_2,
ManagedDecimal::<StaticApi, NumDecimals>::from_raw_units(BigUint::from(15u64), 1usize)
);
}
```
38 changes: 4 additions & 34 deletions docs/integrators/deep-history-squad.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,46 +117,16 @@ Apart from the flag mentioned above, the setup of a deep-history observer is ide
Never attach a non-pruned database to a regular observer (i.e. that does not have the above **operation-mode**) - unless you are not interested into the deep-history features. The regular observer irremediably removes, trucates and prunes the data (as configured, for storage efficiency).
:::

Now that we have finished with the installation part, we can proceed with populating the database from a non-pruned database archive. There are two options here:
- Download non-pruned database
- Reconstruct non-pruned database
Now that we have finished with the installation part, we can proceed to populate the non-pruned database. There are two options here:
- Reconstruct non-pruned database (recommended).
- Download non-pruned database (we can provide archives for the required epochs, on request).

[comment]: # (mx-context-auto)

## Downloading non-pruned database

Archives supporting historical lookup are available to download from a Google Cloud Storage [bucket](https://console.cloud.google.com/storage/browser/multiversx-deep-history-archives-mainnet).

In order to avoid unintentional downloads and promote careful fetching of archives, we've enabled the [requester pays](https://cloud.google.com/storage/docs/requester-pays) feature on the bucket that holds the deep-history archives for mainnet.

### Requirements

1. **Google Cloud Platform Account**: An account on Google Cloud Platform with billing enabled is required. Find out how you can manage your billing account and modify your project here:
- https://cloud.google.com/billing/docs/how-to/manage-billing-account
- https://cloud.google.com/billing/docs/how-to/modify-project

2. **Google Cloud SDK**: The Google Cloud SDK includes the `gcloud` command-line tool, which you'll use to interact with Google Cloud Storage. In order to install it, please follow the instructions provided on the [Google Cloud SDK webpage](https://cloud.google.com/sdk/docs/install).

### Downloading archives

Once you have the Google Cloud SDK installed and you're [authenticated](https://cloud.google.com/docs/authentication/gcloud), you can download archives from the Google Cloud Storage bucket using the `gcloud storage cp` command.

Here's an example command that downloads an archive from the `multiversx-deep-history-archives-mainnet` bucket:
```
gcloud storage cp gs://multiversx-deep-history-archives-mainnet/shard-0/Epoch_01200.tar ~/DOWNLOAD_LOCATION --billing-project=BILLING_PROJECT
```
Replace **BILLING_PROJECT** with the name of your billing project and **~/DOWNLOAD_LOCATION** with the directory where the archives should be downloaded.

The following example will download epochs starting with Epoch_01000.tar up to Epoch_01300.tar, for a billing project called **multiversx**:
```
gcloud storage cp gs://multiversx-deep-history-archives-mainnet/shard-0/Epoch_0{1000..1300}.tar ~/Downloads/ --billing-project=multiversx
```

[comment]: # (mx-context-auto)

## Reconstructing non-pruned databases

An alternative to downloading the non-pruned history is to reconstruct it locally (on your own infrastructure).
The recommended method for populating a non-pruned database is to reconstruct it locally (on your own infrastructure).

There are also two options for reconstructing a non-pruned database:
- Based on the **[import-db](/validators/import-db/)** feature, which re-processes past blocks - and, while doing so, retains the whole, non-pruned accounts history.
Expand Down
Loading
Loading