feat: Store origin, destination, interchain_gas_paymaster and sequence on gas payment (#4933)

### Description

Store origin, destination, interchain_gas_paymaster and sequence on gas
payment

### Related issues

- Contributes into
https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/4271

### Backward compatibility

Yes (after the fields are added to database schema)

### Testing

E2E Ethereum and Sealevel tests (for no regressions).

Co-authored-by: Danil Nemirovsky <4614623+ameten@users.noreply.github.com>
pull/4938/head
Danil Nemirovsky 1 day ago committed by GitHub
parent 170a0fc739
commit ae2c3a870b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 29
      rust/main/agents/scraper/migration/src/m20230309_000004_create_table_gas_payment.rs
  2. 3
      rust/main/agents/scraper/src/agent.rs
  3. 26
      rust/main/agents/scraper/src/db/generated/gas_payment.rs
  4. 13
      rust/main/agents/scraper/src/db/payment.rs
  5. 22
      rust/main/agents/scraper/src/store/payments.rs
  6. 11
      rust/main/agents/scraper/src/store/storage.rs

@ -1,3 +1,5 @@
use std::borrow::BorrowMut as _;
use sea_orm::ConnectionTrait; use sea_orm::ConnectionTrait;
use sea_orm_migration::prelude::*; use sea_orm_migration::prelude::*;
@ -39,6 +41,13 @@ impl MigrationTrait for Migration {
.big_unsigned() .big_unsigned()
.not_null(), .not_null(),
) )
.col(ColumnDef::new(GasPayment::Origin).unsigned())
.col(ColumnDef::new(GasPayment::Destination).unsigned())
.col(
ColumnDef::new_with_type(GasPayment::InterchainGasPaymaster, Address)
.borrow_mut(),
)
.col(ColumnDef::new(GasPayment::Sequence).big_integer())
.foreign_key( .foreign_key(
ForeignKey::create() ForeignKey::create()
.from_col(GasPayment::TxId) .from_col(GasPayment::TxId)
@ -49,6 +58,16 @@ impl MigrationTrait for Migration {
.from_col(GasPayment::Domain) .from_col(GasPayment::Domain)
.to(Domain::Table, Domain::Id), .to(Domain::Table, Domain::Id),
) )
.foreign_key(
ForeignKey::create()
.from_col(GasPayment::Origin)
.to(Domain::Table, Domain::Id),
)
.foreign_key(
ForeignKey::create()
.from_col(GasPayment::Destination)
.to(Domain::Table, Domain::Id),
)
.index( .index(
Index::create() Index::create()
// don't need domain because TxId includes it // don't need domain because TxId includes it
@ -136,6 +155,16 @@ pub enum GasPayment {
/// Used to disambiguate duplicate payments from multiple payments made in /// Used to disambiguate duplicate payments from multiple payments made in
/// same transaction. /// same transaction.
LogIndex, LogIndex,
/// Domain ID of the chain the payment was made on; technically duplicating
/// field Domain, but Domain becomes ambiguous as we add Destination domain as well.
Origin,
/// Domain ID of the chain the payment was made for.
Destination,
/// Interchain Gas Paymaster contract address
InterchainGasPaymaster,
/// Sequence of this payment for indexing by agent. It can be null if agent
/// does not use sequence-aware indexing.
Sequence,
} }
#[derive(Iden)] #[derive(Iden)]

@ -62,8 +62,9 @@ impl BaseAgent for Scraper {
let chain_setup = settings.chain_setup(domain).expect("Missing chain config"); let chain_setup = settings.chain_setup(domain).expect("Missing chain config");
let store = HyperlaneDbStore::new( let store = HyperlaneDbStore::new(
db.clone(), db.clone(),
chain_setup.addresses.mailbox,
domain.clone(), domain.clone(),
chain_setup.addresses.mailbox,
chain_setup.addresses.interchain_gas_paymaster,
settings settings
.build_provider(domain, &metrics.clone()) .build_provider(domain, &metrics.clone())
.await? .await?

@ -21,6 +21,10 @@ pub struct Model {
pub gas_amount: BigDecimal, pub gas_amount: BigDecimal,
pub tx_id: i64, pub tx_id: i64,
pub log_index: i64, pub log_index: i64,
pub origin: Option<i32>,
pub destination: Option<i32>,
pub interchain_gas_paymaster: Option<Vec<u8>>,
pub sequence: Option<i64>,
} }
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] #[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
@ -33,6 +37,10 @@ pub enum Column {
GasAmount, GasAmount,
TxId, TxId,
LogIndex, LogIndex,
Origin,
Destination,
InterchainGasPaymaster,
Sequence,
} }
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)] #[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
@ -49,7 +57,9 @@ impl PrimaryKeyTrait for PrimaryKey {
#[derive(Copy, Clone, Debug, EnumIter)] #[derive(Copy, Clone, Debug, EnumIter)]
pub enum Relation { pub enum Relation {
Destination,
Domain, Domain,
Origin,
Transaction, Transaction,
} }
@ -65,6 +75,14 @@ impl ColumnTrait for Column {
Self::GasAmount => ColumnType::Decimal(Some((78u32, 0u32))).def(), Self::GasAmount => ColumnType::Decimal(Some((78u32, 0u32))).def(),
Self::TxId => ColumnType::BigInteger.def(), Self::TxId => ColumnType::BigInteger.def(),
Self::LogIndex => ColumnType::BigInteger.def(), Self::LogIndex => ColumnType::BigInteger.def(),
Self::Origin => ColumnType::Integer.def().null(),
Self::Destination => ColumnType::Integer.def().null(),
Self::InterchainGasPaymaster => {
ColumnType::Binary(sea_orm::sea_query::BlobSize::Blob(None))
.def()
.null()
}
Self::Sequence => ColumnType::BigInteger.def().null(),
} }
} }
} }
@ -72,10 +90,18 @@ impl ColumnTrait for Column {
impl RelationTrait for Relation { impl RelationTrait for Relation {
fn def(&self) -> RelationDef { fn def(&self) -> RelationDef {
match self { match self {
Self::Destination => Entity::belongs_to(super::domain::Entity)
.from(Column::Destination)
.to(super::domain::Column::Id)
.into(),
Self::Domain => Entity::belongs_to(super::domain::Entity) Self::Domain => Entity::belongs_to(super::domain::Entity)
.from(Column::Domain) .from(Column::Domain)
.to(super::domain::Column::Id) .to(super::domain::Column::Id)
.into(), .into(),
Self::Origin => Entity::belongs_to(super::domain::Entity)
.from(Column::Origin)
.to(super::domain::Column::Id)
.into(),
Self::Transaction => Entity::belongs_to(super::transaction::Entity) Self::Transaction => Entity::belongs_to(super::transaction::Entity)
.from(Column::TxId) .from(Column::TxId)
.to(super::transaction::Column::Id) .to(super::transaction::Column::Id)

@ -3,7 +3,7 @@ use itertools::Itertools;
use sea_orm::{prelude::*, ActiveValue::*, Insert, QuerySelect}; use sea_orm::{prelude::*, ActiveValue::*, Insert, QuerySelect};
use tracing::{debug, instrument, trace}; use tracing::{debug, instrument, trace};
use hyperlane_core::{h256_to_bytes, InterchainGasPayment, LogMeta}; use hyperlane_core::{address_to_bytes, h256_to_bytes, InterchainGasPayment, LogMeta, H256};
use migration::OnConflict; use migration::OnConflict;
use crate::conversions::u256_to_decimal; use crate::conversions::u256_to_decimal;
@ -14,6 +14,7 @@ use super::generated::gas_payment;
pub struct StorablePayment<'a> { pub struct StorablePayment<'a> {
pub payment: &'a InterchainGasPayment, pub payment: &'a InterchainGasPayment,
pub sequence: Option<i64>,
pub meta: &'a LogMeta, pub meta: &'a LogMeta,
/// The database id of the transaction the payment was made in /// The database id of the transaction the payment was made in
pub txn_id: i64, pub txn_id: i64,
@ -24,9 +25,11 @@ impl ScraperDb {
pub async fn store_payments( pub async fn store_payments(
&self, &self,
domain: u32, domain: u32,
interchain_gas_paymaster: &H256,
payments: impl Iterator<Item = StorablePayment<'_>>, payments: impl Iterator<Item = StorablePayment<'_>>,
) -> Result<u64> { ) -> Result<u64> {
let latest_id_before = self.latest_payment_id(domain).await?; let latest_id_before = self.latest_payment_id(domain).await?;
let interchain_gas_paymaster = address_to_bytes(interchain_gas_paymaster);
// we have a race condition where a message may not have been scraped yet even // we have a race condition where a message may not have been scraped yet even
let models = payments let models = payments
@ -39,6 +42,10 @@ impl ScraperDb {
gas_amount: Set(u256_to_decimal(storable.payment.gas_amount)), gas_amount: Set(u256_to_decimal(storable.payment.gas_amount)),
tx_id: Unchanged(storable.txn_id), tx_id: Unchanged(storable.txn_id),
log_index: Unchanged(storable.meta.log_index.as_u64() as i64), log_index: Unchanged(storable.meta.log_index.as_u64() as i64),
origin: Set(Some(domain as i32)),
destination: Set(Some(storable.payment.destination as i32)),
interchain_gas_paymaster: Set(Some(interchain_gas_paymaster.clone())),
sequence: Set(storable.sequence),
}) })
.collect_vec(); .collect_vec();
@ -61,6 +68,10 @@ impl ScraperDb {
gas_payment::Column::TimeCreated, gas_payment::Column::TimeCreated,
gas_payment::Column::Payment, gas_payment::Column::Payment,
gas_payment::Column::GasAmount, gas_payment::Column::GasAmount,
gas_payment::Column::Origin,
gas_payment::Column::Destination,
gas_payment::Column::InterchainGasPaymaster,
gas_payment::Column::Sequence,
]) ])
.to_owned(), .to_owned(),
) )

@ -28,16 +28,30 @@ impl HyperlaneLogStore<InterchainGasPayment> for HyperlaneDbStore {
let storable = payments let storable = payments
.iter() .iter()
.filter_map(|(payment, meta)| { .filter_map(|(payment, meta)| {
txns.get(&meta.transaction_id) txns.get(&meta.transaction_id).map(|txn| {
.map(|txn| (payment.inner(), meta, txn.id)) (
payment.inner(),
payment.sequence.map(|v| v as i64),
meta,
txn.id,
)
})
}) })
.map(|(payment, meta, txn_id)| StorablePayment { .map(|(payment, sequence, meta, txn_id)| StorablePayment {
payment, payment,
sequence,
meta, meta,
txn_id, txn_id,
}); });
let stored = self.db.store_payments(self.domain.id(), storable).await?; let stored = self
.db
.store_payments(
self.domain.id(),
&self.interchain_gas_paymaster_address,
storable,
)
.await?;
Ok(stored as u32) Ok(stored as u32)
} }
} }

