hopr_chain_rpc/
rpc.rs

1//! General purpose high-level RPC operations implementation (`HoprRpcOperations`).
2//!
3//! The purpose of this module is to give implementation of the [HoprRpcOperations] trait:
4//! [RpcOperations] type, which is the main API exposed by this crate.
5use std::{sync::Arc, time::Duration};
6
7use SafeSingleton::SafeSingletonInstance;
8use alloy::{
9    network::EthereumWallet,
10    providers::{
11        CallItemBuilder, Identity, PendingTransaction, Provider, ProviderBuilder, RootProvider,
12        fillers::{
13            BlobGasFiller, CachedNonceManager, ChainIdFiller, FillProvider, GasFiller, JoinFill, NonceFiller,
14            WalletFiller,
15        },
16    },
17    rpc::{
18        client::RpcClient,
19        types::{Block, TransactionRequest},
20    },
21    signers::local::PrivateKeySigner,
22    sol,
23};
24use async_trait::async_trait;
25use hopr_bindings::hoprnodemanagementmodule::HoprNodeManagementModule::{self, HoprNodeManagementModuleInstance};
26use hopr_chain_types::{ContractAddresses, ContractInstances, NetworkRegistryProxy};
27use hopr_crypto_types::{
28    keypairs::{ChainKeypair, Keypair},
29    prelude::Hash,
30};
31use hopr_internal_types::prelude::{EncodedWinProb, WinningProbability};
32use hopr_primitive_types::prelude::*;
33use primitive_types::U256;
34use serde::{Deserialize, Serialize};
35use tracing::debug;
36use url::Url;
37use validator::Validate;
38
39// use crate::middleware::GnosisScan;
40use crate::{
41    HoprRpcOperations, NodeSafeModuleStatus,
42    client::GasOracleFiller,
43    errors::{Result, RpcError},
44    transport::HttpRequestor,
45};
46
47// define basic safe abi
48sol!(
49    #![sol(abi)]
50    #![sol(rpc)]
51    contract SafeSingleton {
52        function isModuleEnabled(address module) public view returns (bool);
53    }
54);
55
56/// Default gas oracle URL for Gnosis chain.
57pub const DEFAULT_GAS_ORACLE_URL: &str = "https://ggnosis.blockscan.com/gasapi.ashx?apikey=key&method=gasoracle";
58
59/// Configuration of the RPC related parameters.
60#[derive(Clone, Debug, PartialEq, Eq, smart_default::SmartDefault, Serialize, Deserialize, Validate)]
61pub struct RpcOperationsConfig {
62    /// Blockchain id
63    ///
64    /// Default is 100.
65    #[default = 100]
66    pub chain_id: u64,
67    /// Addresses of all deployed contracts
68    ///
69    /// Default contains empty (null) addresses.
70    pub contract_addrs: ContractAddresses,
71    /// Address of the node's module.
72    ///
73    /// Defaults to null address.
74    pub module_address: Address,
75    /// Address of the node's safe contract.
76    ///
77    /// Defaults to null address.
78    pub safe_address: Address,
79    /// Expected block time of the blockchain
80    ///
81    /// Defaults to 5 seconds
82    #[default(Duration::from_secs(5))]
83    pub expected_block_time: Duration,
84    /// The largest amount of blocks to fetch at once when fetching a range of blocks.
85    ///
86    /// If the requested block range size is N, then the client will always fetch `min(N, max_block_range_fetch_size)`
87    ///
88    /// Defaults to 2000 blocks
89    #[validate(range(min = 1))]
90    #[default = 2000]
91    pub max_block_range_fetch_size: u64,
92    /// Interval for polling on TX submission
93    ///
94    /// Defaults to 7 seconds.
95    #[default(Duration::from_secs(7))]
96    pub tx_polling_interval: Duration,
97    /// Finalization chain length
98    ///
99    /// The number of blocks including and decreasing from the chain HEAD
100    /// that the logs will be buffered for before being considered
101    /// successfully joined to the chain.
102    ///
103    /// Defaults to 8
104    #[validate(range(min = 1, max = 100))]
105    #[default = 8]
106    pub finality: u32,
107    /// URL to the gas price oracle.
108    ///
109    /// Defaults to [`DEFAULT_GAS_ORACLE_URL`].
110    #[default(Some(DEFAULT_GAS_ORACLE_URL.parse().unwrap()))]
111    pub gas_oracle_url: Option<Url>,
112}
113
114pub(crate) type HoprProvider<R> = FillProvider<
115    JoinFill<
116        JoinFill<
117            JoinFill<
118                JoinFill<
119                    JoinFill<JoinFill<Identity, WalletFiller<EthereumWallet>>, ChainIdFiller>,
120                    NonceFiller<CachedNonceManager>,
121                >,
122                GasFiller,
123            >,
124            GasOracleFiller<R>,
125        >,
126        BlobGasFiller,
127    >,
128    RootProvider,
129>;
130
131/// Implementation of `HoprRpcOperations` and `HoprIndexerRpcOperations` trait via `alloy`
132#[derive(Debug, Clone)]
133pub struct RpcOperations<R: HttpRequestor + 'static + Clone> {
134    pub(crate) provider: Arc<HoprProvider<R>>,
135    pub(crate) cfg: RpcOperationsConfig,
136    contract_instances: Arc<ContractInstances<HoprProvider<R>>>,
137    node_module: HoprNodeManagementModuleInstance<HoprProvider<R>>,
138    node_safe: SafeSingletonInstance<HoprProvider<R>>,
139}
140
141#[cfg_attr(test, mockall::automock)]
142impl<R: HttpRequestor + 'static + Clone> RpcOperations<R> {
143    pub fn new(
144        rpc_client: RpcClient,
145        requestor: R,
146        chain_key: &ChainKeypair,
147        cfg: RpcOperationsConfig,
148        use_dummy_nr: Option<bool>,
149    ) -> Result<Self> {
150        let wallet =
151            PrivateKeySigner::from_slice(chain_key.secret().as_ref()).map_err(|e| RpcError::SignerError(e.into()))?;
152
153        let provider = ProviderBuilder::new()
154            .disable_recommended_fillers()
155            .wallet(wallet)
156            .filler(ChainIdFiller::default())
157            .filler(NonceFiller::new(CachedNonceManager::default()))
158            .filler(GasFiller)
159            .filler(GasOracleFiller::new(requestor.clone(), cfg.gas_oracle_url.clone()))
160            .filler(BlobGasFiller)
161            .connect_client(rpc_client);
162
163        debug!("{:?}", cfg.contract_addrs);
164
165        Ok(Self {
166            contract_instances: Arc::new(ContractInstances::new(
167                &cfg.contract_addrs,
168                provider.clone(),
169                use_dummy_nr.unwrap_or(cfg!(test)),
170            )),
171            node_module: HoprNodeManagementModule::new(cfg.module_address.into(), provider.clone()),
172            node_safe: SafeSingleton::new(cfg.safe_address.into(), provider.clone()),
173            cfg,
174            provider: Arc::new(provider),
175        })
176    }
177
178    pub(crate) async fn get_block_number(&self) -> Result<u64> {
179        Ok(self
180            .provider
181            .get_block_number()
182            .await?
183            .saturating_sub(self.cfg.finality as u64))
184    }
185
186    pub(crate) async fn get_block(&self, block_number: u64) -> Result<Option<Block>> {
187        let sanitized_block_number = block_number.saturating_sub(self.cfg.finality as u64);
188        let result = self.provider.get_block_by_number(sanitized_block_number.into()).await?;
189        Ok(result)
190    }
191
192    pub(crate) async fn get_xdai_balance(&self, address: Address) -> Result<XDaiBalance> {
193        Ok(XDaiBalance::from(U256::from_be_bytes(
194            self.provider.get_balance(address.into()).await?.to_be_bytes::<32>(),
195        )))
196    }
197
198    pub(crate) async fn get_hopr_balance(&self, address: Address) -> Result<HoprBalance> {
199        Ok(HoprBalance::from(U256::from_be_bytes(
200            self.contract_instances
201                .token
202                .balanceOf(address.into())
203                .call()
204                .await?
205                .to_be_bytes::<32>(),
206        )))
207    }
208
209    pub(crate) async fn get_hopr_allowance(&self, owner: Address, spender: Address) -> Result<HoprBalance> {
210        Ok(HoprBalance::from(U256::from_be_bytes(
211            self.contract_instances
212                .token
213                .allowance(owner.into(), spender.into())
214                .call()
215                .await?
216                .to_be_bytes::<32>(),
217        )))
218    }
219}
220
221#[async_trait]
222impl<R: HttpRequestor + 'static + Clone> HoprRpcOperations for RpcOperations<R> {
223    async fn get_timestamp(&self, block_number: u64) -> Result<Option<u64>> {
224        Ok(self.get_block(block_number).await?.map(|b| b.header.timestamp))
225    }
226
227    async fn get_xdai_balance(&self, address: Address) -> Result<XDaiBalance> {
228        self.get_xdai_balance(address).await
229    }
230
231    async fn get_hopr_balance(&self, address: Address) -> Result<HoprBalance> {
232        self.get_hopr_balance(address).await
233    }
234
235    async fn get_hopr_allowance(&self, owner: Address, spender: Address) -> Result<HoprBalance> {
236        self.get_hopr_allowance(owner, spender).await
237    }
238
239    async fn get_minimum_network_winning_probability(&self) -> Result<WinningProbability> {
240        match self.contract_instances.win_prob_oracle.currentWinProb().call().await {
241            Ok(encoded_win_prob) => {
242                let mut encoded: EncodedWinProb = Default::default();
243                encoded.copy_from_slice(&encoded_win_prob.to_be_bytes_vec());
244                Ok(encoded.into())
245            }
246            Err(e) => Err(e.into()),
247        }
248    }
249
250    async fn get_minimum_network_ticket_price(&self) -> Result<HoprBalance> {
251        Ok(self
252            .contract_instances
253            .price_oracle
254            .currentTicketPrice()
255            .call()
256            .await
257            .map(|v| HoprBalance::from(U256::from_be_bytes(v.to_be_bytes::<32>())))?)
258    }
259
260    async fn get_eligibility_status(&self, address: Address) -> Result<bool> {
261        // 2) check if the node is registered to an account. In case the selfRegister is disabled
262        // (i.e., when the staking threshold is zero) this value is used to check if the node is eligible
263        let tx_2 = CallItemBuilder::new(
264            self.contract_instances
265                .network_registry
266                .nodeRegisterdToAccount(address.into()),
267        )
268        .allow_failure(false);
269
270        // 3) check if the node is registered and eligible
271        let tx_3 = CallItemBuilder::new(
272            self.contract_instances
273                .network_registry
274                .isNodeRegisteredAndEligible(address.into()),
275        )
276        .allow_failure(true);
277
278        debug!(address = %self.contract_instances.network_registry_proxy.address(),"building tx_3 for eligibility check");
279        // 1) check if the staking threshold set in the implementation is above zero
280        let (stake_threshold, node_registration, quick_check) = match &self.contract_instances.network_registry_proxy {
281            NetworkRegistryProxy::Dummy(c) => {
282                debug!(proxy_address = %c.address(), "Using dummy network registry proxy for eligibility check");
283                let tx_1_dummy = CallItemBuilder::new(c.maxAllowedRegistrations(address.into())).allow_failure(false);
284                let multicall = self
285                    .provider
286                    .multicall()
287                    .add_call(tx_1_dummy)
288                    .add_call(tx_2)
289                    .add_call(tx_3);
290                let (max_allowed_registration, node_registered_to_account, quick_check) =
291                    multicall.aggregate3().await.map_err(RpcError::MulticallError)?;
292                (
293                    max_allowed_registration
294                        .map_err(|e| RpcError::MulticallFailure(e.idx, e.return_data.to_string()))?,
295                    node_registered_to_account
296                        .map_err(|e| RpcError::MulticallFailure(e.idx, e.return_data.to_string()))?,
297                    quick_check,
298                )
299            }
300            NetworkRegistryProxy::Safe(c) => {
301                debug!(proxy_address = %c.address(), "Using safe network registry proxy for eligibility check");
302                let tx_1_proxy = CallItemBuilder::new(c.stakeThreshold()).allow_failure(false);
303                let multicall = self
304                    .provider
305                    .multicall()
306                    .add_call(tx_1_proxy)
307                    .add_call(tx_2)
308                    .add_call(tx_3);
309                let (stake_threshold, node_registered_to_account, quick_check) =
310                    multicall.aggregate3().await.map_err(RpcError::MulticallError)?;
311                (
312                    stake_threshold.map_err(|e| RpcError::MulticallFailure(e.idx, e.return_data.to_string()))?,
313                    node_registered_to_account
314                        .map_err(|e| RpcError::MulticallFailure(e.idx, e.return_data.to_string()))?,
315                    quick_check,
316                )
317            }
318        };
319
320        match &quick_check {
321            Ok(eligibility_quick_check) => return Ok(*eligibility_quick_check),
322            Err(e) => {
323                // check in details what is the failure message
324                // The "division by zero" error is caused by the self-registration,
325                // which is forbidden to the public and thus returns false
326                // therefore the eligibility check should be ignored
327                // In EVM it returns `Panic(0x12)` error, where `0x4e487b71` is the function selector
328                // https://docs.soliditylang.org/en/v0.8.12/control-structures.html#panic-via-assert-and-error-via-require
329                if e.return_data.starts_with(&[0x4e, 0x48, 0x7b, 0x71])
330                    && e.return_data[e.return_data.len() - 1] == 0x12
331                {
332                    // when receiving division by zero error, if the staking threshold is zero
333                    // and registered account is not zero, return true
334                    return Ok(stake_threshold.is_zero() && !node_registration.is_zero());
335                }
336                return Ok(false);
337            }
338        }
339    }
340
341    async fn get_node_management_module_target_info(&self, target: Address) -> Result<Option<U256>> {
342        match self.node_module.tryGetTarget(target.into()).call().await {
343            Ok(returned_result) => Ok(returned_result
344                ._0
345                .then_some(U256::from_big_endian(returned_result._1.to_be_bytes_vec().as_slice()))),
346            Err(e) => Err(e.into()),
347        }
348    }
349
350    async fn get_safe_from_node_safe_registry(&self, node_address: Address) -> Result<Address> {
351        match self
352            .contract_instances
353            .safe_registry
354            .nodeToSafe(node_address.into())
355            .call()
356            .await
357        {
358            Ok(returned_result) => Ok(returned_result.into()),
359            Err(e) => Err(e.into()),
360        }
361    }
362
363    async fn get_module_target_address(&self) -> Result<Address> {
364        match self.node_module.owner().call().await {
365            Ok(returned_result) => Ok(returned_result.into()),
366            Err(e) => Err(e.into()),
367        }
368    }
369
370    async fn get_channel_closure_notice_period(&self) -> Result<Duration> {
371        // TODO: should we cache this value internally ?
372        match self
373            .contract_instances
374            .channels
375            .noticePeriodChannelClosure()
376            .call()
377            .await
378        {
379            Ok(returned_result) => Ok(Duration::from_secs(returned_result.into())),
380            Err(e) => Err(e.into()),
381        }
382    }
383
384    // Check on-chain status of, node, safe, and module
385    async fn check_node_safe_module_status(&self, node_address: Address) -> Result<NodeSafeModuleStatus> {
386        // 1) check if the node is already included into the module
387        let tx_1 = CallItemBuilder::new(self.node_module.isNode(node_address.into())).allow_failure(false);
388        // 2) if the module is enabled in the safe
389        let tx_2 =
390            CallItemBuilder::new(self.node_safe.isModuleEnabled(self.cfg.module_address.into())).allow_failure(false);
391        // 3) if the safe is the owner of the module
392        let tx_3 = CallItemBuilder::new(self.node_module.owner()).allow_failure(false);
393        let multicall = self.provider.multicall().add_call(tx_1).add_call(tx_2).add_call(tx_3);
394
395        let (node_in_module_inclusion, module_safe_enabling, safe_of_module_ownership) =
396            multicall.aggregate3_value().await.map_err(RpcError::MulticallError)?;
397
398        let is_node_included_in_module =
399            node_in_module_inclusion.map_err(|e| RpcError::MulticallFailure(e.idx, e.return_data.to_string()))?;
400        let is_module_enabled_in_safe =
401            module_safe_enabling.map_err(|e| RpcError::MulticallFailure(e.idx, e.return_data.to_string()))?;
402        let is_safe_owner_of_module = self.cfg.safe_address.eq(&safe_of_module_ownership
403            .map_err(|e| RpcError::MulticallFailure(e.idx, e.return_data.to_string()))?
404            .0
405            .0
406            .into());
407
408        Ok(NodeSafeModuleStatus {
409            is_node_included_in_module,
410            is_module_enabled_in_safe,
411            is_safe_owner_of_module,
412        })
413    }
414
415    async fn send_transaction(&self, tx: TransactionRequest) -> Result<PendingTransaction> {
416        let sent_tx = self.provider.send_transaction(tx).await?;
417
418        let pending_tx = sent_tx.register().await.map_err(RpcError::PendingTransactionError)?;
419
420        Ok(pending_tx)
421    }
422
423    async fn send_transaction_with_confirm(&self, tx: TransactionRequest) -> Result<Hash> {
424        let sent_tx = self.provider.send_transaction(tx).await?;
425
426        let receipt = sent_tx.get_receipt().await.map_err(RpcError::PendingTransactionError)?;
427
428        let tx_hash = Hash::from(receipt.transaction_hash.0);
429
430        // Check the transaction status. `status()` returns `true` for successful transactions
431        // and `false` for failed or reverted transactions.
432        if receipt.status() {
433            Ok(tx_hash)
434        } else {
435            // Transaction failed, raise an error
436            Err(RpcError::TransactionFailed(tx_hash))
437        }
438    }
439}
440
441#[cfg(test)]
442mod tests {
443    use std::{sync::Arc, time::Duration};
444
445    use alloy::{
446        network::{Ethereum, TransactionBuilder},
447        primitives::{Bytes, U256, address},
448        providers::{MULTICALL3_ADDRESS, Provider},
449        rpc::{client::ClientBuilder, types::TransactionRequest},
450        transports::{http::ReqwestTransport, layers::RetryBackoffLayer},
451    };
452    use hex_literal::hex;
453    use hopr_async_runtime::prelude::sleep;
454    use hopr_chain_types::{ContractAddresses, ContractInstances, NetworkRegistryProxy, utils::create_native_transfer};
455    use hopr_crypto_types::keypairs::{ChainKeypair, Keypair};
456    use hopr_primitive_types::prelude::*;
457    use primitive_types::H160;
458    use tracing::debug;
459
460    use crate::{
461        HoprRpcOperations, PendingTransaction,
462        client::{AnvilRpcClient, create_rpc_client_to_anvil},
463        errors::Result,
464        rpc::{RpcOperations, RpcOperationsConfig},
465    };
466
467    lazy_static::lazy_static! {
468        static ref RANDY: Address = hex!("762614a5ed652457a2f1cdb8006380530c26ae6a").into();
469        static ref MULTICALL3_DEPLOY_CODE: [u8; 3926] = hex!("f90f538085174876e800830f42408080b90f00608060405234801561001057600080fd5b50610ee0806100206000396000f3fe6080604052600436106100f35760003560e01c80634d2301cc1161008a578063a8b0574e11610059578063a8b0574e1461025a578063bce38bd714610275578063c3077fa914610288578063ee82ac5e1461029b57600080fd5b80634d2301cc146101ec57806372425d9d1461022157806382ad56cb1461023457806386d516e81461024757600080fd5b80633408e470116100c65780633408e47014610191578063399542e9146101a45780633e64a696146101c657806342cbb15c146101d957600080fd5b80630f28c97d146100f8578063174dea711461011a578063252dba421461013a57806327e86d6e1461015b575b600080fd5b34801561010457600080fd5b50425b6040519081526020015b60405180910390f35b61012d610128366004610a85565b6102ba565b6040516101119190610bbe565b61014d610148366004610a85565b6104ef565b604051610111929190610bd8565b34801561016757600080fd5b50437fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0140610107565b34801561019d57600080fd5b5046610107565b6101b76101b2366004610c60565b610690565b60405161011193929190610cba565b3480156101d257600080fd5b5048610107565b3480156101e557600080fd5b5043610107565b3480156101f857600080fd5b50610107610207366004610ce2565b73ffffffffffffffffffffffffffffffffffffffff163190565b34801561022d57600080fd5b5044610107565b61012d610242366004610a85565b6106ab565b34801561025357600080fd5b5045610107565b34801561026657600080fd5b50604051418152602001610111565b61012d610283366004610c60565b61085a565b6101b7610296366004610a85565b610a1a565b3480156102a757600080fd5b506101076102b6366004610d18565b4090565b60606000828067ffffffffffffffff8111156102d8576102d8610d31565b60405190808252806020026020018201604052801561031e57816020015b6040805180820190915260008152606060208201528152602001906001900390816102f65790505b5092503660005b8281101561047757600085828151811061034157610341610d60565b6020026020010151905087878381811061035d5761035d610d60565b905060200281019061036f9190610d8f565b6040810135958601959093506103886020850185610ce2565b73ffffffffffffffffffffffffffffffffffffffff16816103ac6060870187610dcd565b6040516103ba929190610e32565b60006040518083038185875af1925050503d80600081146103f7576040519150601f19603f3d011682016040523d82523d6000602084013e6103fc565b606091505b50602080850191909152901515808452908501351761046d577f08c379a000000000000000000000000000000000000000000000000000000000600052602060045260176024527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060445260846000fd5b5050600101610325565b508234146104e6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f4d756c746963616c6c333a2076616c7565206d69736d6174636800000000000060448201526064015b60405180910390fd5b50505092915050565b436060828067ffffffffffffffff81111561050c5761050c610d31565b60405190808252806020026020018201604052801561053f57816020015b606081526020019060019003908161052a5790505b5091503660005b8281101561068657600087878381811061056257610562610d60565b90506020028101906105749190610e42565b92506105836020840184610ce2565b73ffffffffffffffffffffffffffffffffffffffff166105a66020850185610dcd565b6040516105b4929190610e32565b6000604051808303816000865af19150503d80600081146105f1576040519150601f19603f3d011682016040523d82523d6000602084013e6105f6565b606091505b5086848151811061060957610609610d60565b602090810291909101015290508061067d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060448201526064016104dd565b50600101610546565b5050509250929050565b43804060606106a086868661085a565b905093509350939050565b6060818067ffffffffffffffff8111156106c7576106c7610d31565b60405190808252806020026020018201604052801561070d57816020015b6040805180820190915260008152606060208201528152602001906001900390816106e55790505b5091503660005b828110156104e657600084828151811061073057610730610d60565b6020026020010151905086868381811061074c5761074c610d60565b905060200281019061075e9190610e76565b925061076d6020840184610ce2565b73ffffffffffffffffffffffffffffffffffffffff166107906040850185610dcd565b60405161079e929190610e32565b6000604051808303816000865af19150503d80600081146107db576040519150601f19603f3d011682016040523d82523d6000602084013e6107e0565b606091505b506020808401919091529015158083529084013517610851577f08c379a000000000000000000000000000000000000000000000000000000000600052602060045260176024527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060445260646000fd5b50600101610714565b6060818067ffffffffffffffff81111561087657610876610d31565b6040519080825280602002602001820160405280156108bc57816020015b6040805180820190915260008152606060208201528152602001906001900390816108945790505b5091503660005b82811015610a105760008482815181106108df576108df610d60565b602002602001015190508686838181106108fb576108fb610d60565b905060200281019061090d9190610e42565b925061091c6020840184610ce2565b73ffffffffffffffffffffffffffffffffffffffff1661093f6020850185610dcd565b60405161094d929190610e32565b6000604051808303816000865af19150503d806000811461098a576040519150601f19603f3d011682016040523d82523d6000602084013e61098f565b606091505b506020830152151581528715610a07578051610a07576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060448201526064016104dd565b506001016108c3565b5050509392505050565b6000806060610a2b60018686610690565b919790965090945092505050565b60008083601f840112610a4b57600080fd5b50813567ffffffffffffffff811115610a6357600080fd5b6020830191508360208260051b8501011115610a7e57600080fd5b9250929050565b60008060208385031215610a9857600080fd5b823567ffffffffffffffff811115610aaf57600080fd5b610abb85828601610a39565b90969095509350505050565b6000815180845260005b81811015610aed57602081850181015186830182015201610ad1565b81811115610aff576000602083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b600082825180855260208086019550808260051b84010181860160005b84811015610bb1578583037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001895281518051151584528401516040858501819052610b9d81860183610ac7565b9a86019a9450505090830190600101610b4f565b5090979650505050505050565b602081526000610bd16020830184610b32565b9392505050565b600060408201848352602060408185015281855180845260608601915060608160051b870101935082870160005b82811015610c52577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0888703018452610c40868351610ac7565b95509284019290840190600101610c06565b509398975050505050505050565b600080600060408486031215610c7557600080fd5b83358015158114610c8557600080fd5b9250602084013567ffffffffffffffff811115610ca157600080fd5b610cad86828701610a39565b9497909650939450505050565b838152826020820152606060408201526000610cd96060830184610b32565b95945050505050565b600060208284031215610cf457600080fd5b813573ffffffffffffffffffffffffffffffffffffffff81168114610bd157600080fd5b600060208284031215610d2a57600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81833603018112610dc357600080fd5b9190910192915050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112610e0257600080fd5b83018035915067ffffffffffffffff821115610e1d57600080fd5b602001915036819003821315610a7e57600080fd5b8183823760009101908152919050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc1833603018112610dc357600080fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa1833603018112610dc357600080fdfea2646970667358221220bb2b5c71a328032f97c676ae39a1ec2148d3e5d6f73d95e9b17910152d61f16264736f6c634300080c00331ca0edce47092c0f398cebf3ffc267f05c8e7076e3b89445e0fe50f6332273d4569ba01b0b9d000e19b24c5869b0fc3b22b0d6fa47cd63316875cbbd577d76e6fde086");
470        static ref MULTICALL3_DEPLOYER_ADDRESS: alloy::primitives::Address = address!("05f32b3cc3888453ff71b01135b34ff8e41263f2");
471    }
472
473    pub const ETH_VALUE_FOR_MULTICALL3_DEPLOYER: u128 = 100_000_000_000_000_000;
474    // 0.1 (anvil) ETH
475
476    pub async fn wait_until_tx(pending: PendingTransaction, timeout: Duration) {
477        let tx_hash = *pending.tx_hash();
478        sleep(timeout).await;
479        pending
480            .await
481            .unwrap_or_else(|_| panic!("timeout awaiting tx hash {tx_hash} after {} seconds", timeout.as_secs()));
482    }
483
484    /// Deploy a MULTICALL contract into Anvil local chain for testing
485    pub async fn deploy_multicall3_to_anvil<P: Provider>(provider: &P) -> Result<()> {
486        // check if the multicall3 contract is already deployed. If deployed, skip all
487        let code = provider.get_code_at(MULTICALL3_ADDRESS).await?;
488        if code != Bytes::default() {
489            debug!(
490                "Multicall3 contract is already deployed at address {:?}",
491                MULTICALL3_ADDRESS
492            );
493            return Ok(());
494        }
495
496        // Fund Multicall3 deployer and deploy ERC1820Registry
497        let tx = TransactionRequest::default()
498            .with_to(*MULTICALL3_DEPLOYER_ADDRESS)
499            .with_value(U256::from(ETH_VALUE_FOR_MULTICALL3_DEPLOYER));
500
501        provider.send_transaction(tx).await?.watch().await?;
502
503        provider
504            .send_raw_transaction(MULTICALL3_DEPLOY_CODE.as_ref())
505            .await?
506            .watch()
507            .await?;
508        Ok(())
509    }
510
511    #[tokio::test]
512    async fn test_should_estimate_tx() -> anyhow::Result<()> {
513        let _ = env_logger::builder().is_test(true).try_init();
514
515        let expected_block_time = Duration::from_secs(1);
516        let anvil = hopr_chain_types::utils::create_anvil(Some(expected_block_time));
517        let chain_key_0 = ChainKeypair::from_secret(anvil.keys()[0].to_bytes().as_ref())?;
518
519        let mut server = mockito::Server::new_async().await;
520        let gas_oracle_mock = server.mock("GET", "/gas_oracle")
521            .with_status(200)
522            .with_body(r#"{"status":"1","message":"OK","result":{"LastBlock":"38791478","SafeGasPrice":"1.1","ProposeGasPrice":"1.1","FastGasPrice":"1.6","UsdPrice":"0.999985432689946"}}"#)
523            .expect(0)
524            .create_async()
525            .await;
526
527        let cfg = RpcOperationsConfig {
528            chain_id: anvil.chain_id(),
529            tx_polling_interval: Duration::from_millis(10),
530            expected_block_time,
531            finality: 2,
532            gas_oracle_url: Some((server.url() + "/gas_oracle").parse()?),
533            ..RpcOperationsConfig::default()
534        };
535
536        let transport_client = ReqwestTransport::new(anvil.endpoint_url());
537
538        let rpc_client = ClientBuilder::default()
539            .layer(RetryBackoffLayer::new(2, 100, 100))
540            .transport(transport_client.clone(), transport_client.guess_local());
541
542        // Wait until contracts deployments are final
543        sleep((1 + cfg.finality) * expected_block_time).await;
544
545        let rpc = RpcOperations::new(rpc_client, transport_client.client().clone(), &chain_key_0, cfg, None)?;
546
547        // call eth_gas_estimate
548        let fees = rpc.provider.estimate_eip1559_fees().await?;
549
550        assert!(
551            fees.max_priority_fee_per_gas.ge(&0_u128),
552            // fees.max_priority_fee_per_gas.ge(&100_000_000_u128),
553            "estimated_max_priority_fee must be equal or greater than 0, 0.1 gwei"
554        );
555
556        let estimated_gas_price = rpc.provider.get_gas_price().await?;
557        assert!(
558            estimated_gas_price.ge(&100_000_000_u128),
559            "estimated_max_fee must be greater than 0.1 gwei"
560        );
561
562        gas_oracle_mock.assert();
563
564        Ok(())
565    }
566
567    #[tokio::test]
568    async fn test_should_send_tx() -> anyhow::Result<()> {
569        let _ = env_logger::builder().is_test(true).try_init();
570
571        let expected_block_time = Duration::from_secs(1);
572        let anvil = hopr_chain_types::utils::create_anvil(Some(expected_block_time));
573        let chain_key_0 = ChainKeypair::from_secret(anvil.keys()[0].to_bytes().as_ref())?;
574
575        let cfg = RpcOperationsConfig {
576            chain_id: anvil.chain_id(),
577            tx_polling_interval: Duration::from_millis(10),
578            expected_block_time,
579            finality: 2,
580            gas_oracle_url: None,
581            ..RpcOperationsConfig::default()
582        };
583
584        let transport_client = ReqwestTransport::new(anvil.endpoint_url());
585
586        let rpc_client = ClientBuilder::default()
587            .layer(RetryBackoffLayer::new(2, 100, 100))
588            .transport(transport_client.clone(), transport_client.guess_local());
589
590        // Wait until contracts deployments are final
591        sleep((1 + cfg.finality) * expected_block_time).await;
592
593        let rpc = RpcOperations::new(rpc_client, transport_client.client().clone(), &chain_key_0, cfg, None)?;
594
595        let balance_1: XDaiBalance = rpc.get_xdai_balance((&chain_key_0).into()).await?;
596        assert!(balance_1.amount().gt(&0.into()), "balance must be greater than 0");
597
598        // Test 1: Send 1 ETH to some random address, do not wait for confirmation
599        let tx_1 = create_native_transfer::<Ethereum>(*RANDY, U256::from(1000000_u32));
600        let tx_hash = rpc.send_transaction(tx_1).await?;
601
602        wait_until_tx(tx_hash, Duration::from_secs(8)).await;
603
604        // Test 2: Send 1 ETH to some random address, wait for confirmation
605        let tx_2 = create_native_transfer::<Ethereum>(*RANDY, U256::from(1000000_u32));
606        let _receipt = rpc.send_transaction_with_confirm(tx_2).await?;
607
608        Ok(())
609    }
610
611    #[tokio::test]
612    async fn test_should_send_consecutive_txs() -> anyhow::Result<()> {
613        let _ = env_logger::builder().is_test(true).try_init();
614
615        let expected_block_time = Duration::from_secs(1);
616        let anvil = hopr_chain_types::utils::create_anvil(Some(expected_block_time));
617        let chain_key_0 = ChainKeypair::from_secret(anvil.keys()[0].to_bytes().as_ref())?;
618
619        let cfg = RpcOperationsConfig {
620            chain_id: anvil.chain_id(),
621            tx_polling_interval: Duration::from_millis(10),
622            expected_block_time,
623            finality: 2,
624            gas_oracle_url: None,
625            ..RpcOperationsConfig::default()
626        };
627
628        let transport_client = ReqwestTransport::new(anvil.endpoint_url());
629
630        let rpc_client = ClientBuilder::default()
631            .layer(RetryBackoffLayer::new(2, 100, 100))
632            .transport(transport_client.clone(), transport_client.guess_local());
633
634        // Wait until contracts deployments are final
635        sleep((1 + cfg.finality) * expected_block_time).await;
636
637        let rpc = RpcOperations::new(
638            rpc_client,
639            transport_client.client().clone(),
640            &chain_key_0,
641            cfg.clone(),
642            None,
643        )?;
644
645        let balance_1: XDaiBalance = rpc.get_xdai_balance((&chain_key_0).into()).await?;
646        assert!(balance_1.amount().gt(&0.into()), "balance must be greater than 0");
647
648        let txs_count = 5_u64;
649        let send_amount = 1000000_u64;
650
651        // Send 1 ETH to some random address
652        futures::future::join_all((0..txs_count).map(|_| async {
653            rpc.send_transaction(create_native_transfer::<Ethereum>(*RANDY, U256::from(send_amount)))
654                .await
655                .expect("tx should be sent")
656                .await
657                .expect("tx should resolve")
658        }))
659        .await;
660
661        sleep((1 + cfg.finality) * expected_block_time).await;
662
663        let balance_2: XDaiBalance = rpc.get_xdai_balance((&chain_key_0).into()).await?;
664
665        assert!(
666            balance_2.amount() <= balance_1.amount() - txs_count * send_amount,
667            "balance must be less"
668        );
669
670        Ok(())
671    }
672
673    #[tokio::test]
674    async fn test_get_balance_native() -> anyhow::Result<()> {
675        let _ = env_logger::builder().is_test(true).try_init();
676
677        let expected_block_time = Duration::from_secs(1);
678        let anvil = hopr_chain_types::utils::create_anvil(Some(expected_block_time));
679        let chain_key_0 = ChainKeypair::from_secret(anvil.keys()[0].to_bytes().as_ref())?;
680
681        let cfg = RpcOperationsConfig {
682            chain_id: anvil.chain_id(),
683            tx_polling_interval: Duration::from_millis(10),
684            expected_block_time,
685            finality: 2,
686            gas_oracle_url: None,
687            ..RpcOperationsConfig::default()
688        };
689
690        let transport_client = ReqwestTransport::new(anvil.endpoint_url());
691
692        let rpc_client = ClientBuilder::default()
693            .layer(RetryBackoffLayer::new(2, 100, 100))
694            .transport(transport_client.clone(), transport_client.guess_local());
695
696        // Wait until contracts deployments are final
697        sleep((1 + cfg.finality) * expected_block_time).await;
698
699        let rpc = RpcOperations::new(rpc_client, transport_client.client().clone(), &chain_key_0, cfg, None)?;
700
701        let balance_1: XDaiBalance = rpc.get_xdai_balance((&chain_key_0).into()).await?;
702        assert!(balance_1.amount().gt(&0.into()), "balance must be greater than 0");
703
704        // Send 1 ETH to some random address
705        let tx_hash = rpc
706            .send_transaction(create_native_transfer::<Ethereum>(*RANDY, U256::from(1_u32)))
707            .await?;
708
709        wait_until_tx(tx_hash, Duration::from_secs(8)).await;
710
711        let balance_2: XDaiBalance = rpc.get_xdai_balance((&chain_key_0).into()).await?;
712        assert!(balance_2.lt(&balance_1), "balance must be diminished");
713
714        Ok(())
715    }
716
717    #[tokio::test]
718    async fn test_get_balance_token() -> anyhow::Result<()> {
719        let _ = env_logger::builder().is_test(true).try_init();
720
721        let expected_block_time = Duration::from_secs(1);
722        let anvil = hopr_chain_types::utils::create_anvil(Some(expected_block_time));
723        let chain_key_0 = ChainKeypair::from_secret(anvil.keys()[0].to_bytes().as_ref())?;
724
725        // Deploy contracts
726        let contract_instances = {
727            let client = create_rpc_client_to_anvil(&anvil, &chain_key_0);
728            ContractInstances::deploy_for_testing(client, &chain_key_0).await?
729        };
730
731        let cfg = RpcOperationsConfig {
732            chain_id: anvil.chain_id(),
733            tx_polling_interval: Duration::from_millis(10),
734            expected_block_time,
735            finality: 2,
736            contract_addrs: ContractAddresses::from(&contract_instances),
737            gas_oracle_url: None,
738            ..RpcOperationsConfig::default()
739        };
740
741        let amount = 1024_u64;
742        let _ = hopr_chain_types::utils::mint_tokens(contract_instances.token, U256::from(amount)).await;
743
744        let transport_client = ReqwestTransport::new(anvil.endpoint_url());
745
746        let rpc_client = ClientBuilder::default()
747            .layer(RetryBackoffLayer::new(2, 100, 100))
748            .transport(transport_client.clone(), transport_client.guess_local());
749
750        // Wait until contracts deployments are final
751        sleep((1 + cfg.finality) * expected_block_time).await;
752
753        let rpc = RpcOperations::new(rpc_client, transport_client.client().clone(), &chain_key_0, cfg, None)?;
754
755        let balance: HoprBalance = rpc.get_hopr_balance((&chain_key_0).into()).await?;
756        assert_eq!(amount, balance.amount().as_u64(), "invalid balance");
757
758        Ok(())
759    }
760
761    #[tokio::test]
762    async fn test_check_node_safe_module_status() -> anyhow::Result<()> {
763        let _ = env_logger::builder().is_test(true).try_init();
764
765        let expected_block_time = Duration::from_secs(1);
766        let anvil = hopr_chain_types::utils::create_anvil(Some(expected_block_time));
767        let chain_key_0 = ChainKeypair::from_secret(anvil.keys()[0].to_bytes().as_ref())?;
768
769        // Deploy contracts
770        let (contract_instances, module, safe) = {
771            let client = create_rpc_client_to_anvil(&anvil, &chain_key_0);
772            let instances = ContractInstances::deploy_for_testing(client.clone(), &chain_key_0).await?;
773
774            // deploy MULTICALL contract to anvil
775            deploy_multicall3_to_anvil(&client.clone()).await?;
776
777            let (module, safe) = hopr_chain_types::utils::deploy_one_safe_one_module_and_setup_for_testing::<
778                Arc<AnvilRpcClient>,
779            >(&instances, client.clone(), &chain_key_0)
780            .await?;
781
782            // deploy a module and safe instance and add node into the module. The module is enabled by default in the
783            // safe
784            (instances, module, safe)
785        };
786
787        let cfg = RpcOperationsConfig {
788            chain_id: anvil.chain_id(),
789            tx_polling_interval: Duration::from_millis(10),
790            expected_block_time,
791            finality: 2,
792            contract_addrs: ContractAddresses::from(&contract_instances),
793            module_address: module,
794            safe_address: safe,
795            gas_oracle_url: None,
796            ..RpcOperationsConfig::default()
797        };
798
799        let transport_client = ReqwestTransport::new(anvil.endpoint_url());
800
801        let rpc_client = ClientBuilder::default()
802            .layer(RetryBackoffLayer::new(2, 100, 100))
803            .transport(transport_client.clone(), transport_client.guess_local());
804
805        // Wait until contracts deployments are final
806        sleep((1 + cfg.finality) * expected_block_time).await;
807
808        let rpc = RpcOperations::new(rpc_client, transport_client.client().clone(), &chain_key_0, cfg, None)?;
809
810        let result_before_including_node = rpc.check_node_safe_module_status((&chain_key_0).into()).await?;
811        // before including node to the safe and module, only the first chck is false, the others are true
812        assert!(
813            !result_before_including_node.is_node_included_in_module,
814            "node should not be included in a default module"
815        );
816        assert!(
817            result_before_including_node.is_module_enabled_in_safe,
818            "module should be enabled in a default safe"
819        );
820        assert!(
821            result_before_including_node.is_safe_owner_of_module,
822            "safe should not be the owner of a default module"
823        );
824
825        // including node to the module
826        hopr_chain_types::utils::include_node_to_module_by_safe(
827            contract_instances.channels.provider().clone(),
828            safe,
829            module,
830            (&chain_key_0).into(),
831            &chain_key_0,
832        )
833        .await?;
834
835        let result_with_node_included = rpc.check_node_safe_module_status((&chain_key_0).into()).await?;
836        // after the node gets included into the module, all checks should be true
837        assert!(
838            result_with_node_included.is_node_included_in_module,
839            "node should be included in a default module"
840        );
841        assert!(
842            result_with_node_included.is_module_enabled_in_safe,
843            "module should be enabled in a default safe"
844        );
845        assert!(
846            result_with_node_included.is_safe_owner_of_module,
847            "safe should be the owner of a default module"
848        );
849
850        Ok(())
851    }
852
853    #[tokio::test]
854    async fn test_get_eligibility_status() -> anyhow::Result<()> {
855        let _ = env_logger::builder().is_test(true).try_init();
856
857        let expected_block_time = Duration::from_secs(1);
858        let anvil = hopr_chain_types::utils::create_anvil(Some(expected_block_time));
859        let chain_key_0 = ChainKeypair::from_secret(anvil.keys()[0].to_bytes().as_ref())?;
860        let node_address: H160 = chain_key_0.public().to_address().into();
861
862        // Deploy contracts
863        let (contract_instances, module, safe) = {
864            let client = create_rpc_client_to_anvil(&anvil, &chain_key_0);
865            let instances = ContractInstances::deploy_for_testing(client.clone(), &chain_key_0).await?;
866
867            // deploy MULTICALL contract to anvil
868            deploy_multicall3_to_anvil(&client.clone()).await?;
869
870            let (module, safe) = hopr_chain_types::utils::deploy_one_safe_one_module_and_setup_for_testing::<
871                Arc<AnvilRpcClient>,
872            >(&instances, client.clone(), &chain_key_0)
873            .await?;
874
875            // deploy a module and safe instance and add node into the module. The module is enabled by default in the
876            // safe
877            (instances, module, safe)
878        };
879
880        let cfg = RpcOperationsConfig {
881            chain_id: anvil.chain_id(),
882            tx_polling_interval: Duration::from_millis(10),
883            expected_block_time,
884            finality: 2,
885            contract_addrs: ContractAddresses::from(&contract_instances),
886            module_address: module,
887            safe_address: safe,
888            gas_oracle_url: None,
889            ..RpcOperationsConfig::default()
890        };
891
892        let transport_client = ReqwestTransport::new(anvil.endpoint_url());
893
894        let rpc_client = ClientBuilder::default()
895            .layer(RetryBackoffLayer::new(2, 100, 100))
896            .transport(transport_client.clone(), transport_client.guess_local());
897
898        // Wait until contracts deployments are final
899        sleep((1 + cfg.finality) * expected_block_time).await;
900
901        let rpc = RpcOperations::new(
902            rpc_client,
903            transport_client.client().clone(),
904            &chain_key_0,
905            cfg.clone(),
906            None,
907        )?;
908
909        // check the eligibility status (before registering in the NetworkRegistry contract)
910        let result_before_register_in_the_network_registry = rpc.get_eligibility_status(node_address.into()).await?;
911
912        assert!(
913            !result_before_register_in_the_network_registry,
914            "node should not be eligible"
915        );
916
917        // register the node
918        match &rpc.contract_instances.network_registry_proxy {
919            NetworkRegistryProxy::Dummy(e) => {
920                let _ = e.ownerAddAccount(cfg.safe_address.into()).send().await?.watch().await?;
921            }
922            NetworkRegistryProxy::Safe(_) => {}
923        };
924
925        let _ = rpc
926            .contract_instances
927            .network_registry
928            .managerRegister(
929                vec![cfg.safe_address.into()],
930                vec![alloy::primitives::Address::from_slice(node_address.as_ref())],
931            )
932            .send()
933            .await?
934            .watch()
935            .await?;
936
937        // check the eligibility status (after registering in the NetworkRegistry contract)
938        let result_after_register_in_the_network_registry = rpc.get_eligibility_status(node_address.into()).await?;
939
940        assert!(result_after_register_in_the_network_registry, "node should be eligible");
941        Ok(())
942    }
943
944    #[tokio::test]
945    async fn test_get_eligibility_status_for_staking_proxy() -> anyhow::Result<()> {
946        let _ = env_logger::builder().is_test(true).try_init();
947
948        let expected_block_time = Duration::from_secs(1);
949        let anvil = hopr_chain_types::utils::create_anvil(Some(expected_block_time));
950        let chain_key_0 = ChainKeypair::from_secret(anvil.keys()[0].to_bytes().as_ref())?;
951        let node_address: H160 = chain_key_0.public().to_address().into();
952
953        // Deploy contracts
954        let (contract_instances, module, safe) = {
955            let client = create_rpc_client_to_anvil(&anvil, &chain_key_0);
956            let instances =
957                ContractInstances::deploy_for_testing_with_staking_proxy(client.clone(), &chain_key_0).await?;
958
959            // deploy MULTICALL contract to anvil
960            deploy_multicall3_to_anvil(&client.clone()).await?;
961
962            let (module, safe) = hopr_chain_types::utils::deploy_one_safe_one_module_and_setup_for_testing::<
963                Arc<AnvilRpcClient>,
964            >(&instances, client.clone(), &chain_key_0)
965            .await?;
966
967            // deploy a module and safe instance and add node into the module. The module is enabled by default in the
968            // safe
969            (instances, module, safe)
970        };
971
972        let cfg = RpcOperationsConfig {
973            chain_id: anvil.chain_id(),
974            tx_polling_interval: Duration::from_millis(10),
975            expected_block_time,
976            finality: 2,
977            contract_addrs: ContractAddresses::from(&contract_instances),
978            module_address: module,
979            safe_address: safe,
980            gas_oracle_url: None,
981            ..RpcOperationsConfig::default()
982        };
983
984        let transport_client = ReqwestTransport::new(anvil.endpoint_url());
985
986        let rpc_client = ClientBuilder::default()
987            .layer(RetryBackoffLayer::new(2, 100, 100))
988            .transport(transport_client.clone(), transport_client.guess_local());
989
990        // Wait until contracts deployments are final
991        sleep((1 + cfg.finality) * expected_block_time).await;
992
993        let rpc = RpcOperations::new(
994            rpc_client,
995            transport_client.client().clone(),
996            &chain_key_0,
997            cfg.clone(),
998            Some(false),
999        )?;
1000
1001        // check the eligibility status (before registering in the NetworkRegistry contract)
1002        let result_before_register_in_the_network_registry = rpc.get_eligibility_status(node_address.into()).await?;
1003
1004        assert!(
1005            !result_before_register_in_the_network_registry,
1006            "node should not be eligible"
1007        );
1008
1009        // register the node
1010        match &rpc.contract_instances.network_registry_proxy {
1011            NetworkRegistryProxy::Dummy(p) => {
1012                let _ = p.ownerAddAccount(cfg.safe_address.into()).send().await?.watch().await?;
1013            }
1014            NetworkRegistryProxy::Safe(_) => {}
1015        };
1016
1017        let _ = rpc
1018            .contract_instances
1019            .network_registry
1020            .managerRegister(
1021                vec![cfg.safe_address.into()],
1022                vec![alloy::primitives::Address::from_slice(node_address.as_ref())],
1023            )
1024            .send()
1025            .await?
1026            .watch()
1027            .await?;
1028
1029        // check the eligibility status (after registering in the NetworkRegistry contract)
1030        let result_after_register_in_the_network_registry = rpc.get_eligibility_status(node_address.into()).await?;
1031
1032        assert!(result_after_register_in_the_network_registry, "node should be eligible");
1033        Ok(())
1034    }
1035}