fix: Register the snowbridge agent in the Dathaven Service instead of the operator node (#428)

## Summary

This PR rename the `rewardsAgentOrigin`, `rewardsMessageOrigin`, etc...
into a less specific less now that the Snowbrige Agent is also being
used to relay slashing messages.

This PR also have a fix to register the Agent address instead of the
operator node address to check the sender of the message. Without this
fix we could never relay rewards or execute slashing because we would
get an error regarding the message.

## What changed 

* Removing the prefix `rewards` everytime we were refering the
snowbridge agent (to clarify that the agent is not only being used by
the reward feature)
* Fix the deployment script to register the `agentAddress` as the
required sender for relaying substrate message

## What is missing

[ ] ~~Rename `onlyRewardsInitiator` and `rewardsInitiator` in the
`DatahavenServiceManager.sol ` for something that would include
slashing~~ This should be done in another PR.
[x] Check the Testnet Deploy script to make sure it is using the agent
address

---------

Co-authored-by: Ahmad Kaouk <56095276+ahmadkaouk@users.noreply.github.com>
This commit is contained in:
undercover-cactus 2026-03-09 14:33:43 +01:00 committed by GitHub
parent f5067ea842
commit b4e22035a3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 115 additions and 102 deletions

View file

@ -35,7 +35,7 @@
"randaoCommitExpiration": 24,
"minNumRequiredSignatures": 2,
"startBlock": 1,
"rewardsMessageOrigin": "0x0000000000000000000000000000000000000000000000000000000000000000",
"messageOrigin": "0x0000000000000000000000000000000000000000000000000000000000000000",
"initialValidatorSetId": 0,
"initialValidatorHashes": [
"0xaeb47a269393297f4b0a3c9c9cfd00c7a4195255274cf39d83dabc2fcc9ff3d7",

View file

@ -99,9 +99,9 @@
/// Initial BEEFY block number. Set to latest finalized block by update-beefy-checkpoint.
/// The BeefyClient will only accept BEEFY commitments with block numbers > startBlock.
"startBlock": 1,
/// The origin linked to the Rewards Agent, the Agent contract who's allowed to submit
/// new reward merkle roots to the RewardsRegistry contract.
"rewardsMessageOrigin": "0x0000000000000000000000000000000000000000000000000000000000000000",
/// The origin linked to the Agent, the Agent contract who's allowed to submit
/// new reward merkle roots or slashes.
"messageOrigin": "0x0000000000000000000000000000000000000000000000000000000000000000",
/// The BEEFY validator set ID for the initial validators.
/// This is fetched from Beefy.ValidatorSetId on the DataHaven chain.
"initialValidatorSetId": 0,

View file

@ -43,7 +43,7 @@
"randaoCommitExpiration": 24,
"minNumRequiredSignatures": 16,
"startBlock": 1,
"rewardsMessageOrigin": "0x0000000000000000000000000000000000000000000000000000000000000000",
"messageOrigin": "0x0000000000000000000000000000000000000000000000000000000000000000",
"initialValidatorSetId": 0,
"initialValidatorHashes": [],
"nextValidatorSetId": 1,

View file

@ -44,7 +44,7 @@
"randaoCommitExpiration": 24,
"minNumRequiredSignatures": 3,
"startBlock": 1303065,
"rewardsMessageOrigin": "0x56490bd3f367447bfaf57bb18e7a45e1b2db7d538fe42098e87d2aa106c6afdd",
"messageOrigin": "0x56490bd3f367447bfaf57bb18e7a45e1b2db7d538fe42098e87d2aa106c6afdd",
"initialValidatorSetId": 2186,
"initialValidatorHashes": [
"0x07ce4f2cd558f4d4b529a3362b6ff7d616ca0893b53252dc62829b8218ea5c10",

View file

@ -44,7 +44,7 @@
"randaoCommitExpiration": 24,
"minNumRequiredSignatures": 5,
"startBlock": 1381173,
"rewardsMessageOrigin": "0xd0d6dbd1ffb401ef613f00e93cd5061ecec03ae35d2f820cd6754a5b5f020215",
"messageOrigin": "0xd0d6dbd1ffb401ef613f00e93cd5061ecec03ae35d2f820cd6754a5b5f020215",
"initialValidatorSetId": 2303,
"initialValidatorHashes": [
"0x0ec3102f334aba804c18b843e45ec874005587122a1b49273b1b04d6fd98b1a2",

View file

@ -0,0 +1 @@
{"Agent": "0xac06641381166cf085281c45292147f833C622d7","AgentOrigin": "0x0000000000000000000000000000000000000000000000000000000000000000"}

View file

@ -1 +1 @@
{"RewardsAgent": "0xac06641381166cf085281c45292147f833C622d7","RewardsAgentOrigin": "0x0000000000000000000000000000000000000000000000000000000000000000"}
{"RewardsAgent": "0xac06641381166cf085281c45292147f833C622d7","AgentOrigin": "0x0000000000000000000000000000000000000000000000000000000000000000"}

View file

@ -1 +1 @@
{"RewardsAgent": "0x2E039a88838241d1Ac738cf2e3C5763ba12571e7","RewardsAgentOrigin": "0x56490bd3f367447bfaf57bb18e7a45e1b2db7d538fe42098e87d2aa106c6afdd"}
{"RewardsAgent": "0x2E039a88838241d1Ac738cf2e3C5763ba12571e7","AgentOrigin": "0x56490bd3f367447bfaf57bb18e7a45e1b2db7d538fe42098e87d2aa106c6afdd"}

View file

@ -12,7 +12,7 @@ contract Config {
bytes32[] initialValidatorHashes;
uint128 nextValidatorSetId;
bytes32[] nextValidatorHashes;
bytes32 rewardsMessageOrigin;
bytes32 messageOrigin;
}
// AVS parameters

View file

@ -123,7 +123,7 @@ abstract contract DeployBase is Script, DeployParams, Accounts {
BeefyClient beefyClient,
AgentExecutor agentExecutor,
IGatewayV2 gateway,
address payable rewardsAgentAddress
address payable agentAddress
) = _deploySnowbridge(snowbridgeConfig);
Logging.logFooter();
_logProgress();
@ -132,14 +132,14 @@ abstract contract DeployBase is Script, DeployParams, Accounts {
(
DataHavenServiceManager serviceManager,
DataHavenServiceManager serviceManagerImplementation
) = _deployDataHavenContracts(avsConfig, proxyAdmin, gateway);
) = _deployDataHavenContracts(avsConfig, proxyAdmin, gateway, agentAddress);
Logging.logFooter();
_logProgress();
// Final configuration (same for both modes)
Logging.logHeader("FINAL CONFIGURATION");
Logging.logContractDeployed("Rewards Agent Address", rewardsAgentAddress);
Logging.logContractDeployed("Agent Address", agentAddress);
Logging.logFooter();
_logProgress();
@ -150,11 +150,11 @@ abstract contract DeployBase is Script, DeployParams, Accounts {
gateway,
serviceManager,
serviceManagerImplementation,
rewardsAgentAddress,
agentAddress,
proxyAdmin
);
_outputRewardsAgentInfo(rewardsAgentAddress, snowbridgeConfig.rewardsMessageOrigin);
_outputAgentInfo(agentAddress, snowbridgeConfig.messageOrigin);
}
/**
@ -201,11 +201,11 @@ abstract contract DeployBase is Script, DeployParams, Accounts {
// Create Agent
Logging.logSection("Creating Snowbridge Agent");
vm.broadcast(_deployerPrivateKey);
gateway.v2_createAgent(config.rewardsMessageOrigin);
address payable rewardsAgentAddress = payable(gateway.agentOf(config.rewardsMessageOrigin));
Logging.logContractDeployed("Rewards Agent", rewardsAgentAddress);
gateway.v2_createAgent(config.messageOrigin);
address payable agentAddress = payable(gateway.agentOf(config.messageOrigin));
Logging.logContractDeployed("Rewards Agent", agentAddress);
return (beefyClient, agentExecutor, gateway, rewardsAgentAddress);
return (beefyClient, agentExecutor, gateway, agentAddress);
}
/**
@ -240,7 +240,8 @@ abstract contract DeployBase is Script, DeployParams, Accounts {
function _deployDataHavenContracts(
AVSConfig memory avsConfig,
ProxyAdmin proxyAdmin,
IGatewayV2 gateway
IGatewayV2 gateway,
address agentAddress
) internal returns (DataHavenServiceManager, DataHavenServiceManager) {
Logging.logHeader("DATAHAVEN CUSTOM CONTRACTS DEPLOYMENT");
@ -269,7 +270,7 @@ abstract contract DeployBase is Script, DeployParams, Accounts {
// Create service manager initialisation parameters struct
ServiceManagerInitParams memory initParams = ServiceManagerInitParams({
avsOwner: avsConfig.avsOwner,
rewardsInitiator: avsConfig.rewardsInitiator,
rewardsInitiator: agentAddress,
validatorsStrategiesAndMultipliers: strategiesAndMultipliers,
gateway: address(gateway),
validatorSetSubmitter: avsConfig.validatorSetSubmitter,
@ -313,38 +314,38 @@ abstract contract DeployBase is Script, DeployParams, Accounts {
IGatewayV2 gateway,
DataHavenServiceManager serviceManager,
DataHavenServiceManager serviceManagerImplementation,
address rewardsAgent,
address agent,
ProxyAdmin proxyAdmin
) internal virtual;
/**
* @notice Output rewards agent info (shared across all deployment types)
* @notice Output agent info (shared across all deployment types)
*/
function _outputRewardsAgentInfo(
address rewardsAgent,
bytes32 rewardsAgentOrigin
function _outputAgentInfo(
address agent,
bytes32 agentOrigin
) internal {
Logging.logHeader("REWARDS AGENT INFO");
Logging.logContractDeployed("RewardsAgent", rewardsAgent);
Logging.logAgentOrigin("RewardsAgentOrigin", vm.toString(rewardsAgentOrigin));
Logging.logHeader("AGENT INFO");
Logging.logContractDeployed("Agent", agent);
Logging.logAgentOrigin("AgentOrigin", vm.toString(agentOrigin));
Logging.logFooter();
// Write to deployment file for future reference
string memory network = _getNetworkName();
string memory rewardsInfoPath =
string.concat(vm.projectRoot(), "/deployments/", network, "-rewards-info.json");
string memory agentInfoPath =
string.concat(vm.projectRoot(), "/deployments/", network, "-agent-info.json");
// Create directory if it doesn't exist
vm.createDir(string.concat(vm.projectRoot(), "/deployments"), true);
// Create JSON with rewards info
string memory json = "{";
json = string.concat(json, '"RewardsAgent": "', vm.toString(rewardsAgent), '",');
json = string.concat(json, '"RewardsAgentOrigin": "', vm.toString(rewardsAgentOrigin), '"');
json = string.concat(json, '"Agent": "', vm.toString(agent), '",');
json = string.concat(json, '"AgentOrigin": "', vm.toString(agentOrigin), '"');
json = string.concat(json, "}");
// Write to file
vm.writeFile(rewardsInfoPath, json);
Logging.logInfo(string.concat("Rewards info saved to: ", rewardsInfoPath));
vm.writeFile(agentInfoPath, json);
Logging.logInfo(string.concat("Agent info saved to: ", agentInfoPath));
}
}

View file

@ -24,8 +24,7 @@ contract DeployParams is Script, Config {
config.minNumRequiredSignatures =
vm.parseJsonUint(configJson, ".snowbridge.minNumRequiredSignatures");
config.startBlock = vm.parseJsonUint(configJson, ".snowbridge.startBlock").toUint64();
config.rewardsMessageOrigin =
vm.parseJsonBytes32(configJson, ".snowbridge.rewardsMessageOrigin");
config.messageOrigin = vm.parseJsonBytes32(configJson, ".snowbridge.messageOrigin");
// Load validators from file or generate placeholder ones in dev mode
bool isDevMode = keccak256(abi.encodePacked(vm.envOr("DEV_MODE", string("false"))))

View file

@ -1518,7 +1518,7 @@ impl datahaven_runtime_common::rewards_adapter::RewardsSubmissionConfig for Main
}
fn rewards_agent_origin() -> H256 {
runtime_params::dynamic_params::runtime_config::RewardsAgentOrigin::get()
runtime_params::dynamic_params::runtime_config::AgentOrigin::get()
}
fn strategies_and_multipliers() -> Vec<(H160, u128)> {
@ -1692,9 +1692,9 @@ impl datahaven_runtime_common::slashes_adapter::SlashesSubmissionConfig for Main
runtime_params::dynamic_params::runtime_config::DatahavenServiceManagerAddress::get()
}
// TODO: remove `slashes_` prefix and just call it `agent_origin`
fn slashes_agent_origin() -> H256 {
runtime_params::dynamic_params::runtime_config::RewardsAgentOrigin::get()
// TODO: Can we use the same as reward and just rename the config to `AgentOrigin` ?
runtime_params::dynamic_params::runtime_config::AgentOrigin::get()
}
fn strategies() -> Vec<Address> {

View file

@ -48,9 +48,9 @@ pub mod dynamic_params {
#[codec(index = 3)]
#[allow(non_upper_case_globals)]
/// The RewardsAgentOrigin is the hash of the string "external_validators_rewards"
/// The AgentOrigin is the hash of the string "external_validators_rewards"
/// TODO: Decide which agent origin we want to use. Currently for testing it's the zero hash
pub static RewardsAgentOrigin: H256 = H256::from_slice(&hex!(
pub static AgentOrigin: H256 = H256::from_slice(&hex!(
"c505dfb2df107d106d08bd0f1a0acd10052ca9aa078629a4ccfd0c90c6e69b65"
));

View file

@ -1514,7 +1514,7 @@ impl datahaven_runtime_common::rewards_adapter::RewardsSubmissionConfig for Stag
}
fn rewards_agent_origin() -> H256 {
runtime_params::dynamic_params::runtime_config::RewardsAgentOrigin::get()
runtime_params::dynamic_params::runtime_config::AgentOrigin::get()
}
fn strategies_and_multipliers() -> Vec<(H160, u128)> {
@ -1689,7 +1689,7 @@ impl datahaven_runtime_common::slashes_adapter::SlashesSubmissionConfig for Stag
}
fn slashes_agent_origin() -> H256 {
runtime_params::dynamic_params::runtime_config::RewardsAgentOrigin::get()
runtime_params::dynamic_params::runtime_config::AgentOrigin::get()
// TODO: Can we use the same as reward and just rename the config to `AgentOrigin` ?
}
@ -1951,8 +1951,8 @@ mod tests {
/// Test that the Rewards Agent ID (used for Snowbridge outbound messages from the rewards pallet)
/// is correctly computed from the chain's genesis hash and the ExternalValidatorRewardsAccount.
///
/// This test verifies the value that should be set as `RewardsAgentOrigin` in runtime parameters
/// and as `rewardsMessageOrigin` in the AVS contract configuration.
/// This test verifies the value that should be set as `AgentOrigin` in runtime parameters
/// and as `messageOrigin` in the AVS contract configuration.
///
/// The Agent ID is computed following Snowbridge's pattern for GlobalConsensus locations:
/// blake2_256(SCALE_ENCODE("GlobalConsensus", ByGenesis(genesis_hash), compact_len, "AccountKey20", account_key))
@ -1992,8 +1992,8 @@ mod tests {
// Hash with blake2_256
let computed_agent_id = H256(blake2_256(&encoded));
// Expected Agent ID - this value must match RewardsAgentOrigin in runtime_params.rs
// If this test fails, update RewardsAgentOrigin to match the computed value.
// Expected Agent ID - this value must match AgentOrigin in runtime_params.rs
// If this test fails, update AgentOrigin to match the computed value.
let expected_agent_id = H256(hex_literal::hex!(
"56490bd3f367447bfaf57bb18e7a45e1b2db7d538fe42098e87d2aa106c6afdd"
));
@ -2003,8 +2003,8 @@ mod tests {
expected_agent_id,
"Computed Rewards Agent ID must match expected value.\n\
This value should be set as:\n\
- RewardsAgentOrigin in runtime_params.rs\n\
- rewardsMessageOrigin in AVS contract config\n\
- AgentOrigin in runtime_params.rs\n\
- messageOrigin in AVS contract config\n\
\n\
Rewards account: 0x{}\n\
Genesis hash: 0x{}\n\

View file

@ -51,13 +51,13 @@ pub mod dynamic_params {
#[codec(index = 3)]
#[allow(non_upper_case_globals)]
/// The RewardsAgentOrigin is the Agent ID for the rewards pallet's outbound Snowbridge messages.
/// The AgentOrigin is the Agent ID for the rewards/slashes pallet's outbound Snowbridge messages.
/// Computed as: blake2_256(SCALE_ENCODE("GlobalConsensus", ByGenesis(genesis_hash), interior))
/// where interior = SCALE_ENCODE("AccountKey20", ExternalValidatorRewardsAccount)
///
/// For stagenet with genesis hash 0x72d0856fd339e09cb21df7bac8ac3120bd871e327ec0e1658395df68acef9bee
/// and rewards account 0x6d6f646c64682f65767265770000000000000000 (from PalletId "dh/evrew"):
pub static RewardsAgentOrigin: H256 = H256::from_slice(&hex!(
pub static AgentOrigin: H256 = H256::from_slice(&hex!(
"56490bd3f367447bfaf57bb18e7a45e1b2db7d538fe42098e87d2aa106c6afdd"
));

View file

@ -1518,7 +1518,7 @@ impl datahaven_runtime_common::rewards_adapter::RewardsSubmissionConfig for Test
}
fn rewards_agent_origin() -> H256 {
runtime_params::dynamic_params::runtime_config::RewardsAgentOrigin::get()
runtime_params::dynamic_params::runtime_config::AgentOrigin::get()
}
fn strategies_and_multipliers() -> Vec<(H160, u128)> {
@ -1693,8 +1693,7 @@ impl datahaven_runtime_common::slashes_adapter::SlashesSubmissionConfig for Test
}
fn slashes_agent_origin() -> H256 {
runtime_params::dynamic_params::runtime_config::RewardsAgentOrigin::get()
// TODO: Can we use the same as reward and just rename the config to `AgentOrigin` ?
runtime_params::dynamic_params::runtime_config::AgentOrigin::get()
}
fn strategies() -> Vec<Address> {
@ -1973,8 +1972,8 @@ mod tests {
/// Test that the Rewards Agent ID (used for Snowbridge outbound messages from the rewards pallet)
/// is correctly computed from the chain's genesis hash and the ExternalValidatorRewardsAccount.
///
/// This test verifies the value that should be set as `RewardsAgentOrigin` in runtime parameters
/// and as `rewardsMessageOrigin` in the AVS contract configuration.
/// This test verifies the value that should be set as `AgentOrigin` in runtime parameters
/// and as `messageOrigin` in the AVS contract configuration.
///
/// The Agent ID is computed following Snowbridge's pattern for GlobalConsensus locations:
/// blake2_256(SCALE_ENCODE("GlobalConsensus", ByGenesis(genesis_hash), compact_len, "AccountKey20", account_key))
@ -2014,8 +2013,8 @@ mod tests {
// Hash with blake2_256
let computed_agent_id = H256(blake2_256(&encoded));
// Expected Agent ID - this value must match RewardsAgentOrigin in runtime_params.rs
// If this test fails, update RewardsAgentOrigin to match the computed value.
// Expected Agent ID - this value must match AgentOrigin in runtime_params.rs
// If this test fails, update AgentOrigin to match the computed value.
let expected_agent_id = H256(hex_literal::hex!(
"d0d6dbd1ffb401ef613f00e93cd5061ecec03ae35d2f820cd6754a5b5f020215"
));
@ -2025,8 +2024,8 @@ mod tests {
expected_agent_id,
"Computed Rewards Agent ID must match expected value.\n\
This value should be set as:\n\
- RewardsAgentOrigin in runtime_params.rs\n\
- rewardsMessageOrigin in AVS contract config\n\
- AgentOrigin in runtime_params.rs\n\
- messageOrigin in AVS contract config\n\
\n\
Rewards account: 0x{}\n\
Genesis hash: 0x{}\n\

View file

@ -49,13 +49,13 @@ pub mod dynamic_params {
#[codec(index = 3)]
#[allow(non_upper_case_globals)]
/// The RewardsAgentOrigin is the Agent ID for the rewards pallet's outbound Snowbridge messages.
/// The AgentOrigin is the Agent ID for the rewards/slashes pallet's outbound Snowbridge messages.
/// Computed as: blake2_256(SCALE_ENCODE("GlobalConsensus", ByGenesis(genesis_hash), interior))
/// where interior = SCALE_ENCODE("AccountKey20", ExternalValidatorRewardsAccount)
///
/// For testnet with genesis hash 0xdbf403d348916fb0694485bc7f9c0d8c53fdf86664ebac019af209c090c3df99
/// and rewards account 0x6d6f646c64682f65767265770000000000000000 (from PalletId "dh/evrew"):
pub static RewardsAgentOrigin: H256 = H256::from_slice(&hex!(
pub static AgentOrigin: H256 = H256::from_slice(&hex!(
"d0d6dbd1ffb401ef613f00e93cd5061ecec03ae35d2f820cd6754a5b5f020215"
));

View file

@ -1,5 +1,5 @@
{
"version": "0.1.0-autogenerated.18296316742446681711",
"version": "0.1.0-autogenerated.15484599658830368838",
"name": "@polkadot-api/descriptors",
"files": [
"dist"

Binary file not shown.

View file

@ -48,7 +48,7 @@ const palletIdToAccountId20 = (palletId: string): Hex => {
* blake2_256(SCALE_ENCODE(("GlobalConsensus", ByGenesis(genesis_hash), ("AccountKey20", account_key))))
*
* NOTE: This computation follows Snowbridge's pattern but may need verification against
* the actual on-chain Agent ID. The preferred approach is to set RewardsAgentOrigin on
* the actual on-chain Agent ID. The preferred approach is to set AgentOrigin on
* the chain and fetch it via this command.
*
* @param genesisHash - The chain's genesis hash (32 bytes, hex string with 0x prefix)
@ -116,36 +116,36 @@ const computeAgentId = async (genesisHash: Hex, accountKey20: Hex): Promise<Hex>
};
/**
* Fetches the RewardsAgentOrigin from the runtime parameters.
* Fetches the AgentOrigin from the runtime parameters.
*
* @param rpcUrl - WebSocket RPC endpoint of the DataHaven chain
* @returns The RewardsAgentOrigin as a hex string, or null if not set or zero
* @returns The AgentOrigin as a hex string, or null if not set or zero
*/
const fetchRewardsAgentOrigin = async (rpcUrl: string): Promise<Hex | null> => {
const fetchAgentOrigin = async (rpcUrl: string): Promise<Hex | null> => {
logger.info(`📡 Connecting to DataHaven chain at ${rpcUrl}...`);
const { client: papiClient, typedApi: dhApi } = createPapiConnectors(rpcUrl);
try {
logger.info("🔍 Fetching RewardsAgentOrigin from runtime parameters...");
logger.info("🔍 Fetching AgentOrigin from runtime parameters...");
// Query the Parameters pallet for RewardsAgentOrigin
// Query the Parameters pallet for AgentOrigin
const parameter = await dhApi.query.Parameters.Parameters.getValue(
{
type: "RuntimeConfig",
value: { type: "RewardsAgentOrigin", value: undefined }
value: { type: "AgentOrigin", value: undefined }
},
{ at: "best" }
);
if (!parameter) {
logger.info(" RewardsAgentOrigin parameter not found (using default)");
logger.info(" AgentOrigin parameter not found (using default)");
return null;
}
// Extract the value from the parameter result
// The parameter is wrapped in the RuntimeConfig enum variant
if (parameter.type === "RuntimeConfig" && parameter.value.type === "RewardsAgentOrigin") {
if (parameter.type === "RuntimeConfig" && parameter.value.type === "AgentOrigin") {
const origin = parameter.value.value;
if (origin) {
const originHex = origin.asHex();
@ -153,15 +153,15 @@ const fetchRewardsAgentOrigin = async (rpcUrl: string): Promise<Hex | null> => {
const zeroHash =
"0x0000000000000000000000000000000000000000000000000000000000000000" as Hex;
if (originHex === zeroHash) {
logger.info(" RewardsAgentOrigin is set to zero (placeholder)");
logger.info(" AgentOrigin is set to zero (placeholder)");
return null;
}
logger.success(`Found RewardsAgentOrigin: ${originHex}`);
logger.success(`Found AgentOrigin: ${originHex}`);
return originHex as Hex;
}
}
logger.info(" RewardsAgentOrigin value not available");
logger.info(" AgentOrigin value not available");
return null;
} finally {
papiClient.destroy();
@ -193,9 +193,9 @@ const fetchGenesisHash = async (rpcUrl: string): Promise<Hex> => {
* Updates the config file with the rewards message origin.
*
* @param networkId - The network identifier (e.g., "hoodi", "stagenet-hoodi")
* @param rewardsMessageOrigin - The rewards message origin (Agent ID)
* @param messageOrigin - The rewards message origin (Agent ID)
*/
const updateConfigFile = async (networkId: string, rewardsMessageOrigin: Hex): Promise<void> => {
const updateConfigFile = async (networkId: string, messageOrigin: Hex): Promise<void> => {
const configFilePath = `../contracts/config/${networkId}.json`;
const configFile = Bun.file(configFilePath);
@ -211,21 +211,21 @@ const updateConfigFile = async (networkId: string, rewardsMessageOrigin: Hex): P
configJson.snowbridge = {};
}
const oldOrigin = configJson.snowbridge.rewardsMessageOrigin;
configJson.snowbridge.rewardsMessageOrigin = rewardsMessageOrigin;
const oldOrigin = configJson.snowbridge.messageOrigin;
configJson.snowbridge.messageOrigin = messageOrigin;
await Bun.write(configFilePath, `${JSON.stringify(configJson, null, 2)}\n`);
logger.success(`Config file updated: ${configFilePath}`);
if (oldOrigin !== rewardsMessageOrigin) {
logger.info(` rewardsMessageOrigin: ${oldOrigin ?? "unset"} -> ${rewardsMessageOrigin}`);
if (oldOrigin !== messageOrigin) {
logger.info(` messageOrigin: ${oldOrigin ?? "unset"} -> ${messageOrigin}`);
}
};
/**
* Main handler for the update-rewards-origin command.
* Fetches or computes the RewardsAgentOrigin and updates the config file.
* Fetches or computes the AgentOrigin and updates the config file.
*/
export const updateRewardsOrigin = async (options: UpdateRewardsOriginOptions): Promise<void> => {
const networkId = buildNetworkId(options.chain, options.environment);
@ -246,17 +246,17 @@ export const updateRewardsOrigin = async (options: UpdateRewardsOriginOptions):
printDivider();
try {
// Step 1: Try to fetch RewardsAgentOrigin from the chain
let rewardsMessageOrigin = await fetchRewardsAgentOrigin(options.rpcUrl);
// Step 1: Try to fetch AgentOrigin from the chain
let messageOrigin = await fetchAgentOrigin(options.rpcUrl);
printDivider();
if (rewardsMessageOrigin) {
if (messageOrigin) {
// Use the value from the chain
logger.info("✅ Using RewardsAgentOrigin from chain runtime parameters");
logger.info("✅ Using AgentOrigin from chain runtime parameters");
} else {
// Compute the Agent ID from genesis hash and pallet account
logger.info("🔧 Computing RewardsAgentOrigin from genesis hash and pallet account...");
logger.info("🔧 Computing AgentOrigin from genesis hash and pallet account...");
// Get genesis hash (from option or fetch from chain)
const genesisHash = options.genesisHash
@ -272,22 +272,22 @@ export const updateRewardsOrigin = async (options: UpdateRewardsOriginOptions):
// Compute the Agent ID
logger.info("🔐 Computing Agent ID...");
logger.warn(
"⚠️ Note: Computed Agent ID may need verification. Prefer setting RewardsAgentOrigin on-chain."
"⚠️ Note: Computed Agent ID may need verification. Prefer setting AgentOrigin on-chain."
);
rewardsMessageOrigin = await computeAgentId(genesisHash, rewardsAccount);
logger.info(` Agent ID: ${rewardsMessageOrigin}`);
messageOrigin = await computeAgentId(genesisHash, rewardsAccount);
logger.info(` Agent ID: ${messageOrigin}`);
}
printDivider();
// Display the final value
logger.info("📝 Rewards Message Origin:");
logger.info(` ${rewardsMessageOrigin}`);
logger.info(` ${messageOrigin}`);
printDivider();
// Update the config file
await updateConfigFile(networkId, rewardsMessageOrigin);
await updateConfigFile(networkId, messageOrigin);
printDivider();
logger.success(`Rewards message origin updated successfully for ${networkId}`);

View file

@ -212,7 +212,7 @@ const contractsCommand = program
- upgrade: Upgrade contracts by deploying new implementations
- verify: Verify deployed contracts on block explorer
- update-beefy-checkpoint: Fetch BEEFY authorities from a live chain and update config
- update-rewards-origin: Fetch or compute the RewardsAgentOrigin and update config
- update-rewards-origin: Fetch or compute the AgentOrigin and update config
- update-metadata: Update the metadata URI of an existing AVS contract
Common options:
@ -385,17 +385,14 @@ contractsCommand
contractsCommand
.command("update-rewards-origin")
.description(
"Fetch or compute the RewardsAgentOrigin and update the config file with the rewards message origin"
"Fetch or compute the AgentOrigin and update the config file with the rewards message origin"
)
.option("--chain <value>", "Target chain (hoodi, ethereum, anvil)")
.option(
"--environment <value>",
"Deployment environment (stagenet, testnet, mainnet). Config and deployment files will be prefixed with this value."
)
.option(
"--rpc-url <value>",
"WebSocket RPC URL of the DataHaven chain to fetch RewardsAgentOrigin from"
)
.option("--rpc-url <value>", "WebSocket RPC URL of the DataHaven chain to fetch AgentOrigin from")
.option(
"--genesis-hash <value>",
"Chain genesis hash (32 bytes hex). If not provided, will be fetched from the chain."

View file

@ -8,7 +8,7 @@
"value": null
},
{
"name": "RewardsAgentOrigin",
"name": "AgentOrigin",
"value": null
},
{

View file

@ -3,8 +3,9 @@ import { $ } from "bun";
import { Binary, FixedSizeBinary } from "polkadot-api";
import { CROSS_CHAIN_TIMEOUTS, getPapiSigner, logger } from "utils";
import type { Address } from "viem";
import { gatewayAbi } from "../../contract-bindings";
import { getContractInstance, parseDeploymentsFile } from "../../utils/contracts";
import { waitForDataHavenEvent } from "../../utils/events";
import { waitForDataHavenEvent, waitForEthereumEvent } from "../../utils/events";
import { waitFor } from "../../utils/waits";
import { BaseTestSuite } from "../framework";
@ -122,6 +123,8 @@ describe("Should slash an operator", () => {
}, 40000);
it("use sudo to slash operator", async () => {
const { publicClient } = suite.getTestConnectors();
// get era number
const activeEra = await dhApi.query.ExternalValidators.ActiveEra.getValue();
@ -168,6 +171,19 @@ describe("Should slash an operator", () => {
throw new Error("SlashesMessageSent event not found");
}
logger.info("Slashes message sent");
const fromBlock = await publicClient.getBlockNumber();
const deployments = await parseDeploymentsFile();
const _ethEvent = await waitForEthereumEvent({
client: publicClient,
address: deployments.Gateway,
abi: gatewayAbi,
eventName: "SlashingComplete",
fromBlock: fromBlock > 0n ? fromBlock - 1n : fromBlock,
timeout: CROSS_CHAIN_TIMEOUTS.DH_TO_ETH_MS
});
logger.info("Got Ethereum event!");
}, 560000);
it("should detect and slash an unresponsive validator (liveness)", async () => {

View file

@ -184,7 +184,7 @@ export const parseJsonToBeaconCheckpoint = (jsonInput: any): BeaconCheckpoint =>
const DATAHAVEN_PARAM_NAMES = [
"EthereumGatewayAddress",
"RewardsUpdateSelector",
"RewardsAgentOrigin",
"AgentOrigin",
"DatahavenServiceManagerAddress"
] as const;