casper_execution_engine/engine_state/
wasm_v1.rs

1use std::{collections::BTreeSet, convert::TryFrom};
2
3use serde::Serialize;
4use thiserror::Error;
5
6use casper_storage::{data_access_layer::TransferResult, tracking_copy::TrackingCopyCache};
7use casper_types::{
8    account::AccountHash,
9    bytesrepr::Bytes,
10    contract_messages::Messages,
11    execution::{Effects, TransformKindV2},
12    BlockHash, BlockTime, CLValue, DeployHash, Digest, ExecutableDeployItem, Gas, InitiatorAddr,
13    Key, Phase, PricingMode, ProtocolVersion, RuntimeArgs, TransactionEntryPoint, TransactionHash,
14    TransactionInvocationTarget, TransactionTarget, TransactionV1Hash, Transfer, URefAddr, U512,
15};
16
17use crate::engine_state::Error as EngineError;
18
19const DEFAULT_ENTRY_POINT: &str = "call";
20
21/// Structure that needs to be filled with data so the engine can assemble wasm for deploy.
22pub struct SessionDataDeploy<'a> {
23    deploy_hash: &'a DeployHash,
24    session: &'a ExecutableDeployItem,
25    initiator_addr: &'a InitiatorAddr,
26    signers: BTreeSet<AccountHash>,
27    is_standard_payment: bool,
28}
29
30impl<'a> SessionDataDeploy<'a> {
31    /// Constructor
32    pub fn new(
33        deploy_hash: &'a DeployHash,
34        session: &'a ExecutableDeployItem,
35        initiator_addr: &'a InitiatorAddr,
36        signers: BTreeSet<AccountHash>,
37        is_standard_payment: bool,
38    ) -> Self {
39        Self {
40            deploy_hash,
41            session,
42            initiator_addr,
43            signers,
44            is_standard_payment,
45        }
46    }
47
48    /// Deploy hash of the deploy
49    pub fn deploy_hash(&self) -> &DeployHash {
50        self.deploy_hash
51    }
52
53    /// executable item of the deploy
54    pub fn session(&self) -> &ExecutableDeployItem {
55        self.session
56    }
57
58    /// initiator address of the deploy
59    pub fn initiator_addr(&self) -> &InitiatorAddr {
60        self.initiator_addr
61    }
62
63    /// signers of the deploy
64    pub fn signers(&self) -> BTreeSet<AccountHash> {
65        self.signers.clone()
66    }
67}
68
69/// Structure that needs to be filled with data so the engine can assemble wasm for v1.
70pub struct SessionDataV1<'a> {
71    args: &'a RuntimeArgs,
72    target: &'a TransactionTarget,
73    entry_point: &'a TransactionEntryPoint,
74    is_install_upgrade: bool,
75    hash: &'a TransactionV1Hash,
76    pricing_mode: &'a PricingMode,
77    initiator_addr: &'a InitiatorAddr,
78    signers: BTreeSet<AccountHash>,
79    is_standard_payment: bool,
80}
81
82impl<'a> SessionDataV1<'a> {
83    #[allow(clippy::too_many_arguments)]
84    /// Constructor
85    pub fn new(
86        args: &'a RuntimeArgs,
87        target: &'a TransactionTarget,
88        entry_point: &'a TransactionEntryPoint,
89        is_install_upgrade: bool,
90        hash: &'a TransactionV1Hash,
91        pricing_mode: &'a PricingMode,
92        initiator_addr: &'a InitiatorAddr,
93        signers: BTreeSet<AccountHash>,
94        is_standard_payment: bool,
95    ) -> Self {
96        Self {
97            args,
98            target,
99            entry_point,
100            is_install_upgrade,
101            hash,
102            pricing_mode,
103            initiator_addr,
104            signers,
105            is_standard_payment,
106        }
107    }
108
109    /// Runtime args passed with the transaction.
110    pub fn args(&self) -> &RuntimeArgs {
111        self.args
112    }
113
114    /// Target of the transaction.
115    pub fn target(&self) -> &TransactionTarget {
116        self.target
117    }
118
119    /// Entry point of the transaction
120    pub fn entry_point(&self) -> &TransactionEntryPoint {
121        self.entry_point
122    }
123
124    /// Should session be allowed to perform install/upgrade operations
125    pub fn is_install_upgrade(&self) -> bool {
126        self.is_install_upgrade
127    }
128
129    /// Hash of the transaction
130    pub fn hash(&self) -> &TransactionV1Hash {
131        self.hash
132    }
133
134    /// initiator address of the transaction
135    pub fn initiator_addr(&self) -> &InitiatorAddr {
136        self.initiator_addr
137    }
138
139    /// signers of the transaction
140    pub fn signers(&self) -> BTreeSet<AccountHash> {
141        self.signers.clone()
142    }
143
144    /// Pricing mode of the transaction
145    pub fn pricing_mode(&self) -> &PricingMode {
146        self.pricing_mode
147    }
148}
149
150/// Wrapper enum abstracting data for assmbling WasmV1Requests
151pub enum SessionInputData<'a> {
152    /// Variant for sessions created from deploy transactions
153    DeploySessionData {
154        /// Deploy session data
155        data: SessionDataDeploy<'a>,
156    },
157    /// Variant for sessions created from v1 transactions
158    SessionDataV1 {
159        /// v1 session data
160        data: SessionDataV1<'a>,
161    },
162}
163
164impl SessionInputData<'_> {
165    /// Transaction hash for the session
166    pub fn transaction_hash(&self) -> TransactionHash {
167        match self {
168            SessionInputData::DeploySessionData { data } => {
169                TransactionHash::Deploy(*data.deploy_hash())
170            }
171            SessionInputData::SessionDataV1 { data } => TransactionHash::V1(*data.hash()),
172        }
173    }
174
175    /// Initiator address for the session
176    pub fn initiator_addr(&self) -> &InitiatorAddr {
177        match self {
178            SessionInputData::DeploySessionData { data } => data.initiator_addr(),
179            SessionInputData::SessionDataV1 { data } => data.initiator_addr(),
180        }
181    }
182
183    /// Signers for the session
184    pub fn signers(&self) -> BTreeSet<AccountHash> {
185        match self {
186            SessionInputData::DeploySessionData { data } => data.signers(),
187            SessionInputData::SessionDataV1 { data } => data.signers(),
188        }
189    }
190
191    /// determines if the transaction from which this session data was created is a standard payment
192    pub fn is_standard_payment(&self) -> bool {
193        match self {
194            SessionInputData::DeploySessionData { data } => data.is_standard_payment,
195            SessionInputData::SessionDataV1 { data } => data.is_standard_payment,
196        }
197    }
198
199    /// Is install upgrade allowed?
200    pub fn is_install_upgrade_allowed(&self) -> bool {
201        match self {
202            SessionInputData::DeploySessionData { .. } => true,
203            SessionInputData::SessionDataV1 { data } => data.is_install_upgrade,
204        }
205    }
206}
207
208/// Error returned if constructing a new [`WasmV1Request`] fails.
209#[derive(Clone, Eq, PartialEq, Error, Serialize, Debug)]
210pub enum InvalidRequest {
211    /// Missing custom payment.
212    #[error("custom payment not found for {0}")]
213    CustomPaymentNotFound(TransactionHash),
214    /// Unexpected variant.
215    #[error("unexpected variant for {0} attempting {1}")]
216    UnexpectedVariant(TransactionHash, String),
217    /// Unsupported mode.
218    #[error("unsupported mode for {0} attempting {1}")]
219    UnsupportedMode(TransactionHash, String),
220    /// Invalid entry point.
221    #[error("invalid entry point for {0} attempting {1}")]
222    InvalidEntryPoint(TransactionHash, String),
223    /// Invalid target.
224    #[error("invalid target for {0} attempting {1}")]
225    InvalidTarget(TransactionHash, String),
226    /// Unsupported category.
227    #[error("invalid category for {0} attempting {1}")]
228    InvalidCategory(TransactionHash, String),
229}
230
231#[derive(Debug, Clone)]
232pub enum SessionKind {
233    InstallUpgradeBytecode,
234    GenericBytecode,
235}
236
237/// The item to be executed.
238#[derive(Debug, Clone)]
239pub enum ExecutableItem {
240    /// Deploy model byte code.
241    Deploy(Bytes),
242    /// Payment byte code.
243    PaymentBytes(Bytes),
244    /// Session byte code.
245    SessionBytes {
246        /// The kind of session.
247        kind: SessionKind,
248        /// The compiled Wasm.
249        module_bytes: Bytes,
250    },
251    /// An attempt to invoke a stored entity or package.
252    Invocation(TransactionInvocationTarget),
253}
254
255impl ExecutableItem {
256    /// Is install upgrade allowed?
257    pub fn is_install_upgrade_allowed(&self) -> bool {
258        match self {
259            ExecutableItem::Deploy(_) => true,
260            ExecutableItem::PaymentBytes(_) | ExecutableItem::Invocation(_) => false,
261            ExecutableItem::SessionBytes { kind, .. } => {
262                matches!(kind, SessionKind::InstallUpgradeBytecode)
263            }
264        }
265    }
266}
267
268/// Block info.
269#[derive(Copy, Clone, Debug)]
270pub struct BlockInfo {
271    /// State root hash of the global state in which the transaction will be executed.
272    pub state_hash: Digest,
273    /// Block time represented as a unix timestamp.
274    pub block_time: BlockTime,
275    /// Parent block hash
276    pub parent_block_hash: BlockHash,
277    /// Block height
278    pub block_height: u64,
279    /// Protocol version
280    pub protocol_version: ProtocolVersion,
281}
282
283impl BlockInfo {
284    /// A new instance of `[BlockInfo]`.
285    pub fn new(
286        state_hash: Digest,
287        block_time: BlockTime,
288        parent_block_hash: BlockHash,
289        block_height: u64,
290        protocol_version: ProtocolVersion,
291    ) -> Self {
292        BlockInfo {
293            state_hash,
294            block_time,
295            parent_block_hash,
296            block_height,
297            protocol_version,
298        }
299    }
300
301    /// Apply different state hash.
302    pub fn with_state_hash(&mut self, state_hash: Digest) {
303        self.state_hash = state_hash;
304    }
305
306    /// State hash.
307    pub fn state_hash(&self) -> Digest {
308        self.state_hash
309    }
310
311    /// Block time.
312    pub fn block_time(&self) -> BlockTime {
313        self.block_time
314    }
315
316    /// Parent block hash.
317    pub fn parent_block_hash(&self) -> BlockHash {
318        self.parent_block_hash
319    }
320
321    /// Block height.
322    pub fn block_height(&self) -> u64 {
323        self.block_height
324    }
325
326    /// Protocol version.
327    pub fn protocol_version(&self) -> ProtocolVersion {
328        self.protocol_version
329    }
330}
331
332/// A request to execute the given Wasm on the V1 runtime.
333#[derive(Debug)]
334pub struct WasmV1Request {
335    /// Block info.
336    pub block_info: BlockInfo,
337    /// The hash identifying the transaction.
338    pub transaction_hash: TransactionHash,
339    /// The number of Motes per unit of Gas to be paid for execution.
340    pub gas_limit: Gas,
341    /// The transaction's initiator.
342    pub initiator_addr: InitiatorAddr,
343    /// The executable item.
344    pub executable_item: ExecutableItem,
345    /// The entry point to call when executing.
346    pub entry_point: String,
347    /// The runtime args.
348    pub args: RuntimeArgs,
349    /// The account hashes of the signers of the transaction.
350    pub authorization_keys: BTreeSet<AccountHash>,
351    /// Execution phase.
352    pub phase: Phase,
353}
354
355impl WasmV1Request {
356    /// New from executable deploy item or InvalidRequest error.
357    pub fn new_from_executable_deploy_item(
358        block_info: BlockInfo,
359        gas_limit: Gas,
360        transaction_hash: TransactionHash,
361        initiator_addr: InitiatorAddr,
362        authorization_keys: BTreeSet<AccountHash>,
363        session_item: &ExecutableDeployItem,
364    ) -> Result<Self, InvalidRequest> {
365        let executable_info =
366            build_session_info_for_executable_item(session_item, transaction_hash)?;
367        Ok(Self::new_from_executable_info(
368            block_info,
369            gas_limit,
370            transaction_hash,
371            initiator_addr,
372            authorization_keys,
373            executable_info,
374        ))
375    }
376
377    /// New payment from executable deploy item or InvalidRequest error.
378    pub fn new_payment_from_executable_deploy_item(
379        block_info: BlockInfo,
380        gas_limit: Gas,
381        transaction_hash: TransactionHash,
382        initiator_addr: InitiatorAddr,
383        authorization_keys: BTreeSet<AccountHash>,
384        payment_item: &ExecutableDeployItem,
385    ) -> Result<Self, InvalidRequest> {
386        let executable_info =
387            build_payment_info_for_executable_item(payment_item, transaction_hash)?;
388        Ok(Self::new_from_executable_info(
389            block_info,
390            gas_limit,
391            transaction_hash,
392            initiator_addr,
393            authorization_keys,
394            executable_info,
395        ))
396    }
397
398    pub(crate) fn new_from_executable_info(
399        block_info: BlockInfo,
400        gas_limit: Gas,
401        transaction_hash: TransactionHash,
402        initiator_addr: InitiatorAddr,
403        authorization_keys: BTreeSet<AccountHash>,
404        executable_info: impl Executable,
405    ) -> Self {
406        let executable_item = executable_info.item();
407        Self {
408            block_info,
409            transaction_hash,
410            gas_limit,
411            initiator_addr,
412            authorization_keys,
413            executable_item,
414            entry_point: executable_info.entry_point().clone(),
415            args: executable_info.args().clone(),
416            phase: executable_info.phase(),
417        }
418    }
419
420    /// Creates a new request from a transaction for use as the session code.
421    pub fn new_session(
422        block_info: BlockInfo,
423        gas_limit: Gas,
424        session_input_data: &SessionInputData,
425    ) -> Result<Self, InvalidRequest> {
426        let session_info = SessionInfo::try_from(session_input_data)?;
427        let transaction_hash = session_input_data.transaction_hash();
428        let initiator_addr = session_input_data.initiator_addr().clone();
429        let authorization_keys = session_input_data.signers().clone();
430        Ok(WasmV1Request::new_from_executable_info(
431            block_info,
432            gas_limit,
433            transaction_hash,
434            initiator_addr,
435            authorization_keys,
436            session_info,
437        ))
438    }
439
440    /// Creates a new request from a transaction for use as custom payment.
441    pub fn new_custom_payment(
442        block_info: BlockInfo,
443        gas_limit: Gas,
444        session_input_data: &SessionInputData,
445    ) -> Result<Self, InvalidRequest> {
446        let payment_info = PaymentInfo::try_from(session_input_data)?;
447        let transaction_hash = session_input_data.transaction_hash();
448        let initiator_addr = session_input_data.initiator_addr().clone();
449        let authorization_keys = session_input_data.signers().clone();
450        Ok(WasmV1Request::new_from_executable_info(
451            block_info,
452            gas_limit,
453            transaction_hash,
454            initiator_addr,
455            authorization_keys,
456            payment_info,
457        ))
458    }
459}
460
461/// Wasm v1 result.
462#[derive(Clone, Debug)]
463pub struct WasmV1Result {
464    /// List of transfers that happened during execution.
465    transfers: Vec<Transfer>,
466    /// Gas limit.
467    limit: Gas,
468    /// Gas consumed.
469    consumed: Gas,
470    /// Execution effects.
471    effects: Effects,
472    /// Messages emitted during execution.
473    messages: Messages,
474    /// Did the wasm execute successfully?
475    error: Option<EngineError>,
476    /// Result captured from a ret call.
477    ret: Option<CLValue>,
478    /// Tracking copy cache captured during execution.
479    cache: Option<TrackingCopyCache>,
480}
481
482impl WasmV1Result {
483    /// Creates a new instance.
484    #[allow(clippy::too_many_arguments)]
485    pub fn new(
486        limit: Gas,
487        consumed: Gas,
488        effects: Effects,
489        transfers: Vec<Transfer>,
490        messages: Messages,
491        error: Option<EngineError>,
492        ret: Option<CLValue>,
493        cache: Option<TrackingCopyCache>,
494    ) -> Self {
495        WasmV1Result {
496            limit,
497            consumed,
498            effects,
499            transfers,
500            messages,
501            error,
502            ret,
503            cache,
504        }
505    }
506
507    /// Error, if any.
508    pub fn error(&self) -> Option<&EngineError> {
509        self.error.as_ref()
510    }
511
512    /// List of transfers that happened during execution.
513    pub fn transfers(&self) -> &Vec<Transfer> {
514        &self.transfers
515    }
516
517    /// Gas limit.
518    pub fn limit(&self) -> Gas {
519        self.limit
520    }
521
522    /// Gas consumed.
523    pub fn consumed(&self) -> Gas {
524        self.consumed
525    }
526
527    /// Execution effects.
528    pub fn effects(&self) -> &Effects {
529        &self.effects
530    }
531
532    /// Tracking copy cache captured during execution.
533    pub fn cache(&self) -> Option<&TrackingCopyCache> {
534        self.cache.as_ref()
535    }
536
537    /// Messages emitted during execution.
538    pub fn messages(&self) -> &Messages {
539        &self.messages
540    }
541
542    /// Result captured from a ret call.
543    pub fn ret(&self) -> Option<&CLValue> {
544        self.ret.as_ref()
545    }
546
547    /// Root not found.
548    pub fn root_not_found(gas_limit: Gas, state_hash: Digest) -> Self {
549        WasmV1Result {
550            transfers: Vec::default(),
551            effects: Effects::new(),
552            messages: Vec::default(),
553            limit: gas_limit,
554            consumed: Gas::zero(),
555            error: Some(EngineError::RootNotFound(state_hash)),
556            ret: None,
557            cache: None,
558        }
559    }
560
561    /// Precondition failure.
562    pub fn precondition_failure(gas_limit: Gas, error: EngineError) -> Self {
563        WasmV1Result {
564            transfers: Vec::default(),
565            effects: Effects::new(),
566            messages: Vec::default(),
567            limit: gas_limit,
568            consumed: Gas::zero(),
569            error: Some(error),
570            ret: None,
571            cache: None,
572        }
573    }
574
575    /// Failed to transform transaction into an executable item.
576    pub fn invalid_executable_item(gas_limit: Gas, error: InvalidRequest) -> Self {
577        WasmV1Result {
578            transfers: Vec::default(),
579            effects: Effects::new(),
580            messages: Vec::default(),
581            limit: gas_limit,
582            consumed: Gas::zero(),
583            error: Some(EngineError::InvalidExecutableItem(error)),
584            ret: None,
585            cache: None,
586        }
587    }
588
589    /// Returns `true` if this is a precondition failure.
590    ///
591    /// Precondition variant is further described as an execution failure which does not have any
592    /// effects, and has a gas cost of 0.
593    pub fn has_precondition_failure(&self) -> bool {
594        self.error.is_some() && self.consumed == Gas::zero() && self.effects.is_empty()
595    }
596
597    /// Converts a transfer result to an execution result.
598    pub fn from_transfer_result(transfer_result: TransferResult, consumed: Gas) -> Option<Self> {
599        // NOTE: for native / wasmless operations limit and consumed are always equal, and
600        // we can get away with simplifying to one or the other here.
601        // this is NOT true of wasm based operations however.
602        match transfer_result {
603            TransferResult::RootNotFound => None,
604            TransferResult::Success {
605                transfers,
606                effects,
607                cache,
608            } => Some(WasmV1Result {
609                transfers,
610                limit: consumed,
611                consumed,
612                effects,
613                messages: Messages::default(),
614                error: None,
615                ret: None,
616                cache: Some(cache),
617            }),
618            TransferResult::Failure(te) => {
619                Some(WasmV1Result {
620                    transfers: vec![],
621                    limit: consumed,
622                    consumed,
623                    effects: Effects::default(), // currently not returning effects on failure
624                    messages: Messages::default(),
625                    error: Some(EngineError::Transfer(te)),
626                    ret: None,
627                    cache: None,
628                })
629            }
630        }
631    }
632
633    /// Checks effects for an AddUInt512 transform to a balance at imputed addr
634    /// and for exactly the imputed amount.
635    pub fn balance_increased_by_amount(&self, addr: URefAddr, amount: U512) -> bool {
636        if self.effects.is_empty() || self.effects.transforms().is_empty() {
637            return false;
638        }
639
640        let key = Key::Balance(addr);
641        if let Some(transform) = self.effects.transforms().iter().find(|x| x.key() == &key) {
642            if let TransformKindV2::AddUInt512(added) = transform.kind() {
643                return *added == amount;
644            }
645        }
646        false
647    }
648}
649
650/// Helper struct to carry item, entry_point, and arg info for a `WasmV1Request`.
651struct ExecutableInfo {
652    item: ExecutableItem,
653    entry_point: String,
654    args: RuntimeArgs,
655}
656
657pub(crate) trait Executable {
658    fn item(&self) -> ExecutableItem;
659    fn entry_point(&self) -> &String;
660    fn args(&self) -> &RuntimeArgs;
661    fn phase(&self) -> Phase;
662}
663
664/// New type for hanging session specific impl's off of.
665struct SessionInfo(ExecutableInfo);
666
667impl Executable for SessionInfo {
668    fn item(&self) -> ExecutableItem {
669        self.0.item.clone()
670    }
671
672    fn entry_point(&self) -> &String {
673        &self.0.entry_point
674    }
675
676    fn args(&self) -> &RuntimeArgs {
677        &self.0.args
678    }
679
680    fn phase(&self) -> Phase {
681        Phase::Session
682    }
683}
684
685impl TryFrom<&SessionInputData<'_>> for PaymentInfo {
686    type Error = InvalidRequest;
687
688    fn try_from(input_data: &SessionInputData) -> Result<Self, Self::Error> {
689        match input_data {
690            SessionInputData::DeploySessionData { data } => PaymentInfo::try_from(data),
691            SessionInputData::SessionDataV1 { data } => PaymentInfo::try_from(data),
692        }
693    }
694}
695
696impl TryFrom<&SessionInputData<'_>> for SessionInfo {
697    type Error = InvalidRequest;
698
699    fn try_from(input_data: &SessionInputData) -> Result<Self, Self::Error> {
700        match input_data {
701            SessionInputData::DeploySessionData { data } => SessionInfo::try_from(data),
702            SessionInputData::SessionDataV1 { data } => SessionInfo::try_from(data),
703        }
704    }
705}
706
707impl TryFrom<&SessionDataDeploy<'_>> for SessionInfo {
708    type Error = InvalidRequest;
709
710    fn try_from(deploy_data: &SessionDataDeploy) -> Result<Self, Self::Error> {
711        let transaction_hash = TransactionHash::Deploy(*deploy_data.deploy_hash());
712        let session_item = deploy_data.session();
713        build_session_info_for_executable_item(session_item, transaction_hash)
714    }
715}
716
717fn build_session_info_for_executable_item(
718    session_item: &ExecutableDeployItem,
719    transaction_hash: TransactionHash,
720) -> Result<SessionInfo, InvalidRequest> {
721    let session: ExecutableItem;
722    let session_entry_point: String;
723    let session_args: RuntimeArgs;
724    match session_item {
725        ExecutableDeployItem::ModuleBytes { module_bytes, args } => {
726            session = ExecutableItem::Deploy(module_bytes.clone());
727            session_entry_point = DEFAULT_ENTRY_POINT.to_string();
728            session_args = args.clone();
729        }
730        ExecutableDeployItem::StoredContractByHash {
731            hash,
732            entry_point,
733            args,
734        } => {
735            session = ExecutableItem::Invocation(
736                TransactionInvocationTarget::new_invocable_entity((*hash).into()),
737            );
738            session_entry_point = entry_point.clone();
739            session_args = args.clone();
740        }
741        ExecutableDeployItem::StoredContractByName {
742            name,
743            entry_point,
744            args,
745        } => {
746            session = ExecutableItem::Invocation(
747                TransactionInvocationTarget::new_invocable_entity_alias(name.clone()),
748            );
749            session_entry_point = entry_point.clone();
750            session_args = args.clone();
751        }
752        ExecutableDeployItem::StoredVersionedContractByHash {
753            hash,
754            version,
755            entry_point,
756            args,
757        } => {
758            session = ExecutableItem::Invocation(TransactionInvocationTarget::ByPackageHash {
759                addr: hash.value(),
760                version: *version,
761                protocol_version_major: None,
762            });
763            session_entry_point = entry_point.clone();
764            session_args = args.clone();
765        }
766        ExecutableDeployItem::StoredVersionedContractByName {
767            name,
768            version,
769            entry_point,
770            args,
771        } => {
772            session = ExecutableItem::Invocation(TransactionInvocationTarget::ByPackageName {
773                name: name.to_owned(),
774                version: *version,
775                protocol_version_major: None,
776            });
777            session_entry_point = entry_point.clone();
778            session_args = args.clone();
779        }
780        ExecutableDeployItem::Transfer { .. } => {
781            return Err(InvalidRequest::UnsupportedMode(
782                transaction_hash,
783                session_item.to_string(),
784            ));
785        }
786    }
787
788    Ok(SessionInfo(ExecutableInfo {
789        item: session,
790        entry_point: session_entry_point,
791        args: session_args,
792    }))
793}
794
795impl TryFrom<&SessionDataV1<'_>> for SessionInfo {
796    type Error = InvalidRequest;
797
798    fn try_from(v1_txn: &SessionDataV1) -> Result<Self, Self::Error> {
799        let transaction_hash = TransactionHash::V1(*v1_txn.hash());
800        let args = v1_txn.args().clone();
801        let session = match v1_txn.target() {
802            TransactionTarget::Native => {
803                return Err(InvalidRequest::InvalidTarget(
804                    transaction_hash,
805                    v1_txn.target().to_string(),
806                ));
807            }
808            TransactionTarget::Stored { id, .. } => {
809                let TransactionEntryPoint::Custom(entry_point) = v1_txn.entry_point() else {
810                    return Err(InvalidRequest::InvalidEntryPoint(
811                        transaction_hash,
812                        v1_txn.entry_point().to_string(),
813                    ));
814                };
815                let item = ExecutableItem::Invocation(id.clone());
816                ExecutableInfo {
817                    item,
818                    entry_point: entry_point.clone(),
819                    args,
820                }
821            }
822            TransactionTarget::Session { module_bytes, .. } => {
823                if *v1_txn.entry_point() != TransactionEntryPoint::Call {
824                    return Err(InvalidRequest::InvalidEntryPoint(
825                        transaction_hash,
826                        v1_txn.entry_point().to_string(),
827                    ));
828                };
829                let kind = if v1_txn.is_install_upgrade() {
830                    SessionKind::InstallUpgradeBytecode
831                } else {
832                    SessionKind::GenericBytecode
833                };
834                let item = ExecutableItem::SessionBytes {
835                    kind,
836                    module_bytes: module_bytes.clone(),
837                };
838                ExecutableInfo {
839                    item,
840                    entry_point: DEFAULT_ENTRY_POINT.to_owned(),
841                    args,
842                }
843            }
844        };
845
846        Ok(SessionInfo(session))
847    }
848}
849/// New type for hanging payment specific impl's off of.
850struct PaymentInfo(ExecutableInfo);
851
852impl Executable for PaymentInfo {
853    fn item(&self) -> ExecutableItem {
854        self.0.item.clone()
855    }
856
857    fn entry_point(&self) -> &String {
858        &self.0.entry_point
859    }
860
861    fn args(&self) -> &RuntimeArgs {
862        &self.0.args
863    }
864
865    fn phase(&self) -> Phase {
866        Phase::Payment
867    }
868}
869
870impl TryFrom<&SessionDataDeploy<'_>> for PaymentInfo {
871    type Error = InvalidRequest;
872
873    fn try_from(deploy_data: &SessionDataDeploy) -> Result<Self, Self::Error> {
874        let payment_item = deploy_data.session();
875        let transaction_hash = TransactionHash::Deploy(*deploy_data.deploy_hash());
876        build_payment_info_for_executable_item(payment_item, transaction_hash)
877    }
878}
879
880fn build_payment_info_for_executable_item(
881    payment_item: &ExecutableDeployItem,
882    transaction_hash: TransactionHash,
883) -> Result<PaymentInfo, InvalidRequest> {
884    match payment_item {
885        ExecutableDeployItem::ModuleBytes { module_bytes, args } => {
886            let payment = if module_bytes.is_empty() {
887                return Err(InvalidRequest::UnsupportedMode(
888                    transaction_hash,
889                    "standard payment is no longer handled by the execution engine".to_string(),
890                ));
891            } else {
892                ExecutableItem::PaymentBytes(module_bytes.clone())
893            };
894            Ok(PaymentInfo(ExecutableInfo {
895                item: payment,
896                entry_point: DEFAULT_ENTRY_POINT.to_string(),
897                args: args.clone(),
898            }))
899        }
900        ExecutableDeployItem::StoredContractByHash {
901            hash,
902            args,
903            entry_point,
904        } => Ok(PaymentInfo(ExecutableInfo {
905            item: ExecutableItem::Invocation(TransactionInvocationTarget::ByHash(hash.value())),
906            entry_point: entry_point.clone(),
907            args: args.clone(),
908        })),
909        ExecutableDeployItem::StoredContractByName {
910            name,
911            args,
912            entry_point,
913        } => Ok(PaymentInfo(ExecutableInfo {
914            item: ExecutableItem::Invocation(TransactionInvocationTarget::ByName(name.clone())),
915            entry_point: entry_point.clone(),
916            args: args.clone(),
917        })),
918        ExecutableDeployItem::StoredVersionedContractByHash {
919            args,
920            hash,
921            version,
922            entry_point,
923        } => Ok(PaymentInfo(ExecutableInfo {
924            item: ExecutableItem::Invocation(TransactionInvocationTarget::ByPackageHash {
925                addr: hash.value(),
926                version: *version,
927                protocol_version_major: None,
928            }),
929            entry_point: entry_point.clone(),
930            args: args.clone(),
931        })),
932        ExecutableDeployItem::StoredVersionedContractByName {
933            name,
934            version,
935            args,
936            entry_point,
937        } => Ok(PaymentInfo(ExecutableInfo {
938            item: ExecutableItem::Invocation(TransactionInvocationTarget::ByPackageName {
939                name: name.clone(),
940                version: *version,
941                protocol_version_major: None,
942            }),
943            entry_point: entry_point.clone(),
944            args: args.clone(),
945        })),
946        ExecutableDeployItem::Transfer { .. } => Err(InvalidRequest::UnexpectedVariant(
947            transaction_hash,
948            "payment item".to_string(),
949        )),
950    }
951}
952
953impl TryFrom<&SessionDataV1<'_>> for PaymentInfo {
954    type Error = InvalidRequest;
955
956    fn try_from(v1_txn: &SessionDataV1) -> Result<Self, Self::Error> {
957        let transaction_hash = TransactionHash::V1(*v1_txn.hash());
958        match v1_txn.pricing_mode() {
959            mode @ PricingMode::PaymentLimited {
960                standard_payment, ..
961            } => {
962                if *standard_payment {
963                    return Err(InvalidRequest::UnsupportedMode(
964                        transaction_hash,
965                        mode.to_string(),
966                    ));
967                }
968            }
969            mode @ PricingMode::Fixed { .. } | mode @ PricingMode::Prepaid { .. } => {
970                return Err(InvalidRequest::UnsupportedMode(
971                    transaction_hash,
972                    mode.to_string(),
973                ));
974            }
975        };
976
977        let payment = match v1_txn.target() {
978            TransactionTarget::Session { module_bytes, .. } => {
979                if *v1_txn.entry_point() != TransactionEntryPoint::Call {
980                    return Err(InvalidRequest::InvalidEntryPoint(
981                        transaction_hash,
982                        v1_txn.entry_point().to_string(),
983                    ));
984                };
985                let item = ExecutableItem::PaymentBytes(module_bytes.clone());
986                ExecutableInfo {
987                    item,
988                    entry_point: DEFAULT_ENTRY_POINT.to_owned(),
989                    args: v1_txn.args().clone(),
990                }
991            }
992            TransactionTarget::Native | TransactionTarget::Stored { .. } => {
993                return Err(InvalidRequest::InvalidTarget(
994                    transaction_hash,
995                    v1_txn.target().to_string(),
996                ));
997            }
998        };
999
1000        Ok(PaymentInfo(payment))
1001    }
1002}