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
/*
 * SPDX-License-Identifier: Apache-2.0
 */
#![allow(dead_code)]

use std::collections::HashMap;

use crate::contractapi::contract::*;
use crate::contractapi::transaction;
use crate::{blockchain::TransactionContext, dataapi::serializer::*, dataapi::WireBuffer};
use crate::{contract::ContractError};

use log::{debug};

/// Internal definition of a contract

pub struct ContractDefn {
    name: String,
    methods: HashMap<String, transaction::TransactionFn>,
    contract: Box<dyn Contract + Send>,
    converter: Box<dyn Converter + Send>,
}

impl ContractDefn {
    pub fn new(c: Box<dyn Contract + Send>) -> ContractDefn {
        let mut cd = ContractDefn {
            name: c.name(),
            methods: HashMap::new(),
            contract: c,
            converter: Box::new(JSONConverter {}),
        };

        let fns = cd.contract.get_fn_metadata();
        for t in fns {
            debug!("Function {:?}", t);
            cd.add_tx_fn(t);
        }

        cd
    }

    pub fn add_tx_fn(self: &mut ContractDefn, tx: transaction::TransactionFn) {
        self.methods.insert(tx.get_name(), tx);
    }

    pub fn add_new_method(self: &mut ContractDefn, name: &str, func: fn(&str) -> bool) {
        let tx = transaction::TransactionFn::new(name);
        debug!("{:?}", tx);
        self.methods.insert(String::from(name), tx);
    }

    pub fn get_txfn(
        self: &ContractDefn,
        name: &str,
    ) -> Result<&transaction::TransactionFn, String> {
        match self.methods.get(&String::from(name)) {
            Some(t) => Ok(t),
            None => Err(String::from("Unable to find tx")),
        }
    }

    pub fn invoke(
        self: &ContractDefn,
        ctx: &TransactionContext,
        name: String,
        args: &[Vec<u8>],
        transient: &HashMap<String, Vec<u8>>,
    ) -> Result<WireBuffer, ContractError> {
       
        debug!("Invoking tx fn {} {:#?} {}", name, args, args.len());

        let txfn = self.get_txfn(&name[..])?;
        let transient_ids = txfn.get_transient_ids();
        let mut updated_args = Vec::<WireBuffer>::new();
        // got the tx fn, now to loop over the supplied args
        for (pos, p) in txfn.get_parameters().iter().enumerate() {
            debug!("{} {:?}", pos, p);
            // if the parameter is in the transient list, then get it from the hashmap instead
            if transient_ids.contains(&p.name){
                let temp_buffer = transient.get::<_>(&p.name).unwrap().clone();
                updated_args.push(WireBuffer::new(
                    temp_buffer,
                    p.type_schema, /*,Box::new(JSONConverter {})*/
                ));

            } else {
                updated_args.push(WireBuffer::new(
                    args[pos].clone(),
                    p.type_schema, /*,Box::new(JSONConverter {})*/
                ));
            }
        }

        let buffer = self
            .contract
            .route3(name, updated_args, txfn.get_return())?;
        debug!("Returned buffer {:?}", &buffer);
        Ok(buffer)
    }
}

// Test section
#[cfg(test)]
mod tests {
    use super::*;
    use crate::contractapi::transaction::*;
    use crate::data::TypeSchema;
    use claim::assert_ok;
    use mockall::{mock, predicate::*};

    mock! {
        TestContract {}
        // First trait to implement on C
        trait Metadata {
            fn get_fn_metadata(&self) -> Vec<TransactionFn>;
        }
        // Second trait to implement on C
        trait Routing {
            fn route3(
                &self,
                tx_fn: String,
                args: Vec<WireBuffer>,
                return_wb: TypeSchema,
            ) -> Result<WireBuffer, ContractError>;
        }
    }

    impl Contract for MockTestContract {
        fn name(&self) -> String {
            "TestContract".to_string()
        }
    }

    #[test]
    fn new_defn() {
        let contract = MockTestContract::new();
        let mut b = Box::new(contract);

        let mut tx_fns = Vec::<TransactionFn>::new();
        let mut txfn1 = TransactionFnBuilder::new();
        txfn1.name("testfn");
        let t = txfn1.build();
        tx_fns.push(t);

        b.expect_get_fn_metadata().return_const(tx_fns);
        let contract_defn = ContractDefn::new(b);
        assert_ok!(contract_defn.get_txfn("testfn"));
    }
}