@ -28,9 +28,10 @@ const CHUNK_SIZE: usize = 50;
/// connections needed to scrape the contracts on a single blockchain. /// connections needed to scrape the contracts on a single blockchain.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct HyperlaneDbStore { pub struct HyperlaneDbStore {
pub(crate) mailbox_address: H256,
pub(crate) domain: HyperlaneDomain,
pub(crate) db: ScraperDb, pub(crate) db: ScraperDb,
pub(crate) domain: HyperlaneDomain,
pub(crate) mailbox_address: H256,
pub(crate) interchain_gas_paymaster_address: H256,
provider: Arc<dyn HyperlaneProvider>, provider: Arc<dyn HyperlaneProvider>,
cursor: Arc<BlockCursor>, cursor: Arc<BlockCursor>,
} }
@ -39,8 +40,9 @@ pub struct HyperlaneDbStore {
impl HyperlaneDbStore { impl HyperlaneDbStore {
pub async fn new( pub async fn new(
db: ScraperDb, db: ScraperDb,
mailbox_address: H256,
domain: HyperlaneDomain, domain: HyperlaneDomain,
mailbox_address: H256,
interchain_gas_paymaster_address: H256,
provider: Arc<dyn HyperlaneProvider>, provider: Arc<dyn HyperlaneProvider>,
index_settings: &IndexSettings, index_settings: &IndexSettings,
) -> Result<Self> { ) -> Result<Self> {
@ -51,8 +53,9 @@ impl HyperlaneDbStore {
Ok(Self { Ok(Self {
db, db,
domain, domain,
provider,
mailbox_address, mailbox_address,
interchain_gas_paymaster_address,
provider,
cursor, cursor,
}) })
} }

Loading…
Cancel
Save