Skip to content

Commit

Permalink
feat: gateway task processor will accurately compute the consumed gas…
Browse files Browse the repository at this point in the history
… on failure
  • Loading branch information
roberts-pumpurs committed Jan 22, 2025
1 parent 6f2ae2f commit c668576
Show file tree
Hide file tree
Showing 12 changed files with 88 additions and 42 deletions.
17 changes: 17 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ rest-service = { path = "crates/rest-service" }
retrying-solana-http-sender = { path = "crates/retrying-solana-http-sender" }
solana-gateway-task-processor = { path = "crates/solana-gateway-task-processor" }
effective-tx-sender = { path = "crates/effective-tx-sender" }
gateway-gas-computation = { path = "crates/gateway-gas-computation" }

# Relayer core
amplifier-api = { git = "https://github.com/eigerco/axelar-relayer-core.git", rev = "5c909d2" }
Expand Down
21 changes: 21 additions & 0 deletions crates/gateway-gas-computation/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[package]
name = "gateway-gas-computation"
version.workspace = true
authors.workspace = true
repository.workspace = true
homepage.workspace = true
license.workspace = true
edition.workspace = true

[dependencies]
solana-sdk.workspace = true
solana-rpc-client.workspace = true
solana-listener.workspace = true
futures.workspace = true
axelar-solana-gateway.workspace = true
eyre.workspace = true
borsh.workspace = true
axelar-executable.workspace = true

