Skip to content

Commit

Permalink
refactor: move common utilities into indexer-common crate
Browse files Browse the repository at this point in the history
  • Loading branch information
Jannis committed Sep 20, 2023
1 parent cc960b5 commit ec054bc
Show file tree
Hide file tree
Showing 27 changed files with 980 additions and 743 deletions.
675 changes: 371 additions & 304 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
[workspace]
members = [
"native",
"common",
"service",
# "common",
]
resolver = "2"

Expand Down
32 changes: 32 additions & 0 deletions common/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
[package]
name = "indexer-common"
version = "0.1.0"
edition = "2021"

[dependencies]
alloy-primitives = { version = "0.3.3", features = ["serde"] }
anyhow = "1.0.75"
arc-swap = "1.6.0"
bs58 = "0.5.0"
eip-712-derive = { git = "https://github.com/graphprotocol/eip-712-derive" }
ethereum-types = "0.14.1"
ethers = "2.0.10"
ethers-core = "2.0.10"
faux = { version = "0.1.10", optional = true }
keccak-hash = "0.10.0"
lazy_static = "1.4.0"
log = "0.4.20"
reqwest = "0.11.20"
secp256k1 = { version = "0.27.0", features = ["recovery"] }
serde = { version = "1.0.188", features = ["derive"] }
serde_json = "1.0.107"
tokio = { version = "1.32.0", features = ["full", "macros", "rt"] }

[dev-dependencies]
env_logger = "0.9.0"
faux = "0.1.10"
test-log = "0.2.12"
wiremock = "0.5.19"

[features]
mock = ["dep:faux"]
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
use alloy_primitives::Address;
use anyhow::Result;
use ethers::signers::coins_bip39::English;
use ethers::signers::MnemonicBuilder;
use ethers::signers::Signer;
use ethers::signers::Wallet;
use ethers::signers::{MnemonicBuilder, Signer, Wallet};
use ethers_core::k256::ecdsa::SigningKey;
use ethers_core::types::U256;
use serde::Deserialize;
use serde::Deserializer;

use crate::common::types::SubgraphDeploymentID;
use crate::types::SubgraphDeploymentID;

pub mod monitor;

