Bläddra i källkod

Add tests for primitive arithmetic and bitwise

This is includes long 256 bit ops.

Signed-off-by: Sean Young <sean@mess.org>
Sean Young 5 år sedan
förälder
incheckning
41e0656289

+ 70 - 20
src/emit/ethabiencoder.rs

@@ -18,7 +18,7 @@ impl EthAbiEncoder {
         &self,
         &self,
         contract: &Contract<'a>,
         contract: &Contract<'a>,
         load: bool,
         load: bool,
-        function: FunctionValue,
+        function: FunctionValue<'a>,
         ty: &ast::Type,
         ty: &ast::Type,
         arg: BasicValueEnum<'a>,
         arg: BasicValueEnum<'a>,
         fixed: &mut PointerValue<'a>,
         fixed: &mut PointerValue<'a>,
@@ -32,7 +32,7 @@ impl EthAbiEncoder {
             | ast::Type::Int(_)
             | ast::Type::Int(_)
             | ast::Type::Uint(_)
             | ast::Type::Uint(_)
             | ast::Type::Bytes(_) => {
             | ast::Type::Bytes(_) => {
-                self.encode_primitive(contract, load, ty, *fixed, arg);
+                self.encode_primitive(contract, load, function, ty, *fixed, arg);
 
 
                 *fixed = unsafe {
                 *fixed = unsafe {
                     contract.builder.build_gep(
                     contract.builder.build_gep(
@@ -43,7 +43,14 @@ impl EthAbiEncoder {
                 };
                 };
             }
             }
             ast::Type::Enum(n) => {
             ast::Type::Enum(n) => {
-                self.encode_primitive(contract, load, &contract.ns.enums[*n].ty, *fixed, arg);
+                self.encode_primitive(
+                    contract,
+                    load,
+                    function,
+                    &contract.ns.enums[*n].ty,
+                    *fixed,
+                    arg,
+                );
             }
             }
             ast::Type::Array(_, dim) => {
             ast::Type::Array(_, dim) => {
                 let arg = if load {
                 let arg = if load {
@@ -89,6 +96,7 @@ impl EthAbiEncoder {
                     self.encode_primitive(
                     self.encode_primitive(
                         contract,
                         contract,
                         false,
                         false,
+                        function,
                         &ast::Type::Uint(32),
                         &ast::Type::Uint(32),
                         *fixed,
                         *fixed,
                         (*offset).into(),
                         (*offset).into(),
@@ -123,6 +131,7 @@ impl EthAbiEncoder {
                     self.encode_primitive(
                     self.encode_primitive(
                         contract,
                         contract,
                         false,
                         false,
+                        function,
                         &ast::Type::Uint(32),
                         &ast::Type::Uint(32),
                         *dynamic,
                         *dynamic,
                         len.into(),
                         len.into(),
@@ -254,6 +263,7 @@ impl EthAbiEncoder {
                 self.encode_primitive(
                 self.encode_primitive(
                     contract,
                     contract,
                     false,
                     false,
+                    function,
                     &ast::Type::Uint(32),
                     &ast::Type::Uint(32),
                     *fixed,
                     *fixed,
                     (*offset).into(),
                     (*offset).into(),
@@ -291,7 +301,14 @@ impl EthAbiEncoder {
                     .into_int_value();
                     .into_int_value();
 
 
                 // write the current offset to fixed
                 // write the current offset to fixed
-                self.encode_primitive(contract, false, &ast::Type::Uint(32), *dynamic, len.into());
+                self.encode_primitive(
+                    contract,
+                    false,
+                    function,
+                    &ast::Type::Uint(32),
+                    *dynamic,
+                    len.into(),
+                );
 
 
                 *dynamic = unsafe {
                 *dynamic = unsafe {
                     contract.builder.build_gep(
                     contract.builder.build_gep(
@@ -363,13 +380,14 @@ impl EthAbiEncoder {
     }
     }
 
 
     /// ABI encode a single primitive
     /// ABI encode a single primitive
-    fn encode_primitive(
+    fn encode_primitive<'a>(
         &self,
         &self,
-        contract: &Contract,
+        contract: &Contract<'a>,
         load: bool,
         load: bool,
+        function: FunctionValue<'a>,
         ty: &ast::Type,
         ty: &ast::Type,
         dest: PointerValue,
         dest: PointerValue,
-        arg: BasicValueEnum,
+        arg: BasicValueEnum<'a>,
     ) {
     ) {
         match ty {
         match ty {
             ast::Type::Bool => {
             ast::Type::Bool => {
@@ -463,6 +481,41 @@ impl EthAbiEncoder {
                     arg
                     arg
                 };
                 };
 
 
+                let dest8 = contract.builder.build_pointer_cast(
+                    dest,
+                    contract.context.i8_type().ptr_type(AddressSpace::Generic),
+                    "dest8",
+                );
+
+                if let ast::Type::Int(_) = ty {
+                    let negative = contract.builder.build_int_compare(
+                        IntPredicate::SLT,
+                        arg.into_int_value(),
+                        arg.into_int_value().get_type().const_zero(),
+                        "neg",
+                    );
+
+                    let signval = contract
+                        .builder
+                        .build_select(
+                            negative,
+                            contract.context.i64_type().const_int(std::u64::MAX, true),
+                            contract.context.i64_type().const_zero(),
+                            "val",
+                        )
+                        .into_int_value();
+
+                    contract.builder.build_call(
+                        contract.module.get_function("__memset8").unwrap(),
+                        &[
+                            dest8.into(),
+                            signval.into(),
+                            contract.context.i32_type().const_int(4, false).into(),
+                        ],
+                        "",
+                    );
+                }
+
                 // now convert to be
                 // now convert to be
                 let bswap = contract.llvm_bswap(*n as u32);
                 let bswap = contract.llvm_bswap(*n as u32);
 
 
@@ -474,12 +527,6 @@ impl EthAbiEncoder {
                     .unwrap()
                     .unwrap()
                     .into_int_value();
                     .into_int_value();
 
 
-                let dest8 = contract.builder.build_pointer_cast(
-                    dest,
-                    contract.context.i8_type().ptr_type(AddressSpace::Generic),
-                    "dest8",
-                );
-
                 // our value is big endian, 32 bytes. So, find the offset within the 32 bytes
                 // our value is big endian, 32 bytes. So, find the offset within the 32 bytes
                 // where our value starts
                 // where our value starts
                 let int8_ptr = unsafe {
                 let int8_ptr = unsafe {
@@ -690,9 +737,11 @@ impl EthAbiEncoder {
                 let val = if load {
                 let val = if load {
                     arg.into_pointer_value()
                     arg.into_pointer_value()
                 } else {
                 } else {
-                    let temp = contract
-                        .builder
-                        .build_alloca(arg.into_int_value().get_type(), &format!("bytes{}", n));
+                    let temp = contract.build_alloca(
+                        function,
+                        arg.into_int_value().get_type(),
+                        &format!("bytes{}", n),
+                    );
 
 
                     contract.builder.build_store(temp, arg.into_int_value());
                     contract.builder.build_store(temp, arg.into_int_value());
 
 
@@ -1208,9 +1257,9 @@ impl EthAbiEncoder {
             }
             }
             ast::Type::Bytes(b) => {
             ast::Type::Bytes(b) => {
                 let int_type = contract.context.custom_width_int_type(*b as u32 * 8);
                 let int_type = contract.context.custom_width_int_type(*b as u32 * 8);
-                let type_size = int_type.size_of();
 
 
-                let store = to.unwrap_or_else(|| contract.builder.build_alloca(int_type, "stack"));
+                let store =
+                    to.unwrap_or_else(|| contract.build_alloca(function, int_type, "stack"));
 
 
                 contract.builder.build_call(
                 contract.builder.build_call(
                     contract.module.get_function("__beNtoleN").unwrap(),
                     contract.module.get_function("__beNtoleN").unwrap(),
@@ -1225,8 +1274,9 @@ impl EthAbiEncoder {
                             )
                             )
                             .into(),
                             .into(),
                         contract
                         contract
-                            .builder
-                            .build_int_truncate(type_size, contract.context.i32_type(), "size")
+                            .context
+                            .i32_type()
+                            .const_int(*b as u64, false)
                             .into(),
                             .into(),
                     ],
                     ],
                     "",
                     "",

+ 3 - 3
src/emit/ewasm.rs

@@ -672,7 +672,7 @@ impl EwasmTarget {
         selector: Option<IntValue<'b>>,
         selector: Option<IntValue<'b>>,
         constant: Option<(PointerValue<'b>, u64)>,
         constant: Option<(PointerValue<'b>, u64)>,
         load: bool,
         load: bool,
-        function: FunctionValue,
+        function: FunctionValue<'b>,
         args: &[BasicValueEnum<'b>],
         args: &[BasicValueEnum<'b>],
         spec: &[ast::Parameter],
         spec: &[ast::Parameter],
     ) -> (PointerValue<'b>, IntValue<'b>) {
     ) -> (PointerValue<'b>, IntValue<'b>) {
@@ -1151,7 +1151,7 @@ impl<'a> TargetRuntime<'a> for EwasmTarget {
         contract: &Contract<'b>,
         contract: &Contract<'b>,
         selector: Option<IntValue<'b>>,
         selector: Option<IntValue<'b>>,
         load: bool,
         load: bool,
-        function: FunctionValue,
+        function: FunctionValue<'b>,
         args: &[BasicValueEnum<'b>],
         args: &[BasicValueEnum<'b>],
         spec: &[ast::Parameter],
         spec: &[ast::Parameter],
     ) -> (PointerValue<'b>, IntValue<'b>) {
     ) -> (PointerValue<'b>, IntValue<'b>) {
@@ -1182,7 +1182,7 @@ impl<'a> TargetRuntime<'a> for EwasmTarget {
     fn create_contract<'b>(
     fn create_contract<'b>(
         &mut self,
         &mut self,
         contract: &Contract<'b>,
         contract: &Contract<'b>,
-        function: FunctionValue,
+        function: FunctionValue<'b>,
         success: Option<&mut BasicValueEnum<'b>>,
         success: Option<&mut BasicValueEnum<'b>>,
         contract_no: usize,
         contract_no: usize,
         constructor_no: Option<usize>,
         constructor_no: Option<usize>,

+ 1 - 1
src/emit/generic.rs

@@ -540,7 +540,7 @@ impl<'a> TargetRuntime<'a> for GenericTarget {
         contract: &Contract<'b>,
         contract: &Contract<'b>,
         selector: Option<IntValue<'b>>,
         selector: Option<IntValue<'b>>,
         load: bool,
         load: bool,
-        function: FunctionValue,
+        function: FunctionValue<'b>,
         args: &[BasicValueEnum<'b>],
         args: &[BasicValueEnum<'b>],
         spec: &[ast::Parameter],
         spec: &[ast::Parameter],
     ) -> (PointerValue<'b>, IntValue<'b>) {
     ) -> (PointerValue<'b>, IntValue<'b>) {

+ 2 - 2
src/emit/mod.rs

@@ -67,7 +67,7 @@ pub trait TargetRuntime<'a> {
         contract: &Contract<'a>,
         contract: &Contract<'a>,
         selector: Option<IntValue<'a>>,
         selector: Option<IntValue<'a>>,
         load: bool,
         load: bool,
-        function: FunctionValue,
+        function: FunctionValue<'a>,
         args: &[BasicValueEnum<'a>],
         args: &[BasicValueEnum<'a>],
         spec: &[ast::Parameter],
         spec: &[ast::Parameter],
     ) -> (PointerValue<'a>, IntValue<'a>);
     ) -> (PointerValue<'a>, IntValue<'a>);
@@ -193,7 +193,7 @@ pub trait TargetRuntime<'a> {
     fn create_contract<'b>(
     fn create_contract<'b>(
         &mut self,
         &mut self,
         contract: &Contract<'b>,
         contract: &Contract<'b>,
-        function: FunctionValue,
+        function: FunctionValue<'b>,
         success: Option<&mut BasicValueEnum<'b>>,
         success: Option<&mut BasicValueEnum<'b>>,
         contract_no: usize,
         contract_no: usize,
         constructor_no: Option<usize>,
         constructor_no: Option<usize>,

+ 1 - 1
src/emit/sabre.rs

@@ -605,7 +605,7 @@ impl<'a> TargetRuntime<'a> for SabreTarget {
         contract: &Contract<'b>,
         contract: &Contract<'b>,
         selector: Option<IntValue<'b>>,
         selector: Option<IntValue<'b>>,
         load: bool,
         load: bool,
-        function: FunctionValue,
+        function: FunctionValue<'b>,
         args: &[BasicValueEnum<'b>],
         args: &[BasicValueEnum<'b>],
         spec: &[ast::Parameter],
         spec: &[ast::Parameter],
     ) -> (PointerValue<'b>, IntValue<'b>) {
     ) -> (PointerValue<'b>, IntValue<'b>) {

+ 1 - 1
src/emit/solana.rs

@@ -535,7 +535,7 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
         contract: &Contract<'a>,
         contract: &Contract<'a>,
         selector: Option<IntValue<'a>>,
         selector: Option<IntValue<'a>>,
         load: bool,
         load: bool,
-        function: FunctionValue,
+        function: FunctionValue<'a>,
         args: &[BasicValueEnum<'a>],
         args: &[BasicValueEnum<'a>],
         spec: &[ast::Parameter],
         spec: &[ast::Parameter],
     ) -> (PointerValue<'a>, IntValue<'a>) {
     ) -> (PointerValue<'a>, IntValue<'a>) {

+ 1 - 1
src/emit/substrate.rs

@@ -2995,7 +2995,7 @@ impl<'a> TargetRuntime<'a> for SubstrateTarget {
     fn create_contract<'b>(
     fn create_contract<'b>(
         &mut self,
         &mut self,
         contract: &Contract<'b>,
         contract: &Contract<'b>,
-        function: FunctionValue,
+        function: FunctionValue<'b>,
         success: Option<&mut BasicValueEnum<'b>>,
         success: Option<&mut BasicValueEnum<'b>>,
         contract_no: usize,
         contract_no: usize,
         constructor_no: Option<usize>,
         constructor_no: Option<usize>,

BIN
stdlib/solana.bc


+ 20 - 0
stdlib/solana.c

@@ -46,6 +46,16 @@ void __bzero8(void *_dest, uint32_t length)
     while (--length);
     while (--length);
 }
 }
 
 
+void __memset8(void *_dest, uint64_t val, uint32_t length)
+{
+    uint64_t *dest = _dest;
+
+    do
+    {
+        *dest++ = val;
+    } while (--length);
+}
+
 // Create a new vector. If initial is -1 then clear the data. This is done since a null pointer valid in wasm
 // Create a new vector. If initial is -1 then clear the data. This is done since a null pointer valid in wasm
 struct vector *vector_new(uint32_t members, uint32_t size, uint8_t *initial)
 struct vector *vector_new(uint32_t members, uint32_t size, uint8_t *initial)
 {
 {
@@ -122,6 +132,16 @@ void __beNtoleN(uint8_t *from, uint8_t *to, uint32_t length)
     } while (--length);
     } while (--length);
 }
 }
 
 
+void __leNtobeN(uint8_t *from, uint8_t *to, uint32_t length)
+{
+    to += length;
+
+    do
+    {
+        *--to = *from++;
+    } while (--length);
+}
+
 // This function is for used for abi encoding integers
 // This function is for used for abi encoding integers
 // ABI encoding is big endian.
 // ABI encoding is big endian.
 void __leNtobe32(uint8_t *from, uint8_t *to, uint32_t length)
 void __leNtobe32(uint8_t *from, uint8_t *to, uint32_t length)

+ 1 - 1
tests/solana.rs

@@ -17,7 +17,7 @@ use std::mem::{align_of, size_of};
 
 
 mod solana_tests;
 mod solana_tests;
 
 
-fn build_solidity(src: &'static str) -> VM {
+fn build_solidity(src: &str) -> VM {
     let mut cache = FileCache::new();
     let mut cache = FileCache::new();
 
 
     cache.set_file_contents("test.sol", src.to_string());
     cache.set_file_contents("test.sol", src.to_string());

+ 616 - 9
tests/solana_tests/primitives.rs

@@ -1,13 +1,14 @@
 use crate::build_solidity;
 use crate::build_solidity;
-
-/*
-
-We need to test
- - uintN
- - intN
- - bytesN
- - enum
-*/
+use num_bigint::{BigInt, BigUint};
+use rand::Rng;
+use std::ops::Add;
+use std::ops::BitAnd;
+use std::ops::Div;
+use std::ops::Mul;
+use std::ops::Rem;
+use std::ops::Shl;
+use std::ops::Shr;
+use std::ops::Sub;
 
 
 #[test]
 #[test]
 #[should_panic]
 #[should_panic]
@@ -163,3 +164,609 @@ fn test_enum() {
         &[ethabi::Token::Uint(ethereum_types::U256::from(6))],
         &[ethabi::Token::Uint(ethereum_types::U256::from(6))],
     );
     );
 }
 }
+
+#[test]
+fn bytes() {
+    let mut rng = rand::thread_rng();
+
+    for width in 1..32 {
+        let src = r#"
+        contract test {
+            function return_literal() public returns (bytes7) {
+                return hex"01020304050607";
+            }
+
+            function return_arg(bytes7 x) public returns (bytes7) {
+                return x;
+            }
+
+            function or(bytesN a, bytesN b) public returns (bytesN) {
+                return a | b;
+            }
+
+            function and(bytesN a, bytesN b) public returns (bytesN) {
+                return a & b;
+            }
+
+            function xor(bytesN a, bytesN b) public returns (bytesN) {
+                return a ^ b;
+            }
+
+            function shift_left(bytesN a, uint32 r) public returns (bytesN) {
+                return a << r;
+            }
+
+            function shift_right(bytesN a, uint32 r) public returns (bytesN) {
+                return a >> r;
+            }
+        }"#
+        .replace("bytesN", &format!("bytes{}", width));
+
+        let mut vm = build_solidity(&src);
+
+        vm.constructor(&[]);
+
+        let returns = vm.function("return_literal", &[]);
+
+        assert_eq!(
+            returns,
+            vec![ethabi::Token::FixedBytes(vec![1, 2, 3, 4, 5, 6, 7]),]
+        );
+
+        let returns = vm.function(
+            "return_arg",
+            &[ethabi::Token::FixedBytes(vec![1, 2, 3, 4, 5, 6, 7])],
+        );
+
+        assert_eq!(
+            returns,
+            vec![ethabi::Token::FixedBytes(vec![1, 2, 3, 4, 5, 6, 7])]
+        );
+
+        for _ in 0..10 {
+            let mut a = Vec::new();
+            let mut b = Vec::new();
+
+            a.resize(width, 0);
+            b.resize(width, 0);
+
+            rng.fill(&mut a[..]);
+            rng.fill(&mut b[..]);
+
+            let or = vm.function(
+                "or",
+                &[
+                    ethabi::Token::FixedBytes(a.to_vec()),
+                    ethabi::Token::FixedBytes(b.to_vec()),
+                ],
+            );
+
+            let res: Vec<u8> = a.iter().zip(b.iter()).map(|(a, b)| a | b).collect();
+
+            println!(
+                "{} | {} = {}",
+                hex::encode(&a),
+                hex::encode(&b),
+                hex::encode(&res)
+            );
+
+            assert_eq!(or, vec![ethabi::Token::FixedBytes(res)]);
+
+            let and = vm.function(
+                "and",
+                &[
+                    ethabi::Token::FixedBytes(a.to_vec()),
+                    ethabi::Token::FixedBytes(b.to_vec()),
+                ],
+            );
+
+            let res: Vec<u8> = a.iter().zip(b.iter()).map(|(a, b)| a & b).collect();
+
+            assert_eq!(and, vec![ethabi::Token::FixedBytes(res)]);
+
+            let xor = vm.function(
+                "xor",
+                &[
+                    ethabi::Token::FixedBytes(a.to_vec()),
+                    ethabi::Token::FixedBytes(b.to_vec()),
+                ],
+            );
+
+            let res: Vec<u8> = a.iter().zip(b.iter()).map(|(a, b)| a ^ b).collect();
+
+            assert_eq!(xor, vec![ethabi::Token::FixedBytes(res)]);
+
+            let r = rng.gen::<u32>() % (width as u32 * 8);
+
+            println!("w = {} r = {}", width, r);
+
+            let shl = vm.function(
+                "shift_left",
+                &[
+                    ethabi::Token::FixedBytes(a.to_vec()),
+                    ethabi::Token::Uint(ethereum_types::U256::from(r)),
+                ],
+            );
+
+            let mut res = (BigUint::from_bytes_be(&a) << r).to_bytes_be();
+
+            while res.len() > width {
+                res.remove(0);
+            }
+
+            while res.len() < width {
+                res.insert(0, 0);
+            }
+
+            assert_eq!(shl, vec![ethabi::Token::FixedBytes(res)]);
+
+            let shr = vm.function(
+                "shift_right",
+                &[
+                    ethabi::Token::FixedBytes(a.to_vec()),
+                    ethabi::Token::Uint(ethereum_types::U256::from(r)),
+                ],
+            );
+
+            let mut res = (BigUint::from_bytes_be(&a) >> r).to_bytes_be();
+
+            while res.len() < width {
+                res.insert(0, 0);
+            }
+
+            assert_eq!(shr, vec![ethabi::Token::FixedBytes(res)]);
+        }
+    }
+}
+
+#[test]
+fn uint() {
+    let mut rng = rand::thread_rng();
+
+    for width in &[8, 16, 32, 64, 128, 256] {
+        let width: usize = *width;
+        let src = r#"
+        contract test {
+            function add(uintN a, uintN b) public returns (uintN) {
+                return a + b;
+            }
+
+            function sub(uintN a, uintN b) public returns (uintN) {
+                return a - b;
+            }
+
+            function mul(uintN a, uintN b) public returns (uintN) {
+                return a * b;
+            }
+
+            function div(uintN a, uintN b) public returns (uintN) {
+                return a / b;
+            }
+
+            function mod(uintN a, uintN b) public returns (uintN) {
+                return a % b;
+            }
+
+            function pow(uintN a, uintN b) public returns (uintN) {
+                return a ** b;
+            }
+
+            function or(uintN a, uintN b) public returns (uintN) {
+                return a | b;
+            }
+
+            function and(uintN a, uintN b) public returns (uintN) {
+                return a & b;
+            }
+
+            function xor(uintN a, uintN b) public returns (uintN) {
+                return a ^ b;
+            }
+
+            function shift_left(uintN a, uint32 r) public returns (uintN) {
+                return a << r;
+            }
+
+            function shift_right(uintN a, uint32 r) public returns (uintN) {
+                return a >> r;
+            }
+        }"#
+        .replace("uintN", &format!("uint{}", width));
+
+        let mut vm = build_solidity(&src);
+
+        vm.constructor(&[]);
+
+        for _ in 0..10 {
+            let mut a = Vec::new();
+            let mut b = Vec::new();
+
+            a.resize(width / 8, 0);
+            b.resize(width / 8, 0);
+
+            rng.fill(&mut a[..]);
+            rng.fill(&mut b[..]);
+
+            let mut a = ethereum_types::U256::from_big_endian(&a);
+            let mut b = ethereum_types::U256::from_big_endian(&b);
+
+            rng.fill(&mut a.0[..]);
+            rng.fill(&mut b.0[..]);
+
+            truncate_uint(&mut a, width);
+            truncate_uint(&mut b, width);
+
+            let add = vm.function("add", &[ethabi::Token::Uint(a), ethabi::Token::Uint(b)]);
+
+            let (mut res, _) = a.overflowing_add(b);
+
+            truncate_uint(&mut res, width);
+
+            assert_eq!(add, vec![ethabi::Token::Uint(res)]);
+
+            let sub = vm.function("sub", &[ethabi::Token::Uint(a), ethabi::Token::Uint(b)]);
+
+            let (mut res, _) = a.overflowing_sub(b);
+
+            truncate_uint(&mut res, width);
+
+            assert_eq!(sub, vec![ethabi::Token::Uint(res)]);
+
+            let mul = vm.function("mul", &[ethabi::Token::Uint(a), ethabi::Token::Uint(b)]);
+
+            let (mut res, _) = a.overflowing_mul(b);
+
+            truncate_uint(&mut res, width);
+
+            assert_eq!(mul, vec![ethabi::Token::Uint(res)]);
+
+            let pow = vm.function("pow", &[ethabi::Token::Uint(a), ethabi::Token::Uint(b)]);
+
+            let (mut res, _) = a.overflowing_pow(b);
+
+            truncate_uint(&mut res, width);
+
+            assert_eq!(pow, vec![ethabi::Token::Uint(res)]);
+
+            if b != ethereum_types::U256::zero() {
+                let div = vm.function("div", &[ethabi::Token::Uint(a), ethabi::Token::Uint(b)]);
+
+                let mut res = a.div(b);
+
+                truncate_uint(&mut res, width);
+
+                assert_eq!(div, vec![ethabi::Token::Uint(res)]);
+
+                let add = vm.function("mod", &[ethabi::Token::Uint(a), ethabi::Token::Uint(b)]);
+
+                let mut res = a.rem(b);
+
+                truncate_uint(&mut res, width);
+
+                assert_eq!(add, vec![ethabi::Token::Uint(res)]);
+            }
+
+            let or = vm.function("or", &[ethabi::Token::Uint(a), ethabi::Token::Uint(b)]);
+
+            let mut res = ethereum_types::U256([
+                a.0[0] | b.0[0],
+                a.0[1] | b.0[1],
+                a.0[2] | b.0[2],
+                a.0[3] | b.0[3],
+            ]);
+
+            truncate_uint(&mut res, width);
+
+            assert_eq!(or, vec![ethabi::Token::Uint(res)]);
+
+            let and = vm.function("and", &[ethabi::Token::Uint(a), ethabi::Token::Uint(b)]);
+
+            let mut res = ethereum_types::U256([
+                a.0[0] & b.0[0],
+                a.0[1] & b.0[1],
+                a.0[2] & b.0[2],
+                a.0[3] & b.0[3],
+            ]);
+
+            truncate_uint(&mut res, width);
+
+            assert_eq!(and, vec![ethabi::Token::Uint(res)]);
+
+            let xor = vm.function("xor", &[ethabi::Token::Uint(a), ethabi::Token::Uint(b)]);
+
+            let mut res = ethereum_types::U256([
+                a.0[0] ^ b.0[0],
+                a.0[1] ^ b.0[1],
+                a.0[2] ^ b.0[2],
+                a.0[3] ^ b.0[3],
+            ]);
+
+            truncate_uint(&mut res, width);
+
+            assert_eq!(xor, vec![ethabi::Token::Uint(res)]);
+
+            let r = rng.gen::<u32>() % (width as u32);
+
+            let shl = vm.function(
+                "shift_left",
+                &[
+                    ethabi::Token::Uint(a),
+                    ethabi::Token::Uint(ethereum_types::U256::from(r)),
+                ],
+            );
+
+            let mut res = a.shl(r);
+
+            truncate_uint(&mut res, width);
+
+            assert_eq!(shl, vec![ethabi::Token::Uint(res)]);
+
+            let shr = vm.function(
+                "shift_right",
+                &[
+                    ethabi::Token::Uint(a),
+                    ethabi::Token::Uint(ethereum_types::U256::from(r)),
+                ],
+            );
+
+            let mut res = a.shr(r);
+
+            truncate_uint(&mut res, width);
+
+            assert_eq!(shr, vec![ethabi::Token::Uint(res)]);
+        }
+    }
+}
+
+fn truncate_uint(n: &mut ethereum_types::U256, width: usize) {
+    let mut bits = 256 - width;
+
+    let mut offset = 3;
+
+    while bits > 64 {
+        n.0[offset] = 0;
+
+        offset -= 1;
+        bits -= 64;
+    }
+
+    if bits > 0 {
+        n.0[offset] &= (1 << (64 - bits)) - 1;
+    }
+}
+
+#[test]
+fn int() {
+    let mut rng = rand::thread_rng();
+
+    for width in &[8, 16, 32, 64, 128, 256] {
+        let width: usize = *width;
+        let src = r#"
+        contract test {
+            function add(intN a, intN b) public returns (intN) {
+                return a + b;
+            }
+
+            function sub(intN a, intN b) public returns (intN) {
+                return a - b;
+            }
+
+            function mul(intN a, intN b) public returns (intN) {
+                return a * b;
+            }
+
+            function div(intN a, intN b) public returns (intN) {
+                 return a / b;
+            }
+
+            function mod(intN a, intN b) public returns (intN) {
+                return a % b;
+            }
+
+            function or(intN a, intN b) public returns (intN) {
+                return a | b;
+            }
+
+            function and(intN a, intN b) public returns (intN) {
+                return a & b;
+            }
+
+            function xor(intN a, intN b) public returns (intN) {
+                return a ^ b;
+            }
+
+            function shift_left(intN a, uint32 r) public returns (intN) {
+                return a << r;
+            }
+
+            function shift_right(intN a, uint32 r) public returns (intN) {
+                return a >> r;
+            }
+        }"#
+        .replace("intN", &format!("int{}", width));
+
+        let mut vm = build_solidity(&src);
+
+        vm.constructor(&[]);
+
+        for _ in 0..10 {
+            let mut a_bs = Vec::new();
+            let mut b_bs = Vec::new();
+
+            a_bs.resize(width / 8, 0);
+            b_bs.resize(width / 8, 0);
+
+            rng.fill(&mut a_bs[..]);
+            rng.fill(&mut b_bs[..]);
+
+            let mut a = ethereum_types::U256::from_big_endian(&a_bs);
+            let mut b = ethereum_types::U256::from_big_endian(&b_bs);
+
+            truncate_int(&mut a, width);
+            truncate_int(&mut b, width);
+
+            let big_a = eth_to_bigint(&a, width);
+            let big_b = eth_to_bigint(&b, width);
+
+            let add = vm.function("add", &[ethabi::Token::Int(a), ethabi::Token::Int(b)]);
+
+            let res = big_a.clone().add(&big_b);
+
+            let res = bigint_to_eth(&res, width);
+
+            assert_eq!(add, vec![ethabi::Token::Int(res)]);
+
+            let sub = vm.function("sub", &[ethabi::Token::Int(a), ethabi::Token::Int(b)]);
+
+            let res = bigint_to_eth(&big_a.clone().sub(&big_b), width);
+
+            assert_eq!(sub, vec![ethabi::Token::Int(res)]);
+
+            let mul = vm.function("mul", &[ethabi::Token::Int(a), ethabi::Token::Int(b)]);
+
+            let res = bigint_to_eth(&big_a.clone().mul(&big_b), width);
+
+            assert_eq!(mul, vec![ethabi::Token::Int(res)]);
+
+            if b != ethereum_types::U256::zero() {
+                let div = vm.function("div", &[ethabi::Token::Int(a), ethabi::Token::Int(b)]);
+
+                let res = bigint_to_eth(&big_a.clone().div(&big_b), width);
+
+                assert_eq!(div, vec![ethabi::Token::Int(res)]);
+
+                let add = vm.function("mod", &[ethabi::Token::Int(a), ethabi::Token::Int(b)]);
+
+                let res = big_a.clone().rem(&big_b);
+
+                let res = bigint_to_eth(&res, width);
+
+                assert_eq!(add, vec![ethabi::Token::Int(res)]);
+            }
+
+            let or = vm.function("or", &[ethabi::Token::Int(a), ethabi::Token::Int(b)]);
+
+            let mut res = ethereum_types::U256([
+                a.0[0] | b.0[0],
+                a.0[1] | b.0[1],
+                a.0[2] | b.0[2],
+                a.0[3] | b.0[3],
+            ]);
+
+            truncate_int(&mut res, width);
+
+            assert_eq!(or, vec![ethabi::Token::Int(res)]);
+
+            let and = vm.function("and", &[ethabi::Token::Int(a), ethabi::Token::Int(b)]);
+
+            let mut res = ethereum_types::U256([
+                a.0[0] & b.0[0],
+                a.0[1] & b.0[1],
+                a.0[2] & b.0[2],
+                a.0[3] & b.0[3],
+            ]);
+
+            truncate_int(&mut res, width);
+
+            assert_eq!(and, vec![ethabi::Token::Int(res)]);
+
+            let xor = vm.function("xor", &[ethabi::Token::Int(a), ethabi::Token::Int(b)]);
+
+            let mut res = ethereum_types::U256([
+                a.0[0] ^ b.0[0],
+                a.0[1] ^ b.0[1],
+                a.0[2] ^ b.0[2],
+                a.0[3] ^ b.0[3],
+            ]);
+
+            truncate_int(&mut res, width);
+
+            assert_eq!(xor, vec![ethabi::Token::Int(res)]);
+
+            let r = rng.gen::<u32>() % (width as u32);
+
+            let shl = vm.function(
+                "shift_left",
+                &[
+                    ethabi::Token::Int(a),
+                    ethabi::Token::Uint(ethereum_types::U256::from(r)),
+                ],
+            );
+
+            let mut res = a.shl(r);
+
+            truncate_int(&mut res, width);
+
+            assert_eq!(shl, vec![ethabi::Token::Int(res)]);
+
+            let shr = vm.function(
+                "shift_right",
+                &[
+                    ethabi::Token::Int(a),
+                    ethabi::Token::Uint(ethereum_types::U256::from(r)),
+                ],
+            );
+
+            let res = bigint_to_eth(&big_a.clone().shr(r), width);
+
+            assert_eq!(shr, vec![ethabi::Token::Int(res)]);
+        }
+    }
+}
+
+fn truncate_int(n: &mut ethereum_types::U256, width: usize) {
+    let sign =
+        n.bitand(ethereum_types::U256::from(1) << (width - 1)) != ethereum_types::U256::zero();
+
+    let mut bits = 256 - width;
+
+    let mut offset = 3;
+
+    while bits > 64 {
+        n.0[offset] = if sign { u64::MAX } else { 0 };
+
+        offset -= 1;
+        bits -= 64;
+    }
+
+    if bits > 0 {
+        if sign {
+            n.0[offset] |= !((1 << (64 - bits)) - 1);
+        } else {
+            n.0[offset] &= (1 << (64 - bits)) - 1;
+        }
+    }
+}
+
+fn bigint_to_eth(v: &BigInt, width: usize) -> ethereum_types::U256 {
+    let mut buf = v.to_signed_bytes_be();
+    let width = width / 8;
+
+    while buf.len() > width {
+        buf.remove(0);
+    }
+
+    let sign = if (buf[0] & 128) != 0 { 0xff } else { 0 };
+
+    while buf.len() < 32 {
+        buf.insert(0, sign);
+    }
+
+    ethereum_types::U256::from_big_endian(&buf)
+}
+
+fn eth_to_bigint(v: &ethereum_types::U256, width: usize) -> BigInt {
+    let mut buf = Vec::new();
+
+    buf.resize(32, 0);
+
+    v.to_big_endian(&mut buf);
+
+    let width = width / 8;
+
+    while buf.len() > width {
+        buf.remove(0);
+    }
+
+    BigInt::from_signed_bytes_be(&buf)
+}