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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
use blockchain_traits::TransactionOutcome;
use oasis_types::{AccountMeta, Address};

use crate::{
    output::{Event, Receipt},
    State,
};

#[derive(Debug)]
pub struct PendingTransaction<'bc> {
    pub caller: Address,
    pub callee: Address,
    pub value: u64,
    pub state: State<'bc>,
    pub input: Vec<u8>,
    pub outcome: TransactionOutcome,
    pub output: Vec<u8>,
    pub events: Vec<Event>,
    pub gas_left: u64,
    pub base_gas: u64,
}

impl<'bc> blockchain_traits::PendingTransaction for PendingTransaction<'bc> {
    type Address = Address;
    type AccountMeta = AccountMeta;

    fn address(&self) -> &Self::Address {
        &self.callee
    }

    fn sender(&self) -> &Self::Address {
        &self.caller
    }

    fn value(&self) -> u64 {
        self.value
    }

    fn input(&self) -> &[u8] {
        self.input.as_slice()
    }

    fn transact(
        &mut self,
        callee: Self::Address,
        value: u64,
        input: &[u8],
    ) -> Box<dyn blockchain_traits::Receipt<Address = Self::Address>> {
        let caller = self.callee;
        let mut receipt = Receipt {
            caller,
            callee,
            value,
            gas_used: 0, // TODO(#116)
            output: Vec::new(),
            events: Vec::new(),
            outcome: TransactionOutcome::Success,
        };

        if self.gas_left < self.base_gas {
            receipt.outcome = TransactionOutcome::InsufficientGas;
            return box receipt;
        }

        if !self.state.contains_key(&callee) {
            receipt.outcome = TransactionOutcome::NoAccount;
            return box receipt;
        }

        let mut ptx_state = self.state.clone();

        let caller_acct = ptx_state.get_mut(&caller).unwrap().to_mut();

        if caller_acct.balance < value {
            receipt.outcome = TransactionOutcome::InsufficientFunds;
            return box receipt;
        } else {
            caller_acct.balance -= value
        }

        ptx_state.get_mut(&callee).unwrap().to_mut().balance += value;

        let mut pending_transaction = PendingTransaction {
            caller: self.callee,
            callee,
            value,
            input: input.to_vec(),
            outcome: TransactionOutcome::Success,
            state: ptx_state,
            events: Vec::new(),
            output: Vec::new(),
            base_gas: self.base_gas,
            gas_left: self.gas_left - self.base_gas,
        };

        let main_fn = self.state.get(&callee).unwrap().main;

        if let Some(main) = main_fn {
            let ptx: &mut dyn blockchain_traits::PendingTransaction<
                Address = Address,
                AccountMeta = AccountMeta,
            > = &mut pending_transaction;
            let errno = main(unsafe {
                // Extend the lifetime, as required by the FFI type.
                // This is only unsafe if the `main` fn stores the pointer,
                // but this is disallowed by the precondition on `main`.
                &(std::mem::transmute::<&mut _, &'static mut _>(ptx) as *mut _) as *const _
            });
            if errno != 0 {
                pending_transaction.outcome = TransactionOutcome::Aborted;
            }
        }

        receipt.outcome = pending_transaction.outcome;
        receipt.output = pending_transaction.output;
        if blockchain_traits::Receipt::reverted(&receipt) {
            receipt.events.clear();
        } else {
            self.state = pending_transaction.state;
            receipt
                .events
                .append(&mut pending_transaction.events.clone());
            self.events.append(&mut pending_transaction.events);
        }
        box receipt
    }

    fn ret(&mut self, data: &[u8]) {
        assert!(self.output.is_empty());
        self.output = data.to_vec()
    }

    fn err(&mut self, data: &[u8]) {
        assert!(self.output.is_empty());
        self.output = data.to_vec();
        self.outcome = TransactionOutcome::Aborted;
    }

    fn emit(&mut self, topics: &[&[u8]], data: &[u8]) {
        self.events.push(Event {
            emitter: self.callee,
            topics: topics
                .iter()
                .map(|t| {
                    let mut t_arr = [0u8; 32];
                    t_arr.copy_from_slice(&t[..32]);
                    t_arr
                })
                .collect(),
            data: data.to_vec(),
        });
    }

    fn state(&self) -> &dyn blockchain_traits::KVStore {
        self.state.get(&self.callee).map(|acct| &**acct).unwrap()
    }

    fn state_mut(&mut self) -> &mut dyn blockchain_traits::KVStoreMut {
        self.state
            .get_mut(&self.callee)
            .map(std::borrow::Cow::to_mut)
            .unwrap()
    }

    fn code_at(&self, addr: &Self::Address) -> Option<&[u8]> {
        self.state.get(addr).map(|acct| acct.code.as_ref())
    }

    fn account_meta_at(&self, addr: &Self::Address) -> Option<Self::AccountMeta> {
        self.state.get(addr).map(|acct| AccountMeta {
            balance: acct.balance,
            expiry: acct.expiry,
        })
    }
}