// Copyright (c) Subzero Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

//! # Rialo Shared Types
//!
//! This crate contains shared RPC types used across the Rialo ecosystem,
//! including the CDK and other components. These types are kept separate
//! from implementation-specific functionality to minimize dependencies.

use serde::{Deserialize, Serialize};

/// Account information that is returned by the Rialo RPC API.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct AccountInfoResponseValue {
    pub kelvin: u64,
    pub owner: String,
    pub data: Vec<String>,
    pub executable: bool,
    #[serde(rename = "rentEpoch")]
    pub rent_epoch: u64,
    pub space: usize,
}

/// Subscription returned by Rialo RPC to describe a `Subscription` object stored by a user for
/// a given topic.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Subscription {
    /// Subscription kind.
    pub kind: SubscriptionKind,
    /// Topic.
    pub topic: String,
    /// Instructions to be executed.
    pub instructions: Vec<Instruction>,
    /// Subscriber pubkey in base58.
    pub subscriber: String,
    /// The account pubkey in base58 that stores the event data. Used when
    /// the subscription precisely targets a specific event account.
    pub event_account: Option<String>,
    /// The timestamp range to trigger the transaction, if provided.
    /// Left boundary is inclusive, right boundary is exclusive.
    pub timestamp_range: Option<(u64, u64)>,
}

/// A representation of an instruction in Subscription.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Instruction {
    /// The program ID invoked, as a base58-encoded string.
    pub program_id: String,
    /// Metadata for the accounts involved in the instruction.
    pub accounts: Vec<AccountMeta>,
    /// Opaque binary data passed to the program.
    pub data: Vec<u8>,
}

/// Metadata about an account used in an Instruction.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct AccountMeta {
    /// The account's public key, in base58-encoded form.
    pub pubkey: String,
    /// Whether the account must sign the transaction.
    pub is_signer: bool,
    /// Whether the account can be written to during instruction execution.
    pub is_writable: bool,
}

/// Subscription kind of the Subscription.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum SubscriptionKind {
    /// Persistent subscription which fires every time a matching event is received
    Persistent,
    /// One-shot subscription which fires a single time when a matching event is received
    OneShot,
}

/// Errors that can occur during instruction execution.
///
/// These errors represent various failure modes that can occur when
/// executing program instructions within a transaction.
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub enum InstructionError {
    /// Deprecated! Use CustomError instead!
    /// The program instruction returned an error
    GenericError,

    /// The arguments provided to a program were invalid
    InvalidArgument,

    /// An instruction's data contents were invalid
    InvalidInstructionData,

    /// An account's data contents was invalid
    InvalidAccountData,

    /// An account's data was too small
    AccountDataTooSmall,

    /// An account's balance was too small to complete the instruction
    InsufficientFunds,

    /// The account did not have the expected program id
    IncorrectProgramId,

    /// A signature was required but not found
    MissingRequiredSignature,

    /// An initialize instruction was sent to an account that has already been initialized.
    AccountAlreadyInitialized,

    /// An attempt to operate on an account that hasn't been initialized.
    UninitializedAccount,

    /// Program's instruction kelvin balance does not equal the balance after the instruction
    UnbalancedInstruction,

    /// Program illegally modified an account's program id
    ModifiedProgramId,

    /// Program spent the kelvins of an account that doesn't belong to it
    ExternalAccountKelvinSpend,

    /// Program modified the data of an account that doesn't belong to it
    ExternalAccountDataModified,

    /// Read-only account's kelvins modified
    ReadonlyKelvinChange,

    /// Read-only account's data was modified
    ReadonlyDataModified,

    /// An account was referenced more than once in a single instruction
    // Deprecated, instructions can now contain duplicate accounts
    DuplicateAccountIndex,

    /// Executable bit on account changed, but shouldn't have
    ExecutableModified,

    /// Rent_epoch account changed, but shouldn't have
    RentEpochModified,

    /// The instruction expected additional account keys
    NotEnoughAccountKeys,

    /// Program other than the account's owner changed the size of the account data
    AccountDataSizeChanged,

    /// The instruction expected an executable account
    AccountNotExecutable,

    /// Failed to borrow a reference to account data, already borrowed
    AccountBorrowFailed,

    /// Account data has an outstanding reference after a program's execution
    AccountBorrowOutstanding,

    /// The same account was multiply passed to an on-chain program's entrypoint, but the program
    /// modified them differently.  A program can only modify one instance of the account because
    /// the runtime cannot determine which changes to pick or how to merge them if both are modified
    DuplicateAccountOutOfSync,

    /// Allows on-chain programs to implement program-specific error types and see them returned
    /// by the Solana runtime. A program-specific error may be any type that is represented as
    /// or serialized to a u32 integer.
    Custom(u32),

    /// The return value from the program was invalid.  Valid errors are either a defined builtin
    /// error value or a user-defined error in the lower 32 bits.
    InvalidError,

    /// Executable account's data was modified
    ExecutableDataModified,

    /// Executable account's kelvins modified
    ExecutableKelvinChange,

    /// Executable accounts must be rent exempt
    ExecutableAccountNotRentExempt,

    /// Unsupported program id
    UnsupportedProgramId,

    /// Cross-program invocation call depth too deep
    CallDepth,

    /// An account required by the instruction is missing
    MissingAccount,

    /// Cross-program invocation reentrancy not allowed for this instruction
    ReentrancyNotAllowed,

    /// Length of the seed is too long for address generation
    MaxSeedLengthExceeded,

    /// Provided seeds do not result in a valid address
    InvalidSeeds,

    /// Failed to reallocate account data of this length
    InvalidRealloc,

    /// Computational budget exceeded
    ComputationalBudgetExceeded,

    /// Cross-program invocation with unauthorized signer or writable account
    PrivilegeEscalation,

    /// Failed to create program execution environment
    ProgramEnvironmentSetupFailure,

    /// Program failed to complete
    ProgramFailedToComplete,

    /// Program failed to compile
    ProgramFailedToCompile,

    /// Account is immutable
    Immutable,

    /// Incorrect authority provided
    IncorrectAuthority,

    /// Failed to serialize or deserialize account data
    ///
    /// Warning: This error should never be emitted by the runtime.
    ///
    /// This error includes strings from the underlying 3rd party Borsh crate
    /// which can be dangerous because the error strings could change across
    /// Borsh versions. Only programs can use this error because they are
    /// consistent across Solana software versions.
    ///
    BorshIoError(String),

    /// An account does not have enough kelvins to be rent-exempt
    AccountNotRentExempt,

    /// Invalid account owner
    InvalidAccountOwner,

    /// Program arithmetic overflowed
    ArithmeticOverflow,

    /// Unsupported sysvar
    UnsupportedSysvar,

    /// Illegal account owner
    IllegalOwner,

    /// Accounts data allocations exceeded the maximum allowed per transaction
    MaxAccountsDataAllocationsExceeded,

    /// Max accounts exceeded
    MaxAccountsExceeded,

    /// Max instruction trace length exceeded
    MaxInstructionTraceLengthExceeded,

    /// Builtin programs must consume compute units
    BuiltinProgramsMustConsumeComputeUnits,
    // Note: For any new error added here an equivalent ProgramError and its
    // conversions must also be added
}

