Skip to content
Open
Show file tree
Hide file tree
Changes from 5 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
6 changes: 3 additions & 3 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@ Chain specifications are located in `/res/` directory.
| AURA seed | `AURA_SEED_FILE=/path/to/seed` | - | Path to AURA consensus seed file |
| GRANDPA seed | `GRANDPA_SEED_FILE=/path/to/seed` | - | Path to GRANDPA finality seed file |
| Cross-chain seed | `CROSS_CHAIN_SEED_FILE=/path/to/seed` | - | Path to cross-chain seed file |
| BEEFY seed | `BEEFY_SEED_FILE=/path/to/seed` | - | Path to BEEFY seed file (ECDSA, like cross-chain) |
Comment thread
LGLO marked this conversation as resolved.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Add the BEEFY seed to the single-node command

The single-node example immediately below this new option still supplies only AURA, GRANDPA, and cross-chain seeds and does not use --alice; node/src/command.rs inserts a beef keystore entry only when BEEFY_SEED_FILE is set. Because the dev authority's registered BEEFY public key has no matching secret under the BEEFY key type, this advertised one-validator setup produces blocks but cannot produce BEEFY signatures. Fresh evidence is that the multi-node examples were updated with the new variable while the single-node command at line 308 remains unchanged.

AGENTS.md reference: AGENTS.md:L69-L69

Useful? React with 👍 / 👎.

| Chain spec | `CHAIN=dev` | `--chain dev` | Network to connect to |
| Base path | `BASE_PATH=/tmp/node-1` | `--base-path /tmp/node-1` | Data directory |
| Validator mode | `VALIDATOR=true` | `--validator` | Run as validator (true/1/TRUE) |
Expand Down
22 changes: 22 additions & 0 deletions changes/node/added/beefy-key-provisioning.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#node #keystore #beefy
# Provide BEEFY keys via seed file, and add them to the local-env validators

Added `BEEFY_SEED_FILE`, alongside the existing `AURA_SEED_FILE`,
`BABE_SEED_FILE`, `GRANDPA_SEED_FILE` and `CROSS_CHAIN_SEED_FILE`. The seed is
inserted into the keystore under the `beef` key type as ECDSA, like the
cross-chain key.

Validators need a BEEFY key in their keystore once beefy is part of
`SessionKeys`; the key is looked up by `(key type, public key)`, so a key
inserted only under another key type does not satisfy a BEEFY request even when
the bytes are identical.

The local-env validators (nodes 2 to 5) now carry one. The committee registers
each validator's cross-chain key as its beefy key — `beefy_pub_key` equals
`sidechain_pub_key` in `res/local/permissioned-candidates-config.json` — so the
existing cross-chain key material is reused rather than new keys generated:
keystore entries for the keystore-mounted nodes, and `seeds/beefy.seed` plus
`BEEFY_SEED_FILE` for the seed-file node.

PR: https://github.com/midnightntwrk/midnight-node/pull/1953
Issue: https://github.com/midnightntwrk/midnight-node/issues/1742
26 changes: 26 additions & 0 deletions changes/node/changed/beefy-genesis-authorities-from-session.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#node #genesis #beefy
# Let session seed the BEEFY genesis authorities

Building genesis panicked once beefy became a session key:

```
GenesisBuilder_build_state -> pallet_session::GenesisConfig::build
-> <(_, _, _) as SessionHandler>::on_genesis_session -> unwrap_failed
```

`pallet_beefy` (pallet index 21) initialized its authorities from the chain
spec's `BeefyConfig::authorities`, and then `pallet_session` (index 30)
initialized them a second time via the now three-element `SessionHandler`.
`pallet_beefy::initialize` returns `Err` when the authority list is already
populated, and `on_genesis_session` turns that into
`.expect("Authorities vec too big")` — a misleading message, since nothing is
too big.

The chain spec now leaves `beefy: Default::default()` (empty authorities,
`genesis_block: Some(1)`), matching `aura`, `babe` and `grandpa` beside it, and
lets `pallet_session` seed the BEEFY authorities from the committee's session
keys. Note this changes genesis storage, and therefore the genesis hash, so it
applies to chains created from this version onward.

PR: https://github.com/midnightntwrk/midnight-node/pull/1953
Issue: https://github.com/midnightntwrk/midnight-node/issues/1742
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#node #committee-selection #beefy
# Decode the committee by pallet storage version in the membership watcher

The committee-membership watcher called the `get_current_committee` runtime
API, which decodes committee members with the node binary's own `SessionKeys`.
Between updating the nodes and running the runtime upgrade, that meant decoding
pre-beefy (aura + grandpa) committee bytes as the three-key shape: the decode
falls short and yields the empty default, so the watcher logged every validator
as not being in a zero-size committee.

