Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 5 additions & 4 deletions Cargo.lock

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

39 changes: 38 additions & 1 deletion config/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,15 @@ pub struct Connections {
pub requested_blocks_batch_limit: u32,
}

/// Struct to store all values of an API key
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, PartialStruct, Serialize)]
pub struct ApiKey {
pub id: String,
pub key: String,
pub reward: Option<u64>,
pub rate: Option<u32>,
}

/// Witnessing-specific configuration.
#[derive(Clone, Debug, Eq, PartialEq, PartialStruct)]
#[partial_struct(derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize))]
Expand All @@ -289,6 +298,19 @@ pub struct Witnessing {
/// and we are taking as small of a risk as possible when committing to specially crafted data
/// requests that may be potentially ill-intended.
pub proxies: Vec<String>,

/// Binary flag telling whether to enable default data request witnessing
#[partial_struct(serde(default))]
pub enabled: bool,

/// Binary flag telling whether to enable data request witnessing using API keys
#[partial_struct(serde(default))]
pub enabled_with_keys: bool,

/// Collection of the API keys defined in the config file
/// The
#[partial_struct(serde(default))]
pub api_keys: Vec<ApiKey>,
}

/// Available storage backends
Expand Down Expand Up @@ -346,7 +368,7 @@ pub struct JsonRPC {
#[derive(PartialStruct, Debug, Clone, PartialEq, Eq)]
#[partial_struct(derive(Deserialize, Serialize, Default, Debug, Clone, PartialEq, Eq))]
pub struct Mining {
/// Binary flag telling whether to enable the MiningManager or not
/// Binary flag telling whether to enable the mining of blocks or not
pub enabled: bool,
/// Limits the number of retrievals to perform during a single epoch.
/// This tries to prevent nodes from forking out or being left in a
Expand Down Expand Up @@ -1147,6 +1169,18 @@ impl Witnessing {
.proxies
.clone()
.unwrap_or_else(|| defaults.witnessing_proxies()),
enabled: config
.enabled
.to_owned()
.unwrap_or_else(|| defaults.witnessing_enabled()),
enabled_with_keys: config
.enabled_with_keys
.to_owned()
.unwrap_or_else(|| defaults.witnessing_with_keys_enabled()),
api_keys: config
.api_keys
.to_owned()
.unwrap_or_else(|| defaults.api_keys()),
}
}

Expand All @@ -1155,6 +1189,9 @@ impl Witnessing {
allow_unproxied: Some(self.allow_unproxied),
paranoid_percentage: Some(self.paranoid_percentage),
proxies: Some(self.proxies.clone()),
enabled: Some(self.enabled),
enabled_with_keys: Some(self.enabled_with_keys),
api_keys: Some(self.api_keys.clone()),
}
}

Expand Down
19 changes: 19 additions & 0 deletions config/src/defaults.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
//!
//! This module contains per-environment default values for the Witnet
//! protocol params.
use crate::config::ApiKey;

use std::{
collections::{HashMap, HashSet},
net::{IpAddr, Ipv4Addr, SocketAddr},
Expand Down Expand Up @@ -146,6 +148,23 @@ pub trait Defaults {
vec![]
}

/// Node should have witnessing enabled by default
fn witnessing_enabled(&self) -> bool {
true
}

/// Witnessing using API keys is disabled by default since it requires setting up (paid) API
/// keys by the operator.
fn witnessing_with_keys_enabled(&self) -> bool {
false
}

/// Adds a collation of API keys to resolve data requests which contain an API key placeholder
/// in the URL or headers
fn api_keys(&self) -> Vec<ApiKey> {
vec![]
}

/// Timestamp at the start of epoch 0
fn consensus_constants_checkpoint_zero_timestamp(&self) -> i64;

Expand Down
1 change: 1 addition & 0 deletions data_structures/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ protobuf = { version = "2.28.0", features = ["with-serde"] }
protobuf-convert = "0.4.0"
rand = "0.8.5"
rand_distr = "0.4.3"
regex = "1.11.3"
serde = { version = "1.0.104", features = ["derive"] }
serde_cbor = "0.11.1"
serde_json = "1.0.48"
Expand Down
14 changes: 13 additions & 1 deletion data_structures/src/builders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use crate::{
transaction::Transaction,
types::{
Address, Command, GetPeers, InventoryAnnouncement, InventoryRequest, IpAddress, LastBeacon,
Message, Peers, Verack, Version,
Message, Peers, RegisteredApiKeys, SignedRegisteredApiKeys, Verack, Version,
},
};

Expand Down Expand Up @@ -150,6 +150,18 @@ impl Message {
Message::build_message(magic, Command::SuperBlockVote(superblock_vote))
}

/// Function to build SignedRegisteredApiKeys messages
pub fn build_api_keys_message(
magic: u16,
keys: RegisteredApiKeys,
signature: KeyedSignature,
) -> Message {
Message::build_message(
magic,
Command::SignedRegisteredApiKeys(SignedRegisteredApiKeys { keys, signature }),
)
}

/// Function to build a message from a command
fn build_message(magic: u16, command: Command) -> Message {
Message {
Expand Down
12 changes: 11 additions & 1 deletion data_structures/src/capabilities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,15 @@ pub enum Capability {
Mining = 0,
/// The universal HTTP GET / HTTP POST / WIP-0019 RNG capability
Witnessing = 1,
/// The HTTP GET / HTTP POST capability which requires API keys
WitnessingWithKey = 2,
}

pub const ALL_CAPABILITIES: [Capability; 2] = [Capability::Mining, Capability::Witnessing];
pub const ALL_CAPABILITIES: [Capability; 3] = [
Capability::Mining,
Capability::Witnessing,
Capability::WitnessingWithKey,
];

#[derive(Copy, Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
pub struct CapabilityMap<T>
Expand All @@ -35,6 +41,7 @@ where
{
pub mining: T,
pub witnessing: T,
pub witnessing_with_key: T,
}

impl<T> CapabilityMap<T>
Expand All @@ -46,6 +53,7 @@ where
match capability {
Capability::Mining => self.mining,
Capability::Witnessing => self.witnessing,
Capability::WitnessingWithKey => self.witnessing_with_key,
}
}

Expand All @@ -54,12 +62,14 @@ where
match capability {
Capability::Mining => self.mining = value,
Capability::Witnessing => self.witnessing = value,
Capability::WitnessingWithKey => self.witnessing_with_key = value,
}
}

#[inline]
pub fn update_all(&mut self, value: T) {
self.mining = value;
self.witnessing = value;
self.witnessing_with_key = value;
}
}
82 changes: 80 additions & 2 deletions data_structures/src/chain/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use ethereum_types::U256;
use futures::future::BoxFuture;
use ordered_float::OrderedFloat;
use partial_struct::PartialStruct;
use regex::Regex;
use serde::{Deserialize, Serialize};
use std::{
cell::{Cell, RefCell},
Expand Down Expand Up @@ -54,6 +55,7 @@ use crate::{
RevealTransaction, StakeTransaction, TallyTransaction, Transaction, TxInclusionProof,
UnstakeTransaction, VTTransaction,
},
types::RegisteredApiKeys,
utxo_pool::{OldUnspentOutputsPool, OwnUnspentOutputsPool, UnspentOutputsPool},
vrf::{BlockEligibilityClaim, DataRequestEligibilityClaim},
wit::{WIT_DECIMAL_PLACES, Wit},
Expand Down Expand Up @@ -2077,6 +2079,12 @@ pub enum RADType {
/// HTTP HEAD request
#[serde(rename = "HTTP-HEAD")]
HttpHead,
/// HTTP GET request requiring an API key
#[serde(rename = "HTTP-GET-KEY")]
HttpGetKey,
/// HTTP POST request requiring an API key
#[serde(rename = "HTTP-POST-KEY")]
HttpPostKey,
}

impl RADType {
Expand All @@ -2086,6 +2094,10 @@ impl RADType {
RADType::HttpGet | RADType::HttpPost | RADType::HttpHead
)
}

pub fn is_http_key(&self) -> bool {
matches!(self, RADType::HttpGetKey | RADType::HttpPostKey)
}
}

