Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(bens): Added TLD-based fallback for name search in networks #1175

Open
wants to merge 23 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
c661327
updated function with serch by picking 5 names+tld
Ilyak777 Jan 6, 2025
f38fd92
deleted unnecessary empty lines
Ilyak777 Jan 6, 2025
a113537
Merge branch 'main' into feat/bens/add-tld-for-name-in-network-search
Ilyak777 Jan 6, 2025
e4376da
fixed spelling problems
Ilyak777 Jan 6, 2025
67cf6d7
Merge branch 'feat/bens/add-tld-for-name-in-network-search' of github…
Ilyak777 Jan 6, 2025
a2b3fed
used fmt to solve spelling issues
Ilyak777 Jan 6, 2025
99b159b
updated core function
Ilyak777 Jan 16, 2025
213c5ff
fixed spellings with rust rules
Ilyak777 Jan 16, 2025
4a7da33
updated code with clippy suggestions
Ilyak777 Jan 16, 2025
9e2e224
added first test cases for tld function
Ilyak777 Jan 16, 2025
34a79d7
added printsLn to capture moment where app crashes
Ilyak777 Jan 22, 2025
5e0e2ad
added new func to extract the exact domain
Ilyak777 Jan 23, 2025
46d13c1
updated after fmt command
Ilyak777 Jan 23, 2025
a9d0009
updated mock data
Ilyak777 Jan 23, 2025
3f746a5
added map_err and fixed with fmt
Ilyak777 Jan 23, 2025
5d5755b
removed Rabbit suggestion with mapping error
Ilyak777 Jan 23, 2025
57c745d
deleted all pintLn from protocoler and domain
Ilyak777 Jan 23, 2025
6c41165
returned variables to readme
Ilyak777 Jan 23, 2025
5b461ba
Merge branch 'main' into feat/bens/add-tld-for-name-in-network-search
Ilyak777 Jan 23, 2025
9bfeb5d
Update blockscout-ens/bens-logic/src/protocols/protocoler.rs
Ilyak777 Jan 24, 2025
506c310
updated due to comments in review
Ilyak777 Jan 27, 2025
64213c9
pulled main into this branch
Ilyak777 Jan 27, 2025
062a4ec
added abcnews with another domain to seed file and added prints
Ilyak777 Feb 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 48 additions & 45 deletions blockscout-ens/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,47 +10,50 @@ Service is **multi-chain**, meaning that only one instance of `graph-node`, `pos

## Current supported domains

| Subgraph Name | Network | TLD | Note |
|--------------|---------|-----|------|
| ens-subgraph | Ethereum | .eth | |
| rns-subgraph | Rootstock | .rsk | |
| genome-subgraph | Gnosis | .gno | SpaceID contracts |
| bns-subgraph | Base | .base | |
| mode-subgraph | Mode | .mode | SpaceID contracts |
| lightlink-subgraph | Lightlink | .ll | SpaceID contracts |
| zns-subgraph | Polygon | .poly | |
| d3-connect-subgraph | Shibarium | .shib | |

| Subgraph Name | Network | TLD | Note |
| ------------------- | --------- | ----- | ----------------- |
| ens-subgraph | Ethereum | .eth | |
| rns-subgraph | Rootstock | .rsk | |
| genome-subgraph | Gnosis | .gno | SpaceID contracts |
| bns-subgraph | Base | .base | |
| mode-subgraph | Mode | .mode | SpaceID contracts |
| lightlink-subgraph | Lightlink | .ll | SpaceID contracts |
| zns-subgraph | Polygon | .poly | |
| d3-connect-subgraph | Shibarium | .shib | |

## Envs

[anchor]: <> (anchors.envs.start.envs_main)

| Variable | Req&#x200B;uir&#x200B;ed | Description | Default value |
| --- | --- | --- | --- |
| `BENS__DATABASE__CONNECT__URL` | true | e.g. `postgresql://postgres:postgres@localhost:5432/postgres` | |
| `BENS__DATABASE__CREATE_DATABASE` | | | `false` |
| `BENS__DATABASE__RUN_MIGRATIONS` | | | `false` |
| `BENS__SERVER__HTTP__ADDR` | | | `0.0.0.0:8050` |
| `BENS__SERVER__HTTP__ENABLED` | | | `true` |
| `BENS__SERVER__HTTP__MAX_BODY_SIZE` | | | `2097152` |
| `BENS__SUBGRAPHS_READER__REFRESH_CACHE_SCHEDULE` | | | `0 0 * * * *` |
| `BENS__TRACING__ENABLED` | | | `true` |
| `BENS__TRACING__FORMAT` | | | `default` |

