diff --git a/CMakeModules/wasm.cmake b/CMakeModules/wasm.cmake index fd1fbeb3942..343e612ddca 100644 --- a/CMakeModules/wasm.cmake +++ b/CMakeModules/wasm.cmake @@ -39,7 +39,7 @@ macro(add_wast_target target SOURCE_FILES INCLUDE_FOLDERS DESTINATION_FOLDER) add_custom_command(OUTPUT ${outfile}.bc DEPENDS ${infile} - COMMAND ${WASM_CLANG} -emit-llvm -O3 --std=c++14 --target=wasm32 -I ${INCLUDE_FOLDERS} -fno-threadsafe-statics -fno-rtti -fno-exceptions -c ${infile} -o ${outfile}.bc + COMMAND ${WASM_CLANG} -emit-llvm -O3 --std=c++14 --target=wasm32 -fno-builtin -ffreestanding -nostdlib -I ${INCLUDE_FOLDERS} -fno-threadsafe-statics -fno-rtti -fno-exceptions -c ${infile} -o ${outfile}.bc IMPLICIT_DEPENDS CXX ${infile} COMMENT "Building LLVM bitcode ${outfile}.bc" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} diff --git a/contracts/CMakeLists.txt b/contracts/CMakeLists.txt index 07a6de6edee..c28e7f89522 100644 --- a/contracts/CMakeLists.txt +++ b/contracts/CMakeLists.txt @@ -1,4 +1,5 @@ add_subdirectory(currency) add_subdirectory(exchange) add_subdirectory(infinite) +add_subdirectory(test_api) #add_subdirectory(social) diff --git a/contracts/eoslib/db.h b/contracts/eoslib/db.h index 95ce034cbd7..dae97dabdb5 100644 --- a/contracts/eoslib/db.h +++ b/contracts/eoslib/db.h @@ -5,6 +5,7 @@ #pragma once #include +extern "C" { /** * @defgroup database Database API * @brief APIs that store and retreive data on the blockchain @@ -160,7 +161,7 @@ int32_t upper_bound_secondary_i128i128( AccountName scope, AccountName code, Tab int32_t lower_bound_secondary_i128i128( AccountName scope, AccountName code, TableName table, const void* key, void* data, uint32_t len ); -int32_t load_secondary_i128i128( AccountName scope, AccountName code, TableName table, const void* secondary, void* data, uint32_t len ); +int32_t load_secondary_i128i128( AccountName scope, AccountName code, TableName table, void* data, uint32_t len ); /** * @param data - must point to at lest 32 bytes containing {primary,secondary} @@ -174,4 +175,4 @@ bool remove_i128i128( AccountName scope, TableName table, const void* data ); bool store_i128i128( AccountName scope, TableName table, const void* data, uint32_t len ); ///@} dbi128i128 - +} diff --git a/contracts/eoslib/db.hpp b/contracts/eoslib/db.hpp index 9426e45de43..916d96654a9 100644 --- a/contracts/eoslib/db.hpp +++ b/contracts/eoslib/db.hpp @@ -1,8 +1,6 @@ #pragma once -extern "C" { #include -} diff --git a/contracts/eoslib/types.hpp b/contracts/eoslib/types.hpp index d2136b37bd9..3c641fdc705 100644 --- a/contracts/eoslib/types.hpp +++ b/contracts/eoslib/types.hpp @@ -21,20 +21,27 @@ namespace eos { * @ingroup types */ static constexpr uint64_t string_to_name( const char* str ) { + uint32_t len = 0; while( str[len] ) ++len; uint64_t value = 0; - for( uint32_t i = 0; i <= 12 && i < len; ++i ) { - value <<= 5; - value |= char_to_symbol( str[ len -1 - i ] ); - } + for( uint32_t i = 0; i <= 12; ++i ) { + uint64_t c = 0; + if( i < len && i <= 12 ) c = char_to_symbol( str[i] ); - if( len >= 13 ) { - value <<= 4; - value |= 0x0f & char_to_symbol( str[ 12 ] ); + if( i < 12 ) { + c &= 0x1f; + c <<= 64-5*(i+1); + } + else { + c &= 0x0f; + } + + value |= c; } + return value; } diff --git a/contracts/test_api/CMakeLists.txt b/contracts/test_api/CMakeLists.txt new file mode 100644 index 00000000000..eb17ef21164 --- /dev/null +++ b/contracts/test_api/CMakeLists.txt @@ -0,0 +1,2 @@ +file(GLOB SOURCE_FILES "*.cpp") +add_wast_target(test_api "${SOURCE_FILES}" "${CMAKE_SOURCE_DIR}/contracts" ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/contracts/test_api/test_api.cpp b/contracts/test_api/test_api.cpp new file mode 100644 index 00000000000..b06fad00a83 --- /dev/null +++ b/contracts/test_api/test_api.cpp @@ -0,0 +1,55 @@ +#include +#include "test_api.hpp" + +extern "C" { + + void init() { + + } + + void apply( unsigned long long code, unsigned long long action ) { + + //eos::print("==> CONTRACT: ", code, " ", action, "\n"); + + //test_types + WASM_TEST_HANDLER(test_types, types_size); + WASM_TEST_HANDLER(test_types, char_to_symbol); + WASM_TEST_HANDLER(test_types, string_to_name); + WASM_TEST_HANDLER(test_types, name_class); + + //test_message + WASM_TEST_HANDLER(test_message, read_message); + WASM_TEST_HANDLER(test_message, read_message_to_0); + WASM_TEST_HANDLER(test_message, read_message_to_64k); + WASM_TEST_HANDLER_EX(test_message, require_notice); + WASM_TEST_HANDLER(test_message, require_auth); + WASM_TEST_HANDLER(test_message, assert_false); + WASM_TEST_HANDLER(test_message, assert_true); + WASM_TEST_HANDLER(test_message, now); + + //test_print + WASM_TEST_HANDLER(test_print, test_prints); + WASM_TEST_HANDLER(test_print, test_printi); + WASM_TEST_HANDLER(test_print, test_printi128); + WASM_TEST_HANDLER(test_print, test_printn); + + //test_math + WASM_TEST_HANDLER(test_math, test_multeq_i128); + WASM_TEST_HANDLER(test_math, test_diveq_i128); + WASM_TEST_HANDLER(test_math, test_diveq_i128_by_0); + + //test db + WASM_TEST_HANDLER(test_db, key_i64_general); + WASM_TEST_HANDLER(test_db, key_i64_remove_all); + WASM_TEST_HANDLER(test_db, key_i64_small_load); + WASM_TEST_HANDLER(test_db, key_i64_small_store); + WASM_TEST_HANDLER(test_db, key_i64_store_scope); + WASM_TEST_HANDLER(test_db, key_i64_remove_scope); + WASM_TEST_HANDLER(test_db, key_i64_not_found); + WASM_TEST_HANDLER(test_db, key_i128i128_general); + + //unhandled test call + WASM_TEST_ERROR_CODE = WASM_TEST_FAIL; + } + +} diff --git a/contracts/test_api/test_api.hpp b/contracts/test_api/test_api.hpp new file mode 100644 index 00000000000..1bc5a3c5d9e --- /dev/null +++ b/contracts/test_api/test_api.hpp @@ -0,0 +1,96 @@ +#pragma once + +#define WASM_TEST_ERROR_CODE *((unsigned int *)((1<<16) - 2*sizeof(unsigned int))) +#define WASM_TEST_ERROR_MESSAGE *((unsigned int *)((1<<16) - 1*sizeof(unsigned int))) + +#define WASM_TEST_FAIL 0xDEADBEEF +#define WASM_TEST_PASS 0xB0CABACA + +#define WASM_ASSERT(m, message) if(!(m)) { WASM_TEST_ERROR_MESSAGE=(unsigned int)message; return WASM_TEST_FAIL; } + +typedef unsigned long long u64; +#define WASM_TEST_HANDLER(CLASS, METHOD) \ + if( u32(action>>32) == DJBH(#CLASS) && u32(action) == DJBH(#METHOD) ) { \ + WASM_TEST_ERROR_CODE = CLASS::METHOD(); \ + return; \ + } + +#define WASM_TEST_HANDLER_EX(CLASS, METHOD) \ + if( u32(action>>32) == DJBH(#CLASS) && u32(action) == DJBH(#METHOD) ) { \ + WASM_TEST_ERROR_CODE = CLASS::METHOD(code, action); \ + return; \ + } + +typedef unsigned int u32; +static constexpr u32 DJBH(const char* cp) +{ + u32 hash = 5381; + while (*cp) + hash = 33 * hash ^ (unsigned char) *cp++; + return hash; +} + +#pragma pack(push, 1) +struct dummy_message { + char a; //1 + unsigned long long b; //8 + int c; //4 +}; + +struct u128_msg { + unsigned __int128 values[3]; //16*3 +}; +#pragma pack(pop) + +static_assert( sizeof(dummy_message) == 13 , "unexpected packing" ); +static_assert( sizeof(u128_msg) == 16*3 , "unexpected packing" ); + +struct test_types { + static unsigned int types_size(); + static unsigned int char_to_symbol(); + static unsigned int string_to_name(); + static unsigned int name_class(); +}; + +struct test_print { + static unsigned int test_prints(); + static unsigned int test_printi(); + static unsigned int test_printi128(); + static unsigned int test_printn(); +}; + +#define DUMMY_MESSAGE_DEFAULT_A 0x45 +#define DUMMY_MESSAGE_DEFAULT_B 0xab11cd1244556677 +#define DUMMY_MESSAGE_DEFAULT_C 0x7451ae12 + +struct test_message { + + static unsigned int read_message(); + static unsigned int read_message_to_0(); + static unsigned int read_message_to_64k(); + static unsigned int require_notice(unsigned long long code, unsigned long long action); + static unsigned int require_auth(); + static unsigned int assert_false(); + static unsigned int assert_true(); + static unsigned int now(); + +}; + +struct test_math { + static unsigned int test_multeq_i128(); + static unsigned int test_diveq_i128(); + static unsigned int test_diveq_i128_by_0(); +}; + +struct test_db { + static unsigned int key_i64_general(); + static unsigned int key_i64_remove_all(); + static unsigned int key_i64_small_load(); + static unsigned int key_i64_small_store(); + static unsigned int key_i64_store_scope(); + static unsigned int key_i64_remove_scope(); + static unsigned int key_i64_not_found(); + + static unsigned int key_i128i128_general(); +}; + diff --git a/contracts/test_api/test_db.cpp b/contracts/test_api/test_db.cpp new file mode 100644 index 00000000000..d6223dbb1fb --- /dev/null +++ b/contracts/test_api/test_db.cpp @@ -0,0 +1,306 @@ +#include +#include +#include + +#include "test_api.hpp" + +#pragma pack(push, 1) +struct TestModel { + AccountName name; + unsigned char age; + uint64_t phone; +}; + +struct TestModelV2 : TestModel { + TestModelV2() : new_field(0) {} + uint64_t new_field; +}; + + struct OrderID { + AccountName name = 0; + uint64_t number = 0; + }; + +struct TestModel128x2 { + OrderID id; + uint128_t price; + uint64_t extra; +}; +#pragma pack(pop) + +extern "C" { + void my_memset(void *vptr, unsigned char val, unsigned int size) { + char *ptr = (char *)vptr; + while(size--) { *(ptr++)=val; } + } +} + +unsigned int test_db::key_i64_general() { + + uint32_t res = 0; + + TestModel alice{ N(alice), 20, 4234622}; + TestModel bob { N(bob), 15, 11932435}; + TestModel carol{ N(carol), 30, 545342453}; + TestModel dave { N(dave), 46, 6535354}; + + res = store_i64(currentCode(), N(test_table), &dave, sizeof(TestModel)); + WASM_ASSERT(res != 0, "store dave" ); + + res = store_i64(currentCode(), N(test_table), &carol, sizeof(TestModel)); + WASM_ASSERT(res != 0, "store carol" ); + + res = store_i64(currentCode(), N(test_table), &bob, sizeof(TestModel)); + WASM_ASSERT(res != 0, "store bob" ); + + res = store_i64(currentCode(), N(test_table), &alice, sizeof(TestModel)); + WASM_ASSERT(res != 0, "store alice" ); + + my_memset(&alice, 0, sizeof(TestModel)); + + WASM_ASSERT(alice.name == 0 && alice.age == 0 && alice.phone == 0, "my_memset"); + + alice.name = N(alice); + + res = load_i64(currentCode(), currentCode(), N(test_table), &alice, sizeof(TestModel)); + WASM_ASSERT(res == sizeof(TestModel) && alice.age == 20 && alice.phone == 4234622, "alice error 1"); + + alice.age = 21; + alice.phone = 1234; + + res = store_i64(currentCode(), N(test_table), &alice, sizeof(TestModel)); + WASM_ASSERT(res == 0, "store alice 2" ); + + my_memset(&alice, 0, sizeof(TestModel)); + alice.name = N(alice); + + res = load_i64(currentCode(), currentCode(), N(test_table), &alice, sizeof(TestModel)); + WASM_ASSERT(res == sizeof(TestModel) && alice.age == 21 && alice.phone == 1234, "alice error 2"); + + my_memset(&bob, 0, sizeof(TestModel)); + bob.name = N(bob); + + my_memset(&carol, 0, sizeof(TestModel)); + carol.name = N(carol); + + my_memset(&dave, 0, sizeof(TestModel)); + dave.name = N(dave); + + res = load_i64(currentCode(), currentCode(), N(test_table), &bob, sizeof(TestModel)); + WASM_ASSERT(res == sizeof(TestModel) && bob.age == 15 && bob.phone == 11932435, "bob error 1"); + + res = load_i64(currentCode(), currentCode(), N(test_table), &carol, sizeof(TestModel)); + WASM_ASSERT(res == sizeof(TestModel) && carol.age == 30 && carol.phone == 545342453, "carol error 1"); + + res = load_i64(currentCode(), currentCode(), N(test_table), &dave, sizeof(TestModel)); + WASM_ASSERT(res == sizeof(TestModel) && dave.age == 46 && dave.phone == 6535354, "dave error 1"); + + res = load_i64(currentCode(), N(other_code), N(test_table), &alice, sizeof(TestModel)); + WASM_ASSERT(res == -1, "other_code"); + + res = load_i64(currentCode(), currentCode(), N(other_table), &alice, sizeof(TestModel)); + WASM_ASSERT(res == -1, "other_table"); + + + TestModelV2 alicev2; + alicev2.name = N(alice); + + res = load_i64(currentCode(), currentCode(), N(test_table), &alicev2, sizeof(TestModelV2)); + WASM_ASSERT(res == sizeof(TestModel) && alicev2.age == 21 && alicev2.phone == 1234, "alicev2 load"); + + alicev2.new_field = 66655444; + res = store_i64(currentCode(), N(test_table), &alicev2, sizeof(TestModelV2)); + WASM_ASSERT(res == 0, "store alice 3" ); + + my_memset(&alicev2, 0, sizeof(TestModelV2)); + alicev2.name = N(alice); + + res = load_i64(currentCode(), currentCode(), N(test_table), &alicev2, sizeof(TestModelV2)); + WASM_ASSERT(res == sizeof(TestModelV2) && alicev2.age == 21 && alicev2.phone == 1234 && alicev2.new_field == 66655444, "alice model v2"); + + return WASM_TEST_PASS; +} + +unsigned int test_db::key_i64_remove_all() { + + uint32_t res = 0; + + res = remove_i64(currentCode(), N(test_table), N(alice)); + WASM_ASSERT(res == 1, "remove alice"); + res = remove_i64(currentCode(), N(test_table), N(bob)); + WASM_ASSERT(res == 1, "remove bob"); + res = remove_i64(currentCode(), N(test_table), N(carol)); + WASM_ASSERT(res == 1, "remove carol"); + res = remove_i64(currentCode(), N(test_table), N(dave)); + WASM_ASSERT(res == 1, "remove dave"); + + + res = remove_i64(currentCode(), N(test_table), N(alice)); + WASM_ASSERT(res == 0, "remove alice 2"); + res = remove_i64(currentCode(), N(test_table), N(bob)); + WASM_ASSERT(res == 0, "remove bob 2"); + res = remove_i64(currentCode(), N(test_table), N(carol)); + WASM_ASSERT(res == 0, "remove carol 2"); + res = remove_i64(currentCode(), N(test_table), N(dave)); + WASM_ASSERT(res == 0, "remove dave 2"); + + return WASM_TEST_PASS; +} + +unsigned int test_db::key_i64_small_load() { + uint64_t dummy = 0; + load_i64(currentCode(), currentCode(), N(just_uint64), &dummy, sizeof(uint64_t)-1); + return WASM_TEST_PASS; +} + +unsigned int test_db::key_i64_small_store() { + uint64_t dummy = 0; + store_i64(currentCode(), N(just_uint64), &dummy, sizeof(uint64_t)-1); + return WASM_TEST_PASS; +} + +unsigned int test_db::key_i64_store_scope() { + uint64_t dummy = 0; + store_i64(currentCode(), N(just_uint64), &dummy, sizeof(uint64_t)); + return WASM_TEST_PASS; +} + +unsigned int test_db::key_i64_remove_scope() { + uint64_t dummy = 0; + store_i64(currentCode(), N(just_uint64), &dummy, sizeof(uint64_t)); + return WASM_TEST_PASS; +} + +unsigned int test_db::key_i64_not_found() { + uint64_t dummy = 1000; + + auto res = load_i64(currentCode(), currentCode(), N(just_uint64), &dummy, sizeof(uint64_t)); + WASM_ASSERT(res == -1, "i64_not_found load"); + + res = remove_i64(currentCode(), N(just_uint64), dummy); + WASM_ASSERT(res == 0, "i64_not_found remove"); + return WASM_TEST_PASS; +} + +// extern "C" { +// unsigned int store_set_in_table(uint64_t table_name); +// }; + +unsigned int store_set_in_table(uint64_t table_name) +{ + + uint32_t res = 0; + + TestModel128x2 alice0{{N(alice),0}, 500, N(alice0)}; + TestModel128x2 alice1{{N(alice),1}, 400, N(alice1)}; + TestModel128x2 alice2{{N(alice),2}, 300, N(alice2)}; + + res = store_i128i128(currentCode(), table_name, &alice0, sizeof(TestModel128x2)); + WASM_ASSERT(res > 0, "store alice0" ); + + res = store_i128i128(currentCode(), table_name, &alice1, sizeof(TestModel128x2)); + WASM_ASSERT(res > 0, "store alice1" ); + + res = store_i128i128(currentCode(), table_name, &alice2, sizeof(TestModel128x2)); + WASM_ASSERT(res > 0, "store alice2" ); + + TestModel128x2 bob0{{N(bob), 0}, 1, N(bob0)}; + TestModel128x2 bob1{{N(bob), 1}, 2, N(bob1)}; + TestModel128x2 bob2{{N(bob), 2}, 3, N(bob2)}; + TestModel128x2 bob3{{N(bob), 3}, 4, N(bob3)}; + + res = store_i128i128(currentCode(), table_name, &bob0, sizeof(TestModel128x2)); + WASM_ASSERT(res > 0, "store bob0" ); + + res = store_i128i128(currentCode(), table_name, &bob1, sizeof(TestModel128x2)); + WASM_ASSERT(res > 0, "store bob1" ); + + res = store_i128i128(currentCode(), table_name, &bob2, sizeof(TestModel128x2)); + WASM_ASSERT(res > 0, "store bob2" ); + + res = store_i128i128(currentCode(), table_name, &bob3, sizeof(TestModel128x2)); + WASM_ASSERT(res > 0, "store bob3" ); + + TestModel128x2 carol0{{N(carol), 0}, 900, N(carol0)}; + TestModel128x2 carol1{{N(carol), 1}, 800, N(carol1)}; + TestModel128x2 carol2{{N(carol), 2}, 700, N(carol2)}; + TestModel128x2 carol3{{N(carol), 3}, 600, N(carol3)}; + + res = store_i128i128(currentCode(), table_name, &carol0, sizeof(TestModel128x2)); + WASM_ASSERT(res > 0, "store carol0" ); + + res = store_i128i128(currentCode(), table_name, &carol1, sizeof(TestModel128x2)); + eos::print("Store carol1 ", res, "\n"); + WASM_ASSERT(res > 0, "store carol1" ); + eos::print("Store carol1 ", res, " ", carol1.id.name, " ", carol1.id.number, " ", carol1.price, " ", carol1.extra, "\n"); + + + res = store_i128i128(currentCode(), table_name, &carol2, sizeof(TestModel128x2)); + WASM_ASSERT(res > 0, "store carol2" ); + + res = store_i128i128(currentCode(), table_name, &carol3, sizeof(TestModel128x2)); + WASM_ASSERT(res > 0, "store carol3" ); + + TestModel128x2 dave0{{N(dave) ,0}, 8, N(dave0)}; + TestModel128x2 dave1{{N(dave) ,1}, 7, N(dave1)}; + TestModel128x2 dave2{{N(dave) ,2}, 5, N(dave2)}; + TestModel128x2 dave3{{N(dave) ,3}, 4, N(dave3)}; + + res = store_i128i128(currentCode(), table_name, &dave0, sizeof(TestModel128x2)); + WASM_ASSERT(res > 0, "store dave0" ); + + res = store_i128i128(currentCode(), table_name, &dave1, sizeof(TestModel128x2)); + WASM_ASSERT(res > 0, "store dave1" ); + + res = store_i128i128(currentCode(), table_name, &dave2, sizeof(TestModel128x2)); + WASM_ASSERT(res > 0, "store dave2" ); + + res = store_i128i128(currentCode(), table_name, &dave3, sizeof(TestModel128x2)); + WASM_ASSERT(res > 0, "store dave3" ); + + return WASM_TEST_PASS; +} + +unsigned int test_db::key_i128i128_general() { + + uint32_t res = 0; + + if(store_set_in_table(N(table1)) != WASM_TEST_PASS) + return WASM_TEST_FAIL; + + // if(store_set_in_table(N(table2)) != WASM_TEST_PASS) + // return WASM_TEST_FAIL; + + // if(store_set_in_table(N(table3)) != WASM_TEST_PASS) + // return WASM_TEST_FAIL; + + TestModel128x2 tmp; + my_memset(&tmp, 0, sizeof(TestModel128x2)); + tmp.id.name = N(carol); + tmp.id.number = 1; + tmp.price = 800; + + res = load_primary_i128i128( currentCode(), currentCode(), N(table1), &tmp, sizeof(TestModel128x2) ); + + WASM_ASSERT( res == sizeof(TestModel128x2) && + tmp.id.name == N(carol) && + tmp.id.number == 1 && + tmp.price == 800 && + tmp.extra == N(carol1), + "carol1 primary load"); + + my_memset(&tmp, 0, sizeof(TestModel128x2)); + tmp.price = 4; //TestModel128x2 dave3{{N(dave) ,3}, 4, N(dave3)}; + //TestModel128x2 bob3{{N(bob), 3}, 4, N(bob3)}; + + res = load_secondary_i128i128( currentCode(), currentCode(), N(table1), &tmp, sizeof(TestModel128x2) ); + //eos::print("SECOND -> ", res, " ", tmp.id.name, " ", tmp.id.number, " ", tmp.price, " ", tmp.extra, "\n"); + WASM_ASSERT( res == sizeof(TestModel128x2) && + tmp.id.name == N(bob) && + tmp.id.number == 3 && + tmp.price == 4 && + tmp.extra == N(bob3), + "bob3 secondary load"); + + return WASM_TEST_PASS; +} diff --git a/contracts/test_api/test_math.cpp b/contracts/test_api/test_math.cpp new file mode 100644 index 00000000000..b51900d383a --- /dev/null +++ b/contracts/test_api/test_math.cpp @@ -0,0 +1,30 @@ +#include +#include +#include + +#include "test_api.hpp" + +unsigned int test_math::test_multeq_i128() { + u128_msg msg; + auto n = readMessage(&msg, sizeof(u128_msg)); + WASM_ASSERT( n == sizeof(u128_msg), "test_multeq_i128 n == sizeof(u128_msg)" ); + multeq_i128(msg.values, msg.values+1); + WASM_ASSERT( msg.values[0] == msg.values[2], "test_multeq_i128 msg.values[0] == msg.values[2]" ); + return WASM_TEST_PASS; +} + +unsigned int test_math::test_diveq_i128() { + u128_msg msg; + auto n = readMessage(&msg, sizeof(u128_msg)); + WASM_ASSERT( n == sizeof(u128_msg), "test_diveq_i128 n == sizeof(u128_msg)" ); + diveq_i128(msg.values, msg.values+1); + WASM_ASSERT( msg.values[0] == msg.values[2], "test_diveq_i128 msg.values[0] == msg.values[2]" ); + return WASM_TEST_PASS; +} + +unsigned int test_math::test_diveq_i128_by_0() { + unsigned __int128 a = 100; + unsigned __int128 b = 0; + diveq_i128(&a, &b); + return WASM_TEST_PASS; +} diff --git a/contracts/test_api/test_message.cpp b/contracts/test_api/test_message.cpp new file mode 100644 index 00000000000..a680d65b51f --- /dev/null +++ b/contracts/test_api/test_message.cpp @@ -0,0 +1,79 @@ +#include + +#include "test_api.hpp" + +unsigned int test_message::read_message() { + + char buffer[100]; + uint32_t total = 0; + + WASM_ASSERT( currentCode() == N(test_api), "currentCode() == N(test_api)" ); + + WASM_ASSERT(messageSize() == sizeof(dummy_message), "messageSize() == sizeof(dummy_message)"); + + total = readMessage(buffer, 30); + WASM_ASSERT(total == sizeof(dummy_message) , "readMessage(30)" ); + + total = readMessage(buffer, 100); + WASM_ASSERT(total == sizeof(dummy_message) , "readMessage(100)" ); + + total = readMessage(buffer, 5); + WASM_ASSERT(total == 5 , "readMessage(5)" ); + + total = readMessage(buffer, sizeof(dummy_message) ); + WASM_ASSERT(total == sizeof(dummy_message), "readMessage(sizeof(dummy_message))" ); + + dummy_message *dummy13 = reinterpret_cast(buffer); + WASM_ASSERT(dummy13->a == DUMMY_MESSAGE_DEFAULT_A, "dummy13->a == DUMMY_MESSAGE_DEFAULT_A"); + WASM_ASSERT(dummy13->b == DUMMY_MESSAGE_DEFAULT_B, "dummy13->b == DUMMY_MESSAGE_DEFAULT_B"); + WASM_ASSERT(dummy13->c == DUMMY_MESSAGE_DEFAULT_C, "dummy13->c == DUMMY_MESSAGE_DEFAULT_C"); + + return WASM_TEST_PASS; +} + +unsigned int test_message::read_message_to_0() { + uint32_t total = readMessage((void *)0, 0x7FFFFFFF); + return WASM_TEST_PASS; +} + +unsigned int test_message::read_message_to_64k() { + uint32_t total = readMessage( (void *)((1<<16)-1), 0x7FFFFFFF); + return WASM_TEST_PASS; +} + +unsigned int test_message::require_notice(unsigned long long code, unsigned long long action) { + if( code == N(test_api) ) { + eos::requireNotice( N(acc1) ); + eos::requireNotice( N(acc2) ); + eos::requireNotice( N(acc1), N(acc2) ); + return WASM_TEST_FAIL; + } else if ( code == N(acc1) || code == N(acc2) ) { + return WASM_TEST_PASS; + } + return WASM_TEST_FAIL; +} + +unsigned int test_message::require_auth() { + eos::requireAuth( N(acc3) ); + eos::requireAuth( N(acc4) ); + return WASM_TEST_PASS; +} + +unsigned int test_message::assert_false() { + assert(false, "test_message::assert_false"); + return WASM_TEST_PASS; +} + +unsigned int test_message::assert_true() { + assert(true, "test_message::assert_true"); + return WASM_TEST_PASS; +} + +unsigned int test_message::now() { + uint32_t tmp = 0; + uint32_t total = readMessage(&tmp, sizeof(uint32_t)); + WASM_ASSERT( total == sizeof(uint32_t), "total == sizeof(uint32_t)"); + WASM_ASSERT( tmp == ::now(), "tmp == now()" ); + return WASM_TEST_PASS; +} + diff --git a/contracts/test_api/test_print.cpp b/contracts/test_api/test_print.cpp new file mode 100644 index 00000000000..04dc9bf59da --- /dev/null +++ b/contracts/test_api/test_print.cpp @@ -0,0 +1,44 @@ +#include + +#include "test_api.hpp" + +unsigned int test_print::test_prints() { + prints("ab"); + prints(nullptr); + prints("c\0test_prints"); + prints(0); + prints("efg"); + prints(0); + return WASM_TEST_PASS; +} + +unsigned int test_print::test_printi() { + printi(0); + printi(556644); + printi(-1); + return WASM_TEST_PASS; +} + +unsigned int test_print::test_printi128() { + uint128_t a(-1); + uint128_t b(0); + uint128_t c(87654323456); + printi128(&a); + printi128(&b); + printi128(&c); + return WASM_TEST_PASS; +} + +unsigned int test_print::test_printn() { + printn(N(abcde)); + printn(N(abBde)); + printn(N(1q1q1qAA)); + printn(N()); + printn(N(AAAAAA)); + printn(N(abcdefghijk)); + printn(N(abcdefghijkl)); + printn(N(abcdefghijklm)); + printn(N(abcdefghijklmn)); + printn(N(abcdefghijklmno)); + return WASM_TEST_PASS; +} diff --git a/contracts/test_api/test_types.cpp b/contracts/test_api/test_types.cpp new file mode 100644 index 00000000000..cb48e8f2616 --- /dev/null +++ b/contracts/test_api/test_types.cpp @@ -0,0 +1,103 @@ +#include + +#include "test_api.hpp" + +unsigned int test_types::types_size() { + + WASM_ASSERT( sizeof(int64_t) == 8, "int64_t size != 8"); + WASM_ASSERT( sizeof(uint64_t) == 8, "uint64_t size != 8"); + WASM_ASSERT( sizeof(uint32_t) == 4, "uint32_t size != 4"); + WASM_ASSERT( sizeof(int32_t) == 4, "int32_t size != 4"); + WASM_ASSERT( sizeof(uint128_t) == 16, "uint128_t size != 16"); + WASM_ASSERT( sizeof(int128_t) == 16, "int128_t size != 16"); + WASM_ASSERT( sizeof(uint8_t) == 1, "uint8_t size != 1"); + + WASM_ASSERT( sizeof(AccountName) == 8, "AccountName size != 8"); + WASM_ASSERT( sizeof(TokenName) == 8, "TokenName size != 8"); + WASM_ASSERT( sizeof(TableName) == 8, "TableName size != 8"); + WASM_ASSERT( sizeof(Time) == 4, "Time size != 4"); + + return WASM_TEST_PASS; +} + +unsigned int test_types::char_to_symbol() { + + WASM_ASSERT( eos::char_to_symbol('a') == 1, "eos::char_to_symbol('a') != 1"); + WASM_ASSERT( eos::char_to_symbol('b') == 2, "eos::char_to_symbol('b') != 2"); + WASM_ASSERT( eos::char_to_symbol('c') == 3, "eos::char_to_symbol('c') != 3"); + WASM_ASSERT( eos::char_to_symbol('d') == 4, "eos::char_to_symbol('d') != 4"); + WASM_ASSERT( eos::char_to_symbol('e') == 5, "eos::char_to_symbol('e') != 5"); + WASM_ASSERT( eos::char_to_symbol('f') == 6, "eos::char_to_symbol('f') != 6"); + WASM_ASSERT( eos::char_to_symbol('g') == 7, "eos::char_to_symbol('g') != 7"); + WASM_ASSERT( eos::char_to_symbol('h') == 8, "eos::char_to_symbol('h') != 8"); + WASM_ASSERT( eos::char_to_symbol('i') == 9, "eos::char_to_symbol('i') != 9"); + WASM_ASSERT( eos::char_to_symbol('j') == 10, "eos::char_to_symbol('j') != 10"); + WASM_ASSERT( eos::char_to_symbol('k') == 11, "eos::char_to_symbol('k') != 11"); + WASM_ASSERT( eos::char_to_symbol('l') == 12, "eos::char_to_symbol('l') != 12"); + WASM_ASSERT( eos::char_to_symbol('m') == 13, "eos::char_to_symbol('m') != 13"); + WASM_ASSERT( eos::char_to_symbol('n') == 14, "eos::char_to_symbol('n') != 14"); + WASM_ASSERT( eos::char_to_symbol('o') == 15, "eos::char_to_symbol('o') != 15"); + WASM_ASSERT( eos::char_to_symbol('p') == 16, "eos::char_to_symbol('p') != 16"); + WASM_ASSERT( eos::char_to_symbol('q') == 17, "eos::char_to_symbol('q') != 17"); + WASM_ASSERT( eos::char_to_symbol('r') == 18, "eos::char_to_symbol('r') != 18"); + WASM_ASSERT( eos::char_to_symbol('s') == 19, "eos::char_to_symbol('s') != 19"); + WASM_ASSERT( eos::char_to_symbol('t') == 20, "eos::char_to_symbol('t') != 20"); + WASM_ASSERT( eos::char_to_symbol('u') == 21, "eos::char_to_symbol('u') != 21"); + WASM_ASSERT( eos::char_to_symbol('v') == 22, "eos::char_to_symbol('v') != 22"); + WASM_ASSERT( eos::char_to_symbol('w') == 23, "eos::char_to_symbol('w') != 23"); + WASM_ASSERT( eos::char_to_symbol('x') == 24, "eos::char_to_symbol('x') != 24"); + WASM_ASSERT( eos::char_to_symbol('y') == 25, "eos::char_to_symbol('y') != 25"); + WASM_ASSERT( eos::char_to_symbol('z') == 26, "eos::char_to_symbol('z') != 26"); + WASM_ASSERT( eos::char_to_symbol('1') == 27, "eos::char_to_symbol('1') != 27"); + WASM_ASSERT( eos::char_to_symbol('2') == 28, "eos::char_to_symbol('2') != 28"); + WASM_ASSERT( eos::char_to_symbol('3') == 29, "eos::char_to_symbol('3') != 29"); + WASM_ASSERT( eos::char_to_symbol('4') == 30, "eos::char_to_symbol('4') != 30"); + WASM_ASSERT( eos::char_to_symbol('5') == 31, "eos::char_to_symbol('5') != 31"); + + for(unsigned char i = 0; i<255; i++) { + if((i >= 'a' && i <= 'z') || (i >= '1' || i <= '5')) continue; + WASM_ASSERT( eos::char_to_symbol(i) == 0, "eos::char_to_symbol() != 0"); + } + + return WASM_TEST_PASS; +} + +unsigned int test_types::string_to_name() { + + WASM_ASSERT( eos::string_to_name("a") == N(a) , "eos::string_to_name(a)" ); + WASM_ASSERT( eos::string_to_name("ba") == N(ba) , "eos::string_to_name(ba)" ); + WASM_ASSERT( eos::string_to_name("cba") == N(cba) , "eos::string_to_name(cba)" ); + WASM_ASSERT( eos::string_to_name("dcba") == N(dcba) , "eos::string_to_name(dcba)" ); + WASM_ASSERT( eos::string_to_name("edcba") == N(edcba) , "eos::string_to_name(edcba)" ); + WASM_ASSERT( eos::string_to_name("fedcba") == N(fedcba) , "eos::string_to_name(fedcba)" ); + WASM_ASSERT( eos::string_to_name("gfedcba") == N(gfedcba) , "eos::string_to_name(gfedcba)" ); + WASM_ASSERT( eos::string_to_name("hgfedcba") == N(hgfedcba) , "eos::string_to_name(hgfedcba)" ); + WASM_ASSERT( eos::string_to_name("ihgfedcba") == N(ihgfedcba) , "eos::string_to_name(ihgfedcba)" ); + WASM_ASSERT( eos::string_to_name("jihgfedcba") == N(jihgfedcba) , "eos::string_to_name(jihgfedcba)" ); + WASM_ASSERT( eos::string_to_name("kjihgfedcba") == N(kjihgfedcba) , "eos::string_to_name(kjihgfedcba)" ); + WASM_ASSERT( eos::string_to_name("lkjihgfedcba") == N(lkjihgfedcba) , "eos::string_to_name(lkjihgfedcba)" ); + WASM_ASSERT( eos::string_to_name("mlkjihgfedcba") == N(mlkjihgfedcba) , "eos::string_to_name(mlkjihgfedcba)" ); + WASM_ASSERT( eos::string_to_name("mlkjihgfedcba1") == N(mlkjihgfedcba2) , "eos::string_to_name(mlkjihgfedcba2)" ); + WASM_ASSERT( eos::string_to_name("mlkjihgfedcba55") == N(mlkjihgfedcba14) , "eos::string_to_name(mlkjihgfedcba14)" ); + + WASM_ASSERT( eos::string_to_name("azAA34") == N(azBB34) , "eos::string_to_name N(azBB34)" ); + WASM_ASSERT( eos::string_to_name("AZaz12Bc34") == N(AZaz12Bc34) , "eos::string_to_name AZaz12Bc34" ); + WASM_ASSERT( eos::string_to_name("AAAAAAAAAAAAAAA") == eos::string_to_name("BBBBBBBBBBBBBDDDDDFFFGG") , "eos::string_to_name BBBBBBBBBBBBBDDDDDFFFGG" ); + + return WASM_TEST_PASS; +} + +unsigned int test_types::name_class() { + + WASM_ASSERT ( eos::Name(eos::string_to_name("azAA34")).value == N(azAA34), "eos::Name != N(azAA34)" ); + WASM_ASSERT ( eos::Name(eos::string_to_name("AABBCC")).value == 0, "eos::Name != N(0)" ); + WASM_ASSERT ( eos::Name(eos::string_to_name("AA11")).value == N(AA11), "eos::Name != N(AA11)" ); + WASM_ASSERT ( eos::Name(eos::string_to_name("11AA")).value == N(11), "eos::Name != N(11)" ); + WASM_ASSERT ( eos::Name(eos::string_to_name("22BBCCXXAA")).value == N(22), "eos::Name != N(22)" ); + WASM_ASSERT ( eos::Name(eos::string_to_name("AAAbbcccdd")) == eos::Name(eos::string_to_name("AAAbbcccdd")), "eos::Name == eos::Name" ); + + uint64_t tmp = eos::Name(eos::string_to_name("11bbcccdd")); + WASM_ASSERT(N(11bbcccdd) == tmp, "N(11bbcccdd) == tmp"); + + return WASM_TEST_PASS; +} \ No newline at end of file diff --git a/libraries/chain/include/eos/chain/message.hpp b/libraries/chain/include/eos/chain/message.hpp index 0df9382718f..050b4978484 100644 --- a/libraries/chain/include/eos/chain/message.hpp +++ b/libraries/chain/include/eos/chain/message.hpp @@ -26,6 +26,10 @@ struct Message : public types::Message { :types::Message(code, type, authorization, Bytes()) { set(type, std::forward(value)); } + + Message(const AccountName& code, const vector& authorization, const types::FuncName& type) + :types::Message(code, type, authorization, Bytes()) {} + Message(const types::Message& m) : types::Message(m) {} template diff --git a/libraries/chain/include/eos/chain/message_handling_contexts.hpp b/libraries/chain/include/eos/chain/message_handling_contexts.hpp index 1cd51206ddc..74c59ff3fa4 100644 --- a/libraries/chain/include/eos/chain/message_handling_contexts.hpp +++ b/libraries/chain/include/eos/chain/message_handling_contexts.hpp @@ -52,6 +52,8 @@ class message_validate_context { uint128_t* primary, uint128_t* secondary, char* data, uint32_t maxlen ); int32_t load_primary_i128i128( Name scope, Name code, Name table, uint128_t* primary, uint128_t* secondary, char* data, uint32_t maxlen ); + int32_t load_secondary_i128i128( Name scope, Name code, Name table, + uint128_t* primary, uint128_t* secondary, char* data, uint32_t maxlen ); int32_t lowerbound_primary_i128i128( Name scope, Name code, Name table, uint128_t* primary, uint128_t* secondary, char* data, uint32_t maxlen ); int32_t lowerbound_secondary_i128i128( Name scope, Name code, Name table, diff --git a/libraries/chain/include/eos/chain/wasm_interface.hpp b/libraries/chain/include/eos/chain/wasm_interface.hpp index a5703baf2f3..1b80753f0fd 100644 --- a/libraries/chain/include/eos/chain/wasm_interface.hpp +++ b/libraries/chain/include/eos/chain/wasm_interface.hpp @@ -8,6 +8,7 @@ namespace eos { namespace chain { class chain_controller; +typedef int32_t (message_validate_context::*load_i128i128_fnc)(Name, Name, Name, uint128_t* , uint128_t*, char* , uint32_t); /** * @class wasm_interface @@ -62,6 +63,8 @@ class wasm_interface { fc::time_point checktimeStart; wasm_interface(); + + int32_t load_i128i128_object( uint64_t scope, uint64_t code, uint64_t table, int32_t valueptr, int32_t valuelen, load_i128i128_fnc function ); }; diff --git a/libraries/chain/message_handling_contexts.cpp b/libraries/chain/message_handling_contexts.cpp index d3a451ebf44..b0b70f4623d 100644 --- a/libraries/chain/message_handling_contexts.cpp +++ b/libraries/chain/message_handling_contexts.cpp @@ -229,6 +229,32 @@ int32_t message_validate_context::load_primary_i128i128( Name scope, Name code, } return copylen; } + +int32_t message_validate_context::load_secondary_i128i128( Name scope, Name code, Name table, + uint128_t* primary, uint128_t* secondary, char* value, uint32_t valuelen ) { + + require_scope( scope ); + const auto& idx = db.get_index(); + auto itr = idx.lower_bound( boost::make_tuple( uint64_t(scope), + uint64_t(code), + uint64_t(table), + *secondary, uint128_t(0) ) ); + + if( itr == idx.end() || + itr->scope != (scope) || + itr->code != (code) || + itr->table != (table) || + itr->secondary_key != *secondary ) return -1; + + *primary = itr->primary_key; + + auto copylen = std::min(itr->value.size(),valuelen); + if( copylen ) { + itr->value.copy(value, copylen); + } + return copylen; +} + int32_t message_validate_context::lowerbound_primary_i128i128( Name scope, Name code, Name table, uint128_t* primary, uint128_t* secondary, char* value, uint32_t valuelen ) { @@ -346,11 +372,9 @@ int32_t apply_context::store_i128i128( Name scope, Name table, uint128_t primary if( obj ) { wlog( "modify" ); mutable_db.modify( *obj, [&]( auto& o ) { - o.primary_key = primary; - o.secondary_key = secondary; o.value.assign(value, valuelen); }); - return valuelen; + return 0; } else { wlog( "new" ); mutable_db.create( [&](auto& o) { diff --git a/libraries/chain/wasm_interface.cpp b/libraries/chain/wasm_interface.cpp index eb301ca69d2..9b86b11c78c 100644 --- a/libraries/chain/wasm_interface.cpp +++ b/libraries/chain/wasm_interface.cpp @@ -1,3 +1,4 @@ +#include #include #include #include "Platform/Platform.h" @@ -33,6 +34,31 @@ DEFINE_INTRINSIC_FUNCTION0(env,checktime,checktime,none) { } } + int32_t load_i128i128_object( uint64_t scope, uint64_t code, uint64_t table, int32_t valueptr, int32_t valuelen, load_i128i128_fnc function ) { + + static const uint32_t keylen = 2*sizeof(uint128_t); + + FC_ASSERT( valuelen >= keylen, "insufficient data passed" ); + + auto& wasm = wasm_interface::get(); + FC_ASSERT( wasm.current_validate_context, "no validate context found" ); + + char* value = memoryArrayPtr( wasm.current_memory, valueptr, valuelen ); + uint128_t* primary = reinterpret_cast(value); + uint128_t* secondary = primary + 1; + + valuelen -= keylen; + value += keylen; + + auto res = (wasm.current_validate_context->*function)( + Name(scope), Name(code), Name(table), + primary, secondary, value, valuelen + ); + + if(res > 0) res += keylen; + return res; + } + DEFINE_INTRINSIC_FUNCTION2(env,multeq_i128,multeq_i128,none,i32,self,i32,other) { auto& wasm = wasm_interface::get(); auto mem = wasm.current_memory; @@ -55,25 +81,25 @@ DEFINE_INTRINSIC_FUNCTION0(env,now,now,i32) { return wasm_interface::get().current_validate_context->controller.head_block_time().sec_since_epoch(); } -DEFINE_INTRINSIC_FUNCTION4(env,store_i128i128,store_i128i128,i32,i64,scope,i64,table,i32,data,i32,datalen) { +DEFINE_INTRINSIC_FUNCTION4(env,store_i128i128,store_i128i128,i32,i64,scope,i64,table,i32,valueptr,i32,valuelen) { + static const uint32_t keylen = 2*sizeof(uint128_t); - FC_ASSERT( datalen >= keylen, "insufficient data passed" ); + + FC_ASSERT( valuelen >= keylen, "insufficient data passed" ); auto& wasm = wasm_interface::get(); - FC_ASSERT( wasm.current_apply_context, "not a valid apply context" ); - - char* v = memoryArrayPtr( wasm.current_memory, data, datalen ); + FC_ASSERT( wasm.current_apply_context, "no apply context found" ); - uint128_t* primary = ((uint128_t*)v); + char* value = memoryArrayPtr( wasm.current_memory, valueptr, valuelen ); + uint128_t* primary = reinterpret_cast(value); uint128_t* secondary = primary + 1; + + valuelen -= keylen; + value += keylen; - char* value = (char*)(secondary+1); - uint32_t valuelen = datalen - keylen; - auto result = wasm_interface::get().current_apply_context->store_i128i128( - Name(scope), Name(table), - *primary, *secondary, value, valuelen ) + keylen; - wdump((datalen)(valuelen)(result)); - return result; + //wdump((datalen)(valuelen)(result)); + return wasm.current_apply_context->store_i128i128( Name(scope), Name(table), + *primary, *secondary, value, valuelen ); } struct i128_keys { @@ -89,66 +115,28 @@ DEFINE_INTRINSIC_FUNCTION3(env,remove_i128i128,remove_i128i128,i32,i64,scope,i64 return wasm_interface::get().current_apply_context->remove_i128i128( Name(scope), Name(table), keys.primary, keys.secondary ); } -DEFINE_INTRINSIC_FUNCTION5(env,load_primary_i128i128,load_primary_i128i128,i32,i64,scope,i64,code,i64,table,i32,data,i32,datalen) { - auto& wasm = wasm_interface::get(); - char* v = &memoryRef( wasm.current_memory, data ); - return wasm_interface::get().current_validate_context->load_primary_i128i128( Name(scope), Name(code), Name(table), - (uint128_t*)v, (uint128_t*)(v+sizeof(uint128_t)), v, datalen-(2*sizeof(uint128_t)) ); +DEFINE_INTRINSIC_FUNCTION5(env,load_primary_i128i128,load_primary_i128i128,i32,i64,scope,i64,code,i64,table,i32,valueptr,i32,valuelen) { + return load_i128i128_object(scope, code, table, valueptr, valuelen, &message_validate_context::load_primary_i128i128); } - -DEFINE_INTRINSIC_FUNCTION5(env,load_secondary_i128i128,load_secondary_i128i128,i32,i64,scope,i64,code,i64,table,i32,data,i32,datalen) { - FC_ASSERT( !"load_secondary_i128i128 not implemented" ); - return 0; +DEFINE_INTRINSIC_FUNCTION5(env,load_secondary_i128i128,load_secondary_i128i128,i32,i64,scope,i64,code,i64,table,i32,valueptr,i32,valuelen) { + return load_i128i128_object(scope, code, table, valueptr, valuelen, &message_validate_context::load_secondary_i128i128); } -DEFINE_INTRINSIC_FUNCTION5(env,back_primary_i128i128,back_primary_i128i128,i32,i64,scope,i64,code,i64,table,i32,data,i32,datalen) { - wlog( "back primary" ); - auto& wasm = wasm_interface::get(); - char* v = &memoryRef( wasm.current_memory, data ); - return wasm_interface::get().current_validate_context->back_primary_i128i128( Name(scope), Name(code), Name(table), - (uint128_t*)v, (uint128_t*)(v+sizeof(uint128_t)), v, datalen-(2*sizeof(uint128_t)) ); -} -DEFINE_INTRINSIC_FUNCTION5(env,front_primary_i128i128,front_primary,i32,i64,scope,i64,code,i64,table,i32,data,i32,datalen) { - wlog( "front primary" ); - auto& wasm = wasm_interface::get(); - char* v = &memoryRef( wasm.current_memory, data ); - return wasm_interface::get().current_validate_context->front_primary_i128i128( Name(scope), Name(code), Name(table), - (uint128_t*)v, (uint128_t*)(v+sizeof(uint128_t)), - v, datalen-(2*sizeof(uint128_t)) ) + 2*sizeof(uint128_t); +DEFINE_INTRINSIC_FUNCTION5(env,back_primary_i128i128,back_primary_i128i128,i32,i64,scope,i64,code,i64,table,i32,valueptr,i32,valuelen) { + return load_i128i128_object(scope, code, table, valueptr, valuelen, &message_validate_context::back_primary_i128i128); } -DEFINE_INTRINSIC_FUNCTION5(env,back_secondary_i128i128,back_secondary_i128i128,i32,i64,scope,i64,code,i64,table,i32,data,i32,datalen) { - wlog( "back secondary" ); - auto& wasm = wasm_interface::get(); - char* v = memoryArrayPtr( wasm.current_memory, data, datalen ); - - - - return wasm_interface::get().current_validate_context->back_secondary_i128i128( Name(scope), Name(code), Name(table), - (uint128_t*)v, (uint128_t*)(v+sizeof(uint128_t)), v, datalen-(2*sizeof(uint128_t)) ); +DEFINE_INTRINSIC_FUNCTION5(env,front_primary_i128i128,front_primary_i128i128,i32,i64,scope,i64,code,i64,table,i32,valueptr,i32,valuelen) { + return load_i128i128_object(scope, code, table, valueptr, valuelen, &message_validate_context::front_primary_i128i128); } -DEFINE_INTRINSIC_FUNCTION5(env,front_secondary_i128i128,front_secondary_i128i128,i32,i64,scope,i64,code,i64,table,i32,data,i32,datalen) { - wlog( "front secondary" ); - FC_ASSERT( datalen >= 2*sizeof(uint128_t), "insufficient data passed" ); - auto& wasm = wasm_interface::get(); - char* v = memoryArrayPtr( wasm.current_memory, data, datalen ); - - uint128_t* primary = (uint128_t*)v; - uint128_t* secondary = primary+1; - const uint32_t keylen = 2*sizeof(uint128_t); - - char* value = v + keylen; - uint64_t valuelen = datalen - keylen; +DEFINE_INTRINSIC_FUNCTION5(env,back_secondary_i128i128,back_secondary_i128i128,i32,i64,scope,i64,code,i64,table,i32,valueptr,i32,valuelen) { + return load_i128i128_object(scope, code, table, valueptr, valuelen, &message_validate_context::back_secondary_i128i128); +} - auto result = wasm_interface::get().current_validate_context->front_secondary_i128i128( Name(scope), Name(code), Name(table), - primary, secondary, value, valuelen); - if( result >= 0) { - result += keylen; - } - wdump((result)(datalen)); - return result; +DEFINE_INTRINSIC_FUNCTION5(env,front_secondary_i128i128,front_secondary_i128i128,i32,i64,scope,i64,code,i64,table,i32,valueptr,i32,valuelen) { + return load_i128i128_object(scope, code, table, valueptr, valuelen, &message_validate_context::front_secondary_i128i128); } DEFINE_INTRINSIC_FUNCTION0(env,currentCode,currentCode,i64) { @@ -175,16 +163,22 @@ DEFINE_INTRINSIC_FUNCTION1(env,requireScope,requireScope,none,i64,scope) { wasm_interface::get().current_validate_context->require_scope( scope ); } -DEFINE_INTRINSIC_FUNCTION4(env,store_i64,store_i64,i32,i64,scope,i64,table,i32,valueptr,i32,valuelen) +DEFINE_INTRINSIC_FUNCTION4(env,store_i64,store_i64,i32,i64,scope,i64,table,i32,valueptr,i32,valuelen) { + static const uint32_t keylen = sizeof(uint64_t); + + FC_ASSERT( valuelen >= sizeof(uint64_t) ); + auto& wasm = wasm_interface::get(); FC_ASSERT( wasm.current_apply_context, "no apply context found" ); - FC_ASSERT( valuelen >= sizeof(uint64_t) ); auto mem = wasm.current_memory; char* value = memoryArrayPtr( mem, valueptr, valuelen); uint64_t* key = reinterpret_cast(value); + valuelen -= keylen; + value += keylen; + //idump((Name(scope))(Name(code))(Name(table))(Name(key))(valuelen) ); return wasm.current_apply_context->store_i64( scope, table, *key, value, valuelen ); @@ -193,17 +187,23 @@ DEFINE_INTRINSIC_FUNCTION4(env,store_i64,store_i64,i32,i64,scope,i64,table,i32,v DEFINE_INTRINSIC_FUNCTION5(env,load_i64,load_i64,i32,i64,scope,i64,code,i64,table,i32,valueptr,i32,valuelen) { //idump((Name(scope))(Name(code))(Name(table))(Name(key))(valuelen) ); - - FC_ASSERT( valuelen >= sizeof(uint64_t) ); + static const uint32_t keylen = sizeof(uint64_t); + + FC_ASSERT( valuelen >= keylen ); auto& wasm = wasm_interface::get(); - FC_ASSERT( wasm.current_validate_context, "no apply context found" ); + FC_ASSERT( wasm.current_validate_context, "no validate context found" ); auto mem = wasm.current_memory; char* value = memoryArrayPtr( mem, valueptr, valuelen); uint64_t* key = reinterpret_cast(value); - return wasm.current_validate_context->load_i64( scope, code, table, *key, value, valuelen ); + valuelen -= keylen; + value += keylen; + + auto res = wasm.current_validate_context->load_i64( scope, code, table, *key, value, valuelen ); + if(res > 0) res += keylen; + return res; } DEFINE_INTRINSIC_FUNCTION3(env,remove_i64,remove_i64,i32,i64,scope,i64,table,i64,key) { @@ -394,7 +394,7 @@ DEFINE_INTRINSIC_FUNCTION1(env,free,free,none,i32,ptr) { FC_ASSERT( getFunctionType(call)->parameters.size() == 2 ); // idump((current_validate_context->msg.code)(current_validate_context->msg.type)(current_validate_context->code)); - std::vector args = { Value(uint64_t(current_validate_context->msg.code)), + std::vector args = { Value(uint64_t(current_validate_context->code)), Value(uint64_t(current_validate_context->msg.type)) }; auto& state = *current_state; diff --git a/libraries/types/include/eos/types/native.hpp b/libraries/types/include/eos/types/native.hpp index 152827e9e8f..b70a4de10bc 100644 --- a/libraries/types/include/eos/types/native.hpp +++ b/libraries/types/include/eos/types/native.hpp @@ -67,26 +67,33 @@ namespace eos { namespace types { } static constexpr uint64_t string_to_name( const char* str ) { + uint32_t len = 0; while( str[len] ) ++len; uint64_t value = 0; - for( uint32_t i = 0; i <= 12 && i < len; ++i ) { - value <<= 5; - value |= char_to_symbol( str[ len -1 - i ] ); - } + for( uint32_t i = 0; i <= 12; ++i ) { + uint64_t c = 0; + if( i < len && i <= 12 ) c = char_to_symbol( str[i] ); + + if( i < 12 ) { + c &= 0x1f; + c <<= 64-5*(i+1); + } + else { + c &= 0x0f; + } - if( len == 13 ) { - value <<= 4; - value |= 0x0f & char_to_symbol( str[ 12 ] ); + value |= c; } + return value; } struct Name { uint64_t value = 0; - bool valid()const { return 0 == (value >> 60); } + bool valid()const { return true; } bool empty()const { return 0 == value; } bool good()const { return !empty() && valid(); } @@ -95,17 +102,9 @@ namespace eos { namespace types { void set( const char* str ) { try { - value = 0; const auto len = strnlen(str,14); FC_ASSERT( len <= 13 ); - for( uint32_t i = 0; i <= 12 && i < len; ++i ) { - value <<= 5; - value |= char_to_symbol( str[ len -1 - i ] ); - } - if( 13 == len ) { - value <<= 4; - value |= 0x0f & char_to_symbol( str[ 12 ] ); - } + value = string_to_name(str); }FC_CAPTURE_AND_RETHROW( (str) ) } Name( uint64_t v = 0 ):value(v){ @@ -114,15 +113,15 @@ namespace eos { namespace types { explicit operator String()const { static const char* charmap = ".abcdefghijklmnopqrstuvwxyz12345"; - String str; + + String str(13,'.'); + uint64_t tmp = value; - for( uint32_t i = 0; i < 12; ++i ) { - char c = charmap[tmp & 0x1f ]; - str += c; - tmp >>= 5; + for( uint32_t i = 0; i <= 12; ++i ) { + char c = charmap[tmp & (i == 0 ? 0x0f : 0x1f)]; + str[12-i] = c; + tmp >>= (i == 0 ? 4 : 5); } - char c = charmap[tmp & 0x0f]; - str += c; boost::algorithm::trim_right_if( str, []( char c ){ return c == '.'; } ); return str; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e909d20667c..618833781d1 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -16,4 +16,10 @@ if(WASM_TOOLCHAIN) target_link_libraries( slow_test eos_native_contract eos_chain chainbase eos_utilities eos_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) target_include_directories( slow_test PUBLIC ${CMAKE_SOURCE_DIR}/contracts ) add_dependencies(slow_test currency exchange) + + file(GLOB API_TESTS "api_tests/*.cpp") + add_executable( api_test ${API_TESTS} ${COMMON_SOURCES} ) + target_link_libraries( api_test eos_native_contract eos_chain chainbase eos_utilities eos_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) + target_include_directories( api_test PUBLIC ${CMAKE_SOURCE_DIR}/contracts ) + add_dependencies(api_test test_api) endif() \ No newline at end of file diff --git a/tests/api_tests/api_tests.cpp b/tests/api_tests/api_tests.cpp new file mode 100644 index 00000000000..fe96833cfed --- /dev/null +++ b/tests/api_tests/api_tests.cpp @@ -0,0 +1,374 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "../common/database_fixture.hpp" + +#include +#include +#include +#include +#include +#include + +#include +#include +FC_REFLECT( dummy_message, (a)(b)(c) ); +FC_REFLECT( u128_msg, (values) ); + +using namespace eos; +using namespace chain; + +namespace bio = boost::iostreams; + +BOOST_AUTO_TEST_SUITE(api_tests) + +vector assemble_wast( const std::string& wast ) { + // std::cout << "\n" << wast << "\n"; + IR::Module module; + std::vector parseErrors; + WAST::parseModule(wast.c_str(),wast.size(),module,parseErrors); + if(parseErrors.size()) + { + // Print any parse errors; + std::cerr << "Error parsing WebAssembly text file:" << std::endl; + for(auto& error : parseErrors) + { + std::cerr << ":" << error.locus.describe() << ": " << error.message.c_str() << std::endl; + std::cerr << error.locus.sourceLine << std::endl; + std::cerr << std::setw(error.locus.column(8)) << "^" << std::endl; + } + FC_ASSERT( !"error parsing wast" ); + } + + try + { + // Serialize the WebAssembly module. + Serialization::ArrayOutputStream stream; + WASM::serialize(stream,module); + return stream.getBytes(); + } + catch(Serialization::FatalSerializationException exception) + { + std::cerr << "Error serializing WebAssembly binary file:" << std::endl; + std::cerr << exception.message << std::endl; + throw; + } +} + +void SetCode( testing_blockchain& chain, AccountName account, const char* wast ) { + try { + types::setcode handler; + handler.account = account; + + auto wasm = assemble_wast( wast ); + handler.code.resize(wasm.size()); + memcpy( handler.code.data(), wasm.data(), wasm.size() ); + + { + eos::chain::SignedTransaction trx; + trx.scope = {account}; + trx.messages.resize(1); + trx.messages[0].code = config::EosContractName; + //trx.messages[0].recipients = {account}; + trx.setMessage(0, "setcode", handler); + trx.expiration = chain.head_block_time() + 100; + trx.set_reference_block(chain.head_block_id()); + chain.push_transaction(trx); + chain.produce_blocks(1); + } +} FC_LOG_AND_RETHROW( ) } + +uint32_t CallFunction( testing_blockchain& chain, const types::Message& msg, const vector& data, const vector& scope = {N(test_api)}) { + static int expiration = 1; + eos::chain::SignedTransaction trx; + trx.scope = scope; + + //msg.data.clear(); + vector& dest = *(vector *)(&msg.data); + std::copy(data.begin(), data.end(), std::back_inserter(dest)); + + std::cout << "MANDO: " << msg.code << " " << msg.type << std::endl; + trx.emplaceMessage(msg); + + trx.expiration = chain.head_block_time() + expiration++; + trx.set_reference_block(chain.head_block_id()); + //idump((trx)); + chain.push_transaction(trx); + + // + auto& wasm = wasm_interface::get(); + uint32_t *res = Runtime::memoryArrayPtr(wasm.current_memory, (1<<16) - 2*sizeof(uint32_t), 2); + + if(res[0] == WASM_TEST_FAIL) { + char *str_err = &Runtime::memoryRef(wasm.current_memory, res[1]); + wlog( "${err}", ("err",str_err)); + } + + return res[0]; +} + + +using namespace std; +string readFile2(const string &fileName) +{ + ifstream ifs(fileName.c_str(), ios::in | ios::binary | ios::ate); + + ifstream::pos_type fileSize = ifs.tellg(); + ifs.seekg(0, ios::beg); + + vector bytes(fileSize); + ifs.read(bytes.data(), fileSize); + + return string(bytes.data(), fileSize); +} + +uint64_t TEST_METHOD(const char* CLASS, const char *METHOD) { + //std::cerr << CLASS << "::" << METHOD << std::endl; + return ( (uint64_t(DJBH(CLASS))<<32) | uint32_t(DJBH(METHOD)) ); +} + +#define CALL_TEST_FUNCTION(TYPE, AUTH, DATA) CallFunction(chain, Message{"test_api", AUTH, TYPE}, DATA) +#define CALL_TEST_FUNCTION_SCOPE(TYPE, AUTH, DATA, SCOPE) CallFunction(chain, Message{"test_api", AUTH, TYPE}, DATA, SCOPE) + +bool is_access_violation(fc::unhandled_exception const & e) { + try { + std::rethrow_exception(e.get_inner_exception()); + } + catch (const Runtime::Exception& e) { + return e.cause == Runtime::Exception::Cause::accessViolation; + } catch (...) { + + } + return false; +} + +bool is_tx_missing_recipient(tx_missing_recipient const & e) { return true;} +bool is_tx_missing_auth(tx_missing_auth const & e) { return true; } +bool is_tx_missing_scope(tx_missing_scope const& e) { return true; } +bool is_assert_exception(fc::assert_exception const & e) { return true; } + +std::vector capture; + +struct MySink : public bio::sink +{ + + std::streamsize write(const char* s, std::streamsize n) + { + std::string tmp; + tmp.assign(s, n); + capture.push_back(tmp); + std::cout << "stream : [" << tmp << "]" << std::endl; + return n; + } +}; +uint32_t last_fnc_err = 0; +#define CAPTURE(STREAM, EXEC) \ + {\ + capture.clear(); \ + bio::stream_buffer sb; sb.open(MySink()); \ + std::streambuf *oldbuf = std::STREAM.rdbuf(&sb); \ + last_fnc_err = EXEC; \ + std::STREAM.rdbuf(oldbuf); \ + } + + +BOOST_FIXTURE_TEST_CASE(test_all, testing_fixture) +{ try { + + std::string test_api_wast_str(test_api_wast); + //auto test_api_wast = readFile2("/home/matu/Documents/Dev/eos/contracts/test_api/test_api.wast"); + //std::cout << test_api_wast << std::endl; + + Make_Blockchain(chain); + chain.produce_blocks(2); + Make_Account(chain, test_api); + Make_Account(chain, another); + Make_Account(chain, acc1); + Make_Account(chain, acc2); + Make_Account(chain, acc3); + Make_Account(chain, acc4); + chain.produce_blocks(1); + + //Set test code + SetCode(chain, "test_api", test_api_wast_str.c_str()); + SetCode(chain, "acc1", test_api_wast_str.c_str()); + SetCode(chain, "acc2", test_api_wast_str.c_str()); + + //Test types + BOOST_CHECK_MESSAGE( CALL_TEST_FUNCTION( TEST_METHOD("test_types", "types_size"), {}, {} ) == WASM_TEST_PASS, "test_types::types_size()" ); + BOOST_CHECK_MESSAGE( CALL_TEST_FUNCTION( TEST_METHOD("test_types", "char_to_symbol"), {}, {} ) == WASM_TEST_PASS, "test_types::char_to_symbol()" ); + BOOST_CHECK_MESSAGE( CALL_TEST_FUNCTION( TEST_METHOD("test_types", "string_to_name"), {}, {} ) == WASM_TEST_PASS, "test_types::string_to_name()" ); + BOOST_CHECK_MESSAGE( CALL_TEST_FUNCTION( TEST_METHOD("test_types", "name_class"), {}, {} ) == WASM_TEST_PASS, "test_types::name_class()" ); + + //Test message + dummy_message dummy13{DUMMY_MESSAGE_DEFAULT_A, DUMMY_MESSAGE_DEFAULT_B, DUMMY_MESSAGE_DEFAULT_C}; + BOOST_CHECK_MESSAGE( CALL_TEST_FUNCTION( TEST_METHOD("test_message", "read_message"), {}, fc::raw::pack(dummy13) ) == WASM_TEST_PASS, "test_message::read_message()" ); + + std::vector raw_bytes((1<<16)); + BOOST_CHECK_MESSAGE( CALL_TEST_FUNCTION( TEST_METHOD("test_message", "read_message_to_0"), {}, raw_bytes) == WASM_TEST_PASS, "test_message::read_message_to_0()" ); + + raw_bytes.resize((1<<16)+1); + BOOST_CHECK_EXCEPTION( CALL_TEST_FUNCTION( TEST_METHOD("test_message", "read_message_to_0"), {}, raw_bytes), + fc::unhandled_exception, is_access_violation ); + + raw_bytes.resize(1); + BOOST_CHECK_MESSAGE( CALL_TEST_FUNCTION( TEST_METHOD("test_message", "read_message_to_64k"), {}, raw_bytes) == WASM_TEST_PASS, "test_message::read_message_to_64k()" ); + + raw_bytes.resize(2); + BOOST_CHECK_EXCEPTION( CALL_TEST_FUNCTION( TEST_METHOD("test_message", "read_message_to_64k"), {}, raw_bytes), + fc::unhandled_exception, is_access_violation ); + + BOOST_CHECK_MESSAGE( CALL_TEST_FUNCTION( TEST_METHOD("test_message", "require_notice"), {}, raw_bytes) == WASM_TEST_PASS, "test_message::require_notice()" ); + + BOOST_CHECK_EXCEPTION( CALL_TEST_FUNCTION( TEST_METHOD("test_message", "require_auth"), {}, {}), + tx_missing_auth, is_tx_missing_auth ); + + auto a3only = vector{{"acc3","active"}}; + BOOST_CHECK_EXCEPTION( CALL_TEST_FUNCTION( TEST_METHOD("test_message", "require_auth"), a3only, {}), + tx_missing_auth, is_tx_missing_auth ); + + auto a4only = vector{{"acc4","active"}}; + BOOST_CHECK_EXCEPTION( CALL_TEST_FUNCTION( TEST_METHOD("test_message", "require_auth"), a4only, {}), + tx_missing_auth, is_tx_missing_auth ); + + auto a3a4 = vector{{"acc3","active"}, {"acc4","active"}}; + BOOST_CHECK_MESSAGE( CALL_TEST_FUNCTION( TEST_METHOD("test_message", "require_auth"), a3a4, {}) == WASM_TEST_PASS, "test_message::require_auth()"); + + BOOST_CHECK_EXCEPTION( CALL_TEST_FUNCTION( TEST_METHOD("test_message", "assert_false"), {}, {}), + fc::assert_exception, is_assert_exception ); + + BOOST_CHECK_MESSAGE( CALL_TEST_FUNCTION( TEST_METHOD("test_message", "assert_true"), {}, {}) == WASM_TEST_PASS, "test_message::assert_true()"); + + chain.produce_blocks(1); + + uint32_t now = chain.head_block_time().sec_since_epoch(); + BOOST_CHECK_MESSAGE( CALL_TEST_FUNCTION( TEST_METHOD("test_message", "now"), {}, fc::raw::pack(now)) == WASM_TEST_PASS, "test_message::now()"); + + chain.produce_blocks(1); + BOOST_CHECK_MESSAGE( CALL_TEST_FUNCTION( TEST_METHOD("test_message", "now"), {}, fc::raw::pack(now)) == WASM_TEST_FAIL, "test_message::now()"); + + //Test print + CAPTURE(cerr, CALL_TEST_FUNCTION( TEST_METHOD("test_print", "test_prints"), {}, {}) ); + + BOOST_CHECK_EQUAL( last_fnc_err , WASM_TEST_PASS); + BOOST_CHECK_EQUAL( capture.size() , 3); + BOOST_CHECK_EQUAL( (capture[0] == "ab" && capture[1] == "c" && capture[2] == "efg") , true); + + auto U64Str = [](uint64_t v) -> std::string { + std::stringstream s; + s << v; + return s.str(); + }; + + CAPTURE(cerr, CALL_TEST_FUNCTION( TEST_METHOD("test_print", "test_printi"), {}, {}) ); + BOOST_CHECK_EQUAL( capture.size() , 3); + BOOST_CHECK_EQUAL( capture[0], U64Str(0) ); + BOOST_CHECK_EQUAL( capture[1], U64Str(556644) ); + BOOST_CHECK_EQUAL( capture[2], U64Str(-1) ); + + auto U128Str = [](uint128_t value) -> std::string { + fc::uint128_t v(value>>64, uint64_t(value) ); + return fc::variant(v).get_string(); + }; + + CAPTURE(cerr, CALL_TEST_FUNCTION( TEST_METHOD("test_print", "test_printi128"), {}, {}) ); + BOOST_CHECK_EQUAL( capture.size() , 3); + BOOST_CHECK_EQUAL( capture[0], U128Str(-1)); + BOOST_CHECK_EQUAL( capture[1], U128Str(0)); + BOOST_CHECK_EQUAL( capture[2], U128Str(87654323456)); + + CAPTURE(cerr, CALL_TEST_FUNCTION( TEST_METHOD("test_print", "test_printn"), {}, {}) ); + BOOST_CHECK_EQUAL( capture.size() , 8); + BOOST_CHECK_EQUAL( ( + capture[0] == "abcde" && + capture[1] == "ab.de" && + capture[2] == "1q1q1q" && + capture[3] == "abcdefghijk" && + capture[4] == "abcdefghijkl" && + capture[5] == "abcdefghijklm" && + capture[6] == "abcdefghijklm" && + capture[7] == "abcdefghijklm" + ), true); + + //Test math + std::random_device rd; + std::mt19937_64 gen(rd()); + std::uniform_int_distribution dis; + + for(int i=0; i<10; i++) { + u128_msg msg; + msg.values[0] = dis(gen); msg.values[0] <<= 64; msg.values[0] |= dis(gen); + msg.values[1] = dis(gen); msg.values[1] <<= 64; msg.values[1] |= dis(gen); + msg.values[2] = msg.values[0] * msg.values[1]; + BOOST_CHECK_MESSAGE( CALL_TEST_FUNCTION( TEST_METHOD("test_math", "test_multeq_i128"), {}, fc::raw::pack(msg) ) == WASM_TEST_PASS, "test_math::test_multeq_i128()" ); + } + + for(int i=0; i<10; i++) { + u128_msg msg; + msg.values[0] = dis(gen); msg.values[0] <<= 64; msg.values[0] |= dis(gen); + msg.values[1] = dis(gen); msg.values[1] <<= 64; msg.values[1] |= dis(gen); + msg.values[2] = msg.values[0] / msg.values[1]; + BOOST_CHECK_MESSAGE( CALL_TEST_FUNCTION( TEST_METHOD("test_math", "test_diveq_i128"), {}, fc::raw::pack(msg) ) == WASM_TEST_PASS, "test_math::test_diveq_i128()" ); + } + + BOOST_CHECK_EXCEPTION( CALL_TEST_FUNCTION( TEST_METHOD("test_math", "test_diveq_i128_by_0"), {}, {} ), + fc::assert_exception, is_assert_exception ); + + //Test db (i64) + const auto& idx = chain_db.get_index(); + + BOOST_CHECK_MESSAGE( CALL_TEST_FUNCTION( TEST_METHOD("test_db", "key_i64_general"), {}, {} ) == WASM_TEST_PASS, "test_db::key_i64_general()" ); + BOOST_CHECK_EQUAL( std::distance(idx.begin(), idx.end()) , 4); + + auto itr = idx.lower_bound( boost::make_tuple( N(test_api), N(test_api), N(test_table)) ); + + BOOST_CHECK_EQUAL((uint64_t)itr->key, N(alice)); ++itr; + BOOST_CHECK_EQUAL((uint64_t)itr->key, N(bob)); ++itr; + BOOST_CHECK_EQUAL((uint64_t)itr->key, N(carol)); ++itr; + BOOST_CHECK_EQUAL((uint64_t)itr->key, N(dave)); + + BOOST_CHECK_MESSAGE( CALL_TEST_FUNCTION( TEST_METHOD("test_db", "key_i64_remove_all"), {}, {} ) == WASM_TEST_PASS, "test_db::key_i64_remove_all()" ); + BOOST_CHECK_EQUAL( std::distance(idx.begin(), idx.end()) , 0); + + BOOST_CHECK_EXCEPTION( CALL_TEST_FUNCTION( TEST_METHOD("test_db", "key_i64_small_load"), {}, {} ), + fc::assert_exception, is_assert_exception ); + + BOOST_CHECK_EXCEPTION( CALL_TEST_FUNCTION( TEST_METHOD("test_db", "key_i64_small_store"), {}, {} ), + fc::assert_exception, is_assert_exception ); + + BOOST_CHECK_EXCEPTION( CALL_TEST_FUNCTION_SCOPE( TEST_METHOD("test_db", "key_i64_store_scope"), {}, {}, {N(another)} ), + tx_missing_scope, is_tx_missing_scope ); + + BOOST_CHECK_EXCEPTION( CALL_TEST_FUNCTION_SCOPE( TEST_METHOD("test_db", "key_i64_remove_scope"), {}, {}, {N(another)} ), + tx_missing_scope, is_tx_missing_scope ); + + BOOST_CHECK_MESSAGE( CALL_TEST_FUNCTION( TEST_METHOD("test_db", "key_i64_not_found"), {}, {} ) == WASM_TEST_PASS, "test_db::key_i64_not_found()" ); + + //Test db (i128i128) + BOOST_CHECK_MESSAGE( CALL_TEST_FUNCTION( TEST_METHOD("test_db", "key_i128i128_general"), {}, {} ) == WASM_TEST_PASS, "test_db::key_i128i128_general()" ); + +} FC_LOG_AND_RETHROW() } + + +BOOST_AUTO_TEST_SUITE_END()