mirror of
https://github.com/datahaven-xyz/datahaven
synced 2026-05-24 09:50:01 +00:00
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:
parent
f5067ea842
commit
b4e22035a3
24 changed files with 115 additions and 102 deletions
|
|
@ -35,7 +35,7 @@
|
|||
"randaoCommitExpiration": 24,
|
||||
"minNumRequiredSignatures": 2,
|
||||
"startBlock": 1,
|
||||
"rewardsMessageOrigin": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"messageOrigin": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"initialValidatorSetId": 0,
|
||||
"initialValidatorHashes": [
|
||||
"0xaeb47a269393297f4b0a3c9c9cfd00c7a4195255274cf39d83dabc2fcc9ff3d7",
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@
|
|||
"randaoCommitExpiration": 24,
|
||||
"minNumRequiredSignatures": 16,
|
||||
"startBlock": 1,
|
||||
"rewardsMessageOrigin": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"messageOrigin": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"initialValidatorSetId": 0,
|
||||
"initialValidatorHashes": [],
|
||||
"nextValidatorSetId": 1,
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@
|
|||
"randaoCommitExpiration": 24,
|
||||
"minNumRequiredSignatures": 3,
|
||||
"startBlock": 1303065,
|
||||
"rewardsMessageOrigin": "0x56490bd3f367447bfaf57bb18e7a45e1b2db7d538fe42098e87d2aa106c6afdd",
|
||||
"messageOrigin": "0x56490bd3f367447bfaf57bb18e7a45e1b2db7d538fe42098e87d2aa106c6afdd",
|
||||
"initialValidatorSetId": 2186,
|
||||
"initialValidatorHashes": [
|
||||
"0x07ce4f2cd558f4d4b529a3362b6ff7d616ca0893b53252dc62829b8218ea5c10",
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@
|
|||
"randaoCommitExpiration": 24,
|
||||
"minNumRequiredSignatures": 5,
|
||||
"startBlock": 1381173,
|
||||
"rewardsMessageOrigin": "0xd0d6dbd1ffb401ef613f00e93cd5061ecec03ae35d2f820cd6754a5b5f020215",
|
||||
"messageOrigin": "0xd0d6dbd1ffb401ef613f00e93cd5061ecec03ae35d2f820cd6754a5b5f020215",
|
||||
"initialValidatorSetId": 2303,
|
||||
"initialValidatorHashes": [
|
||||
"0x0ec3102f334aba804c18b843e45ec874005587122a1b49273b1b04d6fd98b1a2",
|
||||
|
|
|
|||
1
contracts/deployments/anvil-agent-info.json
Normal file
1
contracts/deployments/anvil-agent-info.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"Agent": "0xac06641381166cf085281c45292147f833C622d7","AgentOrigin": "0x0000000000000000000000000000000000000000000000000000000000000000"}
|
||||
|
|
@ -1 +1 @@
|
|||
{"RewardsAgent": "0xac06641381166cf085281c45292147f833C622d7","RewardsAgentOrigin": "0x0000000000000000000000000000000000000000000000000000000000000000"}
|
||||
{"RewardsAgent": "0xac06641381166cf085281c45292147f833C622d7","AgentOrigin": "0x0000000000000000000000000000000000000000000000000000000000000000"}
|
||||
|
|
@ -1 +1 @@
|
|||
{"RewardsAgent": "0x2E039a88838241d1Ac738cf2e3C5763ba12571e7","RewardsAgentOrigin": "0x56490bd3f367447bfaf57bb18e7a45e1b2db7d538fe42098e87d2aa106c6afdd"}
|
||||
{"RewardsAgent": "0x2E039a88838241d1Ac738cf2e3C5763ba12571e7","AgentOrigin": "0x56490bd3f367447bfaf57bb18e7a45e1b2db7d538fe42098e87d2aa106c6afdd"}
|
||||
|
|
@ -12,7 +12,7 @@ contract Config {
|
|||
bytes32[] initialValidatorHashes;
|
||||
uint128 nextValidatorSetId;
|
||||
bytes32[] nextValidatorHashes;
|
||||
bytes32 rewardsMessageOrigin;
|
||||
bytes32 messageOrigin;
|
||||
}
|
||||
|
||||
// AVS parameters
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"))))
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
));
|
||||
|
||||
|
|
|
|||
|
|
@ -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\
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
));
|
||||
|
||||
|
|
|
|||
|
|
@ -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\
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
));
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
|
@ -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}`);
|
||||
|
|
|
|||
|
|
@ -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."
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
"value": null
|
||||
},
|
||||
{
|
||||
"name": "RewardsAgentOrigin",
|
||||
"name": "AgentOrigin",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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 () => {
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@ export const parseJsonToBeaconCheckpoint = (jsonInput: any): BeaconCheckpoint =>
|
|||
const DATAHAVEN_PARAM_NAMES = [
|
||||
"EthereumGatewayAddress",
|
||||
"RewardsUpdateSelector",
|
||||
"RewardsAgentOrigin",
|
||||
"AgentOrigin",
|
||||
"DatahavenServiceManagerAddress"
|
||||
] as const;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue