aboutsummaryrefslogblamecommitdiffstats
path: root/solidityEndToEndTest.cpp
blob: 35c2a3b0288a04731da01370fd77bdb780ad88e5 (plain) (tree)








































                                                                                      
                                                    
 
                                                                  

                                                                                
                                        
                                                 
                                

         
                                                                  
         

                                                             

         
                                                                      
         
                                                                     


         

                                                              
                                                  

                                                                                                                                                                                



                                                                                                                             
                                                         


                                


                                                                                                             
                                                         

                                                                                 
                    


                                                                                                                              
                                                      

         



                                                 
                       

  
                                                                          





                                                                                                                   
                                  
                             
                                                              





                                                               

                                                      









                                                                                                      





                                                                       










                                                                                                      





                                                                       

 












                                                                                                   



































                                                                                                   
















                                                                                                          





                                                                        










                                                                                                                        

                                                                                                                         


                                                                








                                                                                                                            

                                                                              


                                                                                                                      






                                                                                                                                  

                                                                          










                                                                                                     


                                                                      

 








                                                                                                    

                                                                      










                                                                                                  

                                                                         









                                                                                                        

                                                                             








                                                                                                 

                                                                        
 






                           

/*
    This file is part of cpp-ethereum.

    cpp-ethereum is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    cpp-ethereum is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with cpp-ethereum.  If not, see <http://www.gnu.org/licenses/>.
*/
/**
 * @author Christian <c@ethdev.com>
 * @date 2014
 * Unit tests for the solidity expression compiler, testing the behaviour of the code.
 */

#include <string>
#include <boost/test/unit_test.hpp>
#include <libethereum/State.h>
#include <libethereum/Executive.h>
#include <libsolidity/CompilerStack.h>

using namespace std;

namespace dev
{
namespace solidity
{
namespace test
{

class ExecutionFramework
{
public:
    ExecutionFramework() { g_logVerbosity = 0; }

    bytes const& compileAndRun(std::string const& _sourceCode)
    {
        bytes code = dev::solidity::CompilerStack::compile(_sourceCode);
        sendMessage(code, true);
        BOOST_REQUIRE(!m_output.empty());
        return m_output;
    }

    bytes const& callFunction(byte _index, bytes const& _data)
    {
        sendMessage(bytes(1, _index) + _data, false);
        return m_output;
    }

    bytes const& callFunction(byte _index, u256 const& _argument1)
    {
        return callFunction(_index, toBigEndian(_argument1));
    }

private:
    void sendMessage(bytes const& _data, bool _isCreation)
    {
        eth::Executive executive(m_state);
        eth::Transaction t = _isCreation ? eth::Transaction(0, m_gasPrice, m_gas, _data, 0, KeyPair::create().sec())
                                         : eth::Transaction(0, m_gasPrice, m_gas, m_contractAddress, _data, 0, KeyPair::create().sec());
        bytes transactionRLP = t.rlp();
        try
        {
            // this will throw since the transaction is invalid, but it should nevertheless store the transaction
            executive.setup(&transactionRLP);
        }
        catch (...) {}
        if (_isCreation)
        {
            BOOST_REQUIRE(!executive.create(Address(), 0, m_gasPrice, m_gas, &_data, Address()));
            m_contractAddress = executive.newAddress();
            BOOST_REQUIRE(m_contractAddress);
            BOOST_REQUIRE(m_state.addressHasCode(m_contractAddress));
        }
        else
            BOOST_REQUIRE(!executive.call(m_contractAddress, Address(), 0, m_gasPrice, &_data, m_gas, Address()));
        BOOST_REQUIRE(executive.go());
        executive.finalize();
        m_output = executive.out().toVector();
    }