/// Reasons a transaction might be rejected.
///
/// This enum represents all the possible ways a transaction can fail
/// during processing, validation, or execution.
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub enum TransactionError {
    /// An account is already being processed in another transaction in a way
    /// that does not support parallelism
    AccountInUse,

    /// A `Pubkey` appears twice in the transaction's `account_keys`.  Instructions can reference
    /// `Pubkey`s more than once but the message must contain a list with no duplicate keys
    AccountLoadedTwice,

    /// Attempt to debit an account but found no record of a prior credit.
    AccountNotFound,

    /// Attempt to load a program that does not exist
    ProgramAccountNotFound,

    /// The from `Pubkey` does not have sufficient balance to pay the fee to schedule the transaction
    InsufficientFundsForFee,

    /// This account may not be used to pay transaction fees
    InvalidAccountForFee,

    /// The bank has seen this transaction before. This can occur under normal operation
    /// when a UDP packet is duplicated, as a user error from a client not updating
    /// its `recent_blockhash`, or as a double-spend attack.
    AlreadyProcessed,

    /// The transaction is too stale (seconds since `UNIX_EPOCH`).
    TimestampTooStale,

    /// The transaction is too far in the future (seconds since `UNIX_EPOCH`).
    TimestampInFuture,

    /// An error occurred while processing an instruction. The first element of the tuple
    /// indicates the instruction index in which the error occurred.
    InstructionError(u8, InstructionError),

    /// Transaction contains an invalid account reference
    InvalidAccountIndex,

    /// Transaction did not pass signature verification
    SignatureFailure,

    /// This program may not be used for executing instructions
    InvalidProgramForExecution,

    /// Transaction failed to sanitize accounts offsets correctly
    /// implies that account locks are not taken for this TX, and should
    /// not be unlocked.
    SanitizeFailure,

    /// Transaction processing left an account with an outstanding borrowed reference
    AccountBorrowOutstanding,

    /// Transaction would exceed max Block Cost Limit
    WouldExceedMaxBlockCostLimit,

    /// Transaction version is unsupported
    UnsupportedVersion,

    /// Transaction loads a writable account that cannot be written
    InvalidWritableAccount,

    /// Transaction would exceed max account limit within the block
    WouldExceedMaxAccountCostLimit,

    /// Transaction would exceed account data limit within the block
    WouldExceedAccountDataBlockLimit,

    /// Transaction locked too many accounts
    TooManyAccountLocks,

    /// Transaction leaves an account with a lower balance than rent-exempt minimum
    InvalidRentPayingAccount,

    /// Transaction would exceed max Vote Cost Limit
    WouldExceedMaxVoteCostLimit,

    /// Transaction contains a duplicate instruction that is not allowed
    DuplicateInstruction(u8),

    /// Transaction results in an account with insufficient funds for rent
    InsufficientFundsForRent { account_index: u8 },

    /// Transaction exceeded max loaded accounts data size cap
    MaxLoadedAccountsDataSizeExceeded,

    /// LoadedAccountsDataSizeLimit set for transaction must be greater than 0.
    InvalidLoadedAccountsDataSizeLimit,

    /// Sanitized transaction differed before/after feature activiation. Needs to be resanitized.
    ResanitizationNeeded,

    /// The total balance before the transaction does not equal the total balance after the transaction
    UnbalancedTransaction,

    /// Program cache hit max limit.
    ProgramCacheHitMaxLimit,

    /// Failed to serialize transaction instructions.
    SerializationFailure,

    /// Transaction has invalid config hash prefix.
    InvalidConfigHashPrefix,
}
