Skip to content

Commit

Permalink
feat:aggregator
Browse files Browse the repository at this point in the history
  • Loading branch information
ZCalz committed Oct 9, 2024
1 parent a7be321 commit e8edd8c
Show file tree
Hide file tree
Showing 13 changed files with 1,240 additions and 6 deletions.
22 changes: 22 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ orml-traits = { path = "orml/traits", default-features = false }
cyborg-primitives = { path = "primitives/cyborg", default-features = false }
pallet-edge-connect = { path = "pallets/edge-connect", default-features = false }
pallet-task-management = { path = "pallets/task-management", default-features = false }
#pallet-status-aggregator = { path = "pallets/status-aggregator", default-features = false }
pallet-status-aggregator = { path = "pallets/status-aggregator", default-features = false }


# Members
Expand Down
1 change: 1 addition & 0 deletions container-chains/nodes/simple/src/chain_spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ fn testnet_genesis(
transaction_payment: Default::default(),
tx_pause: Default::default(),
system: Default::default(),
oracle_membership: Default::default(),
};

serde_json::to_value(g).unwrap()
Expand Down
5 changes: 3 additions & 2 deletions container-chains/runtime-templates/simple/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ tanssi-runtime-common = { workspace = true }
cyborg-primitives = { workspace = true }
pallet-edge-connect = { workspace = true }
pallet-task-management = { workspace = true }
pallet-status-aggregator = { workspace = true }

# Orml
orml-oracle = { workspace = true }
Expand Down Expand Up @@ -194,7 +195,7 @@ std = [
"pallet-task-management/std",
"orml-oracle/std",
# "orml-traits/std",
# "pallet-status-aggregator/std",
"pallet-status-aggregator/std",
]

# Allow to print logs details (no wasm:stripped)
Expand Down Expand Up @@ -245,7 +246,7 @@ runtime-benchmarks = [
"pallet-task-management/runtime-benchmarks",
"orml-oracle/runtime-benchmarks",
# "orml-traits/runtime-benchmarks",
# "pallet-status-aggregator/runtime-benchmarks",
"pallet-status-aggregator/runtime-benchmarks",
]