It now reads `CurrentCommittee` and `pallet-session-validator-management`'s
on-chain storage version straight from state and lets the runtime pick the
matching shape (`committee_keys_migrated` / `decode_current_committee`),
upgrading legacy-shaped members on the fly. Storage version `>= 2` means the
committee carries beefy keys; absent or lower means it does not.

PR: https://github.com/midnightntwrk/midnight-node/pull/1953
Issue: https://github.com/midnightntwrk/midnight-node/issues/1742
20 changes: 20 additions & 0 deletions changes/runtime/changed/add-beefy-session-key.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#runtime #session-keys #beefy

# Add the BEEFY session key

`opaque::SessionKeys` gains `beefy`, translated by the
`AddBeefyToSessionKeysMigration` cutover, which gives each validator its own
cross-chain key as its beefy key. Both are ECDSA and the committee registers
them as equal (`beefy_pub_key == sidechain_pub_key`), so the migrated
authority set is one the validators actually hold secrets for — a derived
placeholder would leave BEEFY unable to reach a quorum until the next
committee rotation.

Because BEEFY is now a session key, `pallet_session`'s genesis initializes
the BEEFY authorities from the committee, so the chain spec no longer sets
`BeefyConfig::authorities` itself — see the separate genesis change.

The runtime version moves to `002_001_001`.

PR: https://github.com/midnightntwrk/midnight-node/pull/1953
Issue: https://github.com/midnightntwrk/midnight-node/issues/1742
12 changes: 12 additions & 0 deletions changes/runtime/changed/combined-session-migration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#runtime #migration #committee-selection

# Combine committee v1-to-v2 and session-key migrations in the runtime

This chain moves `pallet-session-validator-management` from v1 to v2 in a single
upgrade, which spans two changes the toolkit ships as separate migrations: the
`AuthorityKeys` shape change (`migrations::authority_keys`) and v2's new
`QueuedCommittee` (`migrations::v2`). They cannot be wired one after the other,
so a custom migration that combines both is added.

PR: https://github.com/midnightntwrk/midnight-node/pull/1953
Issue: https://github.com/midnightntwrk/midnight-node/issues/1742
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"//Bob"
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0xf8d74108dbe199c4a6e4ef457046db37c325ba3f709b14cabfa1885663e4c589
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"volume wire damp response scene tape truck easily enjoy shaft face message"
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"victory horse scatter empty silly live royal differ friend nurse poverty benefit"
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,7 @@ services:
AURA_SEED_FILE: "/seeds/aura.seed"
CROSS_CHAIN_SEED_FILE: "/seeds/cross_chain.seed"
GRANDPA_SEED_FILE: "/seeds/grandpa.seed"
BEEFY_SEED_FILE: "/seeds/beefy.seed"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Seed BEEFY in generated compose overrides

This provisions the checked-in local environment, but the from-genesis and fork/mock paths generate validator overrides through genesisComposeOverride.ts and mockComposeOverride.ts, and both still create only AURA, GRANDPA, and cross-chain seed files/environment variables. Validators launched through those workflows therefore have no beef keystore entry for their advertised BEEFY key, so BEEFY finality cannot advance; both generators and their tests should set BEEFY_SEED_FILE, reusing the cross-chain seed where appropriate.

AGENTS.md reference: AGENTS.md:L69-L69

Useful? React with 👍 / 👎.

