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
//! Contract function call builder.

use spec::{Function as FunctionInterface, ParamType};
use token::Token;
use encoder::Encoder;
use decoder::Decoder;
use signature::short_signature;
use errors::{Error, ErrorKind};

/// Contract function call builder.
#[derive(Clone, Debug)]
pub struct Function {
	interface: FunctionInterface,
}

impl Function {
	/// Creates new function call builder.
	pub fn new(interface: FunctionInterface) -> Self {
		Function {
			interface: interface
		}
	}

	/// Returns function params.
	pub fn input_params(&self) -> Vec<ParamType> {
		self.interface.input_param_types()
	}

	/// Return output params.
	pub fn output_params(&self) -> Vec<ParamType> {
		self.interface.output_param_types()
	}

	/// Prepares ABI function call with given input params.
	pub fn encode_call(&self, tokens: Vec<Token>) -> Result<Vec<u8>, Error> {
		let params = self.interface.input_param_types();

		if !type_check(&tokens, &params) {
			return Err(ErrorKind::InvalidData.into());
		}

		let signed = short_signature(&self.interface.name, &params).to_vec();
		let encoded = Encoder::encode(tokens);
		Ok(signed.into_iter().chain(encoded.into_iter()).collect())
	}

	/// Parses the ABI function output to list of tokens.
	pub fn decode_output(&self, data: Vec<u8>) -> Result<Vec<Token>, Error> {
		Decoder::decode(&self.interface.output_param_types(), data)
	}

	/// Get the name of the function.
	pub fn name(&self) -> &str {
		&self.interface.name
	}
}

/// Check if all the types of the tokens match the given parameter types.
pub fn type_check(tokens: &[Token], param_types: &[ParamType]) -> bool {
	param_types.len() == tokens.len() && {
		param_types.iter().zip(tokens).all(|e| {
			let (param_type, token) = e;
			token.type_check(param_type)
		})
	}
}

#[cfg(test)]
mod tests {
	use hex::FromHex;
	use spec::{Function as FunctionInterface, ParamType, Param};
	use token::Token;
	use super::Function;
	use super::type_check;

	#[test]
	fn test_function_encode_call() {
		let interface = FunctionInterface {
			name: "baz".to_owned(),
			inputs: vec![Param {
				name: "a".to_owned(),
				kind: ParamType::Uint(32),
			}, Param {
				name: "b".to_owned(),
				kind: ParamType::Bool,
			}],
			outputs: vec![]
		};

		let func = Function::new(interface);
		let mut uint = [0u8; 32];
		uint[31] = 69;
		let encoded = func.encode_call(vec![Token::Uint(uint), Token::Bool(true)]).unwrap();
		let expected = "cdcd77c000000000000000000000000000000000000000000000000000000000000000450000000000000000000000000000000000000000000000000000000000000001".from_hex().unwrap();
		assert_eq!(encoded, expected);
	}

	#[test]
	fn test_type_check() {
		fn assert_type_check(tokens: Vec<Token>, param_types: Vec<ParamType>) {
			assert!(type_check(&tokens, &param_types))
		}

		fn assert_not_type_check(tokens: Vec<Token>, param_types: Vec<ParamType>) {
			assert!(!type_check(&tokens, &param_types))
		}

		assert_type_check(vec![Token::Uint([0u8; 32]), Token::Bool(false)], vec![ParamType::Uint(256), ParamType::Bool]);
		assert_type_check(vec![Token::Uint([0u8; 32]), Token::Bool(false)], vec![ParamType::Uint(32), ParamType::Bool]);

		assert_not_type_check(vec![Token::Uint([0u8; 32])], vec![ParamType::Uint(32), ParamType::Bool]);
		assert_not_type_check(vec![Token::Uint([0u8; 32]), Token::Bool(false)], vec![ParamType::Uint(32)]);
		assert_not_type_check(vec![Token::Bool(false), Token::Uint([0u8; 32])], vec![ParamType::Uint(32), ParamType::Bool]);

		assert_type_check(vec![Token::FixedBytes(vec![0, 0, 0, 0])], vec![ParamType::FixedBytes(4)]);
		assert_not_type_check(vec![Token::FixedBytes(vec![0, 0, 0, 0])], vec![ParamType::FixedBytes(3)]);

		assert_type_check(vec![Token::Array(vec![Token::Bool(false), Token::Bool(true)])], vec![ParamType::Array(Box::new(ParamType::Bool))]);
		assert_not_type_check(vec![Token::Array(vec![Token::Bool(false), Token::Uint([0u8; 32])])], vec![ParamType::Array(Box::new(ParamType::Bool))]);
		assert_not_type_check(vec![Token::Array(vec![Token::Bool(false), Token::Bool(true)])], vec![ParamType::Array(Box::new(ParamType::Address))]);

		assert_type_check(vec![Token::FixedArray(vec![Token::Bool(false), Token::Bool(true)])], vec![ParamType::FixedArray(Box::new(ParamType::Bool), 2)]);
		assert_not_type_check(vec![Token::FixedArray(vec![Token::Bool(false), Token::Bool(true)])], vec![ParamType::FixedArray(Box::new(ParamType::Bool), 3)]);
		assert_not_type_check(vec![Token::FixedArray(vec![Token::Bool(false), Token::Uint([0u8; 32])])], vec![ParamType::FixedArray(Box::new(ParamType::Bool), 2)]);
		assert_not_type_check(vec![Token::FixedArray(vec![Token::Bool(false), Token::Bool(true)])], vec![ParamType::FixedArray(Box::new(ParamType::Address), 2)]);
	}
}