try-runtime = [
Expand Down
21 changes: 18 additions & 3 deletions container-chains/runtime-templates/simple/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
spec_name: create_runtime_str!("container-chain-template"),
impl_name: create_runtime_str!("container-chain-template"),
authoring_version: 1,
spec_version: 801,
spec_version: 802,
impl_version: 0,
apis: RUNTIME_API_VERSIONS,
transaction_version: 1,
Expand Down Expand Up @@ -677,7 +677,7 @@ parameter_types! {

impl orml_oracle::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type OnNewData = (); // SStatusAggregator;
type OnNewData = StatusAggregator;
type CombineData = DummyCombineData<Runtime>;
type Time = Timestamp;
type OracleKey = (AccountId, WorkerId);
Expand Down Expand Up @@ -718,6 +718,21 @@ impl pallet_task_management::Config for Runtime {
type WeightInfo = pallet_task_management::weights::SubstrateWeight<Runtime>;
}

parameter_types! {
pub const MaxBlockRangePeriod: BlockNumber = 50u32; // Set the max block range to 100 blocks
}

impl pallet_status_aggregator::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = ();

type MaxBlockRangePeriod = MaxBlockRangePeriod;
type ThresholdUptimeStatus = ConstU8<75>;
type MaxAggregateParamLength = ConstU32<300>;

type WorkerInfoHandler = EdgeConnect;
}

// Create the runtime by composing the FRAME pallets that were previously configured.
construct_runtime!(
pub enum Runtime
Expand Down Expand Up @@ -763,7 +778,7 @@ construct_runtime!(

EdgeConnect: pallet_edge_connect = 120,
TaskManagement: pallet_task_management = 121,
// StatusAggregator: pallet_status_aggregator = 122,
StatusAggregator: pallet_status_aggregator = 122,
}
);

Expand Down
Binary file not shown.
56 changes: 56 additions & 0 deletions pallets/status-aggregator/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
[package]
name = "pallet-status-aggregator"
description = "Pallet for determining worker status aggregated from oracle feed"
version = "0.1.0"
edition = "2021"
publish = false

[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

[dependencies]
codec = { package = "parity-scale-codec", version = "3.6.12", default-features = false, features = [
"derive",
] }
scale-info = { version = "2.11.1", default-features = false, features = [
"derive",
] }

log = { workspace = true }
orml-traits = { workspace = true }
orml-oracle = { workspace = true }
pallet-edge-connect = { workspace = true }
cyborg-primitives = { workspace = true }
pallet-timestamp = { workspace = true }

# frame deps
frame-benchmarking = { workspace = true, default-features = false, optional = true }
frame-support = { workspace = true, default-features = false }
frame-system = { workspace = true, default-features = false }

sp-std = { workspace = true, default-features = false }

[dev-dependencies]
sp-core = { workspace = true }
sp-io = { workspace = true }
sp-runtime = { workspace = true }

[features]
default = ["std"]
runtime-benchmarks = [
"frame-benchmarking/runtime-benchmarks",
"frame-support/runtime-benchmarks",
"frame-system/runtime-benchmarks",
]
std = [
"codec/std",
"orml-traits/std",
"frame-benchmarking?/std",
"frame-support/std",
"frame-system/std",
"scale-info/std",
]
try-runtime = [
"frame-support/try-runtime",
"frame-system/try-runtime",
]
25 changes: 25 additions & 0 deletions pallets/status-aggregator/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Status Feed Aggregator

This pallet serves as a support pallet to the oracle. It processes feed data utilizing the `T::OnNewData` trait from the oracle pallet onto this pallet. The pallet keeps aggregated data and entries up to a block interval defined by `MaxBlockRangePeriod` in the pallet config.

## Usage Overview

First an oracle data feeder must be register onto the `OracleMembership` pallet to gain `Oracle` feeder access. Afterwards, the feeder can call the `feed_value()` extrinsic on the `Oracle` pallet.

The `feed_value()` extrinsic takes in a value type of `BoundedVec<(T::OracleKey, T::OracleValue), T::MaxFeedValues>

- `T::MaxFeedValues` limits how many values a feeder can enter in each entry
- `T::OracleKey`: identifies the specific worker to enter information for
- `T::OracleValue`: represents any data we assign to the worker which will be processesed by this aggregator pallet

From here, the status feed aggregator handles all the heavy lifting to process data for each worker based on the values entered.

## Pallet Logic

Data first arrives into this pallet from the `T::OnNewData` trait implementation. It checks whether an oracle feeder provided information for a specfic worker yet via `SubmittedPerPeriod`. If not, `WorkerStatusEntriesPerPeriod` updates by pushing this data along with the current block number onto a bounded vector for a given worker.

After each `MaxBlockRangePeriod` interval, the `on_finalize()` hook calculates all data in `WorkerStatusEntriesPerPeriod` via `derive_status_percentages_for_period()`. Afterwards, storage resets for `SubmittedPerPeriod`, `WorkerStatusEntriesPerPeriod`, and `LastClearedBlock` updates.

The results of each calulation per period can be accessed from `ResultingWorkerStatusPercentages` and `ResultingWorkerStatus`.


123 changes: 123 additions & 0 deletions pallets/status-aggregator/src/benchmarking.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
#![cfg(feature = "runtime-benchmarks")] // Ensures this code is only compiled when benchmarking is enabled

use super::*;
pub use cyborg_primitives::{oracle::ProcessStatus, worker::WorkerId};
use frame_benchmarking::v2::*;

#[benchmarks]
mod benchmarks {
use super::*;

#[benchmark]
fn derive_status_percentages_for_period() {
// Constant defining the maximum number of feed values to generate.
const MAX_FEED_VALUES: u32 = 12;
let max_limit = MAX_FEED_VALUES - 2;

// Loop to generate pseudo-random account IDs and worker IDs
// and simulate inserting data into the system for benchmarking.
for seed in 0..max_limit {
// Generate a pseudo-random account ID using the `account` helper function
let account_id: T::AccountId = account("benchmark_account", 0, seed);
// Generate a pseudo-random worker ID
let worker_id: WorkerId = (seed as u64) * 12345;
// Create a ProcessStatus struct with random online/available status
let process_status = ProcessStatus {
online: seed % 2 == 0,
available: seed % 3 == 0,
};

// Call the `on_new_data` function of the pallet with generated data
Pallet::<T>::on_new_data(
&account_id.clone(),
&(account_id.clone(), worker_id),
&process_status,
);
}

// Benchmark block to measure performance of `derive_status_percentages_for_period`.
#[block]
{
Pallet::<T>::benchmark_derive_status_percentages_for_period();
}

// Set up a test account and worker ID for validation after data insertion
let test_account_id: T::AccountId = account("benchmark_account", 0, 1);
let test_worker_id: WorkerId = (1 as u64) * 12345;

// Assert that WorkerStatusPercentage for the test account and worker ID exists in the
// ResultingWorkerStatusPercentages storage.
assert!(
ResultingWorkerStatusPercentages::<T>::contains_key((
test_account_id.clone(),
test_worker_id
)),
"WorkerStatusPercentage not found in storage"
);

// Assert that WorkerStatus for the test account and worker ID exists in the
// ResultingWorkerStatus storage.
assert!(
ResultingWorkerStatus::<T>::contains_key((test_account_id.clone(), test_worker_id)),
"WorkerStatus not found in storage"
);
}

#[benchmark]
fn on_new_data() {
// Constant defining the max number of feed values
const MAX_FEED_VALUES: u32 = 12;

// Benchmark block to measure performance of `on_new_data`.
#[block]
{
let max_limit = MAX_FEED_VALUES - 2;

// Loop to generate and insert mock data for benchmarking
for seed in 0..max_limit {
// Generate a pseudo-random account ID using the `account` helper function
let account_id: T::AccountId = account("benchmark_account", 0, seed);
// Generate a pseudo-random worker ID
let worker_id: WorkerId = (seed as u64) * 12345;
// Create a ProcessStatus struct with random online/available status
let process_status = ProcessStatus {
online: seed % 2 == 0,
available: seed % 3 == 0,
};

// Call the `on_new_data` function of the pallet with generated data
Pallet::<T>::on_new_data(
&account_id.clone(),
&(account_id.clone(), worker_id),
&process_status,
);
}
}

// Set up a test account and worker ID for validation after data insertion
let test_account_id: T::AccountId = account("benchmark_account", 0, 1);
let test_worker_id: WorkerId = (1 as u64) * 12345;

// Assert that submission exists for the given account and worker ID in SubmittedPerPeriod
assert!(
SubmittedPerPeriod::<T>::get((
test_account_id.clone(),
(test_account_id.clone(), test_worker_id)
)),
"Submission not found"
);

// Assert that key exists in WorkerStatusEntriesPerPeriod for the test account and worker ID
assert!(
WorkerStatusEntriesPerPeriod::<T>::contains_key((test_account_id.clone(), test_worker_id)),
"Entry key does not exists in WorkerStatusEntriesPerPeriod"
);
}

// Defines the benchmark test suite, linking it to the pallet and mock runtime
impl_benchmark_test_suite!(
Pallet,
crate::benchmarking::test::new_test_ext(),
crate::mock::Test,
);
}
Loading

0 comments on commit e8edd8c

Please sign in to comment.