[anchor]: <> (anchors.envs.end.envs_main)
| Variable | Req&#x200B;uir&#x200B;ed | Description | Default value |
| ------------------------------------------------ | ------------------------ | ------------------------------------------------------------ | -------------- |
| `BENS__DATABASE__CONNECT__URL` | true | e.g.`postgresql://postgres:postgres@localhost:5432/postgres` | |
| `BENS__DATABASE__CREATE_DATABASE` | | | `false` |
| `BENS__DATABASE__RUN_MIGRATIONS` | | | `false` |
| `BENS__SERVER__HTTP__ADDR` | | | `0.0.0.0:8050` |
| `BENS__SERVER__HTTP__ENABLED` | | | `true` |
| `BENS__SERVER__HTTP__MAX_BODY_SIZE` | | | `2097152` |
| `BENS__SUBGRAPHS_READER__REFRESH_CACHE_SCHEDULE` | | | `0 0 * * * *` |
| `BENS__TRACING__ENABLED` | | | `true` |
| `BENS__TRACING__FORMAT` | | | `default` |

## Quickstart developer run

1. Install [just](https://github.com/casey/just), [dotenv-cli](https://www.npmjs.com/package/dotenv-cli)

2. Run commands:
```bash
just graph-node-start
just deploy-subgraph ens-sepolia
just run-dev
```

```bash
just graph-node-start
just deploy-subgraph ens-sepolia
just run-dev
```

## Quickstart test run

1. Install [just](https://github.com/casey/just)
2. Run command:

```bash
just test-with-db
```

## Contribute

Expand All @@ -67,25 +70,25 @@ If you want to add your name service procol to blockscout you should:
```

6. Deploy subgraph to graph-node (read more in [how to deploy subgraphs guide](./graph-node/README.md#deploy-subgraph-to-graph-node))
```bash
just deploy-subgraph <protocol_name>
```

```bash
just deploy-subgraph <protocol_name>
```

7. Add protocol to [dev.json](./bens-server/config/dev.json) config and start `bens-server` connected to common database (read more in [how to start bens guide](./bens-server/README.md#to-start-locally))

```bash
just run-dev
```
```bash
just run-dev
```

8. Check that `bens-server` responses with valid domains. You can find swagger docs at [https://blockscout.github.io/swaggers/services/bens/main/index.html](https://blockscout.github.io/swaggers/services/bens/main/index.html)

9. Add your protocol to list of [supported domains](#current-supported-domains)

10. Update default config of BENS server for [production](./bens-server/config/prod.json) and [staging](./bens-server/config/staging.json)

11. Finally, create PR with:
* New directory inside `blockscout-ens/graph-node/subgraphs` with your subgraph code
* Updated BENS config
* Updated supported domains list
* Result of indexed data: proof that your indexed subgraph contains correct amount of domains, resolved_addresses and so on

- New directory inside `blockscout-ens/graph-node/subgraphs` with your subgraph code
- Updated BENS config
- Updated supported domains list
- Result of indexed data: proof that your indexed subgraph contains correct amount of domains, resolved_addresses and so on

[anchor]: [anchor]:
178 changes: 169 additions & 9 deletions blockscout-ens/bens-logic/src/protocols/protocoler.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
blockscout::BlockscoutClient,
protocols::{DomainNameOnProtocol, ProtocolError},
protocols::{DomainName, DomainNameOnProtocol, ProtocolError},
};
use alloy::primitives::{Address, B256};
use anyhow::anyhow;
Expand Down Expand Up @@ -120,6 +120,7 @@ pub struct D3ConnectProtocol {
pub native_token_contract: Address,
#[serde(default)]
pub disable_offchain_resolve: bool,
pub empty_label_hash: Option<B256>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
Expand All @@ -145,6 +146,7 @@ pub enum AddressResolveTechnique {
#[serde(rename = "addr2name")]
Addr2Name,
}
const MAX_NAMES_LIMIT: usize = 5;

impl Tld {
pub fn new(tld: &str) -> Tld {
Expand Down Expand Up @@ -288,34 +290,155 @@ impl Protocoler {
name: &str,
network_id: i64,
maybe_filter: Option<NonEmpty<String>>,
) -> Result<Vec<DomainNameOnProtocol>, ProtocolError> {
let tlds = self
.networks
.get(&network_id)
.ok_or_else(|| ProtocolError::NetworkNotFound(network_id))?
.use_protocols
.iter()
.filter_map(|protocol_name| {
self.protocols
.get(protocol_name)
.map(|protocol| protocol.info.tld_list.iter().cloned())
})
.flatten()
.collect::<Vec<Tld>>();

if name.contains('.') {
let direct = self.names_options_in_network_exact(name, network_id, maybe_filter)?;
if direct.is_empty() {
Err(ProtocolError::InvalidName {
name: name.to_string(),
reason: "No matching protocols for given TLD".to_string(),
})
} else {
Ok(direct.into_iter().take(MAX_NAMES_LIMIT).collect())
}
} else {
let all_names_with_protocols: Vec<_> = tlds
.into_iter()
.map(|tld| format!("{}.{}", name, tld.0))
.flat_map(|name_with_tld| {
self.names_options_in_network_with_suggestions(
&name_with_tld,
network_id,
maybe_filter.clone(),
)
.unwrap_or_default()
})
.take(MAX_NAMES_LIMIT)
.collect();

if all_names_with_protocols.is_empty() {
Err(ProtocolError::InvalidName {
name: name.to_string(),
reason: "No valid TLDs".to_string(),
})
} else {
Ok(all_names_with_protocols)
}
}
}


pub fn names_options_in_network_exact(
&self,
name: &str,
network_id: i64,
maybe_filter: Option<NonEmpty<String>>,
) -> Result<Vec<DomainNameOnProtocol>, ProtocolError> {
let tld = Tld::from_domain_name(name).ok_or_else(|| ProtocolError::InvalidName {
name: name.to_string(),
reason: "no tld found".to_string(),
reason: "Invalid TLD".to_string(),
})?;

let protocols = self.protocols_of_network_for_tld(network_id, tld, maybe_filter)?;
let names_with_protocols = protocols
.into_iter()
.map(|p| DomainNameOnProtocol::from_str(name, p))
.collect::<Result<_, _>>()?;
Ok(names_with_protocols)

let mut results = Vec::new();
for deployed_protocol in protocols {
let empty_label_hash = match &deployed_protocol.protocol.info.protocol_specific {
ProtocolSpecific::EnsLike(ens_like) => ens_like.empty_label_hash,
ProtocolSpecific::D3Connect(d3_connect) => d3_connect.empty_label_hash, // Теперь извлекаем хэш
};

println!(
"Creating DomainName with name: {}, empty_label_hash: {:?}",
name, empty_label_hash
);

let domain_name = DomainName::new(name, empty_label_hash)?;
results.push(DomainNameOnProtocol {
inner: domain_name,
deployed_protocol,
});
}

Ok(results)
}


fn names_options_in_network_with_suggestions(
&self,
name_with_tld: &str,
network_id: i64,
maybe_filter: Option<NonEmpty<String>>,
) -> Result<Vec<DomainNameOnProtocol>, ProtocolError> {
println!(
"names_options_in_network_with_suggestions called with: name_with_tld = {}",
name_with_tld
);

let protocols = self.protocols_of_network_for_tld(network_id, Tld::from_domain_name(name_with_tld).unwrap(), maybe_filter)?;

let mut results = Vec::new();
for deployed_protocol in protocols {
let empty_label_hash = match &deployed_protocol.protocol.info.protocol_specific {
ProtocolSpecific::EnsLike(ens_like) => ens_like.empty_label_hash,
ProtocolSpecific::D3Connect(d3_connect) => d3_connect.empty_label_hash, // Исправлено
};

println!(
"Creating DomainName with name: {}, empty_label_hash: {:?}",
name_with_tld, empty_label_hash
);

let domain_name = DomainName::new(name_with_tld, empty_label_hash)?;
results.push(DomainNameOnProtocol {
inner: domain_name,
deployed_protocol,
});
}

Ok(results)
}


pub fn main_name_in_network(
&self,
name: &str,
network_id: i64,
maybe_filter: Option<NonEmpty<String>>,
) -> Result<DomainNameOnProtocol, ProtocolError> {
// Логируем передаваемые параметры
println!(
"main_name_in_network called with: name = {}, network_id = {}, maybe_filter = {:?}",
name, network_id, maybe_filter
);

let maybe_name = self
.names_options_in_network(name, network_id, maybe_filter)
.map(|mut names| names.pop())?;
.names_options_in_network(name, network_id, maybe_filter.clone())
.map(|mut names| {
println!("names_options_in_network returned: {:?}", names);
names.pop()
})?;
let name = maybe_name.ok_or_else(|| ProtocolError::InvalidName {
name: name.to_string(),
reason: "no protocol found".to_string(),
})?;
Ok(name)
}


pub fn name_in_protocol(
&self,
Expand Down Expand Up @@ -351,3 +474,40 @@ impl Protocol {
})
}
}

#[cfg(test)]
mod tld_tests {
use super::Tld;

#[test]
fn tld_new_trims_dot() {
let tld = Tld::new(".eth");
assert_eq!(tld.0, "eth");
}

#[test]
fn tld_new_no_dot() {
let tld = Tld::new("eth");
assert_eq!(tld.0, "eth");
}

#[test]
fn from_domain_name_works() {
let domain = "vitalik.eth";
let tld = Tld::from_domain_name(domain).unwrap();
assert_eq!(tld.0, "eth");
}

#[test]
fn from_domain_name_empty() {
let domain = ".";
let tld = Tld::from_domain_name(domain);
assert!(tld.is_none());
}

#[test]
fn reverse_works() {
let rev = Tld::reverse();
assert_eq!(rev.0, "reverse");
}
}
2 changes: 2 additions & 0 deletions blockscout-ens/bens-logic/src/subgraph/sql/domain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ pub async fn get_domain(
.unwrap_or_default();
let schema = &domain_name.deployed_protocol.protocol.subgraph_schema;
let protocol_slug = &domain_name.deployed_protocol.protocol.info.slug;
println!("schema: {}, protocol_slug: {}, only_active_clause: {} ", schema, protocol_slug, only_active_clause);
let maybe_domain = sqlx::query_as(&format!(
r#"
SELECT
Expand Down Expand Up @@ -172,6 +173,7 @@ pub async fn get_domain(
.bind(&domain_name.inner.id)
.fetch_optional(pool)
.await?;
println!("maybe_domain: {:?}", maybe_domain);
Ok(maybe_domain)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ INSERT INTO sgd2.domain (vid, block_range, id, name, label_name, labelhash, pare
INSERT INTO sgd2.domain (vid, block_range, id, name, label_name, labelhash, parent, subdomain_count, resolved_address, resolver, ttl, is_migrated, created_at, owner, registrant, wrapped_owner, expiry_date) VALUES (80, '[7159086,)', '0xaf7e7a1a258e296dc2cf161babcb749c704a097543f60dfa773cfe7d8c352164', 'm4d5urfer.gno', 'm4d5urfer', '\xd103cd5b22869a252cecbfb30098984980df558548696898656bdc4201686e83', '0x634ae5e4e77ee5a262a820f4a9eacd51ac137dd75989e5a5d993f5b1db797fba', 0, '0xbf7c9a8ced61d1067c4ec6afdfc3567ef518176b', '0x6d3b3f99177fb2a5de7f9e928a9bd807bf7b5bad-0xaf7e7a1a258e296dc2cf161babcb749c704a097543f60dfa773cfe7d8c352164', NULL, true, 1701248665, '0xbf7c9a8ced61d1067c4ec6afdfc3567ef518176b', '0xbf7c9a8ced61d1067c4ec6afdfc3567ef518176b', NULL, 1740581617);
INSERT INTO sgd2.domain (vid, block_range, id, name, label_name, labelhash, parent, subdomain_count, resolved_address, resolver, ttl, is_migrated, created_at, owner, registrant, wrapped_owner, expiry_date) VALUES (81, '[7159135,7172710)', '0x91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2', '[e5e14487b78f85faa6e1808e89246cf57dd34831548ff2e6097380d98db2504a].[dec08c9dbbdd0890e300eb5062089b2d4b1c40e3673bbccb5423f7b37dcf9a9c]', NULL, '\xe5e14487b78f85faa6e1808e89246cf57dd34831548ff2e6097380d98db2504a', '0xa097f6721ce401e757d1223a763fef49b8b5f90bb18567ddb86fd205dff71d34', 17, NULL, NULL, NULL, true, 1700719455, '0xd322333e88acefc5bf19470d137c0f2272104ae8', NULL, NULL, NULL);
INSERT INTO sgd2.domain (vid, block_range, id, name, label_name, labelhash, parent, subdomain_count, resolved_address, resolver, ttl, is_migrated, created_at, owner, registrant, wrapped_owner, expiry_date) VALUES (82, '[7159135,7172710)', '0x634ae5e4e77ee5a262a820f4a9eacd51ac137dd75989e5a5d993f5b1db797fba', '[959002e6c39d7f4735b5d874e0b6532d0b63a6404fe8b0978a5b357455421cca].[000027d83d9f5da7a98672efb882948cc95183501c1ae26ee8d49635711757d2]', NULL, '\x959002e6c39d7f4735b5d874e0b6532d0b63a6404fe8b0978a5b357455421cca', '0x1a13b687a5ff1d8ab1a9e189e1507a6abe834a9296cc8cff937905e3dee0c4f6', 20, NULL, NULL, NULL, true, 1700725990, '0xfd3d666db2557983f3f04d61f90e35cc696f6d60', NULL, NULL, NULL);
INSERT INTO sgd2.domain (vid, block_range, id, name, label_name, labelhash, parent, subdomain_count, resolved_address, resolver, ttl, is_migrated, created_at, owner, registrant, wrapped_owner, expiry_date) VALUES (83, '[7159135,)', '0xa3504cdec527495c69c760c85d5be9996252f853b91fd0df04c5b6aa2deb3347', 'levvv.gno', 'levvv', '\x1a8247ca2a4190d90c748b31fa6517e5560c1b7a680f03ff73dbbc3ed2c0ed66', '0x634ae5e4e77ee5a262a820f4a9eacd51ac137dd75989e5a5d993f5b1db797fba', 0, '0xc0de20a37e2dac848f81a93bd85fe4acdde7c0de', '0x6d3b3f99177fb2a5de7f9e928a9bd807bf7b5bad-0xa3504cdec527495c69c760c85d5be9996252f853b91fd0df04c5b6aa2deb3347', NULL, true, 1701248965, '0xc0de20a37e2dac848f81a93bd85fe4acdde7c0de', '0xc0de20a37e2dac848f81a93bd85fe4acdde7c0de', NULL, 1740581917);
INSERT INTO sgd2.domain (vid, block_range, id, name, label_name, labelhash, parent, subdomain_count, resolved_address, resolver, ttl, is_migrated, created_at, owner, registrant, wrapped_owner, expiry_date) VALUES (83, '[7159135,)', '0xa3504cdec527495c69c760c85d5be9996252f853b91fd0df04c5b6aa2deb3347', 'levvv.gno', 'levvv', '\x1a8247ca2a4190d90c748b31fa6517e5560c1b7a680f03ff73dbbc3ed2c0ed66', '0x634ae5e4e77ee5a262a820f4a9eacd51ac137dd75989e5a5d993f5b1db797fba', 0, '0xc0de20a37e2dac848f81a93bd85fe4acdde7c0de', '0x6d3b3f99177fb2a5de7f9e928a9bd807bf7b5bad-0xa3504cdec527495c69c760c85d5be9996252f853b91fd0df04c5b6aa2deb3347', NULL, true, 1701248965, '0xc0de20a37e2dac848f81a93bd85fe4acdde7c0de', '0xc0de20a37e2dac848f81a93bd85fe4acdde7c0de', NULL, 2147483647);
INSERT INTO sgd2.domain (vid, block_range, id, name, label_name, labelhash, parent, subdomain_count, resolved_address, resolver, ttl, is_migrated, created_at, owner, registrant, wrapped_owner, expiry_date) VALUES (84, '[7159135,)', '0xfa3f00061c56ae92d9119664eed5aa76f8fb76a85db04e3723ef6162461ce564', '[18e0569ab965dcaf3e90b6af8998c8dbf4a9c989fa7966343d08c632022dab7d].[e5e14487b78f85faa6e1808e89246cf57dd34831548ff2e6097380d98db2504a].[dec08c9dbbdd0890e300eb5062089b2d4b1c40e3673bbccb5423f7b37dcf9a9c]', NULL, '\x18e0569ab965dcaf3e90b6af8998c8dbf4a9c989fa7966343d08c632022dab7d', '0x91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2', 0, NULL, '0x6d3b3f99177fb2a5de7f9e928a9bd807bf7b5bad-0xfa3f00061c56ae92d9119664eed5aa76f8fb76a85db04e3723ef6162461ce564', NULL, true, 1701248965, '0xc0de20a37e2dac848f81a93bd85fe4acdde7c0de', NULL, NULL, NULL);
INSERT INTO sgd2.domain (vid, block_range, id, name, label_name, labelhash, parent, subdomain_count, resolved_address, resolver, ttl, is_migrated, created_at, owner, registrant, wrapped_owner, expiry_date) VALUES (85, '[7172710,7180457)', '0x91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2', '[e5e14487b78f85faa6e1808e89246cf57dd34831548ff2e6097380d98db2504a].[dec08c9dbbdd0890e300eb5062089b2d4b1c40e3673bbccb5423f7b37dcf9a9c]', NULL, '\xe5e14487b78f85faa6e1808e89246cf57dd34831548ff2e6097380d98db2504a', '0xa097f6721ce401e757d1223a763fef49b8b5f90bb18567ddb86fd205dff71d34', 18, NULL, NULL, NULL, true, 1700719455, '0xd322333e88acefc5bf19470d137c0f2272104ae8', NULL, NULL, NULL);
INSERT INTO sgd2.domain (vid, block_range, id, name, label_name, labelhash, parent, subdomain_count, resolved_address, resolver, ttl, is_migrated, created_at, owner, registrant, wrapped_owner, expiry_date) VALUES (86, '[7172710,7180457)', '0x634ae5e4e77ee5a262a820f4a9eacd51ac137dd75989e5a5d993f5b1db797fba', '[959002e6c39d7f4735b5d874e0b6532d0b63a6404fe8b0978a5b357455421cca].[000027d83d9f5da7a98672efb882948cc95183501c1ae26ee8d49635711757d2]', NULL, '\x959002e6c39d7f4735b5d874e0b6532d0b63a6404fe8b0978a5b357455421cca', '0x1a13b687a5ff1d8ab1a9e189e1507a6abe834a9296cc8cff937905e3dee0c4f6', 21, NULL, NULL, NULL, true, 1700725990, '0xfd3d666db2557983f3f04d61f90e35cc696f6d60', NULL, NULL, NULL);
Expand Down
Loading
Loading