mirror of
https://github.com/datahaven-xyz/datahaven
synced 2026-05-24 09:50:01 +00:00
fix: 🛡️ Check origin for validator set messages (#343)
### Context The function `v2_sendMessage()` on Snowbridge Gateway contract is **permissionless** (I'm shocked this is the design choice). Any EOA/contract on Ethereum can build a message and send it through our DH bridge. While we don't change our Snowbridge fork, then this will continue to be the case. ### Problem We use `v2_sendMessage()` to send **permissioned** operations to our chain. For instance: update our validator set message (coming next, _slashing-related_ messages). So we do need to restrict the processing of the incoming messages on the Substrate side. ### Fix - I've added a check to `EigenLayerMessageProcessor` that enforces `message.origin` to be only a configured `AuthorisedOrigin`. - I've added an `AuthorisedOrigin` to `pallet_external_validators::Config` - I've configured the `AuthorisedOrigin` to be `DatahavenServiceManagerAddress` in all three runtimes ### Stages - [x] Implementation - [x] Runtime integration tests - [x] Collect `DatahavenServiceManagerAddress` parameter for e2e tests to work Fixes https://github.com/datahaven-xyz/sr-datahaven/issues/12 --------- Co-authored-by: Steve Degosserie <723552+stiiifff@users.noreply.github.com>
This commit is contained in:
parent
ec475833e7
commit
733218ac79
16 changed files with 356 additions and 33 deletions
|
|
@ -24,13 +24,13 @@
|
|||
"allocationConfigurationDelay": 75,
|
||||
"beaconChainGenesisTimestamp": 1695902400
|
||||
},
|
||||
"avs": {
|
||||
"avsOwner": "0x976EA74026E726554dB657fA54763abd0C3a0aa9",
|
||||
"rewardsInitiator": "0x14dC79964da2C08b23698B3D3cc7Ca32193d9955",
|
||||
"vetoCommitteeMember": "0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f",
|
||||
"vetoWindowBlocks": 100,
|
||||
"validatorsStrategies": []
|
||||
},
|
||||
"avs": {
|
||||
"avsOwner": "0x976EA74026E726554dB657fA54763abd0C3a0aa9",
|
||||
"rewardsInitiator": "0x14dC79964da2C08b23698B3D3cc7Ca32193d9955",
|
||||
"vetoCommitteeMember": "0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f",
|
||||
"vetoWindowBlocks": 100,
|
||||
"validatorsStrategies": []
|
||||
},
|
||||
"snowbridge": {
|
||||
"randaoCommitDelay": 4,
|
||||
"randaoCommitExpiration": 24,
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@ pub mod pallet {
|
|||
BoundedVec, DefaultNoBound,
|
||||
},
|
||||
frame_system::pallet_prelude::*,
|
||||
sp_core::H160,
|
||||
sp_runtime::{traits::Convert, SaturatedConversion},
|
||||
sp_std::vec::Vec,
|
||||
};
|
||||
|
|
@ -163,6 +164,10 @@ pub mod pallet {
|
|||
type OnEraStart: OnEraStart;
|
||||
type OnEraEnd: OnEraEnd;
|
||||
|
||||
/// Authorized Ethereum origin for validator-set update messages coming via Snowbridge.
|
||||
#[pallet::constant]
|
||||
type AuthorizedOrigin: Get<H160>;
|
||||
|
||||
/// The weight information of this pallet.
|
||||
type WeightInfo: WeightInfo;
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ use {
|
|||
},
|
||||
frame_system::{self as system, EnsureSignedBy},
|
||||
pallet_balances::AccountData,
|
||||
sp_core::H256,
|
||||
sp_core::{H160, H256},
|
||||
sp_runtime::{
|
||||
testing::UintAuthorityId,
|
||||
traits::{BlakeTwo256, ConvertInto, IdentityLookup, OpaqueKeys},
|
||||
|
|
@ -51,6 +51,8 @@ frame_support::construct_runtime!(
|
|||
parameter_types! {
|
||||
pub const BlockHashCount: u64 = 250;
|
||||
pub const SS58Prefix: u8 = 42;
|
||||
// Dummy authorized origin used only for tests.
|
||||
pub static MockAuthorizedOrigin: H160 = H160::repeat_byte(0x0);
|
||||
}
|
||||
|
||||
impl system::Config for Test {
|
||||
|
|
@ -143,6 +145,7 @@ impl Config for Test {
|
|||
type SessionsPerEra = SessionsPerEra;
|
||||
type OnEraStart = Mock;
|
||||
type OnEraEnd = Mock;
|
||||
type AuthorizedOrigin = MockAuthorizedOrigin;
|
||||
type WeightInfo = ();
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
type Currency = Balances;
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ where
|
|||
};
|
||||
let decode_result = Self::decode_message(payload.as_slice());
|
||||
if let Ok(payload) = decode_result {
|
||||
payload.message_id == EL_MESSAGE_ID
|
||||
payload.message_id == EL_MESSAGE_ID && message.origin == T::AuthorizedOrigin::get()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
|
@ -100,6 +100,11 @@ where
|
|||
_who: AccountId,
|
||||
snow_msg: SnowbridgeMessage,
|
||||
) -> Result<[u8; 32], DispatchError> {
|
||||
// Defensively re-check the Ethereum origin before mutating the validator set.
|
||||
if snow_msg.origin != T::AuthorizedOrigin::get() {
|
||||
return Err(DispatchError::Other("unauthorized validator-set origin"));
|
||||
}
|
||||
|
||||
// Extract and decode the raw payload that came from Ethereum
|
||||
let payload = match &snow_msg.xcm {
|
||||
snowbridge_inbound_queue_primitives::v2::Payload::Raw(payload) => payload,
|
||||
|
|
|
|||
|
|
@ -1416,6 +1416,8 @@ impl pallet_external_validators::Config for Runtime {
|
|||
type SessionsPerEra = SessionsPerEra;
|
||||
type OnEraStart = (ExternalValidatorsSlashes, ExternalValidatorsRewards);
|
||||
type OnEraEnd = ExternalValidatorsRewards;
|
||||
type AuthorizedOrigin =
|
||||
runtime_params::dynamic_params::runtime_config::DatahavenServiceManagerAddress;
|
||||
type WeightInfo = mainnet_weights::pallet_external_validators::WeightInfo<Runtime>;
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
type Currency = Balances;
|
||||
|
|
@ -1636,7 +1638,9 @@ impl pallet_external_validator_slashes::SendMessage<AccountId> for SlashesSendAd
|
|||
let calldata = Vec::new();
|
||||
|
||||
let command = Command::CallContract {
|
||||
target: runtime_params::dynamic_params::runtime_config::DatahavenAVSAddress::get(),
|
||||
target:
|
||||
runtime_params::dynamic_params::runtime_config::DatahavenServiceManagerAddress::get(
|
||||
),
|
||||
calldata,
|
||||
gas: 1_000_000, // TODO: Determine appropriate gas value after testing
|
||||
value: 0,
|
||||
|
|
@ -1690,6 +1694,15 @@ parameter_types! {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use dhp_bridge::{
|
||||
InboundCommand, Message as BridgeMessage, Payload as BridgePayload, EL_MESSAGE_ID,
|
||||
};
|
||||
use frame_support::assert_ok;
|
||||
use snowbridge_inbound_queue_primitives::v2::{
|
||||
EthereumAsset, Message as SnowbridgeMessage, MessageProcessor, Payload as SnowPayload,
|
||||
};
|
||||
use sp_core::H160;
|
||||
use sp_io::TestExternalities;
|
||||
use xcm_builder::GlobalConsensusConvertsFor;
|
||||
use xcm_executor::traits::ConvertLocation;
|
||||
|
||||
|
|
@ -1712,7 +1725,6 @@ mod tests {
|
|||
#[test]
|
||||
fn test_rewards_send_adapter_with_zero_address() {
|
||||
use pallet_external_validators_rewards::types::{EraRewardsUtils, SendMessage};
|
||||
use sp_io::TestExternalities;
|
||||
|
||||
TestExternalities::default().execute_with(|| {
|
||||
// Create test rewards utils
|
||||
|
|
@ -1735,9 +1747,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_rewards_send_adapter_with_valid_address() {
|
||||
use frame_support::assert_ok;
|
||||
use pallet_external_validators_rewards::types::{EraRewardsUtils, SendMessage};
|
||||
use sp_io::TestExternalities;
|
||||
|
||||
TestExternalities::default().execute_with(|| {
|
||||
// Set a valid (non-zero) rewards registry address
|
||||
|
|
@ -1783,4 +1793,86 @@ mod tests {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn build_snowbridge_message(origin: H160) -> SnowbridgeMessage {
|
||||
// Minimal valid EigenLayer payload carrying an empty validator set
|
||||
let bridge_payload = BridgePayload::<Runtime> {
|
||||
message_id: EL_MESSAGE_ID,
|
||||
message: BridgeMessage::V1(InboundCommand::ReceiveValidators {
|
||||
validators: Vec::new(),
|
||||
external_index: 0,
|
||||
}),
|
||||
};
|
||||
|
||||
let payload_bytes = bridge_payload.encode();
|
||||
|
||||
SnowbridgeMessage {
|
||||
gateway: H160::zero(),
|
||||
nonce: 0,
|
||||
origin,
|
||||
assets: Vec::<EthereumAsset>::new(),
|
||||
xcm: SnowPayload::Raw(payload_bytes),
|
||||
claimer: None,
|
||||
value: 0,
|
||||
execution_fee: 0,
|
||||
relayer_fee: 0,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_eigenlayer_message_processor_rejects_wrong_origin() {
|
||||
use sp_runtime::DispatchError;
|
||||
|
||||
TestExternalities::default().execute_with(|| {
|
||||
// Configure an authorized origin address in runtime parameters
|
||||
let authorized_origin = H160::from_low_u64_be(0x1234);
|
||||
assert_ok!(pallet_parameters::Pallet::<Runtime>::set_parameter(
|
||||
RuntimeOrigin::root(),
|
||||
RuntimeParameters::RuntimeConfig(
|
||||
runtime_params::dynamic_params::runtime_config::Parameters::DatahavenServiceManagerAddress(
|
||||
runtime_params::dynamic_params::runtime_config::DatahavenServiceManagerAddress,
|
||||
Some(authorized_origin),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
// Build a message with a different (unauthorized) origin
|
||||
let wrong_origin = H160::from_low_u64_be(0x9999);
|
||||
let snow_msg = build_snowbridge_message(wrong_origin);
|
||||
|
||||
let relayer: AccountId = Default::default();
|
||||
let result =
|
||||
dhp_bridge::EigenLayerMessageProcessor::<Runtime>::process_message(relayer, snow_msg);
|
||||
|
||||
assert!(matches!(
|
||||
result,
|
||||
Err(DispatchError::Other("unauthorized validator-set origin"))
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_eigenlayer_message_processor_accepts_authorized_origin() {
|
||||
TestExternalities::default().execute_with(|| {
|
||||
// Configure the authorized origin to match the ServiceManager address
|
||||
let authorized_origin = H160::from_low_u64_be(0x1234);
|
||||
assert_ok!(pallet_parameters::Pallet::<Runtime>::set_parameter(
|
||||
RuntimeOrigin::root(),
|
||||
RuntimeParameters::RuntimeConfig(
|
||||
runtime_params::dynamic_params::runtime_config::Parameters::DatahavenServiceManagerAddress(
|
||||
runtime_params::dynamic_params::runtime_config::DatahavenServiceManagerAddress,
|
||||
Some(authorized_origin),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
let snow_msg = build_snowbridge_message(authorized_origin);
|
||||
let relayer: AccountId = Default::default();
|
||||
|
||||
let result =
|
||||
dhp_bridge::EigenLayerMessageProcessor::<Runtime>::process_message(relayer, snow_msg);
|
||||
|
||||
assert!(result.is_ok(), "Message from authorized origin should be accepted");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -337,8 +337,9 @@ pub mod dynamic_params {
|
|||
|
||||
#[codec(index = 36)]
|
||||
#[allow(non_upper_case_globals)]
|
||||
/// The AVS ethereum address for Datahaven. Via this address we relay slashing requests or other requests.
|
||||
pub static DatahavenAVSAddress: H160 = H160::repeat_byte(0x0);
|
||||
/// The Ethereum address of the DataHavenServiceManager contract.
|
||||
/// This address is used both for authorized slashing requests and validator-set update messages.
|
||||
pub static DatahavenServiceManagerAddress: H160 = H160::repeat_byte(0x0);
|
||||
|
||||
// ╔══════════════════════ Validator Rewards Inflation ═══════════════════════╗
|
||||
|
||||
|
|
|
|||
|
|
@ -1412,6 +1412,8 @@ impl pallet_external_validators::Config for Runtime {
|
|||
type SessionsPerEra = SessionsPerEra;
|
||||
type OnEraStart = (ExternalValidatorsSlashes, ExternalValidatorsRewards);
|
||||
type OnEraEnd = ExternalValidatorsRewards;
|
||||
type AuthorizedOrigin =
|
||||
runtime_params::dynamic_params::runtime_config::DatahavenServiceManagerAddress;
|
||||
type WeightInfo = stagenet_weights::pallet_external_validators::WeightInfo<Runtime>;
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
type Currency = Balances;
|
||||
|
|
@ -1632,7 +1634,9 @@ impl pallet_external_validator_slashes::SendMessage<AccountId> for SlashesSendAd
|
|||
let calldata = Vec::new();
|
||||
|
||||
let command = Command::CallContract {
|
||||
target: runtime_params::dynamic_params::runtime_config::DatahavenAVSAddress::get(),
|
||||
target:
|
||||
runtime_params::dynamic_params::runtime_config::DatahavenServiceManagerAddress::get(
|
||||
),
|
||||
calldata,
|
||||
gas: 1_000_000, // TODO: Determine appropriate gas value after testing
|
||||
value: 0,
|
||||
|
|
@ -1686,6 +1690,15 @@ parameter_types! {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use dhp_bridge::{
|
||||
InboundCommand, Message as BridgeMessage, Payload as BridgePayload, EL_MESSAGE_ID,
|
||||
};
|
||||
use frame_support::assert_ok;
|
||||
use snowbridge_inbound_queue_primitives::v2::{
|
||||
EthereumAsset, Message as SnowbridgeMessage, MessageProcessor, Payload as SnowPayload,
|
||||
};
|
||||
use sp_core::H160;
|
||||
use sp_io::TestExternalities;
|
||||
use xcm_builder::GlobalConsensusConvertsFor;
|
||||
use xcm_executor::traits::ConvertLocation;
|
||||
|
||||
|
|
@ -1707,9 +1720,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_rewards_send_adapter_with_zero_address() {
|
||||
use frame_support::assert_ok;
|
||||
use pallet_external_validators_rewards::types::{EraRewardsUtils, SendMessage};
|
||||
use sp_io::TestExternalities;
|
||||
|
||||
TestExternalities::default().execute_with(|| {
|
||||
// First, set RewardsRegistryAddress to zero
|
||||
|
|
@ -1742,9 +1753,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_rewards_send_adapter_with_valid_address() {
|
||||
use frame_support::assert_ok;
|
||||
use pallet_external_validators_rewards::types::{EraRewardsUtils, SendMessage};
|
||||
use sp_io::TestExternalities;
|
||||
|
||||
TestExternalities::default().execute_with(|| {
|
||||
// Set a valid (non-zero) rewards registry address
|
||||
|
|
@ -1790,4 +1799,86 @@ mod tests {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn build_snowbridge_message(origin: H160) -> SnowbridgeMessage {
|
||||
// Minimal valid EigenLayer payload carrying an empty validator set
|
||||
let bridge_payload = BridgePayload::<Runtime> {
|
||||
message_id: EL_MESSAGE_ID,
|
||||
message: BridgeMessage::V1(InboundCommand::ReceiveValidators {
|
||||
validators: Vec::new(),
|
||||
external_index: 0,
|
||||
}),
|
||||
};
|
||||
|
||||
let payload_bytes = bridge_payload.encode();
|
||||
|
||||
SnowbridgeMessage {
|
||||
gateway: H160::zero(),
|
||||
nonce: 0,
|
||||
origin,
|
||||
assets: Vec::<EthereumAsset>::new(),
|
||||
xcm: SnowPayload::Raw(payload_bytes),
|
||||
claimer: None,
|
||||
value: 0,
|
||||
execution_fee: 0,
|
||||
relayer_fee: 0,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_eigenlayer_message_processor_rejects_wrong_origin() {
|
||||
use sp_runtime::DispatchError;
|
||||
|
||||
TestExternalities::default().execute_with(|| {
|
||||
// Configure an authorized origin address in runtime parameters
|
||||
let authorized_origin = H160::from_low_u64_be(0x1234);
|
||||
assert_ok!(pallet_parameters::Pallet::<Runtime>::set_parameter(
|
||||
RuntimeOrigin::root(),
|
||||
RuntimeParameters::RuntimeConfig(
|
||||
runtime_params::dynamic_params::runtime_config::Parameters::DatahavenServiceManagerAddress(
|
||||
runtime_params::dynamic_params::runtime_config::DatahavenServiceManagerAddress,
|
||||
Some(authorized_origin),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
// Build a message with a different (unauthorized) origin
|
||||
let wrong_origin = H160::from_low_u64_be(0x9999);
|
||||
let snow_msg = build_snowbridge_message(wrong_origin);
|
||||
|
||||
let relayer: AccountId = Default::default();
|
||||
let result =
|
||||
dhp_bridge::EigenLayerMessageProcessor::<Runtime>::process_message(relayer, snow_msg);
|
||||
|
||||
assert!(matches!(
|
||||
result,
|
||||
Err(DispatchError::Other("unauthorized validator-set origin"))
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_eigenlayer_message_processor_accepts_authorized_origin() {
|
||||
TestExternalities::default().execute_with(|| {
|
||||
// Configure the authorized origin to match the ServiceManager address
|
||||
let authorized_origin = H160::from_low_u64_be(0x1234);
|
||||
assert_ok!(pallet_parameters::Pallet::<Runtime>::set_parameter(
|
||||
RuntimeOrigin::root(),
|
||||
RuntimeParameters::RuntimeConfig(
|
||||
runtime_params::dynamic_params::runtime_config::Parameters::DatahavenServiceManagerAddress(
|
||||
runtime_params::dynamic_params::runtime_config::DatahavenServiceManagerAddress,
|
||||
Some(authorized_origin),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
let snow_msg = build_snowbridge_message(authorized_origin);
|
||||
let relayer: AccountId = Default::default();
|
||||
|
||||
let result =
|
||||
dhp_bridge::EigenLayerMessageProcessor::<Runtime>::process_message(relayer, snow_msg);
|
||||
|
||||
assert!(result.is_ok(), "Message from authorized origin should be accepted");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -342,8 +342,9 @@ pub mod dynamic_params {
|
|||
|
||||
#[codec(index = 36)]
|
||||
#[allow(non_upper_case_globals)]
|
||||
/// The AVS ethereum address for Datahaven. Via this address we relay slashing requests or other requests.
|
||||
pub static DatahavenAVSAddress: H160 = H160::repeat_byte(0x0);
|
||||
/// The Ethereum address of the DataHavenServiceManager contract.
|
||||
/// This address is used both for authorized slashing requests and validator-set update messages.
|
||||
pub static DatahavenServiceManagerAddress: H160 = H160::repeat_byte(0x0);
|
||||
|
||||
// ╔══════════════════════ Validator Rewards Inflation ═══════════════════════╗
|
||||
|
||||
|
|
|
|||
|
|
@ -1416,6 +1416,8 @@ impl pallet_external_validators::Config for Runtime {
|
|||
type SessionsPerEra = SessionsPerEra;
|
||||
type OnEraStart = (ExternalValidatorsSlashes, ExternalValidatorsRewards);
|
||||
type OnEraEnd = ExternalValidatorsRewards;
|
||||
type AuthorizedOrigin =
|
||||
runtime_params::dynamic_params::runtime_config::DatahavenServiceManagerAddress;
|
||||
type WeightInfo = testnet_weights::pallet_external_validators::WeightInfo<Runtime>;
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
type Currency = Balances;
|
||||
|
|
@ -1636,7 +1638,9 @@ impl pallet_external_validator_slashes::SendMessage<AccountId> for SlashesSendAd
|
|||
let calldata = Vec::new();
|
||||
|
||||
let command = Command::CallContract {
|
||||
target: runtime_params::dynamic_params::runtime_config::DatahavenAVSAddress::get(),
|
||||
target:
|
||||
runtime_params::dynamic_params::runtime_config::DatahavenServiceManagerAddress::get(
|
||||
),
|
||||
calldata,
|
||||
gas: 1_000_000, // TODO: Determine appropriate gas value after testing
|
||||
value: 0,
|
||||
|
|
@ -1690,6 +1694,15 @@ parameter_types! {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use dhp_bridge::{
|
||||
InboundCommand, Message as BridgeMessage, Payload as BridgePayload, EL_MESSAGE_ID,
|
||||
};
|
||||
use frame_support::assert_ok;
|
||||
use snowbridge_inbound_queue_primitives::v2::{
|
||||
EthereumAsset, Message as SnowbridgeMessage, MessageProcessor, Payload as SnowPayload,
|
||||
};
|
||||
use sp_core::H160;
|
||||
use sp_io::TestExternalities;
|
||||
use xcm_builder::GlobalConsensusConvertsFor;
|
||||
use xcm_executor::traits::ConvertLocation;
|
||||
|
||||
|
|
@ -1753,9 +1766,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_rewards_send_adapter_with_valid_address() {
|
||||
use frame_support::assert_ok;
|
||||
use pallet_external_validators_rewards::types::{EraRewardsUtils, SendMessage};
|
||||
use sp_io::TestExternalities;
|
||||
|
||||
TestExternalities::default().execute_with(|| {
|
||||
// Set a valid (non-zero) rewards registry address
|
||||
|
|
@ -1801,4 +1812,86 @@ mod tests {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn build_snowbridge_message(origin: H160) -> SnowbridgeMessage {
|
||||
// Minimal valid EigenLayer payload carrying an empty validator set
|
||||
let bridge_payload = BridgePayload::<Runtime> {
|
||||
message_id: EL_MESSAGE_ID,
|
||||
message: BridgeMessage::V1(InboundCommand::ReceiveValidators {
|
||||
validators: Vec::new(),
|
||||
external_index: 0,
|
||||
}),
|
||||
};
|
||||
|
||||
let payload_bytes = bridge_payload.encode();
|
||||
|
||||
SnowbridgeMessage {
|
||||
gateway: H160::zero(),
|
||||
nonce: 0,
|
||||
origin,
|
||||
assets: Vec::<EthereumAsset>::new(),
|
||||
xcm: SnowPayload::Raw(payload_bytes),
|
||||
claimer: None,
|
||||
value: 0,
|
||||
execution_fee: 0,
|
||||
relayer_fee: 0,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_eigenlayer_message_processor_rejects_wrong_origin() {
|
||||
use sp_runtime::DispatchError;
|
||||
|
||||
TestExternalities::default().execute_with(|| {
|
||||
// Configure an authorized origin address in runtime parameters
|
||||
let authorized_origin = H160::from_low_u64_be(0x1234);
|
||||
assert_ok!(pallet_parameters::Pallet::<Runtime>::set_parameter(
|
||||
RuntimeOrigin::root(),
|
||||
RuntimeParameters::RuntimeConfig(
|
||||
runtime_params::dynamic_params::runtime_config::Parameters::DatahavenServiceManagerAddress(
|
||||
runtime_params::dynamic_params::runtime_config::DatahavenServiceManagerAddress,
|
||||
Some(authorized_origin),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
// Build a message with a different (unauthorized) origin
|
||||
let wrong_origin = H160::from_low_u64_be(0x9999);
|
||||
let snow_msg = build_snowbridge_message(wrong_origin);
|
||||
|
||||
let relayer: AccountId = Default::default();
|
||||
let result =
|
||||
dhp_bridge::EigenLayerMessageProcessor::<Runtime>::process_message(relayer, snow_msg);
|
||||
|
||||
assert!(matches!(
|
||||
result,
|
||||
Err(DispatchError::Other("unauthorized validator-set origin"))
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_eigenlayer_message_processor_accepts_authorized_origin() {
|
||||
TestExternalities::default().execute_with(|| {
|
||||
// Configure the authorized origin to match the ServiceManager address
|
||||
let authorized_origin = H160::from_low_u64_be(0x1234);
|
||||
assert_ok!(pallet_parameters::Pallet::<Runtime>::set_parameter(
|
||||
RuntimeOrigin::root(),
|
||||
RuntimeParameters::RuntimeConfig(
|
||||
runtime_params::dynamic_params::runtime_config::Parameters::DatahavenServiceManagerAddress(
|
||||
runtime_params::dynamic_params::runtime_config::DatahavenServiceManagerAddress,
|
||||
Some(authorized_origin),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
let snow_msg = build_snowbridge_message(authorized_origin);
|
||||
let relayer: AccountId = Default::default();
|
||||
|
||||
let result =
|
||||
dhp_bridge::EigenLayerMessageProcessor::<Runtime>::process_message(relayer, snow_msg);
|
||||
|
||||
assert!(result.is_ok(), "Message from authorized origin should be accepted");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -338,8 +338,9 @@ pub mod dynamic_params {
|
|||
|
||||
#[codec(index = 36)]
|
||||
#[allow(non_upper_case_globals)]
|
||||
/// The AVS ethereum address for Datahaven. Via this address we relay slashing requests or other requests.
|
||||
pub static DatahavenAVSAddress: H160 = H160::repeat_byte(0x0);
|
||||
/// The Ethereum address of the DataHavenServiceManager contract.
|
||||
/// This address is used both for authorized slashing requests and validator-set update messages.
|
||||
pub static DatahavenServiceManagerAddress: H160 = H160::repeat_byte(0x0);
|
||||
|
||||
// ╔══════════════════════ Validator Rewards Inflation ═══════════════════════╗
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"version": "0.1.0-autogenerated.16922587894837334890",
|
||||
"version": "0.1.0-autogenerated.1683923751375582060",
|
||||
"name": "@polkadot-api/descriptors",
|
||||
"files": [
|
||||
"dist"
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -14,5 +14,9 @@
|
|||
{
|
||||
"name": "RewardsAgentOrigin",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"name": "DatahavenServiceManagerAddress",
|
||||
"value": null
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -127,6 +127,7 @@ export const updateParameters = async (
|
|||
const rewardsRegistryAddress = deployments.RewardsRegistry;
|
||||
const rewardsAgentOrigin = rewardsInfo.RewardsAgentOrigin;
|
||||
const updateRewardsMerkleRootSelector = rewardsInfo.updateRewardsMerkleRootSelector;
|
||||
const serviceManagerAddress = deployments.ServiceManager;
|
||||
|
||||
if (gatewayAddress) {
|
||||
logger.debug(`📝 Adding EthereumGatewayAddress parameter: ${gatewayAddress}`);
|
||||
|
|
@ -168,6 +169,16 @@ export const updateParameters = async (
|
|||
} else {
|
||||
logger.warn("⚠️ RewardsAgentOrigin not found in deployments file");
|
||||
}
|
||||
|
||||
if (serviceManagerAddress) {
|
||||
logger.debug(`📝 Adding DatahavenServiceManagerAddress parameter: ${serviceManagerAddress}`);
|
||||
parameterCollection.addParameter({
|
||||
name: "DatahavenServiceManagerAddress",
|
||||
value: serviceManagerAddress
|
||||
});
|
||||
} else {
|
||||
logger.warn("⚠️ ServiceManager address not found in deployments file");
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Failed to read parameters from deployment: ${error}`);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -360,10 +360,10 @@ describe("Validator Set Update", () => {
|
|||
});
|
||||
|
||||
if (!externalValidatorsSetEvent.data) {
|
||||
logger.error("ExternalValidatorsSet event not found");
|
||||
throw new Error("ExternalValidatorsSet event not found");
|
||||
logger.warn("ExternalValidatorsSet event not observed; will rely on storage check.");
|
||||
} else {
|
||||
logger.success("ExternalValidatorsSet event found");
|
||||
}
|
||||
logger.success("ExternalValidatorsSet event found");
|
||||
|
||||
logger.info(
|
||||
"🔍 Checking the new validators are present in the ExternalValidators pallet storage..."
|
||||
|
|
|
|||
|
|
@ -188,7 +188,8 @@ export type DataHavenRuntimeParameterKey =
|
|||
| "EthereumGatewayAddress"
|
||||
| "RewardsRegistryAddress"
|
||||
| "RewardsUpdateSelector"
|
||||
| "RewardsAgentOrigin";
|
||||
| "RewardsAgentOrigin"
|
||||
| "DatahavenServiceManagerAddress";
|
||||
|
||||
/**
|
||||
* Interface for raw JSON parameters before conversion
|
||||
|
|
@ -230,6 +231,14 @@ const rawRewardsAgentOriginSchema = z.object({
|
|||
value: hexStringSchema.nullable().optional()
|
||||
});
|
||||
|
||||
/**
|
||||
* Schema for raw DatahavenServiceManagerAddress parameter
|
||||
*/
|
||||
const rawDatahavenServiceManagerAddressSchema = z.object({
|
||||
name: z.literal("DatahavenServiceManagerAddress"),
|
||||
value: hexStringSchema.nullable().optional()
|
||||
});
|
||||
|
||||
/**
|
||||
* Union schema for raw DataHaven parameters (for parsing JSON)
|
||||
*/
|
||||
|
|
@ -237,7 +246,8 @@ export const rawDataHavenParameterSchema = z.discriminatedUnion("name", [
|
|||
rawEthereumGatewayAddressSchema,
|
||||
rawRewardsRegistryAddressSchema,
|
||||
rawRewardsUpdateSelectorSchema,
|
||||
rawRewardsAgentOriginSchema
|
||||
rawRewardsAgentOriginSchema,
|
||||
rawDatahavenServiceManagerAddressSchema
|
||||
]);
|
||||
|
||||
/**
|
||||
|
|
@ -281,6 +291,12 @@ function convertParameter(rawParam: any): ParsedDataHavenParameter {
|
|||
value: new FixedSizeBinary<32>(hexToUint8Array(rawParam.value))
|
||||
};
|
||||
}
|
||||
if (rawParam.name === "DatahavenServiceManagerAddress" && rawParam.value) {
|
||||
return {
|
||||
name: rawParam.name,
|
||||
value: new FixedSizeBinary<20>(hexToUint8Array(rawParam.value))
|
||||
};
|
||||
}
|
||||
|
||||
// For other parameter types, add conversion logic here
|
||||
return rawParam;
|
||||
|
|
|
|||
Loading…
Reference in a new issue