Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c1712f3

Browse files
committed
datasource, graph, graphql, mock, store: Add network identifier check on startup
Check that we are connected to the same Ethereum network as the last time this network name was used.
1 parent e1b4a07 commit c1712f3

File tree

8 files changed

+135
-37
lines changed

8 files changed

+135
-37
lines changed

datasource/ethereum/src/block_ingestor.rs

Lines changed: 48 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,6 @@ where
4141
logger: slog::Logger,
4242
polling_interval: Duration,
4343
) -> Result<BlockIngestor<S, T>, Error> {
44-
// Add a head block pointer for this network name if one does not already exist
45-
store
46-
.lock()
47-
.unwrap()
48-
.add_network_if_missing(&network_name)?;
49-
5044
Ok(BlockIngestor {
5145
store,
5246
network_name,
@@ -61,24 +55,55 @@ where
6155
// Currently, there is no way to stop block ingestion, so just leak self
6256
let static_self: &'static _ = Box::leak(Box::new(self));
6357

64-
// Create stream that emits at polling interval
65-
tokio::timer::Interval::new(Instant::now(), static_self.polling_interval)
58+
// First, add Ethereum network info it store
59+
static_self
60+
.add_network_to_store()
6661
.map_err(move |e| {
67-
error!(static_self.logger, "timer::Interval failed: {:?}", e);
68-
}).for_each(move |_| {
69-
// Attempt to poll
70-
static_self.do_poll().then(move |result| {
71-
if let Err(e) = result {
72-
// Some polls will fail due to transient issues
73-
warn!(
74-
static_self.logger,
75-
"failed to poll for latest block: {:?}", e
76-
);
77-
}
78-
79-
// Continue polling even if polling failed
80-
future::ok(())
81-
})
62+
panic!("Failed to load Ethereum network info: {}", e);
63+
}).and_then(move |()| {
64+
// Create stream that emits at polling interval
65+
tokio::timer::Interval::new(Instant::now(), static_self.polling_interval)
66+
.map_err(move |e| {
67+
error!(static_self.logger, "timer::Interval failed: {:?}", e);
68+
}).for_each(move |_| {
69+
// Attempt to poll
70+
static_self.do_poll().then(move |result| {
71+
if let Err(e) = result {
72+
// Some polls will fail due to transient issues
73+
warn!(
74+
static_self.logger,
75+
"failed to poll for latest block: {:?}", e
76+
);
77+
}
78+
79+
// Continue polling even if polling failed
80+
future::ok(())
81+
})
82+
})
83+
})
84+
}
85+
86+
fn add_network_to_store<'a>(&'a self) -> impl Future<Item = (), Error = Error> + 'a {
87+
let web3 = Web3::new(self.web3_transport.clone());
88+
89+
// Ask Ethereum node for info to identify the network
90+
let net_ver_future = web3.net().version();
91+
let gen_block_future = web3.eth().block(BlockNumber::Earliest.into());
92+
net_ver_future
93+
.join(gen_block_future)
94+
.map_err(|e| format_err!("could not get network info from Ethereum: {}", e))
95+
.and_then(move |(net_version, gen_block)| {
96+
let gen_block_hash = gen_block
97+
.expect("Ethereum node could not find genesis block")
98+
.hash
99+
.unwrap();
100+
101+
// Add Ethereum network info to store
102+
self.store.lock().unwrap().add_network_if_missing(
103+
&self.network_name,
104+
&net_version,
105+
gen_block_hash,
106+
)
82107
})
83108
}
84109

