#![cfg_attr(not(feature = "std"), no_std)]
use codec::{Decode, Encode, MaxEncodedLen};
use frame_support::traits::{ReservableCurrency, Time};
use scale_info::TypeInfo;
pub use t3rn_abi::recode::Codec as T3rnCodec;
pub use t3rn_types::{gateway, types::Bytes};
#[cfg(feature = "std")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "no_std")]
use sp_runtime::RuntimeDebug as Debug;
use sp_runtime::{
traits::{BlakeTwo256, IdentifyAccount, Verify},
MultiSignature,
};
pub use gateway_inbound_protocol::GatewayInboundProtocol;
use frame_support::sp_runtime::traits::Zero;
use sp_std::{prelude::*, vec};
#[cfg(feature = "std")]
use std::fmt::Debug;
pub mod account_manager;
pub mod attesters;
pub mod circuit;
pub mod claimable;
pub mod clock;
pub mod common;
pub mod contract_metadata;
pub mod contracts_registry;
pub mod executors;
pub mod gateway_inbound_protocol;
pub mod light_client;
pub mod match_format;
pub mod monetary;
pub mod portal;
pub mod rewards;
pub mod signature_caster;
pub mod storage;
pub mod threevm;
pub mod transfers;
pub mod volatile;
pub mod xdns;
pub mod xtx;
use crate::attesters::LatencyStatus;
use t3rn_types::sfx::{SecurityLvl, TargetId};
pub type ChainId = [u8; 4];
pub type ExecutionSource = [u8; 32];
pub const EMPTY_EXECUTION_SOURCE: [u8; 32] = [0u8; 32];
pub fn execution_source_to_option(source: ExecutionSource) -> Option<ExecutionSource> {
if source == EMPTY_EXECUTION_SOURCE {
None
} else {
Some(source)
}
}
#[derive(Clone, Eq, PartialEq, PartialOrd, Ord, Encode, Decode, Debug, TypeInfo)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub enum GatewayType {
ProgrammableInternal(u32),
ProgrammableExternal(u32),
TxOnly(u32),
OnCircuit(u32),
}
impl Default for GatewayType {
fn default() -> GatewayType {
GatewayType::ProgrammableExternal(0)
}
}
impl GatewayType {
pub fn fetch_nonce(self) -> u32 {
match self {
Self::ProgrammableInternal(nonce) => nonce,
Self::ProgrammableExternal(nonce) => nonce,
Self::OnCircuit(nonce) => nonce,
Self::TxOnly(nonce) => nonce,
}
}
}
#[derive(Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(Default)]
pub enum TreasuryAccount {
#[default]
Treasury,
Escrow,
Fee,
Parachain,
Slash,
}
pub trait TreasuryAccountProvider<Account> {
fn get_treasury_account(treasury_account: TreasuryAccount) -> Account;
}
#[derive(Clone, Eq, PartialEq, Encode, Decode, Debug, PartialOrd, Ord, TypeInfo)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(Default)]
pub enum GatewayVendor {
Polkadot,
Kusama,
#[default]
Rococo,
Ethereum,
Sepolia,
XBI,
Attesters,
}
use sp_std::slice::Iter;
impl GatewayVendor {
pub fn iterator() -> Iter<'static, GatewayVendor> {
static VENDORS: [GatewayVendor; 7] = [
GatewayVendor::Polkadot,
GatewayVendor::Kusama,
GatewayVendor::Rococo,
GatewayVendor::Ethereum,
GatewayVendor::Sepolia,
GatewayVendor::XBI,
GatewayVendor::Attesters,
];
VENDORS.iter()
}
pub fn eta_per_speed_mode_in_epochs<Epoch: From<u32>>(&self, speed_mode: &SpeedMode) -> Epoch {
match self {
GatewayVendor::Polkadot
| GatewayVendor::Kusama
| GatewayVendor::Rococo
| GatewayVendor::Attesters
| GatewayVendor::XBI => match speed_mode {
SpeedMode::Fast => 4u32.into(),
SpeedMode::Rational => 6u32.into(),
SpeedMode::Finalized => 8u32.into(),
SpeedMode::Instant => 1u32.into(),
},
GatewayVendor::Ethereum | GatewayVendor::Sepolia => match speed_mode {
SpeedMode::Fast => 1u32.into(),
SpeedMode::Rational => 2u32.into(),
SpeedMode::Finalized => 3u32.into(),
SpeedMode::Instant => 1u32.into(),
},
}
}
pub fn calculate_offsets<BlockNumber: From<u32> + Clone + Saturating + Zero>(
&self,
speed_mode: &SpeedMode,
emergency_offset: BlockNumber,
epoch_history: Option<Vec<EpochEstimate<BlockNumber>>>,
) -> (BlockNumber, BlockNumber) {
let eta_in_epochs = self.eta_per_speed_mode_in_epochs::<BlockNumber>(speed_mode);
let (submit_by_local_offset, submit_by_remote_offset) = epoch_history
.and_then(|history| history.last().cloned())
.map(|record| {
(
record
.moving_average_local
.clone()
.saturating_mul(eta_in_epochs.clone()),
record.moving_average_remote.saturating_mul(eta_in_epochs),
)
})
.unwrap_or_else(|| (emergency_offset.clone(), emergency_offset.clone()));
if submit_by_local_offset.is_zero() || submit_by_remote_offset.is_zero() {
(emergency_offset.clone(), emergency_offset)
} else {
(submit_by_local_offset, submit_by_remote_offset)
}
}
}
#[cfg(test)]
mod tests_gateway_vendor {
use super::*;
#[test]
fn test_calculate_offsets_no_history() {
let vendor = GatewayVendor::Polkadot;
let speed_mode = SpeedMode::Fast;
let emergency_offset = 10;
let (local_offset, remote_offset) =
vendor.calculate_offsets::<u32>(&speed_mode, emergency_offset, None);
assert_eq!(local_offset, emergency_offset);
assert_eq!(remote_offset, emergency_offset);
}
#[test]
fn test_calculate_offsets_zero_average() {
let vendor = GatewayVendor::Polkadot;
let speed_mode = SpeedMode::Fast;
let emergency_offset = 10u32;
let epoch_history = Some(vec![EpochEstimate {
local: 20,
remote: 30,
moving_average_local: 0,
moving_average_remote: 0,
}]);
let (local_offset, remote_offset) =
vendor.calculate_offsets::<u32>(&speed_mode, emergency_offset, epoch_history);
assert_eq!(local_offset, emergency_offset);
assert_eq!(remote_offset, emergency_offset);
}
#[test]
fn test_calculate_offsets_non_zero_average() {
let vendor = GatewayVendor::Polkadot;
let speed_mode = SpeedMode::Fast;
let emergency_offset = 10u32;
let epoch_history = Some(vec![EpochEstimate {
local: 20,
remote: 30,
moving_average_local: 5,
moving_average_remote: 7,
}]);
let (local_offset, remote_offset) =
vendor.calculate_offsets::<u32>(&speed_mode, emergency_offset, epoch_history);
assert_eq!(local_offset, 5 * 4); assert_eq!(remote_offset, 7 * 4); }
#[test]
fn test_calculate_offsets_with_speed_modes() {
let vendor = GatewayVendor::Polkadot;
let emergency_offset = 10u32;
let epoch_history = Some(vec![EpochEstimate {
local: 20,
remote: 30,
moving_average_local: 5,
moving_average_remote: 7,
}]);
for (expected_mul, speed_mode) in &[
(4, SpeedMode::Fast),
(6, SpeedMode::Rational),
(8, SpeedMode::Finalized),
] {
let (local_offset, remote_offset) = vendor.calculate_offsets::<u32>(
speed_mode,
emergency_offset,
epoch_history.clone(),
);
assert_eq!(local_offset, 5 * expected_mul); assert_eq!(remote_offset, 7 * expected_mul); }
}
}
#[derive(Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(Default)]
pub enum ExecutionVendor {
#[default]
Substrate,
EVM,
}
impl TokenInfo {
pub fn match_execution_vendor(&self) -> ExecutionVendor {
match self {
TokenInfo::Substrate(_) => ExecutionVendor::Substrate,
TokenInfo::Ethereum(_) => ExecutionVendor::EVM,
}
}
}
#[derive(Clone, Eq, PartialEq, Encode, Decode, Debug)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub struct GenericPrimitivesHeader {
pub parent_hash: Option<sp_core::hash::H256>,
pub number: u64,
pub state_root: Option<sp_core::hash::H256>,
pub extrinsics_root: Option<sp_core::hash::H256>,
pub digest: Option<sp_runtime::generic::Digest>,
}
#[derive(Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub struct GatewayPointer {
pub id: ChainId,
pub vendor: GatewayVendor,
pub gateway_type: GatewayType,
}
#[derive(Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo, Default)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub struct GatewayGenesisConfig {
pub modules_encoded: Option<Vec<u8>>,
pub extrinsics_version: u8,
pub genesis_hash: Vec<u8>,
}
#[derive(Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub enum TokenInfo {
Substrate(SubstrateToken),
Ethereum(EthereumToken),
}
#[derive(Debug, Clone, Eq, PartialEq, Encode, Default, Decode, TypeInfo, MaxEncodedLen)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub enum SpeedMode {
Fast,
Rational,
#[default]
Finalized,
Instant,
}
#[derive(Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub struct SubstrateToken {
pub id: u32,
pub symbol: Vec<u8>,
pub decimals: u8,
}
#[derive(Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub struct EthereumToken {
pub symbol: Vec<u8>,
pub decimals: u8,
pub address: Option<[u8; 20]>,
}
impl Default for TokenInfo {
fn default() -> Self {
Self::Substrate(SubstrateToken {
symbol: Encode::encode("TKN"),
decimals: 9,
id: 42,
})
}
}
#[derive(Eq, PartialEq, Encode, Decode, Debug, Clone, Default, TypeInfo)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub struct Compose<Account, Balance> {
pub name: Vec<u8>,
pub code_txt: Vec<u8>,
pub exec_type: Vec<u8>,
pub dest: Account,
pub value: Balance,
pub bytes: Vec<u8>,
pub input_data: Vec<u8>,
}
pub type FetchContractsResult = Result<Vec<u8>, ContractAccessError>;
#[derive(Eq, PartialEq, Encode, Decode, Debug, Clone)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub enum ReadLatestGatewayHeight {
Success { encoded_height: Vec<u8> },
Error,
}
#[derive(Eq, PartialEq, Encode, Decode, Debug, Clone)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub enum ContractAccessError {
DoesntExist,
IsTombstone,
}
pub type GenericAddress = sp_runtime::MultiAddress<sp_runtime::AccountId32, ()>;
pub trait EscrowTrait<T: frame_system::Config> {
type Currency: ReservableCurrency<T::AccountId>;
type Time: Time;
}
#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, TypeInfo)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub struct CircuitOutboundMessage {
pub name: Bytes,
pub module_name: Bytes,
pub method_name: Bytes,
pub sender: Option<Bytes>,
pub target: Option<Bytes>,
pub arguments: Vec<Bytes>,
pub expected_output: Vec<GatewayExpectedOutput>,
pub extra_payload: Option<ExtraMessagePayload>,
pub gateway_vendor: GatewayVendor,
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub struct RpcPayloadUnsigned<'a> {
pub method_name: &'a str,
pub params: Vec<Bytes>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub struct RpcPayloadSigned<'a> {
pub method_name: &'a str,
pub signed_extrinsic: Bytes,
}
impl CircuitOutboundMessage {
pub fn to_jsonrpc_unsigned(&self) -> Result<RpcPayloadUnsigned, &'static str> {
let method_name: &str = sp_std::str::from_utf8(&self.name[..])
.map_err(|_| "`Can't decode method name to &str")?;
Ok(RpcPayloadUnsigned {
method_name,
params: self.arguments.clone(),
})
}
pub fn to_jsonrpc_signed(&self) -> Result<RpcPayloadSigned, &'static str> {
let method_name: &str = sp_std::str::from_utf8(&self.name[..])
.map_err(|_| "`Can't decode method name to &str")?;
let signed_ext = self
.extra_payload
.as_ref()
.map(|payload| payload.tx_signed.clone())
.ok_or("no signed extrinsic provided")?;
Ok(RpcPayloadSigned {
method_name,
signed_extrinsic: signed_ext,
})
}
}
#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, TypeInfo)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub enum ProofTriePointer {
State,
Transaction,
Receipts,
}
#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, TypeInfo)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub struct CircuitInboundResult {
pub result_format: Bytes,
pub proof_type: ProofTriePointer,
}
#[derive(Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub struct XDNSTopology<AccountId> {
pub gateways: Vec<crate::xdns::FullGatewayRecord<AccountId>>,
pub assets: Vec<crate::xdns::TokenRecord>,
}
#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, TypeInfo)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub enum GatewayExpectedOutput {
Storage {
key: Vec<Vec<u8>>,
value: Vec<Option<Bytes>>,
},
Events { signatures: Vec<Bytes> },
Extrinsic {
block_height: Option<u64>,
},
Output { output: Bytes },
}
#[derive(Encode, Decode, Clone, Default, Debug, PartialEq, Eq, TypeInfo)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub struct GatewaysOverview<BlockNumber> {
data: Vec<(TargetId, BlockNumber, Vec<GatewayActivity<BlockNumber>>)>,
}
#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, TypeInfo)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub struct FinalityVerifierActivity<BlockNumber> {
pub verifier: GatewayVendor,
pub reported_at: BlockNumber,
pub justified_height: BlockNumber,
pub finalized_height: BlockNumber,
pub updated_height: BlockNumber,
pub epoch: BlockNumber,
pub is_active: bool,
}
impl<BlockNumber: Zero + Clone + Saturating + Default + Ord> FinalityVerifierActivity<BlockNumber> {
pub fn new_for_finalized_compare(
reported_at: BlockNumber,
finalized_height: BlockNumber,
) -> Self {
FinalityVerifierActivity {
verifier: Default::default(),
reported_at,
justified_height: Zero::zero(),
finalized_height,
updated_height: Zero::zero(),
epoch: Zero::zero(),
is_active: false,
}
}
pub fn determine_finalized_reports_increase(
activities: &[FinalityVerifierActivity<BlockNumber>],
) -> Option<(BlockNumber, BlockNumber)> {
if activities.len() < 2 {
return None
}
let mut sorted_activities: Vec<_> = activities.iter().collect();
sorted_activities.sort_by(|a, b| a.reported_at.cmp(&b.reported_at));
let oldest_report = sorted_activities.first()?;
let latest_report = sorted_activities.last()?;
if oldest_report.finalized_height.is_zero()
|| oldest_report.reported_at.is_zero()
|| latest_report.finalized_height.is_zero()
|| latest_report.reported_at.is_zero()
{
return None
}
let finalized_height_increase = latest_report
.finalized_height
.clone()
.saturating_sub(oldest_report.finalized_height.clone());
let reported_at_increase = latest_report
.reported_at
.clone()
.saturating_sub(oldest_report.reported_at.clone());
Some((finalized_height_increase, reported_at_increase))
}
}
#[cfg(test)]
mod tests_finality_verifier_activity {
use super::*;
#[test]
fn test_zero_cases_return_none() {
let activity1 = FinalityVerifierActivity::<u32>::new_for_finalized_compare(0u32, 100u32);
let activity2 = FinalityVerifierActivity::new_for_finalized_compare(100u32, 0u32);
let activities = vec![activity1, activity2];
assert_eq!(
FinalityVerifierActivity::<u32>::determine_finalized_reports_increase(&activities),
None
);
}
#[test]
fn test_length_below_two_returns_none() {
let activity = FinalityVerifierActivity::<u32>::new_for_finalized_compare(50u32, 100u32);
let activities = vec![activity];
assert_eq!(
FinalityVerifierActivity::<u32>::determine_finalized_reports_increase(&activities),
None
);
}
#[test]
fn test_determines_increase_for_two_elements() {
let activity1 = FinalityVerifierActivity::<u32>::new_for_finalized_compare(50u32, 100u32);
let activity2 = FinalityVerifierActivity::<u32>::new_for_finalized_compare(100u32, 200u32);
let activities = vec![activity1, activity2];
let result =
FinalityVerifierActivity::<u32>::determine_finalized_reports_increase(&activities);
assert_eq!(result, Some((100u32, 50u32)));
}
#[test]
fn test_determines_none_for_two_non_increasing_elements() {
let activity1 = FinalityVerifierActivity::<u32>::new_for_finalized_compare(50u32, 100u32);
let activity2 = FinalityVerifierActivity::<u32>::new_for_finalized_compare(100u32, 100u32);
let activities = vec![activity1, activity2];
let result =
FinalityVerifierActivity::<u32>::determine_finalized_reports_increase(&activities);
assert_eq!(result, Some((0u32, 50u32)));
}
#[test]
fn test_determines_increase_for_three_elements() {
let activity1 = FinalityVerifierActivity::<u32>::new_for_finalized_compare(50u32, 100u32);
let activity2 = FinalityVerifierActivity::<u32>::new_for_finalized_compare(75u32, 150u32);
let activity3 = FinalityVerifierActivity::<u32>::new_for_finalized_compare(100u32, 200u32);
let activities = vec![activity1, activity2, activity3];
let result =
FinalityVerifierActivity::<u32>::determine_finalized_reports_increase(&activities);
assert_eq!(result, Some((100u32, 50u32)));
}
}
impl<BlockNumber: Zero> Default for FinalityVerifierActivity<BlockNumber> {
fn default() -> Self {
FinalityVerifierActivity {
verifier: GatewayVendor::Rococo,
reported_at: Zero::zero(),
justified_height: Zero::zero(),
finalized_height: Zero::zero(),
updated_height: Zero::zero(),
epoch: Zero::zero(),
is_active: false,
}
}
}
#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, TypeInfo)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub struct GatewayActivity<BlockNumber> {
pub gateway_id: TargetId,
pub reported_at: BlockNumber,
pub justified_height: BlockNumber,
pub finalized_height: BlockNumber,
pub updated_height: BlockNumber,
pub attestation_latency: Option<LatencyStatus>,
pub security_lvl: SecurityLvl,
pub is_active: bool,
}
#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, TypeInfo)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub struct ExtraMessagePayload {
pub signer: Bytes,
pub module_name: Bytes,
pub method_name: Bytes,
pub call_bytes: Bytes,
pub signature: Bytes,
pub extra: Bytes,
pub tx_signed: Bytes,
pub custom_payload: Option<Bytes>,
}
pub fn retrieve_gateway_pointers(gateway_id: ChainId) -> Result<Vec<GatewayPointer>, &'static str> {
Ok(vec![GatewayPointer {
id: gateway_id,
gateway_type: GatewayType::ProgrammableExternal(0),
vendor: GatewayVendor::Rococo,
}])
}
pub type AccountId = <<MultiSignature as Verify>::Signer as IdentifyAccount>::AccountId;
pub type AccountPublic = <MultiSignature as Verify>::Signer;
pub type BlockNumber = u32;
use crate::xdns::EpochEstimate;
use sp_runtime::traits::Saturating;
pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic;
pub type Header = sp_runtime::generic::Header<BlockNumber, BlakeTwo256>;
pub type Block = sp_runtime::generic::Block<Header, UncheckedExtrinsic>;
pub type Nonce = u32;
pub type Balance = u128;
pub type Hash = sp_core::H256;