Skip to content

Commit 0f736a4

Browse files
committed
fail when ens rainbow not present
1 parent 7b4228c commit 0f736a4

File tree

5 files changed

+69
-4
lines changed

5 files changed

+69
-4
lines changed

graph/src/components/store/traits.rs

+2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ pub trait EnsLookup: Send + Sync + 'static {
3232
/// Find the reverse of keccak256 for `hash` through looking it up in the
3333
/// rainbow table.
3434
fn find_name(&self, hash: &str) -> Result<Option<String>, StoreError>;
35+
// Check if the rainbow table is filled.
36+
fn is_table_empty(&self) -> Result<bool, StoreError>;
3537
}
3638

3739
/// An entry point for all operations that require access to the node's storage

runtime/wasm/src/host_exports.rs

+4
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,10 @@ impl<C: Blockchain> HostExports<C> {
676676
Ok(self.ens_lookup.find_name(hash)?)
677677
}
678678

679+
pub(crate) fn is_ens_data_empty(&self) -> Result<bool, anyhow::Error> {
680+
Ok(self.ens_lookup.is_table_empty()?)
681+
}
682+
679683
pub(crate) fn log_log(
680684
&self,
681685
logger: &Logger,

runtime/wasm/src/module/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1689,6 +1689,9 @@ impl<C: Blockchain> WasmInstanceContext<C> {
16891689

16901690
let hash: String = asc_get(self, hash_ptr, gas)?;
16911691
let name = self.ctx.host_exports.ens_name_by_hash(&*hash)?;
1692+
if name.is_none() && self.ctx.host_exports.is_ens_data_empty()? {
1693+
return Err(anyhow!("Missing ENS data from database").into());
1694+
}
16921695

16931696
// map `None` to `null`, and `Some(s)` to a runtime string
16941697
name.map(|name| asc_new(self, &*name, gas).map_err(Into::into))

store/postgres/src/primary.rs

+12
Original file line numberDiff line numberDiff line change
@@ -1495,6 +1495,18 @@ impl<'a> Connection<'a> {
14951495
.map_err(|e| anyhow!("error looking up ens_name for hash {}: {}", hash, e).into())
14961496
}
14971497

1498+
pub fn is_ens_table_empty(&self) -> Result<bool, StoreError> {
1499+
use ens_names as dsl;
1500+
1501+
dsl::table
1502+
.select(dsl::name)
1503+
.limit(1)
1504+
.get_result::<String>(self.conn.as_ref())
1505+
.optional()
1506+
.map(|r| r.is_none())
1507+
.map_err(|e| anyhow!("error if ens table is empty: {}", e).into())
1508+
}
1509+
14981510
pub fn record_active_copy(&self, src: &Site, dst: &Site) -> Result<(), StoreError> {
14991511
use active_copies as cp;
15001512

store/postgres/src/subgraph_store.rs

+48-4
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use diesel::{
66
};
77
use std::{
88
collections::{BTreeMap, HashMap},
9-
sync::{Arc, Mutex},
9+
sync::{atomic::AtomicU8, Arc, Mutex},
1010
};
1111
use std::{fmt, io::Write};
1212
use std::{iter::FromIterator, time::Duration};
@@ -1147,23 +1147,67 @@ impl SubgraphStoreInner {
11471147
}
11481148
}
11491149

1150+
const STATE_ENS_NOT_CHECKED: u8 = 0;
1151+
const STATE_ENS_EMPTY: u8 = 1;
1152+
const STATE_ENS_NOT_EMPTY: u8 = 2;
1153+
1154+
/// EnsLookup reads from a rainbow table store in postgres that needs to be manually
1155+
/// loaded. To avoid unnecessary database roundtrips, the empty table check is lazy
1156+
/// and will not be retried. Once the table is checked, any subsequent calls will
1157+
/// just used the stored result.
11501158
struct EnsLookup {
11511159
primary: ConnectionPool,
1160+
// In order to keep the struct lock free, we'll use u8 for the status:
1161+
// 0 - Not Checked
1162+
// 1 - Checked - empty
1163+
// 2 - Checked - non empty
1164+
state: AtomicU8,
1165+
}
1166+
1167+
impl EnsLookup {
1168+
pub fn new(pool: ConnectionPool) -> Self {
1169+
Self {
1170+
primary: pool,
1171+
state: AtomicU8::new(STATE_ENS_NOT_CHECKED),
1172+
}
1173+
}
1174+
1175+
fn is_table_empty(pool: &ConnectionPool) -> Result<bool, StoreError> {
1176+
let conn = pool.get()?;
1177+
primary::Connection::new(conn).is_ens_table_empty()
1178+
}
11521179
}
11531180

11541181
impl EnsLookupTrait for EnsLookup {
11551182
fn find_name(&self, hash: &str) -> Result<Option<String>, StoreError> {
11561183
let conn = self.primary.get()?;
11571184
primary::Connection::new(conn).find_ens_name(hash)
11581185
}
1186+
1187+
fn is_table_empty(&self) -> Result<bool, StoreError> {
1188+
match self.state.load(std::sync::atomic::Ordering::SeqCst) {
1189+
STATE_ENS_NOT_CHECKED => {}
1190+
STATE_ENS_EMPTY => return Ok(true),
1191+
STATE_ENS_NOT_EMPTY => return Ok(false),
1192+
_ => unreachable!("unsupported state"),
1193+
}
1194+
1195+
let is_empty = Self::is_table_empty(&self.primary)?;
1196+
let new_state = match is_empty {
1197+
true => STATE_ENS_EMPTY,
1198+
false => STATE_ENS_NOT_EMPTY,
1199+
};
1200+
self.state
1201+
.store(new_state, std::sync::atomic::Ordering::SeqCst);
1202+
1203+
Ok(is_empty)
1204+
}
11591205
}
11601206

11611207
#[async_trait::async_trait]
11621208
impl SubgraphStoreTrait for SubgraphStore {
11631209
fn ens_lookup(&self) -> Arc<dyn EnsLookupTrait> {
1164-
Arc::new(EnsLookup {
1165-
primary: self.mirror.primary().clone(),
1166-
})
1210+
Arc::new(EnsLookup::new(self.mirror.primary().clone()))
11671211
}
11681212

11691213
// FIXME: This method should not get a node_id

0 commit comments

Comments
 (0)