graph/src/components/store.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,12 @@ pub type SubgraphEntityPair = (String, String);
145145
/// Common trait for block data store implementations.
146146
pub trait BlockStore {
147147
/// Add a new network, but only if one with this name does not already exist in the block store
148-
fn add_network_if_missing(&self, network_name: &str) -> Result<(), Error>;
148+
fn add_network_if_missing(
149+
&self,
150+
network_name: &str,
151+
net_version: &str,
152+
genesis_block_hash: H256,
153+
) -> Result<(), Error>;
149154

150155
/// Insert blocks into the store (or update if they are already present).
151156
fn upsert_blocks<'a, B: Stream<Item = Block<Transaction>, Error = Error> + Send + 'a>(

graphql/tests/query.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ impl BasicStore for TestStore {
190190
}
191191

192192
impl BlockStore for TestStore {
193-
fn add_network_if_missing(&self, _network_name: &str) -> Result<(), Error> {
193+
fn add_network_if_missing(&self, _: &str, _: &str, _: H256) -> Result<(), Error> {
194194
unimplemented!()
195195
}
196196

mock/src/store.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ impl BasicStore for MockStore {
5555
}
5656

5757
impl BlockStore for MockStore {
58-
fn add_network_if_missing(&self, _: &str) -> Result<(), Error> {
58+
fn add_network_if_missing(&self, _: &str, _: &str, _: H256) -> Result<(), Error> {
5959
unimplemented!()
6060
}
6161

@@ -102,7 +102,7 @@ impl BasicStore for FakeStore {
102102
}
103103

104104
impl BlockStore for FakeStore {
105-
fn add_network_if_missing(&self, _: &str) -> Result<(), Error> {
105+
fn add_network_if_missing(&self, _: &str, _: &str, _: H256) -> Result<(), Error> {
106106
panic!("called FakeStore")
107107
}
108108

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/**************************************************************
2+
* REMOVE etherum_networks COLUMNS
3+
**************************************************************/
4+
5+
ALTER TABLE ethereum_networks
6+
DROP COLUMN net_version,
7+
DROP COLUMN genesis_block_hash;
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/**************************************************************
2+
* ADD etherum_networks COLUMNS
3+
**************************************************************/
4+
5+
ALTER TABLE ethereum_networks
6+
ADD COLUMN net_version VARCHAR,
7+
ADD COLUMN genesis_block_hash VARCHAR,
8+
ADD CHECK ((net_version IS NULL) = (genesis_block_hash IS NULL));

store/postgres/src/db_schema.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ table! {
1313
name -> Varchar,
1414
head_block_hash -> Nullable<Varchar>,
1515
head_block_number -> Nullable<BigInt>,
16+
net_version -> Nullable<Varchar>,
17+
genesis_block_hash -> Nullable<Varchar>,
1618
}
1719
}
1820

store/postgres/src/store.rs

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use diesel::pg::Pg;
33
use diesel::pg::PgConnection;
44
use diesel::prelude::*;
55
use diesel::sql_types::Text;
6-
use diesel::{debug_query, delete, insert_into, result, select};
6+
use diesel::{debug_query, delete, insert_into, result, select, update};
77
use filter::store_filter;
88
use futures::sync::mpsc::{channel, Sender};
99
use std::collections::HashMap;
@@ -330,17 +330,68 @@ impl BasicStore for Store {
330330
}
331331

332332
impl BlockStore for Store {
333-
fn add_network_if_missing(&self, network_name: &str) -> Result<(), Error> {
333+
fn add_network_if_missing(
334+
&self,
335+
new_network_name: &str,
336+
new_net_version: &str,
337+
new_genesis_block_hash: H256,
338+
) -> Result<(), Error> {
334339
use db_schema::ethereum_networks::dsl::*;
335340

336-
insert_into(ethereum_networks)
337-
.values((
338-
name.eq(network_name),
339-
head_block_hash.eq::<Option<String>>(None),
340-
head_block_number.eq::<Option<i64>>(None),
341-
)).on_conflict(name)
342-
.do_nothing()
343-
.execute(&*self.conn.lock().unwrap())?;
341+
let network_identifiers_opt = ethereum_networks
342+
.select((net_version, genesis_block_hash))
343+
.filter(name.eq(new_network_name))
344+
.first::<(Option<String>, Option<String>)>(&*self.conn.lock().unwrap())
345+
.optional()?;
346+
347+
match network_identifiers_opt {
348+
// Network is missing in database
349+
None => {
350+
insert_into(ethereum_networks)
351+
.values((
352+
name.eq(new_network_name),
353+
head_block_hash.eq::<Option<String>>(None),
354+
head_block_number.eq::<Option<i64>>(None),
355+
net_version.eq::<Option<String>>(Some(new_net_version.to_owned())),
356+
genesis_block_hash
357+
.eq::<Option<String>>(Some(format!("{:x}", new_genesis_block_hash))),
358+
)).on_conflict(name)
359+
.do_nothing()
360+
.execute(&*self.conn.lock().unwrap())?;
361+
}
362+
363+
// Network is in database and has identifiers
364+
Some((Some(last_net_version), Some(last_genesis_block_hash))) => {
365+
if last_net_version != new_net_version {
366+
panic!(
367+
"Ethereum node provided net_version {}, \
368+
but we expected {}. Did you change networks \
369+
without changing the network name?",
370+
new_net_version, last_net_version
371+
);
372+
}
373+
374+
if last_genesis_block_hash.parse().ok() != Some(new_genesis_block_hash) {
375+
panic!(
376+
"Ethereum node provided genesis block hash {}, \
377+
but we expected {}. Did you change networks \
378+
without changing the network name?",
379+
new_genesis_block_hash, last_genesis_block_hash
380+
);
381+
}
382+
}
383+
384+
// Network is in database but is missing identifiers
385+
Some(_) => {
386+
update(ethereum_networks)
387+
.set((
388+
net_version.eq::<Option<String>>(Some(new_net_version.to_owned())),
389+
genesis_block_hash
390+
.eq::<Option<String>>(Some(format!("{:x}", new_genesis_block_hash))),
391+
)).filter(name.eq(new_network_name))
392+
.execute(&*self.conn.lock().unwrap())?;
393+
}
394+
}
344395

345396
Ok(())
346397
}

0 commit comments

Comments
 (0)