Skip to content

Commit

Permalink
Add timestamp to meta block
Browse files Browse the repository at this point in the history
  • Loading branch information
mangas committed Jul 18, 2022
1 parent cb0a898 commit 167861a
Show file tree
Hide file tree
Showing 10 changed files with 93 additions and 28 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 8 additions & 2 deletions graph/src/components/store/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -353,8 +353,14 @@ pub trait ChainStore: Send + Sync + 'static {
/// may purge any other blocks with that number
fn confirm_block_hash(&self, number: BlockNumber, hash: &BlockHash) -> Result<usize, Error>;

/// Find the block with `block_hash` and return the network name and number
fn block_number(&self, hash: &BlockHash) -> Result<Option<(String, BlockNumber)>, StoreError>;
/// Find the block with `block_hash` and return the network name, number and timestamp if present.
/// Currently, the timestamp is only returned if it's present in the top level block. This format is
/// depends on the chain and the implementation of Blockchain::Block for the specific chain.
/// eg: {"block": { "timestamp": 123123123 } }
fn block_number(
&self,
hash: &BlockHash,
) -> Result<Option<(String, BlockNumber, Option<String>)>, StoreError>;

/// Tries to retrieve all transactions receipts for a given block.
async fn transaction_receipts_in_block(
Expand Down
2 changes: 2 additions & 0 deletions graphql/src/schema/meta.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ type _Block_ {
hash: Bytes
"The block number"
number: Int!
"Timestamp of the block if available, format depends on the chain"
timestamp: String
}

enum _SubgraphErrorPolicy_ {
Expand Down
2 changes: 1 addition & 1 deletion node/src/manager/commands/rewind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ fn block_ptr(
None => bail!("can not find chain store for {}", chain),
Some(store) => store,
};
if let Some((_, number)) = chain_store.block_number(&block_ptr_to.hash)? {
if let Some((_, number, _)) = chain_store.block_number(&block_ptr_to.hash)? {
if number != block_ptr_to.number {
bail!(
"the given hash is for block number {} but the command specified block number {}",
Expand Down
5 changes: 3 additions & 2 deletions server/index-node/src/resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,8 +228,8 @@ impl<S: Store> IndexNodeResolver<S> {
let chain_store = chain.chain_store();
let call_cache = chain.call_cache();

let block_number = match chain_store.block_number(&block_hash) {
Ok(Some((_, n))) => n,
let (block_number, timestamp) = match chain_store.block_number(&block_hash) {
Ok(Some((_, n, timestamp))) => (n, timestamp),
Ok(None) => {
error!(
self.logger,
Expand Down Expand Up @@ -275,6 +275,7 @@ impl<S: Store> IndexNodeResolver<S> {
block: object! {
hash: cached_call.block_ptr.hash.hash_hex(),
number: cached_call.block_ptr.number,
timestamp: timestamp.clone(),
},
contractAddress: &cached_call.contract_address[..],
returnValue: &cached_call.return_value[..],
Expand Down
38 changes: 25 additions & 13 deletions store/postgres/src/chain_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ mod data {
use graph::prelude::{
serde_json as json, BlockNumber, BlockPtr, CachedEthereumCall, Error, StoreError,
};
use serde::Deserialize;
use std::fmt;
use std::iter::FromIterator;
use std::{convert::TryFrom, io::Write};
Expand Down Expand Up @@ -589,34 +590,42 @@ mod data {
}
}

/// timestamp's representation depends the blockchain::Block implementation, on
/// ethereum this is a U256 but on different chains it will most likely be different.
pub(super) fn block_number(
&self,
conn: &PgConnection,
hash: &BlockHash,
) -> Result<Option<BlockNumber>, StoreError> {
) -> Result<Option<(BlockNumber, Option<String>)>, StoreError> {
const TIMESTAMP_QUERY: &str =
"coalesce(data->'block'->>'timestamp', data->>'timestamp')";

let number = match self {
Storage::Shared => {
use public::ethereum_blocks as b;

b::table
.select(b::number)
.select((b::number, sql(TIMESTAMP_QUERY)))
.filter(b::hash.eq(format!("{:x}", hash)))
.first::<i64>(conn)
.first::<(i64, String)>(conn)
.optional()?
}
Storage::Private(Schema { blocks, .. }) => blocks
.table()
.select(blocks.number())
.select((blocks.number(), sql(TIMESTAMP_QUERY)))
.filter(blocks.hash().eq(hash.as_slice()))
.first::<i64>(conn)
.first::<(i64, String)>(conn)
.optional()?,
};
number
.map(|number| {
BlockNumber::try_from(number)
.map_err(|e| StoreError::QueryExecutionError(e.to_string()))
})
.transpose()

match number {
None => Ok(None),
Some((number, ts)) => {
let number = BlockNumber::try_from(number)
.map_err(|e| StoreError::QueryExecutionError(e.to_string()))?;
Ok(Some((number, Some(ts))))
}
}
}

/// Find the first block that is missing from the database needed to
Expand Down Expand Up @@ -1687,12 +1696,15 @@ impl ChainStoreTrait for ChainStore {
.confirm_block_hash(&conn, &self.chain, number, hash)
}

fn block_number(&self, hash: &BlockHash) -> Result<Option<(String, BlockNumber)>, StoreError> {
fn block_number(
&self,
hash: &BlockHash,
) -> Result<Option<(String, BlockNumber, Option<String>)>, StoreError> {
let conn = self.get_conn()?;
Ok(self
.storage
.block_number(&conn, hash)?
.map(|number| (self.chain.clone(), number)))
.map(|(number, timestamp)| (self.chain.clone(), number, timestamp)))
}

async fn transaction_receipts_in_block(
Expand Down
2 changes: 1 addition & 1 deletion store/postgres/src/query_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ impl QueryStoreTrait for QueryStore {
let subgraph_network = self.network_name();
self.chain_store
.block_number(block_hash)?
.map(|(network_name, number)| {
.map(|(network_name, number, _timestamp)| {
if network_name == subgraph_network {
Ok(number)
} else {
Expand Down
34 changes: 34 additions & 0 deletions store/postgres/tests/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1981,6 +1981,40 @@ fn cleanup_cached_blocks() {
})
}

#[test]
/// checks if retrieving the timestamp from the data blob works.
/// on ethereum, the block has timestamp as U256 so it will always have a value
fn parse_timestamp() {
const EXPECTED_TS: &str = "0x62ceae26";

run_test(|store, _, _| async move {
use block_store::*;
// The test subgraph is at block 2. Since we don't ever delete
// the genesis block, the only block eligible for cleanup is BLOCK_ONE
// and the first retained block is block 2.
block_store::set_chain(
vec![
&*GENESIS_BLOCK,
&*BLOCK_ONE,
&*BLOCK_TWO,
&*BLOCK_THREE_TIMESTAMP,
],
NETWORK_NAME,
);
let chain_store = store
.block_store()
.chain_store(NETWORK_NAME)
.expect("fake chain store");

let (_network, number, timestamp) = chain_store
.block_number(&BLOCK_THREE_TIMESTAMP.block_hash())
.expect("block_number to return correct number and timestamp")
.unwrap();
assert_eq!(number, 3);
assert_eq!(timestamp.unwrap(), EXPECTED_TS);
})
}

#[test]
fn reorg_tracking() {
async fn update_john(
Expand Down
1 change: 1 addition & 0 deletions store/test-store/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ lazy_static = "1.1"
hex-literal = "0.3"
diesel = { version = "1.4.8", features = ["postgres", "serde_json", "numeric", "r2d2"] }
graph-chain-ethereum = { path = "../../chain/ethereum" }
serde = "1.0"
26 changes: 17 additions & 9 deletions store/test-store/src/block_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ use graph::components::store::BlockStore;
use graph::{
blockchain::Block,
prelude::{
serde_json, web3::types::H256, BlockHash, BlockNumber, BlockPtr, EthereumBlock,
LightEthereumBlock,
serde_json, web3::types::H256, web3::types::U256, BlockHash, BlockNumber, BlockPtr,
EthereumBlock, LightEthereumBlock,
},
};

Expand All @@ -16,23 +16,25 @@ lazy_static! {
pub static ref GENESIS_BLOCK: FakeBlock = FakeBlock {
number: super::GENESIS_PTR.number,
hash: super::GENESIS_PTR.hash_hex(),
timestamp: None,
parent_hash: NO_PARENT.to_string()
};
pub static ref BLOCK_ONE: FakeBlock = GENESIS_BLOCK
.make_child("8511fa04b64657581e3f00e14543c1d522d5d7e771b54aa3060b662ade47da13");
.make_child("8511fa04b64657581e3f00e14543c1d522d5d7e771b54aa3060b662ade47da13", None);
pub static ref BLOCK_ONE_SIBLING: FakeBlock =
GENESIS_BLOCK.make_child("b98fb783b49de5652097a989414c767824dff7e7fd765a63b493772511db81c1");
GENESIS_BLOCK.make_child("b98fb783b49de5652097a989414c767824dff7e7fd765a63b493772511db81c1", None);
pub static ref BLOCK_ONE_NO_PARENT: FakeBlock = FakeBlock::make_no_parent(
1,
"7205bdfcf4521874cf38ce38c879ff967bf3a069941286bfe267109ad275a63d"
);

pub static ref BLOCK_TWO: FakeBlock = BLOCK_ONE.make_child("f8ccbd3877eb98c958614f395dd351211afb9abba187bfc1fb4ac414b099c4a6");
pub static ref BLOCK_TWO: FakeBlock = BLOCK_ONE.make_child("f8ccbd3877eb98c958614f395dd351211afb9abba187bfc1fb4ac414b099c4a6", None);
pub static ref BLOCK_TWO_NO_PARENT: FakeBlock = FakeBlock::make_no_parent(2, "3b652b00bff5e168b1218ff47593d516123261c4487629c4175f642ee56113fe");
pub static ref BLOCK_THREE: FakeBlock = BLOCK_TWO.make_child("7347afe69254df06729e123610b00b8b11f15cfae3241f9366fb113aec07489c");
pub static ref BLOCK_THREE: FakeBlock = BLOCK_TWO.make_child("7347afe69254df06729e123610b00b8b11f15cfae3241f9366fb113aec07489c", None);
pub static ref BLOCK_THREE_NO_PARENT: FakeBlock = FakeBlock::make_no_parent(3, "fa9ebe3f74de4c56908b49f5c4044e85825f7350f3fa08a19151de82a82a7313");
pub static ref BLOCK_FOUR: FakeBlock = BLOCK_THREE.make_child("7cce080f5a49c2997a6cc65fc1cee9910fd8fc3721b7010c0b5d0873e2ac785e");
pub static ref BLOCK_FIVE: FakeBlock = BLOCK_FOUR.make_child("7b0ea919e258eb2b119eb32de56b85d12d50ac6a9f7c5909f843d6172c8ba196");
pub static ref BLOCK_THREE_TIMESTAMP: FakeBlock = BLOCK_TWO.make_child("6b834521bb753c132fdcf0e1034803ed9068e324112f8750ba93580b393a986b", Some(U256::from(1657712166)));
pub static ref BLOCK_FOUR: FakeBlock = BLOCK_THREE.make_child("7cce080f5a49c2997a6cc65fc1cee9910fd8fc3721b7010c0b5d0873e2ac785e", None);
pub static ref BLOCK_FIVE: FakeBlock = BLOCK_FOUR.make_child("7b0ea919e258eb2b119eb32de56b85d12d50ac6a9f7c5909f843d6172c8ba196", None);
pub static ref BLOCK_SIX_NO_PARENT: FakeBlock = FakeBlock::make_no_parent(6, "6b834521bb753c132fdcf0e1034803ed9068e324112f8750ba93580b393a986b");
}

Expand All @@ -45,14 +47,16 @@ pub struct FakeBlock {
pub number: BlockNumber,
pub hash: String,
pub parent_hash: String,
pub timestamp: Option<U256>,
}

impl FakeBlock {
pub fn make_child(&self, hash: &str) -> Self {
pub fn make_child(&self, hash: &str, timestamp: Option<U256>) -> Self {
FakeBlock {
number: self.number + 1,
hash: hash.to_owned(),
parent_hash: self.hash.clone(),
timestamp,
}
}

Expand All @@ -61,6 +65,7 @@ impl FakeBlock {
number,
hash: hash.to_owned(),
parent_hash: NO_PARENT.to_string(),
timestamp: None,
}
}

Expand All @@ -79,6 +84,9 @@ impl FakeBlock {
block.number = Some(self.number.into());
block.parent_hash = parent_hash;
block.hash = Some(H256(self.block_hash().as_slice().try_into().unwrap()));
if let Some(ts) = self.timestamp {
block.timestamp = ts;
}

EthereumBlock {
block: Arc::new(block),
Expand Down

0 comments on commit 167861a

Please sign in to comment.