|
| 1 | +--- |
| 2 | +sidebar_position: 2 |
| 3 | +--- |
| 4 | + |
| 5 | +# Commit Module |
| 6 | + |
| 7 | +While a module can be written in any language, we currently provide some utilities for Rust, with the goal of supporting more generalized APIs and simplify development in languages other than Rust. |
| 8 | + |
| 9 | +In Rust, we provide utilities to load and run modules. Simply add to your `Cargo.toml`: |
| 10 | +```toml |
| 11 | +commit-boost = { git = "https://github.com/Commit-Boost/commit-boost-client", version = "..." } |
| 12 | +``` |
| 13 | + |
| 14 | +You will now be able to import the utils with: |
| 15 | +```rust |
| 16 | +use commit_boost::prelude::*; |
| 17 | +``` |
| 18 | + |
| 19 | + |
| 20 | +## Config |
| 21 | +Your module will likely need a configuration for the Node Operator to customize. This will have to be in the `cb-config.toml` file, in the correct `[[module]]` section. In the module, you can define and load your config as follows. |
| 22 | + |
| 23 | +First, define all the parameters needed in a struct: |
| 24 | +```rust |
| 25 | +#[derive(Debug, Deserialize)] |
| 26 | +struct ExtraConfig { |
| 27 | + sleep_secs: u64, |
| 28 | +} |
| 29 | +``` |
| 30 | +then pass that struct to the `load_commit_module_config` function, which will load and parse the config. Your custom config will be under the `extra` field. |
| 31 | + |
| 32 | +```rust |
| 33 | +let config = load_commit_module_config::<ExtraConfig>().unwrap(); |
| 34 | +let to_sleep = config.extra.sleep_secs; |
| 35 | +``` |
| 36 | + |
| 37 | +The loaded `config` also has a few other useful fields: |
| 38 | +- the unique `id` of the module |
| 39 | +- chain spec |
| 40 | +- a `SignerClient` to call the [SignerAPI](/api), already setup with the correct JWT |
| 41 | + |
| 42 | + |
| 43 | +## Requesting signatures |
| 44 | +At its core the Signer Module simply provides a signature on a 32-byte data digest. The signatures are currently provided with either the validator keys (BLS) or a proxy key (BLS or ECDSA) for a given validator key, both on the [builder domain](https://github.com/Commit-Boost/commit-boost-client/blob/main/crates/common/src/signature.rs#L88-L96). |
| 45 | + |
| 46 | +In the example we use `TreeHash`, already used in the CL, to create the digest from a custom struct: |
| 47 | +```rust |
| 48 | +#[derive(TreeHash)] |
| 49 | +struct Datagram { |
| 50 | + data: u64, |
| 51 | +} |
| 52 | +``` |
| 53 | + |
| 54 | +Furthermore, in order to request a signature, we'd need a public key of the validator. You can get a list of available keys by calling: |
| 55 | +```rust |
| 56 | +let pubkeys = config.signer_client.get_pubkeys().await.unwrap(); |
| 57 | +``` |
| 58 | + |
| 59 | +Which will call the `get_pubkeys` endpoint of the [SignerAPI](/api), returning all the consensus pubkeys and the corresponding proxy keys, of your module. |
| 60 | + |
| 61 | +Note that the requests are authenticated using a JWT, that must be regularly refreshed as it expires after a certain time. To do so, you can call: |
| 62 | +```rust |
| 63 | +config.signer_client.refresh_token().await.unwrap(); |
| 64 | +``` |
| 65 | +You have the `SIGNER_JWT_EXPIRATION` constant available in the `commit-boost` crate, which is the time in seconds after which the JWT will expire. |
| 66 | + |
| 67 | +Then, we can request a signature either with a consensus key or with a proxy key: |
| 68 | + |
| 69 | +### With a consensus key |
| 70 | +Requesting a signature is as simple as: |
| 71 | +```rust |
| 72 | +let datagram = Datagram { data: 1 }; |
| 73 | +let request = SignConsensusRequest::builder(pubkey).with_msg(&datagram); |
| 74 | +let signature = config.signer_client.request_consensus_signature(&request).await.unwrap(); |
| 75 | +``` |
| 76 | + |
| 77 | +Where `pubkey` is the validator (consensus) public key for which the signature is requested. |
| 78 | + |
| 79 | +### With a proxy key |
| 80 | +You'll have to first request a proxy key be generated for a given consensus key. |
| 81 | +We support two signature schemes for proxies: BLS or ECDSA. |
| 82 | + |
| 83 | +To request a proxy: |
| 84 | +```rust |
| 85 | +// BLS proxy |
| 86 | +let proxy_delegation = self.config.signer_client.generate_proxy_key_bls(pubkey).await?; |
| 87 | +let proxy_pubkey = proxy_delegation.message.proxy; |
| 88 | + |
| 89 | +// or ECDSA proxy |
| 90 | +let proxy_delegation = self.config.signer_client.generate_proxy_key_ecdsa(pubkey).await?; |
| 91 | +let proxy_address = proxy_delegation.message.proxy; |
| 92 | +``` |
| 93 | + |
| 94 | +Where `pubkey` is the validator (consensus) public key for which a proxy is to be generated. |
| 95 | + |
| 96 | +Then you can use the generated proxy key to request a signature: |
| 97 | +```rust |
| 98 | +// if `proxy_pubkey` is a BLS proxy |
| 99 | +let datagram = Datagram { data: 1 }; |
| 100 | +let request = SignProxyRequest::builder(proxy_pubkey).with_msg(&datagram); |
| 101 | +let signature = config.signer_client.request_proxy_signature_bls(&request).await.unwrap(); |
| 102 | + |
| 103 | +// or for ECDSA proxy |
| 104 | +let datagram = Datagram { data: 1 }; |
| 105 | +let request = SignProxyRequest::builder(proxy_address).with_msg(&datagram); |
| 106 | +let signature = config.signer_client.request_proxy_signature_ecdsa(&request).await.unwrap(); |
| 107 | +``` |
| 108 | + |
| 109 | +## Metrics |
| 110 | +We provide support for modules to record custom metrics which are automatically scraped by Prometheus. This involves three steps |
| 111 | +### Define metrics |
| 112 | +You can use the `prometheus` crate to create a custom registry and metrics, for example: |
| 113 | + |
| 114 | +```rust |
| 115 | +static ref MY_CUSTOM_REGISTRY: Registry = Registry::new_custom(Some("da_commit".to_string()), None).unwrap(); |
| 116 | +static ref SIG_RECEIVED_COUNTER: IntCounter = IntCounter::new("signature_received", "successful signature requests received").unwrap(); |
| 117 | +``` |
| 118 | + |
| 119 | +### Start Metrics Provider |
| 120 | +When starting the module, you should register all metrics, and start the `MetricsProvider`: |
| 121 | +```rust |
| 122 | +MY_CUSTOM_REGISTRY.register(Box::new(SIG_RECEIVED_COUNTER.clone())).unwrap(); |
| 123 | +MetricsProvider::load_and_run(MY_CUSTOM_REGISTRY.clone()); |
| 124 | +``` |
| 125 | +The `MetricsProvider` will load the configuration needed and start a server with a `/metrics` endpoint for Prometheus to scrape. |
| 126 | + |
| 127 | +### Record metrics |
| 128 | +All that is left is to use the metrics throughout your code: |
| 129 | +```rust |
| 130 | +SIG_RECEIVED_COUNTER.inc(); |
| 131 | +``` |
| 132 | +These will be automatically scraped by the Prometheus service running, and exposed on port `9090`. We plan to allow developers to ship pre-made dashboards together with their modules, to allow Node Operators to have an improved oversight on the modules they are running. |
0 commit comments