mirror of
https://github.com/alexgo-io/stacks-puppet-node.git
synced 2026-01-13 08:40:45 +08:00
Merge pull request #4560 from stacks-network/feat/add-override-affirmations-to-config
Add affirmation overrides config option and add default xenon ones ne…
This commit is contained in:
@@ -943,7 +943,8 @@ impl<'a> BurnchainDBTransaction<'a> {
|
||||
affirmation_map: AffirmationMap,
|
||||
) -> Result<(), DBError> {
|
||||
assert_eq!((affirmation_map.len() as u64) + 1, reward_cycle);
|
||||
let qry = "INSERT INTO overrides (reward_cycle, affirmation_map) VALUES (?1, ?2)";
|
||||
let qry =
|
||||
"INSERT OR REPLACE INTO overrides (reward_cycle, affirmation_map) VALUES (?1, ?2)";
|
||||
let args: &[&dyn ToSql] = &[&u64_to_sql(reward_cycle)?, &affirmation_map.encode()];
|
||||
|
||||
let mut stmt = self.sql_tx.prepare(qry)?;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::collections::HashSet;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::net::{Ipv4Addr, SocketAddr, ToSocketAddrs};
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
@@ -10,6 +10,8 @@ use clarity::vm::costs::ExecutionCost;
|
||||
use clarity::vm::types::{AssetIdentifier, PrincipalData, QualifiedContractIdentifier};
|
||||
use lazy_static::lazy_static;
|
||||
use rand::RngCore;
|
||||
use serde::Deserialize;
|
||||
use stacks::burnchains::affirmation::AffirmationMap;
|
||||
use stacks::burnchains::bitcoin::BitcoinNetworkType;
|
||||
use stacks::burnchains::{Burnchain, MagicBytes, BLOCKSTACK_MAGIC_MAINNET};
|
||||
use stacks::chainstate::nakamoto::signer_set::NakamotoSigners;
|
||||
@@ -198,6 +200,74 @@ mod tests {
|
||||
Some("password".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_load_affirmation_map() {
|
||||
let affirmation_string = "nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnppnnnnnnnnnnnnnnnnnnnnnnnnpppppnnnnnnnnnnnnnnnnnnnnnnnpppppppppppppppnnnnnnnnnnnnnnnnnnnnnnnppppppppppnnnnnnnnnnnnnnnnnnnppppnnnnnnnnnnnnnnnnnnnnnnnppppppppnnnnnnnnnnnnnnnnnnnnnnnppnppnnnnnnnnnnnnnnnnnnnnnnnppppnnnnnnnnnnnnnnnnnnnnnnnnnppppppnnnnnnnnnnnnnnnnnnnnnnnnnppnnnnnnnnnnnnnnnnnnnnnnnnnpppppppnnnnnnnnnnnnnnnnnnnnnnnnnnpnnnnnnnnnnnnnnnnnnnnnnnnnpppnppppppppppppppnnppppnpa";
|
||||
let affirmation =
|
||||
AffirmationMap::decode(affirmation_string).expect("Failed to decode affirmation map");
|
||||
let config = Config::from_config_file(
|
||||
ConfigFile::from_str(&format!(
|
||||
r#"
|
||||
[[burnchain.affirmation_overrides]]
|
||||
reward_cycle = 1
|
||||
affirmation = "{affirmation_string}"
|
||||
"#
|
||||
))
|
||||
.expect("Expected to be able to parse config file from string"),
|
||||
)
|
||||
.expect("Expected to be able to parse affirmation map from file");
|
||||
|
||||
assert_eq!(config.burnchain.affirmation_overrides.len(), 1);
|
||||
assert_eq!(config.burnchain.affirmation_overrides.get(&0), None);
|
||||
assert_eq!(
|
||||
config.burnchain.affirmation_overrides.get(&1),
|
||||
Some(&affirmation)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_fail_to_load_invalid_affirmation_map() {
|
||||
let bad_affirmation_string = "bad_map";
|
||||
let file = ConfigFile::from_str(&format!(
|
||||
r#"
|
||||
[[burnchain.affirmation_overrides]]
|
||||
reward_cycle = 1
|
||||
affirmation = "{bad_affirmation_string}"
|
||||
"#
|
||||
))
|
||||
.expect("Expected to be able to parse config file from string");
|
||||
|
||||
assert!(Config::from_config_file(file).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_load_empty_affirmation_map() {
|
||||
let config = Config::from_config_file(
|
||||
ConfigFile::from_str(r#""#)
|
||||
.expect("Expected to be able to parse config file from string"),
|
||||
)
|
||||
.expect("Expected to be able to parse affirmation map from file");
|
||||
|
||||
assert!(config.burnchain.affirmation_overrides.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_include_xenon_default_affirmation_overrides() {
|
||||
let config = Config::from_config_file(
|
||||
ConfigFile::from_str(
|
||||
r#"
|
||||
[burnchain]
|
||||
chain = "bitcoin"
|
||||
mode = "xenon"
|
||||
"#,
|
||||
)
|
||||
.expect("Expected to be able to parse config file from string"),
|
||||
)
|
||||
.expect("Expected to be able to parse affirmation map from file");
|
||||
// Should default add xenon affirmation overrides
|
||||
assert_eq!(config.burnchain.affirmation_overrides.len(), 3);
|
||||
}
|
||||
}
|
||||
|
||||
impl ConfigFile {
|
||||
@@ -223,15 +293,17 @@ impl ConfigFile {
|
||||
}
|
||||
|
||||
pub fn xenon() -> ConfigFile {
|
||||
let burnchain = BurnchainConfigFile {
|
||||
let mut burnchain = BurnchainConfigFile {
|
||||
mode: Some("xenon".to_string()),
|
||||
rpc_port: Some(18332),
|
||||
peer_port: Some(18333),
|
||||
peer_host: Some("bitcoind.xenon.blockstack.org".to_string()),
|
||||
peer_host: Some("bitcoind.testnet.stacks.co".to_string()),
|
||||
magic_bytes: Some("T2".into()),
|
||||
..BurnchainConfigFile::default()
|
||||
};
|
||||
|
||||
burnchain.add_affirmation_overrides_xenon();
|
||||
|
||||
let node = NodeConfigFile {
|
||||
bootstrap_node: Some("029266faff4c8e0ca4f934f34996a96af481df94a89b0c9bd515f3536a95682ddc@seed.testnet.hiro.so:30444".to_string()),
|
||||
miner: Some(false),
|
||||
@@ -1185,6 +1257,7 @@ pub struct BurnchainConfig {
|
||||
pub sunset_end: Option<u32>,
|
||||
pub wallet_name: String,
|
||||
pub ast_precheck_size_height: Option<u64>,
|
||||
pub affirmation_overrides: HashMap<u64, AffirmationMap>,
|
||||
}
|
||||
|
||||
impl BurnchainConfig {
|
||||
@@ -1220,6 +1293,7 @@ impl BurnchainConfig {
|
||||
sunset_end: None,
|
||||
wallet_name: "".to_string(),
|
||||
ast_precheck_size_height: None,
|
||||
affirmation_overrides: HashMap::new(),
|
||||
}
|
||||
}
|
||||
pub fn get_rpc_url(&self, wallet: Option<String>) -> String {
|
||||
@@ -1274,6 +1348,12 @@ pub const EPOCH_CONFIG_2_4_0: &'static str = "2.4";
|
||||
pub const EPOCH_CONFIG_2_5_0: &'static str = "2.5";
|
||||
pub const EPOCH_CONFIG_3_0_0: &'static str = "3.0";
|
||||
|
||||
#[derive(Clone, Deserialize, Default, Debug)]
|
||||
pub struct AffirmationOverride {
|
||||
pub reward_cycle: u64,
|
||||
pub affirmation: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Default, Debug)]
|
||||
pub struct BurnchainConfigFile {
|
||||
pub chain: Option<String>,
|
||||
@@ -1304,9 +1384,38 @@ pub struct BurnchainConfigFile {
|
||||
pub sunset_end: Option<u32>,
|
||||
pub wallet_name: Option<String>,
|
||||
pub ast_precheck_size_height: Option<u64>,
|
||||
pub affirmation_overrides: Option<Vec<AffirmationOverride>>,
|
||||
}
|
||||
|
||||
impl BurnchainConfigFile {
|
||||
/// Add affirmation overrides required to sync Xenon Testnet node.
|
||||
///
|
||||
/// The Xenon Testnet Stacks 2.4 activation height occurred before the finalized SIP-024 updates and release of the stacks-node versioned 2.4.0.0.0.
|
||||
/// This caused the Stacks Xenon testnet to undergo a deep reorg when 2.4.0.0.0 was finalized. This deep reorg meant that 3 reward cycles were
|
||||
/// invalidated, which requires overrides in the affirmation map to continue correct operation. Those overrides are required for cycles 413, 414, and 415.
|
||||
pub fn add_affirmation_overrides_xenon(&mut self) {
|
||||
let default_overrides = vec![
|
||||
AffirmationOverride {
|
||||
reward_cycle: 413,
|
||||
affirmation: "nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnppnnnnnnnnnnnnnnnnnnnnnnnnpppppnnnnnnnnnnnnnnnnnnnnnnnpppppppppppppppnnnnnnnnnnnnnnnnnnnnnnnppppppppppnnnnnnnnnnnnnnnnnnnppppnnnnnnnnnnnnnnnnnnnnnnnppppppppnnnnnnnnnnnnnnnnnnnnnnnppnppnnnnnnnnnnnnnnnnnnnnnnnppppnnnnnnnnnnnnnnnnnnnnnnnnnppppppnnnnnnnnnnnnnnnnnnnnnnnnnppnnnnnnnnnnnnnnnnnnnnnnnnnpppppppnnnnnnnnnnnnnnnnnnnnnnnnnnpnnnnnnnnnnnnnnnnnnnnnnnnnpppnppppppppppppppnnppppnpa".to_string()
|
||||
},
|
||||
AffirmationOverride {
|
||||
reward_cycle: 414,
|
||||
affirmation: "nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnppnnnnnnnnnnnnnnnnnnnnnnnnpppppnnnnnnnnnnnnnnnnnnnnnnnpppppppppppppppnnnnnnnnnnnnnnnnnnnnnnnppppppppppnnnnnnnnnnnnnnnnnnnppppnnnnnnnnnnnnnnnnnnnnnnnppppppppnnnnnnnnnnnnnnnnnnnnnnnppnppnnnnnnnnnnnnnnnnnnnnnnnppppnnnnnnnnnnnnnnnnnnnnnnnnnppppppnnnnnnnnnnnnnnnnnnnnnnnnnppnnnnnnnnnnnnnnnnnnnnnnnnnpppppppnnnnnnnnnnnnnnnnnnnnnnnnnnpnnnnnnnnnnnnnnnnnnnnnnnnnpppnppppppppppppppnnppppnpaa".to_string()
|
||||
},
|
||||
AffirmationOverride {
|
||||
reward_cycle: 415,
|
||||
affirmation: "nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnppnnnnnnnnnnnnnnnnnnnnnnnnpppppnnnnnnnnnnnnnnnnnnnnnnnpppppppppppppppnnnnnnnnnnnnnnnnnnnnnnnppppppppppnnnnnnnnnnnnnnnnnnnppppnnnnnnnnnnnnnnnnnnnnnnnppppppppnnnnnnnnnnnnnnnnnnnnnnnppnppnnnnnnnnnnnnnnnnnnnnnnnppppnnnnnnnnnnnnnnnnnnnnnnnnnppppppnnnnnnnnnnnnnnnnnnnnnnnnnppnnnnnnnnnnnnnnnnnnnnnnnnnpppppppnnnnnnnnnnnnnnnnnnnnnnnnnnpnnnnnnnnnnnnnnnnnnnnnnnnnpppnppppppppppppppnnppppnpaaa".to_string()
|
||||
}];
|
||||
if let Some(affirmation_overrides) = self.affirmation_overrides.as_mut() {
|
||||
for affirmation in default_overrides {
|
||||
affirmation_overrides.push(affirmation);
|
||||
}
|
||||
} else {
|
||||
self.affirmation_overrides = Some(default_overrides);
|
||||
};
|
||||
}
|
||||
|
||||
fn into_config_default(
|
||||
mut self,
|
||||
default_burnchain_config: BurnchainConfig,
|
||||
@@ -1315,6 +1424,7 @@ impl BurnchainConfigFile {
|
||||
if self.magic_bytes.is_none() {
|
||||
self.magic_bytes = ConfigFile::xenon().burnchain.unwrap().magic_bytes;
|
||||
}
|
||||
self.add_affirmation_overrides_xenon();
|
||||
}
|
||||
|
||||
let mode = self.mode.unwrap_or(default_burnchain_config.mode);
|
||||
@@ -1333,6 +1443,19 @@ impl BurnchainConfigFile {
|
||||
}
|
||||
}
|
||||
|
||||
let mut affirmation_overrides = HashMap::new();
|
||||
if let Some(aos) = self.affirmation_overrides {
|
||||
for ao in aos {
|
||||
let Some(affirmation_map) = AffirmationMap::decode(&ao.affirmation) else {
|
||||
return Err(format!(
|
||||
"Invalid affirmation override for reward cycle {}: {}",
|
||||
ao.reward_cycle, ao.affirmation
|
||||
));
|
||||
};
|
||||
affirmation_overrides.insert(ao.reward_cycle, affirmation_map);
|
||||
}
|
||||
}
|
||||
|
||||
let mut config = BurnchainConfig {
|
||||
chain: self.chain.unwrap_or(default_burnchain_config.chain),
|
||||
chain_id: if is_mainnet {
|
||||
@@ -1422,6 +1545,7 @@ impl BurnchainConfigFile {
|
||||
pox_prepare_length: self
|
||||
.pox_prepare_length
|
||||
.or(default_burnchain_config.pox_prepare_length),
|
||||
affirmation_overrides,
|
||||
};
|
||||
|
||||
if let BitcoinNetworkType::Mainnet = config.get_bitcoin_network().1 {
|
||||
|
||||
@@ -1083,9 +1083,19 @@ impl RunLoop {
|
||||
let liveness_thread = self.spawn_chain_liveness_thread(globals.clone());
|
||||
|
||||
// Wait for all pending sortitions to process
|
||||
let burnchain_db = burnchain_config
|
||||
.open_burnchain_db(false)
|
||||
let mut burnchain_db = burnchain_config
|
||||
.open_burnchain_db(true)
|
||||
.expect("FATAL: failed to open burnchain DB");
|
||||
if !self.config.burnchain.affirmation_overrides.is_empty() {
|
||||
let tx = burnchain_db
|
||||
.tx_begin()
|
||||
.expect("FATAL: failed to begin burnchain DB tx");
|
||||
for (reward_cycle, affirmation) in self.config.burnchain.affirmation_overrides.iter() {
|
||||
tx.set_override_affirmation_map(*reward_cycle, affirmation.clone()).expect(&format!("FATAL: failed to set affirmation override ({affirmation}) for reward cycle {reward_cycle}"));
|
||||
}
|
||||
tx.commit()
|
||||
.expect("FATAL: failed to commit burnchain DB tx");
|
||||
}
|
||||
let burnchain_db_tip = burnchain_db
|
||||
.get_canonical_chain_tip()
|
||||
.expect("FATAL: failed to query burnchain DB");
|
||||
|
||||
Reference in New Issue
Block a user