-
Notifications
You must be signed in to change notification settings - Fork 11
Add beacon chain update plugin and cranker executable #280
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
base: master
Are you sure you want to change the base?
Changes from 8 commits
76654be
429c899
f2ac82c
dff465d
0fc09cc
04f8348
0402d22
92bb896
e1c9aa3
24d6374
09cd13c
8f20ec5
b56d806
f92c2c6
d8b7d9e
8907468
70612d2
7ddd427
92634ce
3d10584
1624347
4816e6d
351a94a
6f11d07
5699d75
8804659
69a0a96
a3e44e4
2df59b1
18efd89
cbb1cf4
723c19a
9580154
18603cb
ec2429c
12dceb5
a4f1a71
1bf51df
abe0a31
7ec6547
44a5135
45882f6
7a32fe1
2bcb00b
736dc1d
f08351c
8b86db2
4e3efa0
3a5eabf
87e418c
7b22e17
bc58ab1
b3a4bd3
8813d87
1acf232
36cca89
d743f96
63e9910
99ea74f
dbf4fac
996a817
f4c232a
0ba6177
5711f75
7dd0e3b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,182 @@ | ||
| # Cron String Parser - Implementation Summary | ||
|
|
||
| A complete cron expression parser has been added to the `cron_plugin` to enable string-based schedule configuration. | ||
|
|
||
| ## Files Created | ||
|
|
||
| ### 1. Header File | ||
| **`plugins/cron_plugin/include/sysio/services/cron_parser.hpp`** | ||
| - Public API for parsing cron expressions | ||
| - Two functions: | ||
| - `parse_cron_schedule()` - Returns `std::optional` (safe) | ||
| - `parse_cron_schedule_or_throw()` - Throws on error | ||
|
|
||
| ### 2. Implementation | ||
| **`plugins/cron_plugin/src/services/cron_parser.cpp`** | ||
| - Complete parser implementation supporting: | ||
| - Wildcards: `*` | ||
| - Exact values: `5` | ||
| - Ranges: `1-5` | ||
| - Steps: `*/5` or `10-50/5` | ||
| - Lists: `1,3,5,7` | ||
| - Validates all field ranges | ||
| - Supports standard 5-field and extended 6-field formats | ||
|
|
||
| ### 3. Tests | ||
| **`plugins/cron_plugin/test/test_cron_parser.cpp`** | ||
| - Comprehensive test suite with 25+ test cases | ||
| - Tests valid parsing, error handling, and real-world examples | ||
|
|
||
| ### 4. Documentation | ||
| **`plugins/cron_plugin/CRON_PARSER_USAGE.md`** | ||
| - Complete usage guide with examples | ||
| - Common schedule patterns | ||
| - Integration examples | ||
|
|
||
| ## Quick Start | ||
|
|
||
| ### Include the header | ||
| ```cpp | ||
| #include <sysio/services/cron_parser.hpp> | ||
| ``` | ||
|
|
||
| ### Parse a cron expression | ||
| ```cpp | ||
| using namespace sysio::services; | ||
|
|
||
| // Safe parsing (returns optional) | ||
| auto sched_opt = parse_cron_schedule("*/5 * * * *"); | ||
| if (sched_opt) { | ||
| auto& cron = app().get_plugin<cron_plugin>(); | ||
| cron.add_job(*sched_opt, []() { | ||
| ilog("Runs every 5 minutes"); | ||
| }); | ||
| } | ||
|
|
||
| // Or with error handling (throws on failure) | ||
| try { | ||
| auto sched = parse_cron_schedule_or_throw("0 9-17 * * 1-5"); | ||
| // Use schedule... | ||
| } catch (const fc::exception& e) { | ||
| elog("Parse error: {}", e.to_detail_string()); | ||
| } | ||
| ``` | ||
|
|
||
| ## Format Support | ||
|
|
||
| ### Standard Format (5 fields) | ||
| ``` | ||
| minute hour day-of-month month day-of-week | ||
| ``` | ||
|
|
||
| **Example:** `"*/15 9-17 * * 1-5"` = Every 15 minutes, 9 AM-5 PM, weekdays | ||
|
|
||
| ### Extended Format (6 fields - with milliseconds) | ||
| ``` | ||
| milliseconds minute hour day-of-month month day-of-week | ||
| ``` | ||
|
|
||
| **Example:** `"*/5000 * * * * *"` = Every 5 seconds | ||
|
|
||
| ## Common Patterns | ||
|
|
||
| | Description | Expression | | ||
| |-------------|------------| | ||
| | Every minute | `* * * * *` | | ||
| | Every 5 minutes | `*/5 * * * *` | | ||
| | Hourly at :00 | `0 * * * *` | | ||
| | Daily at midnight | `0 0 * * *` | | ||
| | Business hours (9-5, weekdays) | `0 9-17 * * 1-5` | | ||
| | Every 15 minutes during business hours | `*/15 9-17 * * 1-5` | | ||
| | First of month | `0 0 1 * *` | | ||
| | Weekly (Sunday 2 AM) | `0 2 * * 0` | | ||
| | Every 5 seconds (extended) | `*/5000 * * * * *` | | ||
|
|
||
| ## Integration Example | ||
|
|
||
| ### Using in beacon_chain_update_plugin | ||
|
|
||
| ```cpp | ||
| void beacon_chain_update_plugin::plugin_initialize(const variables_map& options) { | ||
| // Get schedule from config | ||
| std::string schedule_expr = "0 */6 * * *"; // Every 6 hours | ||
|
|
||
| if (options.count("beacon-chain-update-schedule")) { | ||
| schedule_expr = options.at("beacon-chain-update-schedule").as<std::string>(); | ||
| } | ||
|
|
||
| try { | ||
| _update_schedule = parse_cron_schedule_or_throw(schedule_expr); | ||
| ilog("Beacon chain update schedule: {}", schedule_expr); | ||
| } catch (const fc::exception& e) { | ||
| elog("Invalid schedule expression '{}': {}", | ||
| schedule_expr, e.to_detail_string()); | ||
| throw; | ||
| } | ||
| } | ||
|
|
||
| void beacon_chain_update_plugin::plugin_startup() { | ||
| auto& cron = app().get_plugin<cron_plugin>(); | ||
|
|
||
| _update_job_id = cron.add_job( | ||
| _update_schedule, | ||
| [this]() { | ||
| update_beacon_chain_data(); | ||
| }, | ||
| cron_service::job_metadata_t{ | ||
| .one_at_a_time = true, | ||
| .tags = {"beacon-chain", "update"}, | ||
| .label = "beacon_chain_updater" | ||
| } | ||
| ); | ||
|
|
||
| ilog("Started beacon chain update job: {}", _update_job_id); | ||
| } | ||
| ``` | ||
|
|
||
| ## Building | ||
|
|
||
| The parser is automatically included when building the `cron_plugin`. The `plugin_target()` macro in CMakeLists.txt will pick up the new source file. | ||
|
|
||
| To build: | ||
| ```bash | ||
| ninja -C build/debug-claude cron_plugin | ||
| ``` | ||
|
|
||
| To run tests: | ||
| ```bash | ||
| ./build/debug-claude/plugins/cron_plugin/test/test_cron_plugin --run_test=cron_parser_tests | ||
| ``` | ||
|
|
||
| ## Features | ||
|
|
||
| ✅ Standard cron syntax support | ||
| ✅ Extended format with milliseconds (sub-minute precision) | ||
| ✅ All operators: wildcards, ranges, steps, lists | ||
| ✅ Comprehensive validation | ||
| ✅ Error handling (optional or exception-based) | ||
| ✅ Full test coverage | ||
| ✅ Documentation with examples | ||
| ✅ Zero external dependencies (uses C++20 standard library) | ||
|
|
||
| ## Next Steps | ||
|
|
||
| 1. **Build and test:** | ||
| ```bash | ||
| ninja -C build/debug-claude cron_plugin | ||
| ./build/debug-claude/plugins/cron_plugin/test/test_cron_plugin | ||
| ``` | ||
|
|
||
| 2. **Use in your plugin:** | ||
| ```cpp | ||
| #include <sysio/services/cron_parser.hpp> | ||
| auto schedule = parse_cron_schedule_or_throw("*/5 * * * *"); | ||
| ``` | ||
|
|
||
| 3. **Add config option** (optional): | ||
| ```cpp | ||
| cfg.add_options() | ||
| ("my-schedule", | ||
| bpo::value<std::string>()->default_value("*/5 * * * *"), | ||
| "Cron expression for scheduling (e.g., '*/5 * * * *' for every 5 minutes)"); | ||
| ``` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,6 +12,9 @@ | |
| #include <fc/network/ethereum/ethereum_abi.hpp> | ||
| #include <fc/network/json_rpc/json_rpc_client.hpp> | ||
|
|
||
| #include <future> | ||
| #include <utility> | ||
|
|
||
| namespace fc::network::ethereum { | ||
| using namespace fc::crypto; | ||
| using namespace fc::crypto::ethereum; | ||
|
|
@@ -355,6 +358,16 @@ class ethereum_client : public std::enable_shared_from_this<ethereum_client> { | |
| */ | ||
| std::string send_raw_transaction(const std::string& raw_tx_data); | ||
|
|
||
| /** | ||
| * @brief Receives a transaction hash that resolves to the block number once the transaction is included in a block. | ||
| * @param tx_hash The transaction hash | ||
| * @return A future<uint64_t> that resolves to the block number of the block | ||
| * the transaction was included in. The future is fulfilled by a background | ||
| * thread that polls eth_getTransactionReceipt until the receipt is available. | ||
| * @throws fc::network::json_rpc::json_rpc_exception if the initial RPC call fails. | ||
| */ | ||
| std::future<uint64_t> identify_block_for_transaction(const std::string& tx_hash); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| /** | ||
| * @brief Retrieves logs based on filter parameters. | ||
| * @param params The filter parameters for fetching logs. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1048,7 +1048,12 @@ void fc::from_variant(const fc::variant& var, fc::network::ethereum::abi::contra | |
|
|
||
| FC_ASSERT(var.is_object(), "Variant must be an object to deserialize ABI contract"); | ||
| auto& obj = var.get_object(); | ||
| vo.name = obj["name"].as_string(); | ||
| const auto name_itr = obj.find("name"); | ||
| const bool deferred_name = name_itr == obj.end(); | ||
| if (!deferred_name) { | ||
| vo.name = name_itr->value().as_string(); | ||
| } | ||
|
|
||
| auto type_str = obj["type"].as_string(); | ||
| vo.type = fc::reflector<fc::network::ethereum::abi::invoke_target_type>::from_string(type_str.c_str()); | ||
|
|
||
|
|
@@ -1072,4 +1077,19 @@ void fc::from_variant(const fc::variant& var, fc::network::ethereum::abi::contra | |
|
|
||
| parse_components(vo.inputs, "inputs"); | ||
| parse_components(vo.outputs, "outputs"); | ||
| bool missed = true; | ||
| if(deferred_name) { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I had initially put this in for reporting what we don't expect to see, and determine if something else needs to be reported. Not sure if we should drop or at least keep it in here while we are still working on ethereum client scripts
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. changed to throwing exception to identify what we may be missing. |
||
| if(type_str == "receive") { | ||
| auto state_mutability_str = obj["stateMutability"].as_string(); | ||
| if (state_mutability_str == "payable") { | ||
| missed = false; | ||
| } | ||
| } | ||
| if(missed) { | ||
| elog("no name for:"); | ||
| for(auto itr = obj.begin(); itr != obj.end(); ++itr) { | ||
| ilog("key: {}", itr->key()); | ||
| } | ||
|
brianjohnson5972 marked this conversation as resolved.
Outdated
|
||
| } | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.