-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Guilherme Felipe da Silva <gfsilva.eng@gmail.com>
- Loading branch information
Showing
11 changed files
with
448 additions
and
85 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
use std::sync::Arc; | ||
|
||
use amplifier_api::types::{ | ||
CallEvent, CallEventMetadata, Event, EventBase, EventId, EventMetadata, GatewayV2Message, | ||
MessageId, PublishEventsRequest, TxId, | ||
}; | ||
use axelar_solana_gateway::processor::{CallContractOffchainDataEvent, GatewayEvent}; | ||
use axum::extract::{Json, State}; | ||
use axum::http::StatusCode; | ||
use axum::response::IntoResponse; | ||
use futures::SinkExt as _; | ||
use gateway_event_stack::{MatchContext, ProgramInvocationState}; | ||
use relayer_amplifier_api_integration::AmplifierCommand; | ||
use serde::Deserialize; | ||
use solana_listener::{fetch_logs, SolanaTransaction}; | ||
use solana_sdk::signature::Signature; | ||
use thiserror::Error; | ||
|
||
use crate::component::ServiceState; | ||
|
||
pub(crate) const PATH: &str = "/call-contract-offchain-data"; | ||
|
||
#[derive(Deserialize)] | ||
pub(crate) struct CallContractOffchainData { | ||
transaction_signature: Signature, | ||
data: Vec<u8>, | ||
} | ||
|
||
#[derive(Debug, Error)] | ||
pub(crate) enum CallContractOffchainDataError { | ||
/// Failed to fetch transaction logs. | ||
#[error("Failed to fetch transaction logs: {0}")] | ||
FailedToFetchTransactionLogs(#[from] eyre::Report), | ||
|
||
/// The hash of the given payload doesn't match the one emitted in the transaction logs. | ||
#[error("Payload hashes don't match")] | ||
PayloadHashMismatch, | ||
|
||
#[error("Successful transaction with CallContractOffchainDataEvent not found")] | ||
EventNotFound, | ||
|
||
#[error("Failed to relay message to Axelar Amplifier")] | ||
FailedToRelayToAmplifier, | ||
} | ||
|
||
impl IntoResponse for CallContractOffchainDataError { | ||
fn into_response(self) -> axum::response::Response { | ||
let error_tuple = match self { | ||
Self::FailedToFetchTransactionLogs(report) => { | ||
(StatusCode::INTERNAL_SERVER_ERROR, report.to_string()) | ||
} | ||
Self::FailedToRelayToAmplifier => (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()), | ||
|
||
Self::PayloadHashMismatch => (StatusCode::BAD_REQUEST, self.to_string()), | ||
Self::EventNotFound => (StatusCode::NOT_FOUND, self.to_string()), | ||
}; | ||
|
||
error_tuple.into_response() | ||
} | ||
} | ||
|
||
pub(crate) async fn handler( | ||
State(state): State<Arc<ServiceState>>, | ||
Json(payload): Json<CallContractOffchainData>, | ||
) -> Result<(), CallContractOffchainDataError> { | ||
let solana_transaction = fetch_logs(payload.transaction_signature, &state.rpc_client()).await?; | ||
|
||
let match_context = MatchContext::new(&axelar_solana_gateway::id().to_string()); | ||
let invocations = gateway_event_stack::build_program_event_stack( | ||
&match_context, | ||
solana_transaction.logs.as_slice(), | ||
gateway_event_stack::parse_gateway_logs, | ||
); | ||
|
||
for invocation in invocations { | ||
if let ProgramInvocationState::Succeeded(events) = invocation { | ||
for (idx, event) in events { | ||
if let GatewayEvent::CallContractOffchainData(event_data) = event { | ||
if event_data.payload_hash == solana_sdk::hash::hash(&payload.data).to_bytes() { | ||
let amplifier_event = build_amplifier_event( | ||
state.chain_name().to_owned(), | ||
&solana_transaction, | ||
event_data, | ||
payload, | ||
idx, | ||
); | ||
|
||
let command = AmplifierCommand::PublishEvents(PublishEventsRequest { | ||
events: vec![amplifier_event], | ||
}); | ||
|
||
let mut sender = state.amplifier_client().sender.clone(); | ||
|
||
sender.send(command).await.map_err(|_err| { | ||
CallContractOffchainDataError::FailedToRelayToAmplifier | ||
})?; | ||
|
||
return Ok(()); | ||
} | ||
|
||
return Err(CallContractOffchainDataError::PayloadHashMismatch); | ||
} | ||
} | ||
} | ||
} | ||
|
||
Err(CallContractOffchainDataError::EventNotFound) | ||
} | ||
|
||
fn build_amplifier_event( | ||
source_chain: String, | ||
transaction: &SolanaTransaction, | ||
solana_event: CallContractOffchainDataEvent, | ||
payload: CallContractOffchainData, | ||
log_index: usize, | ||
) -> Event { | ||
let tx_id = TxId(transaction.signature.to_string()); | ||
let message_id = MessageId::new(&transaction.signature.to_string(), log_index); | ||
let event_id = EventId::new(&transaction.signature.to_string(), log_index); | ||
let source_address = solana_event.sender_key.to_string(); | ||
|
||
Event::Call( | ||
CallEvent::builder() | ||
.base( | ||
EventBase::builder() | ||
.event_id(event_id) | ||
.meta(Some( | ||
EventMetadata::builder() | ||
.tx_id(Some(tx_id)) | ||
.timestamp(None) | ||
.from_address(Some(source_address.clone())) | ||
.finalized(Some(true)) | ||
.extra(CallEventMetadata::builder().build()) | ||
.build(), | ||
)) | ||
.build(), | ||
) | ||
.message( | ||
GatewayV2Message::builder() | ||
.message_id(message_id) | ||
.source_chain(source_chain) | ||
.source_address(source_address) | ||
.destination_address(solana_event.destination_contract_address) | ||
.payload_hash(solana_event.payload_hash.to_vec()) | ||
.build(), | ||
) | ||
.destination_chain(solana_event.destination_chain) | ||
.payload(payload.data) | ||
.build(), | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,18 @@ | ||
use std::net::SocketAddrV4; | ||
//! This crate provides a REST service component for the relayer. | ||
use core::net::SocketAddr; | ||
|
||
use serde::{Deserialize, Serialize}; | ||
|
||
mod call_contract_offchain_data; | ||
pub mod component; | ||
|
||
/// Configuration for the REST service. | ||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] | ||
pub struct Config { | ||
pub socket_addr: SocketAddrV4, | ||
/// Source chain name. | ||
pub chain_name: String, | ||
/// The socket address to bind to. | ||
pub socket_addr: SocketAddr, | ||
/// The maximum size of the data in a contract call with offchain data handling. | ||
pub call_contract_offchain_data_size_limit: usize, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters