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

feat: Add Atlas configuration via Stacks.toml #3618

Merged
merged 8 commits into from
Apr 4, 2023
2 changes: 1 addition & 1 deletion src/chainstate/coordinator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -473,7 +473,7 @@ impl<'a, T: BlockEventDispatcher, U: RewardSetProvider, B: BurnchainHeaderReader
let canonical_sortition_tip =
SortitionDB::get_canonical_sortition_tip(sortition_db.conn()).unwrap();

let atlas_config = atlas_config.unwrap_or(AtlasConfig::default(false));
let atlas_config = atlas_config.unwrap_or(AtlasConfig::new(false));
let atlas_db =
AtlasDB::connect(atlas_config.clone(), &format!("{}/atlas", path), true).unwrap();

Expand Down
2 changes: 1 addition & 1 deletion src/chainstate/coordinator/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4343,7 +4343,7 @@ fn atlas_stop_start() {
let initial_balances = vec![(signer_pk.clone().into(), balance)];
let atlas_qci = QualifiedContractIdentifier::new(signer_pk.clone().into(), atlas_name.clone());
// include our simple contract in the atlas config
let mut atlas_config = AtlasConfig::default(false);
let mut atlas_config = AtlasConfig::new(false);
atlas_config.contracts.insert(atlas_qci.clone());

setup_states(
Expand Down
46 changes: 41 additions & 5 deletions src/net/atlas/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ lazy_static! {
pub static ref BNS_CHARS_REGEX: Regex = Regex::new("^([a-z0-9]|[-_])*$").unwrap();
}

const ATTACHMENTS_MAX_SIZE_MIN: u32 = 1_048_576;
const MAX_UNINSTANTIATED_ATTACHMENTS_MIN: u32 = 10_000;
const UNINSTANTIATED_ATTACHMENTS_EXPIRE_AFTER_MIN: u32 = 3_600;
const UNRESOLVED_ATTACHMENT_INSTANCES_EXPIRE_AFTER_MIN: u32 = 172_800;

#[derive(Debug, Clone)]
pub struct AtlasConfig {
pub contracts: HashSet<QualifiedContractIdentifier>,
Expand All @@ -65,18 +70,49 @@ pub struct AtlasConfig {
}

impl AtlasConfig {
pub fn default(mainnet: bool) -> AtlasConfig {
pub fn new(mainnet: bool) -> AtlasConfig {
let mut contracts = HashSet::new();
contracts.insert(boot_code_id("bns", mainnet));
AtlasConfig {
contracts,
attachments_max_size: 1_048_576,
max_uninstantiated_attachments: 10_000,
uninstantiated_attachments_expire_after: 3_600,
unresolved_attachment_instances_expire_after: 172_800,
attachments_max_size: ATTACHMENTS_MAX_SIZE_MIN,
max_uninstantiated_attachments: MAX_UNINSTANTIATED_ATTACHMENTS_MIN,
uninstantiated_attachments_expire_after: UNINSTANTIATED_ATTACHMENTS_EXPIRE_AFTER_MIN,
unresolved_attachment_instances_expire_after:
UNRESOLVED_ATTACHMENT_INSTANCES_EXPIRE_AFTER_MIN,
genesis_attachments: None,
}
}

pub fn validate(&self) -> Result<(), String> {
if self.attachments_max_size < ATTACHMENTS_MAX_SIZE_MIN {
Err(format!(
"Invalid value for `attachments_max_size`: {}. Expected {} or greater",
self.attachments_max_size, ATTACHMENTS_MAX_SIZE_MIN
))
} else if self.max_uninstantiated_attachments < MAX_UNINSTANTIATED_ATTACHMENTS_MIN {
Err(format!(
"Invalid value for `max_uninstantiated_attachments`: {}. Expected {} or greater",
self.max_uninstantiated_attachments, MAX_UNINSTANTIATED_ATTACHMENTS_MIN
))
} else if self.uninstantiated_attachments_expire_after
< UNINSTANTIATED_ATTACHMENTS_EXPIRE_AFTER_MIN
{
Err(format!(
"Invalid value for `uninstantiated_attachments_expire_after`: {}. Expected {} or greater",
self.uninstantiated_attachments_expire_after, UNINSTANTIATED_ATTACHMENTS_EXPIRE_AFTER_MIN
))
} else if self.unresolved_attachment_instances_expire_after
< UNRESOLVED_ATTACHMENT_INSTANCES_EXPIRE_AFTER_MIN
{
Err(format!(
"Invalid value for `unresolved_attachment_instances_expire_after`: {}. Expected {} or greater",
self.unresolved_attachment_instances_expire_after, UNRESOLVED_ATTACHMENT_INSTANCES_EXPIRE_AFTER_MIN
))
} else {
Ok(())
}
}
}

#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Hash)]
Expand Down
3 changes: 1 addition & 2 deletions src/net/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2713,8 +2713,7 @@ pub mod test {
}

let atlasdb_path = format!("{}/atlas.sqlite", &test_path);
let atlasdb =
AtlasDB::connect(AtlasConfig::default(false), &atlasdb_path, true).unwrap();
let atlasdb = AtlasDB::connect(AtlasConfig::new(false), &atlasdb_path, true).unwrap();

let conf = config.clone();
let post_flight_callback = move |clarity_tx: &mut ClarityTx| {
Expand Down
2 changes: 1 addition & 1 deletion src/net/p2p.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5540,7 +5540,7 @@ mod test {
initial_neighbors,
)
.unwrap();
let atlas_config = AtlasConfig::default(false);
let atlas_config = AtlasConfig::new(false);
let atlasdb = AtlasDB::connect_memory(atlas_config).unwrap();

let local_peer = PeerDB::get_local_peer(db.conn()).unwrap();
Expand Down
10 changes: 10 additions & 0 deletions testnet/stacks-node/Stacks.toml
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,13 @@ amount = 100000000
# "STGT7GSMZG7EA0TS6MVSKT5JC1DCDFGZWJJZXN8A.contract.nft-token",
# "stx"
# ]

## Atlas database
## The Atlas database, which handles file attachments, can be configured here
## The values used in the example below are the minimum values which Atlas will accept
##
#[atlas]
#attachments_max_size = 1048576
#max_uninstantiated_attachments = 10000
#uninstantiated_attachments_expire_after = 3600
#unresolved_attachment_instances_expire_after = 172800
60 changes: 45 additions & 15 deletions testnet/stacks-node/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ use stacks::cost_estimates::metrics::ProportionalDotProduct;
use stacks::cost_estimates::CostEstimator;
use stacks::cost_estimates::FeeEstimator;
use stacks::cost_estimates::PessimisticEstimator;
use stacks::net::atlas::AtlasConfig;
use stacks::net::connection::ConnectionOptions;
use stacks::net::{Neighbor, NeighborKey, PeerAddress};
use stacks::util::get_epoch_time_ms;
Expand All @@ -55,6 +56,7 @@ pub struct ConfigFile {
pub connection_options: Option<ConnectionOptionsFile>,
pub fee_estimation: Option<FeeEstimationConfigFile>,
pub miner: Option<MinerConfigFile>,
pub atlas: Option<AtlasConfigFile>,
}

#[derive(Clone, Deserialize, Default)]
Expand Down Expand Up @@ -360,6 +362,7 @@ pub struct Config {
pub connection_options: ConnectionOptions,
pub miner: MinerConfig,
pub estimation: FeeEstimationConfig,
pub atlas: AtlasConfig,
}

lazy_static! {
Expand Down Expand Up @@ -1107,6 +1110,16 @@ impl Config {
None => FeeEstimationConfig::default(),
};

let mainnet = burnchain.mode == "mainnet";
let atlas = match config_file.atlas {
Some(f) => f.into_config(mainnet),
None => AtlasConfig::new(mainnet),
};

atlas
.validate()
.map_err(|e| format!("Atlas config error: {e}"))?;

Ok(Config {
node,
burnchain,
Expand All @@ -1115,6 +1128,7 @@ impl Config {
connection_options,
estimation,
miner,
atlas,
})
}

Expand Down Expand Up @@ -1266,6 +1280,7 @@ impl std::default::Default for Config {

let connection_options = HELIUM_DEFAULT_CONNECTION_OPTIONS.clone();
let estimation = FeeEstimationConfig::default();
let mainnet = burnchain.mode == "mainnet";

Config {
burnchain,
Expand All @@ -1275,6 +1290,7 @@ impl std::default::Default for Config {
connection_options,
estimation,
miner: MinerConfig::default(),
atlas: AtlasConfig::new(mainnet),
}
}
}
Expand Down Expand Up @@ -1940,7 +1956,7 @@ pub struct NodeConfigFile {
pub chain_liveness_poll_time_secs: Option<u64>,
}

#[derive(Clone, Deserialize, Debug)]
#[derive(Clone, Deserialize, Default, Debug)]
pub struct FeeEstimationConfigFile {
pub cost_estimator: Option<String>,
pub fee_estimator: Option<String>,
Expand All @@ -1951,20 +1967,6 @@ pub struct FeeEstimationConfigFile {
pub fee_rate_window_size: Option<u64>,
}

impl Default for FeeEstimationConfigFile {
fn default() -> Self {
Self {
cost_estimator: None,
fee_estimator: None,
cost_metric: None,
disabled: None,
log_error: None,
fee_rate_fuzzer_fraction: None,
fee_rate_window_size: None,
}
}
}

#[derive(Clone, Deserialize, Default, Debug)]
pub struct MinerConfigFile {
pub min_tx_fee: Option<u64>,
Expand All @@ -1978,6 +1980,34 @@ pub struct MinerConfigFile {
pub candidate_retry_cache_size: Option<u64>,
}

#[derive(Clone, Deserialize, Default, Debug)]
pub struct AtlasConfigFile {
pub attachments_max_size: Option<u32>,
pub max_uninstantiated_attachments: Option<u32>,
pub uninstantiated_attachments_expire_after: Option<u32>,
pub unresolved_attachment_instances_expire_after: Option<u32>,
}

impl AtlasConfigFile {
// Can't inplement `Into` trait because this takes a parameter
fn into_config(&self, mainnet: bool) -> AtlasConfig {
let mut conf = AtlasConfig::new(mainnet);
if let Some(val) = self.attachments_max_size {
Copy link
Member

@jcnelson jcnelson Mar 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The maximum attachment size cannot be smaller than 1_048_576. This is what the current maximum size is in the network, so we have to continue to provide at least that degree of service. Nodes can agree to store larger attachments if they want, however.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add a constant ATTACHMENTS_MAX_SIZE_MIN and default to that if the user sets a lower value. Should we use the current defaults to enforce minimum values for the other parameters in the [atlas] config as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, it's probably better to crash with an error message on invalid config rather than applying hidden defaults. I'll do that instead

conf.attachments_max_size = val
}
if let Some(val) = self.max_uninstantiated_attachments {
conf.max_uninstantiated_attachments = val
}
if let Some(val) = self.uninstantiated_attachments_expire_after {
conf.uninstantiated_attachments_expire_after = val
}
if let Some(val) = self.unresolved_attachment_instances_expire_after {
conf.unresolved_attachment_instances_expire_after = val
}
conf
}
}

#[derive(Clone, Deserialize, Default, Debug)]
pub struct EventObserverConfigFile {
pub endpoint: String,
Expand Down
4 changes: 2 additions & 2 deletions testnet/stacks-node/src/neon_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4100,7 +4100,7 @@ impl StacksNode {
let config = runloop.config().clone();
let is_miner = runloop.is_miner();
let burnchain = runloop.get_burnchain();
let atlas_config = AtlasConfig::default(config.is_mainnet());
let atlas_config = config.atlas.clone();
let keychain = Keychain::default(config.node.seed.clone());

// we can call _open_ here rather than _connect_, since connect is first called in
Expand All @@ -4110,7 +4110,7 @@ impl StacksNode {
true,
burnchain.pox_constants.clone(),
)
.expect("Error while instantiating sor/tition db");
.expect("Error while instantiating sortition db");

Self::setup_ast_size_precheck(&config, &mut sortdb);

Expand Down
2 changes: 1 addition & 1 deletion testnet/stacks-node/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,7 @@ impl Node {
}

fn make_atlas_config() -> AtlasConfig {
AtlasConfig::default(false)
AtlasConfig::new(false)
}

pub fn make_atlas_db(&self) -> AtlasDB {
Expand Down
4 changes: 2 additions & 2 deletions testnet/stacks-node/src/run_loop/neon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -504,7 +504,7 @@ impl RunLoop {
let use_test_genesis_data = use_test_genesis_chainstate(&self.config);

// load up genesis Atlas attachments
let mut atlas_config = AtlasConfig::default(self.config.is_mainnet());
let mut atlas_config = AtlasConfig::new(self.config.is_mainnet());
let genesis_attachments = GenesisData::new(use_test_genesis_data)
.read_name_zonefiles()
.into_iter()
Expand All @@ -515,7 +515,7 @@ impl RunLoop {
let chain_state_db = self.boot_chainstate(burnchain_config);

// NOTE: re-instantiate AtlasConfig so we don't have to keep the genesis attachments around
let moved_atlas_config = AtlasConfig::default(self.config.is_mainnet());
let moved_atlas_config = self.config.atlas.clone();
let moved_config = self.config.clone();
let moved_burnchain_config = burnchain_config.clone();
let mut coordinator_dispatcher = self.event_dispatcher.clone();
Expand Down
2 changes: 1 addition & 1 deletion testnet/stacks-node/src/tests/neon_integrations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7681,7 +7681,7 @@ fn atlas_stress_integration_test() {
let mut attachment_hashes = HashMap::new();
{
let atlasdb_path = conf_bootstrap_node.get_atlas_db_file_path();
let atlasdb = AtlasDB::connect(AtlasConfig::default(false), &atlasdb_path, false).unwrap();
let atlasdb = AtlasDB::connect(AtlasConfig::new(false), &atlasdb_path, false).unwrap();
for ibh in index_block_hashes.iter() {
let indexes = query_rows::<u64, _>(
&atlasdb.conn,
Expand Down