소스 검색

feat(argus): Delete hash chain logic (#2466)

* hash chain state

* delete unused struct
Jayant Krishnamurthy 8 달 전
부모
커밋
5a681d74bc

+ 0 - 3
apps/argus/src/api.rs

@@ -1,7 +1,6 @@
 use {
     crate::{
         chain::reader::{BlockNumber, BlockStatus, EntropyReader},
-        state::HashChainState,
     },
     anyhow::Result,
     axum::{
@@ -78,8 +77,6 @@ impl ApiState {
 pub struct BlockchainState {
     /// The chain id for this blockchain, useful for logging
     pub id: ChainId,
-    /// The hash chain(s) required to serve random numbers for this blockchain
-    pub state: Arc<HashChainState>,
     /// The contract that the server is fulfilling requests for.
     pub contract: Arc<dyn EntropyReader>,
     /// The address of the provider that this server is operating for.

+ 3 - 19
apps/argus/src/command/register_provider.rs

@@ -3,12 +3,10 @@ use {
         api::{get_register_uri, ChainId},
         chain::ethereum::SignablePythContract,
         config::{Config, EthereumConfig, ProviderConfig, RegisterProviderOptions},
-        state::PebbleHashChain,
     },
     anyhow::{anyhow, Result},
     ethers::{
         abi::Bytes,
-        signers::{LocalWallet, Signer},
         types::U256,
     },
     std::sync::Arc,
@@ -45,27 +43,13 @@ pub async fn register_provider_from_config(
         Arc::new(SignablePythContract::from_config(chain_config, &private_key_string).await?);
     // Create a new random hash chain.
     let random = rand::random::<[u8; 32]>();
-    let secret = provider_config
-        .secret
-        .load()?
-        .ok_or(anyhow!("Please specify a provider secret in the config"))?;
 
-    let commitment_length = provider_config.chain_length;
-    tracing::info!("Generating hash chain");
-    let chain = PebbleHashChain::from_config(
-        &secret,
-        chain_id,
-        &private_key_string.parse::<LocalWallet>()?.address(),
-        &chain_config.contract_addr,
-        &random,
-        commitment_length,
-        provider_config.chain_sample_interval,
-    )?;
-    tracing::info!("Done generating hash chain");
+    // FIXME: delete this
+    let commitment_length = 1000;
 
     // Arguments to the contract to register our new provider.
     let fee_in_wei = chain_config.fee;
-    let commitment = chain.reveal_ith(0)?;
+    let commitment = [0; 32];
     // Store the random seed and chain length in the metadata field so that we can regenerate the hash
     // chain at-will. (This is secure because you can't generate the chain unless you also have the secret)
     let commitment_metadata = CommitmentMetadata {

+ 1 - 82
apps/argus/src/command/run.rs

@@ -2,10 +2,8 @@ use {
     crate::{
         api::{self, BlockchainState, ChainId},
         chain::ethereum::InstrumentedPythContract,
-        command::register_provider::CommitmentMetadata,
-        config::{Commitment, Config, EthereumConfig, RunOptions},
+        config::{Config, EthereumConfig, RunOptions},
         keeper::{self, keeper_metrics::KeeperMetrics},
-        state::{HashChainState, PebbleHashChain},
     },
     fortuna::eth_utils::traced_client::{RpcMetrics, TracedClient},
     anyhow::{anyhow, Error, Result},
@@ -107,22 +105,16 @@ pub async fn run_keeper(
 
 pub async fn run(opts: &RunOptions) -> Result<()> {
     let config = Config::load(&opts.config.config)?;
-    let secret = config.provider.secret.load()?.ok_or(anyhow!(
-        "Please specify a provider secret in the config file."
-    ))?;
     let (tx_exit, rx_exit) = watch::channel(false);
     let metrics_registry = Arc::new(RwLock::new(Registry::default()));
     let rpc_metrics = Arc::new(RpcMetrics::new(metrics_registry.clone()).await);
 
     let mut tasks = Vec::new();
     for (chain_id, chain_config) in config.chains.clone() {
-        let secret_copy = secret.clone();
         let rpc_metrics = rpc_metrics.clone();
         tasks.push(spawn(async move {
             let state = setup_chain_state(
                 &config.provider.address,
-                &secret_copy,
-                config.provider.chain_sample_interval,
                 &chain_id,
                 &chain_config,
                 rpc_metrics,
@@ -189,8 +181,6 @@ pub async fn run(opts: &RunOptions) -> Result<()> {
 
 async fn setup_chain_state(
     provider: &Address,
-    secret: &str,
-    chain_sample_interval: u64,
     chain_id: &ChainId,
     chain_config: &EthereumConfig,
     rpc_metrics: Arc<RpcMetrics>,
@@ -200,80 +190,9 @@ async fn setup_chain_state(
         chain_id.clone(),
         rpc_metrics,
     )?);
-    let mut provider_commitments = chain_config.commitments.clone().unwrap_or_default();
-    provider_commitments.sort_by(|c1, c2| {
-        c1.original_commitment_sequence_number
-            .cmp(&c2.original_commitment_sequence_number)
-    });
-
-    let provider_info = contract.get_provider_info(*provider).call().await?;
-    let latest_metadata = bincode::deserialize::<CommitmentMetadata>(
-        &provider_info.commitment_metadata,
-    )
-    .map_err(|e| {
-        anyhow!(
-            "Chain: {} - Failed to deserialize commitment metadata: {}",
-            &chain_id,
-            e
-        )
-    })?;
-
-    let last_prior_commitment = provider_commitments.last();
-    if last_prior_commitment.is_some()
-        && last_prior_commitment
-            .unwrap()
-            .original_commitment_sequence_number
-            >= provider_info.original_commitment_sequence_number
-    {
-        return Err(anyhow!("The current hash chain for chain id {} has configured commitments for sequence numbers greater than the current on-chain sequence number. Are the commitments configured correctly?", &chain_id));
-    }
-
-    provider_commitments.push(Commitment {
-        seed: latest_metadata.seed,
-        chain_length: latest_metadata.chain_length,
-        original_commitment_sequence_number: provider_info.original_commitment_sequence_number,
-    });
-
-    // TODO: we may want to load the hash chain in a lazy/fault-tolerant way. If there are many blockchains,
-    // then it's more likely that some RPC fails. We should tolerate these faults and generate the hash chain
-    // later when a user request comes in for that chain.
-
-    let mut offsets = Vec::<usize>::new();
-    let mut hash_chains = Vec::<PebbleHashChain>::new();
-
-    for commitment in &provider_commitments {
-        let offset = commitment.original_commitment_sequence_number.try_into()?;
-        offsets.push(offset);
-
-        let pebble_hash_chain = PebbleHashChain::from_config(
-            secret,
-            chain_id,
-            provider,
-            &chain_config.contract_addr,
-            &commitment.seed,
-            commitment.chain_length,
-            chain_sample_interval,
-        )
-        .map_err(|e| anyhow!("Failed to create hash chain: {}", e))?;
-        hash_chains.push(pebble_hash_chain);
-    }
-
-    let chain_state = HashChainState {
-        offsets,
-        hash_chains,
-    };
-
-    if chain_state.reveal(provider_info.original_commitment_sequence_number)?
-        != provider_info.original_commitment
-    {
-        return Err(anyhow!("The root of the generated hash chain for chain id {} does not match the commitment. Are the secret and chain length configured correctly?", &chain_id));
-    } else {
-        tracing::info!("Root of chain id {} matches commitment", &chain_id);
-    }
 
     let state = BlockchainState {
         id: chain_id.clone(),
-        state: Arc::new(chain_state),
         contract,
         provider_address: *provider,
         reveal_delay_blocks: chain_config.reveal_delay_blocks,

+ 6 - 70
apps/argus/src/command/setup_provider.rs

@@ -2,9 +2,8 @@ use {
     crate::{
         api::{get_register_uri, ChainId},
         chain::ethereum::{ProviderInfo, SignablePythContract},
-        command::register_provider::{register_provider_from_config, CommitmentMetadata},
+        command::register_provider::register_provider_from_config,
         config::{Config, EthereumConfig, SetupProviderOptions},
-        state::{HashChainState, PebbleHashChain},
     },
     anyhow::{anyhow, Result},
     ethers::{
@@ -56,8 +55,6 @@ pub async fn setup_provider(opts: &SetupProviderOptions) -> Result<()> {
 
 /// Setup provider for a single chain.
 /// 1. Register if there was no previous registration.
-/// 2. Re-register if there are no more random numbers to request on the contract.
-/// 3. Re-register if there is a mismatch in generated hash chain.
 /// 4. Update provider fee if there is a mismatch with the fee set on contract.
 /// 5. Update provider uri if there is a mismatch with the uri set on contract.
 #[tracing::instrument(name = "setup_chain_provider", skip_all, fields(chain_id = chain_id))]
@@ -79,72 +76,11 @@ async fn setup_chain_provider(
     let provider_info = contract.get_provider_info(provider_address).call().await?;
     tracing::info!("Provider info: {:?}", provider_info);
 
-    let mut register = false;
-
-    // This condition satisfies for both when there is no registration and when there are no
-    // more random numbers left to request
-    if provider_info.end_sequence_number <= provider_info.sequence_number {
-        tracing::info!(
-            "endSequenceNumber <= sequenceNumber. endSequenceNumber={}, sequenceNumber={}",
-            provider_info.end_sequence_number,
-            provider_info.sequence_number
-        );
-        register = true;
-    } else {
-        let metadata =
-            bincode::deserialize::<CommitmentMetadata>(&provider_info.commitment_metadata)
-                .map_err(|e| {
-                    anyhow!(
-                        "Chain: {} - Failed to deserialize commitment metadata: {}",
-                        &chain_id,
-                        e
-                    )
-                })?;
-
-        let secret = provider_config.secret.load()?.ok_or(anyhow!(
-            "Please specify a provider secret in the config file."
-        ))?;
-        if metadata.chain_length != provider_config.chain_length {
-            tracing::info!(
-                "Chain length mismatch. metadata.chain_length={}, provider_config.chain_length={}",
-                metadata.chain_length,
-                provider_config.chain_length
-            );
-            register = true;
-        } else {
-            let hash_chain = PebbleHashChain::from_config(
-                &secret,
-                chain_id,
-                &provider_address,
-                &chain_config.contract_addr,
-                &metadata.seed,
-                provider_config.chain_length,
-                provider_config.chain_sample_interval,
-            )?;
-            let chain_state = HashChainState {
-                offsets: vec![provider_info
-                    .original_commitment_sequence_number
-                    .try_into()?],
-                hash_chains: vec![hash_chain],
-            };
-
-            if chain_state.reveal(provider_info.original_commitment_sequence_number)?
-                != provider_info.original_commitment
-            {
-                tracing::info!(
-                    "The root of the generated hash chain does not match the commitment",
-                );
-                register = true;
-            }
-        }
-    }
-    if register {
-        tracing::info!("Registering");
-        register_provider_from_config(provider_config, chain_id, chain_config)
-            .await
-            .map_err(|e| anyhow!("Chain: {} - Failed to register provider: {}", &chain_id, e))?;
-        tracing::info!("Registered");
-    }
+    tracing::info!("Registering");
+    register_provider_from_config(provider_config, chain_id, chain_config)
+        .await
+        .map_err(|e| anyhow!("Chain: {} - Failed to register provider: {}", &chain_id, e))?;
+    tracing::info!("Registered");
 
     let provider_info = contract.get_provider_info(provider_address).call().await?;
 

+ 0 - 30
apps/argus/src/config.rs

@@ -172,9 +172,6 @@ pub struct EthereumConfig {
     #[serde(default)]
     pub fee: u128,
 
-    /// Historical commitments made by the provider.
-    pub commitments: Option<Vec<Commitment>>,
-
     /// Maximum number of hashes to record in a request.
     /// This should be set according to the maximum gas limit the provider supports for callbacks.
     pub max_num_hashes: Option<u32>,
@@ -272,17 +269,6 @@ impl EscalationPolicyConfig {
     }
 }
 
-/// A commitment that the provider used to generate random numbers at some point in the past.
-/// These historical commitments need to be stored in the configuration to support transition points where
-/// the commitment changes. In theory, this information is stored on the blockchain, but unfortunately it
-/// is hard to retrieve from there.
-#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
-pub struct Commitment {
-    pub seed: [u8; 32],
-    pub chain_length: u64,
-    pub original_commitment_sequence_number: u64,
-}
-
 /// Configuration values that are common to a single provider (and shared across chains).
 #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
 pub struct ProviderConfig {
@@ -298,27 +284,11 @@ pub struct ProviderConfig {
     /// the private key (e.g., running the server).
     pub private_key: SecretString,
 
-    /// The provider's secret which is a 64-char hex string.
-    /// The secret is used for generating new hash chains
-    pub secret: SecretString,
-
-    /// The length of the hash chain to generate.
-    pub chain_length: u64,
-
-    /// How frequently the hash chain is sampled -- increase this value to tradeoff more
-    /// compute per request for less RAM use.
-    #[serde(default = "default_chain_sample_interval")]
-    pub chain_sample_interval: u64,
-
     /// The address of the fee manager for the provider. Set this value to the keeper wallet address to
     /// enable keeper balance top-ups.
     pub fee_manager: Option<Address>,
 }
 
-fn default_chain_sample_interval() -> u64 {
-    1
-}
-
 /// Configuration values for the keeper service that are shared across chains.
 #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
 pub struct KeeperConfig {

+ 0 - 4
apps/argus/src/keeper.rs

@@ -7,7 +7,6 @@ use {
             get_latest_safe_block, process_backlog, process_new_blocks, watch_blocks_wrapper,
             BlockRange,
         },
-        keeper::commitment::update_commitments_loop,
         keeper::fee::adjust_fee_wrapper,
         keeper::fee::withdraw_fees_wrapper,
         keeper::track::track_accrued_pyth_fees,
@@ -27,7 +26,6 @@ use {
 };
 
 pub(crate) mod block;
-pub(crate) mod commitment;
 pub(crate) mod fee;
 pub(crate) mod keeper_metrics;
 pub(crate) mod process_event;
@@ -166,8 +164,6 @@ pub async fn run_keeper_threads(
         .in_current_span(),
     );
 
-    spawn(update_commitments_loop(contract.clone(), chain_state.clone()).in_current_span());
-
     // Spawn a thread to track the provider info and the balance of the keeper
     spawn(
         async move {

+ 0 - 64
apps/argus/src/keeper/commitment.rs

@@ -1,64 +0,0 @@
-use {
-    crate::{
-        api::BlockchainState, chain::ethereum::InstrumentedSignablePythContract,
-        keeper::block::get_latest_safe_block,
-    },
-    fortuna::eth_utils::utils::send_and_confirm,
-    anyhow::{anyhow, Result},
-    std::sync::Arc,
-    tokio::time::{self, Duration},
-    tracing::{self, Instrument},
-};
-
-/// Check whether we need to manually update the commitments to reduce numHashes for future
-/// requests and reduce the gas cost of the reveal.
-const UPDATE_COMMITMENTS_INTERVAL: Duration = Duration::from_secs(30);
-const UPDATE_COMMITMENTS_THRESHOLD_FACTOR: f64 = 0.95;
-
-#[tracing::instrument(name = "update_commitments", skip_all)]
-pub async fn update_commitments_loop(
-    contract: Arc<InstrumentedSignablePythContract>,
-    chain_state: BlockchainState,
-) {
-    loop {
-        if let Err(e) = update_commitments_if_necessary(contract.clone(), &chain_state)
-            .in_current_span()
-            .await
-        {
-            tracing::error!("Update commitments. error: {:?}", e);
-        }
-        time::sleep(UPDATE_COMMITMENTS_INTERVAL).await;
-    }
-}
-
-pub async fn update_commitments_if_necessary(
-    contract: Arc<InstrumentedSignablePythContract>,
-    chain_state: &BlockchainState,
-) -> Result<()> {
-    //TODO: we can reuse the result from the last call from the watch_blocks thread to reduce RPCs
-    let latest_safe_block = get_latest_safe_block(chain_state).in_current_span().await;
-    let provider_address = chain_state.provider_address;
-    let provider_info = contract
-        .get_provider_info(provider_address)
-        .block(latest_safe_block) // To ensure we are not revealing sooner than we should
-        .call()
-        .await
-        .map_err(|e| anyhow!("Error while getting provider info. error: {:?}", e))?;
-    if provider_info.max_num_hashes == 0 {
-        return Ok(());
-    }
-    let threshold =
-        ((provider_info.max_num_hashes as f64) * UPDATE_COMMITMENTS_THRESHOLD_FACTOR) as u64;
-    if provider_info.sequence_number - provider_info.current_commitment_sequence_number > threshold
-    {
-        let seq_number = provider_info.sequence_number - 1;
-        let provider_revelation = chain_state
-            .state
-            .reveal(seq_number)
-            .map_err(|e| anyhow!("Error revealing: {:?}", e))?;
-        let contract_call =
-            contract.advance_provider_commitment(provider_address, seq_number, provider_revelation);
-        send_and_confirm(contract_call).await?;
-    }
-    Ok(())
-}

+ 2 - 5
apps/argus/src/keeper/process_event.rs

@@ -5,7 +5,7 @@ use {
         chain::{ethereum::InstrumentedSignablePythContract, reader::RequestedWithCallbackEvent},
     },
     fortuna::eth_utils::utils::{submit_tx_with_backoff, EscalationPolicy},
-    anyhow::{anyhow, Result},
+    anyhow::Result,
     ethers::types::U256,
     std::sync::Arc,
     tracing,
@@ -36,10 +36,7 @@ pub async fn process_event_with_backoff(
     metrics.requests.get_or_create(&account_label).inc();
     tracing::info!("Started processing event");
 
-    let provider_revelation = chain_state
-        .state
-        .reveal(event.sequence_number)
-        .map_err(|e| anyhow!("Error revealing: {:?}", e))?;
+    let provider_revelation = [0; 32];
 
     let contract_call = contract.reveal_with_callback(
         event.provider_address,

+ 0 - 1
apps/argus/src/lib.rs

@@ -3,4 +3,3 @@ pub mod chain;
 pub mod command;
 pub mod config;
 pub mod keeper;
-pub mod state;

+ 0 - 172
apps/argus/src/state.rs

@@ -1,172 +0,0 @@
-use {
-    crate::api::ChainId,
-    anyhow::{ensure, Result},
-    ethers::types::Address,
-    sha3::{Digest, Keccak256},
-};
-
-/// A hash chain of a specific length. The hash chain has the property that
-/// hash(chain.reveal_ith(i)) == chain.reveal_ith(i - 1)
-///
-/// The implementation subsamples the elements of the chain such that it uses less memory
-/// to keep the chain around.
-#[derive(Clone)]
-pub struct PebbleHashChain {
-    hash: Vec<[u8; 32]>,
-    sample_interval: usize,
-    length: usize,
-}
-
-impl PebbleHashChain {
-    // Given a secret, we hash it with Keccak256 len times to get the final hash, this is an S/KEY
-    // like protocol in which revealing the hashes in reverse proves knowledge.
-    pub fn new(secret: [u8; 32], length: usize, sample_interval: usize) -> Self {
-        assert!(sample_interval > 0, "Sample interval must be positive");
-        let mut hash = Vec::<[u8; 32]>::with_capacity(length);
-        let mut current: [u8; 32] = Keccak256::digest(secret).into();
-
-        hash.push(current);
-        for i in 1..length {
-            current = Keccak256::digest(current).into();
-            if i % sample_interval == 0 {
-                hash.push(current);
-            }
-        }
-
-        hash.reverse();
-
-        Self {
-            hash,
-            sample_interval,
-            length,
-        }
-    }
-
-    pub fn from_config(
-        secret: &str,
-        chain_id: &ChainId,
-        provider_address: &Address,
-        contract_address: &Address,
-        random: &[u8; 32],
-        chain_length: u64,
-        sample_interval: u64,
-    ) -> Result<Self> {
-        let mut input: Vec<u8> = vec![];
-        input.extend_from_slice(&hex::decode(secret.trim())?);
-        input.extend_from_slice(chain_id.as_bytes());
-        input.extend_from_slice(provider_address.as_bytes());
-        input.extend_from_slice(contract_address.as_bytes());
-        input.extend_from_slice(random);
-
-        let secret: [u8; 32] = Keccak256::digest(input).into();
-        Ok(Self::new(
-            secret,
-            chain_length.try_into()?,
-            sample_interval.try_into()?,
-        ))
-    }
-
-    pub fn reveal_ith(&self, i: usize) -> Result<[u8; 32]> {
-        ensure!(i < self.len(), "index not in range");
-
-        // Note that subsample_interval may not perfectly divide length, in which case the uneven segment is
-        // actually at the *front* of the list. Thus, it's easier to compute indexes from the end of the list.
-        let index_from_end_of_subsampled_list = ((self.len() - 1) - i) / self.sample_interval;
-        let mut i_index = self.len() - 1 - index_from_end_of_subsampled_list * self.sample_interval;
-        let mut val = self.hash[self.hash.len() - 1 - index_from_end_of_subsampled_list];
-
-        while i_index > i {
-            val = Keccak256::digest(val).into();
-            i_index -= 1;
-        }
-
-        Ok(val)
-    }
-
-    #[allow(clippy::len_without_is_empty)]
-    pub fn len(&self) -> usize {
-        self.length
-    }
-}
-
-/// `HashChainState` tracks the mapping between on-chain sequence numbers to hash chains.
-/// This struct is required to handle the case where the provider rotates their commitment,
-/// which requires tracking multiple hash chains here.
-pub struct HashChainState {
-    // The sequence number where the hash chain starts. Must be stored in sorted order.
-    pub offsets: Vec<usize>,
-    pub hash_chains: Vec<PebbleHashChain>,
-}
-
-impl HashChainState {
-    pub fn from_chain_at_offset(offset: usize, chain: PebbleHashChain) -> HashChainState {
-        HashChainState {
-            offsets: vec![offset],
-            hash_chains: vec![chain],
-        }
-    }
-
-    pub fn reveal(&self, sequence_number: u64) -> Result<[u8; 32]> {
-        let sequence_number: usize = sequence_number.try_into()?;
-        let chain_index = self
-            .offsets
-            .partition_point(|x| x <= &sequence_number)
-            .checked_sub(1)
-            .ok_or(anyhow::anyhow!(
-                "Hash chain for the requested sequence number is not available."
-            ))?;
-        self.hash_chains[chain_index].reveal_ith(sequence_number - self.offsets[chain_index])
-    }
-}
-
-#[cfg(test)]
-mod test {
-    use {
-        crate::state::PebbleHashChain,
-        sha3::{Digest, Keccak256},
-    };
-
-    fn run_hash_chain_test(secret: [u8; 32], length: usize, sample_interval: usize) {
-        // Calculate the hash chain the naive way as a comparison point to the subsampled implementation.
-        let mut basic_chain = Vec::<[u8; 32]>::with_capacity(length);
-        let mut current: [u8; 32] = Keccak256::digest(secret).into();
-        basic_chain.push(current);
-        for _ in 1..length {
-            current = Keccak256::digest(current).into();
-            basic_chain.push(current);
-        }
-
-        basic_chain.reverse();
-
-        let chain = PebbleHashChain::new(secret, length, sample_interval);
-
-        let mut last_val = chain.reveal_ith(0).unwrap();
-
-        #[allow(clippy::needless_range_loop)]
-        for i in 1..length {
-            let cur_val = chain.reveal_ith(i).unwrap();
-            println!("{}", i);
-            assert_eq!(basic_chain[i], cur_val);
-
-            let expected_last_val: [u8; 32] = Keccak256::digest(cur_val).into();
-            assert_eq!(expected_last_val, last_val);
-            last_val = cur_val;
-        }
-    }
-
-    #[test]
-    fn test_hash_chain() {
-        run_hash_chain_test([0u8; 32], 10, 1);
-        run_hash_chain_test([0u8; 32], 10, 2);
-        run_hash_chain_test([0u8; 32], 10, 3);
-        run_hash_chain_test([1u8; 32], 10, 1);
-        run_hash_chain_test([1u8; 32], 10, 2);
-        run_hash_chain_test([1u8; 32], 10, 3);
-        run_hash_chain_test([0u8; 32], 100, 1);
-        run_hash_chain_test([0u8; 32], 100, 2);
-        run_hash_chain_test([0u8; 32], 100, 3);
-        run_hash_chain_test([0u8; 32], 100, 7);
-        run_hash_chain_test([0u8; 32], 100, 50);
-        run_hash_chain_test([0u8; 32], 100, 55);
-    }
-}