[lints]
workspace = true
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Utility crate to compute the total gas used for multi-transaction operations on the Gateway
use axelar_solana_gateway::instructions::GatewayInstruction;
use futures::stream::FuturesUnordered;
use futures::TryStreamExt as _;
Expand Down Expand Up @@ -39,7 +41,7 @@ use solana_sdk::signature::Signature;
/// May return an error if:
/// * RPC calls fail (e.g., fetching signatures or logs).
/// * Parsing of instruction data from transaction logs fails.
pub(crate) async fn compute_total_gas(
pub async fn compute_total_gas(
gateway_program_id: Pubkey,
tx: &SolanaTransaction,
rpc: &RpcClient,
Expand Down
1 change: 1 addition & 0 deletions crates/solana-axelar-relayer/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ mod tests {
gas_service_program_address: gas_service_program_id,
gas_service_config_pda,
signing_keypair,
commitment: CommitmentConfig::finalized(),
},
solana_rpc: retrying_solana_http_sender::Config {
max_concurrent_rpc_requests,
Expand Down
1 change: 1 addition & 0 deletions crates/solana-event-forwarder/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ futures.workspace = true
axelar-solana-gas-service.workspace = true
solana-gateway-task-processor.workspace = true
relayer-amplifier-api-integration.workspace = true
gateway-gas-computation.workspace = true
solana-listener.workspace = true
relayer-engine.workspace = true
tracing.workspace = true
Expand Down
3 changes: 1 addition & 2 deletions crates/solana-event-forwarder/src/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use gateway_event_stack::{
build_program_event_stack, parse_gas_service_log, parse_gateway_logs, MatchContext,
ProgramInvocationState,
};
use gateway_gas_computation::compute_total_gas;
use itertools::Itertools as _;
use relayer_amplifier_api_integration::amplifier_api::types::{
BigInt, CallEvent, CallEventMetadata, CommandId, Event, EventBase, EventId, EventMetadata,
Expand All @@ -25,8 +26,6 @@ use relayer_amplifier_api_integration::AmplifierCommand;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::Signature;

use crate::gas_cost_computation::compute_total_gas;

/// The core component that is responsible for ingesting raw Solana events.
///
/// As a result, the logs get parsed, filtererd and mapped to Amplifier API events.
Expand Down
1 change: 0 additions & 1 deletion crates/solana-event-forwarder/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,5 @@
mod component;
mod config;
mod gas_cost_computation;
pub use component::SolanaEventForwarder;
pub use config::Config;
2 changes: 2 additions & 0 deletions crates/solana-gateway-task-processor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ axelar-solana-gateway.workspace = true
axelar-solana-its.workspace = true
axelar-solana-governance.workspace = true
axelar-solana-encoding.workspace = true
gateway-gas-computation.workspace = true
solana-listener.workspace = true
tracing.workspace = true
futures.workspace = true
common-serde-utils = { workspace = true }
Expand Down
61 changes: 23 additions & 38 deletions crates/solana-gateway-task-processor/src/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use core::task::Poll;
use std::collections::VecDeque;
use std::sync::Arc;

use amplifier_api::chrono::DateTime;
use amplifier_api::chrono::{DateTime, Utc};
use amplifier_api::types::{
BigInt, CannotExecuteMessageEventV2, CannotExecuteMessageEventV2Metadata,
CannotExecuteMessageReason, Event, EventBase, EventId, EventMetadata, MessageExecutedEvent,
Expand All @@ -22,22 +22,19 @@ use axelar_solana_gateway::BytemuckedPda as _;
use effective_tx_sender::ComputeBudgetError;
use eyre::{Context as _, OptionExt as _};
use futures::stream::{FusedStream as _, FuturesOrdered, FuturesUnordered};
use futures::{SinkExt as _, StreamExt as _, TryFutureExt};
use futures::{SinkExt as _, StreamExt as _};
use num_traits::FromPrimitive as _;
use relayer_amplifier_api_integration::AmplifierCommand;
use relayer_amplifier_state::State;
use solana_client::nonblocking::rpc_client::RpcClient;
use solana_client::rpc_config::RpcTransactionConfig;
use solana_client::rpc_response::RpcSimulateTransactionResult;
use solana_listener::fetch_logs;
use solana_sdk::commitment_config::CommitmentConfig;
use solana_sdk::instruction::{Instruction, InstructionError};
use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::{Keypair, Signature};
use solana_sdk::signer::Signer as _;
use solana_sdk::transaction::TransactionError;
use solana_transaction_status::{
EncodedConfirmedTransactionWithStatusMeta, UiTransactionEncoding, UiTransactionStatusMeta,
};
use tracing::{info_span, instrument, Instrument as _};

use crate::config;
Expand Down Expand Up @@ -156,6 +153,7 @@ impl<S: State> SolanaTxPusher<S> {
name_of_the_solana_chain: self.name_on_amplifier.clone(),
gas_service_config_pda: self.config.gas_service_config_pda,
gas_service_program_id: self.config.gas_service_program_address,
commitment: self.config.commitment,
}
}
}
Expand Down Expand Up @@ -187,6 +185,7 @@ struct ConfigMetadata {
name_of_the_solana_chain: String,
gateway_root_pda: Pubkey,
gas_service_config_pda: Pubkey,
commitment: CommitmentConfig,
gas_service_program_id: Pubkey,
}

Expand Down Expand Up @@ -224,26 +223,37 @@ async fn process_task(
source: ref _source,
signature,
}) => {
let (meta, maybe_block_time) =
get_confirmed_transaction_metadata(solana_rpc_client, &signature).await?;
let tx = fetch_logs(metadata.commitment, signature, &solana_rpc_client).await?;

let tx = tx.tx();
let total_fee = gateway_gas_computation::compute_total_gas(
axelar_solana_gateway::id(),
tx,
&solana_rpc_client,
metadata.commitment,
)
.await
.unwrap_or(tx.cost_in_lamports);

// todo we need to do smart computation of the fees here
message_executed_event(
signature,
source_chain,
message_id,
MessageExecutionStatus::Reverted,
maybe_block_time,
tx.timestamp,
Token {
token_id: None,
amount: BigInt::from_u64(meta.fee),
amount: BigInt::from_u64(total_fee),
},
)
}
_ => {
// Any other error, probably happening before execution: Simulation error,
// error building an instruction, parsing pubkey, rpc transport error,
// etc.
//
// todo: if we fail here, then we don't get re-imbursed for gas we spend
// uploading the payload data. Wait for changes on Amplifeir API.
cannot_execute_message_event(
task_item.id,
source_chain,
Expand Down Expand Up @@ -273,43 +283,18 @@ async fn process_task(
Ok(())
}

async fn get_confirmed_transaction_metadata(
solana_rpc_client: &RpcClient,
signature: &Signature,
) -> Result<(UiTransactionStatusMeta, Option<i64>), eyre::Error> {
let config = RpcTransactionConfig {
encoding: Some(UiTransactionEncoding::Binary),
commitment: Some(CommitmentConfig::confirmed()),
max_supported_transaction_version: Some(0),
};

let EncodedConfirmedTransactionWithStatusMeta {
transaction: transaction_with_meta,
block_time,
..
} = solana_rpc_client
.get_transaction_with_config(signature, config)
.await?;

let meta = transaction_with_meta
.meta
.ok_or_eyre("transaction metadata not available")?;

Ok((meta, block_time))
}

fn message_executed_event(
tx_signature: Signature,
source_chain: String,
message_id: TxEvent,
status: MessageExecutionStatus,
block_time: Option<i64>,
block_time: Option<DateTime<Utc>>,
cost: Token,
) -> Event {
let event_id = EventId::tx_reverted_event_id(&tx_signature.to_string());
let metadata = MessageExecutedEventMetadata::builder().build();
let event_metadata = EventMetadata::builder()
.timestamp(block_time.and_then(|secs| DateTime::from_timestamp(secs, 0)))
.timestamp(block_time)
.extra(metadata)
.build();
let event_base = EventBase::builder()
Expand Down
6 changes: 6 additions & 0 deletions crates/solana-gateway-task-processor/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use serde::Deserialize;
use solana_sdk::commitment_config::CommitmentConfig;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::Keypair;
use typed_builder::TypedBuilder;
Expand Down Expand Up @@ -26,6 +27,11 @@ pub struct Config {
/// Can be represented as a base58 string or 64 element array `[42, 42, ..]`
#[serde(deserialize_with = "serde_utils::deserialize_keypair")]
pub signing_keypair: Keypair,

/// Commitment config to use for solana RPC interactions
#[builder(default = CommitmentConfig::finalized())]
#[serde(default = "CommitmentConfig::finalized")]
pub commitment: CommitmentConfig,
}

pub(crate) mod config_defaults {
Expand Down
12 changes: 12 additions & 0 deletions crates/solana-listener/src/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,18 @@ pub enum TxStatus {
}

impl TxStatus {
/// Assert that the TX was successful and return the inner object
///
/// # Panics
/// if the tx had failed
#[must_use]
pub fn tx(&self) -> &SolanaTransaction {
match self {
Self::Successful(solana_transaction) => solana_transaction,
Self::Failed { tx, .. } => tx,
}
}

/// Assert that the TX was successful and return the inner object
///
/// # Panics
Expand Down

0 comments on commit c668576

Please sign in to comment.