/// RAD request data structure
Expand Down Expand Up @@ -2233,9 +2245,11 @@ impl RADRetrieve {
// Anything is fine
Ok(())
}
RADType::HttpGet => check(&[Field::Kind, Field::Url, Field::Script], &[Field::Headers]),
RADType::HttpGet | RADType::HttpGetKey => {
check(&[Field::Kind, Field::Url, Field::Script], &[Field::Headers])
}
RADType::Rng => check(&[Field::Kind, Field::Script], &[]),
RADType::HttpPost => {
RADType::HttpPost | RADType::HttpPostKey => {
// In HttpPost the body is optional because empty body should also be allowed
check(
&[Field::Kind, Field::Url, Field::Script],
Expand Down Expand Up @@ -2297,6 +2311,66 @@ impl RADRetrieve {
.saturating_add(body_weight)
.saturating_add(headers_weight)
}

/// Function to check if an URL contains an API key placeholder
pub fn api_key_in_url(&self) -> bool {
if self.kind != RADType::HttpGetKey && self.kind != RADType::HttpPostKey {
return false;
}

let rex = Regex::new(r".+<[a-zA-Z0-9_-]+_API_KEY>.*").unwrap();

rex.is_match(&self.url)
}

/// Function to get the placeholder of API key
pub fn url_extract_api_key(&self) -> Option<&str> {
if self.kind != RADType::HttpGetKey && self.kind != RADType::HttpPostKey {
return None;
}

let rex = Regex::new(r".+(<[a-zA-Z0-9_-]+_API_KEY>).*").unwrap();

match rex.captures(&self.url) {
Some(captures) => Some(captures.get(1).unwrap().as_str()),
None => None,
}
}

/// Function to check if the value of a header is an API key placeholder
pub fn api_key_in_headers(&self) -> bool {
if self.kind != RADType::HttpGetKey && self.kind != RADType::HttpPostKey {
return false;
}

let rex = Regex::new(r".*<[a-zA-Z0-9_-]+_API_KEY>.*").unwrap();

self.headers.iter().any(|(_, value)| rex.is_match(&value))
}

/// Function to get the placeholder of API key
pub fn headers_extract_api_key(&self) -> Option<(usize, &str)> {
if self.kind != RADType::HttpGetKey && self.kind != RADType::HttpPostKey {
return None;
}

let rex = Regex::new(r".*(<[a-zA-Z0-9_-]+_API_KEY>).*").unwrap();

for (idx, (_, header_value)) in self.headers.iter().enumerate() {
if rex.is_match(&header_value) {
return Some((
idx,
rex.captures(&header_value)
.unwrap()
.get(1)
.unwrap()
.as_str(),
));
}
}

None
}
}

/// Filter stage
Expand Down Expand Up @@ -4841,6 +4915,10 @@ pub enum SignaturesToVerify {
SuperBlockVote {
superblock_vote: SuperBlockVote,
},
SignedRegisteredApiKeys {
keys: RegisteredApiKeys,
signature: KeyedSignature,
},
}

// Auxiliar functions for test
Expand Down
Loading
Loading