entrypoint: ["/bin/bash", "/entrypoint.sh"]
labels:
io.midnight.role: validator
Expand Down
2 changes: 1 addition & 1 deletion metadata/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "midnight-node-metadata"
version = "2.1.0"
version = "2.1.1"
Comment thread
LGLO marked this conversation as resolved.
Outdated
edition = "2024"
build = "build.rs"
license-file.workspace = true
Expand Down
Binary file modified metadata/static/midnight_metadata.scale
Binary file not shown.
Binary file added metadata/static/midnight_metadata_2.1.1.scale
Binary file not shown.
2 changes: 1 addition & 1 deletion node/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "midnight-node"
version = "2.1.0"
version = "2.1.1"
description = "Midnight blockchain node"
authors = ["Substrate DevHub <https://github.com/substrate-developer-hub>"]
homepage = "https://substrate.io/"
Expand Down
5 changes: 5 additions & 0 deletions node/src/cfg/midnight_cfg/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ pub struct MidnightCfg {
/// Docs: https://paritytech.github.io/polkadot-sdk/master/sp_core/crypto/struct.AddressUri.html#structfield.phrase
pub cross_chain_seed_file: Option<String>,

/// Path to file containing a secret string to use as the BEEFY seed (32 bytes)
/// Seed should be either a Phrase, hexadecimal string, or ss58-compatible string.
/// Docs: https://paritytech.github.io/polkadot-sdk/master/sp_core/crypto/struct.AddressUri.html#structfield.phrase
pub beefy_seed_file: Option<String>,

/// Mock ariadne parameters
pub use_main_chain_follower_mock: bool,
/// Required if use_main_chain_follower_mock is true
Expand Down
16 changes: 6 additions & 10 deletions node/src/chain_spec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use serde_valid::Validate as _;
use midnight_node_ledger_helpers::BlockContext;

use midnight_node_runtime::{
AccountId, BeefyConfig, Block, BridgeConfig, C2MBridgeConfig, CNightObservationCall,
AccountId, Block, BridgeConfig, C2MBridgeConfig, CNightObservationCall,
CNightObservationConfig, CouncilConfig, CouncilMembershipConfig, CrossChainPublic,
FederatedAuthorityObservationConfig, MidnightCall, MidnightConfig, MidnightSystemCall,
RuntimeCall, RuntimeGenesisConfig, SessionCommitteeManagementConfig, SidechainConfig,
Expand All @@ -31,12 +31,13 @@ use midnight_primitives_cnight_observation::ObservedUtxos;
use sc_chain_spec::{ChainSpecExtension, GenericChainSpec};
use sidechain_domain::{AssetName, MainchainAddress, McTxHash};
use sp_consensus_aura::sr25519::AuthorityId as AuraId;
use sp_consensus_beefy::ecdsa_crypto::AuthorityId as BeefyId;
use sp_consensus_grandpa::AuthorityId as GrandpaId;
use sp_core::{Encode, H256, Pair, Public};
use sp_partner_chains_bridge::{
MainChainScripts as BridgeMainChainScripts, SubminimalTransfersConfig,
};
use sp_runtime::traits::{IdentifyAccount, One, Verify};
use sp_runtime::traits::{IdentifyAccount, Verify};
use std::{fmt, str::FromStr};

/// Parse asset name from config - accepts either hex-encoded string or plain UTF-8 string.
Expand Down Expand Up @@ -112,6 +113,7 @@ pub fn authority_keys_from_seed(s: &str) -> AuthorityKeys {
session: SessionKeys {
aura: get_from_seed::<AuraId>(s),
grandpa: get_from_seed::<GrandpaId>(s),
beefy: get_from_seed::<BeefyId>(s),
},
cross_chain: get_from_seed::<CrossChainPublic>(s),
}
Expand Down Expand Up @@ -243,6 +245,7 @@ fn genesis_config<T: MidnightNetwork>(genesis: T) -> Result<serde_json::Value, C
session: SessionKeys {
aura: keys.aura_pubkey.into(),
grandpa: keys.grandpa_pubkey.into(),
beefy: keys.beefy_pubkey.into(),
},
cross_chain: keys.crosschain_pubkey.into(),
})
Expand All @@ -257,14 +260,7 @@ fn genesis_config<T: MidnightNetwork>(genesis: T) -> Result<serde_json::Value, C
system: Default::default(),
aura: Default::default(),
babe: Default::default(),
beefy: BeefyConfig {
authorities: genesis
.initial_authorities()
.iter()
.map(|v| v.beefy_pubkey.into())
.collect(),
genesis_block: Some(One::one()),
},
beefy: Default::default(),
grandpa: Default::default(),
midnight: MidnightConfig {
_config: Default::default(),
Expand Down
16 changes: 15 additions & 1 deletion node/src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ use sidechain_domain::mainchain_epoch::MainchainEpochConfig;
use sp_core::{
ByteArray, Pair,
crypto::key_types::{
AURA as AURA_KEY_TYPE, BABE as BABE_KEY_TYPE, GRANDPA as GRANDPA_KEY_TYPE,
AURA as AURA_KEY_TYPE, BABE as BABE_KEY_TYPE, BEEFY as BEEFY_KEY_TYPE,
GRANDPA as GRANDPA_KEY_TYPE,
},
offchain::KeyTypeId,
};
Expand Down Expand Up @@ -277,6 +278,19 @@ fn run_node(cfg: Cfg) -> sc_cli::Result<()> {
log::info!("CROSS_CHAIN pubkey: {}", &keypair.public())
}

if let Some(seed_file) = &cfg.midnight_cfg.beefy_seed_file {
let seed = std::fs::read_to_string(seed_file).map_err(|e| {
sc_cli::Error::Input(format!(
"error when reading BEEFY seed file at {seed_file}. Error: {e}"
))
})?;
let seed = seed.trim();
let (keypair, _) = sp_core::ecdsa::Pair::from_string_with_seed(seed, None)
.map_err(|e| sc_cli::Error::Input(format!("Invalid BEEFY seed: {e}")))?;
keystore.insert(BEEFY_KEY_TYPE, seed, &keypair.public().to_raw_vec()).unwrap();
log::info!("BEEFY pubkey: {}", &keypair.public())
}

// Hold the database backend handle outside the tokio runtime so we can
// explicitly drop it after all async tasks have finished. Without this,
// the backend's Arc may be leaked inside aborted tokio tasks during
Expand Down
54 changes: 37 additions & 17 deletions node/src/committee_membership.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,33 +20,33 @@
//! index, and emits a single INFO (in committee) or WARN (not in committee)
//! line per session.

use authority_selection_inherents::{AuthoritySelectionInputs, CommitteeMember};
use futures::StreamExt;
use midnight_node_runtime::{
CrossChainPublic,
opaque::{Block, SessionKeys},
committee_keys_migrated, current_committee_storage_key, decode_current_committee,
opaque::Block, session_committee_management_storage_version_key,
};
use midnight_primitives_session_info::SessionInfoApi;
use sc_client_api::BlockchainEvents;
use sidechain_domain::ScEpochNumber;
use sc_client_api::{Backend, BlockchainEvents, StorageProvider};
use sp_api::ProvideRuntimeApi;
use sp_consensus_aura::sr25519::AuthorityId as AuraId;
use sp_core::crypto::key_types::AURA as AURA_KEY_TYPE;
use sp_core::storage::StorageKey;
use sp_keystore::{Keystore, KeystorePtr};
use sp_session_validator_management::{CommitteeMember as _, SessionValidatorManagementApi};
use sp_session_validator_management::CommitteeMember as _;
use std::sync::Arc;

const LOG_TARGET: &str = "committee-membership";

pub async fn watch<C>(client: Arc<C>, keystore: KeystorePtr)
pub async fn watch<C, B>(client: Arc<C>, keystore: KeystorePtr)
where
C: ProvideRuntimeApi<Block> + BlockchainEvents<Block> + Send + Sync + 'static,
C::Api: SessionValidatorManagementApi<
Block,
CommitteeMember<CrossChainPublic, SessionKeys>,
AuthoritySelectionInputs,
ScEpochNumber,
> + SessionInfoApi<Block>,
C: ProvideRuntimeApi<Block>
+ BlockchainEvents<Block>
+ StorageProvider<Block, B>
+ Send
+ Sync
+ 'static,
B: Backend<Block>,
C::Api: SessionInfoApi<Block>,
{
let mut notifications = client.import_notification_stream();
let mut last_session: Option<u32> = None;
Expand All @@ -70,16 +70,36 @@ where
}
last_session = Some(session_index);

let committee = match client.runtime_api().get_current_committee(block_hash) {
Ok((_epoch, committee)) => committee,
// Read `CurrentCommittee` and the pallet's storage version straight from state, then let
// the runtime decode the committee in the shape that version implies. Going through the
// runtime API instead would decode with this binary's `SessionKeys`, which fails while the
// nodes are updated but the runtime upgrade that adds the beefy key has not run yet.
let version_bytes = match client
.storage(block_hash, &StorageKey(session_committee_management_storage_version_key()))
{
Ok(bytes) => bytes.unwrap_or_default(),
Err(err) => {
log::error!(
target: LOG_TARGET,
"Failed to query current committee at {block_hash:?}: {err}",
"Failed to read session-committee-management storage version at {block_hash:?}: {err}",
);
continue;
},
};
let committee_bytes =
match client.storage(block_hash, &StorageKey(current_committee_storage_key())) {
Ok(bytes) => bytes.unwrap_or_default(),
Err(err) => {
log::error!(
target: LOG_TARGET,
"Failed to read current committee at {block_hash:?}: {err}",
);
continue;
},
};

let migrated = committee_keys_migrated(version_bytes.0.as_slice());
let (_epoch, committee) = decode_current_committee(committee_bytes.0.as_slice(), migrated);

let local_aura_keys: Vec<AuraId> = keystore
.sr25519_public_keys(AURA_KEY_TYPE)
Expand Down
2 changes: 1 addition & 1 deletion runtime/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "midnight-node-runtime"
version = "2.1.0"
version = "2.1.1"
description = "A fresh FRAME-based Substrate node, ready for hacking."
authors = ["Substrate DevHub <https://github.com/substrate-developer-hub>"]
edition = "2024"
Expand Down
Loading
Loading