#[derive(Debug, Eq, PartialEq)]
pub struct Allocation {
Expand Down Expand Up @@ -148,8 +148,9 @@ pub fn allocation_signer(indexer_mnemonic: &str, allocation: &Allocation) -> Res
mod test {
use std::str::FromStr;

use crate::prelude::SubgraphDeploymentID;

use super::*;
use crate::common::types::SubgraphDeploymentID;

const INDEXER_OPERATOR_MNEMONIC: &str = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use log::{info, warn};
use tokio::sync::watch::{Receiver, Sender};
use tokio::sync::RwLock;

use crate::{common::allocation::Allocation, common::network_subgraph::NetworkSubgraph};
use crate::prelude::{Allocation, NetworkSubgraph};

#[derive(Debug)]
struct AllocationMonitorInner {
Expand All @@ -23,14 +23,14 @@ struct AllocationMonitorInner {
watch_receiver: Receiver<()>,
}

#[cfg_attr(test, faux::create)]
#[cfg_attr(any(test, feature = "mock"), faux::create)]
#[derive(Debug, Clone)]
pub struct AllocationMonitor {
_monitor_handle: Arc<tokio::task::JoinHandle<()>>,
inner: Arc<AllocationMonitorInner>,
}

#[cfg_attr(test, faux::methods)]
#[cfg_attr(any(test, feature = "mock"), faux::methods)]
impl AllocationMonitor {
pub async fn new(
network_subgraph: NetworkSubgraph,
Expand Down Expand Up @@ -79,18 +79,15 @@ impl AllocationMonitor {
.to_string(),
Some(serde_json::json!({ "id": graph_network_id })),
)
.await?;

let res_json: serde_json::Value = serde_json::from_str(res.graphql_response.as_str())
.await
.map_err(|e| {
anyhow::anyhow!(
"Failed to parse current epoch response from network subgraph: {}",
e
)
})?;

res_json
.get("data")
res.get("data")
.and_then(|d| d.get("graphNetwork"))
.and_then(|d| d.get("currentEpoch"))
.and_then(|d| d.as_u64())
Expand All @@ -104,7 +101,7 @@ impl AllocationMonitor {
indexer_address: &Address,
closed_at_epoch_threshold: u64,
) -> Result<HashMap<Address, Allocation>> {
let res = network_subgraph
let mut res = network_subgraph
.network_query(
r#"
query allocations($indexer: ID!, $closedAtEpochThreshold: Int!) {
Expand Down Expand Up @@ -157,17 +154,15 @@ impl AllocationMonitor {
.to_string(),
Some(serde_json::json!({ "indexer": indexer_address, "closedAtEpochThreshold": closed_at_epoch_threshold })),
)
.await;

let mut res_json: serde_json::Value = serde_json::from_str(res?.graphql_response.as_str())
.await
.map_err(|e| {
anyhow::anyhow!(
"Failed to fetch current allocations from network subgraph: {}",
e
)
})?;

let indexer_json = res_json
let indexer_json = res
.get_mut("data")
.and_then(|d| d.get_mut("indexer"))
.ok_or_else(|| anyhow::anyhow!("No data / indexer not found on chain",))?;
Expand Down Expand Up @@ -286,7 +281,7 @@ mod tests {
use wiremock::matchers::{method, path};
use wiremock::{Mock, MockServer, ResponseTemplate};

use crate::common::network_subgraph::NetworkSubgraph;
use crate::prelude::NetworkSubgraph;
use crate::test_vectors;

use super::*;
Expand Down
188 changes: 188 additions & 0 deletions common/src/attestations/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
// Copyright 2023-, GraphOps and Semiotic Labs.
// SPDX-License-Identifier: Apache-2.0

use anyhow::Result;
use ethers::signers::coins_bip39::English;
use ethers::signers::MnemonicBuilder;
use ethers::signers::Signer;
use ethers::signers::Wallet;
use ethers_core::k256::ecdsa::SigningKey;

use crate::prelude::{Allocation, SubgraphDeploymentID};

pub mod signer;
pub mod signers;

pub fn derive_key_pair(
indexer_mnemonic: &str,
epoch: u64,
deployment: &SubgraphDeploymentID,
index: u64,
) -> Result<Wallet<SigningKey>> {
let mut derivation_path = format!("m/{}/", epoch);
derivation_path.push_str(
&deployment
.ipfs_hash()
.as_bytes()
.iter()
.map(|char| char.to_string())
.collect::<Vec<String>>()
.join("/"),
);
derivation_path.push_str(format!("/{}", index).as_str());

Ok(MnemonicBuilder::<English>::default()
.derivation_path(&derivation_path)
.expect("Valid derivation path")
.phrase(indexer_mnemonic)
.build()?)
}

pub fn attestation_signer_for_allocation(
indexer_mnemonic: &str,
allocation: &Allocation,
) -> Result<SigningKey> {
// Guess the allocation index by enumerating all indexes in the
// range [0, 100] and checking for a match
for i in 0..100 {
// The allocation was either created at the epoch it intended to or one
// epoch later. So try both both.
for created_at_epoch in [allocation.created_at_epoch, allocation.created_at_epoch - 1] {
let allocation_wallet = derive_key_pair(
indexer_mnemonic,
created_at_epoch,
&allocation.subgraph_deployment.id,
i,
)?;
if allocation_wallet.address().as_fixed_bytes() == allocation.id {
return Ok(allocation_wallet.signer().clone());
}
}
}
Err(anyhow::anyhow!(
"Could not find allocation signer for allocation {}",
allocation.id
))
}

#[cfg(test)]
mod tests {
use alloy_primitives::Address;
use ethers_core::types::U256;
use std::str::FromStr;
use test_log::test;

use crate::prelude::{Allocation, AllocationStatus, SubgraphDeployment, SubgraphDeploymentID};

use super::*;

const INDEXER_OPERATOR_MNEMONIC: &str = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";

#[test]
fn test_derive_key_pair() {
assert_eq!(
derive_key_pair(
INDEXER_OPERATOR_MNEMONIC,
953,
&SubgraphDeploymentID::new(
"0xbbde25a2c85f55b53b7698b9476610c3d1202d88870e66502ab0076b7218f98a"
)
.unwrap(),
0
)
.unwrap()
.address()
.as_fixed_bytes(),
Address::from_str("0xfa44c72b753a66591f241c7dc04e8178c30e13af").unwrap()
);

assert_eq!(
derive_key_pair(
INDEXER_OPERATOR_MNEMONIC,
940,
&SubgraphDeploymentID::new(
"0xbbde25a2c85f55b53b7698b9476610c3d1202d88870e66502ab0076b7218f98a"
)
.unwrap(),
2
)
.unwrap()
.address()
.as_fixed_bytes(),
Address::from_str("0xa171cd12c3dde7eb8fe7717a0bcd06f3ffa65658").unwrap()
);
}

#[test]
fn test_allocation_signer() {
// Note that we use `derive_key_pair` to derive the private key

let allocation = Allocation {
id: Address::from_str("0xa171cd12c3dde7eb8fe7717a0bcd06f3ffa65658").unwrap(),
status: AllocationStatus::Null,
subgraph_deployment: SubgraphDeployment {
id: SubgraphDeploymentID::new(
"0xbbde25a2c85f55b53b7698b9476610c3d1202d88870e66502ab0076b7218f98a",
)
.unwrap(),
denied_at: None,
staked_tokens: U256::zero(),
signalled_tokens: U256::zero(),
query_fees_amount: U256::zero(),
},
indexer: Address::ZERO,
allocated_tokens: U256::zero(),
created_at_epoch: 940,
created_at_block_hash: "".to_string(),
closed_at_epoch: None,
closed_at_epoch_start_block_hash: None,
previous_epoch_start_block_hash: None,
poi: None,
query_fee_rebates: None,
query_fees_collected: None,
};
assert_eq!(
attestation_signer_for_allocation(INDEXER_OPERATOR_MNEMONIC, &allocation).unwrap(),
*derive_key_pair(
INDEXER_OPERATOR_MNEMONIC,
940,
&allocation.subgraph_deployment.id,
2
)
.unwrap()
.signer()
);
}

#[test]
fn test_allocation_signer_error() {
// Note that because allocation will try 200 derivations paths, this is a slow test

let allocation = Allocation {
// Purposefully wrong address
id: Address::from_str("0xdeadbeefcafebabedeadbeefcafebabedeadbeef").unwrap(),
status: AllocationStatus::Null,
subgraph_deployment: SubgraphDeployment {
id: SubgraphDeploymentID::new(
"0xbbde25a2c85f55b53b7698b9476610c3d1202d88870e66502ab0076b7218f98a",
)
.unwrap(),
denied_at: None,
staked_tokens: U256::zero(),
signalled_tokens: U256::zero(),
query_fees_amount: U256::zero(),
},
indexer: Address::ZERO,
allocated_tokens: U256::zero(),
created_at_epoch: 940,
created_at_block_hash: "".to_string(),
closed_at_epoch: None,
closed_at_epoch_start_block_hash: None,
previous_epoch_start_block_hash: None,
poi: None,
query_fee_rebates: None,
query_fees_collected: None,
};
assert!(attestation_signer_for_allocation(INDEXER_OPERATOR_MNEMONIC, &allocation).is_err());
}
}
Loading

0 comments on commit ec054bc

Please sign in to comment.