    Address m_contractAddress;
    eth::State m_state;
    u256 const m_gasPrice = 100 * eth::szabo;
    u256 const m_gas = 1000000;
    bytes m_output;
};

BOOST_FIXTURE_TEST_SUITE(SolidityCompilerEndToEndTest, ExecutionFramework)

BOOST_AUTO_TEST_CASE(smoke_test)
{
    char const* sourceCode = "contract test {\n"
                             "  function f(uint a) returns(uint d) { return a * 7; }\n"
                             "}\n";
    compileAndRun(sourceCode);
    u256 a = 0x200030004;
    BOOST_CHECK(callFunction(0, a) == toBigEndian(a * 7));
}

BOOST_AUTO_TEST_CASE(empty_contract)
{
    char const* sourceCode = "contract test {\n"
                             "}\n";
    compileAndRun(sourceCode);
    BOOST_CHECK(callFunction(0, bytes()).empty());
}

BOOST_AUTO_TEST_CASE(recursive_calls)
{
    char const* sourceCode = "contract test {\n"
                             "  function f(uint n) returns(uint nfac) {\n"
                             "    if (n <= 1) return 1;\n"
                             "    else return n * f(n - 1);\n"
                             "  }\n"
                             "}\n";
    compileAndRun(sourceCode);
    BOOST_CHECK(callFunction(0, u256(0)) == toBigEndian(u256(1)));
    BOOST_CHECK(callFunction(0, u256(1)) == toBigEndian(u256(1)));
    BOOST_CHECK(callFunction(0, u256(2)) == toBigEndian(u256(2)));
    BOOST_CHECK(callFunction(0, u256(3)) == toBigEndian(u256(6)));
    BOOST_CHECK(callFunction(0, u256(4)) == toBigEndian(u256(24)));
}

BOOST_AUTO_TEST_CASE(while_loop)
{
    char const* sourceCode = "contract test {\n"
                             "  function f(uint n) returns(uint nfac) {\n"
                             "    nfac = 1;\n"
                             "    var i = 2;\n"
                             "    while (i <= n) nfac *= i++;\n"
                             "  }\n"
                             "}\n";
    compileAndRun(sourceCode);
    BOOST_CHECK(callFunction(0, u256(0)) == toBigEndian(u256(1)));
    BOOST_CHECK(callFunction(0, u256(1)) == toBigEndian(u256(1)));
    BOOST_CHECK(callFunction(0, u256(2)) == toBigEndian(u256(2)));
    BOOST_CHECK(callFunction(0, u256(3)) == toBigEndian(u256(6)));
    BOOST_CHECK(callFunction(0, u256(4)) == toBigEndian(u256(24)));
}

BOOST_AUTO_TEST_CASE(break_outside_loop)
{
    // break and continue outside loops should be simply ignored
    char const* sourceCode = "contract test {\n"
                             "  function f(uint x) returns(uint y) {\n"
                             "    break; continue; return 2;\n"
                             "  }\n"
                             "}\n";
    ExecutionFramework framework;
    framework.compileAndRun(sourceCode);
    BOOST_CHECK(framework.callFunction(0, u256(0)) == toBigEndian(u256(2)));
}

BOOST_AUTO_TEST_CASE(nested_loops)
{
    // tests that break and continue statements in nested loops jump to the correct place
    char const* sourceCode = "contract test {\n"
                             "  function f(uint x) returns(uint y) {\n"
                             "    while (x > 1) {\n"
                             "      if (x == 10) break;\n"
                             "      while (x > 5) {\n"
                             "        if (x == 8) break;\n"
                             "        x--;\n"
                             "        if (x == 6) continue;\n"
                             "        return x;\n"
                             "      }\n"
                             "      x--;\n"
                             "      if (x == 3) continue;\n"
                             "      break;\n"
                             "    }\n"
                             "    return x;\n"
                             "  }\n"
                             "}\n";
    ExecutionFramework framework;
    framework.compileAndRun(sourceCode);
    BOOST_CHECK(framework.callFunction(0, u256(0)) == toBigEndian(u256(0)));
    BOOST_CHECK(framework.callFunction(0, u256(1)) == toBigEndian(u256(1)));
    BOOST_CHECK(framework.callFunction(0, u256(2)) == toBigEndian(u256(1)));
    BOOST_CHECK(framework.callFunction(0, u256(3)) == toBigEndian(u256(2)));
    BOOST_CHECK(framework.callFunction(0, u256(4)) == toBigEndian(u256(2)));
    BOOST_CHECK(framework.callFunction(0, u256(5)) == toBigEndian(u256(4)));
    BOOST_CHECK(framework.callFunction(0, u256(6)) == toBigEndian(u256(5)));
    BOOST_CHECK(framework.callFunction(0, u256(7)) == toBigEndian(u256(5)));
    BOOST_CHECK(framework.callFunction(0, u256(8)) == toBigEndian(u256(7)));
    BOOST_CHECK(framework.callFunction(0, u256(9)) == toBigEndian(u256(8)));
    BOOST_CHECK(framework.callFunction(0, u256(10)) == toBigEndian(u256(10)));
    BOOST_CHECK(framework.callFunction(0, u256(11)) == toBigEndian(u256(10)));
}

BOOST_AUTO_TEST_CASE(calling_other_functions)
{
    // note that the index of a function is its index in the sorted sequence of functions
    char const* sourceCode = "contract collatz {\n"
                             "  function run(uint x) returns(uint y) {\n"
                             "    while ((y = x) > 1) {\n"
                             "      if (x % 2 == 0) x = evenStep(x);\n"
                             "      else x = oddStep(x);\n"
                             "    }\n"
                             "  }\n"
                             "  function evenStep(uint x) returns(uint y) {\n"
                             "    return x / 2;\n"
                             "  }\n"
                             "  function oddStep(uint x) returns(uint y) {\n"
                             "    return 3 * x + 1;\n"
                             "  }\n"
                             "}\n";
    compileAndRun(sourceCode);
    BOOST_CHECK(callFunction(2, u256(0)) == toBigEndian(u256(0)));
    BOOST_CHECK(callFunction(2, u256(1)) == toBigEndian(u256(1)));
    BOOST_CHECK(callFunction(2, u256(2)) == toBigEndian(u256(1)));
    BOOST_CHECK(callFunction(2, u256(8)) == toBigEndian(u256(1)));
    BOOST_CHECK(callFunction(2, u256(127)) == toBigEndian(u256(1)));
}

BOOST_AUTO_TEST_CASE(many_local_variables)
{
    char const* sourceCode = "contract test {\n"
                             "  function run(uint x1, uint x2, uint x3) returns(uint y) {\n"
                             "    var a = 0x1; var b = 0x10; var c = 0x100;\n"
                             "    y = a + b + c + x1 + x2 + x3;\n"
                             "    y += b + x2;\n"
                             "  }\n"
                             "}\n";
    compileAndRun(sourceCode);
    BOOST_CHECK(callFunction(0, toBigEndian(u256(0x1000)) + toBigEndian(u256(0x10000)) + toBigEndian(u256(0x100000)))
                == toBigEndian(u256(0x121121)));
}

BOOST_AUTO_TEST_CASE(packing_unpacking_types)
{
    char const* sourceCode = "contract test {\n"
                             "  function run(bool a, uint32 b, uint64 c) returns(uint256 y) {\n"
                             "    if (a) y = 1;\n"
                             "    y = y * 0x100000000 | ~b;\n"
                             "    y = y * 0x10000000000000000 | ~c;\n"
                             "  }\n"
                             "}\n";
    compileAndRun(sourceCode);
    BOOST_CHECK(callFunction(0, fromHex("01""0f0f0f0f""f0f0f0f0f0f0f0f0"))
                == fromHex("00000000000000000000000000000000000000""01""f0f0f0f0""0f0f0f0f0f0f0f0f"));
}

BOOST_AUTO_TEST_CASE(multiple_return_values)
{
    char const* sourceCode = "contract test {\n"
                             "  function run(bool x1, uint x2) returns(uint y1, bool y2, uint y3) {\n"
                             "    y1 = x2; y2 = x1;\n"
                             "  }\n"
                             "}\n";
    compileAndRun(sourceCode);
    BOOST_CHECK(callFunction(0, bytes(1, 1) + toBigEndian(u256(0xcd)))
                == toBigEndian(u256(0xcd)) + bytes(1, 1) + toBigEndian(u256(0)));
}

BOOST_AUTO_TEST_CASE(short_circuiting)
{
    char const* sourceCode = "contract test {\n"
                             "  function run(uint x) returns(uint y) {\n"
                             "    x == 0 || ((x = 8) > 0);\n"
                             "    return x;"
                             "  }\n"
                             "}\n";
    compileAndRun(sourceCode);
    BOOST_CHECK(callFunction(0, u256(0)) == toBigEndian(u256(0)));
    BOOST_CHECK(callFunction(0, u256(1)) == toBigEndian(u256(8)));
}

BOOST_AUTO_TEST_CASE(high_bits_cleaning)
{
    char const* sourceCode = "contract test {\n"
                             "  function run() returns(uint256 y) {\n"
                             "    uint32 x = uint32(0xffffffff) + 10;\n"
                             "    if (x >= 0xffffffff) return 0;\n"
                             "    return x;"
                             "  }\n"
                             "}\n";
    compileAndRun(sourceCode);
    BOOST_CHECK(callFunction(0, bytes()) == toBigEndian(u256(9)));
}

BOOST_AUTO_TEST_CASE(sign_extension)
{
    char const* sourceCode = "contract test {\n"
                             "  function run() returns(uint256 y) {\n"
                             "    int64 x = -int32(0xff);\n"
                             "    if (x >= 0xff) return 0;\n"
                             "    return -uint256(x);"
                             "  }\n"
                             "}\n";
    compileAndRun(sourceCode);
    BOOST_CHECK(callFunction(0, bytes()) == toBigEndian(u256(0xff)));
}

BOOST_AUTO_TEST_CASE(small_unsigned_types)
{
    char const* sourceCode = "contract test {\n"
                             "  function run() returns(uint256 y) {\n"
                             "    uint32 x = uint32(0xffffff) * 0xffffff;\n"
                             "    return x / 0x100;"
                             "  }\n"
                             "}\n";
    compileAndRun(sourceCode);
    BOOST_CHECK(callFunction(0, bytes()) == toBigEndian(u256(0xfe0000)));
}

BOOST_AUTO_TEST_CASE(small_signed_types)
{
    char const* sourceCode = "contract test {\n"
                             "  function run() returns(int256 y) {\n"
                             "    return -int32(10) * -int64(20);\n"
                             "  }\n"
                             "}\n";
    compileAndRun(sourceCode);
    BOOST_CHECK(callFunction(0, bytes()) == toBigEndian(u256(200)));
}

BOOST_AUTO_TEST_SUITE_END()

}
}
} // end namespaces