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
extern crate sputnikvm;
extern crate bigint;

use sputnikvm::{VM, AccountCommitment, RequireError, VMStatus, AccountChange, Log};
use bigint::{Address, U256, M256, H256, Gas};
use std::collections::HashSet;
use std::collections::hash_map::Values;

pub trait Callback {
    fn balance(&self, Address) -> U256;
    fn nonce(&self, Address) -> U256;
    fn code(&self, Address) -> Vec<u8>;
    fn storage(&self, Address, U256) -> M256;
    fn exists(&self, Address) -> bool;
    fn blockhash(&self, U256) -> H256;
}

pub struct CallbackVM<'a, V: VM, C: Callback + 'a>(V, &'a C);

impl<'a, V: VM, C: Callback + 'a> CallbackVM<'a, V, C> {
    pub fn new(vm: V, callback: &'a C) -> Self {
        CallbackVM(vm, callback)
    }

    pub fn fire(&mut self) {
        loop {
            match self.0.fire() {
                Ok(()) => break,
                Err(RequireError::Account(address)) => {
                    if self.1.exists(address) {
                        let commit = AccountCommitment::Full {
                            address,
                            nonce: self.1.nonce(address),
                            balance: self.1.balance(address),
                            code: self.1.code(address),
                        };
                        self.0.commit_account(commit).unwrap();
                    } else {
                        self.0.commit_account(AccountCommitment::Nonexist(address)).unwrap();
                    }
                },
                Err(RequireError::AccountCode(address)) => {
                    if self.1.exists(address) {
                        let commit = AccountCommitment::Code {
                            address,
                            code: self.1.code(address),
                        };
                        self.0.commit_account(commit).unwrap();
                    } else {
                        self.0.commit_account(AccountCommitment::Nonexist(address)).unwrap();
                    }
                },
                Err(RequireError::AccountStorage(address, index)) => {
                    if self.1.exists(address) {
                        let commit = AccountCommitment::Storage {
                            address, index,
                            value: self.1.storage(address, index),
                        };
                        self.0.commit_account(commit).unwrap();
                    } else {
                        self.0.commit_account(AccountCommitment::Nonexist(address)).unwrap();
                    }
                },
                Err(RequireError::Blockhash(number)) => {
                    self.0.commit_blockhash(number, self.1.blockhash(number)).unwrap();
                },
            }
        }
    }

    pub fn status(&self) -> VMStatus {
        self.0.status()
    }

    pub fn accounts(&self) -> Values<Address, AccountChange> {
        self.0.accounts()
    }

    pub fn used_addresses(&self) -> HashSet<Address> {
        self.0.used_addresses()
    }

    pub fn out(&self) -> &[u8] {
        self.0.out()
    }

    pub fn available_gas(&self) -> Gas {
        self.0.available_gas()
    }

    pub fn refunded_gas(&self) -> Gas {
        self.0.refunded_gas()
    }

    pub fn logs(&self) -> &[Log] {
        self.0.logs()
    }

    pub fn removed(&self) -> &[Address] {
        self.0.removed()
    }
}