1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
//! Predicate representations with required data to be executed during VM runtime

use crate::interpreter::MemoryRange;

use fuel_tx::{
    field,
    ConsensusParameters,
};

/// Runtime representation of a predicate
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct RuntimePredicate {
    program: MemoryRange,
    idx: usize,
}

impl RuntimePredicate {
    /// Memory slice with the program representation of the predicate
    pub const fn program(&self) -> &MemoryRange {
        &self.program
    }

    /// Index of the transaction input that maps to this predicate
    pub const fn idx(&self) -> usize {
        self.idx
    }

    /// Create a new runtime predicate from a transaction, given the input index
    ///
    /// Return `None` if the tx input doesn't map to an input with a predicate
    pub fn from_tx<T>(params: &ConsensusParameters, tx: &T, idx: usize) -> Option<Self>
    where
        T: field::Inputs,
    {
        let (ofs, len) = tx.inputs_predicate_offset_at(idx)?;
        let addr = ofs.saturating_add(params.tx_offset());
        let range = MemoryRange::new(addr, len).expect("Invalid memory range");
        Some(Self {
            program: range,
            idx,
        })
    }
}

#[test]
fn from_tx_works() {
    use fuel_asm::op;
    use fuel_tx::TransactionBuilder;
    use fuel_types::bytes;
    use fuel_vm::prelude::*;
    use rand::{
        rngs::StdRng,
        Rng,
        SeedableRng,
    };

    use std::iter;

    let rng = &mut StdRng::seed_from_u64(2322u64);

    let params = ConsensusParameters::default();
    let height = 1.into();

    #[rustfmt::skip]
    let predicate: Vec<u8> = vec![
        op::addi(0x10, 0x00, 0x01),
        op::addi(0x10, 0x10, 0x01),
        op::ret(0x01),
    ].into_iter().collect();

    let predicate_data = b"If people do not believe that mathematics is simple, it is only because they do not realize how complicated life is.".to_vec();

    let owner = (*Contract::root_from_code(&predicate)).into();
    let a = Input::coin_predicate(
        rng.gen(),
        owner,
        rng.gen(),
        rng.gen(),
        rng.gen(),
        rng.gen(),
        rng.gen(),
        predicate.clone(),
        predicate_data.clone(),
    );

    let b = Input::message_coin_predicate(
        rng.gen(),
        rng.gen(),
        rng.gen(),
        rng.gen(),
        rng.gen(),
        predicate.clone(),
        predicate_data.clone(),
    );

    let c = Input::message_data_predicate(
        rng.gen(),
        rng.gen(),
        rng.gen(),
        rng.gen(),
        rng.gen(),
        vec![0xff; 10],
        predicate.clone(),
        predicate_data,
    );

    let inputs = vec![a, b, c];

    for i in inputs {
        let tx = TransactionBuilder::script(vec![], vec![])
            .with_params(params)
            .add_input(i)
            .add_random_fee_input()
            .finalize_checked_basic(height);

        // assert invalid idx wont panic
        let idx = 1;
        let runtime = RuntimePredicate::from_tx(&params, tx.as_ref(), idx);

        assert!(runtime.is_none());

        // fetch the input predicate
        let idx = 0;
        let runtime = RuntimePredicate::from_tx(&params, tx.as_ref(), idx)
            .expect("failed to generate predicate from valid tx");

        assert_eq!(idx, runtime.idx());

        let mut interpreter = Interpreter::without_storage();

        assert!(interpreter
            .init_predicate(
                fuel_vm::context::Context::PredicateVerification {
                    program: Default::default()
                },
                tx.transaction().clone(),
                Default::default(),
                tx.transaction().limit()
            )
            .is_ok());

        let pad = bytes::padded_len(&predicate) - predicate.len();

        // assert we are testing an edge case
        assert_ne!(0, pad);

        let padded_predicate: Vec<u8> = predicate
            .iter()
            .copied()
            .chain(iter::repeat(0u8).take(pad))
            .collect();

        let program = runtime.program();
        let program = &interpreter.memory()[program.usizes()];

        // assert the program in the vm memory is the same of the input
        assert_eq!(program, &padded_predicate);
    }
}